Skip to content

Commit 5c6a1e4

Browse files
Merge remote-tracking branch 'upstream/develop'
2 parents 508f894 + cc66d6f commit 5c6a1e4

File tree

3 files changed

+217
-20
lines changed

3 files changed

+217
-20
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace xivModdingFramework.Mods.DataContainers
5+
{
6+
public class BackupModPackData
7+
{
8+
/// <summary>
9+
/// The name of the mod pack
10+
/// </summary>
11+
public string Name { get; set; }
12+
13+
/// <summary>
14+
/// The mod pack author
15+
/// </summary>
16+
public string Author = "TexTools";
17+
18+
/// <summary>
19+
/// The mod pack version
20+
/// </summary>
21+
public Version Version = new Version("1.0.0");
22+
23+
/// <summary>
24+
/// The modpack Url
25+
/// </summary>
26+
public string Url = "";
27+
28+
/// <summary>
29+
/// The description for the mod pack
30+
/// </summary>
31+
public string Description = "";
32+
33+
/// <summary>
34+
/// The list of mods to back up
35+
/// </summary>
36+
public List<BackupModData> ModsToBackup { get; set; }
37+
}
38+
39+
public class BackupModData
40+
{
41+
/// <summary>
42+
/// Simple mod data
43+
/// </summary>
44+
public SimpleModData SimpleModData { get; set; }
45+
46+
/// <summary>
47+
/// Mod pack that the mod is a part of
48+
/// </summary>
49+
public ModPack ModPack { get; set; }
50+
}
51+
}

xivModdingFramework/Mods/DataContainers/ModList.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using System.Diagnostics.Contracts;
2121
using System.Security.Cryptography;
2222
using System.Text;
23+
using xivModdingFramework.General.Enums;
2324
using xivModdingFramework.Helpers;
2425

2526
namespace xivModdingFramework.Mods.DataContainers
@@ -114,6 +115,27 @@ public bool IsCustomFile()
114115
{
115116
return data.modOffset == data.originalOffset;
116117
}
118+
119+
/// <summary>
120+
/// Creates a Mod instance using the provided JSON
121+
/// </summary>
122+
/// <param name="modsJson"></param>
123+
/// <param name="sourceApplication"></param>
124+
/// <returns></returns>
125+
public static Mod MakeModFromJson(ModsJson modsJson, string sourceApplication)
126+
{
127+
return new Mod
128+
{
129+
source = sourceApplication,
130+
name = modsJson.Name,
131+
category = modsJson.Category,
132+
fullPath = modsJson.FullPath,
133+
datFile = IOUtil.GetDataFileFromPath(modsJson.FullPath).GetDataFileName(),
134+
enabled = true,
135+
modPack = modsJson.ModPackEntry,
136+
data = new Data()
137+
};
138+
}
117139
}
118140

119141
public class Data

xivModdingFramework/Mods/FileTypes/TTMP.cs

Lines changed: 144 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public class TTMP
5050

5151
private readonly string _currentWizardTTMPVersion = "1.3w";
5252
private readonly string _currentSimpleTTMPVersion = "1.3s";
53+
private readonly string _currentBackupTTMPVersion = "1.0b";
5354
private const string _minimumAssembly = "1.3.0.0";
5455

5556
private string _tempMPD, _tempMPL, _source;
@@ -335,6 +336,122 @@ public async Task<int> CreateSimpleModPack(SimpleModPackData modPackData, Direct
335336
return processCount;
336337
}
337338

339+
/// <summary>
340+
/// Creates a backup modpack which retains the original modpacks on import
341+
/// </summary>
342+
/// <param name="backupModpackData">The data that will go into the mod pack</param>
343+
/// <param name="gameDirectory">The game directory</param>
344+
/// <param name="progress">The progress of the mod pack creation</param>
345+
/// <param name="overwriteModpack">Whether or not to overwrite an existing modpack with the same name</param>
346+
/// <returns>The number of mods processed for the mod pack</returns>
347+
public async Task<int> CreateBackupModpack(BackupModPackData backupModpackData, DirectoryInfo gameDirectory, IProgress<(int current, int total, string message)> progress, bool overwriteModpack)
348+
{
349+
var processCount = await Task.Run<int>(() =>
350+
{
351+
var dat = new Dat(gameDirectory);
352+
353+
var guid = Guid.NewGuid();
354+
355+
var dir = Path.Combine(Path.GetTempPath(), guid.ToString());
356+
Directory.CreateDirectory(dir);
357+
358+
359+
_tempMPD = Path.Combine(dir, "TTMPD.mpd");
360+
_tempMPL = Path.Combine(dir, "TTMPL.mpl");
361+
362+
var modCount = 0;
363+
364+
var modPackJson = new ModPackJson
365+
{
366+
TTMPVersion = _currentBackupTTMPVersion,
367+
Name = backupModpackData.Name,
368+
Author = backupModpackData.Author,
369+
Version = backupModpackData.Version.ToString(),
370+
MinimumFrameworkVersion = _minimumAssembly,
371+
Url = backupModpackData.Url,
372+
Description = backupModpackData.Description,
373+
SimpleModsList = new List<ModsJson>()
374+
};
375+
376+
try
377+
{
378+
using (var binaryWriter = new BinaryWriter(File.Open(_tempMPD, FileMode.Create)))
379+
{
380+
foreach (var backupModData in backupModpackData.ModsToBackup)
381+
{
382+
if (ForbiddenModTypes.Contains(Path.GetExtension(backupModData.SimpleModData.FullPath))) continue;
383+
384+
var modsJson = new ModsJson
385+
{
386+
Name = backupModData.SimpleModData.Name,
387+
Category = backupModData.SimpleModData.Category.GetEnDisplayName(),
388+
FullPath = backupModData.SimpleModData.FullPath,
389+
ModSize = backupModData.SimpleModData.ModSize,
390+
DatFile = backupModData.SimpleModData.DatFile,
391+
IsDefault = backupModData.SimpleModData.IsDefault,
392+
ModOffset = binaryWriter.BaseStream.Position,
393+
ModPackEntry = backupModData.ModPack,
394+
};
395+
396+
var rawData = dat.GetRawData(backupModData.SimpleModData.ModOffset,
397+
XivDataFiles.GetXivDataFile(backupModData.SimpleModData.DatFile),
398+
backupModData.SimpleModData.ModSize);
399+
400+
if (rawData == null)
401+
{
402+
throw new Exception("Unable to obtain data for the following mod\n\n" +
403+
$"Name: {backupModData.SimpleModData.Name}\nFull Path: {backupModData.SimpleModData.FullPath}\n" +
404+
$"Mod Offset: {backupModData.SimpleModData.ModOffset}\nData File: {backupModData.SimpleModData.DatFile}\n\n" +
405+
$"Unselect the above mod and try again.");
406+
}
407+
408+
binaryWriter.Write(rawData);
409+
410+
modPackJson.SimpleModsList.Add(modsJson);
411+
412+
progress?.Report((++modCount, backupModpackData.ModsToBackup.Count, string.Empty));
413+
}
414+
}
415+
416+
progress?.Report((0, backupModpackData.ModsToBackup.Count, GeneralStrings.TTMP_Creating));
417+
418+
File.WriteAllText(_tempMPL, JsonConvert.SerializeObject(modPackJson));
419+
420+
var modPackPath = Path.Combine(_modPackDirectory.FullName, $"{backupModpackData.Name}.ttmp2");
421+
422+
if (File.Exists(modPackPath) && !overwriteModpack)
423+
{
424+
var fileNum = 1;
425+
modPackPath = Path.Combine(_modPackDirectory.FullName, $"{backupModpackData.Name}({fileNum}).ttmp2");
426+
while (File.Exists(modPackPath))
427+
{
428+
fileNum++;
429+
modPackPath = Path.Combine(_modPackDirectory.FullName, $"{backupModpackData.Name}({fileNum}).ttmp2");
430+
}
431+
}
432+
else if (File.Exists(modPackPath) && overwriteModpack)
433+
{
434+
File.Delete(modPackPath);
435+
}
436+
437+
var zf = new ZipFile();
438+
zf.UseZip64WhenSaving = Zip64Option.AsNecessary;
439+
zf.CompressionLevel = Ionic.Zlib.CompressionLevel.None;
440+
zf.AddFile(_tempMPL, "");
441+
zf.AddFile(_tempMPD, "");
442+
zf.Save(modPackPath);
443+
}
444+
finally
445+
{
446+
Directory.Delete(dir, true);
447+
}
448+
449+
return modCount;
450+
});
451+
452+
return processCount;
453+
}
454+
338455
/// <summary>
339456
/// Gets the data from a mod pack including images if present
340457
/// </summary>
@@ -461,8 +578,9 @@ public static string GetVersion(DirectoryInfo modPackDirectory)
461578
/// <param name="progress">The progress of the import</param>
462579
/// <param name="GetRootConversionsFunction">Function called part-way through import to resolve rood conversions, if any are desired. Function takes a List of files, the in-progress modified index and modlist files, and returns a dictionary of conversion data. If this function throws and OperationCancelledException, the import is cancelled.</param>
463580
/// <returns>The number of total mods imported</returns>
464-
public async Task<(int ImportCount, int ErrorCount, string Errors, float Duration)> ImportModPackAsync(DirectoryInfo modPackDirectory, List<ModsJson> modsJson,
465-
DirectoryInfo gameDirectory, DirectoryInfo modListDirectory, IProgress<(int current, int total, string message)> progress, Func<HashSet<string>, Dictionary<XivDataFile, IndexFile>, ModList, Task<Dictionary<XivDependencyRoot, (XivDependencyRoot Root, int Variant)>>> GetRootConversionsFunction = null )
581+
public async Task<(int ImportCount, int ErrorCount, string Errors, float Duration)> ImportModPackAsync(
582+
DirectoryInfo modPackDirectory, List<ModsJson> modsJson, DirectoryInfo gameDirectory, DirectoryInfo modListDirectory, IProgress<(int current, int total, string message)> progress,
583+
Func<HashSet<string>, Dictionary<XivDataFile, IndexFile>, ModList, Task<Dictionary<XivDependencyRoot, (XivDependencyRoot Root, int Variant)>>> GetRootConversionsFunction = null)
466584
{
467585
if (modsJson == null || modsJson.Count == 0) return (0, 0, "", 0);
468586

@@ -490,7 +608,7 @@ public static string GetVersion(DirectoryInfo modPackDirectory)
490608
// the *LAST* mod json entry for each file path.
491609
// This keeps us from having to constantly re-query the mod list file, and filters out redundant imports.
492610
var filePaths = new HashSet<string>();
493-
var newList = new List<ModsJson>(modsJson.Count);
611+
var filteredModsJson = new List<ModsJson>(modsJson.Count);
494612
for(int i = modsJson.Count -1; i >= 0; i--)
495613
{
496614
var mj = modsJson[i];
@@ -504,11 +622,10 @@ public static string GetVersion(DirectoryInfo modPackDirectory)
504622
if (ForbiddenModTypes.Contains(Path.GetExtension(mj.FullPath))) continue;
505623

506624
filePaths.Add(mj.FullPath);
507-
newList.Add(mj);
625+
filteredModsJson.Add(mj);
508626
}
509-
modsJson = newList;
510627

511-
if(modsJson.Count == 0)
628+
if(filteredModsJson.Count == 0)
512629
{
513630
return (0, 0, "", 0);
514631
}
@@ -577,7 +694,7 @@ await Task.Run(async () =>
577694
progress.Report((0, 0, "Writing new mod data to DAT files..."));
578695
using (var binaryReader = new BinaryReader(new FileStream(_tempMPD, FileMode.Open)))
579696
{
580-
foreach (var modJson in modsJson)
697+
foreach (var modJson in filteredModsJson)
581698
{
582699
try
583700
{
@@ -598,7 +715,7 @@ await Task.Run(async () =>
598715
Mod mod = null;
599716
if (modsByFile.ContainsKey(modJson.FullPath))
600717
{
601-
mod= modsByFile[modJson.FullPath];
718+
mod = modsByFile[modJson.FullPath];
602719
}
603720

604721
// Always write data to end of file during modpack imports in case we need
@@ -669,17 +786,25 @@ await Task.Run(async () =>
669786
progress.Report((count, totalFiles, "Updating Index file references..."));
670787
}
671788

789+
// Add entries for new modpacks to the mod list
790+
foreach (var modsJson in filteredModsJson)
791+
{
792+
if (modsJson.ModPackEntry == null) continue;
672793

673-
var modPackExists = modList.ModPacks.Any(modpack => modpack.name == modsJson[0].ModPackEntry.name);
794+
var modPackExists = modList.ModPacks.Any(modpack => modpack.name == modsJson.ModPackEntry.name);
674795

675-
if (!modPackExists)
676-
{
677-
modList.ModPacks.Add(modsJson[0].ModPackEntry);
796+
if (!modPackExists)
797+
{
798+
modList.ModPacks.Add(modsJson.ModPackEntry);
799+
}
678800
}
679-
var modPack = modList.ModPacks.First(x => x.name == modsJson[0].ModPackEntry.name);
801+
680802

681803
if (GetRootConversionsFunction != null)
682804
{
805+
// Get the modpack to list the conversions under, this is the just the modpack entry of the first modsJson since they're all the same unless it's a backup
806+
// However, this code shouldn't be used when importing backup modpacks since they already had the choice to change the destination item after the initial import
807+
var modPack = modList.ModPacks.First(x => x.name == filteredModsJson[0].ModPackEntry?.name);
683808

684809
Dictionary<XivDependencyRoot, (XivDependencyRoot Root, int Variant)> rootConversions = null;
685810
try
@@ -730,7 +855,7 @@ await Task.Run(async () =>
730855
foreach (var fileKv in convertedFiles)
731856
{
732857
// Remove the file from our json list, the conversion already handled everything we needed to do with it.
733-
var json = modsJson.RemoveAll(x => x.FullPath == fileKv.Key);
858+
var json = filteredModsJson.RemoveAll(x => x.FullPath == fileKv.Key);
734859

735860
if (fileKv.Key != fileKv.Value)
736861
{
@@ -784,15 +909,15 @@ await Task.Run(async () =>
784909

785910
// Update the Mod List file.
786911

787-
788912
foreach (var file in filePaths)
789913
{
790914
if (ErroneousFiles.Contains(file)) continue;
791915
try
792916
{
793-
var json = modsJson.FirstOrDefault(x => x.FullPath == file);
917+
var json = filteredModsJson.FirstOrDefault(x => x.FullPath == file);
794918
if (json == null) continue;
795919

920+
796921
var mod = modList.Mods.FirstOrDefault(x => x.fullPath == file);
797922
var longOffset = ((long)DatOffsets[file]) * 8L;
798923
var originalOffset = OriginalOffsets[file];
@@ -827,13 +952,12 @@ await Task.Run(async () =>
827952
mod.data.originalOffset = (fileAdditionMod ? longOffset : longOriginal);
828953
mod.data.dataType = fileType;
829954
mod.enabled = true;
830-
mod.modPack = modPack;
955+
mod.modPack = json.ModPackEntry;
831956
modList.Mods.Add(mod);
832957

833958
}
834959
else
835960
{
836-
837961
var fileAdditionMod = originalOffset == 0 || mod.IsCustomFile();
838962
if (fileAdditionMod)
839963
{
@@ -843,7 +967,7 @@ await Task.Run(async () =>
843967
mod.data.modSize = size;
844968
mod.data.modOffset = longOffset;
845969
mod.enabled = true;
846-
mod.modPack = modPack;
970+
mod.modPack = json.ModPackEntry;
847971
mod.data.dataType = fileType;
848972
mod.name = json.Name;
849973
mod.category = json.Category;
@@ -938,7 +1062,7 @@ await Task.Run(async () =>
9381062
count = 0;
9391063
progress.Report((0, 0, "Queuing Cache Updates..."));
9401064
// Metadata files expanded, last thing is to queue everthing up for the Cache.
941-
var files = modsJson.Select(x => x.FullPath).ToList();
1065+
var files = filteredModsJson.Select(x => x.FullPath).ToList();
9421066
try
9431067
{
9441068
XivCache.QueueDependencyUpdate(files);

0 commit comments

Comments
 (0)