Skip to content

Commit 77058fb

Browse files
committed
Update 2.3.1.5
2 parents 360eb01 + 8d454d9 commit 77058fb

File tree

8 files changed

+387
-146
lines changed

8 files changed

+387
-146
lines changed

xivModdingFramework/Cache/XivCache.cs

Lines changed: 65 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,7 +1261,7 @@ public static async Task<List<IItem>> GetFullItemList()
12611261
/// </summary>
12621262
/// <param name="key"></param>
12631263
/// <param name="value"></param>
1264-
public static void SetMetaValue(string key, string value = null)
1264+
public static void SetMetaValue(string key, string value)
12651265
{
12661266
using (var db = new SQLiteConnection(CacheConnectionString))
12671267
{
@@ -1275,6 +1275,20 @@ public static void SetMetaValue(string key, string value = null)
12751275
}
12761276
}
12771277
}
1278+
public static void SetMetaValue(string key, bool value)
1279+
{
1280+
SetMetaValue(key, value ? "True" : "False");
1281+
}
1282+
public static bool GetMetaValueBoolean(string key)
1283+
{
1284+
var st = GetMetaValue(key);
1285+
1286+
bool result = false;
1287+
bool success = Boolean.TryParse(st, out result);
1288+
1289+
if (!success) return false;
1290+
return result;
1291+
}
12781292

12791293
/// <summary>
12801294
/// Retrieves a meta value from the cache.
@@ -2075,38 +2089,40 @@ private static string PopChildQueue()
20752089
{
20762090
db.Open();
20772091

2078-
using (var transaction = db.BeginTransaction())
2092+
var query = "select position, file from dependencies_children_queue";
2093+
using (var selectCmd = new SQLiteCommand(query, db))
20792094
{
2080-
var query = "select position, file from dependencies_children_queue";
2081-
using (var selectCmd = new SQLiteCommand(query, db))
2095+
using (var reader = new CacheReader(selectCmd.ExecuteReader()))
20822096
{
2083-
using (var reader = new CacheReader(selectCmd.ExecuteReader()))
2097+
if (!reader.NextRow())
20842098
{
2085-
if (!reader.NextRow())
2086-
{
2087-
// No entries left.
2088-
return null;
2089-
}
2090-
// Got the new item.
2091-
file = reader.GetString("file");
2092-
position = reader.GetInt32("position");
2099+
// No entries left.
2100+
return null;
20932101
}
2102+
// Got the new item.
2103+
file = reader.GetString("file");
2104+
position = reader.GetInt32("position");
20942105
}
2095-
2096-
// Delete the row we took and all others that match the filename.
2097-
query = "delete from dependencies_children_queue where file = $file";
2098-
using (var deleteCmd = new SQLiteCommand(query, db))
2099-
{
2100-
deleteCmd.Parameters.AddWithValue("file", file);
2101-
deleteCmd.ExecuteScalar();
2102-
}
2103-
2104-
transaction.Commit();
21052106
}
21062107
}
21072108
return file;
21082109
}
21092110

2111+
public static void RemoveFromChildQueue(string file)
2112+
{
2113+
using (var db = new SQLiteConnection(CacheConnectionString))
2114+
{
2115+
db.Open();
2116+
2117+
// Delete the row we took and all others that match the filename.
2118+
var query = "delete from dependencies_children_queue where file = $file";
2119+
using (var deleteCmd = new SQLiteCommand(query, db))
2120+
{
2121+
deleteCmd.Parameters.AddWithValue("file", file);
2122+
deleteCmd.ExecuteScalar();
2123+
}
2124+
}
2125+
}
21102126

21112127
private static string PopParentQueue()
21122128
{
@@ -2116,37 +2132,39 @@ private static string PopParentQueue()
21162132
{
21172133
db.Open();
21182134

2119-
using (var transaction = db.BeginTransaction())
2135+
var query = "select position, file from dependencies_parents_queue";
2136+
using (var selectCmd = new SQLiteCommand(query, db))
21202137
{
2121-
var query = "select position, file from dependencies_parents_queue";
2122-
using (var selectCmd = new SQLiteCommand(query, db))
2138+
using (var reader = new CacheReader(selectCmd.ExecuteReader()))
21232139
{
2124-
using (var reader = new CacheReader(selectCmd.ExecuteReader()))
2140+
if (!reader.NextRow())
21252141
{
2126-
if (!reader.NextRow())
2127-
{
2128-
// No entries left.
2129-
return null;
2130-
}
2131-
// Got the new item.
2132-
file = reader.GetString("file");
2133-
position = reader.GetInt32("position");
2142+
// No entries left.
2143+
return null;
21342144
}
2145+
// Got the new item.
2146+
file = reader.GetString("file");
2147+
position = reader.GetInt32("position");
21352148
}
2136-
2137-
// Delete the row we took and all others that match the filename.
2138-
query = "delete from dependencies_parents_queue where file = $file";
2139-
using (var deleteCmd = new SQLiteCommand(query, db))
2140-
{
2141-
deleteCmd.Parameters.AddWithValue("file", file);
2142-
deleteCmd.ExecuteScalar();
2143-
}
2144-
2145-
transaction.Commit();
21462149
}
21472150
}
21482151
return file;
21492152
}
2153+
public static void RemoveFromParentQueue(string file)
2154+
{
2155+
using (var db = new SQLiteConnection(CacheConnectionString))
2156+
{
2157+
db.Open();
2158+
2159+
// Delete the row we took and all others that match the filename.
2160+
var query = "delete from dependencies_parents_queue where file = $file";
2161+
using (var deleteCmd = new SQLiteCommand(query, db))
2162+
{
2163+
deleteCmd.Parameters.AddWithValue("file", file);
2164+
deleteCmd.ExecuteScalar();
2165+
}
2166+
}
2167+
}
21502168

21512169

21522170
/// <summary>
@@ -2178,6 +2196,7 @@ private static void ProcessDependencyQueue(object sender, DoWorkEventArgs e)
21782196

21792197
// Set a safety timeout here.
21802198
task.Wait(3000);
2199+
RemoveFromChildQueue(file);
21812200
continue;
21822201

21832202
}
@@ -2203,6 +2222,7 @@ private static void ProcessDependencyQueue(object sender, DoWorkEventArgs e)
22032222

22042223
// Set a safety timeout here.
22052224
task.Wait(3000);
2225+
RemoveFromParentQueue(file);
22062226
continue;
22072227
}
22082228
}

xivModdingFramework/Models/DataContainers/TTModel.cs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,8 @@ public uint IndexCount
293293
/// </summary>
294294
public class TTModel
295295
{
296+
public static string _SETTINGS_KEY_EXPORT_ALL_BONES = "setting_export_all_bones";
297+
296298
/// <summary>
297299
/// The internal or external file path where this TTModel originated from.
298300
/// </summary>
@@ -832,7 +834,10 @@ public void SaveToFile(string filePath, Action<bool, string> loggingFunction = n
832834
var connectionString = "Data Source=" + filePath + ";Pooling=False;";
833835
try
834836
{
835-
var boneDict = ResolveBoneHeirarchy(null, XivRace.All_Races, Bones, loggingFunction);
837+
var useAllBones = XivCache.GetMetaValueBoolean(_SETTINGS_KEY_EXPORT_ALL_BONES);
838+
var bones = useAllBones ? null : Bones;
839+
840+
var boneDict = ResolveBoneHeirarchy(null, XivRace.All_Races, bones, loggingFunction);
836841

837842
const string creationScript = "CreateImportDB.sql";
838843
// Spawn a DB connection to do the raw queries.
@@ -1711,8 +1716,8 @@ public static Dictionary<string, SkeletonData> ResolveBoneHeirarchyRaw(List<XivD
17111716
{
17121717
// This is a parent level reference to a base bone.
17131718
exTranslationTable.Add(j.BoneNumber, fullSkel[j.BoneName].BoneNumber);
1714-
}
1715-
else
1719+
}
1720+
else if (exTranslationTable.ContainsKey(j.BoneParent))
17161721
{
17171722
// Run it through the translation to match up with the base skeleton.
17181723
j.BoneParent = exTranslationTable[j.BoneParent];
@@ -1723,6 +1728,19 @@ public static Dictionary<string, SkeletonData> ResolveBoneHeirarchyRaw(List<XivD
17231728

17241729
fullSkel.Add(j.BoneName, j);
17251730
exTranslationTable.Add(originalNumber, j.BoneNumber);
1731+
} else
1732+
{
1733+
// This is a root bone in the EX skeleton that has no parent element in the base skeleton.
1734+
// Just stick it onto the root bone.
1735+
j.BoneParent = fullSkel["n_root"].BoneNumber;
1736+
1737+
// And generate its own new bone number
1738+
var originalNumber = j.BoneNumber;
1739+
j.BoneNumber = fullSkel.Select(x => x.Value.BoneNumber).Max() + 1;
1740+
1741+
fullSkel.Add(j.BoneName, j);
1742+
exTranslationTable.Add(originalNumber, j.BoneNumber);
1743+
17261744
}
17271745
}
17281746
} catch(Exception ex)

xivModdingFramework/Models/FileTypes/Eqp.cs

Lines changed: 108 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,26 @@ public Eqp(DirectoryInfo gameDirectory)
112112

113113
public async Task SaveGimmickParameter(int equipmentId, GimmickParameter param)
114114
{
115+
if (equipmentId == 0)
116+
{
117+
throw new InvalidDataException("Cannot write GMP data for Set 0. (Use Set 1)");
118+
}
119+
115120
var data = await LoadGimmickParameterFile(false);
116121

117122
var offset = ResolveEqpEntryOffset(data, equipmentId);
118123

119124
if (offset == -1)
120125
{
121-
// Fug. Gotta write expansion function.
126+
// Expand the block, then get the offset.
127+
// (GMP files use an identical file structure to EQP files)
128+
data = ExpandEqpBlock(data, equipmentId);
129+
offset = ResolveEqpEntryOffset(data, equipmentId);
130+
131+
if(offset <= 0)
132+
{
133+
throw new InvalidDataException("Unable to resolve GMP data offset.");
134+
}
122135
}
123136

124137
IOUtil.ReplaceBytesAt(data, param.GetBytes(), offset);
@@ -211,9 +224,15 @@ public async Task SaveEqpEntry(int equipmentId, EquipmentParameter data)
211224

212225
if(offset < 0)
213226
{
214-
// Not bothering to write a function to decompress empty EQP blocks, since it seems like SE keeps every equipment block with actual equipment in it decompressed.
215-
// I suppose in theory we could use it for adding custom items in the e4000 range or something.
216-
throw new Exception("Cannot write compressed EQP Entry. (Please report this in Discord!)");
227+
// Expand the data block, then try again.
228+
file = ExpandEqpBlock(file, equipmentId);
229+
230+
offset = ResolveEqpEntryOffset(file, equipmentId);
231+
232+
if(offset < 0)
233+
{
234+
throw new InvalidDataException("Unable to determine EQP set offset.");
235+
}
217236
}
218237

219238
var slotOffset = EquipmentParameterSet.EntryOffsets[data.Slot];
@@ -382,6 +401,91 @@ private async Task SaveEquipmentParameterFile(byte[] file)
382401
}
383402

384403

404+
/// <summary>
405+
/// Expands a compressed EQP block.
406+
/// </summary>
407+
/// <param name="eqpData"></param>
408+
/// <param name="setId"></param>
409+
/// <returns></returns>
410+
private byte[] ExpandEqpBlock(byte[] eqpData, int setId)
411+
{
412+
const int blockSize = 160;
413+
// 160 Entry blocks.
414+
var blockId = setId / blockSize;
415+
var byteNum = blockId / 8;
416+
var bit = 1 << (blockId % 8);
417+
418+
if ((eqpData[byteNum] & bit) != 0)
419+
{
420+
// Block is already uncompressed.
421+
return eqpData;
422+
}
423+
424+
// Flip the flag bit.
425+
eqpData[byteNum] = (byte)(eqpData[byteNum] | bit);
426+
427+
// Okay, now we have to go through and find out many uncompressed blocks
428+
// exist before our block.
429+
int uncompressedBlocks = 0;
430+
int totalUncompressedBlocks = 0;
431+
432+
// Loop bytes
433+
bool found = false;
434+
for (int i = 0; i < 8; i++)
435+
{
436+
var byt = eqpData[i];
437+
// Loop bits
438+
for (int b = 0; b < 8; b++)
439+
{
440+
if (i == byteNum && b == (blockId % 8))
441+
{
442+
// Done seeking.
443+
found = true;
444+
}
445+
446+
var bt = 1 << b;
447+
var on = (byt & bt) != 0;
448+
if (on)
449+
{
450+
if (!found)
451+
{
452+
uncompressedBlocks++;
453+
}
454+
totalUncompressedBlocks++;
455+
}
456+
}
457+
}
458+
459+
// This is the offset where our new block will start.
460+
var baseOffset = uncompressedBlocks * blockSize;
461+
462+
// Total Size of the data.
463+
var totalDataSize = (totalUncompressedBlocks * blockSize * EquipmentParameterEntrySize);
464+
465+
// Pad out to nearest 512 bytes.
466+
var padding = totalDataSize % 512 == 0 ? 0 : 512 - (totalDataSize % 512);
467+
468+
var finalSize = totalDataSize + padding;
469+
470+
// Okay, we've now fully updated the initial block table. We need to copy all the data over.
471+
var newDataByteOffset = (baseOffset * EquipmentParameterEntrySize);
472+
473+
var newData = new byte[finalSize];
474+
475+
// Copy the first half of the data in, then a blank block, then the second half of the data.
476+
Array.Copy(eqpData, 0, newData, 0, newDataByteOffset);
477+
var rem = eqpData.Length - newDataByteOffset;
478+
var rem2 = newData.Length - newDataByteOffset - (blockSize * EquipmentDeformerParameterEntrySize);
479+
480+
// Don't let us try to write padding data off the end of the file.
481+
rem = rem > rem2 ? rem2 : rem;
482+
483+
Array.Copy(eqpData, newDataByteOffset, newData, newDataByteOffset + (blockSize * EquipmentDeformerParameterEntrySize), rem);
484+
485+
// Return new array.
486+
return newData;
487+
}
488+
385489
/// <summary>
386490
/// Resolves the offset to the given EQP data entry.
387491
/// </summary>

0 commit comments

Comments
 (0)