170 lines
5.7 KiB
C#
170 lines
5.7 KiB
C#
using System;
|
|
using System.Runtime.InteropServices;
|
|
using System.IO;
|
|
using LiteDB;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
|
|
namespace ResoniteCacheCleaner
|
|
{
|
|
public class ResoniteCacheCleaner
|
|
{
|
|
public static Config Config = new Config();
|
|
|
|
private static string? ProtonRealRoot; // only useful for linux
|
|
|
|
private delegate string CachePathResolver(string Path);
|
|
|
|
public static LiteDatabase? DB;
|
|
|
|
public static LiteDatabase? NewDB;
|
|
|
|
private static string CachePathResolverLinux(string _path)
|
|
{
|
|
return ProtonRealRoot + _path.Substring(2).Replace('\\', '/');
|
|
}
|
|
|
|
private static string CachePathResolverWindows(string _path)
|
|
{
|
|
return _path;
|
|
}
|
|
|
|
public static List<T> DatabaseExtractAllEntries<T>(LiteDatabase db, string collectionName, Func<T, string>? uniqueKeySelector = null)
|
|
{
|
|
ILiteCollection<T> coll = db.GetCollection<T>(collectionName);
|
|
HashSet<string> uniqueKeys = new HashSet<string>();
|
|
List<T> list = coll.FindAll().ToList();
|
|
if (uniqueKeySelector != null) {
|
|
list.RemoveAll((T e) => !uniqueKeys.Add(uniqueKeySelector(e)));
|
|
}
|
|
return list;
|
|
}
|
|
|
|
static void Main(string[] args)
|
|
{
|
|
// CHECK LINUX, SET Config.DataPath
|
|
string UserPath;
|
|
bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
|
|
CachePathResolver CachePathHandler = CachePathResolverWindows;
|
|
|
|
if (IsLinux) {
|
|
Console.Error.WriteLine("On Linux, assuming Proton path.");
|
|
ProtonRealRoot = Environment.GetEnvironmentVariable("HOME")
|
|
+ "/.steam/steam/steamapps/compatdata/2519830/pfx/drive_c";
|
|
UserPath = ProtonRealRoot + "/users/steamuser";
|
|
CachePathHandler = CachePathResolverLinux;
|
|
} else {
|
|
UserPath = Environment.GetEnvironmentVariable("USERPROFILE");
|
|
}
|
|
|
|
Config.DataPath = Path.Combine(new string[5] { UserPath, "AppData", "LocalLow", "Yellow Dog Man Studios", "Resonite" });
|
|
if (!Directory.Exists(Config.DataPath)) {
|
|
Console.Error.WriteLine("Config.DataPath not found. Try manually specifying it with -DataPath");
|
|
Environment.Exit(1);
|
|
}
|
|
|
|
Console.Error.WriteLine("DataPath: " + Config.DataPath);
|
|
|
|
// GET MACHINE ID
|
|
string LocalKeyPath = Path.Combine(Config.DataPath, "LocalKey.bin");
|
|
if (!File.Exists(LocalKeyPath)) {
|
|
Console.Error.WriteLine("No localkey.bin in DataPath. Is it the right DataPath?\nIf so, launch Resonite at least once.");
|
|
Environment.Exit(1);
|
|
}
|
|
string MachineID = FrooxHelper.GetMachineID(LocalKeyPath);
|
|
Console.Error.WriteLine("Machine ID: " + MachineID);
|
|
|
|
// GET DATABASE
|
|
string DatabasePath = Path.Combine(Config.DataPath, "Data2.litedb");
|
|
if (!File.Exists(DatabasePath)) {
|
|
Console.Error.WriteLine("No Data.litedb in DataPath. Is it the right DataPath?");
|
|
Environment.Exit(1);
|
|
}
|
|
|
|
// CONNECT TO DATABASE
|
|
string ConnStr = FrooxHelper.ProcessConnection("filename=" + DatabasePath + ";", MachineID);
|
|
Console.Error.WriteLine(ConnStr);
|
|
DB = new LiteDatabase(ConnStr);
|
|
|
|
// INITIALIZE DATE THRESHOLD
|
|
DateTime DateThreshold = DateTime.Now.AddDays(Config.AccessTimeDays * -1f);
|
|
|
|
// FETCH ASSETS
|
|
Console.WriteLine("Fetching Assets...");
|
|
var _assetReader = DB.Execute("SELECT $._id, $.path FROM Assets");
|
|
|
|
var FlaggedAssetsDel = new List<int>();
|
|
var FlaggedAssetsOld = new List<Tuple<int, string>>();
|
|
long FlaggedAssetsSizeBytes = 0;
|
|
while (_assetReader.Read()) {
|
|
// set ID and Path
|
|
var Asset = (Dictionary<string, BsonValue>)_assetReader.Current.RawValue;
|
|
int AssetID = Asset["_id"].AsInt32;
|
|
string AssetPath = Asset["Path"].AsString;
|
|
|
|
// There are 3 types of assets: Assets, Cache, and PreCache
|
|
// PreCache seems important enough to not touch at all. That can build up fine.
|
|
// Assets and Cache are the two we want to flag for checking
|
|
string[] _splitArr = AssetPath.Split('\\');
|
|
string FullPath;
|
|
if (_splitArr.Length == 1) { // Asset
|
|
FullPath = Path.Combine(Config.DataPath, "Assets", AssetPath);
|
|
} else if (_splitArr[_splitArr.Length - 2] == "Cache") { // Cache
|
|
FullPath = CachePathHandler(AssetPath);
|
|
} else { // PreCache
|
|
continue;
|
|
}
|
|
|
|
if (!File.Exists(FullPath)) {
|
|
FlaggedAssetsDel.Add(AssetID);
|
|
} else if (File.GetLastAccessTime(FullPath) < DateThreshold) {
|
|
var _fi = new FileInfo(FullPath);
|
|
FlaggedAssetsOld.Add(Tuple.Create(AssetID, FullPath));
|
|
FlaggedAssetsSizeBytes += _fi.Length;
|
|
}
|
|
}
|
|
|
|
// USER PROMPT
|
|
Console.WriteLine("Nonexistent files: " + FlaggedAssetsDel.Count);
|
|
Console.WriteLine("Files last accessed more than " + Config.AccessTimeDays + " days ago (will be removed from disk): " + FlaggedAssetsOld.Count);
|
|
Console.WriteLine("Total database entries to delete: " + (FlaggedAssetsDel.Count + FlaggedAssetsOld.Count));
|
|
Console.WriteLine("Estimated Space Saved: " + Util.BytesToString(FlaggedAssetsSizeBytes));
|
|
Console.WriteLine("Continue? [Y]es/[N]o");
|
|
|
|
if (Console.ReadLine().ToLower() != "y") {
|
|
Console.WriteLine("Input not y. Not continuing.");
|
|
Environment.Exit(0);
|
|
}
|
|
|
|
// COMMIT TO DATABASE CLEARING
|
|
DB.Execute("BEGIN");
|
|
try {
|
|
foreach (var _assetID in FlaggedAssetsDel) {
|
|
DB.Execute("DELETE Assets WHERE _id = " + _assetID);
|
|
}
|
|
foreach (var _asset in FlaggedAssetsOld) {
|
|
DB.Execute("DELETE Assets WHERE _id = " + _asset.Item1);
|
|
File.Delete(_asset.Item2);
|
|
}
|
|
} catch (Exception e) {
|
|
DB.Execute("ROLLBACK");
|
|
Console.Error.WriteLine("Exception occured, Database not updated:");
|
|
Console.Error.WriteLine(e.Message);
|
|
Environment.Exit(1);
|
|
}
|
|
DB.Execute("COMMIT");
|
|
|
|
// Console.WriteLine("Database Updated. Rebuilding (this will take a while)...");
|
|
//
|
|
// DatabaseBackup = DatabasePath + ".bak";
|
|
// File.Move(DatabasePath, DatabaseBackup);
|
|
//
|
|
// NewDB = new LiteDatabase(ConnStr);
|
|
//
|
|
// DB.Rebuild();
|
|
|
|
Environment.Exit(0);
|
|
|
|
}
|
|
}
|
|
}
|