Steam Library Shortcuts: Difference between revisions
Jump to navigation
Jump to search
(→Reading the shortcuts.vdf: Updating the code to the current structure on the shortucts.vdf; Not sure if it meant like this) |
|||
Line 23: | Line 23: | ||
{{For|the format structure|[[Add_Non-Steam_Game#File_format|Adding Non-Steam Games]]}} | {{For|the format structure|[[Add_Non-Steam_Game#File_format|Adding Non-Steam Games]]}} | ||
<syntaxhighlight lang=csharp> | <syntaxhighlight lang=csharp> | ||
// 1. Read all bytes from the file at 'filepath'. | |||
// 2. Convert the bytes to a string using the appropriate encoding. | |||
// 3. Assign the string to 'shortcutsString'. | |||
// 4. Return the list with the data | |||
// | |||
// Unicodes: | |||
// \u0000 - NUL | |||
// \u0001 - SOH | |||
// \u0002 - STX | |||
public class Shortcut | |||
{ | |||
// adding '?' to declare as a nullable property, for example "Icon" would be null if the icon wasn't set | |||
// public string? AppId { get; set; } | |||
public string? AppName { get; set; } | |||
public string? Exe { get; set; } | |||
public string? StartDir { get; set; } | |||
public string? Icon { get; set; } | |||
public string? ShortcutPath { get; set; } | |||
public string? LaunchOptions { get; set; } | |||
public bool IsHidden { get; set; } | |||
public bool AllowDesktopConfig { get; set; } | |||
public bool AllowOverlay { get; set; } | |||
public bool OpenVR { get; set; } | |||
public bool Devkit { get; set; } | |||
public string? DevkitGameID { get; set; } | |||
public string? DevkitOverrideAppID { get; set; } | |||
// public string? LastPlayTime { get; set; } | |||
public string? FlatpakAppID { get; set; } | |||
public List<string> Tags { get; set; } = new List<string>(); | |||
} | |||
public static List<Shortcut> ReadAndReturn(string filepath) | |||
{ | |||
string shortcutsString = File.ReadAllText(filepath, Encoding.GetEncoding("ISO-8859-1")); // Read the file with the correct encoding | |||
List<Shortcut> result = new List<Shortcut>(); | |||
int start = shortcutsString.IndexOf("\u0000shortcuts\u0000") + "\u0000shortcuts\u0000".Length; | |||
int end = shortcutsString.LastIndexOf("\u0008\u0008"); | |||
shortcutsString = shortcutsString.Substring(start, end - start); | |||
Shortcut shortcut = null; | |||
string word = ""; | |||
string key = ""; | |||
bool readingTags = false; | |||
int tagId = -1; | |||
foreach(char c in shortcutsString.ToCharArray()) | |||
{ | |||
if (c == '\u0000') { | |||
if (word.EndsWith("\u0001AppName")) { | |||
if (shortcut != null) | |||
result.Add(shortcut); | |||
// New shortcut | |||
shortcut = new Shortcut(); | |||
key = "\u0001AppName"; | |||
} | |||
else if ( | |||
//word == "\u0002appid" || // NOTE: there is no NUL terminated at the end of it, so it is not read correctly | |||
word == "\u0001Exe" || | |||
word == "\u0001StartDir" || | |||
word == "\u0001icon" || | |||
word == "\u0001ShortcutPath" || | |||
word == "\u0001LaunchOptions" || | |||
word == "\u0002IsHidden" || | |||
word == "\u0002AllowDesktopConfig" || | |||
word == "\u0002AllowOverlay" || | |||
word == "\u0002OpenVR" || | |||
word == "\u0002Devkit" || | |||
word == "\u0002DevkitGameID" || | |||
word == "\u0002DevkitOverrideAppID" || | |||
//word == "\u0002LastPlayTime" || // NOTE: there is no NUL terminated at the end of it, so it is not read correctly | |||
// Which means that this bottom one is getting picked up by the LastPlayTime | |||
word == "\u0001FlatpakAppID" | |||
) { | |||
key = word; | |||
} | |||
else if (word == "tags") { | |||
readingTags = true; | |||
} | |||
else if (key != "") { | |||
switch (key) { | |||
//case "\u0002appid": shortcut.AppId = word; break; | |||
case "\u0001AppName": shortcut.AppName = word; break; | |||
case "\u0001Exe": shortcut.Exe = word.Trim('"'); break; | |||
case "\u0001StartDir": shortcut.StartDir = word; break; | |||
case "\u0001icon": shortcut.Icon = word; break; | |||
case "\u0001ShortcutPath": shortcut.ShortcutPath = word; break; | |||
case "\u0001LaunchOptions": shortcut.LaunchOptions = word; break; | |||
case "\u0002IsHidden": shortcut.IsHidden = (word == "\u0001"); break; | |||
case "\u0002AllowDesktopConfig": shortcut.AllowDesktopConfig = (word == "\u0001"); break; | |||
case "\u0002AllowOverlay": shortcut.AllowOverlay = (word == "\u0001"); break; | |||
case "\u0002OpenVR": shortcut.OpenVR = (word == "\u0001"); break; | |||
case "\u0002Devkit": shortcut.Devkit = (word == "\u0001"); break; | |||
case "\u0002DevkitGameID": shortcut.DevkitGameID = word; break; | |||
case "\u0002DevkitOverrideAppID": shortcut.DevkitOverrideAppID = word; break; | |||
//case "\u0002LastPlayTime": shortcut.LastPlayTime = word; break; | |||
case "\u0001FlatpakAppID": shortcut.FlatpakAppID = word; break; | |||
default: | |||
break; | |||
} | |||
key = ""; | |||
} | |||
else if (readingTags) { | |||
if (word.StartsWith("\u0001")) { | |||
tagId = int.Parse(word.Substring("\u0001".Length)); | |||
} | |||
else if (tagId >= 0) { | |||
shortcut.Tags.Add(word); | |||
tagId = -1; | |||
} | |||
else { | |||
readingTags = false; | |||
} | |||
} | |||
word = ""; | |||
} | |||
else { | |||
word += c; | |||
} | |||
} | |||
if (shortcut != null) | |||
result.Add(shortcut); | |||
return result; | |||
} | |||
// to read and return data, use this somewhere: | |||
List<Shortcut> shortcuts = (YourClassName.)ReadAndReturn(filePath); | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Revision as of 09:21, 26 June 2025

This article has multiple issues. Please help improve it or discuss these issues on the talk page. (Learn how and when to remove these template messages)

This article has no
links to other VDC articles. Please help improve this article by adding links
that are relevant to the context within the existing text.
January 2024


January 2024

This article is an orphan, meaning that few or no articles link to it.
You can help by
adding links to this article from other relevant articles.
January 2024
You can help by

January 2024
Source mods under /sourcemods/, when configured correctly, show up on Steam Library after a restart. Source Mods placed in |all_source_engine_paths|, GoldSrc mods or even Source 2 mods don't show up by default.
Steam shortcuts file
Any game/mod could by added to the Steam Library by creating a shortcut. The shortcuts file for a given user are located under the directory steam_install_dir\\userdata\\, and the path ends with config. The shortcuts are stored inside this directory, in a file named shortcuts.vdf.
Shortcut format
- string AppName
- string Exe
- string StartDir
- string Icon
- string ShortcutPath
- string LaunchOptions
- bool Hidden
- array<string> Tags
Reading the shortcuts.vdf
For the format structure, see Adding Non-Steam Games.
// 1. Read all bytes from the file at 'filepath'.
// 2. Convert the bytes to a string using the appropriate encoding.
// 3. Assign the string to 'shortcutsString'.
// 4. Return the list with the data
//
// Unicodes:
// \u0000 - NUL
// \u0001 - SOH
// \u0002 - STX
public class Shortcut
{
// adding '?' to declare as a nullable property, for example "Icon" would be null if the icon wasn't set
// public string? AppId { get; set; }
public string? AppName { get; set; }
public string? Exe { get; set; }
public string? StartDir { get; set; }
public string? Icon { get; set; }
public string? ShortcutPath { get; set; }
public string? LaunchOptions { get; set; }
public bool IsHidden { get; set; }
public bool AllowDesktopConfig { get; set; }
public bool AllowOverlay { get; set; }
public bool OpenVR { get; set; }
public bool Devkit { get; set; }
public string? DevkitGameID { get; set; }
public string? DevkitOverrideAppID { get; set; }
// public string? LastPlayTime { get; set; }
public string? FlatpakAppID { get; set; }
public List<string> Tags { get; set; } = new List<string>();
}
public static List<Shortcut> ReadAndReturn(string filepath)
{
string shortcutsString = File.ReadAllText(filepath, Encoding.GetEncoding("ISO-8859-1")); // Read the file with the correct encoding
List<Shortcut> result = new List<Shortcut>();
int start = shortcutsString.IndexOf("\u0000shortcuts\u0000") + "\u0000shortcuts\u0000".Length;
int end = shortcutsString.LastIndexOf("\u0008\u0008");
shortcutsString = shortcutsString.Substring(start, end - start);
Shortcut shortcut = null;
string word = "";
string key = "";
bool readingTags = false;
int tagId = -1;
foreach(char c in shortcutsString.ToCharArray())
{
if (c == '\u0000') {
if (word.EndsWith("\u0001AppName")) {
if (shortcut != null)
result.Add(shortcut);
// New shortcut
shortcut = new Shortcut();
key = "\u0001AppName";
}
else if (
//word == "\u0002appid" || // NOTE: there is no NUL terminated at the end of it, so it is not read correctly
word == "\u0001Exe" ||
word == "\u0001StartDir" ||
word == "\u0001icon" ||
word == "\u0001ShortcutPath" ||
word == "\u0001LaunchOptions" ||
word == "\u0002IsHidden" ||
word == "\u0002AllowDesktopConfig" ||
word == "\u0002AllowOverlay" ||
word == "\u0002OpenVR" ||
word == "\u0002Devkit" ||
word == "\u0002DevkitGameID" ||
word == "\u0002DevkitOverrideAppID" ||
//word == "\u0002LastPlayTime" || // NOTE: there is no NUL terminated at the end of it, so it is not read correctly
// Which means that this bottom one is getting picked up by the LastPlayTime
word == "\u0001FlatpakAppID"
) {
key = word;
}
else if (word == "tags") {
readingTags = true;
}
else if (key != "") {
switch (key) {
//case "\u0002appid": shortcut.AppId = word; break;
case "\u0001AppName": shortcut.AppName = word; break;
case "\u0001Exe": shortcut.Exe = word.Trim('"'); break;
case "\u0001StartDir": shortcut.StartDir = word; break;
case "\u0001icon": shortcut.Icon = word; break;
case "\u0001ShortcutPath": shortcut.ShortcutPath = word; break;
case "\u0001LaunchOptions": shortcut.LaunchOptions = word; break;
case "\u0002IsHidden": shortcut.IsHidden = (word == "\u0001"); break;
case "\u0002AllowDesktopConfig": shortcut.AllowDesktopConfig = (word == "\u0001"); break;
case "\u0002AllowOverlay": shortcut.AllowOverlay = (word == "\u0001"); break;
case "\u0002OpenVR": shortcut.OpenVR = (word == "\u0001"); break;
case "\u0002Devkit": shortcut.Devkit = (word == "\u0001"); break;
case "\u0002DevkitGameID": shortcut.DevkitGameID = word; break;
case "\u0002DevkitOverrideAppID": shortcut.DevkitOverrideAppID = word; break;
//case "\u0002LastPlayTime": shortcut.LastPlayTime = word; break;
case "\u0001FlatpakAppID": shortcut.FlatpakAppID = word; break;
default:
break;
}
key = "";
}
else if (readingTags) {
if (word.StartsWith("\u0001")) {
tagId = int.Parse(word.Substring("\u0001".Length));
}
else if (tagId >= 0) {
shortcut.Tags.Add(word);
tagId = -1;
}
else {
readingTags = false;
}
}
word = "";
}
else {
word += c;
}
}
if (shortcut != null)
result.Add(shortcut);
return result;
}
// to read and return data, use this somewhere:
List<Shortcut> shortcuts = (YourClassName.)ReadAndReturn(filePath);
Writing the shortcuts.vdf
private static string BuildShortcuts(List<Shortcut> shortcuts)
{
string shortcutsString = "\u0000shortcuts\u0000";
for (int i = 0; i < shortcuts.Count; i++)
{
shortcutsString += "\u0000" + i + "\u0000";
shortcutsString += BuildShortcut(shortcuts[i]);
shortcutsString += "\u0008";
}
shortcutsString += "\u0008\u0008";
return shortcutsString;
}
private static string BuildShortcut(Shortcut shortcut)
{
string shortcutString = "";
//shortcutString += "\u0002appid\u0000" + shortcut.GetAppID() + "\u0000";
shortcutString += "\u0001appname\u0000" + shortcut.AppName + "\u0000";
shortcutString += "\u0001exe\u0000\"" + shortcut.Exe + "\"\u0000";
shortcutString += "\u0001StartDir\u0000\"" + shortcut.StartDir + "\"\u0000";
shortcutString += "\u0001icon\u0000" + shortcut.Icon + "\u0000";
shortcutString += "\u0001ShortcutPath\u0000" + shortcut.ShortcutPath + "\u0000";
shortcutString += "\u0001LaunchOptions\u0000" + shortcut.LaunchOptions + "\u0000";
shortcutString += "\u0002hidden\u0000" + (shortcut.Hidden ? "\u0001" : "\u0000") + "\u0000\u0000\u0000";
shortcutString += buildTags(shortcut.Tags);
return shortcutString;
}
private static string buildTags(List<string> tags)
{
var tagString = "\u0000tags\u0000";
for (var i = 0; i < tags.Count; ++i)
{
tagString += "\u0001" + i + "\u0000" + tags[i] + "\u0000";
}
tagString += "\u0008";
return tagString;
}
Get AppId for a shotcut
To display an image in the library, the app id of the shortcut must be known. What has been documented so far is that the app id is given by the following (untested) method:
var longValue = new Long(crcValue, crcValue, true);
longValue = longValue.or(0x80000000);
longValue = longValue.shl(32);
longValue = longValue.or(0x02000000);
return result.ToString();
private string GetSHA1()
{
var data = Encoding.ASCII.GetBytes(Exe);
var hashData = new SHA1Managed().ComputeHash(data);
var hash = string.Empty;
foreach (var b in hashData)
{
hash += b.ToString("X2");
}
return hash;
}

This article has not been added to any content
categories. Please help out by
adding categories.
January 2024


January 2024