Skip to content

Commit f3747f6

Browse files
authored
Merge branch 'TexTools:master' into master
2 parents 8d8c5c4 + 14a511e commit f3747f6

File tree

12 files changed

+189
-79
lines changed

12 files changed

+189
-79
lines changed

xivModdingFramework/Cache/XivDependencyGraph.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,12 @@ public static XivDependencyRootInfo ExtractRootInfo(string internalFilePath)
680680
match = _slotRegex.Match(internalFilePath);
681681
if (match.Success)
682682
{
683-
info.Slot = match.Groups[1].Value;
683+
// Validate the slot name to avoid matching on arbitrary three letter strings that may be present
684+
if (XivItemTypes.GetAvailableSlots(info.PrimaryType).Contains(match.Groups[1].Value)
685+
|| (info.SecondaryType.HasValue && XivItemTypes.GetAvailableSlots(info.SecondaryType.Value).Contains(match.Groups[1].Value)))
686+
{
687+
info.Slot = match.Groups[1].Value;
688+
}
684689
}
685690
}
686691
else

xivModdingFramework/Exd/FileTypes/ExColumnExpectations.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,16 +196,18 @@ internal static class ExColumnExpectations
196196
{
197197
{ "Name", ( 0, ExcelColumnDataType.String ) },
198198
{ "ModelCharaId", ( 8, ExcelColumnDataType.UInt16 ) },
199-
{ "Icon", ( 26, ExcelColumnDataType.UInt16 ) },
199+
{ "Icon", ( 28, ExcelColumnDataType.UInt16 ) },
200200
};
201201

202202
if (language == XivLanguage.Korean)
203203
{
204204
// Set up overrides here if necessary for KR.
205+
columnExpectations["Icon"] = (26, ExcelColumnDataType.UInt16);
205206
}
206207
else if (language == XivLanguage.Chinese)
207208
{
208209
// Set up overrides here if necessary for CN.
210+
columnExpectations["Icon"] = (26, ExcelColumnDataType.UInt16);
209211
}
210212

211213
return columnExpectations;

xivModdingFramework/Helpers/IOUtil.cs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,14 @@
4040
using System.Management;
4141
using Newtonsoft.Json;
4242
using Newtonsoft.Json.Linq;
43+
using System.Text;
4344

4445
namespace xivModdingFramework.Helpers
4546
{
4647
public static class IOUtil
4748
{
49+
private static readonly HashSet<char> _InvalidFileNameChars = new(Path.GetInvalidFileNameChars());
50+
4851
/// <summary>
4952
/// Compresses raw byte data.
5053
/// </summary>
@@ -729,16 +732,32 @@ public static bool IsDirectory(string path)
729732

730733
public static string MakePathSafe(string fileName, bool makeLowercase = true)
731734
{
732-
foreach (var c in Path.GetInvalidFileNameChars())
733-
{
734-
fileName = fileName.Replace(c, '-');
735-
}
736-
if (makeLowercase)
735+
return IOUtil.MakePathSafe(fileName, '-', makeLowercase);
736+
}
737+
738+
public static string MakePathSafe(string fileName, char rep, bool makeLowercase = true)
739+
{
740+
// This approach walks the string once, and hash lookups are O(1).
741+
StringBuilder ret = new(fileName.Length);
742+
foreach (var c in fileName)
737743
{
738-
fileName = fileName.ToLower();
744+
if (_InvalidFileNameChars.Contains(c))
745+
{
746+
ret.Append(rep);
747+
}
748+
else if (makeLowercase)
749+
{
750+
ret.Append(Char.ToLower(c));
751+
}
752+
else
753+
{
754+
ret.Append(c);
755+
}
739756
}
740-
return fileName.Trim();
757+
758+
return ret.ToString().Trim();
741759
}
760+
742761
public static void CopyFolder(string sourcePath, string targetPath)
743762
{
744763
sourcePath = MakeLongPath(sourcePath);
@@ -962,6 +981,5 @@ await Task.Run(async () =>
962981
Trace.WriteLine(ex);
963982
}
964983
}
965-
966984
}
967985
}

xivModdingFramework/Materials/DataContainers/XivMtrl.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ public string GetMaterialIdentifier()
508508
public string GetItemTypeIdentifier()
509509
{
510510
// This regex feels a little janky, but it's good enough for now.
511-
var match = Regex.Match(MTRLPath, "_([a-z]{3})_[a-z0-9]\\.mtrl");
511+
var match = Regex.Match(MTRLPath, "_([a-z]{3})_[a-z0-9]+\\.mtrl");
512512
if (match.Success)
513513
{
514514
return "_" + match.Groups[1].Value;

xivModdingFramework/Materials/FileTypes/STM.cs

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ public Half[] GetData(int offset, int dyeId = 0)
201201
}},
202202
};
203203

204-
public StainingTemplateEntry(byte[] data, int offset, EStainingTemplate templateType)
204+
public StainingTemplateEntry(byte[] data, int offset, EStainingTemplate templateType, bool oldFormat = false)
205205
{
206206
var arrayEnds = new List<ushort>();
207207
var start = offset;
@@ -212,9 +212,9 @@ public StainingTemplateEntry(byte[] data, int offset, EStainingTemplate template
212212
_ItemCount = 5;
213213
}
214214

215+
var numDyes = oldFormat ? 128 : 254;
215216

216217
// This format sucks.
217-
var moreData = new List<ushort>();
218218
for (int i = 0; i < _ItemCount; i++)
219219
{
220220
arrayEnds.Add(BitConverter.ToUInt16(data, offset));
@@ -244,17 +244,17 @@ public StainingTemplateEntry(byte[] data, int offset, EStainingTemplate template
244244
// Single entry used for everything.
245245
type = StainingTemplateArrayType.Singleton;
246246
}
247-
if(arraySize == 0)
247+
else if(arraySize == 0)
248248
{
249249
// No data.
250250
continue;
251251
}
252-
else if (arraySize < 128)
252+
else
253253
{
254254
// Indexed array, where we have [n] # of real entries,
255-
// then 128 one-byte index entries referencing those [n] entries.
255+
// then 254 one-byte index entries referencing those [n] entries.
256256
var totalBytes = (arrayEnds[x] - lastOffset) *2;
257-
var remBytes = totalBytes - 128;
257+
var remBytes = totalBytes - numDyes;
258258

259259
indexStart = start + headerSize + (lastOffset * 2) + remBytes;
260260

@@ -287,9 +287,7 @@ public StainingTemplateEntry(byte[] data, int offset, EStainingTemplate template
287287
if(type == StainingTemplateArrayType.Indexed)
288288
{
289289
var nArray = new List<Half[]>();
290-
var indexes = new byte[128];
291-
var indexCopy = data.Skip(indexStart).Take(129).ToArray();
292-
for (int i = 0; i < 128; i++)
290+
for (int i = 0; i < numDyes; i++)
293291
{
294292
try
295293
{
@@ -321,7 +319,7 @@ public StainingTemplateEntry(byte[] data, int offset, EStainingTemplate template
321319

322320
if (halfData.Count == 1)
323321
{
324-
for (int i = 0; i < 127; i++)
322+
for (int i = 0; i < numDyes; i++)
325323
{
326324
halfData.Add(halfData[0]);
327325
}
@@ -337,8 +335,6 @@ public StainingTemplateEntry(byte[] data, int offset, EStainingTemplate template
337335

338336
lastOffset = arrayEnds[x];
339337
}
340-
341-
var length = lastOffset;
342338
}
343339

344340
}
@@ -378,41 +374,50 @@ public StainingTemplateFile(byte[] data, EStainingTemplate templateType)
378374
TemplateType = templateType;
379375
// Get header size and # of entries.
380376
var Header = BitConverter.ToUInt16(data, 0);
377+
var Version = BitConverter.ToUInt16(data, 2);
381378
var entryCount = BitConverter.ToUInt16(data, 4);
382379
var unknown = BitConverter.ToUInt16(data, 6);
383380

381+
// Number got bigger since Patch 7.2
382+
// stainingtemplate_gud.stm (DT / non-legacy) can be distinguished by Version number
383+
// stainingtemplate.stm (EW / legacy) instead uses a janky heuristic
384+
// Once CN/KR are updated to 7.2, this logic can be removed
385+
bool oldFormat = false;
386+
if (templateType == EStainingTemplate.Dawntrail && Version < 0x201)
387+
oldFormat = true;
388+
else if (templateType == EStainingTemplate.Endwalker && (data[0x0A] != 0x00 || data[0x0B] != 0x00))
389+
oldFormat = true;
384390

385-
Dictionary<ushort, int> entryOffsets = new Dictionary<ushort, int>();
391+
Dictionary<uint, int> entryOffsets = new Dictionary<uint, int>();
386392

387-
List<ushort> keys = new List<ushort>();
388-
List<ushort> values = new List<ushort>();
389-
List<int> sizes = new List<int>();
393+
List<uint> keys = new List<uint>();
390394
var offset = 8;
391395

392396
// Read template Ids
393397
for (int i = 0; i < entryCount; i++)
394398
{
395-
var key = BitConverter.ToUInt16(data, offset);
399+
var key = oldFormat ? (uint)BitConverter.ToUInt16(data, offset) : BitConverter.ToUInt32(data, offset);
396400
entryOffsets.Add(key, 0);
397401
keys.Add(key);
398-
offset += 2;
402+
offset += oldFormat ? 2 : 4;
399403
}
400404

401-
const int _headerEntrySize = 4;
405+
const int _headerEntrySize = 8;
402406
var endOfHeader = (8 + (_headerEntrySize * entryCount));
403407

404408
for (int i = 0; i < entryCount; i++)
405409
{
406-
entryOffsets[keys[i]] = ((BitConverter.ToUInt16(data, offset) * 2) + endOfHeader);
407-
offset += 2;
410+
var rawOffset = oldFormat ? (uint)BitConverter.ToUInt16(data, offset) : BitConverter.ToUInt32(data, offset);
411+
entryOffsets[keys[i]] = (int)rawOffset * 2 + endOfHeader;
412+
offset += oldFormat ? 2 : 4;
408413
}
409414

410415

411416
var idx = 0;
412417
foreach (var kv in entryOffsets)
413418
{
414-
var entry = new StainingTemplateEntry(data, kv.Value, templateType);
415-
Templates.Add(kv.Key, entry);
419+
var entry = new StainingTemplateEntry(data, kv.Value, templateType, oldFormat);
420+
Templates.Add((ushort)kv.Key, entry);
416421
idx++;
417422
}
418423
}

xivModdingFramework/Models/DataContainers/MdlModelData.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ public class MdlModelData
187187
/// <summary>
188188
/// Unknown Usage
189189
/// </summary>
190-
public short Unknown14 { get; set; }
190+
public short Patch72TableSize { get; set; }
191191

192192
/// <summary>
193193
/// Padding?
@@ -245,7 +245,7 @@ public static MdlModelData Read(BinaryReader br)
245245
BoneSetSize = br.ReadInt16(),
246246

247247
Unknown13 = br.ReadInt16(),
248-
Unknown14 = br.ReadInt16(),
248+
Patch72TableSize = br.ReadInt16(),
249249
Unknown15 = br.ReadInt16(),
250250
Unknown16 = br.ReadInt16(),
251251
Unknown17 = br.ReadInt16()
@@ -281,7 +281,7 @@ public void Write(BinaryWriter br)
281281
br.Write(NeckMorphTableSize);
282282
br.Write(BoneSetSize);
283283
br.Write(Unknown13);
284-
br.Write(Unknown14);
284+
br.Write(Patch72TableSize);
285285
br.Write(Unknown15);
286286
br.Write(Unknown16);
287287
br.Write(Unknown17);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// xivModdingFramework
2+
// Copyright © 2018 Rafael Gonzalez - All Rights Reserved
3+
//
4+
// This program is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// This program is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
17+
namespace xivModdingFramework.Models.DataContainers
18+
{
19+
public class UnknownDataPatch72
20+
{
21+
/// <summary>
22+
/// This data block is currently unknown
23+
/// </summary>
24+
/// <remarks>
25+
/// The size of this unknown data block is [ MdlModelData.Patch72TableSize * 16 ]
26+
/// </remarks>
27+
public byte[] Unknown { get; set; }
28+
}
29+
}

xivModdingFramework/Models/DataContainers/XivMdl.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,5 +146,10 @@ public class XivMdl
146146
/// This data is present on heads and seems to affect the shape of the neck on the body mesh
147147
/// </summary>
148148
public List<NeckMorphEntry> NeckMorphTable { get; set; }
149+
150+
/// <summary>
151+
/// Currently unknown data
152+
/// </summary>
153+
public UnknownDataPatch72 UnkDataPatch72 { get; set; }
149154
}
150155
}

xivModdingFramework/Models/FileTypes/Mdl.cs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,15 @@ public static XivMdl GetXivMdl(byte[] mdlData, string mdlPath = "")
943943
}
944944
#endregion
945945

946+
#region Patch 7.2 Unknown Data
947+
// Something to do with shadows (appears on face models new in Patch 7.2)
948+
var unkDataPatch72 = new UnknownDataPatch72
949+
{
950+
Unknown = br.ReadBytes(xivMdl.ModelData.Patch72TableSize * 16)
951+
};
952+
xivMdl.UnkDataPatch72 = unkDataPatch72;
953+
#endregion
954+
946955
#region Padding
947956
// Padding
948957
xivMdl.PaddingSize = br.ReadByte();
@@ -1004,9 +1013,10 @@ public static XivMdl GetXivMdl(byte[] mdlData, string mdlPath = "")
10041013
}
10051014
}
10061015
else {
1007-
if (xivMdl.LoDList[0].VertexDataOffset < br.BaseStream.Position
1008-
|| (xivMdl.LoDList[0].VertexDataOffset % 8 != br.BaseStream.Position % 8)
1009-
&& xivMdl.LoDList[1].VertexDataSize == 0)
1016+
if ((xivMdl.LoDList[0].VertexDataOffset < br.BaseStream.Position
1017+
|| (xivMdl.LoDList[0].VertexDataOffset % 8 != br.BaseStream.Position % 8))
1018+
&& xivMdl.LoDList[1].VertexDataSize == 0 // Avoid applying this fix to vanilla models
1019+
&& xivMdl.ModelData.NeckMorphTableSize != 0x0A) // Avoid applying to face models, which were written incorrectly after patch 7.1
10101020
{
10111021

10121022
var delta = (int)(xivMdl.LoDList[0].VertexDataOffset - br.BaseStream.Position);
@@ -3032,7 +3042,11 @@ public static byte[] MakeUncompressedMdlFile(TTModel ttModel, XivMdl ogMdl, Acti
30323042

30333043
// Unknowns that are probably partly padding.
30343044
basicModelBlock.AddRange(BitConverter.GetBytes(ogModelData.Unknown13));
3035-
basicModelBlock.AddRange(BitConverter.GetBytes(ogModelData.Unknown14));
3045+
// XXX: Not preserving new Patch 7.2 face data
3046+
// It seems to have a dependency on the number of vertices, and thus crashes with custom models with fewer of them
3047+
// It also has a dependency on the order of vertices, and thus has poor results when Blender decides to shuffle them around...
3048+
//basicModelBlock.AddRange(BitConverter.GetBytes(ogModelData.Patch72TableSize));
3049+
basicModelBlock.AddRange(new byte[] { 0, 0 });
30363050
basicModelBlock.AddRange(BitConverter.GetBytes(ogModelData.Unknown15));
30373051
basicModelBlock.AddRange(BitConverter.GetBytes(ogModelData.Unknown16));
30383052
basicModelBlock.AddRange(BitConverter.GetBytes(ogModelData.Unknown17));
@@ -3611,6 +3625,10 @@ public static byte[] MakeUncompressedMdlFile(TTModel ttModel, XivMdl ogMdl, Acti
36113625
bones.Add((byte)boneset0Index);
36123626
}
36133627

3628+
// Fully abort building neck data if we gave up inside the previous loop
3629+
if (basicModelBlock[neckMorphTableSizePointer] == 0)
3630+
break;
3631+
36143632
// Serialize
36153633
neckMorphDataBlock.AddRange(BitConverter.GetBytes(positionAdjust.X));
36163634
neckMorphDataBlock.AddRange(BitConverter.GetBytes(positionAdjust.Y));
@@ -3631,6 +3649,14 @@ public static byte[] MakeUncompressedMdlFile(TTModel ttModel, XivMdl ogMdl, Acti
36313649
}
36323650
#endregion
36333651

3652+
// Patch 7.2 Unknown Data
3653+
#region Patch 7.2 Unknown Data
3654+
// XXX: Not preserving Patch 7.2 face data
3655+
//var unknownPatch72DataBlock = ogMdl.UnkDataPatch72.Unknown;
3656+
var unknownPatch72DataBlock = Array.Empty<byte>();
3657+
3658+
#endregion
3659+
36343660
// Padding
36353661
#region Padding Data Block
36363662

@@ -3786,7 +3812,7 @@ public static byte[] MakeUncompressedMdlFile(TTModel ttModel, XivMdl ogMdl, Acti
37863812
// This is the offset to the beginning of the vertex data
37873813
var combinedDataBlockSize = _MdlHeaderSize + vertexInfoBlock.Count + pathInfoBlock.Count + basicModelBlock.Count + unknownDataBlock0.Length + (60 * ogMdl.LoDList.Count) + extraMeshesBlock.Count + meshDataBlock.Count +
37883814
attributePathDataBlock.Count + (unknownDataBlock1?.Length ?? 0) + meshPartDataBlock.Count + unknownDataBlock2.Length + matPathOffsetDataBlock.Count + bonePathOffsetDataBlock.Count +
3789-
boneSetsBlock.Count + FullShapeDataBlock.Count + partBoneSetsBlock.Count + neckMorphDataBlock.Count + paddingDataBlock.Count + boundingBoxDataBlock.Count + boneBoundingBoxDataBlock.Count;
3815+
boneSetsBlock.Count + FullShapeDataBlock.Count + partBoneSetsBlock.Count + neckMorphDataBlock.Count + unknownPatch72DataBlock.Length + paddingDataBlock.Count + boundingBoxDataBlock.Count + boneBoundingBoxDataBlock.Count;
37903816

37913817
var lodDataBlock = new List<byte>();
37923818
List<int> indexStartInjectPointers = new List<int>();
@@ -3874,6 +3900,7 @@ public static byte[] MakeUncompressedMdlFile(TTModel ttModel, XivMdl ogMdl, Acti
38743900
modelDataBlock.AddRange(FullShapeDataBlock);
38753901
modelDataBlock.AddRange(partBoneSetsBlock);
38763902
modelDataBlock.AddRange(neckMorphDataBlock);
3903+
modelDataBlock.AddRange(unknownPatch72DataBlock);
38773904
modelDataBlock.AddRange(paddingDataBlock);
38783905
modelDataBlock.AddRange(boundingBoxDataBlock);
38793906
modelDataBlock.AddRange(boneBoundingBoxDataBlock);

0 commit comments

Comments
 (0)