@@ -199,6 +199,15 @@ public Task<bool> HaveFilesAddedByTexTools(XivDataFile dataFile)
199
199
} ) ;
200
200
}
201
201
202
+ public async Task < int > GetDataOffset ( string fullPath )
203
+ {
204
+ var dataFile = IOUtil . GetDataFileFromPath ( fullPath ) ;
205
+
206
+ var pathHash = HashGenerator . GetHash ( fullPath . Substring ( 0 , fullPath . LastIndexOf ( "/" , StringComparison . Ordinal ) ) ) ;
207
+ var fileHash = HashGenerator . GetHash ( Path . GetFileName ( fullPath ) ) ;
208
+ return await GetDataOffset ( pathHash , fileHash , dataFile ) ;
209
+
210
+ }
202
211
/// <summary>
203
212
/// Gets the offset for the data in the .dat file
204
213
/// </summary>
@@ -372,14 +381,32 @@ public async Task<bool> FileExists(string fullPath)
372
381
return await FileExists ( fileHash , pathHash , dataFile ) ;
373
382
}
374
383
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 )
384
+
385
+ /// <summary>
386
+ /// Tests if a given file path in FFXIV's internal directory structure
387
+ /// is one that ships with FFXIV, or something added by the framework.
388
+ /// </summary>
389
+ /// <returns></returns>
390
+ public async Task < bool > IsDefaultFilePath ( string fullPath )
391
+ {
392
+ // The framework adds flag files alongside every custom created file.
393
+ // This lets you check for them even if the Modlist gets corrupted/lost.
394
+ var exists = await FileExists ( fullPath ) ;
395
+ var hasFlag = await FileExists ( fullPath + ".flag" ) ;
396
+
397
+ // In order to be considered a DEFAULT file, the file must both EXIST *and* not have a flag.
398
+ var stockFile = exists && ! hasFlag ;
399
+ return stockFile ;
400
+ }
401
+
402
+ /// <summary>
403
+ /// Determines whether the given file path exists
404
+ /// </summary>
405
+ /// <param name="fileHash">The hashed file</param>
406
+ /// <param name="folderHash">The hashed folder</param>
407
+ /// <param name="dataFile">The data file</param>
408
+ /// <returns>True if it exists, False otherwise</returns>
409
+ public async Task < bool > FileExists ( int fileHash , int folderHash , XivDataFile dataFile )
383
410
{
384
411
var indexPath = Path . Combine ( _gameDirectory . FullName , $ "{ dataFile . GetDataFileName ( ) } { IndexExtension } ") ;
385
412
@@ -747,13 +774,26 @@ public bool DeleteFileDescriptor(string fullPath, XivDataFile dataFile)
747
774
// Update folder size
748
775
if ( iHash == uPathHash )
749
776
{
750
- foundFolder = true ;
751
- Array . Copy ( BitConverter . GetBytes ( iFolderSize - 16 ) , 0 , modifiedIndex , position + 8 , 4 ) ;
777
+ if ( iFolderSize == 0 )
778
+ {
779
+ // No more files in the folder, the folder needs to be deleted from the listing
780
+ // (0 size folders are not listed, even if they're parent folders for other folders)
781
+ remainder = modifiedIndex . Length - position - 16 ;
782
+ Array . Copy ( modifiedIndex , position + 16 , modifiedIndex , position , remainder ) ;
783
+
784
+ var newIndex = new byte [ modifiedIndex . Length - 16 ] ;
785
+ Array . Copy ( modifiedIndex , 0 , modifiedIndex , 0 , newIndex . Length ) ;
786
+ foundFolder = true ;
787
+ } else
788
+ {
789
+ Array . Copy ( BitConverter . GetBytes ( iFolderSize - 16 ) , 0 , modifiedIndex , position + 8 , 4 ) ;
790
+ }
752
791
}
753
792
}
754
793
755
794
if ( ! foundFolder )
756
795
{
796
+ // Something went wrong here / The index is in a bad state.
757
797
return false ;
758
798
}
759
799
@@ -876,13 +916,14 @@ public bool DeleteFileDescriptor(string fullPath, XivDataFile dataFile)
876
916
return true ;
877
917
}
878
918
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>
919
+
920
+ /// <summary>
921
+ /// Adds a new file descriptor/stub into the Index files.
922
+ /// </summary>
923
+ /// <param name="fullPath">Full path to the new file.</param>
924
+ /// <param name="dataOffset">Raw DAT file offset to use for the new file.</param>
925
+ /// <param name="dataFile">Which data file set to use.</param>
926
+ /// <returns></returns>
886
927
public bool AddFileDescriptor ( string fullPath , int dataOffset , XivDataFile dataFile )
887
928
{
888
929
fullPath = fullPath . Replace ( "\\ " , "/" ) ;
@@ -910,7 +951,6 @@ public bool AddFileDescriptor(string fullPath, int dataOffset, XivDataFile dataF
910
951
911
952
// Dump the index into memory, since we're going to have to inject data.
912
953
byte [ ] originalIndex = File . ReadAllBytes ( indexPath ) ;
913
- byte [ ] modifiedIndex = new byte [ originalIndex . Length + 16 ] ;
914
954
915
955
// Get all the segment header data
916
956
for ( int i = 0 ; i < SegmentHeaders . Length ; i ++ )
@@ -946,6 +986,11 @@ public bool AddFileDescriptor(string fullPath, int dataOffset, XivDataFile dataF
946
986
injectLocation = position ;
947
987
break ;
948
988
}
989
+ } else if ( iPathHash > uPathHash )
990
+ {
991
+ // This is where the folder should go, it just has no files currently.
992
+ injectLocation = position ;
993
+ break ;
949
994
}
950
995
else
951
996
{
@@ -958,11 +1003,9 @@ public bool AddFileDescriptor(string fullPath, int dataOffset, XivDataFile dataF
958
1003
}
959
1004
}
960
1005
961
- // Cancel if we failed to find the path.
962
- if ( foundFolder == false )
963
- {
964
- return false ;
965
- }
1006
+
1007
+ var totalInjectSize = foundFolder ? 16 : 32 ;
1008
+ byte [ ] modifiedIndex = new byte [ originalIndex . Length + totalInjectSize ] ;
966
1009
967
1010
// Split the file at the injection point.
968
1011
int remainder = originalIndex . Length - injectLocation ;
@@ -983,8 +1026,16 @@ public bool AddFileDescriptor(string fullPath, int dataOffset, XivDataFile dataF
983
1026
// Update other segments' offsets.
984
1027
else
985
1028
{
1029
+
986
1030
SegmentOffsets [ i ] += 16 ;
987
1031
Array . Copy ( BitConverter . GetBytes ( SegmentOffsets [ i ] ) , 0 , modifiedIndex , SegmentHeaders [ i ] + 4 , 4 ) ;
1032
+
1033
+ // We need to create the folder as well.
1034
+ if ( i == 3 && ! foundFolder )
1035
+ {
1036
+ SegmentSizes [ i ] += 16 ;
1037
+ Array . Copy ( BitConverter . GetBytes ( SegmentSizes [ i ] ) , 0 , modifiedIndex , SegmentHeaders [ i ] + 8 , 4 ) ;
1038
+ }
988
1039
}
989
1040
}
990
1041
@@ -995,7 +1046,6 @@ public bool AddFileDescriptor(string fullPath, int dataOffset, XivDataFile dataF
995
1046
996
1047
// Update the folder structure
997
1048
var folderCount = SegmentSizes [ 3 ] / 16 ;
998
- foundFolder = false ;
999
1049
1000
1050
for ( int i = 0 ; i < folderCount ; i ++ )
1001
1051
{
@@ -1010,19 +1060,24 @@ public bool AddFileDescriptor(string fullPath, int dataOffset, XivDataFile dataF
1010
1060
Array . Copy ( BitConverter . GetBytes ( iOffset + 16 ) , 0 , modifiedIndex , position + 4 , 4 ) ;
1011
1061
}
1012
1062
1013
- // Update folder size
1063
+ // Folder exists, but needs its size updated.
1014
1064
if ( iHash == uPathHash )
1015
1065
{
1016
- foundFolder = true ;
1017
1066
Array . Copy ( BitConverter . GetBytes ( iFolderSize + 16 ) , 0 , modifiedIndex , position + 8 , 4 ) ;
1067
+ } else if ( foundFolder == false && iHash > uPathHash ) {
1068
+ foundFolder = true ;
1069
+ // This is where we need to cut the index the second time to make room for the folder data.
1070
+ remainder = modifiedIndex . Length - position - 16 ;
1071
+ Array . Copy ( modifiedIndex , position , modifiedIndex , position + 16 , remainder ) ;
1072
+
1073
+ // The new folder entry now goes at the 16 bytes starting at position
1074
+ Array . Copy ( BitConverter . GetBytes ( uPathHash ) , 0 , modifiedIndex , position , 4 ) ;
1075
+ Array . Copy ( BitConverter . GetBytes ( injectLocation ) , 0 , modifiedIndex , position + 4 , 4 ) ;
1076
+ Array . Copy ( BitConverter . GetBytes ( 16 ) , 0 , modifiedIndex , position + 8 , 4 ) ;
1077
+ Array . Copy ( BitConverter . GetBytes ( 16 ) , 0 , modifiedIndex , position + 12 , 4 ) ;
1018
1078
}
1019
1079
}
1020
1080
1021
- if ( ! foundFolder )
1022
- {
1023
- return false ;
1024
- }
1025
-
1026
1081
// Update SHA-1 Hashes.
1027
1082
SHA1 sha = new SHA1CryptoServiceProvider ( ) ;
1028
1083
byte [ ] shaHash ;
0 commit comments