mirror of
https://github.com/vrcx-team/VRCX.git
synced 2026-04-06 00:32:02 +02:00
* init * SQLite changes * Move html folder, edit build scripts * AppApi interface * Build flags * AppApi inheritance * Finishing touches * Merge upstream changes * Test CI * Fix class inits * Rename AppApi * Merge upstream changes * Fix SQLiteLegacy on Linux, Add Linux interop, build tools * Linux specific localisation strings * Make it run * Bring back most of Linux functionality * Clean up * Fix TTS voices * Fix UI var * Changes * Electron minimise to tray * Remove separate toggle for WlxOverlay * Fixes * Touchups * Move csproj * Window zoom, Desktop Notifications, VR check on Linux * Fix desktop notifications, VR check spam * Fix building on Linux * Clean up * Fix WebApi headers * Rewrite VRCX updater * Clean up * Linux updater * Add Linux to build action * init * SQLite changes * Move html folder, edit build scripts * AppApi interface * Build flags * AppApi inheritance * Finishing touches * Merge upstream changes * Test CI * Fix class inits * Rename AppApi * Merge upstream changes * Fix SQLiteLegacy on Linux, Add Linux interop, build tools * Linux specific localisation strings * Make it run * Bring back most of Linux functionality * Clean up * Fix TTS voices * Changes * Electron minimise to tray * Remove separate toggle for WlxOverlay * Fixes * Touchups * Move csproj * Window zoom, Desktop Notifications, VR check on Linux * Fix desktop notifications, VR check spam * Fix building on Linux * Clean up * Fix WebApi headers * Rewrite VRCX updater * Clean up * Linux updater * Add Linux to build action * Test updater * Rebase and handle merge conflicts * Fix Linux updater * Fix Linux app restart * Fix friend order * Handle AppImageInstaller, show an install message on Linux * Updates to the AppImage installer * Fix Linux updater, fix set version, check for .NET, copy wine prefix * Handle random errors * Rotate tall prints * try fix Linux restart bug * Final --------- Co-authored-by: rs189 <35667100+rs189@users.noreply.github.com>
251 lines
10 KiB
C#
251 lines
10 KiB
C#
// Copyright(c) 2019-2022 pypy, Natsumi and individual contributors.
|
|
// All rights reserved.
|
|
//
|
|
// This work is licensed under the terms of the MIT license.
|
|
// For a copy, see <https://opensource.org/licenses/MIT>.
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Security.Cryptography;
|
|
using System.Net;
|
|
using System.Text;
|
|
using System.Linq;
|
|
using NLog;
|
|
|
|
namespace VRCX
|
|
{
|
|
public class AssetBundleManager
|
|
{
|
|
public static readonly AssetBundleManager Instance;
|
|
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
|
|
|
static AssetBundleManager()
|
|
{
|
|
Instance = new AssetBundleManager();
|
|
}
|
|
|
|
public string GetAssetId(string id, string variant = "")
|
|
{
|
|
using(var sha256 = SHA256.Create())
|
|
{
|
|
byte[] hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(id + variant));
|
|
StringBuilder idHex = new StringBuilder(hash.Length * 2);
|
|
foreach (byte b in hash)
|
|
{
|
|
idHex.AppendFormat("{0:x2}", b);
|
|
}
|
|
return idHex.ToString().ToUpper().Substring(0, 16);
|
|
}
|
|
}
|
|
|
|
public string GetAssetVersion(int version, int variantVersion = 0)
|
|
{
|
|
var versionHex = string.Empty;
|
|
var variantVersionBytes = BitConverter.GetBytes(variantVersion);
|
|
foreach (var b in variantVersionBytes)
|
|
{
|
|
versionHex += b.ToString("X2");
|
|
}
|
|
var versionBytes = BitConverter.GetBytes(version);
|
|
foreach (var b in versionBytes)
|
|
{
|
|
versionHex += b.ToString("X2");
|
|
}
|
|
|
|
return versionHex.PadLeft(32, '0');
|
|
}
|
|
|
|
public (int, int) ReverseHexToDecimal(string hexString)
|
|
{
|
|
if (hexString.Length != 32)
|
|
return (0, 0); // it's cooked
|
|
|
|
try {
|
|
var variantVersionHexString = hexString.Substring(0, 8); // 0..8
|
|
var versionHexString = hexString.Substring(24, 8); // 24..32
|
|
var versionBytes = new byte[4];
|
|
var variantVersionBytes = new byte[4];
|
|
for (var i = 0; i < 4; i++)
|
|
{
|
|
var versionValue = Convert.ToInt32(versionHexString.Substring(i * 2, 2), 16);
|
|
versionBytes[i] = (byte)versionValue;
|
|
var variantVersionValue = Convert.ToInt32(variantVersionHexString.Substring(i * 2, 2), 16);
|
|
variantVersionBytes[i] = (byte)variantVersionValue;
|
|
}
|
|
var version = BitConverter.ToInt32(versionBytes, 0);
|
|
var variantVersion = BitConverter.ToInt32(variantVersionBytes, 0);
|
|
return (version, variantVersion);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
logger.Error($"Failed to convert hex to decimal: {hexString} {ex}");
|
|
return (0, 0); // it's cooked
|
|
}
|
|
}
|
|
|
|
public string GetVRChatCacheLocation()
|
|
{
|
|
return Program.AppApiInstance.GetVRChatCacheLocation();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the full location of the VRChat cache for a specific asset bundle.
|
|
/// </summary>
|
|
/// <param name="id">The ID of the asset bundle.</param>
|
|
/// <param name="version">The version of the asset bundle.</param>
|
|
/// <param name="variant">The variant of the asset bundle.</param>
|
|
/// <param name="variantVersion">The version of the variant of the asset bundle.</param>
|
|
/// <returns>The full location of the VRChat cache for the specified asset bundle.</returns>
|
|
public string GetVRChatCacheFullLocation(string id, int version, string variant = "", int variantVersion = 0)
|
|
{
|
|
var cachePath = GetVRChatCacheLocation();
|
|
var idHash = GetAssetId(id, variant);
|
|
var topDir = Path.Combine(cachePath, idHash);
|
|
var versionLocation = GetAssetVersion(version, variantVersion);
|
|
if (!Directory.Exists(topDir))
|
|
return Path.Combine(topDir, versionLocation);
|
|
var versionSearchPattern = string.Concat("*", versionLocation.AsSpan(8));
|
|
var dirs = Directory.GetDirectories(topDir, versionSearchPattern);
|
|
if (dirs.Length > 0)
|
|
return dirs.OrderByDescending(dir => ReverseHexToDecimal(Path.GetFileName(dir)).Item2).First();
|
|
|
|
return Path.Combine(topDir, versionLocation);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks the VRChat cache for a specific asset bundle.
|
|
/// </summary>
|
|
/// <param name="id">The ID of the asset bundle.</param>
|
|
/// <param name="version">The version of the asset bundle.</param>
|
|
/// <param name="variant">The variant of the asset bundle.</param>
|
|
/// <param name="variantVersion">The version of the variant of the asset bundle.</param>
|
|
/// <returns>A Tuple containing the file size, lock status and path of the asset bundle.</returns>
|
|
public Tuple<long, bool, string> CheckVRChatCache(string id, int version, string variant, int variantVersion)
|
|
{
|
|
long fileSize = -1;
|
|
var isLocked = false;
|
|
var fullLocation = GetVRChatCacheFullLocation(id, version);
|
|
if (!Directory.Exists(fullLocation))
|
|
fullLocation = GetVRChatCacheFullLocation(id, version, variant, variantVersion);
|
|
|
|
var fileLocation = Path.Combine(fullLocation, "__data");
|
|
var cachePath = string.Empty;
|
|
if (File.Exists(fileLocation))
|
|
{
|
|
cachePath = fullLocation;
|
|
FileInfo data = new FileInfo(fileLocation);
|
|
fileSize = data.Length;
|
|
}
|
|
if (File.Exists(Path.Combine(fullLocation, "__lock")))
|
|
{
|
|
isLocked = true;
|
|
}
|
|
return new Tuple<long, bool, string>(fileSize, isLocked, cachePath);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deletes the cache directory for a specific asset bundle.
|
|
/// </summary>
|
|
/// <param name="id">The ID of the asset bundle to delete.</param>
|
|
/// <param name="version">The version of the asset bundle to delete.</param>
|
|
/// <param name="variant">The variant of the asset bundle to delete.</param>
|
|
/// <param name="variantVersion">The version of the variant of the asset bundle to delete.</param>
|
|
public void DeleteCache(string id, int version, string variant, int variantVersion)
|
|
{
|
|
var path = GetVRChatCacheFullLocation(id, version);
|
|
if (Directory.Exists(path))
|
|
Directory.Delete(path, true);
|
|
|
|
path = GetVRChatCacheFullLocation(id, version, variant, variantVersion);
|
|
if (Directory.Exists(path))
|
|
Directory.Delete(path, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deletes the entire VRChat cache directory.
|
|
/// </summary>
|
|
public void DeleteAllCache()
|
|
{
|
|
var cachePath = GetVRChatCacheLocation();
|
|
if (Directory.Exists(cachePath))
|
|
{
|
|
Directory.Delete(cachePath, true);
|
|
Directory.CreateDirectory(cachePath);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes empty directories from the VRChat cache directory and deletes old versions of cached asset bundles.
|
|
/// </summary>
|
|
public List<string> SweepCache()
|
|
{
|
|
var output = new List<string>();
|
|
var cachePath = GetVRChatCacheLocation();
|
|
if (!Directory.Exists(cachePath))
|
|
return output;
|
|
var directories = new DirectoryInfo(cachePath);
|
|
var cacheDirectories = directories.GetDirectories();
|
|
foreach (var cacheDirectory in cacheDirectories)
|
|
{
|
|
// var versionDirectories = cacheDirectory.GetDirectories().OrderBy(d => ReverseHexToDecimal(d.Name)).ToArray();
|
|
var versionDirectories =
|
|
cacheDirectory.GetDirectories().OrderBy(d => d.LastWriteTime).ToArray();
|
|
for (var index = 0; index < versionDirectories.Length; index++)
|
|
{
|
|
var versionDirectory = versionDirectories[index];
|
|
if (versionDirectory.GetDirectories().Length + versionDirectory.GetFiles().Length == 0)
|
|
{
|
|
versionDirectory.Delete(); // delete empty directory
|
|
continue;
|
|
}
|
|
|
|
if (index == versionDirectories.Length - 1)
|
|
continue; // skip last version
|
|
|
|
if (File.Exists(Path.Combine(versionDirectory.FullName, "__lock")))
|
|
continue; // skip locked version
|
|
|
|
versionDirectory.Delete(true);
|
|
output.Add($"{cacheDirectory.Name}\\{versionDirectory.Name}");
|
|
}
|
|
|
|
if (cacheDirectory.GetDirectories().Length + cacheDirectory.GetFiles().Length == 0)
|
|
cacheDirectory.Delete(); // delete empty directory
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the size of the VRChat cache directory in bytes.
|
|
/// </summary>
|
|
/// <returns>The size of the VRChat cache directory in bytes.</returns>
|
|
public long GetCacheSize()
|
|
{
|
|
var cachePath = GetVRChatCacheLocation();
|
|
|
|
if (!Directory.Exists(cachePath)) return 0;
|
|
|
|
return DirSize(new DirectoryInfo(cachePath));
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Recursively calculates the size of a directory and all its subdirectories.
|
|
/// </summary>
|
|
/// <param name="d">The directory to calculate the size of.</param>
|
|
/// <returns>The size of the directory and all its subdirectories in bytes.</returns>
|
|
public long DirSize(DirectoryInfo d)
|
|
{
|
|
long size = 0;
|
|
FileInfo[] files = d.GetFiles("*.*", SearchOption.AllDirectories);
|
|
foreach (FileInfo file in files)
|
|
{
|
|
size += file.Length;
|
|
}
|
|
return size;
|
|
}
|
|
}
|
|
} |