HaloSPV3/HXE

fix: Steam update changed path of LibraryFolders.vdf, breaking our Search Steam Library feature

BinToss opened this issue · 1 comments

Any users who installed MCC (Steam) before this Steam update will be unaffected by this issue.

See HXE.Paths.Steam and HXE.Steam.Libraries at "src/Paths.cs" and "src/Steam/Libraries.cs", respectively.

Solution 1: Update HXE.Paths.Steam.Libraries to current path

  • Implementation Time: negligible
  • Performance Cost: unchanged

public static string Libraries = Combine(Directory, "steamapps", "libraryfolders.vdf");

-            public static string Libraries = Combine(Directory, "steamapps", "libraryfolders.vdf");
+            public static string Libraries = Combine(Directory, "config", "libraryfolders.vdf");

Solution 2: Support both the prior path and current path

If the new path fails, try the old one.

  • Implementation Time: A few hours when including debug and polish
  • Performance Cost: negligible

public static string Libraries = Combine(Directory, "steamapps", "libraryfolders.vdf");

-            public static string Libraries = Combine(Directory, "steamapps", "libraryfolders.vdf");
+            public static string LibrariesOld = Combine(Directory, "steamapps", "libraryfolders.vdf");
+            public static string Libraries = Combine(Directory, "config", "libraryfolders.vdf");

Replace

if (!LibFoldersVdf.Exists() || LibFoldersVdf.Path == null)
throw new FileNotFoundException("Steam Library list not found.");

in
/// <summary>
/// Create an object representing LibraryFolders.vdf on the file system.
/// Local functions will read the contents of this file object.
/// </summary>
public File LibFoldersVdf = (File) Paths.Steam.Libraries;
public List<string> LibList = new List<string>();
public List<string> ReturnPaths = new List<string>();
/// <summary>
/// Read Steam's "libraryfolders.vdf" and assign the library folders to an index array.
/// </summary>
public void ParseLibraries()
{
if (!LibFoldersVdf.Exists() || LibFoldersVdf.Path == null)
throw new FileNotFoundException("Steam Library list not found.");
try
{
var text = LibFoldersVdf.ReadAllText();
List<string> libs = text.Split(new char[] { '\"' }, System.StringSplitOptions.RemoveEmptyEntries).ToList();
libs = libs.Where(line => line.Contains(":")).ToList();
foreach (string line in libs)
{
LibList.Add(line.Replace("\\\\", "\\"));
}
}
catch (System.Exception e)
{
throw new System.Exception("Failed to Parse Steam Libraries file", e);
}
}

with

            if (LibFoldersVdf.Path == null)
            {
                var msg = "Failed to infer the path of Steam's LibraryFolders.vdf." + '\n'
                + " This may occur if the variable storing the path of Steam's directory is null or empty." + '\n'
                + $"The value of HXE.Paths.Steam.Directory is currently '{HXE.Paths.Steam.Directory}'.";
                throw new NullArgumentException(msg);
            }
            else if (!LibFoldersVdf.Exists())
            {
                var librariesOld = (File) Paths.Steam.LibariesOld;
                if (librariesOld.Exists())
                    LibFoldersVdf = librariesOld;
                else throw new FileNotFoundException($"Steam's library list 'LibraryFolders.vdf' not found at either '{HXE.Paths.Steam.Libraries}' or '{HXE.Paths.Steam.LibrariesOld}'.");
            }

Solution 3: Support prior, current, and future file paths

If libraryfolders.vdf cannot be found via Solution 2, search the entirety of Steam's directory for libraryfolders.vdf.

  • Implementation Time: More than twice the time required to implement Solution 2
  • Performance Cost: Up to a minute of waiting depending on the file count. This will require a new GUI feature to display progress or (at the very least) a message to explain what the app is doing in the background and why it will take a significant amount of time to complete.

It would be more performance-efficient to search multiple directories simultaneously at the cost of increased implementation time.

  1. Get the CPU logical core count.
  2. var MaxConcurrentSearches = CPULogicalCoreCount
  3. Get a list of filesystem items. Check multiple directories in parallel. Use MaxConcurrentSearches.
    var matches = GetSearchItems(path).Where(x => x.Path.EndsWith("LibraryFolders.vdf")))
List<SearchItem> GetSearchItems(string basePath, bool recursive = false, string searchTerm = null, FilterType filterType = 0)
{
    if (basePath.IsNullOrEmpty())
        throw new NullArgumentException("GetSearchItems() was used without specifying where to search.");

    /** variables */
    List<SearchItem> searchItems = new();

    /** factory */
    // add all non-directory, non-link items to searchItems
    // if searchTerm != null, only add an item to searchItems if the its path Contains/EndsWith/StartsWith the searchTerm.
    // use Switch-Case statement or separate Methods for each FilterType.

    return searchItems;
}

enum FilterType
{
    Contains = 0,
    EndsWith = 1,
    StartsWith = 2
}

class SearchItem
{
    string Path;
    Status Progress;
    enum Status
    {
        ToSearch = 0,
        Searching = 1,
        Searched = 2
    }
}

Solution 4: Fuzzy Search

If Solution 3 fails, check for files containing paths.
Check those paths for Steam Library characteristics.
Populate LibList with discovered Libraries.

Should we write discovered paths to our own Steam Libs file?

No. We have no plans that could make use of it.

.NET supports recursive searches. I'll let it handle the complicated stuff.
Incoming...