Skip to content

Commit fba4c2d

Browse files
committed
Beta 2.3.3.2b
2 parents 06ab3f7 + 52277bc commit fba4c2d

File tree

4 files changed

+145
-41
lines changed

4 files changed

+145
-41
lines changed

xivModdingFramework/Models/FileTypes/Mdl.cs

Lines changed: 111 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4407,14 +4407,29 @@ public async Task AddRacialModel(int setId, string slot, XivRace newRace, string
44074407
/// <param name="originalPath"></param>
44084408
/// <param name="newPath"></param>
44094409
/// <returns></returns>
4410-
public async Task<long> CopyModel(string originalPath, string newPath, string source)
4410+
public async Task<long> CopyModel(string originalPath, string newPath, string source, bool copyTextures = false)
44114411
{
44124412
var _dat = new Dat(_gameDirectory);
4413-
var model = await GetModel(originalPath);
4414-
var xMdl = await GetRawMdlData(originalPath);
4413+
var _index = new Index(_gameDirectory);
4414+
var _modding = new Modding(_gameDirectory);
4415+
4416+
var fromRoot = await XivCache.GetFirstRoot(originalPath);
4417+
var toRoot = await XivCache.GetFirstRoot(newPath);
4418+
4419+
IItem item = null;
4420+
if (toRoot != null)
4421+
{
4422+
item = toRoot.GetFirstItem();
4423+
}
4424+
4425+
var df = IOUtil.GetDataFileFromPath(originalPath);
4426+
4427+
var index = await _index.GetIndexFile(df);
4428+
var modlist = await _modding.GetModListAsync();
44154429

4416-
var root = await XivCache.GetFirstRoot(newPath);
4417-
var item = root.GetFirstItem();
4430+
var offset = index.Get8xDataOffset(originalPath);
4431+
var xMdl = await GetRawMdlData(originalPath, false, offset);
4432+
var model = TTModel.FromRaw(xMdl);
44184433

44194434
var originalRace = IOUtil.GetRaceFromPath(originalPath);
44204435
var newRace = IOUtil.GetRaceFromPath(newPath);
@@ -4426,56 +4441,127 @@ public async Task<long> CopyModel(string originalPath, string newPath, string so
44264441
ModelModifiers.FixUpSkinReferences(model, newPath);
44274442
}
44284443

4429-
var _imc = new Imc(_gameDirectory);
4430-
4431-
var imcEntries = await _imc.GetEntries(await root.GetImcEntryPaths());
4432-
4433-
var materialSets = new HashSet<byte>();
4434-
imcEntries.ForEach(x => materialSets.Add(x.MaterialSet));
4435-
44364444
// Language is irrelevant here.
44374445
var _mtrl = new Mtrl(_gameDirectory, IOUtil.GetDataFileFromPath(newPath), XivLanguage.None);
44384446

44394447
// Get all variant materials.
4440-
var materialPaths = await GetReferencedMaterialPaths(originalPath, -1, false, false);
4448+
var materialPaths = await GetReferencedMaterialPaths(originalPath, -1, false, false, index, modlist);
4449+
44414450

44424451
var _raceRegex = new Regex("c[0-9]{4}");
44434452

4453+
Dictionary<string, string> validNewMaterials = new Dictionary<string, string>();
4454+
HashSet<string> copiedPaths = new HashSet<string>();
44444455
// Update Material References and clone materials.
4445-
foreach(var material in materialPaths)
4456+
foreach (var material in materialPaths)
44464457
{
4447-
var newMtrlPath = _raceRegex.Replace(material, "c" + newRace.GetRaceCode());
4448-
if (newMtrlPath == material) continue;
44494458

4459+
// Get the new path.
4460+
var path = RootCloner.UpdatePath(fromRoot, toRoot, material);
4461+
4462+
// Adjust race code entries if needed.
4463+
if (toRoot.Info.PrimaryType == XivItemType.equipment || toRoot.Info.PrimaryType == XivItemType.accessory)
4464+
{
4465+
path = _raceRegex.Replace(path, "c" + newRace.GetRaceCode());
4466+
}
4467+
4468+
// Get file names.
44504469
var io = material.LastIndexOf("/", StringComparison.Ordinal);
4451-
var baseMatName = material.Substring(io, material.Length - io);
4470+
var originalMatName = material.Substring(io, material.Length - io);
4471+
4472+
io = path.LastIndexOf("/", StringComparison.Ordinal);
4473+
var newMatName = path.Substring(io, path.Length - io);
4474+
44524475

4453-
io = newMtrlPath.LastIndexOf("/", StringComparison.Ordinal);
4454-
var newMatName = newMtrlPath.Substring(io, newMtrlPath.Length - io);
44554476
// Time to copy the materials!
44564477
try
44574478
{
4458-
var mtrlData = await _dat.GetType2Data(material, false);
4459-
var compressedData = await _dat.CreateType2Data(mtrlData);
4460-
await _dat.WriteModFile(compressedData, newMtrlPath, source, item);
4479+
offset = index.Get8xDataOffset(material);
4480+
var mtrl = await _mtrl.GetMtrlData(offset, material, 11);
4481+
4482+
if (copyTextures)
4483+
{
4484+
for(int i = 0; i < mtrl.TexturePathList.Count; i++)
4485+
{
4486+
var tex = mtrl.TexturePathList[i];
4487+
var ntex = RootCloner.UpdatePath(fromRoot, toRoot, tex);
4488+
if (toRoot.Info.PrimaryType == XivItemType.equipment || toRoot.Info.PrimaryType == XivItemType.accessory)
4489+
{
4490+
ntex = _raceRegex.Replace(ntex, "c" + newRace.GetRaceCode());
4491+
}
4492+
4493+
mtrl.TexturePathList[i] = ntex;
4494+
4495+
await _dat.CopyFile(tex, ntex, source, true, item, index, modlist);
4496+
}
4497+
}
4498+
4499+
mtrl.MTRLPath = path;
4500+
await _mtrl.ImportMtrl(mtrl, item, source, index, modlist);
4501+
4502+
if(!validNewMaterials.ContainsKey(newMatName))
4503+
{
4504+
validNewMaterials.Add(newMatName, path);
4505+
}
4506+
copiedPaths.Add(path);
4507+
44614508

44624509
// Switch out any material references to the material in the model file.
4463-
foreach(var m in model.MeshGroups)
4510+
foreach (var m in model.MeshGroups)
44644511
{
4465-
if(m.Material == baseMatName)
4512+
if(m.Material == originalMatName)
44664513
{
44674514
m.Material = newMatName;
44684515
}
44694516
}
4517+
44704518
} catch(Exception ex)
44714519
{
44724520
// Hmmm. The original material didn't exist. This is pretty not awesome, but I guess a non-critical error...?
44734521
}
44744522
}
44754523

4524+
if (Imc.UsesImc(toRoot) && Imc.UsesImc(fromRoot))
4525+
{
4526+
var _imc = new Imc(XivCache.GameInfo.GameDirectory);
4527+
4528+
var toEntries = await _imc.GetEntries(await toRoot.GetImcEntryPaths(), false, index, modlist);
4529+
var fromEntries = await _imc.GetEntries(await fromRoot.GetImcEntryPaths(), false, index, modlist);
4530+
4531+
var toSets = toEntries.Select(x => x.MaterialSet).Where(x => x != 0).ToList();
4532+
var fromSets = fromEntries.Select(x => x.MaterialSet).Where(x => x != 0).ToList();
4533+
4534+
if(fromSets.Count > 0 && toSets.Count > 0)
4535+
{
4536+
var vReplace = new Regex("/v[0-9]{4}/");
4537+
4538+
// Validate that sufficient material sets have been created at the destination root.
4539+
foreach(var mkv in validNewMaterials)
4540+
{
4541+
var validPath = mkv.Value;
4542+
foreach(var msetId in toSets)
4543+
{
4544+
var testPath = vReplace.Replace(validPath, "/v" + msetId.ToString().PadLeft(4, '0') + "/");
4545+
var copied = copiedPaths.Contains(testPath);
4546+
4547+
// Missing a material set, copy in the known valid material.
4548+
if(!copied)
4549+
{
4550+
await _dat.CopyFile(validPath, testPath, source, true, item, index, modlist);
4551+
}
4552+
}
4553+
}
4554+
}
4555+
}
4556+
4557+
44764558
// Save the final modified mdl.
44774559
var data = await MakeNewMdlFile(model, xMdl);
4478-
var offset = await _dat.WriteModFile(data, newPath, source, item);
4560+
offset = await _dat.WriteModFile(data, newPath, source, item, index, modlist);
4561+
4562+
await _index.SaveIndexFile(index);
4563+
await _modding.SaveModListAsync(modlist);
4564+
44794565
return offset;
44804566
}
44814567

xivModdingFramework/Mods/FileTypes/TTMP.cs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,10 @@ public async Task<int> CreateWizardModPack(ModPackData modPackData, IProgress<do
6565
var dir = Path.Combine(Path.GetTempPath(), guid.ToString());
6666
Directory.CreateDirectory(dir);
6767

68-
6968
_tempMPD = Path.Combine(dir, "TTMPD.mpd");
7069
_tempMPL = Path.Combine(dir, "TTMPL.mpl");
7170

72-
var imageList = new Dictionary<string, string>();
71+
var imageList = new HashSet<string>();
7372
var pageCount = 1;
7473

7574
var modPackJson = new ModPackJson
@@ -109,19 +108,20 @@ public async Task<int> CreateWizardModPack(ModPackData modPackData, IProgress<do
109108

110109
foreach (var modOption in modGroup.OptionList)
111110
{
112-
var randomFileName = "";
113-
111+
var imageFileName = "";
114112
if (modOption.Image != null)
115113
{
116-
randomFileName = $"{Path.GetRandomFileName()}.png";
117-
imageList.Add(randomFileName, modOption.ImageFileName);
114+
var fname = Path.GetFileName(modOption.ImageFileName);
115+
imageFileName = Path.Combine(dir, fname);
116+
File.Copy(modOption.ImageFileName, imageFileName);
117+
imageList.Add(imageFileName);
118118
}
119119

120120
var modOptionJson = new ModOptionJson
121121
{
122122
Name = modOption.Name,
123123
Description = modOption.Description,
124-
ImagePath = randomFileName,
124+
ImagePath = "images/" + Path.GetFileName(imageFileName),
125125
GroupName = modOption.GroupName,
126126
SelectionType = modOption.SelectionType,
127127
IsChecked=modOption.IsChecked,
@@ -179,11 +179,13 @@ public async Task<int> CreateWizardModPack(ModPackData modPackData, IProgress<do
179179

180180
var zf = new ZipFile();
181181
zf.CompressionLevel = Ionic.Zlib.CompressionLevel.None;
182-
zf.AddFile(_tempMPL, "TTMPL.mpl");
183-
zf.AddFile(_tempMPD, "TTMPD.mpd");
182+
zf.AddFile(_tempMPL, "");
183+
zf.AddFile(_tempMPD, "");
184+
zf.Save(modPackPath);
185+
184186
foreach (var image in imageList)
185187
{
186-
zf.AddFile(image.Value, image.Key);
188+
zf.AddFile(image, "images");
187189
}
188190
zf.Save(modPackPath);
189191

@@ -300,8 +302,8 @@ public async Task<int> CreateSimpleModPack(SimpleModPackData modPackData, Direct
300302

301303
var zf = new ZipFile();
302304
zf.CompressionLevel = Ionic.Zlib.CompressionLevel.None;
303-
zf.AddFile(_tempMPL);
304-
zf.AddFile(_tempMPD);
305+
zf.AddFile(_tempMPL, "");
306+
zf.AddFile(_tempMPD, "");
305307
zf.Save(modPackPath);
306308
}
307309
finally
@@ -332,7 +334,7 @@ public async Task<int> CreateSimpleModPack(SimpleModPackData modPackData, Direct
332334

333335
using (var zf = ZipFile.Read(modPackDirectory.FullName))
334336
{
335-
var images = zf.Entries.Where(x => x.FileName.EndsWith(".png"));
337+
var images = zf.Entries.Where(x => x.FileName.EndsWith(".png") || x.FileName.StartsWith("images/"));
336338
var mpl = zf.Entries.First(x => x.FileName.EndsWith(".mpl"));
337339

338340
using (var streamReader = new StreamReader(mpl.OpenReader()))

xivModdingFramework/Mods/RootCloner.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ namespace xivModdingFramework.Mods
2525
{
2626
public static class RootCloner
2727
{
28+
29+
public static bool IsSupported(XivDependencyRoot root)
30+
{
31+
if (root.Info.PrimaryType == XivItemType.equipment) return true;
32+
if (root.Info.PrimaryType == XivItemType.accessory) return true;
33+
if (root.Info.PrimaryType == XivItemType.human && root.Info.SecondaryType == XivItemType.hair) return true;
34+
35+
return false;
36+
}
37+
2838
/// <summary>
2939
/// Copies the entirety of a given root to a new root.
3040
/// </summary>
@@ -34,6 +44,11 @@ public static class RootCloner
3444
/// <returns>Returns a Dictionary of all the file conversion</returns>
3545
public static async Task<Dictionary<string, string>> CloneRoot(XivDependencyRoot Source, XivDependencyRoot Destination, string ApplicationSource, int singleVariant = -1, string saveDirectory = null, IProgress<string> ProgressReporter = null, IndexFile index = null, ModList modlist = null, ModPack modPack = null)
3646
{
47+
if(!IsSupported(Source) || !IsSupported(Destination))
48+
{
49+
throw new InvalidDataException("Cannot clone unsupported root.");
50+
}
51+
3752

3853
if (ProgressReporter != null)
3954
{
@@ -544,7 +559,7 @@ private static async Task<ItemMetadata> GetCachedMetadata(IndexFile index, ModLi
544559
const string CommonPath = "chara/common/";
545560
private static readonly Regex RemoveRootPathRegex = new Regex("chara\\/[a-z]+\\/[a-z][0-9]{4}(?:\\/obj\\/[a-z]+\\/[a-z][0-9]{4})?\\/(.+)");
546561

547-
private static string UpdatePath(XivDependencyRoot Source, XivDependencyRoot Destination, string path)
562+
internal static string UpdatePath(XivDependencyRoot Source, XivDependencyRoot Destination, string path)
548563
{
549564
// For common path items, copy them to our own personal mimic of the path.
550565
if (path.StartsWith(CommonPath))

xivModdingFramework/Variants/FileTypes/Imc.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,14 +176,15 @@ public async Task<FullImcInfo> GetFullImcInfo(IItemModel item, IndexFile index =
176176
/// </summary>
177177
/// <param name="pathsWithOffsets"></param>
178178
/// <returns></returns>
179-
public async Task<List<XivImc>> GetEntries(List<string> pathsWithOffsets, bool forceDefault = false)
179+
public async Task<List<XivImc>> GetEntries(List<string> pathsWithOffsets, bool forceDefault = false, IndexFile index = null, ModList modlist = null)
180180
{
181181
var entries = new List<XivImc>();
182182
var dat = new Dat(_gameDirectory);
183183

184184
var lastPath = "";
185185
byte[] imcByteData = new byte[0];
186186

187+
187188
foreach (var combinedPath in pathsWithOffsets)
188189
{
189190
var binaryMatch = _binaryOffsetRegex.Match(combinedPath);
@@ -198,7 +199,7 @@ public async Task<List<XivImc>> GetEntries(List<string> pathsWithOffsets, bool f
198199
// Only reload this data if we need to.
199200
if (path != lastPath)
200201
{
201-
imcByteData = await dat.GetType2Data(path, forceDefault);
202+
imcByteData = await dat.GetType2Data(path, forceDefault, index, modlist);
202203
}
203204
lastPath = path;
204205

0 commit comments

Comments
 (0)