Files
RobustToolbox/SS14.Shared/ContentPack/ResourceManager.cs
Pieter-Jan Briers d7414930ff RSI support (#552)
* RSI WiP

* More work but we're doing bsdiff now

* RSI loading seems to mostly work.

* Vector2u deserialization test.

* Add in packages again.

* This is the part where I realize I need a path manipulation library.

* The start of a path class but it's late so I'm going to bed.

* HIGHLY theoretical ResourcePath code.

Partially tested but not really.

* Allow x86 for unit tests I guess jesus christ.

Thanks Microsoft for still not shipping x64 VS in 2018.

* Resource paths work & are tested.

* I missed a doc spot.

* ResourcePaths implemented on the server.

* Client works with resource paths.

TIME FOR A REFACTOR.

* Some work but this might be a stupid idea so I migh throw it in the trash.

* Resources refactored completely.

They now only get the requested resourcepath.
They're in charge of opening files to load.

* RSI Loader WORKS.

* Update AudioResource for new loading support.

* Fix package references.

* Fix more references.

* Gonna work now?
2018-04-12 21:53:19 +02:00

223 lines
7.0 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SS14.Shared.Configuration;
using SS14.Shared.Interfaces;
using SS14.Shared.Interfaces.Configuration;
using SS14.Shared.IoC;
using SS14.Shared.Log;
using SS14.Shared.Utility;
namespace SS14.Shared.ContentPack
{
/// <summary>
/// Virtual file system for all disk resources.
/// </summary>
public partial class ResourceManager : IResourceManager
{
private const string DataFolderName = "Space Station 14";
[Dependency]
private readonly IConfigurationManager _config;
private DirectoryInfo _configRoot;
private readonly List<(ResourcePath prefix, IContentRoot root)> _contentRoots = new List<(ResourcePath, IContentRoot)>();
/// <inheritdoc />
public string ConfigDirectory => _configRoot.FullName;
/// <inheritdoc />
public void Initialize()
{
_config.RegisterCVar("resource.pack", "ResourcePack.zip", CVar.ARCHIVE);
var configRoot = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
configRoot = Path.Combine(configRoot, DataFolderName);
configRoot = Path.GetFullPath(configRoot);
_configRoot = Directory.CreateDirectory(configRoot);
}
/// <inheritdoc />
public void MountDefaultContentPack()
{
//Assert server only
var zipPath = _config.GetCVar<string>("resource.pack");
// no pack in config
if (string.IsNullOrWhiteSpace(zipPath))
{
Logger.Warning("[RES] No default ContentPack to load in configuration.");
return;
}
MountContentPack(zipPath);
}
/// <inheritdoc />
public void MountContentPack(string pack, ResourcePath prefix = null)
{
if (prefix == null)
{
prefix = ResourcePath.Root;
}
if (!prefix.IsRooted)
{
throw new ArgumentException("Prefix must be rooted.", nameof(prefix));
}
pack = PathHelpers.ExecutableRelativeFile(pack);
var packInfo = new FileInfo(pack);
if (!packInfo.Exists)
{
throw new FileNotFoundException("Specified ContentPack does not exist: " + packInfo.FullName);
}
//create new PackLoader
var loader = new PackLoader(packInfo);
loader.Mount();
_contentRoots.Add((prefix, loader));
}
/// <inheritdoc />
public void MountContentDirectory(string path, ResourcePath prefix = null)
{
if (prefix == null)
{
prefix = ResourcePath.Root;
}
if (!prefix.IsRooted)
{
throw new ArgumentException("Prefix must be rooted.", nameof(prefix));
}
path = PathHelpers.ExecutableRelativeFile(path);
var pathInfo = new DirectoryInfo(path);
if (!pathInfo.Exists)
{
throw new DirectoryNotFoundException("Specified directory does not exist: " + pathInfo.FullName);
}
var loader = new DirLoader(pathInfo);
loader.Mount();
_contentRoots.Add((prefix, loader));
}
/// <inheritdoc />
public MemoryStream ContentFileRead(string path)
{
return ContentFileRead(new ResourcePath(path));
}
/// <inheritdoc />
public MemoryStream ContentFileRead(ResourcePath path)
{
if (TryContentFileRead(path, out var fileStream))
{
return fileStream;
}
throw new FileNotFoundException($"Path does not exist in the VFS: '{path}'");
}
/// <inheritdoc />
public bool TryContentFileRead(string path, out MemoryStream fileStream)
{
return TryContentFileRead(new ResourcePath(path), out fileStream);
}
/// <inheritdoc />
public bool TryContentFileRead(ResourcePath path, out MemoryStream fileStream)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
if (!path.IsRooted)
{
throw new ArgumentException("Path must be rooted", nameof(path));
}
foreach ((var prefix, var root) in _contentRoots)
{
if (!path.TryRelativeTo(prefix, out var relative))
{
continue;
}
if (root.TryGetFile(relative, out fileStream))
{
return true;
}
}
fileStream = null;
return false;
}
/// <inheritdoc />
public bool ContentFileExists(string path)
{
return ContentFileExists(new ResourcePath(path));
}
/// <inheritdoc />
public bool ContentFileExists(ResourcePath path)
{
return TryContentFileRead(path, out var _);
}
/// <inheritdoc />
public IEnumerable<ResourcePath> ContentFindFiles(string path)
{
return ContentFindFiles(new ResourcePath(path));
}
/// <inheritdoc />
public IEnumerable<ResourcePath> ContentFindFiles(ResourcePath path)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
if (!path.IsRooted)
{
throw new ArgumentException("Path is not rooted", nameof(path));
}
var alreadyReturnedFiles = new HashSet<ResourcePath>();
foreach ((var prefix, var root) in _contentRoots)
{
if (!path.TryRelativeTo(prefix, out var relative))
{
continue;
}
foreach (var filename in root.FindFiles(relative))
{
var newpath = prefix / filename;
if (!alreadyReturnedFiles.Contains(newpath))
{
alreadyReturnedFiles.Add(newpath);
yield return newpath;
}
}
}
}
// TODO: Remove this when/if we can get Godot to load from not-the-filesystem.
public bool TryGetDiskFilePath(ResourcePath path, out string diskPath)
{
// loop over each root trying to get the file
foreach ((var prefix, var root) in _contentRoots)
{
if (!(root is DirLoader dirLoader) || !path.TryRelativeTo(prefix, out var tempPath))
{
continue;
}
diskPath = dirLoader.GetPath(tempPath);
if (File.Exists(diskPath))
return true;
}
diskPath = null;
return false;
}
}
}