[gnome-games/wip/exalm/tools: 2/2] steam: List games from registry



commit acf8f28f1626e8488acb35831c4e8bcd8c445e2e
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Tue Nov 19 19:58:57 2019 +0500

    steam: List games from registry
    
    Since all installed games are listed in the registry, there's no need to
    ever enumerate app manifest files. Instead, just list games from the
    registry. Since tools like Proton don't have name in the registry, they
    can be easily skipped.
    
    Fixes https://gitlab.gnome.org/GNOME/gnome-games/issues/103

 plugins/steam/src/meson.build             |  2 +-
 plugins/steam/src/steam-error.vala        |  1 -
 plugins/steam/src/steam-game-data.vala    | 21 +++++++++
 plugins/steam/src/steam-plugin.vala       | 71 +++++++++++++++---------------
 plugins/steam/src/steam-title.vala        | 21 ---------
 plugins/steam/src/steam-uri-iterator.vala | 65 ++++++----------------------
 plugins/steam/src/steam-uri-source.vala   | 72 ++++++++++++++-----------------
 7 files changed, 101 insertions(+), 152 deletions(-)
---
diff --git a/plugins/steam/src/meson.build b/plugins/steam/src/meson.build
index a473a85f..628ad58a 100644
--- a/plugins/steam/src/meson.build
+++ b/plugins/steam/src/meson.build
@@ -1,10 +1,10 @@
 vala_sources = [
   'steam-cover.vala',
   'steam-error.vala',
+  'steam-game-data.vala',
   'steam-icon.vala',
   'steam-plugin.vala',
   'steam-registry.vala',
-  'steam-title.vala',
   'steam-uri-iterator.vala',
   'steam-uri-source.vala',
   'steam-uid.vala',
diff --git a/plugins/steam/src/steam-error.vala b/plugins/steam/src/steam-error.vala
index 42850d41..1f12286e 100644
--- a/plugins/steam/src/steam-error.vala
+++ b/plugins/steam/src/steam-error.vala
@@ -1,6 +1,5 @@
 // This file is part of GNOME Games. License: GPL-3.0+.
 
 errordomain Games.SteamError {
-       NO_APPID,
        NO_NAME,
 }
diff --git a/plugins/steam/src/steam-game-data.vala b/plugins/steam/src/steam-game-data.vala
new file mode 100644
index 00000000..c4c23646
--- /dev/null
+++ b/plugins/steam/src/steam-game-data.vala
@@ -0,0 +1,21 @@
+// This file is part of GNOME Games. License: GPL-3.0+.
+
+private class Games.SteamGameData : Object {
+       private HashTable<string, string> games;
+
+       construct {
+               games = new HashTable<string, string> (str_hash, str_equal);
+       }
+
+       public void add_game (string appid, string title) {
+               games[appid] = title;
+       }
+
+       public string[] get_appids () {
+               return games.get_keys_as_array ();
+       }
+
+       public string get_title (string appid) {
+               return games[appid];
+       }
+}
diff --git a/plugins/steam/src/steam-plugin.vala b/plugins/steam/src/steam-plugin.vala
index 522c21b3..ba057e12 100644
--- a/plugins/steam/src/steam-plugin.vala
+++ b/plugins/steam/src/steam-plugin.vala
@@ -4,16 +4,20 @@ private class Games.SteamPlugin : Object, Plugin {
        private const string STEAM_APPID = "com.valvesoftware.Steam";
        private const string STEAM_FLATPAK_DIR = "/.var/app/" + STEAM_APPID;
 
-       private const string STEAM_FILE_SCHEME = "steam+file";
-       private const string FLATPAK_STEAM_FILE_SCHEME = "flatpak+steam+file";
+       private const string STEAM_SCHEME = "steam";
+       private const string FLATPAK_STEAM_SCHEME = "flatpak+steam";
        private const string PLATFORM_ID = "Steam";
        private const string PLATFORM_NAME = _("Steam");
        private const string PLATFORM_UID_PREFIX = "steam";
 
        private static Platform platform;
+       private static SteamGameData game_data;
+       private static SteamGameData flatpak_game_data;
 
        static construct {
                platform = new GenericPlatform (PLATFORM_ID, PLATFORM_NAME, PLATFORM_UID_PREFIX);
+               game_data = new SteamGameData ();
+               flatpak_game_data = new SteamGameData ();
 
                // Add directories where Steam installs icons
                var home = Environment.get_home_dir ();
@@ -34,14 +38,14 @@ private class Games.SteamPlugin : Object, Plugin {
                UriSource[] sources = {};
 
                try {
-                       sources += new SteamUriSource (home, STEAM_FILE_SCHEME);
+                       sources += new SteamUriSource (home, STEAM_SCHEME, game_data);
                }
                catch (Error e) {
                        debug (e.message);
                }
 
                try {
-                       sources += new SteamUriSource (home + STEAM_FLATPAK_DIR, FLATPAK_STEAM_FILE_SCHEME);
+                       sources += new SteamUriSource (home + STEAM_FLATPAK_DIR, FLATPAK_STEAM_SCHEME, 
flatpak_game_data);
                }
                catch (Error e) {
                        debug (e.message);
@@ -53,11 +57,11 @@ private class Games.SteamPlugin : Object, Plugin {
        public UriGameFactory[] get_uri_game_factories () {
                var game_uri_adapter = new GenericGameUriAdapter (game_for_steam_uri);
                var factory = new GenericUriGameFactory (game_uri_adapter);
-               factory.add_scheme (STEAM_FILE_SCHEME);
+               factory.add_scheme (STEAM_SCHEME);
 
                var game_uri_adapter_flatpak = new GenericGameUriAdapter (game_for_flatpak_steam_uri);
                var factory_flatpak = new GenericUriGameFactory (game_uri_adapter_flatpak);
-               factory_flatpak.add_scheme (FLATPAK_STEAM_FILE_SCHEME);
+               factory_flatpak.add_scheme (FLATPAK_STEAM_SCHEME);
 
                return { factory, factory_flatpak };
        }
@@ -78,21 +82,28 @@ private class Games.SteamPlugin : Object, Plugin {
        }
 
        private static Game create_game (Uri uri, string app_id, string prefix) throws Error {
-               var file_uri = new Uri.from_uri_and_scheme (uri, "file");
-               var file = file_uri.to_file ();
-               var appmanifest_path = file.get_path ();
-               var registry = new SteamRegistry (appmanifest_path);
-               var game_id = registry.get_data ({"AppState", "appid"});
-               /* The gamegames_id sometimes is identified by appID
-                * see issue https://github.com/Kekun/gnome-games/issues/169 */
-               if (game_id == null)
-                       game_id = registry.get_data ({"AppState", "appID"});
-
-               if (game_id == null)
-                       throw new SteamError.NO_APPID (_("Couldn’t get Steam appid from manifest “%s”."), 
appmanifest_path);
+               var scheme = uri.get_scheme ();
+
+               var uri_string = uri.to_string ();
+               var pos = uri_string.last_index_of ("/");
+               var game_id = uri_string.substring (pos + 1);
+
+               string game_title;
+               switch (scheme) {
+               case STEAM_SCHEME:
+                       game_title = game_data.get_title (game_id);
+                       break;
+
+               case FLATPAK_STEAM_SCHEME:
+                       game_title = flatpak_game_data.get_title (game_id);
+                       break;
+
+               default:
+                       assert_not_reached ();
+               }
 
                var uid = new SteamUid (prefix, game_id);
-               var title = new SteamTitle (registry);
+               var title = new GenericTitle (game_title);
                var icon = new SteamIcon (app_id, game_id);
                var cover = new SteamCover (game_id);
 
@@ -106,14 +117,15 @@ private class Games.SteamPlugin : Object, Plugin {
        private static Runner? create_runner (Game game) throws Error {
                var uri = game.get_uri ();
                var scheme = uri.get_scheme ();
+               var steam_uri = new Uri.from_uri_and_scheme (uri, STEAM_SCHEME);
 
                string[] command;
                switch (scheme) {
-               case STEAM_FILE_SCHEME:
+               case STEAM_SCHEME:
                        command = { "steam" };
                        break;
 
-               case FLATPAK_STEAM_FILE_SCHEME:
+               case FLATPAK_STEAM_SCHEME:
                        command = { "flatpak", "run", STEAM_APPID };
                        break;
 
@@ -121,22 +133,7 @@ private class Games.SteamPlugin : Object, Plugin {
                        assert_not_reached ();
                }
 
-               /* FIXME: Deduplicate the following code with create_game() */
-               var file_uri = new Uri.from_uri_and_scheme (uri, "file");
-               var file = file_uri.to_file ();
-               var appmanifest_path = file.get_path ();
-               var registry = new SteamRegistry (appmanifest_path);
-               var game_id = registry.get_data ({"AppState", "appid"});
-               /* The gamegames_id sometimes is identified by appID
-                * see issue https://github.com/Kekun/gnome-games/issues/169 */
-               if (game_id == null)
-                       game_id = registry.get_data ({"AppState", "appID"});
-
-               if (game_id == null)
-                       throw new SteamError.NO_APPID (_("Couldn’t get Steam appid from manifest “%s”."), 
appmanifest_path);
-
-               command += @"steam://rungameid/$game_id";
-               print(@"WTF $game_id\n");
+               command += steam_uri.to_string ();
                return new CommandRunner (command);
        }
 }
diff --git a/plugins/steam/src/steam-uri-iterator.vala b/plugins/steam/src/steam-uri-iterator.vala
index 891a7569..7d8af978 100644
--- a/plugins/steam/src/steam-uri-iterator.vala
+++ b/plugins/steam/src/steam-uri-iterator.vala
@@ -1,67 +1,28 @@
 // This file is part of GNOME Games. License: GPL-3.0+.
 
 private class Games.SteamUriIterator : Object, UriIterator {
-       private string[] directories;
-       private int directory_index;
-       private FileEnumerator? enumerator;
        private string uri_scheme;
-       private Uri? uri;
+       private string[] appids;
 
-       internal SteamUriIterator (string[] directories, string uri_scheme) {
-               this.directories = directories;
+       private int index;
+
+       internal SteamUriIterator (string uri_scheme, SteamGameData game_data) {
                this.uri_scheme = uri_scheme;
-               directory_index = 0;
-               uri = null;
-               enumerator = null;
+               this.appids = game_data.get_appids ();
+               index = -1;
        }
 
        public new Uri? get () {
-               return uri;
-       }
-
-       public bool next () {
-               while (directory_index < directories.length) {
-                       if (try_next_for_directory (directories[directory_index]))
-                               return true;
-
-                       directory_index++;
-               }
-
-               return false;
-       }
-
-       private bool try_next_for_directory (string directory) {
-               try {
-                       if (next_for_directory (directory))
-                               return true;
-               }
-               catch (Error e) {
-                       debug (e.message);
-               }
-
-               uri = null;
-               enumerator = null;
+               if (index >= appids.length)
+                       return null;
 
-               return false;
+               var appid = appids[index];
+               return new Uri (@"$uri_scheme://rungameid/$appid");
        }
 
-       private bool next_for_directory (string directory) throws Error {
-               if (enumerator == null) {
-                       var file = File.new_for_path (directory);
-                       enumerator = file.enumerate_children (FileAttribute.STANDARD_NAME, 0);
-               }
-
-               FileInfo info = null;
-               do {
-                       info = enumerator.next_file ();
-               } while (info != null && !info.get_name ().has_suffix (".acf"));
-               if (info == null)
-                       return false;
-
-               var filename = Path.build_filename (directory, info.get_name ());
-               var file_uri = new Uri (Filename.to_uri (filename));
-               uri = new Uri.from_uri_and_scheme (file_uri, uri_scheme);
+       public bool next () {
+               index++;
 
-               return true;
+               return (index < appids.length);
        }
 }
diff --git a/plugins/steam/src/steam-uri-source.vala b/plugins/steam/src/steam-uri-source.vala
index a64cdabc..40efad90 100644
--- a/plugins/steam/src/steam-uri-source.vala
+++ b/plugins/steam/src/steam-uri-source.vala
@@ -5,65 +5,57 @@ private class Games.SteamUriSource : Object, UriSource {
        private const string STEAM_DIR = "/.steam";
        // From the home directory.
        private const string REGISTRY_PATH = "/.steam/registry.vdf";
-       // From the home directory.
-       private const string DEFAULT_INSTALL_DIR = "/.local/share/Steam";
-       // From an install directory.
-       private const string[] STEAMAPPS_DIRS = { "/SteamApps", "/steamapps" };
-       // From the default SteamApp directory.
-       private const string LIBRARY_DIRS_REG = "/libraryfolders.vdf";
 
-       private const string[] INSTALL_PATH_REGISTRY_PATH =
-               { "Registry", "HKLM", "Software", "Valve", "Steam", "InstallPath" };
+       private const string[] APPS_REGISTRY_PATH =
+               { "Registry", "HKCU", "Software", "Valve", "Steam", "Apps" };
 
-       private string[] directories;
        private string uri_scheme;
+       private SteamGameData game_data;
 
-       public SteamUriSource (string base_dir, string uri_scheme) throws Error {
-               directories = {};
-
+       public SteamUriSource (string base_dir, string uri_scheme, SteamGameData game_data) throws Error {
                this.uri_scheme = uri_scheme;
+               this.game_data = game_data;
 
                var registry_path = base_dir + REGISTRY_PATH;
                var registry = new SteamRegistry (registry_path);
-               var install_path = registry.get_data (INSTALL_PATH_REGISTRY_PATH);
 
                // If `.steam` dir is a symlink, it could be pointing to another Steam
                // installation, so skip it altogether to avoid duplicating games
                if (FileUtils.test (base_dir + STEAM_DIR, FileTest.IS_SYMLINK))
                        return;
 
-               add_library (base_dir + DEFAULT_INSTALL_DIR);
-
-               if (install_path == null)
-                       return;
-
-               add_library (install_path);
-
-               // `/LibraryFolders/$NUMBER` entries in the libraryfolders.vdf registry
-               // file are library directories.
-               foreach (var steamapps_dir in STEAMAPPS_DIRS) {
-                       var install_steamapps_dir = install_path + steamapps_dir;
-                       var file = File.new_for_path (install_steamapps_dir);
-                       if (!file.query_exists ())
+               var children = registry.get_children (APPS_REGISTRY_PATH);
+               foreach (var appid in children) {
+                       var path = APPS_REGISTRY_PATH;
+                       path += appid;
+
+                       string name = null;
+                       var installed = false;
+
+                       var app_children = registry.get_children (path);
+                       foreach (var child in app_children) {
+                               var lowercase = child.ascii_down ();
+                               var child_path = path;
+                               child_path += child;
+                               if (lowercase == "name") {
+                                       name = registry.get_data (child_path).strip ();
+                               }
+                               else if (lowercase == "installed") {
+                                       var installed_value = registry.get_data (child_path);
+                                       installed = (installed_value == "1");
+                               }
+                       }
+
+                       // Only entries that contain names are actual games.
+                       // Others are DLC or tools like Proton.
+                       if (name == null || !installed)
                                continue;
 
-                       var library_reg_path = install_steamapps_dir + LIBRARY_DIRS_REG;
-                       var library_reg = new SteamRegistry (library_reg_path);
-                       foreach (var child in library_reg.get_children ({ "LibraryFolders" }))
-                               if (/^\d+$/.match (child))
-                                       add_library (library_reg.get_data ({ "LibraryFolders", child }));
+                       game_data.add_game (appid, name);
                }
        }
 
        public UriIterator iterator () {
-               return new SteamUriIterator (directories, uri_scheme);
-       }
-
-       private void add_library (string library) {
-               foreach (var steamapps_dir in STEAMAPPS_DIRS) {
-                       var library_steamapps_dir = library + steamapps_dir;
-                       if (FileUtils.test (library_steamapps_dir, FileTest.EXISTS))
-                               directories += library_steamapps_dir;
-               }
+               return new SteamUriIterator (uri_scheme, game_data);
        }
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]