mirror of
https://github.com/wabbajack-tools/wabbajack.git
synced 2024-08-30 18:42:17 +00:00
Fix ba2 compilation errors (#2286)
* Fix BA2 compilation errors by implementing mipmap detection and usage during texture recompression * Update CHANGELOG.md * Fix broken mipmap support on Linux/OSX
This commit is contained in:
parent
6a9596c9ab
commit
7b46a88fc0
@ -3,12 +3,13 @@
|
|||||||
#### Version - 3.0.6.1 - TBD
|
#### Version - 3.0.6.1 - TBD
|
||||||
* Game support:
|
* Game support:
|
||||||
* Added Mount & Blade II: Bennerlord support (Steam,GOG)
|
* Added Mount & Blade II: Bennerlord support (Steam,GOG)
|
||||||
|
* Fixed BA2 files not being compressed properly due to MipMaps not being detected properly
|
||||||
|
|
||||||
#### Version - 3.0.6.0 - 1/21/2023
|
#### Version - 3.0.6.0 - 1/21/2023
|
||||||
* Add support for Cubemaps in BA2 files, if you have problems with BA2 recompression, be sure to delete your `GlobalVFSCache3.sqlite` from your AppData before the next compile
|
* Add support for Cubemaps in BA2 files, if you have problems with BA2 recompression, be sure to delete your `GlobalVFSCache3.sqlite` from your AppData before the next compile
|
||||||
* Fixed slides not being shown during installation for lists compile with the 3.0 compiler
|
* Fixed slides not being shown during installation for lists compile with the 3.0 compiler
|
||||||
* Set the "While loading slide" debug message to be `Trace` level, set the default minimum log level to `Information`
|
* Set the "While loading slide" debug message to be `Trace` level, set the default minimum log level to `Information`
|
||||||
* Switched back to using TexConv for texture converting on Windows, should greatly improve compatability of texture conversion (on windows systems)
|
* Switched back to using TexConv for texture converting on Windows, should greatly improve compatibility of texture conversion (on windows systems)
|
||||||
|
|
||||||
#### Version - 3.0.5.0 - 12/22/2022
|
#### Version - 3.0.5.0 - 12/22/2022
|
||||||
* Add support for https://www.nexusmods.com/site hosted mods.
|
* Add support for https://www.nexusmods.com/site hosted mods.
|
||||||
|
@ -202,7 +202,7 @@ public class CompilerSanityTests : IAsyncLifetime
|
|||||||
var oldState = await _imageLoader.Load(file);
|
var oldState = await _imageLoader.Load(file);
|
||||||
Assert.NotEqual(DXGI_FORMAT.UNKNOWN, oldState.Format);
|
Assert.NotEqual(DXGI_FORMAT.UNKNOWN, oldState.Format);
|
||||||
_logger.LogInformation("Recompressing {file}", file.FileName);
|
_logger.LogInformation("Recompressing {file}", file.FileName);
|
||||||
await _imageLoader.Recompress(file, 512, 512, DXGI_FORMAT.BC7_UNORM, file, CancellationToken.None);
|
await _imageLoader.Recompress(file, 512, 512, 1, DXGI_FORMAT.BC7_UNORM, file, CancellationToken.None);
|
||||||
|
|
||||||
var state = await _imageLoader.Load(file);
|
var state = await _imageLoader.Load(file);
|
||||||
Assert.Equal(DXGI_FORMAT.BC7_UNORM, state.Format);
|
Assert.Equal(DXGI_FORMAT.BC7_UNORM, state.Format);
|
||||||
|
@ -20,24 +20,31 @@ public class Builder : IBuilder
|
|||||||
|
|
||||||
public async ValueTask AddFile(AFile state, Stream src, CancellationToken token)
|
public async ValueTask AddFile(AFile state, Stream src, CancellationToken token)
|
||||||
{
|
{
|
||||||
switch (_state.Type)
|
try
|
||||||
{
|
{
|
||||||
case BA2EntryType.GNRL:
|
switch (_state.Type)
|
||||||
var result = await FileEntryBuilder.Create((BA2File) state, src, _slab, token);
|
{
|
||||||
lock (_entries)
|
case BA2EntryType.GNRL:
|
||||||
{
|
var result = await FileEntryBuilder.Create((BA2File)state, src, _slab, token);
|
||||||
_entries.Add(result);
|
lock (_entries)
|
||||||
}
|
{
|
||||||
|
_entries.Add(result);
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case BA2EntryType.DX10:
|
case BA2EntryType.DX10:
|
||||||
var resultdx10 = await DX10FileEntryBuilder.Create((BA2DX10File) state, src, _slab, token);
|
var resultdx10 = await DX10FileEntryBuilder.Create((BA2DX10File)state, src, _slab, token);
|
||||||
lock (_entries)
|
lock (_entries)
|
||||||
{
|
{
|
||||||
_entries.Add(resultdx10);
|
_entries.Add(resultdx10);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
throw new InvalidDataException($"Error adding file {state.Path} to archive: {ex.Message}", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -7,8 +7,10 @@ public class ImageState
|
|||||||
public DXGI_FORMAT Format { get; set; }
|
public DXGI_FORMAT Format { get; set; }
|
||||||
public PHash PerceptualHash { get; set; }
|
public PHash PerceptualHash { get; set; }
|
||||||
|
|
||||||
|
public int MipLevels { get; set; }
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return $"ImageState<{Width}, {Height}, {Format}>";
|
return $"ImageState<{Width}, {Height}, {Format}, {MipLevels} levels>";
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -78,7 +78,7 @@ public class FileLoadingTests : IAsyncDisposable
|
|||||||
|
|
||||||
using var ms = new MemoryStream();
|
using var ms = new MemoryStream();
|
||||||
await using var ins = path.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
|
await using var ins = path.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
await imageLoader.Recompress(ins, 128, 128, DXGI_FORMAT.BC1_UNORM, ms, CancellationToken.None, leaveOpen:true);
|
await imageLoader.Recompress(ins, 128, 128, baseState.MipLevels, DXGI_FORMAT.BC1_UNORM, ms, CancellationToken.None, leaveOpen:true);
|
||||||
ms.Length.Should().Be(ins.Length);
|
ms.Length.Should().Be(ins.Length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ public class CrossPlatformImageLoader : IImageLoader
|
|||||||
{
|
{
|
||||||
Width = data.Width,
|
Width = data.Width,
|
||||||
Height = data.Height,
|
Height = data.Height,
|
||||||
|
MipLevels = (int)ddsFile.header.dwMipMapCount,
|
||||||
Format = (DXGI_FORMAT) format
|
Format = (DXGI_FORMAT) format
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -60,16 +61,16 @@ public class CrossPlatformImageLoader : IImageLoader
|
|||||||
new Digest {Coefficients = b.Data});
|
new Digest {Coefficients = b.Data});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Recompress(AbsolutePath input, int width, int height, DXGI_FORMAT format,
|
public async Task Recompress(AbsolutePath input, int width, int height, int mipMaps, DXGI_FORMAT format,
|
||||||
AbsolutePath output,
|
AbsolutePath output,
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
var inData = await input.ReadAllBytesAsync(token);
|
var inData = await input.ReadAllBytesAsync(token);
|
||||||
await using var outStream = output.Open(FileMode.Create, FileAccess.Write);
|
await using var outStream = output.Open(FileMode.Create, FileAccess.Write);
|
||||||
await Recompress(new MemoryStream(inData), width, height, format, outStream, token);
|
await Recompress(new MemoryStream(inData), width, height, mipMaps, format, outStream, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Recompress(Stream input, int width, int height, DXGI_FORMAT format, Stream output,
|
public async Task Recompress(Stream input, int width, int height, int mipMaps, DXGI_FORMAT format, Stream output,
|
||||||
CancellationToken token, bool leaveOpen = false)
|
CancellationToken token, bool leaveOpen = false)
|
||||||
{
|
{
|
||||||
var decoder = new BcDecoder();
|
var decoder = new BcDecoder();
|
||||||
@ -99,7 +100,8 @@ public class CrossPlatformImageLoader : IImageLoader
|
|||||||
Quality = CompressionQuality.Balanced,
|
Quality = CompressionQuality.Balanced,
|
||||||
GenerateMipMaps = true,
|
GenerateMipMaps = true,
|
||||||
Format = ToCompressionFormat(format),
|
Format = ToCompressionFormat(format),
|
||||||
FileFormat = OutputFileFormat.Dds
|
FileFormat = OutputFileFormat.Dds,
|
||||||
|
MaxMipMapLevel = mipMaps
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -18,10 +18,10 @@ public interface IImageLoader
|
|||||||
new Digest {Coefficients = b.Data});
|
new Digest {Coefficients = b.Data});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task Recompress(AbsolutePath input, int width, int height, DXGI_FORMAT format,
|
public Task Recompress(AbsolutePath input, int width, int height, int mipMaps, DXGI_FORMAT format,
|
||||||
AbsolutePath output,
|
AbsolutePath output,
|
||||||
CancellationToken token);
|
CancellationToken token);
|
||||||
|
|
||||||
public Task Recompress(Stream input, int width, int height, DXGI_FORMAT format, Stream output,
|
public Task Recompress(Stream input, int width, int height, int mipMaps, DXGI_FORMAT format, Stream output,
|
||||||
CancellationToken token, bool leaveOpen = false);
|
CancellationToken token, bool leaveOpen = false);
|
||||||
}
|
}
|
@ -57,16 +57,16 @@ public class TexConvImageLoader : IImageLoader
|
|||||||
return ext;
|
return ext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Recompress(AbsolutePath input, int width, int height, DXGI_FORMAT format, AbsolutePath output,
|
public async Task Recompress(AbsolutePath input, int width, int height, int mipMaps, DXGI_FORMAT format, AbsolutePath output,
|
||||||
CancellationToken token)
|
CancellationToken token)
|
||||||
{
|
{
|
||||||
var outFolder = _tempManager.CreateFolder();
|
var outFolder = _tempManager.CreateFolder();
|
||||||
var outFile = input.FileName.RelativeTo(outFolder.Path);
|
var outFile = input.FileName.RelativeTo(outFolder.Path);
|
||||||
await ConvertImage(input, outFolder.Path, width, height, format, input.Extension);
|
await ConvertImage(input, outFolder.Path, width, height, mipMaps, format, input.Extension);
|
||||||
await outFile.MoveToAsync(output, token: token, overwrite:true);
|
await outFile.MoveToAsync(output, token: token, overwrite:true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Recompress(Stream input, int width, int height, DXGI_FORMAT format, Stream output, CancellationToken token,
|
public async Task Recompress(Stream input, int width, int height, int mipMaps, DXGI_FORMAT format, Stream output, CancellationToken token,
|
||||||
bool leaveOpen = false)
|
bool leaveOpen = false)
|
||||||
{
|
{
|
||||||
var type = await DetermineType(input);
|
var type = await DetermineType(input);
|
||||||
@ -75,19 +75,19 @@ public class TexConvImageLoader : IImageLoader
|
|||||||
await input.CopyToAsync(fromFile.Path, token);
|
await input.CopyToAsync(fromFile.Path, token);
|
||||||
var toFile = fromFile.Path.FileName.RelativeTo(toFolder);
|
var toFile = fromFile.Path.FileName.RelativeTo(toFolder);
|
||||||
|
|
||||||
await ConvertImage(fromFile.Path, toFolder.Path, width, height, format, type);
|
await ConvertImage(fromFile.Path, toFolder.Path, width, height, mipMaps, format, type);
|
||||||
await using var fs = toFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
|
await using var fs = toFile.Open(FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||||
await fs.CopyToAsync(output, token);
|
await fs.CopyToAsync(output, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task ConvertImage(AbsolutePath from, AbsolutePath toFolder, int w, int h, DXGI_FORMAT format, Extension fileFormat)
|
public async Task ConvertImage(AbsolutePath from, AbsolutePath toFolder, int w, int h, int mipMaps, DXGI_FORMAT format, Extension fileFormat)
|
||||||
{
|
{
|
||||||
// User isn't renaming the file, so we don't have to create a temporary folder
|
// User isn't renaming the file, so we don't have to create a temporary folder
|
||||||
var ph = new ProcessHelper
|
var ph = new ProcessHelper
|
||||||
{
|
{
|
||||||
Path = @"Tools\texconv.exe".ToRelativePath().RelativeTo(KnownFolders.EntryPoint),
|
Path = @"Tools\texconv.exe".ToRelativePath().RelativeTo(KnownFolders.EntryPoint),
|
||||||
Arguments = new object[] {from, "-ft", fileFormat.ToString()[1..], "-f", format, "-o", toFolder, "-w", w, "-h", h, "-if", "CUBIC", "-singleproc"},
|
Arguments = new object[] {from, "-ft", fileFormat.ToString()[1..], "-f", format, "-o", toFolder, "-w", w, "-h", h, "-m", mipMaps, "-if", "CUBIC", "-singleproc"},
|
||||||
ThrowOnNonZeroExitCode = true,
|
ThrowOnNonZeroExitCode = true,
|
||||||
LogError = true
|
LogError = true
|
||||||
};
|
};
|
||||||
@ -100,7 +100,7 @@ public class TexConvImageLoader : IImageLoader
|
|||||||
await using var tmpFile = _tempManager.CreateFolder();
|
await using var tmpFile = _tempManager.CreateFolder();
|
||||||
var inFile = to.FileName.RelativeTo(tmpFile.Path);
|
var inFile = to.FileName.RelativeTo(tmpFile.Path);
|
||||||
await inFile.WriteAllAsync(from, CancellationToken.None);
|
await inFile.WriteAllAsync(from, CancellationToken.None);
|
||||||
await ConvertImage(inFile, to.Parent, state.Width, state.Height, state.Format, ext);
|
await ConvertImage(inFile, to.Parent, state.Width, state.Height, state.MipLevels, state.Format, ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internals
|
// Internals
|
||||||
@ -133,7 +133,8 @@ public class TexConvImageLoader : IImageLoader
|
|||||||
Width = int.Parse(data["width"]),
|
Width = int.Parse(data["width"]),
|
||||||
Height = int.Parse(data["height"]),
|
Height = int.Parse(data["height"]),
|
||||||
Format = Enum.Parse<DXGI_FORMAT>(data["format"]),
|
Format = Enum.Parse<DXGI_FORMAT>(data["format"]),
|
||||||
PerceptualHash = await GetPHash(path)
|
PerceptualHash = await GetPHash(path),
|
||||||
|
MipLevels = byte.Parse(data["mipLevels"])
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -149,7 +150,7 @@ public class TexConvImageLoader : IImageLoader
|
|||||||
throw new FileNotFoundException($"Can't hash non-existent file {path}");
|
throw new FileNotFoundException($"Can't hash non-existent file {path}");
|
||||||
|
|
||||||
await using var tmp = _tempManager.CreateFolder();
|
await using var tmp = _tempManager.CreateFolder();
|
||||||
await ConvertImage(path, tmp.Path, 512, 512, DXGI_FORMAT.R8G8B8A8_UNORM, Ext.Png);
|
await ConvertImage(path, tmp.Path, 512, 512, 1, DXGI_FORMAT.R8G8B8A8_UNORM, Ext.Png);
|
||||||
|
|
||||||
using var img = await Image.LoadAsync(path.FileName.RelativeTo(tmp.Path).ReplaceExtension(Ext.Png).ToString());
|
using var img = await Image.LoadAsync(path.FileName.RelativeTo(tmp.Path).ReplaceExtension(Ext.Png).ToString());
|
||||||
img.Mutate(x => x.Resize(512, 512, KnownResamplers.Welch).Grayscale(GrayscaleMode.Bt601));
|
img.Mutate(x => x.Resize(512, 512, KnownResamplers.Welch).Grayscale(GrayscaleMode.Bt601));
|
||||||
|
@ -261,7 +261,7 @@ public abstract class AInstaller<T>
|
|||||||
await using var s = await sf.GetStream();
|
await using var s = await sf.GetStream();
|
||||||
await using var of = destPath.Open(FileMode.Create, FileAccess.Write);
|
await using var of = destPath.Open(FileMode.Create, FileAccess.Write);
|
||||||
_logger.LogInformation("Recompressing {Filename}", tt.To.FileName);
|
_logger.LogInformation("Recompressing {Filename}", tt.To.FileName);
|
||||||
await ImageLoader.Recompress(s, tt.ImageState.Width, tt.ImageState.Height, tt.ImageState.Format,
|
await ImageLoader.Recompress(s, tt.ImageState.Width, tt.ImageState.Height, tt.ImageState.MipLevels, tt.ImageState.Format,
|
||||||
of, token);
|
of, token);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -64,7 +64,7 @@ public static class ServiceExtensions
|
|||||||
{
|
{
|
||||||
var diskCache = options.UseLocalCache
|
var diskCache = options.UseLocalCache
|
||||||
? new VFSDiskCache(s.GetService<TemporaryFileManager>()!.CreateFile().Path)
|
? new VFSDiskCache(s.GetService<TemporaryFileManager>()!.CreateFile().Path)
|
||||||
: new VFSDiskCache(KnownFolders.WabbajackAppLocal.Combine("GlobalVFSCache3.sqlite"));
|
: new VFSDiskCache(KnownFolders.WabbajackAppLocal.Combine("GlobalVFSCache4.sqlite"));
|
||||||
var cesiCache = new CesiVFSCache(s.GetRequiredService<ILogger<CesiVFSCache>>(),
|
var cesiCache = new CesiVFSCache(s.GetRequiredService<ILogger<CesiVFSCache>>(),
|
||||||
s.GetRequiredService<Client>());
|
s.GetRequiredService<Client>());
|
||||||
return new FallthroughVFSCache(new IVfsCache[] {diskCache});
|
return new FallthroughVFSCache(new IVfsCache[] {diskCache});
|
||||||
|
@ -136,13 +136,13 @@ public class Context
|
|||||||
token,
|
token,
|
||||||
fileNames.Keys.ToHashSet());
|
fileNames.Keys.ToHashSet());
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await using var stream = await sfn.GetStream();
|
await using var stream = await sfn.GetStream();
|
||||||
var hash = await stream.HashingCopy(Stream.Null, token);
|
var hash = await stream.HashingCopy(Stream.Null, token);
|
||||||
if (hash != file.Hash)
|
if (hash != file.Hash)
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
$"File {file.FullPath} is corrupt, please delete it and retry the installation");
|
$"File {file.FullPath} is corrupt, please delete it and retry the installation, {ex.Message}", ex);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ public static class IndexedVirtualFileExtensions
|
|||||||
{
|
{
|
||||||
bw.Write((ushort) state.Width);
|
bw.Write((ushort) state.Width);
|
||||||
bw.Write((ushort) state.Height);
|
bw.Write((ushort) state.Height);
|
||||||
|
bw.Write((byte)state.MipLevels);
|
||||||
bw.Write((byte) state.Format);
|
bw.Write((byte) state.Format);
|
||||||
state.PerceptualHash.Write(bw);
|
state.PerceptualHash.Write(bw);
|
||||||
}
|
}
|
||||||
@ -46,6 +47,7 @@ public static class IndexedVirtualFileExtensions
|
|||||||
{
|
{
|
||||||
Width = br.ReadUInt16(),
|
Width = br.ReadUInt16(),
|
||||||
Height = br.ReadUInt16(),
|
Height = br.ReadUInt16(),
|
||||||
|
MipLevels = br.ReadByte(),
|
||||||
Format = (DXGI_FORMAT) br.ReadByte(),
|
Format = (DXGI_FORMAT) br.ReadByte(),
|
||||||
PerceptualHash = PHash.Read(br)
|
PerceptualHash = PHash.Read(br)
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user