Files
sbox-public/engine/Sandbox.Engine/Systems/Filesystem/AssetDownloadCache.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

159 lines
4.3 KiB
C#

using Sandbox.Internal;
using System.Text.RegularExpressions;
namespace Sandbox;
/// <summary>
/// Downloadeded assets go in the ".source2/assets" folder, where they are symlinked on demand
/// for use by the engine.
/// </summary>
static class AssetDownloadCache
{
[ConVar( "debug_network_files", ConVarFlags.Protected )]
internal static bool DebugNetworkFiles { get; set; } = false;
static string cacheFolder;
public static void Initialize( string folder )
{
System.IO.Directory.CreateDirectory( folder );
cacheFolder = folder;
}
internal static RedirectFileSystem CreateRedirectFileSystem()
{
if ( string.IsNullOrEmpty( cacheFolder ) )
return null;
return RedirectFileSystem.Create( cacheFolder );
}
/// <summary>
/// We have downloaded an asset file. Store it for reuse in the future
/// </summary>
public static string StoreFile( string relativePath, ulong crc, byte[] data )
{
var cachePath = CreateCacheFilename( relativePath, crc );
var absPath = System.IO.Path.Combine( cacheFolder, cachePath );
// todo - check crc of data
// make sure the directory tree exists
string directory = System.IO.Path.GetDirectoryName( absPath );
System.IO.Directory.CreateDirectory( directory );
// todo - write with retry?
System.IO.File.WriteAllBytes( absPath, data );
return absPath;
}
/// <summary>
/// Try to mount the downloaded file with this path and crc. If it doesn't exist, return false
/// </summary>
public static bool IsFileDownloaded( string relativePath, ulong crc, out bool isCoreContent )
{
isCoreContent = false;
relativePath = relativePath.NormalizeFilename( true ).Trim( '/', '\\' );
// Don't download core content
if ( EngineFileSystem.CoreContent.FileExists( relativePath ) )
{
if ( EngineFileSystem.CoreContent.GetCrc( relativePath ) == crc )
{
isCoreContent = true;
return true;
}
}
var cachePath = CreateCacheFilename( relativePath, crc );
var absPath = System.IO.Path.Combine( cacheFolder, cachePath );
if ( !System.IO.File.Exists( absPath ) )
{
return false;
}
return true;
}
/// <summary>
/// Makes sure the directory exists, and returns the relative path to this file
/// </summary>
public static string CreateCacheFilename( string filePath, ulong crc )
{
var ext = System.IO.Path.GetExtension( filePath );
var targetFile = filePath[..^ext.Length];
targetFile = Regex.Replace( targetFile, "[^a-zA-Z0-9/]", "_" );
targetFile = targetFile.TrimStart( '/', '\\' );
targetFile = $"{targetFile}.{crc:x}{ext}";
return targetFile;
}
/// <summary>
/// Generate the absolute path for this, whether it exists or not
/// </summary>
internal static string GetAbsolutePath( string path, ulong crc )
{
return System.IO.Path.Combine( cacheFolder, CreateCacheFilename( path, crc ) );
}
static string[] _neverDownloadExtensions = new[] { ".dll", ".exe", ".bat", ".cmd", ".msi", ".scr", ".com", ".cpl", ".vbs", ".ps1", ".reg" };
/// <summary>
/// Is a file an legal download or not (filter out executables etc)
/// </summary>
internal static bool IsLegalDownload( string path )
{
if ( string.IsNullOrWhiteSpace( path ) ) return false;
if ( path.Length < 3 ) return false;
if ( path.Contains( ".." ) ) return false;
var ext = System.IO.Path.GetExtension( path );
if ( string.IsNullOrWhiteSpace( ext ) )
return false;
if ( _neverDownloadExtensions.Contains( ext, StringComparer.OrdinalIgnoreCase ) )
return false;
return true;
}
/// <summary>
/// Given a file with a crc, create a unique cache filename for it
/// </summary>
internal static string CreateGameCacheFilename( string path, string crc )
{
return $"{path.ToLowerInvariant().Md5()}.{crc}.cache";
}
internal static bool TryMount( RedirectFileSystem fs, string path, ulong crc )
{
var gc = "/gamecache/" + CreateGameCacheFilename( path, crc.ToString( "x" ) );
if ( EngineFileSystem.Root.FileExists( gc ) )
{
//Log.Info( $"GAMECACHE: [{path}]" );
fs.AddAbsFile( path.NormalizeFilename( true ), EngineFileSystem.Root.GetFullPath( gc ) );
return true;
}
if ( IsFileDownloaded( path, crc, out var wasCoreContent ) )
{
if ( !wasCoreContent )
{
var cachePath = GetAbsolutePath( path, crc );
//Log.Info( $"DOWNLOAD: [{path}]" );
fs.AddAbsFile( path.NormalizeFilename( true ), cachePath );
return true;
}
return true;
}
return false;
}
}