CodeWalker/CodeWalker.Core/GameFiles/GameFile.cs

199 lines
5.5 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CodeWalker.GameFiles
{
// This would make more sense as a enum, but that would cause lots of bloat casting values
public static class LoadState
{
public const int None = 0;
public const int Loaded = 1;
public const int LoadQueued = 2;
}
public abstract class GameFile : Cacheable<GameFileCacheKey>, IDisposable
{
public byte LoadAttempts = 0;
public int loadState = (int)LoadState.None;
[NotifyParentProperty(true)]
public bool LoadQueued {
get => (loadState & LoadState.LoadQueued) == LoadState.LoadQueued;
set {
if (value)
{
Interlocked.Or(ref loadState, LoadState.LoadQueued);
}
else
{
Interlocked.And(ref loadState, ~LoadState.LoadQueued);
}
}
}
[NotifyParentProperty(true)]
public bool Loaded
{
get => (loadState & LoadState.Loaded) == LoadState.Loaded;
set
{
if (value)
{
Interlocked.Or(ref loadState, LoadState.Loaded);
}
else
{
Interlocked.And(ref loadState, ~LoadState.Loaded);
}
}
}
public DateTime LastLoadTime = DateTime.MinValue;
public RpfFileEntry? RpfFileEntry { get; set; }
public string Name { get; set; }
public string FilePath { get; set; } //used by the project form.
public GameFileType Type { get; set; }
public bool IsDisposed { get; set; } = false;
public GameFile(RpfFileEntry? entry, GameFileType type)
{
RpfFileEntry = entry;
Type = type;
MemoryUsage = (entry != null) ? entry.GetFileSize() : 0;
if (entry is RpfResourceFileEntry resent)
{
var newuse = resent.SystemSize + resent.GraphicsSize;
MemoryUsage = newuse;
}
else if (entry is RpfBinaryFileEntry binent)
{
var newuse = binent.FileUncompressedSize;
if (newuse > MemoryUsage)
{
MemoryUsage = newuse;
}
}
}
public bool SetLoadQueued(bool value)
{
if (value)
{
return (Interlocked.Or(ref loadState, LoadState.LoadQueued) & LoadState.LoadQueued) == 0;
}
else
{
return (Interlocked.And(ref loadState, ~LoadState.LoadQueued) & LoadState.LoadQueued) == LoadState.LoadQueued;
}
}
public bool SetLoaded(bool value)
{
if (value)
{
return (Interlocked.Or(ref loadState, LoadState.Loaded) & LoadState.Loaded) == 0;
}
else
{
return (Interlocked.And(ref loadState, ~LoadState.Loaded) & LoadState.Loaded) == LoadState.Loaded;
}
}
public override string ToString() => string.IsNullOrEmpty(Name) ? JenkIndex.GetString(Key.Hash) : Name;
public virtual void Dispose()
{
IsDisposed = true;
GC.SuppressFinalize(this);
}
[DoesNotReturn]
public static void ThrowFileIsNotAResourceException()
{
throw new Exception("File entry wasn't a resource! (is it binary data?)");
}
}
public class GameFileByPathComparer : IEqualityComparer<GameFile>
{
public static readonly GameFileByPathComparer Instance = new GameFileByPathComparer();
public bool Equals(GameFile? x, GameFile? y)
{
if (x is null && y is null)
return true;
if (x is null || y is null)
return false;
if (ReferenceEquals(x, y))
return true;
if (x.Type != y.Type)
return false;
if (x.RpfFileEntry is null && y.RpfFileEntry is null)
return true;
if (x.RpfFileEntry is null || y.RpfFileEntry is null)
return false;
return x.RpfFileEntry.Path.Equals(y.RpfFileEntry.Path, StringComparison.OrdinalIgnoreCase);
}
public int GetHashCode([DisallowNull] GameFile obj)
{
return HashCode.Combine(obj.RpfFileEntry?.Path.GetHashCode(StringComparison.OrdinalIgnoreCase) ?? 0, obj.Type);
}
}
public enum GameFileType : byte
{
Ydd = 0,
Ydr = 1,
Yft = 2,
Ymap = 3,
Ymf = 4,
Ymt = 5,
Ytd = 6,
Ytyp = 7,
Ybn = 8,
Ycd = 9,
Ypt = 10,
Ynd = 11,
Ynv = 12,
Rel = 13,
Ywr = 14,
Yvr = 15,
Gtxd = 16,
Vehicles = 17,
CarCols = 18,
CarModCols = 19,
CarVariations = 20,
VehicleLayouts = 21,
Peds = 22,
Ped = 23,
Yed = 24,
Yld = 25,
Yfd = 26,
Heightmap = 27,
Watermap = 28,
Mrf = 29,
DistantLights = 30,
Ypdb = 31,
PedShopMeta = 32,
}
public readonly record struct GameFileCacheKey(uint Hash, GameFileType Type);
}