Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scope question with feature requests #49

Closed
23 tasks
Nutzzz opened this issue Feb 11, 2023 · 2 comments
Closed
23 tasks

Scope question with feature requests #49

Nutzzz opened this issue Feb 11, 2023 · 2 comments

Comments

@Nutzzz
Copy link

Nutzzz commented Feb 11, 2023

@erri120 : Thanks for your work on the decryption and documentation of the EA Desktop json file! It proved very helpful for my purposes.

The overarching question I have with this Issue is: To what extent are you interested in expanding the scope of this library?

For some time I've been making major contributions to GLC, a lightweight C# Windows-only console multi-launcher app (supporting 17 launchers; and I just added two more). I even hacked in image support for conhost-based shells, e.g., cmd or PowerShell. @Solaire has big plans for 2.0, including moving to separate libraries for each platform. It'd be nice to use GameFinder as a dependency, but as a launcher (rather than to assist modding tools as originally designed here) we need additional data: an .exe or a protocol call to launch the game, an icon (where it's not an .exe launch), and preferably an uninstall entry. Some space for additional metadata would be nice. And of course it'd be great to have support for all the launchers. ;-)

So, to the requests... I'll divide them into separate issues if you're interested in taking up and/or accepting pull requests for any of these, but I don't know to what extent you want this project's scope to grow before I do that.

  1. Wiki:
  • Remove or revise the "Use the registry" alternative from the EA Desktop wiki. It seems backwards: are you suggesting to search all the user's disks for "installerdata.xml" files just to find the registry entries?
  • Add GLC to the Other Implementation sections.
  1. GOG:
  • Instead of the registry, use the SQLite database (%ProgramData%\GOG.com\Galaxy\storage\galaxy-2.0.db).

[This is more robust, e.g., would allow installed games to be found even after a Windows reinstall, and it allows you to discover owned but not-installed games. Also, I don't know why, but the registry is missing at least one of my recently installed GOG games. (In addition, there's additional metadata that can be collected; see Additional fields suggestion below). I'm happy to help implement this based on my work on GLC.]

  1. Additional launchers:
    If you're interested, I'd also be happy to help add handlers to support 16 17 more launchers. Though a few are simple registry scrapes, as above that's not the preferred solution. I've been playing around with the code and so far I've got Amazon and Arc [EDIT: all of the below] working in my fork:
  • Amazon
  • Arc
  • Battle.net
  • Big Fish
  • Game Jolt
  • Humble
  • Indiegala
  • itch
  • Legacy
  • Oculus
  • Paradox
  • Plarium
  • Riot
  • Robot Cache
  • Rockstar
  • Ubisoft
  • Wargaming
  1. Additional fields:
  • Offer a consistent method between handlers to get all available data (i.e., beyond id, name, and install path collected now).

[Admittedly, the available fields will vary a bit between launchers, where the GOG and Amazon local databases in particular offer lots of good metadata. You do currently provide a few methods to help assist in the collection of more data like EADesktopGame.GetInstallerDataFile() and SteamGame.GetManifestPath() (not that the latter file is a particularly rich source of data; in Steam's case a GetRegistryKey() method to easily get the corresponding registry entry in HKLM32\SOFTWARE\Valve\Steam\Steam app ) would have been nice). However, it seems rather odd to force the library's client to reinvent the wheel of often re-parsing a file/regkey to get data that could easily have been collected the first time through in this library.]

  • Access APIs or perform web scrapes to collect additional data.

[I've done some of this in GLC with the low-hanging fruit (e.g., where no authentication is required, or in EA's case, where a C# wrapper, PureOrigin.API, exists).]

  • Ensure compatibility for existing clients of this library.

[As an experiment, I've implemented one possible solution in my fork: I added a generic GameEx record type, and FindAllGamesEx() methods are in each handler to fill it. The existing FindAllGames() methods now call FindAllGamesEx() and convert IDs to longs or ints as necessary. Of course, you may have a better idea.]

@erri120
Copy link
Owner

erri120 commented Feb 11, 2023

Thanks for taking the time to write this up. Let's break this down and start with the big design decisions:

GameFinder history and design

I originally made this library for Wabbajack, but since then, it has been used in various other projects. However, almost all the dependents are modding related. Most of the time, the only thing they are interested in is the installation location. Once they have that, they start doing their own thing. This is why the *Game records only have 3 properties: an identifier, a name, and the installation path.

I did recently add some utility functions like SteamGame.GetManifestPath and EADesktopGame.GetInstallerDataFile which can be helpful for library consumers that need a bit more data.

The development of this library has been heavily influenced by the library consumers. So far, they only require the installation path, which is why GameFinder only returns the bare minimum. I know that a launcher requires more information. I've previously been involved with Playnite and know how much additional information it grabs from the various supported libraries.

More data

The core issue is that GameFinder doesn't know what data the consumer wants. Currently, GameFinder returns the absolute minimum. If you go back in time enough, you will actually find that previous versions of the library extracted almost everything. For Steam, it extracted all values from the .acf file, for EGS it deserialized the entire .json with all values into a class and for GOG it parsed all registry values.

I stopped doing that because it was hell to maintain. The GOG implementation would regularly break, because some registry keys wouldn't exist for some users, and Steam changed their format like three times within a month. Grabbing only the bare essentials makes the library easier to test and maintain.

That being said, what can be done? I would look into adding more utility functions. Provide the bare essentials and have some method that you can use to request more data.

However, it seems rather odd to force the library's client to reinvent the wheel of often re-parsing a file/regkey to get data that could easily have been collected the first time through in this library.

This is true for SteamGame.GetManifestPath which returns the path to the manifest that got parsed beforehand. However, how else should this API be designed? For Steam, it might be possible to deserialize the .acf file and put the deserialized object into the SteamGame object, however this might not work with everything. Take the EA Desktop implementation as an example. The decrypted file is just a JSON file, however I'm deserializing it into an object. If a consumer were to request more data, would the EADesktopGame have the entire JSON contents as a private field or something, that can be used to extract more information?

Also, something I forgot to write down in the docs: all the Store handler functions are pure functions. None of the store handlers have an internal mutable state. They are only classes because it makes configuring stuff easier using constructors. I'm very reluctant on adding state that can be mutated and accessed, if none of the library consumers are using it.

Access APIs or perform web scrapes to collect additional data.

I want to keep the library from accessing the web if possible. Furthermore, I'm considering adding the Origin API as a static helper method, but this won't be the default.

Ensure compatibility for existing clients of this library.

This isn't the Windows API or C++, we don't need a FindGames and FindGamesEx method to not break compatibility or the ABI. Following SemVar and providing good Changelogs with information on how to upgrade is sufficient.

Adding more store handlers

As pointed out before, GameFinder mostly followed the requests of library consumers, which were all modding tools. None of the additional stores, you have listed, have games supported by these tools. I recently marked the Xbox Game Pass implementation as deprecated, because no one was using it and maintaining it had become a chore.

This is another issue: I don't even use all of these stores. If something breaks, or someone requests a new store implementation, I have to install it and start researching. I hate the Epic Games Store and uninstalled it the moment I was done with it. I'm glad the implementation is still working, because I won't be installed the EGS any time soon.

If you want to add more store handlers to GameFinder, I suggest creating individual issues, where we can discuss the store in detail. Just note that I won't allow 20 new implementations any time soon. The current code base is rather small and most implementations are straight forward. Adding more store handlers and more complexity requires more tests and makes the library harder to maintain.

Individual store handler stuff

EA Desktop

Remove or revise the "Use the registry" alternative from the EA Desktop wiki. It seems backwards: are you suggesting to search all the user's disks for "installerdata.xml" files just to find the registry entries?

From the wiki:

Maybe you don't want to find every single game, or you only need a known amount of games. In this case, you can just use the registry key from the installerdata.xml file. Every EA game has a folder called __Installer which contains an installerdata.xml file which can give you the correct registry key [...]

This alternative is for tools that only need to find one or a handful of known games. As an example: let's assume you make a tool for Titanfall 2, and you need to find the installation path programmatically. My method of breaking the encryption might be a bit overkill. Instead, you can, as the tool programmer, open the installerdata.xml file on your machine and extract the registry key. This key can be hardcoded into the tool for a simple registry lookup to find the game installation location.

I'm not suggesting that the tool searches the entire disk for installerdata.xml, but rather the programmer just extracts the required information from their file. This registry key doesn't change.

GOG

Instead of the registry, use the SQLite database (%ProgramData%\GOG.com\Galaxy\storage\galaxy-2.0.db).

I didn't know this file existed, when I created my implementation. However, this an embedded application database, meaning the schema can change with every update. I'm also not so keen on bundling an SQLite client with the store handler. I might consider this, if this were the only method of accessing the required data, but at the moment, I don't see the benefit over using the registry. You can create a separate issue for this, but this would be very low priority.

Other stuff

Add GLC to the Other Implementation sections.

Will do. Also, a quick reminder: GameFinder uses the MIT license, which is very permissive but still requires preservation of copyright and license notices, meaning you have to include the copyright notice and the permission notice in all copies or substantial portions of the Software.

@Nutzzz
Copy link
Author

Nutzzz commented Feb 11, 2023

Thanks very much for the in-depth response. I thought you might feel that way to one extent or another, and I totally understand that point of view. I'll close this issue. Take care!

@Nutzzz Nutzzz closed this as not planned Won't fix, can't repro, duplicate, stale Feb 11, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants