mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Full round trip test passes
This commit is contained in:
parent
6706d5bfb6
commit
528ef51d40
@ -1,8 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using DirectXTexNet;
|
using DirectXTexNet;
|
||||||
using Shipwreck.Phash;
|
using Shipwreck.Phash;
|
||||||
using Shipwreck.Phash.Imaging;
|
using Shipwreck.Phash.Imaging;
|
||||||
@ -10,7 +12,7 @@ using Wabbajack.Common;
|
|||||||
|
|
||||||
namespace Wabbajack.ImageHashing
|
namespace Wabbajack.ImageHashing
|
||||||
{
|
{
|
||||||
public class DDSImage
|
public class DDSImage : IDisposable
|
||||||
{
|
{
|
||||||
private DDSImage(ScratchImage img, TexMetadata metadata, Extension ext)
|
private DDSImage(ScratchImage img, TexMetadata metadata, Extension ext)
|
||||||
{
|
{
|
||||||
@ -20,6 +22,7 @@ namespace Wabbajack.ImageHashing
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Extension DDSExtension = new(".dds");
|
private static Extension DDSExtension = new(".dds");
|
||||||
|
private static Extension TGAExtension = new(".tga");
|
||||||
private ScratchImage _image;
|
private ScratchImage _image;
|
||||||
private TexMetadata _metaData;
|
private TexMetadata _metaData;
|
||||||
|
|
||||||
@ -57,6 +60,17 @@ namespace Wabbajack.ImageHashing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static async Task<DDSImage> FromStream(Stream stream, IPath arg1Name)
|
||||||
|
{
|
||||||
|
var data = await stream.ReadAllAsync();
|
||||||
|
if (arg1Name.FileName.Extension == DDSExtension)
|
||||||
|
return FromDDSMemory(data);
|
||||||
|
if (arg1Name.FileName.Extension == TGAExtension)
|
||||||
|
return FromTGAMemory(data);
|
||||||
|
throw new NotImplementedException("Only DDS and TGA files supported");
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (!_image.IsDisposed)
|
if (!_image.IsDisposed)
|
||||||
@ -66,9 +80,34 @@ namespace Wabbajack.ImageHashing
|
|||||||
public int Width => _metaData.Width;
|
public int Width => _metaData.Width;
|
||||||
public int Height => _metaData.Height;
|
public int Height => _metaData.Height;
|
||||||
|
|
||||||
public void Resize(int width, int height)
|
// Destructively resize a Image
|
||||||
|
public void ResizeRecompressAndSave(int width, int height, DXGI_FORMAT newFormat, AbsolutePath dest)
|
||||||
{
|
{
|
||||||
|
ScratchImage? resized = default;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// First we resize the image, so that changes due to image scaling matter less in the final hash
|
||||||
|
if (CompressedTypes.Contains(_metaData.Format))
|
||||||
|
{
|
||||||
|
using var decompressed = _image.Decompress(DXGI_FORMAT.UNKNOWN);
|
||||||
|
resized = decompressed.Resize(width, height, TEX_FILTER_FLAGS.DEFAULT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
resized = _image.Resize(width, height, TEX_FILTER_FLAGS.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var compressed = resized.Compress(newFormat, TEX_COMPRESS_FLAGS.BC7_QUICK, 0.5f);
|
||||||
|
|
||||||
|
if (dest.Extension == new Extension(".dds"))
|
||||||
|
{
|
||||||
|
compressed.SaveToDDSFile(DDS_FLAGS.NONE, dest.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
resized?.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HashSet<DXGI_FORMAT> CompressedTypes = new HashSet<DXGI_FORMAT>()
|
private static HashSet<DXGI_FORMAT> CompressedTypes = new HashSet<DXGI_FORMAT>()
|
||||||
@ -104,6 +143,7 @@ namespace Wabbajack.ImageHashing
|
|||||||
{
|
{
|
||||||
Width = _metaData.Width,
|
Width = _metaData.Width,
|
||||||
Height = _metaData.Height,
|
Height = _metaData.Height,
|
||||||
|
Format = _metaData.Format,
|
||||||
PerceptualHash = PerceptionHash()
|
PerceptualHash = PerceptionHash()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -160,5 +200,9 @@ namespace Wabbajack.ImageHashing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ResizeRecompressAndSave(ImageState state, AbsolutePath dest)
|
||||||
|
{
|
||||||
|
ResizeRecompressAndSave(state.Width, state.Height, state.Format, dest);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
using System.IO;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using DirectXTexNet;
|
using DirectXTexNet;
|
||||||
using Wabbajack.Common;
|
using Wabbajack.Common;
|
||||||
|
using Wabbajack.Common.Serialization.Json;
|
||||||
|
|
||||||
namespace Wabbajack.ImageHashing
|
namespace Wabbajack.ImageHashing
|
||||||
{
|
{
|
||||||
|
[JsonName("ImageState")]
|
||||||
public class ImageState
|
public class ImageState
|
||||||
{
|
{
|
||||||
public int Width { get; set; }
|
public int Width { get; set; }
|
||||||
@ -29,5 +33,34 @@ namespace Wabbajack.ImageHashing
|
|||||||
bw.Write((byte)Format);
|
bw.Write((byte)Format);
|
||||||
PerceptualHash.Write(bw);
|
PerceptualHash.Write(bw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<ImageState> FromImageStream(Stream stream, Extension ext, bool takeStreamOwnership = true)
|
||||||
|
{
|
||||||
|
var ms = new MemoryStream();
|
||||||
|
await stream.CopyToAsync(ms);
|
||||||
|
if (takeStreamOwnership) await stream.DisposeAsync();
|
||||||
|
|
||||||
|
DDSImage? img = default;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (ext == new Extension(".dds"))
|
||||||
|
img = DDSImage.FromDDSMemory(ms.GetBuffer());
|
||||||
|
else if (ext == new Extension(".tga"))
|
||||||
|
{
|
||||||
|
img = DDSImage.FromTGAMemory(ms.GetBuffer());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("Only DDS and TGA files supported by PHash");
|
||||||
|
}
|
||||||
|
|
||||||
|
return img.ImageState();
|
||||||
|
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
img?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,31 +2,34 @@
|
|||||||
using System.Data;
|
using System.Data;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Shipwreck.Phash;
|
using Shipwreck.Phash;
|
||||||
using Wabbajack.Common;
|
using Wabbajack.Common;
|
||||||
|
|
||||||
namespace Wabbajack.ImageHashing
|
namespace Wabbajack.ImageHashing
|
||||||
{
|
{
|
||||||
|
[JsonConverter(typeof(PHashJsonConverter))]
|
||||||
public struct PHash
|
public struct PHash
|
||||||
{
|
{
|
||||||
private const int SIZE = 40;
|
private const int SIZE = 40;
|
||||||
private readonly byte[] _data;
|
|
||||||
private readonly int _hash;
|
private readonly int _hash;
|
||||||
|
|
||||||
|
public byte[] Data { get; }
|
||||||
|
|
||||||
private PHash(byte[] data)
|
private PHash(byte[] data)
|
||||||
{
|
{
|
||||||
_data = data;
|
Data = data;
|
||||||
if (_data.Length != SIZE)
|
if (Data.Length != SIZE)
|
||||||
throw new DataException();
|
throw new DataException();
|
||||||
|
|
||||||
long h = 0;
|
long h = 0;
|
||||||
h |= _data[0];
|
h |= Data[0];
|
||||||
h <<= 8;
|
h <<= 8;
|
||||||
h |= _data[1];
|
h |= Data[1];
|
||||||
h <<= 8;
|
h <<= 8;
|
||||||
h |= _data[2];
|
h |= Data[2];
|
||||||
h <<= 8;
|
h <<= 8;
|
||||||
h |= _data[3];
|
h |= Data[3];
|
||||||
h <<= 8;
|
h <<= 8;
|
||||||
_hash = (int)h;
|
_hash = (int)h;
|
||||||
}
|
}
|
||||||
@ -49,7 +52,7 @@ namespace Wabbajack.ImageHashing
|
|||||||
if (_hash == 0)
|
if (_hash == 0)
|
||||||
br.Write(new byte[SIZE]);
|
br.Write(new byte[SIZE]);
|
||||||
else
|
else
|
||||||
br.Write(_data);
|
br.Write(Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PHash FromDigest(Digest digest)
|
public static PHash FromDigest(Digest digest)
|
||||||
@ -59,24 +62,24 @@ namespace Wabbajack.ImageHashing
|
|||||||
|
|
||||||
public float Similarity(PHash other)
|
public float Similarity(PHash other)
|
||||||
{
|
{
|
||||||
return ImagePhash.GetCrossCorrelation(this._data, other._data);
|
return ImagePhash.GetCrossCorrelation(this.Data, other.Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return _data.ToBase64();
|
return Data.ToBase64();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
long h = 0;
|
long h = 0;
|
||||||
h |= _data[0];
|
h |= Data[0];
|
||||||
h <<= 8;
|
h <<= 8;
|
||||||
h |= _data[1];
|
h |= Data[1];
|
||||||
h <<= 8;
|
h <<= 8;
|
||||||
h |= _data[2];
|
h |= Data[2];
|
||||||
h <<= 8;
|
h <<= 8;
|
||||||
h |= _data[3];
|
h |= Data[3];
|
||||||
h <<= 8;
|
h <<= 8;
|
||||||
return (int)h;
|
return (int)h;
|
||||||
}
|
}
|
||||||
@ -100,5 +103,27 @@ namespace Wabbajack.ImageHashing
|
|||||||
}
|
}
|
||||||
return img.PerceptionHash();
|
return img.PerceptionHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<PHash> FromFile(AbsolutePath path)
|
||||||
|
{
|
||||||
|
await using var s = await path.OpenRead();
|
||||||
|
return await FromStream(s, path.Extension);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class PHashJsonConverter : JsonConverter<PHash>
|
||||||
|
{
|
||||||
|
public override void WriteJson(JsonWriter writer, PHash value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
writer.WriteValue(value.Data.ToBase64());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override PHash ReadJson(JsonReader reader, Type objectType, PHash existingValue, bool hasExistingValue,
|
||||||
|
JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
return PHash.FromBase64((string)reader.Value!);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ using System.Linq;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Wabbajack.Common;
|
using Wabbajack.Common;
|
||||||
|
using Wabbajack.ImageHashing;
|
||||||
using Wabbajack.Lib.Downloaders;
|
using Wabbajack.Lib.Downloaders;
|
||||||
using Wabbajack.Lib.Validation;
|
using Wabbajack.Lib.Validation;
|
||||||
using Wabbajack.VirtualFileSystem;
|
using Wabbajack.VirtualFileSystem;
|
||||||
@ -153,6 +154,16 @@ namespace Wabbajack.Lib
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TransformedTexture tt:
|
||||||
|
{
|
||||||
|
await using var s = await sf.GetStream();
|
||||||
|
using var img = await DDSImage.FromStream(s, vf.Name);
|
||||||
|
|
||||||
|
img.ResizeRecompressAndSave(tt.ImageState, directive.Directive.To.RelativeTo(OutputFolder));
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case FromArchive _:
|
case FromArchive _:
|
||||||
if (grouped[vf].Count() == 1)
|
if (grouped[vf].Count() == 1)
|
||||||
{
|
{
|
||||||
|
44
Wabbajack.Lib/CompilationSteps/MatchSimilarTextures.cs
Normal file
44
Wabbajack.Lib/CompilationSteps/MatchSimilarTextures.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Wabbajack.Common;
|
||||||
|
using Wabbajack.ImageHashing;
|
||||||
|
using Wabbajack.VirtualFileSystem;
|
||||||
|
|
||||||
|
namespace Wabbajack.Lib.CompilationSteps
|
||||||
|
{
|
||||||
|
public class MatchSimilarTextures : ACompilationStep
|
||||||
|
{
|
||||||
|
private ILookup<RelativePath, VirtualFile> _byName;
|
||||||
|
public MatchSimilarTextures(ACompiler compiler) : base(compiler)
|
||||||
|
{
|
||||||
|
_byName = _compiler.IndexedFiles.SelectMany(kv => kv.Value)
|
||||||
|
.Where(f => f.Name.FileName.Extension == DDS)
|
||||||
|
.ToLookup(f => f.Name.FileName.FileNameWithoutExtension);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Extension DDS = new(".dds");
|
||||||
|
|
||||||
|
|
||||||
|
public override async ValueTask<Directive?> Run(RawSourceFile source)
|
||||||
|
{
|
||||||
|
if (source.Path.Extension == DDS)
|
||||||
|
{
|
||||||
|
var found = _byName[source.Path.FileNameWithoutExtension]
|
||||||
|
.Select(f => (f.ImageState.PerceptualHash.Similarity(source.File.ImageState.PerceptualHash), f))
|
||||||
|
.Where(f => f.Item1 >= 0.90f)
|
||||||
|
.OrderByDescending(f => f.Item1)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
if (found == default) return null;
|
||||||
|
|
||||||
|
var rv = source.EvolveTo<TransformedTexture>();
|
||||||
|
rv.ArchiveHashPath = found.f.MakeRelativePaths();
|
||||||
|
rv.ImageState = found.f.ImageState;
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ using Compression.BSA;
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Wabbajack.Common;
|
using Wabbajack.Common;
|
||||||
using Wabbajack.Common.Serialization.Json;
|
using Wabbajack.Common.Serialization.Json;
|
||||||
|
using Wabbajack.ImageHashing;
|
||||||
using Wabbajack.Lib.Downloaders;
|
using Wabbajack.Lib.Downloaders;
|
||||||
using Wabbajack.VirtualFileSystem;
|
using Wabbajack.VirtualFileSystem;
|
||||||
|
|
||||||
@ -37,10 +38,7 @@ namespace Wabbajack.Lib
|
|||||||
|
|
||||||
public T EvolveTo<T>() where T : Directive, new()
|
public T EvolveTo<T>() where T : Directive, new()
|
||||||
{
|
{
|
||||||
var v = new T();
|
var v = new T {To = Path, Hash = File.Hash, Size = File.Size};
|
||||||
v.To = Path;
|
|
||||||
v.Hash = File.Hash;
|
|
||||||
v.Size = File.Size;
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -253,6 +251,15 @@ namespace Wabbajack.Lib
|
|||||||
public VirtualFile[] Choices { get; set; } = { };
|
public VirtualFile[] Choices { get; set; } = { };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[JsonName("TransformedTexture")]
|
||||||
|
public class TransformedTexture : FromArchive
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The file to apply to the source file to patch it
|
||||||
|
/// </summary>
|
||||||
|
public ImageState ImageState { get; set; } = new();
|
||||||
|
}
|
||||||
|
|
||||||
[JsonName("SourcePatch")]
|
[JsonName("SourcePatch")]
|
||||||
public class SourcePatch
|
public class SourcePatch
|
||||||
{
|
{
|
||||||
|
@ -456,6 +456,7 @@ namespace Wabbajack.Lib
|
|||||||
new IgnoreEndsWith(this, ".log"),
|
new IgnoreEndsWith(this, ".log"),
|
||||||
new DeconstructBSAs(
|
new DeconstructBSAs(
|
||||||
this), // Deconstruct BSAs before building patches so we don't generate massive patch files
|
this), // Deconstruct BSAs before building patches so we don't generate massive patch files
|
||||||
|
new MatchSimilarTextures(this),
|
||||||
new IncludePatches(this),
|
new IncludePatches(this),
|
||||||
new IncludeDummyESPs(this),
|
new IncludeDummyESPs(this),
|
||||||
|
|
||||||
|
@ -5,12 +5,14 @@ using System.Linq;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Compression.BSA;
|
using Compression.BSA;
|
||||||
using Wabbajack.Common;
|
using Wabbajack.Common;
|
||||||
|
using Wabbajack.ImageHashing;
|
||||||
using Wabbajack.Lib;
|
using Wabbajack.Lib;
|
||||||
using Wabbajack.Lib.CompilationSteps;
|
using Wabbajack.Lib.CompilationSteps;
|
||||||
using Wabbajack.Lib.CompilationSteps.CompilationErrors;
|
using Wabbajack.Lib.CompilationSteps.CompilationErrors;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
using Xunit.Sdk;
|
using Xunit.Sdk;
|
||||||
|
using DXGI_FORMAT = DirectXTexNet.DXGI_FORMAT;
|
||||||
using File = Alphaleonis.Win32.Filesystem.File;
|
using File = Alphaleonis.Win32.Filesystem.File;
|
||||||
using Path = Alphaleonis.Win32.Filesystem.Path;
|
using Path = Alphaleonis.Win32.Filesystem.Path;
|
||||||
|
|
||||||
@ -364,6 +366,41 @@ namespace Wabbajack.Test
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CanRecompressAndResizeDDSImages()
|
||||||
|
{
|
||||||
|
var profile = utils.AddProfile();
|
||||||
|
var mod = await utils.AddMod();
|
||||||
|
var nativeFile = await utils.AddModFile(mod, @"native\whitestagbody.dds", 0);
|
||||||
|
var recompressedFile = await utils.AddModFile(mod, @"recompressed\whitestagbody.dds", 0);
|
||||||
|
var resizedFile = await utils.AddModFile(mod, @"resized\whitestagbody.dds", 0);
|
||||||
|
|
||||||
|
var gameBSA = Game.SkyrimSpecialEdition.MetaData().GameLocation().Combine(@"Data\Skyrim - Textures1.bsa");
|
||||||
|
var bsa = await BSADispatch.OpenRead(gameBSA);
|
||||||
|
var ddsExtension = new Extension(".dds");
|
||||||
|
var firstFile = bsa.Files.First(f => f.Path.Extension == ddsExtension);
|
||||||
|
|
||||||
|
await using (var nf = await nativeFile.OpenWrite())
|
||||||
|
{
|
||||||
|
await firstFile.CopyDataTo(nf);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
using var originalDDS = DDSImage.FromFile(nativeFile);
|
||||||
|
originalDDS.ResizeRecompressAndSave(originalDDS.Width, originalDDS.Height, DXGI_FORMAT.BC7_UNORM, recompressedFile);
|
||||||
|
originalDDS.ResizeRecompressAndSave(128, 128, DXGI_FORMAT.BC7_UNORM, resizedFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
await utils.Configure();
|
||||||
|
|
||||||
|
await CompileAndInstall(profile, true);
|
||||||
|
await utils.VerifyInstalledFile(mod, @"native\whitestagbody.dds");
|
||||||
|
Assert.Equal(0.9999227f, (await PHash.FromFile(recompressedFile)).Similarity(await PHash.FromFile(utils.InstalledPath(mod, @"recompressed\whitestagbody.dds"))));
|
||||||
|
Assert.Equal(0.98703325f, (await PHash.FromFile(resizedFile)).Similarity(await PHash.FromFile(utils.InstalledPath(mod, @"resized\whitestagbody.dds"))));
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CanNoMatchIncludeFilesFromBSAs()
|
public async Task CanNoMatchIncludeFilesFromBSAs()
|
||||||
{
|
{
|
||||||
|
@ -95,14 +95,17 @@ namespace Wabbajack.Test
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mod_name"></param>
|
/// <param name="mod_name"></param>
|
||||||
/// <param name="path"></param>
|
/// <param name="path"></param>
|
||||||
/// <param name="random_fill"></param>
|
/// <param name="randomFill"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<AbsolutePath> AddModFile(string mod_name, string path, int random_fill=128)
|
public async Task<AbsolutePath> AddModFile(string mod_name, string path, int randomFill=128)
|
||||||
{
|
{
|
||||||
var full_path = ModsPath.Combine(mod_name, path);
|
var fullPath = ModsPath.Combine(mod_name, path);
|
||||||
full_path.Parent.CreateDirectory();
|
fullPath.Parent.CreateDirectory();
|
||||||
await GenerateRandomFileData(full_path, random_fill);
|
|
||||||
return full_path;
|
if (randomFill != 0)
|
||||||
|
await GenerateRandomFileData(fullPath, randomFill);
|
||||||
|
|
||||||
|
return fullPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task GenerateRandomFileData(AbsolutePath full_path, int random_fill)
|
public async Task GenerateRandomFileData(AbsolutePath full_path, int random_fill)
|
||||||
@ -199,6 +202,9 @@ namespace Wabbajack.Test
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AbsolutePath InstalledPath(string mod, string file) =>
|
||||||
|
InstallPath.Combine((string)Consts.MO2ModFolderName, mod, file);
|
||||||
|
|
||||||
public async Task VerifyInstalledGameFile(string file)
|
public async Task VerifyInstalledGameFile(string file)
|
||||||
{
|
{
|
||||||
var src = GameFolder.Combine(file);
|
var src = GameFolder.Combine(file);
|
||||||
|
@ -19,7 +19,7 @@ namespace Wabbajack.VirtualFileSystem
|
|||||||
public IPath Name { get; set; }
|
public IPath Name { get; set; }
|
||||||
public Hash Hash { get; set; }
|
public Hash Hash { get; set; }
|
||||||
|
|
||||||
public ImageState? ImageState { get; set; }
|
public ImageState ImageState { get; set; }
|
||||||
public long Size { get; set; }
|
public long Size { get; set; }
|
||||||
public List<IndexedVirtualFile> Children { get; set; } = new();
|
public List<IndexedVirtualFile> Children { get; set; } = new();
|
||||||
|
|
||||||
|
@ -49,8 +49,7 @@ namespace Wabbajack.VirtualFileSystem
|
|||||||
public FullPath FullPath { get; private set; }
|
public FullPath FullPath { get; private set; }
|
||||||
|
|
||||||
public Hash Hash { get; internal set; }
|
public Hash Hash { get; internal set; }
|
||||||
public PHash PerceptualHash { get; internal set; }
|
public ImageState ImageState { get; internal set; }
|
||||||
|
|
||||||
public ExtendedHashes ExtendedHashes { get; set; }
|
public ExtendedHashes ExtendedHashes { get; set; }
|
||||||
public long Size { get; internal set; }
|
public long Size { get; internal set; }
|
||||||
|
|
||||||
@ -148,7 +147,8 @@ namespace Wabbajack.VirtualFileSystem
|
|||||||
Size = file.Size,
|
Size = file.Size,
|
||||||
LastModified = extractedFile.LastModifiedUtc.AsUnixTime(),
|
LastModified = extractedFile.LastModifiedUtc.AsUnixTime(),
|
||||||
LastAnalyzed = DateTime.Now.AsUnixTime(),
|
LastAnalyzed = DateTime.Now.AsUnixTime(),
|
||||||
Hash = file.Hash
|
Hash = file.Hash,
|
||||||
|
ImageState = file.ImageState
|
||||||
};
|
};
|
||||||
|
|
||||||
vself.FillFullPath();
|
vself.FillFullPath();
|
||||||
@ -183,7 +183,7 @@ namespace Wabbajack.VirtualFileSystem
|
|||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
Hash = Hash,
|
Hash = Hash,
|
||||||
PerceptualHash = PerceptualHash,
|
ImageState = ImageState,
|
||||||
Name = Name,
|
Name = Name,
|
||||||
Children = Children.Select(c => c.ToIndexedVirtualFile()).ToList(),
|
Children = Children.Select(c => c.ToIndexedVirtualFile()).ToList(),
|
||||||
Size = Size
|
Size = Size
|
||||||
@ -227,7 +227,7 @@ namespace Wabbajack.VirtualFileSystem
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (Consts.TextureExtensions.Contains(relPath.FileName.Extension))
|
if (Consts.TextureExtensions.Contains(relPath.FileName.Extension))
|
||||||
self.PerceptualHash = await PHash.FromStream(stream, relPath.FileName.Extension, false);
|
self.ImageState = await ImageHashing.ImageState.FromImageStream(stream, relPath.FileName.Extension, false);
|
||||||
|
|
||||||
self.FillFullPath(depth);
|
self.FillFullPath(depth);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user