Skip to content

Commit 974b252

Browse files
committed
Fully working initial implementation of standard modpacks.
1 parent 80b9406 commit 974b252

File tree

2 files changed

+163
-6
lines changed

2 files changed

+163
-6
lines changed

xivModdingFramework/Cache/XivDependencyGraph.cs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -487,23 +487,42 @@ public async Task<List<string>> GetModelFiles()
487487

488488
}
489489

490+
private static readonly Regex _materialSetRegex = new Regex("v[0-9]{4}");
491+
490492
/// <summary>
491493
/// Gets all the unique material files in this depency chain.
492494
/// Subsets of this data may be accessed with XivDependencyGraph::GetChildFiles(internalFilePath).
493495
/// </summary>
494496
/// <returns></returns>
495-
public async Task<List<string>> GetMaterialFiles()
497+
public async Task<List<string>> GetMaterialFiles(int materialVariant = -1)
496498
{
497499
var models = await GetModelFiles();
498500
var materials = new HashSet<string>();
499501
if (models != null && models.Count > 0)
500502
{
501-
foreach(var model in models)
503+
var dataFile = IOUtil.GetDataFileFromPath(models[0]);
504+
var _mdl = new Mdl(XivCache.GameInfo.GameDirectory, dataFile);
505+
506+
foreach (var model in models)
502507
{
503508
var mdlMats = await XivCache.GetChildFiles(model);
504-
foreach(var mat in mdlMats)
509+
if (materialVariant < 0)
505510
{
506-
materials.Add(mat);
511+
foreach (var mat in mdlMats)
512+
{
513+
materials.Add(mat);
514+
}
515+
} else {
516+
var replacement = "v" + materialVariant.ToString().PadLeft(4, '0');
517+
foreach (var mat in mdlMats)
518+
{
519+
// Replace any material set references with the new one.
520+
// The hash set will scrub us down to just a single copy.
521+
// This is faster than re-scanning the MDL file.
522+
// And a little more thorough than simply skipping over non-matching refs.
523+
// Since some materials may not have variant references.
524+
materials.Add(_materialSetRegex.Replace(mat, replacement));
525+
}
507526
}
508527
}
509528
}
@@ -515,9 +534,9 @@ public async Task<List<string>> GetMaterialFiles()
515534
/// Subsets of this data may be accessed with XivDependencyGraph::GetChildFiles(internalFilePath).
516535
/// </summary>
517536
/// <returns></returns>
518-
public async Task<List<string>> GetTextureFiles()
537+
public async Task<List<string>> GetTextureFiles(int materialVariant = -1)
519538
{
520-
var materials = await GetMaterialFiles();
539+
var materials = await GetMaterialFiles(materialVariant);
521540
var textures = new HashSet<string>();
522541
if (materials != null && materials.Count > 0)
523542
{

xivModdingFramework/SqPack/FileTypes/Dat.cs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,144 @@ public async Task<XivTex> GetType4Data(string internalPath, bool forceOriginal)
784784
}
785785

786786

787+
public async Task<int> GetCompressedFileSize(int offset, XivDataFile dataFile)
788+
{
789+
790+
var xivTex = new XivTex();
791+
792+
var decompressedData = new List<byte>();
793+
794+
// This formula is used to obtain the dat number in which the offset is located
795+
var datNum = ((offset / 8) & 0x0F) / 2;
796+
797+
await _semaphoreSlim.WaitAsync();
798+
799+
try
800+
{
801+
offset = OffsetCorrection(datNum, offset);
802+
803+
var datPath = Path.Combine(_gameDirectory.FullName, $"{dataFile.GetDataFileName()}{DatExtension}{datNum}");
804+
805+
return await Task.Run(async () =>
806+
{
807+
using (var br = new BinaryReader(File.OpenRead(datPath)))
808+
{
809+
br.BaseStream.Seek(offset, SeekOrigin.Begin);
810+
var headerLength = br.ReadInt32();
811+
var fileType = br.ReadInt32();
812+
var uncompSize = br.ReadInt32();
813+
var unknown = br.ReadInt32();
814+
var maxBufferSize = br.ReadInt32();
815+
var blockCount = br.ReadInt16();
816+
817+
var endOfHeader = offset + headerLength;
818+
819+
if (fileType != 2 && fileType != 3 && fileType != 4)
820+
{
821+
throw new NotSupportedException("Cannot get compressed file size of unknown type.");
822+
}
823+
824+
int compSize = 0;
825+
// Ok, time to parse the block headers and figure out how long the compressed data runs.
826+
if(fileType == 2)
827+
{
828+
br.BaseStream.Seek(endOfHeader + 4, SeekOrigin.Begin);
829+
var lastSize = 0;
830+
var lastOffset = 0;
831+
for(int i = 0; i < blockCount; i++)
832+
{
833+
br.BaseStream.Seek(offset + (24 + (8 * i)), SeekOrigin.Begin);
834+
var blockOffset = br.ReadInt32();
835+
var blockCompressedSize = br.ReadUInt16();
836+
837+
lastOffset = blockOffset;
838+
lastSize = blockCompressedSize;
839+
}
840+
841+
// Pretty straight forward. Header + Total size of the compressed data.
842+
compSize = headerLength + lastOffset + lastSize;
843+
844+
} else if(fileType == 3)
845+
{
846+
847+
// 24 byte header, then 88 bytes to the first chunk offset.
848+
br.BaseStream.Seek(offset + 112, SeekOrigin.Begin);
849+
var firstOffset = br.ReadInt32();
850+
851+
// 24 byte header, then 178 bytes to the start of the block count.
852+
br.BaseStream.Seek(offset + 178, SeekOrigin.Begin);
853+
854+
var totalBlocks = 0;
855+
for (var i = 0; i < 11; i++)
856+
{
857+
totalBlocks += br.ReadUInt16();
858+
}
859+
860+
861+
// 24 byte header, then 208 bytes to the list of block sizes.
862+
br.BaseStream.Seek(offset + 208, SeekOrigin.Begin);
863+
864+
var blockSizes = new int[totalBlocks];
865+
for (var i = 0; i < totalBlocks; i++)
866+
{
867+
blockSizes[i] = br.ReadUInt16();
868+
}
869+
870+
int totalCompressedSize = 0;
871+
foreach(var size in blockSizes)
872+
{
873+
totalCompressedSize += size;
874+
}
875+
876+
877+
// Header + Chunk headers + compressed data.
878+
compSize = headerLength + firstOffset + totalCompressedSize;
879+
} else if(fileType == 4)
880+
{
881+
br.BaseStream.Seek(endOfHeader + 4, SeekOrigin.Begin);
882+
// Textures.
883+
var lastOffset = 0;
884+
var lastSize = 0;
885+
var mipMapInfoOffset = offset + 24;
886+
for (int i = 0, j = 0; i < blockCount; i++)
887+
{
888+
br.BaseStream.Seek(mipMapInfoOffset + j, SeekOrigin.Begin);
889+
890+
j = j + 20;
891+
892+
var offsetFromHeaderEnd = br.ReadInt32();
893+
var mipMapCompressedSize = br.ReadInt32();
894+
895+
896+
lastOffset = offsetFromHeaderEnd;
897+
lastSize = mipMapCompressedSize;
898+
}
899+
900+
// Pretty straight forward. Header + Total size of the compressed data.
901+
compSize = headerLength + lastOffset + lastSize;
902+
903+
}
904+
905+
// Fundamentally all files in the dat are padded out to the nearest 256 bytes.
906+
// The DAT import functions actually add this for us normally, so it's not really
907+
// necessary to treat it as part of the 'file size', but for completeness, it doesn't
908+
// hurt to do so, and then it makes our file size match with the modlist entry file sizes.
909+
if(compSize % 256 != 0)
910+
{
911+
var padding = 256 - (compSize % 256);
912+
compSize += padding;
913+
}
914+
return compSize;
915+
916+
}
917+
});
918+
}
919+
finally
920+
{
921+
_semaphoreSlim.Release();
922+
}
923+
}
924+
787925
/// <summary>
788926
/// Gets the data for Type 4 (Texture) files.
789927
/// </summary>

0 commit comments

Comments
 (0)