Repository: lutris/lutris

Issue:
Failure to read Steam's config.vdf due to wrong case
Lutris can't read Steam's config.vdf file because the "Steam" value is actually lowercase when Lutris expects it to be uppercase.

![19:05:34](https://user-images.githubusercontent.com/17669985/72685104-730c4480-3aef-11ea-9217-4df94fac22bd.png)

Same as #1966.
Failure to read Steam's config.vdf due to wrong case
Lutris can't read Steam's config.vdf file because the "Steam" value is actually lowercase when Lutris expects it to be uppercase.

![19:05:34](https://user-images.githubusercontent.com/17669985/72685104-730c4480-3aef-11ea-9217-4df94fac22bd.png)

Same as #1966.


Discussion:
The original problem was solved here : https://github.com/lutris/lutris/commit/5c833761d53f37571daafd6deaeefa30663ac6ea

We can just try to d o something like this 
`
except KeyError:
        try:
            return config["InstallConfigStore"]["Software"]["valve"]["steam"]
`
I think Strycore will have the final answer on this
Can't the keys for the dictionary be made case insensitive?
I got 2 possible solutions to this.
These are more PoCs, not tested and probably need a little refactoring.
I just wanna know which one I should improve and create a PR for.

1. [Just adding the case that "valve" is lowercase.](https://github.com/connectety/lutris/commit/0c16c6ea4b4234a8cd1e61661af84cb851bb0fcf)
2. [Adding a method, which looks through the dictionary recursively.](https://github.com/connectety/lutris/commit/8736f374b6ef59a024dcb2be51dfc9b7032f7fb8)

Another solution would be to recursively make all keys in the dictionary lowercase.

Which solution is better?
The first method is mostly a hack that resembles the initial implementation (which was bad and ended up not being sufficient).
The second method seems like a way better choice.
I feel the best choice for this would be to have some kind of case insensitive dict like the one implemented here: https://pypi.org/project/pydicti/

The VDF format does seam to be case-sensitive.
The [valve docs](https://developer.valvesoftware.com/wiki/KeyValues) don't say anything about case sensitivity and the vdf python module is case-sensitive. Only this specific case seams to need a case-insensitive implementation.
I feel like an extra library for one case is a bit excessive, but often a good & finished implementation is better then a homemade one.
I am unsure what to do.
There is a vendor folder in the source code where we can put stuff like that. pydicti's code is contained to a single file so it's easy to distribute if we ever need to.
For the time being can you create a PR for your 2nd solution? I this solves the bug, it's a good solution.
The original problem was solved here : https://github.com/lutris/lutris/commit/5c833761d53f37571daafd6deaeefa30663ac6ea

We can just try to d o something like this 
`
except KeyError:
        try:
            return config["InstallConfigStore"]["Software"]["valve"]["steam"]
`
I think Strycore will have the final answer on this
Can't the keys for the dictionary be made case insensitive?
I got 2 possible solutions to this.
These are more PoCs, not tested and probably need a little refactoring.
I just wanna know which one I should improve and create a PR for.

1. [Just adding the case that "valve" is lowercase.](https://github.com/connectety/lutris/commit/0c16c6ea4b4234a8cd1e61661af84cb851bb0fcf)
2. [Adding a method, which looks through the dictionary recursively.](https://github.com/connectety/lutris/commit/8736f374b6ef59a024dcb2be51dfc9b7032f7fb8)

Another solution would be to recursively make all keys in the dictionary lowercase.

Which solution is better?
The first method is mostly a hack that resembles the initial implementation (which was bad and ended up not being sufficient).
The second method seems like a way better choice.
I feel the best choice for this would be to have some kind of case insensitive dict like the one implemented here: https://pypi.org/project/pydicti/

The VDF format does seam to be case-sensitive.
The [valve docs](https://developer.valvesoftware.com/wiki/KeyValues) don't say anything about case sensitivity and the vdf python module is case-sensitive. Only this specific case seams to need a case-insensitive implementation.
I feel like an extra library for one case is a bit excessive, but often a good & finished implementation is better then a homemade one.
I am unsure what to do.
There is a vendor folder in the source code where we can put stuff like that. pydicti's code is contained to a single file so it's easy to distribute if we ever need to.
For the time being can you create a PR for your 2nd solution? I this solves the bug, it's a good solution.

Resolving patch:
diff --git a/lutris/util/steam/config.py b/lutris/util/steam/config.py
--- a/lutris/util/steam/config.py
+++ b/lutris/util/steam/config.py
@@ -26,18 +26,25 @@ def get_default_acf(appid, name):
 
 def read_config(steam_data_dir):
     """Read the Steam configuration and return it as an object"""
+
+    def get_entry_case_insensitive(config_dict, path):
+        for key, value in config_dict.items():
+            if key.lower() == path[0].lower():
+                if len(path) <= 1:
+                    return config_dict[key]
+
+                return get_entry_case_insensitive(config_dict[key], path[1:])
+        raise KeyError(path[0])
+
     config_filename = os.path.join(steam_data_dir, "config/config.vdf")
     if not system.path_exists(config_filename):
         return None
     with open(config_filename, "r") as steam_config_file:
         config = vdf_parse(steam_config_file, {})
     try:
-        return config["InstallConfigStore"]["Software"]["Valve"]["Steam"]
-    except KeyError:
-        try:
-            return config["InstallConfigStore"]["Software"]["valve"]["Steam"]
-        except KeyError as ex:
-            logger.error("Steam config %s is empty: %s", config_filename, ex)
+        return get_entry_case_insensitive(config, ["InstallConfigStore", "Software", "Valve", "Steam"])
+    except KeyError as ex:
+        logger.error("Steam config %s is empty: %s", config_filename, ex)
 
 
 def get_steamapps_paths_for_platform(platform_name):


Test patch: