/// <summary> /// Gets the node at the given address. May be used to build structure. /// </summary> /// <param name="args">An ordered list of keys, like a path</param> /// <param name="create">If true, will create any nodes it does not find along the path.</param> /// <param name="index">Start index of the arg array</param> /// <returns>The FileNode at the given location, or null if the location was not found / created</returns> public VdfFileNode GetNodeAt(string[] args, bool create = true, int index = 0) { if (index >= args.Length) { return(this); } if (NodeType == ValueType.Array) { Dictionary <string, VdfFileNode> data = (Dictionary <string, VdfFileNode>)NodeData; if (ContainsKey(args[index])) { return(data[args[index]].GetNodeAt(args, create, index + 1)); } if (create) { VdfFileNode newNode = new VdfFileNode(); data.Add(args[index], newNode); return(newNode.GetNodeAt(args, create, index + 1)); } } return(null); }
public static Dictionary <int, AppInfo> LoadApps(string path) { Dictionary <int, AppInfo> result = new Dictionary <int, AppInfo>(); BinaryReader bReader = new BinaryReader(new FileStream(path, FileMode.Open, FileAccess.Read)); long fileLength = bReader.BaseStream.Length; // seek to common: start of a new entry byte[] start = new byte[] { 0x00, 0x00, 0x63, 0x6F, 0x6D, 0x6D, 0x6F, 0x6E, 0x00 }; // 0x00 0x00 c o m m o n 0x00 VdfFileNode.ReadBin_SeekTo(bReader, start, fileLength); VdfFileNode node = VdfFileNode.LoadFromBinary(bReader, fileLength); while (node != null) { AppInfo app = AppInfo.FromVdfNode(node); if (app != null) { result.Add(app.Id, app); } VdfFileNode.ReadBin_SeekTo(bReader, start, fileLength); node = VdfFileNode.LoadFromBinary(bReader, fileLength); } bReader.Close(); return(result); }
public static Dictionary <int, PackageInfo> LoadPackages(string path) { Dictionary <int, PackageInfo> result = new Dictionary <int, PackageInfo>(); BinaryReader bReader = new BinaryReader(new FileStream(path, FileMode.Open), Encoding.ASCII); bReader.ReadBytes(38); VdfFileNode node = VdfFileNode.LoadFromBinary(bReader); while (node != null) { PackageInfo p = FromVdfNode(node); if (p != null) { result.Add(p.Id, p); } bReader.ReadBytes(31); node = node = VdfFileNode.LoadFromBinary(bReader); } bReader.Close(); return(result); }
/// <summary> /// Loads a FileNode from stream. /// </summary> /// <param name="stream">Stream to load from</param> /// <returns>FileNode representing the contents of the stream.</returns> public static VdfFileNode LoadFromBinary(BinaryReader stream, long streamLength = -1) { if (streamLength == -1) { streamLength = stream.BaseStream.Length; } if (stream.BaseStream.Position == streamLength) { return(null); } VdfFileNode thisLevel = new VdfFileNode(); bool endOfStream = false; while (!endOfStream) { byte nextByte; try { nextByte = stream.ReadByte(); } catch (EndOfStreamException) { endOfStream = true; nextByte = 8; } // Get key string key = null; if (endOfStream || nextByte == 8 || stream.BaseStream.Position == streamLength) { break; } else if (nextByte == 0) { key = ReadBin_GetStringToken(stream); VdfFileNode newNode; newNode = LoadFromBinary(stream, streamLength); thisLevel[key] = newNode; } else if (nextByte == 1) { key = ReadBin_GetStringToken(stream); thisLevel[key] = new VdfFileNode(ReadBin_GetStringToken(stream)); } else if (nextByte == 2) { key = ReadBin_GetStringToken(stream); int val = stream.ReadInt32(); thisLevel[key] = new VdfFileNode(val); } else if (nextByte == 0xFF) { return(null); } else { throw new ParseException(string.Format(GlobalStrings.TextVdfFile_UnexpectedCharacterKey, nextByte.ToString())); } } return(thisLevel); }
public static PackageInfo FromVdfNode(VdfFileNode node) { VdfFileNode idNode = node.GetNodeAt(new string[] { "packageId" }, false); if ((idNode != null) && idNode.NodeType == ValueType.Int) { int id = idNode.NodeInt; string name = null; VdfFileNode nameNode = node.GetNodeAt(new string[] { "name" }, false); if (nameNode != null && nameNode.NodeType == ValueType.String) { name = nameNode.NodeString; } PackageInfo package = new PackageInfo(id, name); VdfFileNode billingtypeNode = node["billingtype"]; if (billingtypeNode != null && billingtypeNode.NodeType == ValueType.String || billingtypeNode.NodeType == ValueType.Int) { int bType = billingtypeNode.NodeInt; /*if( Enum.IsDefined( typeof(PackageBillingType), bType ) ) { * * } else { * * }*/ package.BillingType = (PackageBillingType)bType; } VdfFileNode appsNode = node["appids"]; if (appsNode != null && appsNode.NodeType == ValueType.Array) { foreach (VdfFileNode aNode in appsNode.NodeArray.Values) { if (aNode.NodeType == ValueType.Int) { package.AppIds.Add(aNode.NodeInt); } } } VdfFileNode expiryNode = node.GetNodeAt(new string[] { "extended", "ExpiryTime" }, false); if (expiryNode != null && expiryNode.NodeType == ValueType.Int) { package.ExpiryTime = GetLocalDateTime(expiryNode.NodeInt); package.Expires = true; } else { package.Expires = false; } return(package); } return(null); }
/// <summary> /// Loads Apps from packageinfo.vdf. /// </summary> /// <param name="path">Path of packageinfo.vdf</param> public static Dictionary <int, PackageInfo> LoadPackages(string path) { Dictionary <int, PackageInfo> result = new Dictionary <int, PackageInfo>(); /* packageinfo.vdf entry example format, sometimes has extra values. Line breaks are only for readability and not part of format. * we only care about *packageid*, *billingtype*, *appids* * *undeciphered*(24 bytes i haven't deciphered) *changenumber*(4 bytes, little endian) * 00 *packageid*(variable size, big endian, ascii) 00 * 02 packageid 00 *packageid*(4 bytes, little endian) * 02 billingtype 00 *billingtype*(4 bytes, little endian) * 02 licensetype 00 *licensetype*(4 bytes, little endian) * 02 status 00 00 00 00 00 00 extended 00 * 08 00 appids 00 02 *entrynumber*(variable size, number stored as string(ascii), starts at 0, random example: 31 38 39=189) 00 *appid*(4 bytes, little endian) * 08 00 depotids 00 02 *entrynumber* 00 *depotid*(4 bytes, little endian) 02 *entrynumber* 00 *depotid* 02 *entrynumber* 00 *depotid* * 08 00 appitems 00 08 08 08 */ BinaryReader bReader = new BinaryReader(new FileStream(path, FileMode.Open), Encoding.ASCII); long fileLength = bReader.BaseStream.Length; // seek to packageid: start of a new entry byte[] packageidBytes = { 0x00, 0x02, 0x70, 0x61, 0x63, 0x6B, 0x61, 0x67, 0x65, 0x69, 0x64, 0x00 }; // 0x00 0x02 p a c k a g e i d 0x00 byte[] billingtypeBytes = { 0x02, 0x62, 0x69, 0x6C, 0x6C, 0x69, 0x6E, 0x67, 0x74, 0x79, 0x70, 0x65, 0x00 }; // 0x02 b i l l i n g t y p e 0x00 byte[] appidsBytes = { 0x08, 0x00, 0x61, 0x70, 0x70, 0x69, 0x64, 0x73, 0x00 }; // 0x08 0x00 appids 0x00 VdfFileNode.ReadBin_SeekTo(bReader, packageidBytes, fileLength); while (bReader.BaseStream.Position < fileLength) { int id = bReader.ReadInt32(); PackageInfo package = new PackageInfo(id); VdfFileNode.ReadBin_SeekTo(bReader, billingtypeBytes, fileLength); package.BillingType = (PackageBillingType)bReader.ReadInt32(); VdfFileNode.ReadBin_SeekTo(bReader, appidsBytes, fileLength); while (bReader.ReadByte() == 0x02) { while (bReader.ReadByte() != 0x00) { } package.AppIds.Add(bReader.ReadInt32()); } result.Add(package.Id, package); VdfFileNode.ReadBin_SeekTo(bReader, packageidBytes, fileLength); } return(result); }
/// <summary> /// Loads a FileNode from stream. /// </summary> /// <param name="stream">Stream to load from</param> /// <returns>FileNode representing the contents of the stream.</returns> public static VdfFileNode LoadFromText(StreamReader stream, bool useFirstAsRoot = false) { VdfFileNode thisLevel = useFirstAsRoot ? null : new VdfFileNode(); ReadText_SkipWhitespace(stream); while (!stream.EndOfStream) { ReadText_SkipWhitespace(stream); // Get key char nextChar = (char)stream.Read(); string key = null; if (stream.EndOfStream || (nextChar == '}')) { break; } if (nextChar == '"') { key = ReadText_GetStringToken(stream); } else { throw new ParseException(string.Format(GlobalStrings.TextVdfFile_UnexpectedCharacterKey, nextChar)); } ReadText_SkipWhitespace(stream); // Get value nextChar = (char)stream.Read(); VdfFileNode newNode; if (nextChar == '"') { newNode = new VdfFileNode(ReadText_GetStringToken(stream)); } else if (nextChar == '{') { newNode = LoadFromText(stream); } else { throw new ParseException(string.Format(GlobalStrings.TextVdfFile_UnexpectedCharacterValue, nextChar)); } if (useFirstAsRoot) { return(newNode); } thisLevel[key] = newNode; } return(thisLevel); }
public static PackageInfo FromVdfNode(VdfFileNode node) { VdfFileNode idNode = node.GetNodeAt(new[] { "packageId" }, false); if ((idNode != null) && (idNode.NodeType == ValueType.Int)) { int id = idNode.NodeInt; string name = null; VdfFileNode nameNode = node.GetNodeAt(new[] { "name" }, false); if ((nameNode != null) && (nameNode.NodeType == ValueType.String)) { name = nameNode.NodeString; } PackageInfo package = new PackageInfo(id, name); VdfFileNode billingtypeNode = node["billingtype"]; if (((billingtypeNode != null) && (billingtypeNode.NodeType == ValueType.String)) || (billingtypeNode.NodeType == ValueType.Int)) { int bType = billingtypeNode.NodeInt; /*if( Enum.IsDefined( typeof(PackageBillingType), bType ) ) { * * } else { * * }*/ package.BillingType = (PackageBillingType)bType; } VdfFileNode appsNode = node["appids"]; if ((appsNode != null) && (appsNode.NodeType == ValueType.Array)) { foreach (VdfFileNode aNode in appsNode.NodeArray.Values) { if (aNode.NodeType == ValueType.Int) { package.AppIds.Add(aNode.NodeInt); } } } return(package); } return(null); }
/// <summary> /// Load identifiers for external games from screenshots.vdf /// </summary> /// <param name="SteamId">Steam user identifier</param> /// <param name="shortcutGames">Found games listed as pairs of {gameName, gameId} </param> /// <returns></returns> private bool LoadShortcutGames(long SteamId, out Dictionary <string, int> shortcutGames) { bool result = false; string filePath = string.Format(Properties.Resources.ScreenshotsFilePath, Settings.Instance().SteamPath, Profile.ID64toDirName(SteamId)); shortcutGames = new Dictionary <string, int>(); StreamReader reader = null; try { reader = new StreamReader(filePath, false); TextVdfFileNode dataRoot = TextVdfFileNode.Load(reader, true); VdfFileNode appsNode = dataRoot.GetNodeAt(new string[] { "shortcutnames" }, false); foreach (KeyValuePair <string, VdfFileNode> shortcutPair in appsNode.NodeArray) { string strId = shortcutPair.Key; ulong ulongId; if (ulong.TryParse(strId, out ulongId)) { int gameId = (int)(ulongId >> 32); string gameName = (string)shortcutPair.Value.NodeData; shortcutGames.Add(gameName, gameId); } else { Program.Logger.Write(LoggerLevel.Warning, GlobalStrings.GameData_ErrorParsingScreenshots, (string)shortcutPair.Value.NodeData); } } result = true; } catch (FileNotFoundException e) { Program.Logger.Write(LoggerLevel.Error, GlobalStrings.GameData_ErrorOpeningConfigFileParam, e.ToString()); } catch (IOException e) { Program.Logger.Write(LoggerLevel.Error, GlobalStrings.GameData_LoadingErrorSteamConfig, e.ToString()); } if (reader != null) { reader.Close(); } return(result); }
public static PackageInfo FromVdfNode( VdfFileNode node ) { VdfFileNode idNode = node.GetNodeAt( new string[] { "packageId" }, false ); if( (idNode != null ) && idNode.NodeType == ValueType.Int ) { int id = idNode.NodeInt; string name = null; VdfFileNode nameNode = node.GetNodeAt( new string[] {"name"}, false ); if( nameNode != null && nameNode.NodeType == ValueType.String ) { name = nameNode.NodeString; } PackageInfo package = new PackageInfo(id, name); VdfFileNode billingtypeNode = node["billingtype"]; if( billingtypeNode != null && billingtypeNode.NodeType == ValueType.String || billingtypeNode.NodeType == ValueType.Int ) { int bType = billingtypeNode.NodeInt; /*if( Enum.IsDefined( typeof(PackageBillingType), bType ) ) { } else { }*/ package.BillingType = (PackageBillingType)bType; } VdfFileNode appsNode = node["appids"]; if( appsNode != null && appsNode.NodeType == ValueType.Array ) { foreach( VdfFileNode aNode in appsNode.NodeArray.Values ) { if( aNode.NodeType == ValueType.Int ) { package.AppIds.Add( aNode.NodeInt ); } } } VdfFileNode expiryNode = node.GetNodeAt( new string[] {"extended", "ExpiryTime"}, false); if( expiryNode != null && expiryNode.NodeType == ValueType.Int ) { package.ExpiryTime = GetLocalDateTime( expiryNode.NodeInt ); package.Expires = true; } else { package.Expires = false; } return package; } return null; }
/// <summary> /// Loads category info from the given steam config file. /// </summary> /// <param name="filePath">The path of the file to open</param> /// <returns>The number of game entries found</returns> public int ImportSteamFile(string filePath, SortedSet <int> ignore, bool ignoreDlc) { Program.Logger.Write(LoggerLevel.Info, GlobalStrings.GameData_OpeningSteamConfigFile, filePath); TextVdfFileNode dataRoot; try { using (StreamReader reader = new StreamReader(filePath, false)) { dataRoot = TextVdfFileNode.Load(reader, true); } } catch (ParseException e) { Program.Logger.Write(LoggerLevel.Error, GlobalStrings.GameData_ErrorParsingConfigFileParam, e.Message); throw new ApplicationException(GlobalStrings.GameData_ErrorParsingSteamConfigFile + e.Message, e); } catch (IOException e) { Program.Logger.Write(LoggerLevel.Error, GlobalStrings.GameData_ErrorOpeningConfigFileParam, e.Message); throw new ApplicationException(GlobalStrings.GameData_ErrorOpeningSteamConfigFile + e.Message, e); } VdfFileNode appsNode = dataRoot.GetNodeAt(new string[] { "Software", "Valve", "Steam", "apps" }, true); int count = GetDataFromVdf(appsNode, ignore, ignoreDlc); Program.Logger.Write(LoggerLevel.Info, GlobalStrings.GameData_SteamConfigFileLoaded, count); return(count); }
/// <summary> /// Get LastPlayed date from a VDF node containing a list of games. /// Any games in the node not found in the game list will be added to the gamelist. /// </summary> /// <param name="appsNode">Node containing the game nodes</param> /// <param name="ignore">Set of games to ignore</param> /// <param name="forceInclude">Include games even if their type is not an included type</param> private void GetLastPlayedFromVdf(VdfFileNode appsNode, SortedSet<int> ignore, AppTypes includedTypes) { Dictionary<string, VdfFileNode> gameNodeArray = appsNode.NodeArray; if (gameNodeArray != null) { foreach (KeyValuePair<string, VdfFileNode> gameNodePair in gameNodeArray) { int gameId; if (int.TryParse(gameNodePair.Key, out gameId)) { if ((ignore != null && ignore.Contains(gameId)) || !Program.GameDB.IncludeItemInGameList(gameId, includedTypes)) { Program.Logger.Write(LoggerLevel.Verbose, GlobalStrings.GameData_SkippedProcessingGame, gameId); } else if (gameNodePair.Value != null && gameNodePair.Value.NodeType == ValueType.Array) { GameInfo game = null; // Add the game to the list if it doesn't exist already if (!Games.ContainsKey(gameId)) { game = new GameInfo(gameId, Program.GameDB.GetName(gameId), this); Games.Add(gameId, game); Program.Logger.Write(LoggerLevel.Verbose, GlobalStrings.GameData_AddedNewGame, gameId, game.Name); } else { game = Games[gameId]; } if (gameNodePair.Value.ContainsKey("LastPlayed") && gameNodePair.Value["LastPlayed"].NodeInt != 0) { DateTime dt = Utility.GetDTFromUTime(gameNodePair.Value["LastPlayed"].NodeInt); if (dt > game.LastPlayed) game.LastPlayed = dt; Program.Logger.Write(LoggerLevel.Verbose, GlobalStrings.GameData_ProcessedGame, gameId, game.LastPlayed.ToString()); } } } } } }
/// <summary> /// Loads in games from a VDF node containing a list of games. /// Any games in the node not found in the game list will be added to the gamelist. /// If a game in the node has a tags subnode, the "favorite" field will be overwritten. /// If a game in the node has a category set, it will overwrite any categories in the gamelist. /// If a game in the node does NOT have a category set, the category in the gamelist will NOT be cleared. /// </summary> /// <param name="appsNode">Node containing the game nodes</param> /// <param name="ignore">Set of games to ignore</param> /// <param name="forceInclude">Include games even if their type is not an included type</param> /// <returns>Number of games loaded</returns> private int IntegrateGamesFromVdf( VdfFileNode appsNode, SortedSet<int> ignore, AppTypes includedTypes ) { int loadedGames = 0; Dictionary<string, VdfFileNode> gameNodeArray = appsNode.NodeArray; if( gameNodeArray != null ) { foreach( KeyValuePair<string, VdfFileNode> gameNodePair in gameNodeArray ) { int gameId; if( int.TryParse( gameNodePair.Key, out gameId ) ) { if( ( ignore != null && ignore.Contains( gameId ) ) || !Program.GameDB.IncludeItemInGameList( gameId, includedTypes ) ) { Program.Logger.Write( LoggerLevel.Verbose, GlobalStrings.GameData_SkippedProcessingGame, gameId ); } else if( gameNodePair.Value != null && gameNodePair.Value.NodeType == ValueType.Array ) { GameInfo game = null; // Add the game to the list if it doesn't exist already if( !Games.ContainsKey( gameId ) ) { game = new GameInfo( gameId, Program.GameDB.GetName( gameId ), this ); Games.Add( gameId, game ); Program.Logger.Write( LoggerLevel.Verbose, GlobalStrings.GameData_AddedNewGame, gameId, game.Name ); } else { game = Games[gameId]; } loadedGames++; game.ApplySource( GameListingSource.SteamConfig ); game.Hidden = ( gameNodePair.Value.ContainsKey( "hidden" ) && gameNodePair.Value["hidden"].NodeInt != 0 ); VdfFileNode tagsNode = gameNodePair.Value["tags"]; if( tagsNode != null ) { Dictionary<string, VdfFileNode> tagArray = tagsNode.NodeArray; if( tagArray != null ) { List<Category> cats = new List<Category>( tagArray.Count ); foreach( VdfFileNode tag in tagArray.Values ) { string tagName = tag.NodeString; if( tagName != null ) { Category c = GetCategory( tagName ); if( c != null ) cats.Add( c ); } } if( cats.Count > 0 ) { SetGameCategories( gameId, cats, false ); } } } Program.Logger.Write( LoggerLevel.Verbose, GlobalStrings.GameData_ProcessedGame, gameId, string.Join( ",", game.Categories ) ); } } } } return loadedGames; }
/// <summary> /// Writes category information out to a steam config file. Also saves any other settings that had been loaded, to avoid setting loss. /// </summary> /// <param name="path">Full path of the steam config file to save</param> public void SaveSteamFile(string filePath, bool discardMissing) { Program.Logger.Write(LoggerLevel.Info, GlobalStrings.GameData_SavingSteamConfigFile, filePath); TextVdfFileNode fileData = new TextVdfFileNode(); try { using (StreamReader reader = new StreamReader(filePath, false)) { fileData = TextVdfFileNode.Load(reader, true); } } catch (Exception e) { Program.Logger.Write(LoggerLevel.Warning, GlobalStrings.GameData_LoadingErrorSteamConfig, e.Message); } VdfFileNode appListNode = fileData.GetNodeAt(new string[] { "Software", "Valve", "Steam", "apps" }, true); if (discardMissing) { Dictionary <string, VdfFileNode> gameNodeArray = appListNode.NodeArray; if (gameNodeArray != null) { foreach (KeyValuePair <string, VdfFileNode> pair in gameNodeArray) { int gameId; if (!(int.TryParse(pair.Key, out gameId) && Games.ContainsKey(gameId))) { Program.Logger.Write(LoggerLevel.Verbose, GlobalStrings.GameData_RemovingGameCategoryFromSteamConfig, gameId); pair.Value.RemoveSubnode("tags"); } } } } foreach (Game game in Games.Values) { if (game.Id > 0) // External games have negative identifier { Program.Logger.Write(LoggerLevel.Verbose, GlobalStrings.GameData_AddingGameToConfigFile, game.Id); VdfFileNode gameNode = (VdfFileNode)appListNode[game.Id.ToString()]; gameNode.RemoveSubnode("tags"); if (game.Category != null || game.Favorite) { VdfFileNode tagsNode = (VdfFileNode)gameNode["tags"]; int key = 0; if (game.Category != null) { tagsNode[key.ToString()] = new TextVdfFileNode(game.Category.Name); key++; } if (game.Favorite) { tagsNode[key.ToString()] = new TextVdfFileNode("favorite"); } } } } Program.Logger.Write(LoggerLevel.Verbose, GlobalStrings.GameData_CleaningUpSteamConfigTree); appListNode.CleanTree(); Program.Logger.Write(LoggerLevel.Info, GlobalStrings.GameData_WritingToDisk); TextVdfFileNode fullFile = new TextVdfFileNode(); fullFile["UserLocalConfigStore"] = fileData; try { FileInfo f = new FileInfo(filePath); f.Directory.Create(); FileStream fStream = f.Open(FileMode.Create, FileAccess.Write, FileShare.None); using (StreamWriter writer = new StreamWriter(fStream)) { fullFile.Save(writer); } fStream.Close(); } catch (ArgumentException e) { Program.Logger.Write(LoggerLevel.Error, GlobalStrings.GameData_ErrorSavingSteamConfigFile, e.ToString()); throw new ApplicationException(GlobalStrings.GameData_FailedToSaveSteamConfigBadPath, e); } catch (IOException e) { Program.Logger.Write(LoggerLevel.Error, GlobalStrings.GameData_ErrorSavingSteamConfigFile, e.ToString()); throw new ApplicationException(GlobalStrings.GameData_FailedToSaveSteamConfigFile + e.Message, e); } catch (UnauthorizedAccessException e) { Program.Logger.Write(LoggerLevel.Error, GlobalStrings.GameData_ErrorSavingSteamConfigFile, e.ToString()); throw new ApplicationException(GlobalStrings.GameData_AccessDeniedSteamConfigFile + e.Message, e); } }
/// <summary> /// Searches a list of games, looking for the one that matches the information in the shortcut node. /// Checks launch ID first, then checks a combination of name and ID, then just checks name. /// </summary> /// <param name="shortcutId">ID of the shortcut node</param> /// <param name="shortcutNode">Shotcut node itself</param> /// <param name="gamesToMatchAgainst">List of game objects to match against</param> /// <param name="shortcutLaunchIds">List of launch IDs referenced by name</param> /// <returns>The index of the matching game if found, -1 otherwise.</returns> private int FindMatchingShortcut( int shortcutId, VdfFileNode shortcutNode, List<GameInfo> gamesToMatchAgainst, StringDictionary shortcutLaunchIds ) { VdfFileNode nodeName = shortcutNode.GetNodeAt( new string[] { "appname" }, false ); string gameName = ( nodeName != null ) ? nodeName.NodeString : null; string launchId = shortcutLaunchIds[gameName]; // First, look for games with matching launch IDs. for( int i = 0; i < gamesToMatchAgainst.Count; i++ ) { if( gamesToMatchAgainst[i].LaunchString == launchId ) return i; } // Second, look for games with matching names AND matching shortcut IDs. for( int i = 0; i < gamesToMatchAgainst.Count; i++ ) { if( gamesToMatchAgainst[i].Id == -( shortcutId + 1 ) && gamesToMatchAgainst[i].Name == gameName ) return i; } // Third, just look for name matches for( int i = 0; i < gamesToMatchAgainst.Count; i++ ) { if( gamesToMatchAgainst[i].Name == gameName ) return i; } return -1; }
public static AppInfo FromVdfNode(VdfFileNode commonNode) { if (commonNode == null || commonNode.NodeType != ValueType.Array) { return(null); } AppInfo result = null; VdfFileNode idNode = commonNode.GetNodeAt(new string[] { "gameid" }, false); int id = -1; if (idNode != null) { if (idNode.NodeType == ValueType.Int) { id = idNode.NodeInt; } else if (idNode.NodeType == ValueType.String) { if (!int.TryParse(idNode.NodeString, out id)) { id = -1; } } } if (id >= 0) { // Get name string name = null; VdfFileNode nameNode = commonNode.GetNodeAt(new string[] { "name" }, false); if (nameNode != null) { name = nameNode.NodeData.ToString(); } // Get type string typeStr = null; AppTypes type = AppTypes.Unknown; VdfFileNode typeNode = commonNode.GetNodeAt(new string[] { "type" }, false); if (typeNode != null) { typeStr = typeNode.NodeData.ToString(); } if (typeStr != null) { if (!Enum.TryParse <AppTypes>(typeStr, true, out type)) { type = AppTypes.Other; } } // Get platforms string oslist = null; AppPlatforms platforms = AppPlatforms.None; VdfFileNode oslistNode = commonNode.GetNodeAt(new string[] { "oslist" }, false); if (oslistNode != null) { oslist = oslistNode.NodeData.ToString(); if (oslist.IndexOf("windows", StringComparison.OrdinalIgnoreCase) != -1) { platforms |= AppPlatforms.Windows; } if (oslist.IndexOf("mac", StringComparison.OrdinalIgnoreCase) != -1) { platforms |= AppPlatforms.Mac; } if (oslist.IndexOf("linux", StringComparison.OrdinalIgnoreCase) != -1) { platforms |= AppPlatforms.Linux; } } result = new AppInfo(id, name, type, platforms); // Get parent VdfFileNode parentNode = commonNode.GetNodeAt(new string[] { "parent" }, false); if (parentNode != null) { result.Parent = parentNode.NodeInt; } } return(result); }
/// <summary> /// Loads in games from a VDF node containing a list of games. /// Any games in the node not found in the game list will be added to the gamelist. /// If a game in the node has a tags subnode, the "favorite" field will be overwritten. /// If a game in the node has a category set, it will overwrite any categories in the gamelist. /// If a game in the node does NOT have a category set, the category in the gamelist will NOT be cleared. /// </summary> /// <param name="appsNode">Node containing the game nodes</param> /// <param name="ignore">Set of games to ignore</param> /// <returns>Number of games loaded</returns> private int IntegrateGamesFromVdf( VdfFileNode appsNode, SortedSet<int> ignore, bool ignoreDlc ) { int loadedGames = 0; Dictionary<string, VdfFileNode> gameNodeArray = appsNode.NodeArray; if( gameNodeArray != null ) { foreach( KeyValuePair<string, VdfFileNode> gameNodePair in gameNodeArray ) { int gameId; if( int.TryParse( gameNodePair.Key, out gameId ) ) { if( ( ignore != null && ignore.Contains( gameId ) ) || ( ignoreDlc && Program.GameDB.IsDlc( gameId ) ) ) { Program.Logger.Write( LoggerLevel.Verbose, GlobalStrings.GameData_SkippedProcessingGame, gameId ); continue; } if( gameNodePair.Value != null && gameNodePair.Value.ContainsKey( "tags" ) ) { SortedSet<Category> cats = new SortedSet<Category>(); loadedGames++; VdfFileNode tagsNode = gameNodePair.Value["tags"]; Dictionary<string, VdfFileNode> tagArray = tagsNode.NodeArray; if( tagArray != null ) { foreach( VdfFileNode tag in tagArray.Values ) { string tagName = tag.NodeString; if( tagName != null ) { Category c = GetCategory( tagName ); if( c != null ) cats.Add( c ); } } } bool hidden = false; if( gameNodePair.Value.ContainsKey( "hidden" ) ) { VdfFileNode hiddenNode = gameNodePair.Value["hidden"]; hidden = ( hiddenNode.NodeString == "1" || hiddenNode.NodeInt == 1 ); } // Add the game to the list if it doesn't exist already if( !Games.ContainsKey( gameId ) ) { GameInfo newGame = new GameInfo( gameId, string.Empty ); Games.Add( gameId, newGame ); newGame.Name = Program.GameDB.GetName( gameId ); Program.Logger.Write( LoggerLevel.Verbose, GlobalStrings.GameData_AddedNewGame, gameId, newGame.Name ); } if( cats.Count > 0 ) { this.SetGameCategories( gameId, cats, false ); } Games[gameId].Hidden = hidden; //TODO: Don't think SortedSet.ToString() does what I hope Program.Logger.Write( LoggerLevel.Verbose, GlobalStrings.GameData_ProcessedGame, gameId, ( cats.Count == 0 ) ? "~" : cats.ToString() ); } } } } return loadedGames; }
/// <summary> /// Adds a non-steam game to the gamelist. /// </summary> /// <param name="gameId">ID of the game in the steam config file</param> /// <param name="gameNode">Node for the game in the steam config file</param> /// <param name="oldShortcuts">List of un-matched non-steam games from the gamelist before the update</param> /// <param name="launchIds">Dictionary of launch ids (name:launchId)</param> /// <param name="newGames">Number of NEW games that have been added to the list</param> /// <param name="preferSteamCategories">If true, prefers to use the categories from the steam config if there is a conflict. If false, prefers to use the categories from the existing gamelist.</param> /// <returns>True if the game was successfully added</returns> private bool IntegrateShortcut( int gameId, VdfFileNode gameNode, List<Game> oldShortcuts, StringDictionary launchIds, ref int newGames, bool preferSteamCategories = true ) { VdfFileNode nodeName = gameNode.GetNodeAt( new string[] { "appname" }, false ); string gameName = ( nodeName != null ) ? nodeName.NodeString : null; int newId = -gameId; if( Games.ContainsKey( newId ) ) { return false; } Game game = new Game( newId, gameName ); Games.Add( newId, game ); game.LaunchString = launchIds[gameName]; int oldShortcutId = FindMatchingShortcut( gameId, gameNode, oldShortcuts, launchIds ); bool oldCatSet = ( oldShortcutId != -1 ) && oldShortcuts[oldShortcutId].Category != null; if( oldShortcutId == -1 ) newGames++; VdfFileNode tagsNode = gameNode.GetNodeAt( new string[] { "tags" }, false ); bool steamCatSet = ( tagsNode != null && tagsNode.NodeType == ValueType.Array && tagsNode.NodeArray.Count > 0 ); //fill in categories if( steamCatSet && (preferSteamCategories || !oldCatSet) ) { foreach( KeyValuePair<string, VdfFileNode> tag in tagsNode.NodeArray) { string tagName = tag.Value.NodeString; if( tagName != null ) { if( tagName == "favorite" ) { game.Favorite = true; } else { game.Category = this.GetCategory( tagName ); } } } } else { game.Category = oldShortcuts[oldShortcutId].Category; game.Favorite = oldShortcuts[oldShortcutId].Favorite; } if( oldShortcutId != -1 ) oldShortcuts.RemoveAt( oldShortcutId ); return true; }
/// <summary> /// Writes category info for shortcut games to shortcuts.vdf config file for specified Steam user. /// Loads the shortcut config file, then tries to match each game in the file against one of the games in the gamelist. If it finds a match, it updates the config file with the new category info. /// </summary> /// <param name="SteamId">Identifier of Steam user to save information</param> /// <param name="discardMissing">If true, category information in shortcuts.vdf file is removed if game is not in Game list</param> public void ExportSteamShortcuts( long SteamId ) { string filePath = string.Format( Properties.Resources.ShortCutsFilePath, Settings.Instance.SteamPath, Profile.ID64toDirName( SteamId ) ); Program.Logger.Write( LoggerLevel.Info, GlobalStrings.GameData_SavingSteamConfigFile, filePath ); FileStream fStream = null; BinaryReader binReader = null; VdfFileNode dataRoot = null; try { fStream = new FileStream( filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite ); binReader = new BinaryReader( fStream ); dataRoot = VdfFileNode.LoadFromBinary( binReader ); } catch( FileNotFoundException e ) { Program.Logger.Write( LoggerLevel.Error, GlobalStrings.GameData_ErrorOpeningConfigFileParam, e.ToString() ); } catch( IOException e ) { Program.Logger.Write( LoggerLevel.Error, GlobalStrings.GameData_LoadingErrorSteamConfig, e.ToString() ); } if( binReader != null ) binReader.Close(); if( fStream != null ) fStream.Close(); if( dataRoot != null ) { List<GameInfo> gamesToSave = new List<GameInfo>(); foreach( int id in Games.Keys ) { if( id < 0 ) gamesToSave.Add( Games[id] ); } StringDictionary launchIds = new StringDictionary(); LoadShortcutLaunchIds( SteamId, out launchIds ); VdfFileNode appsNode = dataRoot.GetNodeAt( new string[] { "shortcuts" }, false ); foreach( KeyValuePair<string, VdfFileNode> shortcutPair in appsNode.NodeArray ) { VdfFileNode nodeGame = shortcutPair.Value; int nodeId = -1; int.TryParse( shortcutPair.Key, out nodeId ); int matchingIndex = FindMatchingShortcut( nodeId, nodeGame, gamesToSave, launchIds ); if( matchingIndex >= 0 ) { GameInfo game = gamesToSave[matchingIndex]; gamesToSave.RemoveAt( matchingIndex ); Program.Logger.Write( LoggerLevel.Verbose, GlobalStrings.GameData_AddingGameToConfigFile, game.Id ); VdfFileNode tagsNode = nodeGame.GetNodeAt( new string[] { "tags" }, true ); Dictionary<string, VdfFileNode> tags = tagsNode.NodeArray; if( tags != null ) { tags.Clear(); } int index = 0; foreach( Category c in game.Categories ) { tagsNode[index.ToString()] = new VdfFileNode( c.Name ); index++; } nodeGame["hidden"] = new VdfFileNode( game.Hidden ? 1 : 0 ); } } if( dataRoot.NodeType == ValueType.Array ) { Program.Logger.Write( LoggerLevel.Info, GlobalStrings.GameData_SavingShortcutConfigFile, filePath ); try { Utility.BackupFile( filePath, Settings.Instance.ConfigBackupCount ); } catch( Exception e ) { Program.Logger.Write( LoggerLevel.Error, GlobalStrings.Log_GameData_ShortcutBackupFailed, e.Message ); } try { string filePathTmp = filePath + ".tmp"; BinaryWriter binWriter; fStream = new FileStream( filePathTmp, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite ); binWriter = new BinaryWriter( fStream ); dataRoot.SaveAsBinary( binWriter ); binWriter.Close(); fStream.Close(); File.Delete( filePath ); File.Move( filePathTmp, filePath ); } catch( ArgumentException e ) { Program.Logger.Write( LoggerLevel.Error, GlobalStrings.GameData_ErrorSavingSteamConfigFile, e.ToString() ); throw new ApplicationException( GlobalStrings.GameData_FailedToSaveSteamConfigBadPath, e ); } catch( IOException e ) { Program.Logger.Write( LoggerLevel.Error, GlobalStrings.GameData_ErrorSavingSteamConfigFile, e.ToString() ); throw new ApplicationException( GlobalStrings.GameData_FailedToSaveSteamConfigFile + e.Message, e ); } catch( UnauthorizedAccessException e ) { Program.Logger.Write( LoggerLevel.Error, GlobalStrings.GameData_ErrorSavingSteamConfigFile, e.ToString() ); throw new ApplicationException( GlobalStrings.GameData_AccessDeniedSteamConfigFile + e.Message, e ); } } } }
private int ImportNonSteamGames(long SteamId, SortedSet <int> ignore) { int result = 0; string filePath = string.Format(Properties.Resources.ShortCutsFilePath, Settings.Instance().SteamPath, Profile.ID64toDirName(SteamId)); Program.Logger.Write(LoggerLevel.Info, GlobalStrings.GameData_OpeningSteamConfigFile, filePath); Dictionary <string, int> shortcutgames; if (LoadShortcutGames(SteamId, out shortcutgames)) { FileStream fStream = null; BinaryReader binReader = null; BinaryVdfFileNode dataRoot = null; try { fStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); binReader = new BinaryReader(fStream); dataRoot = BinaryVdfFileNode.Load(binReader); } finally { if (binReader != null) { binReader.Close(); } if (fStream != null) { fStream.Close(); } } if (dataRoot != null) { VdfFileNode shortcutsNode = dataRoot.GetNodeAt(new string[] { "shortcuts" }, false); foreach (KeyValuePair <string, VdfFileNode> shortcutPair in shortcutsNode.NodeArray) { string indexGame = shortcutPair.Key; VdfFileNode attrGame = shortcutPair.Value; VdfFileNode appGame = attrGame.GetNodeAt(new string[] { "appname" }, false); string gameName = appGame.NodeString; // Check if external game has identifier in screenshots.vdf file (this happens only if game has been launched before from Steam client) if (shortcutgames.ContainsKey(gameName)) { int gameIdInDB = shortcutgames[gameName]; if (!ignore.Contains(gameIdInDB)) { Game gameDB; if (Games.ContainsKey(gameIdInDB)) { gameDB = Games[gameIdInDB]; } else { gameDB = new Game(gameIdInDB, gameName); } string cat0 = null, cat1 = null; VdfFileNode tagsGame = attrGame.GetNodeAt(new string[] { "tags" }, false); if ((tagsGame != null) && (tagsGame.NodeType == ValueType.Array) && (tagsGame.NodeArray.Count > 0) && (tagsGame.NodeArray.ContainsKey("0"))) { VdfFileNode vdfCat = tagsGame.NodeArray["0"]; if (vdfCat.NodeType == ValueType.Value) { cat0 = vdfCat.NodeData.ToString(); } if (tagsGame.NodeArray.ContainsKey("1")) { vdfCat = tagsGame.NodeArray["1"]; if (vdfCat.NodeType == ValueType.Value) { cat1 = vdfCat.NodeData.ToString(); } } } gameDB.Favorite = ((cat0 == "favorite") || (cat1 == "favorite")); if (cat0 != "favorite") { gameDB.Category = GetCategory(cat0); } else { gameDB.Category = GetCategory(cat1); } result++; } } } } } return(result); }
/// <summary> /// Loads a FileNode from stream. /// </summary> /// <param name="stream">Stream to load from</param> /// <returns>FileNode representing the contents of the stream.</returns> public static VdfFileNode LoadFromText( StreamReader stream, bool useFirstAsRoot = false ) { VdfFileNode thisLevel = useFirstAsRoot ? null : new VdfFileNode(); ReadText_SkipWhitespace( stream ); while( !stream.EndOfStream ) { ReadText_SkipWhitespace( stream ); // Get key char nextChar = (char)stream.Read(); string key = null; if( stream.EndOfStream || nextChar == '}' ) { break; } else if( nextChar == '"' ) { key = ReadText_GetStringToken( stream ); } else { throw new ParseException( string.Format( GlobalStrings.TextVdfFile_UnexpectedCharacterKey, nextChar ) ); } ReadText_SkipWhitespace( stream ); // Get value nextChar = (char)stream.Read(); VdfFileNode newNode; if( nextChar == '"' ) { newNode = new VdfFileNode( ReadText_GetStringToken( stream ) ); } else if( nextChar == '{' ) { newNode = LoadFromText( stream ); } else { throw new ParseException( string.Format( GlobalStrings.TextVdfFile_UnexpectedCharacterValue, nextChar ) ); } if( useFirstAsRoot ) { return newNode; } thisLevel[key] = newNode; } return thisLevel; }
/// <summary> /// Gets the node at the given address. May be used to build structure. /// </summary> /// <param name="args">An ordered list of keys, like a path</param> /// <param name="create">If true, will create any nodes it does not find along the path.</param> /// <param name="index">Start index of the arg array</param> /// <returns>The FileNode at the given location, or null if the location was not found / created</returns> public VdfFileNode GetNodeAt( string[] args, bool create = true, int index = 0 ) { if( index >= args.Length ) { return this; } if( this.NodeType == ValueType.Array ) { Dictionary<String, VdfFileNode> data = (Dictionary<String, VdfFileNode>)NodeData; if( ContainsKey( args[index] ) ) { return data[args[index]].GetNodeAt( args, create, index + 1 ); } else if( create ) { VdfFileNode newNode = new VdfFileNode(); data.Add( args[index], newNode ); return newNode.GetNodeAt( args, create, index + 1 ); } } return null; }
/// <summary> /// Searches a list of games, looking for the one that matches the information in the shortcut node. /// Checks launch ID first, then checks a combination of name and ID, then just checks name. /// </summary> /// <param name="shortcutId">ID of the shortcut node</param> /// <param name="shortcutNode">Shotcut node itself</param> /// <param name="gamesToMatchAgainst">List of game objects to match against</param> /// <param name="shortcutLaunchIds">List of launch IDs referenced by name</param> /// <returns>The index of the matching game if found, -1 otherwise.</returns> private int FindMatchingShortcut( int shortcutId, VdfFileNode shortcutNode, List<Game> gamesToMatchAgainst, StringDictionary shortcutLaunchIds ) { VdfFileNode nodeName = shortcutNode.GetNodeAt( new string[] { "appname" }, false ); string gameName = ( nodeName != null ) ? nodeName.NodeString : null; string launchId = shortcutLaunchIds[gameName]; for( int i = 0; i < gamesToMatchAgainst.Count; i++ ) { if( gamesToMatchAgainst[i].LaunchString == launchId ) return i; } for( int i = 0; i < gamesToMatchAgainst.Count; i++ ) { if( gamesToMatchAgainst[i].Id == shortcutId && gamesToMatchAgainst[i].Name == gameName ) return i; } for( int i = 0; i < gamesToMatchAgainst.Count; i++ ) { if( gamesToMatchAgainst[i].Name == gameName ) return i; } return -1; }
/// <summary> /// Adds a non-steam game to the gamelist. /// </summary> /// <param name="gameId">ID of the game in the steam config file</param> /// <param name="gameNode">Node for the game in the steam config file</param> /// <param name="oldShortcuts">List of un-matched non-steam games from the gamelist before the update</param> /// <param name="launchIds">Dictionary of launch ids (name:launchId)</param> /// <param name="newGames">Number of NEW games that have been added to the list</param> /// <param name="preferSteamCategories">If true, prefers to use the categories from the steam config if there is a conflict. If false, prefers to use the categories from the existing gamelist.</param> /// <returns>True if the game was successfully added</returns> private bool IntegrateShortcut( int gameId, VdfFileNode gameNode, List<GameInfo> oldShortcuts, StringDictionary launchIds, ref int newGames, bool preferSteamCategories = true ) { VdfFileNode nodeName = gameNode.GetNodeAt( new string[] { "appname" }, false ); string gameName = ( nodeName != null ) ? nodeName.NodeString : null; // The ID of the created game must be negative int newId = -( gameId + 1 ); // This should never happen, but just in case if( Games.ContainsKey( newId ) ) { return false; } GameInfo game = new GameInfo( newId, gameName ); Games.Add( newId, game ); game.LaunchString = launchIds[gameName]; int oldShortcutId = FindMatchingShortcut( gameId, gameNode, oldShortcuts, launchIds ); bool oldCatSet = ( oldShortcutId != -1 ) && oldShortcuts[oldShortcutId].Categories.Count > 0; if( oldShortcutId == -1 ) newGames++; VdfFileNode tagsNode = gameNode.GetNodeAt( new string[] { "tags" }, false ); bool steamCatSet = ( tagsNode != null && tagsNode.NodeType == ValueType.Array && tagsNode.NodeArray.Count > 0 ); //fill in categories if( steamCatSet && ( preferSteamCategories || !oldCatSet ) ) { // Fill in categories from the Steam shortcut file foreach( KeyValuePair<string, VdfFileNode> tag in tagsNode.NodeArray ) { string tagName = tag.Value.NodeString; game.AddCategory( this.GetCategory( tagName ) ); } } else if( oldShortcutId >= 0 && oldShortcutId < oldShortcuts.Count ) { // Fill in categories from the game list game.SetCategories( oldShortcuts[oldShortcutId].Categories ); } game.Hidden = false; if( gameNode.ContainsKey( "hidden" ) ) { VdfFileNode hiddenNode = gameNode["hidden"]; game.Hidden = ( hiddenNode.NodeString == "1" || hiddenNode.NodeInt == 1 ); } if( oldShortcutId != -1 ) oldShortcuts.RemoveAt( oldShortcutId ); return true; }
/// <summary> /// Adds a non-steam game to the gamelist. /// </summary> /// <param name="gameId">ID of the game in the steam config file</param> /// <param name="gameNode">Node for the game in the steam config file</param> /// <param name="launchIds">Dictionary of launch ids (name:launchId)</param> /// <param name="newGames">Number of NEW games that have been added to the list</param> /// <param name="preferSteamCategories">If true, prefers to use the categories from the steam config if there is a conflict. If false, prefers to use the categories from the existing gamelist.</param> /// <returns>True if the game was successfully added</returns> private bool IntegrateShortcut( int gameId, VdfFileNode gameNode, StringDictionary launchIds ) { VdfFileNode nodeName = gameNode.GetNodeAt( new string[] { "appname" }, false ); string gameName = ( nodeName != null ) ? nodeName.NodeString : null; // The ID of the created game must be negative int newId = -( gameId + 1 ); // This should never happen, but just in case if( Games.ContainsKey( newId ) ) { return false; } //Create the new GameInfo GameInfo game = new GameInfo( newId, gameName, this ); Games.Add( newId, game ); // Fill in the LaunchString game.LaunchString = launchIds[gameName]; // Fill in categories VdfFileNode tagsNode = gameNode.GetNodeAt( new string[] { "tags" }, false ); foreach( KeyValuePair<string, VdfFileNode> tag in tagsNode.NodeArray ) { string tagName = tag.Value.NodeString; game.AddCategory( this.GetCategory( tagName ) ); } // Fill in Hidden game.Hidden = false; if( gameNode.ContainsKey( "hidden" ) ) { VdfFileNode hiddenNode = gameNode["hidden"]; game.Hidden = ( hiddenNode.NodeString == "1" || hiddenNode.NodeInt == 1 ); } return true; }
public static AppInfo FromVdfNode( VdfFileNode commonNode ) { if( commonNode == null || commonNode.NodeType != ValueType.Array ) return null; AppInfo result = null; VdfFileNode idNode = commonNode.GetNodeAt( new string[] { "gameid" }, false ); int id = -1; if( idNode != null ) { if( idNode.NodeType == ValueType.Int ) { id = idNode.NodeInt; } else if( idNode.NodeType == ValueType.String ) { if( !int.TryParse( idNode.NodeString, out id ) ) { id = -1; } } } if( id >= 0 ) { // Get name string name = null; VdfFileNode nameNode = commonNode.GetNodeAt( new string[] { "name" }, false ); if( nameNode != null ) name = nameNode.NodeData.ToString(); // Get type string typeStr = null; AppTypes type = AppTypes.Unknown; VdfFileNode typeNode = commonNode.GetNodeAt( new string[] { "type" }, false ); if( typeNode != null ) typeStr = typeNode.NodeData.ToString(); if( typeStr != null ) { if( !Enum.TryParse<AppTypes>( typeStr, true, out type ) ) { type = AppTypes.Other; } } // Get platforms string oslist = null; AppPlatforms platforms = AppPlatforms.None; VdfFileNode oslistNode = commonNode.GetNodeAt( new string[] { "oslist" }, false ); if( oslistNode != null ) { oslist = oslistNode.NodeData.ToString(); if( oslist.IndexOf( "windows", StringComparison.OrdinalIgnoreCase ) != -1 ) { platforms |= AppPlatforms.Windows; } if( oslist.IndexOf( "mac", StringComparison.OrdinalIgnoreCase ) != -1 ) { platforms |= AppPlatforms.Mac; } if( oslist.IndexOf( "linux", StringComparison.OrdinalIgnoreCase ) != -1 ) { platforms |= AppPlatforms.Linux; } } result = new AppInfo( id, name, type, platforms ); // Get parent VdfFileNode parentNode = commonNode.GetNodeAt( new string[] { "parent" }, false ); if( parentNode != null ) { result.Parent = parentNode.NodeInt; } } return result; }
/// <summary> /// Writes Steam game category information to Steam user config file. /// </summary> /// <param name="filePath">Full path of the steam config file to save</param> /// <param name="discardMissing">If true, any pre-existing game entries in the file that do not have corresponding entries in the GameList are removed</param> public void ExportSteamConfigFile( string filePath, bool discardMissing ) { Program.Logger.Write( LoggerLevel.Info, GlobalStrings.GameData_SavingSteamConfigFile, filePath ); VdfFileNode fileData = new VdfFileNode(); try { using( StreamReader reader = new StreamReader( filePath, false ) ) { fileData = VdfFileNode.LoadFromText( reader, true ); } } catch( Exception e ) { Program.Logger.Write( LoggerLevel.Warning, GlobalStrings.GameData_LoadingErrorSteamConfig, e.Message ); } VdfFileNode appListNode = fileData.GetNodeAt( new string[] { "Software", "Valve", "Steam", "apps" }, true ); // Run through all Delete category data for any games not found in the GameList if( discardMissing ) { Dictionary<string, VdfFileNode> gameNodeArray = appListNode.NodeArray; if( gameNodeArray != null ) { foreach( KeyValuePair<string, VdfFileNode> pair in gameNodeArray ) { int gameId; if( !( int.TryParse( pair.Key, out gameId ) && Games.ContainsKey( gameId ) ) ) { Program.Logger.Write( LoggerLevel.Verbose, GlobalStrings.GameData_RemovingGameCategoryFromSteamConfig, gameId ); pair.Value.RemoveSubnode( "tags" ); } } } } // Force appListNode to be an array, we can't do anything if it's a value appListNode.MakeArray(); foreach( GameInfo game in Games.Values ) { if( game.Id > 0 ) { // External games have negative identifier Program.Logger.Write( LoggerLevel.Verbose, GlobalStrings.GameData_AddingGameToConfigFile, game.Id ); VdfFileNode gameNode = (VdfFileNode)appListNode[game.Id.ToString()]; gameNode.MakeArray(); VdfFileNode tagsNode = (VdfFileNode)gameNode["tags"]; tagsNode.MakeArray(); Dictionary<string, VdfFileNode> tags = tagsNode.NodeArray; if( tags != null ) tags.Clear(); int key = 0; foreach( Category c in game.Categories ) { string name = c.Name; if (name == FAVORITE_NEW_CONFIG_VALUE) name = FAVORITE_CONFIG_VALUE; tagsNode[key.ToString()] = new VdfFileNode( name ); key++; } if( game.Hidden ) { gameNode["hidden"] = new VdfFileNode( "1" ); } else { gameNode.RemoveSubnode( "hidden" ); } } } Program.Logger.Write( LoggerLevel.Verbose, GlobalStrings.GameData_CleaningUpSteamConfigTree ); appListNode.CleanTree(); Program.Logger.Write( LoggerLevel.Info, GlobalStrings.GameData_WritingToDisk ); VdfFileNode fullFile = new VdfFileNode(); fullFile["UserLocalConfigStore"] = fileData; try { Utility.BackupFile( filePath, Settings.Instance.ConfigBackupCount ); } catch( Exception e ) { Program.Logger.Write( LoggerLevel.Error, GlobalStrings.Log_GameData_ConfigBackupFailed, e.Message ); } try { string filePathTmp = filePath + ".tmp"; FileInfo f = new FileInfo( filePathTmp ); f.Directory.Create(); FileStream fStream = f.Open( FileMode.Create, FileAccess.Write, FileShare.None ); using( StreamWriter writer = new StreamWriter( fStream ) ) { fullFile.SaveAsText( writer ); } fStream.Close(); File.Delete( filePath ); File.Move( filePathTmp, filePath ); } catch( ArgumentException e ) { Program.Logger.Write( LoggerLevel.Error, GlobalStrings.GameData_ErrorSavingSteamConfigFile, e.ToString() ); throw new ApplicationException( GlobalStrings.GameData_FailedToSaveSteamConfigBadPath, e ); } catch( IOException e ) { Program.Logger.Write( LoggerLevel.Error, GlobalStrings.GameData_ErrorSavingSteamConfigFile, e.ToString() ); throw new ApplicationException( GlobalStrings.GameData_FailedToSaveSteamConfigFile + e.Message, e ); } catch( UnauthorizedAccessException e ) { Program.Logger.Write( LoggerLevel.Error, GlobalStrings.GameData_ErrorSavingSteamConfigFile, e.ToString() ); throw new ApplicationException( GlobalStrings.GameData_AccessDeniedSteamConfigFile + e.Message, e ); } }
/// <summary> /// Integrate external games defined by Steam user. Only external games with identifier in screenshot.vdf file are included in game DB. /// </summary> /// <param name="SteamId">Identifier of Steam user</param> /// <param name="overWrite">Overwrite actual contents of game DB</param> /// <param name="ignore">List of identifiers of games to be ignored</param> /// <param name="newItems">Returns number of new games integrated</param> /// <returns>Returns number of external games located</returns> public int IntegrateNonSteamGameList(long SteamId, bool overWrite, SortedSet <int> ignore, out int newItems, out int removedItems) { newItems = 0; removedItems = 0; if (SteamId <= 0) { return(0); } int loadedGames = 0; Dictionary <string, int> shortcutgames; if (LoadShortcutGames(SteamId, out shortcutgames)) { string filePath = string.Format(Properties.Resources.ShortCutsFilePath, Settings.Instance().SteamPath, Profile.ID64toDirName(SteamId)); FileStream fStream = null; BinaryReader binReader = null; try { fStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); binReader = new BinaryReader(fStream); BinaryVdfFileNode dataRoot = BinaryVdfFileNode.Load(binReader); VdfFileNode shortcutsNode = dataRoot.GetNodeAt(new string[] { "shortcuts" }, false); if (shortcutsNode != null) { foreach (KeyValuePair <string, VdfFileNode> shortcutPair in shortcutsNode.NodeArray) { //string indexGame = shortcutPair.Key; VdfFileNode attrGame = shortcutPair.Value; VdfFileNode appGame = attrGame.GetNodeAt(new string[] { "appname" }, false); string gameName = appGame.NodeString; // Check if external game has identifier in screenshots.vdf file (this happens only if game has been launched before from Steam client) if (shortcutgames.ContainsKey(gameName)) { bool isNew; if (IntegrateGame(shortcutgames[gameName], gameName, overWrite, ignore, false, out isNew)) { loadedGames++; if (isNew) { newItems++; } } shortcutgames.Remove(gameName); } } } // Remove external games which have been deleted from Steam client foreach (KeyValuePair <string, int> shortcutpair in shortcutgames) { if (RemoveGame(shortcutpair.Value)) { removedItems++; } } } catch (FileNotFoundException e) { Program.Logger.Write(LoggerLevel.Error, GlobalStrings.GameData_ErrorOpeningConfigFileParam, e.ToString()); //throw new ApplicationException(string.Format(GlobalStrings.GameData_ErrorOpeningConfigFileParam, filePath) + e.Message, e); } catch (IOException e) { Program.Logger.Write(LoggerLevel.Error, GlobalStrings.GameData_LoadingErrorSteamConfig, e.ToString()); } catch (ParseException e) { Program.Logger.Write(LoggerLevel.Error, e.ToString()); } finally { if (binReader != null) { binReader.Close(); } if (fStream != null) { fStream.Close(); } } } Program.Logger.Write(LoggerLevel.Info, GlobalStrings.GameData_IntegratedShortCuts, loadedGames, newItems, removedItems); return(loadedGames); }
/// <summary> /// Loads in games from an node containing a list of games. /// </summary> /// <param name="appsNode">Node containing the game nodes</param> /// <param name="ignore">Set of games to ignore</param> /// <returns>Number of games loaded</returns> private int GetDataFromVdf(VdfFileNode appsNode, SortedSet <int> ignore, bool ignoreDlc) { int loadedGames = 0; Dictionary <string, VdfFileNode> gameNodeArray = appsNode.NodeArray; if (gameNodeArray != null) { foreach (KeyValuePair <string, VdfFileNode> gameNodePair in gameNodeArray) { int gameId; if (int.TryParse(gameNodePair.Key, out gameId)) { if ((ignore != null && ignore.Contains(gameId)) || (ignoreDlc && Program.GameDB.IsDlc(gameId))) { Program.Logger.Write(LoggerLevel.Verbose, GlobalStrings.GameData_SkippedProcessingGame, gameId); continue; } if (gameNodePair.Value != null && gameNodePair.Value.ContainsKey("tags")) { Category cat = null; bool fav = false; loadedGames++; VdfFileNode tagsNode = gameNodePair.Value["tags"]; Dictionary <string, VdfFileNode> tagArray = tagsNode.NodeArray; if (tagArray != null) { foreach (VdfFileNode tag in tagArray.Values) { string tagName = tag.NodeString; if (tagName != null) { if (tagName == "favorite") { fav = true; } else { cat = GetCategory(tagName); } } } } if (!Games.ContainsKey(gameId)) { Game newGame = new Game(gameId, string.Empty); Games.Add(gameId, newGame); newGame.Name = Program.GameDB.GetName(gameId); Program.Logger.Write(LoggerLevel.Verbose, GlobalStrings.GameData_AddedNewGame, gameId, newGame.Name); } Games[gameId].Category = cat; Games[gameId].Favorite = fav; Program.Logger.Write(LoggerLevel.Verbose, GlobalStrings.GameData_ProcessedGame, gameId, (cat == null) ? "~none~" : cat.ToString(), fav); } } } } return(loadedGames); }
/// <summary> /// Writes category info for shortcut games to shortcuts.vdf config file for specified Steam user. /// </summary> /// <param name="SteamId">Identifier of Steam user to save information</param> /// <param name="discardMissing">If true, category information in shortcuts.vdf file is removed if game is not in Game list</param> private void SaveShortcutGames(long SteamId, bool discardMissing) { string screenshotsFilePath = string.Format(Properties.Resources.ScreenshotsFilePath, Settings.Instance().SteamPath, Profile.ID64toDirName(SteamId)); Program.Logger.Write(LoggerLevel.Info, GlobalStrings.GameData_SavingSteamConfigFile, screenshotsFilePath); Dictionary <string, int> shortcutgames; if (LoadShortcutGames(SteamId, out shortcutgames)) { string filePath = string.Format(Properties.Resources.ShortCutsFilePath, Settings.Instance().SteamPath, Profile.ID64toDirName(SteamId)); FileStream fStream = null; BinaryReader binReader = null; BinaryVdfFileNode dataRoot = null; try { fStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); binReader = new BinaryReader(fStream); dataRoot = BinaryVdfFileNode.Load(binReader); } catch (FileNotFoundException e) { Program.Logger.Write(LoggerLevel.Error, GlobalStrings.GameData_ErrorOpeningConfigFileParam, e.ToString()); } catch (IOException e) { Program.Logger.Write(LoggerLevel.Error, GlobalStrings.GameData_LoadingErrorSteamConfig, e.ToString()); } if (binReader != null) { binReader.Close(); } if (fStream != null) { fStream.Close(); } if (dataRoot != null) { List <KeyValuePair <string, int>?> listShortCutGames = new List <KeyValuePair <string, int>?>(); VdfFileNode appsNode = dataRoot.GetNodeAt(new string[] { "shortcuts" }, false); foreach (KeyValuePair <string, VdfFileNode> shortcutPair in appsNode.NodeArray) { VdfFileNode attrGame = shortcutPair.Value; VdfFileNode appGame = attrGame.GetNodeAt(new string[] { "appname" }, false); string gameName = appGame.NodeString; // Check if external game has identifier in screenshots.vdf file (this happens only if game has been launched before from Steam client) if (shortcutgames.ContainsKey(gameName)) { VdfFileNode tagsNode = attrGame.GetNodeAt(new string[] { "tags" }, false); int idGame = shortcutgames[gameName]; if (Games.ContainsKey(idGame)) { Program.Logger.Write(LoggerLevel.Verbose, GlobalStrings.GameData_AddingGameToConfigFile, idGame); tagsNode.NodeArray.Clear(); Game game = Games[idGame]; if ((game.Category != null) || (game.Favorite)) { int index = 0; if (game.Category != null) { tagsNode.NodeArray.Add(index.ToString(), new BinaryVdfFileNode(game.Category.Name)); index++; } if (game.Favorite) { tagsNode.NodeArray.Add(index.ToString(), new BinaryVdfFileNode("favorite")); } } } else if (discardMissing) { Program.Logger.Write(LoggerLevel.Verbose, GlobalStrings.GameData_RemovingGameCategoryFromSteamConfig, idGame); tagsNode.NodeArray.Clear(); } } } if (dataRoot.NodeType == ValueType.Array) { Program.Logger.Write(LoggerLevel.Info, GlobalStrings.GameData_SavingShortcutConfigFile, filePath); BinaryWriter binWriter; try { fStream = new FileStream(filePath, FileMode.Truncate, FileAccess.ReadWrite, FileShare.ReadWrite); binWriter = new BinaryWriter(fStream); dataRoot.Save(binWriter); binWriter.Close(); fStream.Close(); } catch (ArgumentException e) { Program.Logger.Write(LoggerLevel.Error, GlobalStrings.GameData_ErrorSavingSteamConfigFile, e.ToString()); throw new ApplicationException(GlobalStrings.GameData_FailedToSaveSteamConfigBadPath, e); } catch (IOException e) { Program.Logger.Write(LoggerLevel.Error, GlobalStrings.GameData_ErrorSavingSteamConfigFile, e.ToString()); throw new ApplicationException(GlobalStrings.GameData_FailedToSaveSteamConfigFile + e.Message, e); } catch (UnauthorizedAccessException e) { Program.Logger.Write(LoggerLevel.Error, GlobalStrings.GameData_ErrorSavingSteamConfigFile, e.ToString()); throw new ApplicationException(GlobalStrings.GameData_AccessDeniedSteamConfigFile + e.Message, e); } } } } }
/// <summary> /// Loads a FileNode from stream. /// </summary> /// <param name="stream">Stream to load from</param> /// <returns>FileNode representing the contents of the stream.</returns> public static VdfFileNode LoadFromBinary( BinaryReader stream, long streamLength = -1 ) { if( streamLength == -1 ) streamLength = stream.BaseStream.Length; if( stream.BaseStream.Position == streamLength ) return null; VdfFileNode thisLevel = new VdfFileNode(); bool endOfStream = false; while( !endOfStream ) { byte nextByte; try { nextByte = stream.ReadByte(); } catch( EndOfStreamException ) { endOfStream = true; nextByte = 8; } // Get key string key = null; if( endOfStream || nextByte == 8 || stream.BaseStream.Position == streamLength ) { break; } else if( nextByte == 0 ) { key = ReadBin_GetStringToken( stream ); VdfFileNode newNode; newNode = LoadFromBinary( stream, streamLength ); thisLevel[key] = newNode; } else if( nextByte == 1 ) { key = ReadBin_GetStringToken( stream ); thisLevel[key] = new VdfFileNode( ReadBin_GetStringToken( stream ) ); } else if( nextByte == 2 ) { key = ReadBin_GetStringToken( stream ); int val = stream.ReadInt32(); thisLevel[key] = new VdfFileNode( val ); } else if( nextByte == 7 ) { key = ReadBin_GetStringToken( stream ); ulong val = stream.ReadUInt64(); thisLevel[key] = new VdfFileNode( val ); } else if( nextByte == 0xFF ) { return null; } else { throw new ParseException( string.Format( GlobalStrings.TextVdfFile_UnexpectedCharacterKey, nextByte.ToString() ) ); } } return thisLevel; }
/// <summary> /// Loads in games from an node containing a list of games. /// </summary> /// <param name="appsNode">Node containing the game nodes</param> /// <param name="ignore">Set of games to ignore</param> /// <returns>Number of games loaded</returns> private int GetDataFromVdf(VdfFileNode appsNode, SortedSet<int> ignore, bool ignoreDlc ) { int loadedGames = 0; Dictionary<string, VdfFileNode> gameNodeArray = appsNode.NodeArray; if( gameNodeArray != null ) { foreach( KeyValuePair<string, VdfFileNode> gameNodePair in gameNodeArray ) { int gameId; if( int.TryParse( gameNodePair.Key, out gameId ) ) { if( ( ignore != null && ignore.Contains( gameId ) ) || ( ignoreDlc && Program.GameDB.IsDlc( gameId ) ) ) { Program.Logger.Write(LoggerLevel.Verbose, GlobalStrings.GameData_SkippedProcessingGame, gameId); continue; } if( gameNodePair.Value != null && gameNodePair.Value.ContainsKey( "tags" ) ) { Category cat = null; bool fav = false; loadedGames++; VdfFileNode tagsNode = gameNodePair.Value["tags"]; Dictionary<string, VdfFileNode> tagArray = tagsNode.NodeArray; if( tagArray != null ) { foreach( VdfFileNode tag in tagArray.Values ) { string tagName = tag.NodeString; if( tagName != null ) { if( tagName == "favorite" ) { fav = true; } else { cat = GetCategory( tagName ); } } } } if( !Games.ContainsKey( gameId ) ) { Game newGame = new Game( gameId, string.Empty ); Games.Add( gameId, newGame ); newGame.Name = Program.GameDB.GetName( gameId ); Program.Logger.Write(LoggerLevel.Verbose, GlobalStrings.GameData_AddedNewGame, gameId, newGame.Name); } Games[gameId].Category = cat; Games[gameId].Favorite = fav; Program.Logger.Write(LoggerLevel.Verbose, GlobalStrings.GameData_ProcessedGame, gameId, (cat == null) ? "~none~" : cat.ToString(), fav); } } } } return loadedGames; }