sonic2kk / steamtinkerlaunch

Linux wrapper tool for use with the Steam client for custom launch options and 3rd party programs
GNU General Public License v3.0
2.03k stars 69 forks source link

[Request for Info] - Help processing shortcuts.vdf #1117

Closed terrymacdonald closed 1 month ago

terrymacdonald commented 1 month ago

Hi Eamonn,

Sorry to bother you, but I'm the developer of DisplayMagician (https://github.com/terrymacdonald/DisplayMagician). I've been asked by a user to add the ability to start non-Steam games through Steam, so that users can take advantage of the steam features for non-steam games.

I've been struggling to figure out how to start the non-steam game shortcuts once I've processed the shortcuts.vdf file, so I was hoping that you might have some ideas on what I could do, as you're obviously a steam client expert based on your code.

My specific issue is that when I extract the shortcut information, I grab the appid #, then I'm am unsure what to do next. With normal steam games I am able to start the game using 'steam://gamelaunchid/', but this doesn't seem to work with non-steam games.

If I add 'Foxit PDF Reader to Steam as a non-steam game, Steam added it as appid 3501819989. This issue I'm getting is that steam://gamelaunchid/3501819989' doesn't work. Steam just shows a configuration error message.

I saved a non-steam game shortcut to the desktop to see what Steam wrote in the shortcut, and it wrote 'steam://gamelaunchid/15040202329267634176'. When I ran this shortcut it worked correctly and loaded Foxit PDF Reader as I expected.

I have a few questions I was hoping you could answer for me?

If you could point me at some other websites or code or anything to help me learn how to do this, I would be so grateful.

Thanks Terry

sonic2kk commented 1 month ago

Hey there!

I think I understand your problem. I'll try to give a bit of clarity on how to accomplish this. This took me a little while to figure out myself. Please let me know if I didn't explain anything correctly, I was not entirely sure how to structure the reply :sweat_smile:

This is a long reply, so the tl;dr is that you can store the following in an unsigned 64bit integer: ( 3501819989 << 32 ) | 0x02000000. You can replace 3501819989 with any unsigned 32bit integer shortcut AppID from shortcuts.vdf.


I want to put this closer to the top but I go over it more at the end of the reply, but a lot of what I know about the Steam Client and how AppIDs works comes from Steam-ROM-Manager. The folks over there deserve the credit. For this particular reply, it's their lengthenAppId function that I figured this out from. The other functions in this file are also what I based my own gists and functions on, but I made them into more generalised snippets specific to Bash.


In short, the AppID you're seeing in the shortcut file that Steam creates (15040202329267634176) is a "long AppID". This is, through some computation I absolutely do not understand, makes a given shortcut AppID (in this case, 3501819989) longer. I suspect that Steam does this to convert it from a 32bit unsigned integer to a 64bit unsigned integer, as some languages (i.e Rust) will complain that the integer given is too big to be held in a u32 type. As to why Steam wants to do this, I have no idea :-)

So in order to run a Non-Steam Game with steam://rungameid/your_id_here, you'll need to convert from the shortcuts.vdf AppID to this long one. In other words you need to know how to go from 3501819989 to 15040202329267634176 in this case.

To go from the AppID in the shortcuts.vdf file to the one you need for the command, you can do this: (shortAppId << 32) | 0x02000000. I am not sure how C# handles bit-shifts like this but most languages allow them just like that, so the following might work:

// Maybe C# has a more appropriate data type for unsigned, or maybe you can denote it as unsigned in some way?
int lengthenAppId(int shortcutAppId)
{
    return ( shortcutAppId << 32 ) | 0x02000000;
}

// Example call - Same applies here about the type, perhaps it has to be denoted as unsigned and/or 64bit?
int rungameidShortcut = lengthenAppId(3501819989)  // Should return 15040202329267634176

For reference, this is trickier in Bash, because Bash is a real pain for any arithmetic. But basically in Bash you need to do the same thing, but use printf to convert it to unsigned:

function lengthen_app_id {
    shortcut_app_id="$1"

    printf "%u" "$(( ( ${shortcut_app_id} << 32 ) | 0x02000000 ))"
}

lengthen_app_id "3501819989"  # Should return 15040202329267634176

But in C# I would hope it's a bit more sane, and the first solution works. However if not, and you end up 3501819989 << 32 return a signed integer like -3406541744475471872, then you can do something like the Bash solution to convert to unsigned (there may also be a bitshift way to convert from signed to unsigned, but I came to using printf quicker).


As an additional example on why the type matters, here is an example in Rust (because I've been messing with Rust recently):

fn main() {
    let i64_aid: i64 = (3501819989 << 32) | 0x02000000;
    let u64_aid: u64 = (3501819989 << 32) | 0x02000000;

    println!("{i64_aid}");  // Wrong,   gives -3406541744441917440
    println!("{u64_aid}");  // Correct, gives 15040202329267634176
}

So you may have your answer already if my explanation wasn't entirely butchered, but for clarity I'll go through them here.

Is there a way that I can use the 'appid' in shortcuts.vdf to run the non-steam game via Steam? Is there something I've missed?

Thankfully yup, you have to be able to grab this AppID as an integer - which it seems you already have, it is 3501819989 in this case. Then you have to lengthen this AppID to run the game from Steam.

how can I get from the appid to the shortcutid? i.e. from 3501819989 to 15040202329267634176 so that I can run the non-steam game.

To go from 3501819989 to 15040202329267634176, you can do (3501819989 << 32) | 0x02000000 in most languages, provided you use an unsigned 64bit integer data type.


I decided to put this bit in its own section, because it's important and I want to be extra explicit in giving full credit to Steam-ROM-Manager.

I've looked at some of your gists, and your steamtinkerlaunch, but can you please point me to some code that I can review? I use C# but can read anything to reverse engineer the code I need. If you could point me at some other websites or code or anything to help me learn how to do this, I would be so grateful.

Steam-ROM-Manager have a function called lengthenAppId. This takes the unsigned 32bit integer AppID from shortcuts.vdf. But knowing what AppID it took wasn't immediately clear to me. I had a look through the usages of these functions around the codebase which is how I figured out that's the AppID it takes.

There is no code in SteamTinkerLaunch that actually lengthens the AppID, so there's nowhere in our codebase for this specific action. However the other code in the SteamTinkerLaunch script for generating AppIDs is here, this code is how we generate the AppIDs to store in the VDF file (as we need to perform some calculations on them before storing them in shortcuts.vdf) as well as to store the AppID used to set grid artwork: https://github.com/sonic2kk/steamtinkerlaunch/blob/d02ff1e0cc1b2e770f81acb55e24a39ba9ac5020/steamtinkerlaunch#L24475-L24480


I hope this helped give you some background, and apologies for the delay; I was at work, and after work I spent a couple of hours figuring this out too.

Please reply if I didn't explain something properly or if you need any more help.

Good luck!

terrymacdonald commented 1 month ago

Eamonn,

You are a legend. This is exactly the information I needed. I cannot thank you enough, and special thanks to the team at Steam-ROM-Manager too!

Thanks

Terry

sonic2kk commented 1 month ago

You're very welcome, good luck with your own project, seems cool :smile:

I don't have a Windows environment or any C# development environment set up, but if you run into any issues with getting the wrong values here, please leave a reply anytime and I'll try to help out!

Smoukus commented 1 month ago

I'm the user that asked for that feature for DisplayMagician. Thousand thanks to Terry for not giving up on this, and to you sonic2kk and folks at Steam-ROM-Manager for being the absolute beasts (in a good way!).