I'll make this modular some day
This commit is contained in:
parent
f3860d0ffe
commit
a92e50dc99
3 changed files with 187 additions and 27 deletions
11
Config.cs
Normal file
11
Config.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace ResoniteCacheCleaner
|
||||
{
|
||||
public class Config
|
||||
{
|
||||
// Delete older than this access time
|
||||
public int AccessTimeDays = 30;
|
||||
|
||||
// DataPath
|
||||
public string DataPath;
|
||||
}
|
||||
}
|
|
@ -2,46 +2,71 @@ 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)
|
||||
{
|
||||
/* set data/cache paths */
|
||||
// CHECK LINUX, SET Config.DataPath
|
||||
string UserPath;
|
||||
string DataPath;
|
||||
string CachePath;
|
||||
bool IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
|
||||
CachePathResolver CachePathHandler = CachePathResolverWindows;
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
|
||||
if (IsLinux) {
|
||||
Console.Error.WriteLine("On Linux, assuming Proton path.");
|
||||
UserPath = Environment.GetEnvironmentVariable("HOME")
|
||||
+ "/.steam/steam/steamapps/compatdata/2519830/pfx/drive_c/users/steamuser";
|
||||
CachePath = Path.Combine(UserPath, "Temp");
|
||||
ProtonRealRoot = Environment.GetEnvironmentVariable("HOME")
|
||||
+ "/.steam/steam/steamapps/compatdata/2519830/pfx/drive_c";
|
||||
UserPath = ProtonRealRoot + "/users/steamuser";
|
||||
CachePathHandler = CachePathResolverLinux;
|
||||
} else {
|
||||
UserPath = Environment.GetEnvironmentVariable("USERPROFILE");
|
||||
CachePath = Environment.GetEnvironmentVariable("TMP") ?? Path.Combine(UserPath, "Temp");
|
||||
}
|
||||
|
||||
string[] _s = { UserPath, "AppData", "LocalLow", "Yellow Dog Man Studios", "Resonite" };
|
||||
DataPath = Path.Combine(_s);
|
||||
CachePath = Path.Combine(CachePath, "Yellow Dog Man Studios", "Resonite", "Cache");
|
||||
if (!Directory.Exists(DataPath)) {
|
||||
Console.Error.WriteLine("DataPath not found. Try manually specifying it with -DataPath");
|
||||
Environment.Exit(1);
|
||||
}
|
||||
if (!Directory.Exists(CachePath)) {
|
||||
Console.Error.WriteLine("CachePath not found. Try manually specifying it with -CachePath");
|
||||
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: " + DataPath);
|
||||
Console.Error.WriteLine("CachePath: " + CachePath);
|
||||
/* end data/cache paths */
|
||||
Console.Error.WriteLine("DataPath: " + Config.DataPath);
|
||||
|
||||
/* get machine id */
|
||||
string LocalKeyPath = Path.Combine(DataPath, "LocalKey.bin");
|
||||
// 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);
|
||||
|
@ -49,18 +74,94 @@ namespace ResoniteCacheCleaner
|
|||
string MachineID = FrooxHelper.GetMachineID(LocalKeyPath);
|
||||
Console.Error.WriteLine("Machine ID: " + MachineID);
|
||||
|
||||
/* get database */
|
||||
string DatabasePath = Path.Combine(DataPath, "Data.litedb");
|
||||
// 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);
|
||||
}
|
||||
|
||||
/* establish connection to db */
|
||||
// CONNECT TO DATABASE
|
||||
string ConnStr = FrooxHelper.ProcessConnection("filename=" + DatabasePath + ";", MachineID);
|
||||
Console.Error.WriteLine(ConnStr);
|
||||
LiteDatabase DB = new LiteDatabase(ConnStr);
|
||||
var RecordCollection = DB.GetCollection("Records");
|
||||
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);
|
||||
|
||||
|
|
48
Util.cs
Normal file
48
Util.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace ResoniteCacheCleaner
|
||||
{
|
||||
internal class Util
|
||||
{
|
||||
|
||||
// surprised this isn't built in to c# given how much stuff c# has
|
||||
// https://stackoverflow.com/a/11124118
|
||||
public static string BytesToString(long value)
|
||||
{
|
||||
string suffix;
|
||||
double readable;
|
||||
switch (Math.Abs(value))
|
||||
{
|
||||
case >= 0x1000000000000000:
|
||||
suffix = "EiB";
|
||||
readable = value >> 50;
|
||||
break;
|
||||
case >= 0x4000000000000:
|
||||
suffix = "PiB";
|
||||
readable = value >> 40;
|
||||
break;
|
||||
case >= 0x10000000000:
|
||||
suffix = "TiB";
|
||||
readable = value >> 30;
|
||||
break;
|
||||
case >= 0x40000000:
|
||||
suffix = "GiB";
|
||||
readable = value >> 20;
|
||||
break;
|
||||
case >= 0x100000:
|
||||
suffix = "MiB";
|
||||
readable = value >> 10;
|
||||
break;
|
||||
case >= 0x400:
|
||||
suffix = "KiB";
|
||||
readable = value;
|
||||
break;
|
||||
default:
|
||||
return value.ToString("0 B");
|
||||
}
|
||||
|
||||
return (readable / 1024).ToString("0.## ", CultureInfo.InvariantCulture) + suffix;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue