Skip to content

Commit 6bc573d

Browse files
committed
Allow expanding compressed EQP blocks.
1 parent 360eb01 commit 6bc573d

File tree

1 file changed

+94
-3
lines changed
  • xivModdingFramework/Models/FileTypes

1 file changed

+94
-3
lines changed

xivModdingFramework/Models/FileTypes/Eqp.cs

Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,15 @@ public async Task SaveEqpEntry(int equipmentId, EquipmentParameter data)
211211

212212
if(offset < 0)
213213
{
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!)");
214+
// Expand the data block, then try again.
215+
file = ExpandEqpBlock(file, equipmentId);
216+
217+
offset = ResolveEqpEntryOffset(file, equipmentId);
218+
219+
if(offset < 0)
220+
{
221+
throw new InvalidDataException("Unable to determine EQP set offset.");
222+
}
217223
}
218224

219225
var slotOffset = EquipmentParameterSet.EntryOffsets[data.Slot];
@@ -382,6 +388,91 @@ private async Task SaveEquipmentParameterFile(byte[] file)
382388
}
383389

384390

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

0 commit comments

Comments
 (0)