@@ -211,9 +211,15 @@ public async Task SaveEqpEntry(int equipmentId, EquipmentParameter data)
211
211
212
212
if ( offset < 0 )
213
213
{
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
+ }
217
223
}
218
224
219
225
var slotOffset = EquipmentParameterSet . EntryOffsets [ data . Slot ] ;
@@ -382,6 +388,91 @@ private async Task SaveEquipmentParameterFile(byte[] file)
382
388
}
383
389
384
390
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
+
385
476
/// <summary>
386
477
/// Resolves the offset to the given EQP data entry.
387
478
/// </summary>
0 commit comments