Skip to content

Commit a16fd85

Browse files
committed
Handle .tex files with broken mip map offsets on import, also remove unnecessary mipmaps (any after reaching minimum size once).
1 parent 4c0e6d2 commit a16fd85

File tree

5 files changed

+117
-1
lines changed

5 files changed

+117
-1
lines changed

Penumbra/Import/TexToolsImporter.Archives.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using OtterGui.Filesystem;
55
using Penumbra.Import.Structs;
66
using Penumbra.Mods;
7+
using Penumbra.Services;
78
using SharpCompress.Archives;
89
using SharpCompress.Archives.Rar;
910
using SharpCompress.Archives.SevenZip;
@@ -146,6 +147,9 @@ private void HandleFileMigrationsAndWrite(IReader reader)
146147
case ".mtrl":
147148
_migrationManager.MigrateMtrlDuringExtraction(reader, _currentModDirectory!.FullName, _extractionOptions);
148149
break;
150+
case ".tex":
151+
_migrationManager.FixMipMaps(reader, _currentModDirectory!.FullName, _extractionOptions);
152+
break;
149153
default:
150154
reader.WriteEntryToDirectory(_currentModDirectory!.FullName, _extractionOptions);
151155
break;

Penumbra/Import/TexToolsImporter.ModPack.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ private void ExtractMod(DirectoryInfo outDirectory, SimpleMod mod)
259259
{
260260
".mdl" => _migrationManager.MigrateTtmpModel(extractedFile.FullName, data.Data),
261261
".mtrl" => _migrationManager.MigrateTtmpMaterial(extractedFile.FullName, data.Data),
262+
".tex" => _migrationManager.FixTtmpMipMaps(extractedFile.FullName, data.Data),
262263
_ => data.Data,
263264
};
264265

Penumbra/Import/Textures/TexFileParser.cs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,75 @@ private static unsafe int CountMipLevels(Stream data, in TexMeta meta, in TexFil
6161
return 13;
6262
}
6363

64+
public static unsafe void FixMipOffsets(long size, ref TexFile.TexHeader header, out long newSize)
65+
{
66+
var width = (uint)header.Width;
67+
var height = (uint)header.Height;
68+
var format = header.Format.ToDXGI();
69+
var bits = format.BitsPerPixel();
70+
var totalSize = 80u;
71+
size -= totalSize;
72+
var minSize = format.IsCompressed() ? 4u : 1u;
73+
for (var i = 0; i < 13; ++i)
74+
{
75+
var requiredSize = (uint)((long)width * height * bits / 8);
76+
if (requiredSize > size)
77+
{
78+
newSize = totalSize;
79+
if (header.MipCount != i)
80+
{
81+
Penumbra.Log.Debug(
82+
$"-- Mip Map Count in TEX header was {header.MipCount}, but file only contains data for {i} Mip Maps, fixed.");
83+
FixLodOffsets(ref header, i);
84+
}
85+
86+
return;
87+
}
88+
89+
if (header.OffsetToSurface[i] != totalSize)
90+
{
91+
Penumbra.Log.Debug(
92+
$"-- Mip Map Offset {i + 1} in TEX header was {header.OffsetToSurface[i]} but should be {totalSize}, fixed.");
93+
header.OffsetToSurface[i] = totalSize;
94+
}
95+
96+
if (width == minSize && height == minSize)
97+
{
98+
newSize = totalSize;
99+
if (header.MipCount != i)
100+
{
101+
Penumbra.Log.Debug($"-- Reduced number of Mip Maps from {header.MipCount} to {i} due to minimum size constraints.");
102+
FixLodOffsets(ref header, i);
103+
}
104+
105+
return;
106+
}
107+
108+
totalSize += requiredSize;
109+
size -= requiredSize;
110+
width = Math.Max(width / 2, minSize);
111+
height = Math.Max(height / 2, minSize);
112+
}
113+
114+
newSize = totalSize;
115+
if (header.MipCount != 13)
116+
{
117+
Penumbra.Log.Debug($"-- Mip Map Count in TEX header was {header.MipCount}, but maximum is 13, fixed.");
118+
FixLodOffsets(ref header, 13);
119+
}
120+
121+
void FixLodOffsets(ref TexFile.TexHeader header, int index)
122+
{
123+
header.MipCount = index;
124+
if (header.LodOffset[2] >= header.MipCount)
125+
header.LodOffset[2] = (byte)(header.MipCount - 1);
126+
if (header.LodOffset[1] >= header.MipCount)
127+
header.LodOffset[1] = header.MipCount > 2 ? (byte)(header.MipCount - 2) : (byte)(header.MipCount - 1);
128+
for (++index; index < 13; ++index)
129+
header.OffsetToSurface[index] = 0;
130+
}
131+
}
132+
64133
private static unsafe void CopyData(ScratchImage image, BinaryReader r)
65134
{
66135
fixed (byte* ptr = image.Pixels)

Penumbra/Mods/Manager/ModImportManager.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ public void ClearImport()
7070
_import = null;
7171
}
7272

73-
7473
public bool AddUnpackedMod([NotNullWhen(true)] out Mod? mod)
7574
{
7675
if (!_modsToAdd.TryDequeue(out var directory))

Penumbra/Services/MigrationManager.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
using Dalamud.Interface.ImGuiNotification;
2+
using Lumina.Data.Files;
23
using OtterGui.Classes;
34
using OtterGui.Services;
5+
using Lumina.Extensions;
6+
using Penumbra.GameData.Files.Utility;
7+
using Penumbra.Import.Textures;
48
using SharpCompress.Common;
59
using SharpCompress.Readers;
610
using MdlFile = Penumbra.GameData.Files.MdlFile;
@@ -296,6 +300,26 @@ public void MigrateMtrlDuringExtraction(IReader reader, string directory, Extrac
296300
}
297301
}
298302

303+
public void FixMipMaps(IReader reader, string directory, ExtractionOptions options)
304+
{
305+
var path = Path.Combine(directory, reader.Entry.Key!);
306+
using var s = new MemoryStream();
307+
using var e = reader.OpenEntryStream();
308+
e.CopyTo(s);
309+
var length = s.Position;
310+
s.Seek(0, SeekOrigin.Begin);
311+
var br = new BinaryReader(s, Encoding.UTF8, true);
312+
var header = br.ReadStructure<TexFile.TexHeader>();
313+
br.Dispose();
314+
TexFileParser.FixMipOffsets(length, ref header, out var actualSize);
315+
316+
s.Seek(0, SeekOrigin.Begin);
317+
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
318+
using var f = File.Open(path, FileMode.Create, FileAccess.Write);
319+
f.Write(header);
320+
f.Write(s.GetBuffer().AsSpan(80, (int)actualSize - 80));
321+
}
322+
299323
/// <summary> Update the data of a .mdl file during TTMP extraction. Returns either the existing array or a new one. </summary>
300324
public byte[] MigrateTtmpModel(string path, byte[] data)
301325
{
@@ -348,6 +372,25 @@ public byte[] MigrateTtmpMaterial(string path, byte[] data)
348372
}
349373
}
350374

375+
public byte[] FixTtmpMipMaps(string path, byte[] data)
376+
{
377+
using var m = new MemoryStream(data);
378+
var br = new BinaryReader(m, Encoding.UTF8, true);
379+
var header = br.ReadStructure<TexFile.TexHeader>();
380+
br.Dispose();
381+
TexFileParser.FixMipOffsets(data.Length, ref header, out var actualSize);
382+
if (actualSize == data.Length)
383+
return data;
384+
385+
var ret = new byte[actualSize];
386+
using var m2 = new MemoryStream(ret);
387+
using var bw = new BinaryWriter(m2);
388+
bw.Write(header);
389+
bw.Write(data.AsSpan(80, (int)actualSize - 80));
390+
391+
return ret;
392+
}
393+
351394

352395
private static bool MigrateModel(string path, MdlFile mdl, bool createBackup)
353396
{

0 commit comments

Comments
 (0)