williamvenner / steamlocate-rs Goto Github PK
View Code? Open in Web Editor NEW🎮 Rust Crate for locating Steam game installation directories (and Steam itself!)
License: MIT License
🎮 Rust Crate for locating Steam game installation directories (and Steam itself!)
License: MIT License
While replacing my existing game path discovery logic with steamlocate v2.0.0-beta.1, I ran into this error while testing on my Fedora machine with a Flatpak Steam install:
Failed parsing VDF file. File kind: App, Error: missing field `LastUpdated` at /home/schuyler/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/appmanifest_2519830.acf
Examining the file, some of the VDF fields (including lastupdated
) are all lowercase rather than CamelCase. Here are the full contents of that file:
"AppState"
{
"appid" "2519830"
"Universe" "1"
"name" "Resonite"
"StateFlags" "4"
"installdir" "Resonite"
"lastupdated" "1702688752"
"SizeOnDisk" "1102323116"
"StagingSize" "0"
"buildid" "12967476"
"LastOwner" "76561198022773299"
"UpdateResult" "0"
"BytesToDownload" "2332576"
"BytesDownloaded" "2332576"
"BytesToStage" "54625540"
"BytesStaged" "54625540"
"TargetBuildID" "12967476"
"AutoUpdateBehavior" "0"
"AllowOtherDownloadsWhileRunning" "0"
"ScheduledAutoUpdate" "0"
"InstalledDepots"
{
"2519832"
{
"manifest" "1396658363472368690"
"size" "514493538"
}
"2519831"
{
"manifest" "5082223756179978205"
"size" "587829578"
}
}
"SharedDepots"
{
"228984" "228980"
"228985" "228980"
"228988" "228980"
"228989" "228980"
}
"UserConfig"
{
"language" "english"
}
"MountedConfig"
{
"language" "english"
}
}
Hi! Do you think it would make sense to support listing games added to Steam via the "add a non-Steam game" option? I'm using steamlocate in Ludusavi, and I got a feature request to look up Proton save data for such games. It requires knowing the configured target path and app name, then calculating the stand-in app ID used for the Proton compatdata subfolder (sample code). The info is stored in <steam>/userdata/<user>/config/shortcuts.vdf
.
I was imagining something like this:
let mut steam = steamlocate::SteamDir::locate()?;
let id = steam.shortcut("Game Name")?.id();
let shortcuts = steam.shortcuts()?;
I opened a ticket for steamy-vdf, but since it doesn't seem to be maintained, I thought it was worth raising here as well for awareness.
Using Rust 1.68.0, this warning is displayed while building:
warning: the following packages contain code that will be rejected by a future version of Rust: nom v1.2.4
Hi @WilliamVenner!
It seems like you've been pretty busy lately. I've got quite a bit of free time on my hands lately, so I'd like to get v2 for this crate prepared with at least one alpha and beta along the way. If you wouldn't mind adding me as a crate owner I could handle working out all that and then ping you for the final review before publishing v2
So I'm not sure what's going on here but for some reason my specific libraryfolders.vdf, I've been trying to figure it out and it's something to do with the very last value: "E:\Steam" being recognized as a } and the parser therefore erroring out. I've been unable to figure it out for hours so any help would be much appreciated.
I've also raised this issue on the steamy-vdf crate github but it seems to be dead unfortunately :/
During the development of my app I wanted to add steam flatpak support but steamlocate suddenly stopped working after that test .
It appears that Steamlocate only check if the steam folder is present and not if the steam flatpak is installed which is a big problems in the case of migration from steam flatpak to native steam since it appears that the steam flatpak folder is not deleted upon uninstall.
app
unwraps to get the result:
steam_apps.apps.get(app_id).unwrap().as_ref()
It should probably be:
steam_apps.apps.get(app_id).and_then(|v| v.as_ref())
What the title says. What justifies the use of both steamy-vdf
and keyvalues-parser
/keyvalues-serde
?
It looks like some Steam update changed the layout used in libraryfolders.vdf
. Here is an anonymized version from my windows install:
"libraryfolders"
{
"contentstatsid" "-9876543210987654321"
"0"
{
"path" "C:\\Program Files (x86)\\Steam"
"label" ""
"contentid" "-9876543210987654321"
"totalsize" "0"
"update_clean_bytes_tally" "98765432109"
"time_last_update_corruption" "0"
"apps"
{
"111" "12345678901"
"210987" "9998887776"
}
}
"1"
{
"path" "D:\\SteamLibrary"
"label" ""
"contentid" "7654321098765432109"
"totalsize" "123456789012"
"update_clean_bytes_tally" "11223344556"
"time_last_update_corruption" "0"
"apps"
{
"10" "101112131"
}
}
}
This matches the same layout present on my linux install
Notably there is a parse error before any information can be extracted because steamy-vdf
has an issue parsing empty strings and the new layout contains the pair "label" ""
. Changing the labels' values manually to contain some text gets past the parse error and instead fails on the different layout
I tried using this crate to try and find a specific game (Guilty Gear XX Accent Core +R) with the ID 348550 and could not find it in the list returned by apps()
or app(&384550)
, I looked in the steam library folder and the appmanifest_384550.acf
is there and appears valid. I skimmed the code in this repository and couldn't seem to find any obvious bugs. Is there a possible solution?
Pinging @WilliamVenner since I would love to hear your thoughts on this since it's your library after all 😄, and @super-continent since you mentioned potentially being interested in helping with some of the changes
steamy-vdf
parsing issues strike again! This time with app manifests. The obvious way forward is to switch the app manifest parsing to also use keyvalues-parser
. The issue is that the SteamApp
type directly exposes a steamy_vdf::Table
, so this will have to be a breaking change.SteamDir::apps
from a caching API to an iterator based APIThe idea here is to switch SteamDir::apps
from returning a reference to an internal hashmap to returning an iterator that tries parsing each manifest and returning a result for each one. This API is inspired by similar situations like std::fs::read_dir
.Something like
impl SteamDir {
pub fn apps(&self) -> AppIter {
...
}
}
impl Iterator for AppIter {
// Each entry is the app id followed by a result for reading and parsing the manifest
type Item = (u32, steamlocate::Result<SteamApp>);
fn next(&mut self) -> Option<Self::Item> {
...
}
}
The reasoning for this switch is that I don't think we gain much by caching, and this switch makes things more flexible along with being more straightforward
The current API just converts all errors that are encountered into None
which makes it hard to track down the source of issues. The fix here is to enumerate possible error types in an enum and expose it for fallible operations
Things like LibraryFolders
' paths
shouldn't be manipulated by user's of the library, so it should be private with a public getter. The same goes for SteamDir
's path
SteamApp
It would be good to look over a large set of app manifest files to see how reasonable it would be to expose all the information within the struct instead of having to expose a type from the parser publicly. The reasoning is twofold
keyvalues-parser
is not stable and it's very possible that I will be making breaking changes in the future (I'm pending a breaking release atm). Breaking changes for exposed types would be a breaking change for this library to upgrade to too which isn't idealCurrently the test suite has a lot of requirements for it to actually be run. It would be good to store some sample libraryfolders and appmanifests, and do some path manipulation to run tests against those instead of requiring steam to be installed with some specific games
cargo clippy
a goThere's some lints that should be easy enough to cleanup :)
If this all sounds good then the task breakdown would be something like
steamy-vdf
to keyvalues-parser
(or keyvalues-serde
)SteamDir::apps
to return an iterator for SteamApp
sLibraryFolders
and SteamDir
SteamApp
instead of exposing the parsed objcargo clippy
lintsApp
to be just be the plain data it holds. Move the installation folder resolution to a method on the librarylocate
is the only featureReally excited to see #13 land, thanks for the work @CosmicHorrorDev.
I've been working on porting some code I had to work with it, but I am running into an issue where I simply get an empty list of shortcuts on my dev machine.
In my case, on macOS, I have a shortcuts.vdf
which looks like this:
00000000 00 73 68 6f 72 74 63 75 74 73 00 00 30 00 02 61 |.shortcuts..0..a|
00000010 70 70 69 64 00 40 e5 b3 ae 01 61 70 70 6e 61 6d |ppid.@峮.appnam|
00000020 65 00 53 65 63 6f 6e 64 20 4c 69 66 65 00 01 45 |e.Second Life..E|
00000030 78 65 00 22 2f 41 70 70 6c 69 63 61 74 69 6f 6e |xe."/Application|
00000040 73 2f 53 65 63 6f 6e 64 20 4c 69 66 65 20 56 69 |s/Second Life Vi|
00000050 65 77 65 72 2e 61 70 70 22 00 01 53 74 61 72 74 |ewer.app"..Start|
00000060 44 69 72 00 22 2f 41 70 70 6c 69 63 61 74 69 6f |Dir."/Applicatio|
00000070 6e 73 2f 22 00 01 69 63 6f 6e 00 00 01 53 68 6f |ns/"..icon...Sho|
00000080 72 74 63 75 74 50 61 74 68 00 00 01 4c 61 75 6e |rtcutPath...Laun|
00000090 63 68 4f 70 74 69 6f 6e 73 00 00 02 49 73 48 69 |chOptions...IsHi|
000000a0 64 64 65 6e 00 00 00 00 00 02 41 6c 6c 6f 77 44 |dden......AllowD|
000000b0 65 73 6b 74 6f 70 43 6f 6e 66 69 67 00 01 00 00 |esktopConfig....|
000000c0 00 02 41 6c 6c 6f 77 4f 76 65 72 6c 61 79 00 01 |..AllowOverlay..|
000000d0 00 00 00 02 4f 70 65 6e 56 52 00 00 00 00 00 02 |....OpenVR......|
000000e0 44 65 76 6b 69 74 00 00 00 00 00 01 44 65 76 6b |Devkit......Devk|
000000f0 69 74 47 61 6d 65 49 44 00 00 02 44 65 76 6b 69 |itGameID...Devki|
00000100 74 4f 76 65 72 72 69 64 65 41 70 70 49 44 00 00 |tOverrideAppID..|
00000110 00 00 00 02 4c 61 73 74 50 6c 61 79 54 69 6d 65 |....LastPlayTime|
00000120 00 cc d8 5e 63 01 46 6c 61 74 70 61 6b 41 70 70 |.��^c.FlatpakApp|
00000130 49 44 00 00 00 74 61 67 73 00 08 08 08 08 |ID...tags.....|
Notably, the key appname
is all-lowercase for this shortcut.
The library I used for this purpose previously, steam_shortcuts_util
, treats all keys as case-insensitive, and doesn't exhibit this problem. I believe this would be the more-correct approach.
The crate would run under the was32-wasi
target, since it can get filesystem access through the wasm runtime running the code.
Lines 118 to 119 in 4982655
i have a test game that when i create a desktop shortcut use
steam steam://rungameid/14819369052371156992
which indicate that the appid is 14819369052371156992
but using Shortcut struct i get 4206273994
but this one will not let me run the game. did i misunderstood how steam handle non steam game appid ?
My application uses steamlocate-rs
to find the install directory of Titanfall2 (1237970
).
A user of my application on Fedora 38 noticed however that it was unable to locate their install of Titanfall2.
On their setup they installed Steam via the native Fedora package.
As such their library folders are located in
~/.local/share/Steam/config/libraryfolders.vdf
~/.local/share/Steam/steamapps/libraryfolders.vdf
which (partially anonymised) looks like:
"libraryfolders"
{
"0"
{
"path" "/home/XXXXXXXXX/.local/share/Steam"
"label" ""
"contentid" "XXXXXXXXXXXXXXXXXXXX"
"totalsize" "0"
"update_clean_bytes_tally" "6504277467"
"time_last_update_corruption" "0"
"apps"
{
"XXXXXXX" "XXXXXXXXXX"
}
}
"1"
{
"path" "/mnt/0678788f-1ffe-4a12-8608-8ef3b8467106/SteamLibrary"
"label" ""
"contentid" "4113662200414089328"
"totalsize" "737229742080"
"update_clean_bytes_tally" "10119163182"
"time_last_update_corruption" "0"
"apps"
{
"XXXXXXXXX" "XXXXXXXXX"
"1237970" "68365272890"
"XXXXXXXXX" "XXXXXXXXX"
"XXXXXXXXX" "XXXXXXXXX"
}
}
}
Unfortunately I have not yet been able to figure out where the game detection fails (reading libraryfolders.vdf
, accessing secondary drive, ...)
For detection I'm using the newest release of steamlocate, i.e. 1.2.1
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.