Skip to content

Commit f59eb18

Browse files
committed
Update 2.2.0.0
2 parents 8c0fae0 + 6b6fc8d commit f59eb18

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+6945
-1695
lines changed

xivModdingFramework/Cache/XivCache.cs

Lines changed: 1499 additions & 199 deletions
Large diffs are not rendered by default.

xivModdingFramework/Cache/XivDependencyGraph.cs

Lines changed: 1806 additions & 0 deletions
Large diffs are not rendered by default.

xivModdingFramework/General/Enums/XivRace.cs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ public static bool IsChildOf(this XivRace possibleChild, XivRace possibleParent,
348348
}
349349

350350
/// <summary>
351-
/// Determines if this race is a parent of a nother given race or not.
351+
/// Determines if this race is a parent of another given race or not.
352352
/// If the values are the same, it is considered TRUE by default.
353353
/// </summary>
354354
/// <param name="possibleParent"></param>
@@ -377,6 +377,35 @@ public static bool IsParentOf(this XivRace possibleParent, XivRace possibleChild
377377

378378
}
379379

380+
/// <summary>
381+
/// Determines if this race is a direct parent of another given race or not.
382+
/// If the values are the same, it is considered TRUE by default.
383+
/// </summary>
384+
/// <param name="possibleParent"></param>
385+
/// <param name="possibleChild"></param>
386+
/// <returns></returns>
387+
public static bool IsDirectParentOf(this XivRace possibleParent, XivRace possibleChild, bool allowSame = true)
388+
{
389+
CheckTree();
390+
if (possibleChild == possibleParent && allowSame)
391+
{
392+
return true;
393+
}
394+
395+
var node = GetNode(possibleChild);
396+
397+
if (node?.Parent != null)
398+
{
399+
node = node.Parent;
400+
if (node.Race == possibleParent)
401+
{
402+
return true;
403+
}
404+
}
405+
406+
return false;
407+
}
408+
380409
/// <summary>
381410
/// Retrieves the tree node for the given race.
382411
/// </summary>
@@ -470,6 +499,25 @@ public static string GetSkinPath(this XivRace startingRace, int body = 1, string
470499
/// </summary>
471500
public static class XivRaces
472501
{
502+
public static readonly List<XivRace> PlayableRaces = new List<XivRace>() {
503+
XivRace.Hyur_Midlander_Male,
504+
XivRace.Hyur_Midlander_Female,
505+
XivRace.Hyur_Highlander_Male,
506+
XivRace.Hyur_Highlander_Female,
507+
XivRace.Elezen_Male,
508+
XivRace.Elezen_Female,
509+
XivRace.Miqote_Male,
510+
XivRace.Miqote_Female,
511+
XivRace.Roegadyn_Male,
512+
XivRace.Roegadyn_Female,
513+
XivRace.Lalafell_Male,
514+
XivRace.Lalafell_Female,
515+
XivRace.AuRa_Male,
516+
XivRace.AuRa_Female,
517+
XivRace.Hrothgar,
518+
XivRace.Viera,
519+
};
520+
473521
/// <summary>
474522
/// Gets the description from the enum value, in this case the Race Code
475523
/// </summary>

xivModdingFramework/Helpers/Constants.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System;
44
using System.Collections.Generic;
55
using System.Text;
6+
using System.Text.RegularExpressions;
67

78
namespace xivModdingFramework.Helpers
89
{
@@ -13,5 +14,12 @@ public static class Constants
1314
/// The alphabet. Now in character array form.
1415
/// </summary>
1516
public static readonly char[] Alphabet = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
17+
public static string BinaryOffsetMarker = "::";
18+
19+
20+
21+
// This commedically long regex validates URL strings.
22+
// Came from - https://mathiasbynens.be/demo/url-regex (@scottgonzales version)
23+
public static readonly Regex UrlValidationRegex = new Regex("#([a-z]([a-z]|\\d|\\+|-|\\.)*):(\\/\\/(((([a-z]|\\d|-|\\.|_|~|[\\x00A0-\\xD7FF\\xF900-\\xFDCF\\xFDF0-\\xFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:)*@)?((\\[(|(v[\\da-f]{1,}\\.(([a-z]|\\d|-|\\.|_|~)|[!\\$&'\\(\\)\\*\\+,;=]|:)+))\\])|((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5]))|(([a-z]|\\d|-|\\.|_|~|[\\x00A0-\\xD7FF\\xF900-\\xFDCF\\xFDF0-\\xFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=])*)(:\\d*)?)(\\/(([a-z]|\\d|-|\\.|_|~|[\\x00A0-\\xD7FF\\xF900-\\xFDCF\\xFDF0-\\xFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)*)*|(\\/((([a-z]|\\d|-|\\.|_|~|[\\x00A0-\\xD7FF\\xF900-\\xFDCF\\xFDF0-\\xFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)+(\\/(([a-z]|\\d|-|\\.|_|~|[\\x00A0-\\xD7FF\\xF900-\\xFDCF\\xFDF0-\\xFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)*)*)?)|((([a-z]|\\d|-|\\.|_|~|[\\x00A0-\\xD7FF\\xF900-\\xFDCF\\xFDF0-\\xFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)+(\\/(([a-z]|\\d|-|\\.|_|~|[\\x00A0-\\xD7FF\\xF900-\\xFDCF\\xFDF0-\\xFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)*)*)|((([a-z]|\\d|-|\\.|_|~|[\\x00A0-\\xD7FF\\xF900-\\xFDCF\\xFDF0-\\xFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)){0})(\\?((([a-z]|\\d|-|\\.|_|~|[\\x00A0-\\xD7FF\\xF900-\\xFDCF\\xFDF0-\\xFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)|[\\xE000-\\xF8FF]|\\/|\\?)*)?(\\#((([a-z]|\\d|-|\\.|_|~|[\\x00A0-\\xD7FF\\xF900-\\xFDCF\\xFDF0-\\xFFEF])|(%[\\da-f]{2})|[!\\$&'\\(\\)\\*\\+,;=]|:|@)|\\/|\\?)*)?#iS");
1624
}
1725
}

xivModdingFramework/Helpers/HashGenerator.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ namespace xivModdingFramework.Helpers
2525
/// </summary>
2626
public class HashGenerator
2727
{
28-
private static int _dwCrc;
29-
3028
private static readonly uint[] CrcTable1 = new uint[]{
3129
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832,
3230
0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
@@ -165,6 +163,7 @@ private static int ComputeCRC(byte[] ecxBytes, int offset)
165163

166164
private static int ComputeCRC(byte[] ecxBytes, int offset, int cbLength)
167165
{
166+
int _dwCrc;
168167
using (var stream = new MemoryStream(ecxBytes))
169168
{
170169
using (var reader = new BinaryReader(stream))

xivModdingFramework/Helpers/IOUtil.cs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ public static string MakeItemSavePath(IItem item, DirectoryInfo saveDirectory, X
133133

134134
return path;
135135
}
136+
137+
136138
public static XivRace GetRaceFromPath(string path)
137139
{
138140
if(path == null)
@@ -152,7 +154,7 @@ public static XivRace GetRaceFromPath(string path)
152154
}
153155
else if (path.Contains(".tex") || path.Contains(".mdl") || path.Contains(".atex"))
154156
{
155-
if (path.Contains("accessory") || path.Contains("weapon") || path.Contains("/common/"))
157+
if (path.Contains("weapon") || path.Contains("/common/"))
156158
{
157159
xivRace = XivRace.All_Races;
158160
}
@@ -250,5 +252,46 @@ public static XivDataFile GetDataFileFromPath(string path)
250252

251253
throw new Exception("Could not resolve data file - Invalid internal FFXIV path.");
252254
}
255+
256+
/// <summary>
257+
/// Cleans a given string of unusual or potentially invalid characters for most use cases, particularly URLs.
258+
/// </summary>
259+
/// <param name="st"></param>
260+
/// <returns></returns>
261+
public static string CleanString(string st)
262+
{
263+
if(st == null)
264+
{
265+
return "";
266+
}
267+
268+
269+
const string invalids = "!\"#$%&'()*+,-./@:;<=>[\\]^_`{|}~";
270+
271+
st = st.Trim();
272+
foreach (var c in invalids)
273+
{
274+
st.Replace(c.ToString(), "");
275+
}
276+
277+
return st;
278+
}
279+
280+
/// <summary>
281+
/// Cleans up and validates a string to ensure it's a valid URL.
282+
/// If the given URL is totally invalid, NULL is returned.
283+
/// </summary>
284+
/// <param name="url"></param>
285+
/// <returns></returns>
286+
public static string ValidateUrl(string url)
287+
{
288+
url = CleanString(url);
289+
if (Constants.UrlValidationRegex.IsMatch(url))
290+
{
291+
return url;
292+
}
293+
return null;
294+
}
295+
253296
}
254297
}

xivModdingFramework/Helpers/ProblemChecker.cs

Lines changed: 62 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,12 @@ public Task RepairIndexDatCounts(XivDataFile dataFile)
138138
});
139139
}
140140

141+
/// <summary>
142+
/// This function returns TRUE if the backups should be used, and FALSE if they should not.
143+
/// </summary>
144+
/// <param name="dataFile"></param>
145+
/// <param name="backupsDirectory"></param>
146+
/// <returns></returns>
141147
public Task<bool> CheckForOutdatedBackups(XivDataFile dataFile, DirectoryInfo backupsDirectory)
142148
{
143149
return Task.Run(() =>
@@ -149,10 +155,32 @@ public Task<bool> CheckForOutdatedBackups(XivDataFile dataFile, DirectoryInfo ba
149155

150156
// Since the material addition directly adds to section 1 we can no longer check for outdated using that section header
151157
// so instead compare the hashes of sections 2 and 3
152-
var backupHashSection2 = _index.GetIndexSection2Hash(backupDataFile);
153-
var currentHashSection2 = _index.GetIndexSection2Hash(currentDataFile);
154-
var backupHashSection3 = _index.GetIndexSection3Hash(backupDataFile);
155-
var currentHashSection3 = _index.GetIndexSection3Hash(currentDataFile);
158+
byte[] currentHashSection2;
159+
byte[] currentHashSection3;
160+
byte[] backupHashSection2;
161+
byte[] backupHashSection3;
162+
try
163+
{
164+
currentHashSection2 = _index.GetIndexSection2Hash(currentDataFile);
165+
currentHashSection3 = _index.GetIndexSection3Hash(currentDataFile);
166+
}
167+
catch
168+
{
169+
// Base files are fucked, use *any* backups.
170+
return true;
171+
}
172+
173+
try
174+
{
175+
backupHashSection2 = _index.GetIndexSection2Hash(backupDataFile);
176+
backupHashSection3 = _index.GetIndexSection3Hash(backupDataFile);
177+
}
178+
catch
179+
{
180+
// Backups are fucked, can't use those.
181+
return false;
182+
}
183+
156184

157185
return backupHashSection2.SequenceEqual(currentHashSection2) && backupHashSection3.SequenceEqual(currentHashSection3);
158186
});
@@ -162,52 +190,39 @@ public Task PerformStartOver(DirectoryInfo backupsDirectory, IProgress<string> p
162190
{
163191
return Task.Run(async () =>
164192
{
165-
progress?.Report("Deleting mods...");
166-
167193
var modding = new Modding(_gameDirectory);
168194
var backupsRestored = false;
169195

196+
// Stop the cache worker since we're blowing up the entire index file and db anyways.
197+
// The cache rebuild will start it up again after the cache is rebuilt.
198+
XivCache.CacheWorkerEnabled = false;
199+
170200
try
171201
{
172-
// Try to restore the index entries to their original values by deleting any files added by TexTools
173-
// and setting mods to disabled
174-
await modding.DeleteAllFilesAddedByTexTools();
175-
await modding.ToggleAllMods(false);
176-
progress?.Report("Restoring index file backups...");
177-
}
178-
catch
179-
{
180-
// If an exception occurred due to a corrupted modlist which couldn't be deserealized restore the backup index
181-
// files by force
202+
// Try restoring the indexes FIRST.
182203
backupsRestored = await RestoreBackups(backupsDirectory);
204+
progress?.Report("Restoring index file backups...");
183205

184206
if (!backupsRestored)
185207
{
186208
throw new Exception("Start Over Failed: Index backups missing/outdated.");
187209
}
188210
}
189-
finally
211+
catch(Exception ex)
190212
{
191-
// If no exception occured, restore the backups anyway just to be safe but don't throw an exception if it fails
192-
// due to outdated or missing backups since setting back the original index values should be enough hopefully
193-
if (!backupsRestored)
213+
try
194214
{
195-
backupsRestored = await RestoreBackups(backupsDirectory);
196-
197-
// If backups were not restored that means they were missing/outdated so try to make new backups now
198-
if (!backupsRestored)
199-
{
200-
try
201-
{
202-
await BackupIndexFiles(backupsDirectory);
203-
}
204-
catch (Exception ex)
205-
{
206-
throw new Exception("Start Over Failed: Failed to update outdated backups.\n\n" + ex.Message);
207-
}
208-
}
215+
// If the index restore failed, try just disabling.
216+
await modding.DeleteAllFilesAddedByTexTools();
217+
await modding.ToggleAllMods(false);
218+
progress?.Report("Index restore failed, attempting to delete all mods instead...");
219+
} catch
220+
{
221+
throw new Exception("Start Over Failed: Index Backups Invalid and Unable to Disable all mods.");
209222
}
210-
223+
}
224+
finally
225+
{
211226
progress?.Report("Deleting modded dat files...");
212227

213228
var dat = new Dat(_gameDirectory);
@@ -241,8 +256,7 @@ public Task PerformStartOver(DirectoryInfo backupsDirectory, IProgress<string> p
241256

242257
await Task.Run(async () =>
243258
{
244-
var _cache = new XivCache(_gameDirectory, language);
245-
_cache.RebuildCache();
259+
XivCache.RebuildCache();
246260
});
247261
}
248262
});
@@ -302,11 +316,18 @@ public Task<bool> RestoreBackups(DirectoryInfo backupsDirectory)
302316

303317
if (!File.Exists(backupFile.FullName)) continue;
304318

305-
var outdatedCheck = await CheckForOutdatedBackups(xivDataFile, backupsDirectory);
306-
307-
if (!outdatedCheck)
319+
try
308320
{
309-
outdated = true;
321+
var outdatedCheck = await CheckForOutdatedBackups(xivDataFile, backupsDirectory);
322+
323+
if (!outdatedCheck)
324+
{
325+
outdated = true;
326+
}
327+
}
328+
catch {
329+
// If the outdated check errored out, we likely have completely broken internal dat files.
330+
// ( Either deleted or 0 byte files ), so replacing them with *anything* is an improvement.
310331
}
311332
}
312333

xivModdingFramework/Items/Categories/Character.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public Task<List<XivCharacter>> GetCharacterList()
6161
new XivCharacter
6262
{Name = XivStrings.Tail, PrimaryCategory = XivStrings.Character, SecondaryCategory = XivStrings.Tail},
6363
new XivCharacter
64-
{Name = XivStrings.Ears, PrimaryCategory = XivStrings.Character, SecondaryCategory = XivStrings.Ears},
64+
{Name = XivStrings.Ear, PrimaryCategory = XivStrings.Character, SecondaryCategory = XivStrings.Ear},
6565
new XivCharacter
6666
{
6767
Name = XivStrings.Face_Paint, PrimaryCategory = XivStrings.Character,
@@ -112,7 +112,7 @@ public async Task<Dictionary<XivRace, int[]>> GetRacesAndNumbersForTextures(XivC
112112
{
113113
folder = XivStrings.TailMtrlFolder;
114114
}
115-
else if (charaItem.SecondaryCategory == XivStrings.Ears)
115+
else if (charaItem.SecondaryCategory == XivStrings.Ear)
116116
{
117117
folder = XivStrings.EarsMtrlFolder;
118118
}
@@ -174,7 +174,7 @@ public async Task<Dictionary<XivRace, int[]>> GetRacesAndNumbersForModels(XivCha
174174
{
175175
folder = XivStrings.TailMDLFolder;
176176
}
177-
else if (charaItem.SecondaryCategory == XivStrings.Ears)
177+
else if (charaItem.SecondaryCategory == XivStrings.Ear)
178178
{
179179
folder = XivStrings.EarsMDLFolder;
180180
}
@@ -234,7 +234,7 @@ public async Task<Dictionary<string, char[]>> GetTypePartForTextures(XivCharacte
234234
typeDict = FaceSlotAbbreviationDictionary;
235235
file = XivStrings.FaceMtrlFile;
236236
}
237-
else if (charaItem.SecondaryCategory == XivStrings.Ears)
237+
else if (charaItem.SecondaryCategory == XivStrings.Ear)
238238
{
239239
folder = string.Format(XivStrings.EarsMtrlFolder, race.GetRaceCode(),
240240
num.ToString().PadLeft(4, '0'));
@@ -296,7 +296,7 @@ public async Task<char[]> GetPartForTextures(XivCharacter charaItem, XivRace rac
296296
folder = string.Format(XivStrings.TailMtrlFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0'));
297297
file = XivStrings.TailMtrlFile;
298298
}
299-
else if (charaItem.SecondaryCategory == XivStrings.Ears)
299+
else if (charaItem.SecondaryCategory == XivStrings.Ear)
300300
{
301301
folder = string.Format(XivStrings.EarsMtrlFolder, race.GetRaceCode(), num.ToString().PadLeft(4, '0'));
302302

@@ -387,7 +387,7 @@ public async Task<List<string>> GetTypeForModels(XivCharacter charaItem, XivRace
387387
typeDict = TailSlotAbbreviationDictionary;
388388
file = XivStrings.TailMDLFile;
389389
}
390-
else if (charaItem.SecondaryCategory == XivStrings.Ears)
390+
else if (charaItem.SecondaryCategory == XivStrings.Ear)
391391
{
392392
folder = string.Format(XivStrings.EarsMDLFolder, race.GetRaceCode(),
393393
num.ToString().PadLeft(4, '0'));
@@ -526,7 +526,7 @@ await Task.Run(() => Parallel.For(0, 300, (i) =>
526526
/// </summary>
527527
private static readonly Dictionary<string, string> EarsSlotAbbreviationDictionary = new Dictionary<string, string>
528528
{
529-
{XivStrings.Ears, "zer"},
529+
{XivStrings.Ear, "zer"},
530530
{XivStrings.InnerEar, "fac_"},
531531
{XivStrings.OuterEar, "" }
532532
};

0 commit comments

Comments
 (0)