mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
Handle .tex files with broken mip map offsets on import, also remove unnecessary mipmaps (any after reaching minimum size once).
This commit is contained in:
parent
4c0e6d2a67
commit
a16fd85a7e
5 changed files with 117 additions and 1 deletions
|
|
@ -4,6 +4,7 @@ using Newtonsoft.Json.Linq;
|
||||||
using OtterGui.Filesystem;
|
using OtterGui.Filesystem;
|
||||||
using Penumbra.Import.Structs;
|
using Penumbra.Import.Structs;
|
||||||
using Penumbra.Mods;
|
using Penumbra.Mods;
|
||||||
|
using Penumbra.Services;
|
||||||
using SharpCompress.Archives;
|
using SharpCompress.Archives;
|
||||||
using SharpCompress.Archives.Rar;
|
using SharpCompress.Archives.Rar;
|
||||||
using SharpCompress.Archives.SevenZip;
|
using SharpCompress.Archives.SevenZip;
|
||||||
|
|
@ -146,6 +147,9 @@ public partial class TexToolsImporter
|
||||||
case ".mtrl":
|
case ".mtrl":
|
||||||
_migrationManager.MigrateMtrlDuringExtraction(reader, _currentModDirectory!.FullName, _extractionOptions);
|
_migrationManager.MigrateMtrlDuringExtraction(reader, _currentModDirectory!.FullName, _extractionOptions);
|
||||||
break;
|
break;
|
||||||
|
case ".tex":
|
||||||
|
_migrationManager.FixMipMaps(reader, _currentModDirectory!.FullName, _extractionOptions);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
reader.WriteEntryToDirectory(_currentModDirectory!.FullName, _extractionOptions);
|
reader.WriteEntryToDirectory(_currentModDirectory!.FullName, _extractionOptions);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -259,6 +259,7 @@ public partial class TexToolsImporter
|
||||||
{
|
{
|
||||||
".mdl" => _migrationManager.MigrateTtmpModel(extractedFile.FullName, data.Data),
|
".mdl" => _migrationManager.MigrateTtmpModel(extractedFile.FullName, data.Data),
|
||||||
".mtrl" => _migrationManager.MigrateTtmpMaterial(extractedFile.FullName, data.Data),
|
".mtrl" => _migrationManager.MigrateTtmpMaterial(extractedFile.FullName, data.Data),
|
||||||
|
".tex" => _migrationManager.FixTtmpMipMaps(extractedFile.FullName, data.Data),
|
||||||
_ => data.Data,
|
_ => data.Data,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,75 @@ public static class TexFileParser
|
||||||
return 13;
|
return 13;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static unsafe void FixMipOffsets(long size, ref TexFile.TexHeader header, out long newSize)
|
||||||
|
{
|
||||||
|
var width = (uint)header.Width;
|
||||||
|
var height = (uint)header.Height;
|
||||||
|
var format = header.Format.ToDXGI();
|
||||||
|
var bits = format.BitsPerPixel();
|
||||||
|
var totalSize = 80u;
|
||||||
|
size -= totalSize;
|
||||||
|
var minSize = format.IsCompressed() ? 4u : 1u;
|
||||||
|
for (var i = 0; i < 13; ++i)
|
||||||
|
{
|
||||||
|
var requiredSize = (uint)((long)width * height * bits / 8);
|
||||||
|
if (requiredSize > size)
|
||||||
|
{
|
||||||
|
newSize = totalSize;
|
||||||
|
if (header.MipCount != i)
|
||||||
|
{
|
||||||
|
Penumbra.Log.Debug(
|
||||||
|
$"-- Mip Map Count in TEX header was {header.MipCount}, but file only contains data for {i} Mip Maps, fixed.");
|
||||||
|
FixLodOffsets(ref header, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.OffsetToSurface[i] != totalSize)
|
||||||
|
{
|
||||||
|
Penumbra.Log.Debug(
|
||||||
|
$"-- Mip Map Offset {i + 1} in TEX header was {header.OffsetToSurface[i]} but should be {totalSize}, fixed.");
|
||||||
|
header.OffsetToSurface[i] = totalSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width == minSize && height == minSize)
|
||||||
|
{
|
||||||
|
newSize = totalSize;
|
||||||
|
if (header.MipCount != i)
|
||||||
|
{
|
||||||
|
Penumbra.Log.Debug($"-- Reduced number of Mip Maps from {header.MipCount} to {i} due to minimum size constraints.");
|
||||||
|
FixLodOffsets(ref header, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
totalSize += requiredSize;
|
||||||
|
size -= requiredSize;
|
||||||
|
width = Math.Max(width / 2, minSize);
|
||||||
|
height = Math.Max(height / 2, minSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
newSize = totalSize;
|
||||||
|
if (header.MipCount != 13)
|
||||||
|
{
|
||||||
|
Penumbra.Log.Debug($"-- Mip Map Count in TEX header was {header.MipCount}, but maximum is 13, fixed.");
|
||||||
|
FixLodOffsets(ref header, 13);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixLodOffsets(ref TexFile.TexHeader header, int index)
|
||||||
|
{
|
||||||
|
header.MipCount = index;
|
||||||
|
if (header.LodOffset[2] >= header.MipCount)
|
||||||
|
header.LodOffset[2] = (byte)(header.MipCount - 1);
|
||||||
|
if (header.LodOffset[1] >= header.MipCount)
|
||||||
|
header.LodOffset[1] = header.MipCount > 2 ? (byte)(header.MipCount - 2) : (byte)(header.MipCount - 1);
|
||||||
|
for (++index; index < 13; ++index)
|
||||||
|
header.OffsetToSurface[index] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static unsafe void CopyData(ScratchImage image, BinaryReader r)
|
private static unsafe void CopyData(ScratchImage image, BinaryReader r)
|
||||||
{
|
{
|
||||||
fixed (byte* ptr = image.Pixels)
|
fixed (byte* ptr = image.Pixels)
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,6 @@ public class ModImportManager(ModManager modManager, Configuration config, ModEd
|
||||||
_import = null;
|
_import = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public bool AddUnpackedMod([NotNullWhen(true)] out Mod? mod)
|
public bool AddUnpackedMod([NotNullWhen(true)] out Mod? mod)
|
||||||
{
|
{
|
||||||
if (!_modsToAdd.TryDequeue(out var directory))
|
if (!_modsToAdd.TryDequeue(out var directory))
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
using Dalamud.Interface.ImGuiNotification;
|
using Dalamud.Interface.ImGuiNotification;
|
||||||
|
using Lumina.Data.Files;
|
||||||
using OtterGui.Classes;
|
using OtterGui.Classes;
|
||||||
using OtterGui.Services;
|
using OtterGui.Services;
|
||||||
|
using Lumina.Extensions;
|
||||||
|
using Penumbra.GameData.Files.Utility;
|
||||||
|
using Penumbra.Import.Textures;
|
||||||
using SharpCompress.Common;
|
using SharpCompress.Common;
|
||||||
using SharpCompress.Readers;
|
using SharpCompress.Readers;
|
||||||
using MdlFile = Penumbra.GameData.Files.MdlFile;
|
using MdlFile = Penumbra.GameData.Files.MdlFile;
|
||||||
|
|
@ -296,6 +300,26 @@ public class MigrationManager(Configuration config) : IService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void FixMipMaps(IReader reader, string directory, ExtractionOptions options)
|
||||||
|
{
|
||||||
|
var path = Path.Combine(directory, reader.Entry.Key!);
|
||||||
|
using var s = new MemoryStream();
|
||||||
|
using var e = reader.OpenEntryStream();
|
||||||
|
e.CopyTo(s);
|
||||||
|
var length = s.Position;
|
||||||
|
s.Seek(0, SeekOrigin.Begin);
|
||||||
|
var br = new BinaryReader(s, Encoding.UTF8, true);
|
||||||
|
var header = br.ReadStructure<TexFile.TexHeader>();
|
||||||
|
br.Dispose();
|
||||||
|
TexFileParser.FixMipOffsets(length, ref header, out var actualSize);
|
||||||
|
|
||||||
|
s.Seek(0, SeekOrigin.Begin);
|
||||||
|
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
|
||||||
|
using var f = File.Open(path, FileMode.Create, FileAccess.Write);
|
||||||
|
f.Write(header);
|
||||||
|
f.Write(s.GetBuffer().AsSpan(80, (int)actualSize - 80));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> Update the data of a .mdl file during TTMP extraction. Returns either the existing array or a new one. </summary>
|
/// <summary> Update the data of a .mdl file during TTMP extraction. Returns either the existing array or a new one. </summary>
|
||||||
public byte[] MigrateTtmpModel(string path, byte[] data)
|
public byte[] MigrateTtmpModel(string path, byte[] data)
|
||||||
{
|
{
|
||||||
|
|
@ -348,6 +372,25 @@ public class MigrationManager(Configuration config) : IService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] FixTtmpMipMaps(string path, byte[] data)
|
||||||
|
{
|
||||||
|
using var m = new MemoryStream(data);
|
||||||
|
var br = new BinaryReader(m, Encoding.UTF8, true);
|
||||||
|
var header = br.ReadStructure<TexFile.TexHeader>();
|
||||||
|
br.Dispose();
|
||||||
|
TexFileParser.FixMipOffsets(data.Length, ref header, out var actualSize);
|
||||||
|
if (actualSize == data.Length)
|
||||||
|
return data;
|
||||||
|
|
||||||
|
var ret = new byte[actualSize];
|
||||||
|
using var m2 = new MemoryStream(ret);
|
||||||
|
using var bw = new BinaryWriter(m2);
|
||||||
|
bw.Write(header);
|
||||||
|
bw.Write(data.AsSpan(80, (int)actualSize - 80));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static bool MigrateModel(string path, MdlFile mdl, bool createBackup)
|
private static bool MigrateModel(string path, MdlFile mdl, bool createBackup)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue