@@ -372,14 +372,32 @@ public async Task<bool> FileExists(string fullPath)
372
372
return await FileExists ( fileHash , pathHash , dataFile ) ;
373
373
}
374
374
375
- /// <summary>
376
- /// Determines whether the given file path exists
377
- /// </summary>
378
- /// <param name="fileHash">The hashed file</param>
379
- /// <param name="folderHash">The hashed folder</param>
380
- /// <param name="dataFile">The data file</param>
381
- /// <returns>True if it exists, False otherwise</returns>
382
- public async Task < bool > FileExists ( int fileHash , int folderHash , XivDataFile dataFile )
375
+
376
+ /// <summary>
377
+ /// Tests if a given file path in FFXIV's internal directory structure
378
+ /// is one that ships with FFXIV, or something added by the framework.
379
+ /// </summary>
380
+ /// <returns></returns>
381
+ public async Task < bool > IsDefaultFilePath ( string fullPath )
382
+ {
383
+ // The framework adds flag files alongside every custom created file.
384
+ // This lets you check for them even if the Modlist gets corrupted/lost.
385
+ var exists = await FileExists ( fullPath ) ;
386
+ var hasFlag = await FileExists ( fullPath + ".flag" ) ;
387
+
388
+ // In order to be considered a DEFAULT file, the file must both EXIST *and* not have a flag.
389
+ var stockFile = exists && hasFlag ;
390
+ return stockFile ;
391
+ }
392
+
393
+ /// <summary>
394
+ /// Determines whether the given file path exists
395
+ /// </summary>
396
+ /// <param name="fileHash">The hashed file</param>
397
+ /// <param name="folderHash">The hashed folder</param>
398
+ /// <param name="dataFile">The data file</param>
399
+ /// <returns>True if it exists, False otherwise</returns>
400
+ public async Task < bool > FileExists ( int fileHash , int folderHash , XivDataFile dataFile )
383
401
{
384
402
var indexPath = Path . Combine ( _gameDirectory . FullName , $ "{ dataFile . GetDataFileName ( ) } { IndexExtension } ") ;
385
403
@@ -747,13 +765,26 @@ public bool DeleteFileDescriptor(string fullPath, XivDataFile dataFile)
747
765
// Update folder size
748
766
if ( iHash == uPathHash )
749
767
{
750
- foundFolder = true ;
751
- Array . Copy ( BitConverter . GetBytes ( iFolderSize - 16 ) , 0 , modifiedIndex , position + 8 , 4 ) ;
768
+ if ( iFolderSize == 0 )
769
+ {
770
+ // No more files in the folder, the folder needs to be deleted from the listing
771
+ // (0 size folders are not listed, even if they're parent folders for other folders)
772
+ remainder = modifiedIndex . Length - position - 16 ;
773
+ Array . Copy ( modifiedIndex , position + 16 , modifiedIndex , position , remainder ) ;
774
+
775
+ var newIndex = new byte [ modifiedIndex . Length - 16 ] ;
776
+ Array . Copy ( modifiedIndex , 0 , modifiedIndex , 0 , newIndex . Length ) ;
777
+ foundFolder = true ;
778
+ } else
779
+ {
780
+ Array . Copy ( BitConverter . GetBytes ( iFolderSize - 16 ) , 0 , modifiedIndex , position + 8 , 4 ) ;
781
+ }
752
782
}
753
783
}
754
784
755
785
if ( ! foundFolder )
756
786
{
787
+ // Something went wrong here / The index is in a bad state.
757
788
return false ;
758
789
}
759
790
@@ -876,14 +907,15 @@ public bool DeleteFileDescriptor(string fullPath, XivDataFile dataFile)
876
907
return true ;
877
908
}
878
909
879
- /// <summary>
880
- /// Adds a new file descriptor/stub into the Index files.
881
- /// </summary>
882
- /// <param name="fullPath">Full path to the new file.</param>
883
- /// <param name="dataOffset">Raw DAT file offset to use for the new file.</param>
884
- /// <param name="dataFile">Which data file set to use.</param>
885
- /// <returns></returns>
886
- public bool AddFileDescriptor ( string fullPath , int dataOffset , XivDataFile dataFile )
910
+
911
+ /// <summary>
912
+ /// Adds a new file descriptor/stub into the Index files.
913
+ /// </summary>
914
+ /// <param name="fullPath">Full path to the new file.</param>
915
+ /// <param name="dataOffset">Raw DAT file offset to use for the new file.</param>
916
+ /// <param name="dataFile">Which data file set to use.</param>
917
+ /// <returns></returns>
918
+ public bool AddFileDescriptor ( string fullPath , int dataOffset , XivDataFile dataFile )
887
919
{
888
920
fullPath = fullPath . Replace ( "\\ " , "/" ) ;
889
921
var pathHash = HashGenerator . GetHash ( fullPath . Substring ( 0 , fullPath . LastIndexOf ( "/" , StringComparison . Ordinal ) ) ) ;
@@ -910,7 +942,6 @@ public bool AddFileDescriptor(string fullPath, int dataOffset, XivDataFile dataF
910
942
911
943
// Dump the index into memory, since we're going to have to inject data.
912
944
byte [ ] originalIndex = File . ReadAllBytes ( indexPath ) ;
913
- byte [ ] modifiedIndex = new byte [ originalIndex . Length + 16 ] ;
914
945
915
946
// Get all the segment header data
916
947
for ( int i = 0 ; i < SegmentHeaders . Length ; i ++ )
@@ -946,6 +977,11 @@ public bool AddFileDescriptor(string fullPath, int dataOffset, XivDataFile dataF
946
977
injectLocation = position ;
947
978
break ;
948
979
}
980
+ } else if ( iPathHash > uPathHash )
981
+ {
982
+ // This is where the folder should go, it just has no files currently.
983
+ injectLocation = position ;
984
+ break ;
949
985
}
950
986
else
951
987
{
@@ -958,11 +994,9 @@ public bool AddFileDescriptor(string fullPath, int dataOffset, XivDataFile dataF
958
994
}
959
995
}
960
996
961
- // Cancel if we failed to find the path.
962
- if ( foundFolder == false )
963
- {
964
- return false ;
965
- }
997
+
998
+ var totalInjectSize = foundFolder ? 16 : 32 ;
999
+ byte [ ] modifiedIndex = new byte [ originalIndex . Length + totalInjectSize ] ;
966
1000
967
1001
// Split the file at the injection point.
968
1002
int remainder = originalIndex . Length - injectLocation ;
@@ -983,8 +1017,16 @@ public bool AddFileDescriptor(string fullPath, int dataOffset, XivDataFile dataF
983
1017
// Update other segments' offsets.
984
1018
else
985
1019
{
1020
+
986
1021
SegmentOffsets [ i ] += 16 ;
987
1022
Array . Copy ( BitConverter . GetBytes ( SegmentOffsets [ i ] ) , 0 , modifiedIndex , SegmentHeaders [ i ] + 4 , 4 ) ;
1023
+
1024
+ // We need to create the folder as well.
1025
+ if ( i == 3 && ! foundFolder )
1026
+ {
1027
+ SegmentSizes [ i ] += 16 ;
1028
+ Array . Copy ( BitConverter . GetBytes ( SegmentSizes [ i ] ) , 0 , modifiedIndex , SegmentHeaders [ i ] + 8 , 4 ) ;
1029
+ }
988
1030
}
989
1031
}
990
1032
@@ -995,7 +1037,7 @@ public bool AddFileDescriptor(string fullPath, int dataOffset, XivDataFile dataF
995
1037
996
1038
// Update the folder structure
997
1039
var folderCount = SegmentSizes [ 3 ] / 16 ;
998
- foundFolder = false ;
1040
+ var foundFolder2 = false ;
999
1041
1000
1042
for ( int i = 0 ; i < folderCount ; i ++ )
1001
1043
{
@@ -1010,19 +1052,24 @@ public bool AddFileDescriptor(string fullPath, int dataOffset, XivDataFile dataF
1010
1052
Array . Copy ( BitConverter . GetBytes ( iOffset + 16 ) , 0 , modifiedIndex , position + 4 , 4 ) ;
1011
1053
}
1012
1054
1013
- // Update folder size
1055
+ // Folder exists, but needs its size updated.
1014
1056
if ( iHash == uPathHash )
1015
1057
{
1016
- foundFolder = true ;
1017
1058
Array . Copy ( BitConverter . GetBytes ( iFolderSize + 16 ) , 0 , modifiedIndex , position + 8 , 4 ) ;
1059
+ } else if ( foundFolder == false && iHash > uPathHash ) {
1060
+ foundFolder = true ;
1061
+ // This is where we need to cut the index the second time to make room for the folder data.
1062
+ remainder = modifiedIndex . Length - position - 16 ;
1063
+ Array . Copy ( modifiedIndex , position , modifiedIndex , position + 16 , remainder ) ;
1064
+
1065
+ // The new folder entry now goes at the 16 bytes starting at position
1066
+ Array . Copy ( BitConverter . GetBytes ( uPathHash ) , 0 , modifiedIndex , position , 4 ) ;
1067
+ Array . Copy ( BitConverter . GetBytes ( injectLocation ) , 0 , modifiedIndex , position + 4 , 4 ) ;
1068
+ Array . Copy ( BitConverter . GetBytes ( 16 ) , 0 , modifiedIndex , position + 8 , 4 ) ;
1069
+ Array . Copy ( BitConverter . GetBytes ( 16 ) , 0 , modifiedIndex , position + 12 , 4 ) ;
1018
1070
}
1019
1071
}
1020
1072
1021
- if ( ! foundFolder )
1022
- {
1023
- return false ;
1024
- }
1025
-
1026
1073
// Update SHA-1 Hashes.
1027
1074
SHA1 sha = new SHA1CryptoServiceProvider ( ) ;
1028
1075
byte [ ] shaHash ;
0 commit comments