Skip to content

Commit 07970c1

Browse files
committed
Add shrink ray tool
1 parent 0542bf0 commit 07970c1

File tree

3 files changed

+229
-2
lines changed

3 files changed

+229
-2
lines changed

xivModdingFramework/Helpers/IOUtil.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -874,7 +874,7 @@ public static int RoundToPowerOfTwo(int x)
874874
return max - x < x - min ? max : min;
875875
}
876876

877-
private static int CeilPower2(int x)
877+
public static int CeilPower2(int x)
878878
{
879879
if (x < 2)
880880
{
@@ -883,7 +883,7 @@ private static int CeilPower2(int x)
883883
return (int)Math.Pow(2, (int)Math.Log(x - 1, 2) + 1);
884884
}
885885

886-
private static int FloorPower2(int x)
886+
public static int FloorPower2(int x)
887887
{
888888
if (x < 1)
889889
{

xivModdingFramework/Mods/ShrinkRay.cs

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.IO;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
using xivModdingFramework.Helpers;
8+
using xivModdingFramework.Models.DataContainers;
9+
using xivModdingFramework.Models.FileTypes;
10+
using xivModdingFramework.Mods.DataContainers;
11+
using xivModdingFramework.SqPack.FileTypes;
12+
using xivModdingFramework.Textures.DataContainers;
13+
using xivModdingFramework.Textures.FileTypes;
14+
15+
namespace xivModdingFramework.Mods
16+
{
17+
public static class ShrinkRay
18+
{
19+
public class ShrinkRaySettings
20+
{
21+
public bool ResaveModels = true;
22+
public bool RemoveExtraFiles = true;
23+
public int MaxTextureSize = 2048;
24+
}
25+
26+
public static async Task<WizardData> ShrinkModpack(string modpackPath, ShrinkRaySettings settings = null)
27+
{
28+
var data = await WizardData.FromModpack(modpackPath);
29+
if (data == null)
30+
{
31+
throw new ArgumentException("The given modpack does not exist or is invalid.");
32+
}
33+
34+
return await ShrinkModpack(data);
35+
}
36+
public static async Task<WizardData> ShrinkModpack(WizardData modpack, ShrinkRaySettings settings = null)
37+
{
38+
if(settings == null)
39+
{
40+
settings = new ShrinkRaySettings();
41+
}
42+
43+
modpack.ClearNulls();
44+
45+
await Task.Run(async () =>
46+
{
47+
var rtx = ModTransaction.BeginReadonlyTransaction();
48+
await ForEachOptionParalell(modpack, async (opt) =>
49+
{
50+
var newKeys = new Dictionary<string, FileStorageInformation>(opt.Files.Count);
51+
foreach (var kv in opt.Files)
52+
{
53+
var path = kv.Key;
54+
var file = kv.Value;
55+
var ext = Path.GetExtension(path);
56+
57+
if (settings.ResaveModels && ext == ".mdl")
58+
{
59+
file = await ResaveModel(path, file);
60+
}
61+
else if (ext == ".tex" || ext == ".atex")
62+
{
63+
file = await ResaveTexture(path, file, settings.MaxTextureSize);
64+
}
65+
66+
newKeys.Add(path, file);
67+
}
68+
69+
opt.Files = newKeys;
70+
});
71+
72+
if (settings.RemoveExtraFiles)
73+
{
74+
modpack.ExtraFiles = new Dictionary<string, string>();
75+
}
76+
});
77+
78+
return modpack;
79+
}
80+
81+
private static async Task ForEachOptionParalell(WizardData data, Func<WizardStandardOptionData, Task> act)
82+
{
83+
var tasks = new List<Task>();
84+
foreach (var p in data.DataPages)
85+
{
86+
foreach (var g in p.Groups)
87+
{
88+
foreach (var o in g.Options)
89+
{
90+
var sData = o.StandardData;
91+
if (sData == null) continue;
92+
93+
tasks.Add(Task.Run(async () => await act(sData)));
94+
}
95+
}
96+
}
97+
await Task.WhenAll(tasks);
98+
}
99+
100+
private static async Task<FileStorageInformation> ResaveModel(string path, FileStorageInformation model)
101+
{
102+
var file = model;
103+
try
104+
{
105+
var data = await TransactionDataHandler.GetUncompressedFile(model);
106+
var raw = Mdl.GetXivMdl(data, path);
107+
var ttm = TTModel.FromRaw(raw);
108+
ttm.MdlVersion = 6;
109+
110+
var res = Mdl.MakeUncompressedMdlFile(ttm, raw);
111+
var tempPath = IOUtil.GetFrameworkTempFile();
112+
File.WriteAllBytes(tempPath, res);
113+
114+
file = new FileStorageInformation()
115+
{
116+
FileSize = res.Length,
117+
RealOffset = 0,
118+
RealPath = tempPath,
119+
StorageType = EFileStorageType.UncompressedIndividual,
120+
};
121+
} catch(Exception ex)
122+
{
123+
Trace.WriteLine(ex);
124+
}
125+
return file;
126+
}
127+
128+
129+
private static async Task<FileStorageInformation> ResaveTexture(string path, FileStorageInformation texture, int maxSize = -1)
130+
{
131+
var file = texture;
132+
try
133+
{
134+
var data = await TransactionDataHandler.GetUncompressedFile(texture);
135+
var xTex = XivTex.FromUncompressedTex(data);
136+
137+
await Tex.EnsureValidSize(xTex, maxSize);
138+
var res = xTex.ToUncompressedTex();
139+
var tempPath = IOUtil.GetFrameworkTempFile();
140+
File.WriteAllBytes(tempPath, res);
141+
142+
file = new FileStorageInformation()
143+
{
144+
FileSize = res.Length,
145+
RealOffset = 0,
146+
RealPath = tempPath,
147+
StorageType = EFileStorageType.UncompressedIndividual,
148+
};
149+
}
150+
catch (Exception ex)
151+
{
152+
Trace.WriteLine(ex);
153+
}
154+
return file;
155+
}
156+
}
157+
}

xivModdingFramework/Textures/FileTypes/Tex.cs

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1408,6 +1408,76 @@ public static byte[] GetColorsetDyeInformationFromFile(string externalFilePath)
14081408
}
14091409
return colorSetExtraData;
14101410
}
1411+
1412+
1413+
/// <summary>
1414+
/// Ensures the given texture meets normal size requirements, with an optional max size.
1415+
/// Returns true if the texture was altered.
1416+
/// </summary>
1417+
/// <param name="tex"></param>
1418+
/// <param name="maxSize"></param>
1419+
/// <returns></returns>
1420+
public static async Task<bool> EnsureValidSize(XivTex tex, int maxSize = -1)
1421+
{
1422+
var usesMips = false;
1423+
if (!string.IsNullOrWhiteSpace(tex.FilePath))
1424+
{
1425+
var df = IOUtil.GetDataFileFromPath(tex.FilePath);
1426+
if(df == XivDataFile._06_Ui)
1427+
{
1428+
usesMips = false;
1429+
} else
1430+
{
1431+
usesMips = true;
1432+
}
1433+
}
1434+
else
1435+
{
1436+
usesMips = tex.MipMapCount > 1;
1437+
}
1438+
1439+
var newWidth = tex.Width;
1440+
var newHeight = tex.Height;
1441+
if (usesMips)
1442+
{
1443+
if(!IOUtil.IsPowerOfTwo(tex.Width))
1444+
{
1445+
newWidth = IOUtil.RoundToPowerOfTwo(tex.Width);
1446+
}
1447+
1448+
if (!IOUtil.IsPowerOfTwo(tex.Height))
1449+
{
1450+
newHeight = IOUtil.RoundToPowerOfTwo(tex.Height);
1451+
}
1452+
}
1453+
1454+
if (maxSize > 0)
1455+
{
1456+
while (newWidth > maxSize || newHeight > maxSize)
1457+
{
1458+
newWidth /= 2;
1459+
newHeight /= 2;
1460+
}
1461+
}
1462+
1463+
var regenMips = false;
1464+
if(usesMips && tex.MipMapCount == 1)
1465+
{
1466+
regenMips = true;
1467+
tex.MipMapCount = GetMipCount(tex.Width, tex.Height);
1468+
} else if(!usesMips && tex.MipMapCount > 1)
1469+
{
1470+
regenMips = true;
1471+
tex.MipMapCount = 1;
1472+
}
1473+
1474+
if(newWidth != tex.Width || newHeight != tex.Height || regenMips)
1475+
{
1476+
await ResizeXivTx(tex, newWidth, newHeight, false);
1477+
return true;
1478+
}
1479+
return false;
1480+
}
14111481
#endregion
14121482

14131483
#region Static Dictionaries

0 commit comments

Comments
 (0)