mtkennerly / ludusavi

Backup tool for PC game saves
MIT License
2.65k stars 58 forks source link

Proton initialized title files not found when casing does not match. #55

Closed wtjones closed 2 years ago

wtjones commented 4 years ago

I had to modify the manifest make this entry pull in. The reason for the lower cases is due to the fact that the wine prefix was created on install, and the game's installer must have defaulted to create my games/company of heroes 2 with that casing for no apparent reason.

The PCGamingWiki could be updated, but would still be broken for users that already had a My Games folder for example.

before:

<winDocuments>/My Games/Company of Heroes 2/Savegames:
      tags:
        - save
      when:
        - os: windows

after:

<winDocuments>/my games/company of heroes 2/Savegames:
      tags:
        - save
      when:
        - os: windows

image

mtkennerly commented 4 years ago

Ah, yeah, since Windows doesn't actually enforce the capitalization, it doesn't make sense to enforce it when checking a Windows entry on Linux. I'll see if I can make it case-insensitive here.

In the meantime, don't forget that manual edits to the manifest will be reset when it downloads a new copy, so you may want to make a custom game entry instead until this is fixed (if it has the same name, it'll override the manifest's entry for that game).

mtkennerly commented 3 years ago

It turns out that the case sensitivity option in the glob crate is ignored outright (https://github.com/rust-lang-nursery/glob/issues/61), so I would need to use a different crate to fix this. I tried globwalk, but it seems to have some other issues/differences that would prevent me from switching, and I'm not sure there are any other viable options right now. I'm afraid this may have to wait \:/

mtkennerly commented 2 years ago

Took another look at this. Here's what I found so far:

Glob v0.3.0

I double checked the behavior of the case sensitivity option, and it seems like it works in some cases, but not all.

Example on Windows (setting: case-sensitive):

Similar behavior on Linux (setting: case-insensitive):

checking: "/home/vagrant/a-custom-game/*.txt"
found:    "/home/vagrant/a-custom-game/file1.txt"
found:    "/home/vagrant/a-custom-game/file2.txt"

checking: "/home/vagrant/a-custom-game/*.Txt"
found:    "/home/vagrant/a-custom-game/file1.txt"
found:    "/home/vagrant/a-custom-game/file2.txt"

checking: "/home/vagrant/a-custom-Game/*.txt"
[no results]

checking: "/home/vagrant/a-custom-g*me/*.txt"
found:    "/home/vagrant/a-custom-game/file1.txt"
found:    "/home/vagrant/a-custom-game/file2.txt"

It seems like the case insensitivity option only affects text that's adjacent to a wildcard.

GlobWalk v0.8.1

Doesn't seem to support absolute paths in globs. Even if you provide an absolute path, I think it tries to find the path relative to the current folder. You can pass in a different base folder, but it doesn't seem to work correctly when the base is just C:, so you'd have to split something like C:/foo/bar/*.txt into two parts: C:/foo and bar/*.txt. I don't think it can find files like C:/*.txt unless the current directory is already C:.

With the below code, it took 28 seconds to scan a single game, and it encountered a large number of "permission denied" errors. Maybe related to https://github.com/Gilnaa/globwalk/issues/29.

diff --git a/src/path.rs b/src/path.rs
index 0cddfac..d0b7825 100644
--- a/src/path.rs
+++ b/src/path.rs
@@ -370,13 +370,15 @@ impl StrictPath {
     }

     pub fn glob(&self) -> Vec<StrictPath> {
-        let options = glob::MatchOptions {
-            case_sensitive: crate::prelude::CASE_INSENSITIVE_OS,
-            require_literal_separator: true,
-            require_literal_leading_dot: false,
-        };
-        match glob::glob_with(&self.render(), options) {
-            Ok(xs) => xs.filter_map(|r| r.ok()).map(StrictPath::from).collect(),
+        let rendered = self.render();
+        let parts: Vec<_> = rendered.split('/').collect();
+        if parts.len() < 3 {
+            return vec![];
+        }
+        let base = format!("{}/{}", parts[0], parts[1]);
+        let path = parts[2..].join("/");
+        match globwalk::GlobWalkerBuilder::new(base, path).case_insensitive(crate::prelude::CASE_INSENSITIVE_OS).follow_links(true).build() {
+            Ok(xs) => xs.filter_map(|r| r.ok()).map(|x| StrictPath::from(x.path())).collect(),
             Err(_) => vec![],
         }
     }

Wax v0.5.0

Significantly slower than the current implementation. Timing for a full backup preview:

It also doesn't natively support globs that begin with a Windows drive letter, which means every path has to be split in a way that it understands.

diff --git a/src/path.rs b/src/path.rs
index 0cddfac..8254569 100644
--- a/src/path.rs
+++ b/src/path.rs
@@ -370,17 +370,16 @@ impl StrictPath {
     }

     pub fn glob(&self) -> Vec<StrictPath> {
-        let options = glob::MatchOptions {
-            case_sensitive: crate::prelude::CASE_INSENSITIVE_OS,
-            require_literal_separator: true,
-            require_literal_leading_dot: false,
+        let (drive, plain) = self.split_drive();
+        let plain = plain.replace('(', "\\(").replace(')', "\\)");
+        let glob = match wax::Glob::new(&plain) {
+            Ok(x) => x,
+            Err(_) => return vec![self.clone()],
         };
-        match glob::glob_with(&self.render(), options) {
-            Ok(xs) => xs.filter_map(|r| r.ok()).map(StrictPath::from).collect(),
-            Err(_) => vec![],
-        }
+        glob.walk(drive + "/").filter_map(|r| r.ok()).map(|x| StrictPath::from(x.path())).collect()
     }

+
     pub fn same_content(&self, other: &StrictPath) -> bool {
         self.try_same_content(other).unwrap_or(false)
     }
mtkennerly commented 2 years ago

I've forked the Glob crate and just published Globetter, which fixes the case sensitivity option. I'm planning to incorporate this into Ludusavi.