@@ -75,6 +75,74 @@ public virtual void Dispose()
75
75
IsDisposed = true ;
76
76
}
77
77
78
+ /// <summary>
79
+ /// Compiles the contents of the specified directory into a new archive.
80
+ /// </summary>
81
+ /// <param name="fromDir">
82
+ /// The directory to compile into an archive
83
+ /// </param>
84
+ /// <param name="toPath">
85
+ /// The destination path of the archive
86
+ /// </param>
87
+ public static void Compile ( string fromDir , string toPath )
88
+ {
89
+ const int HEADER_LENGTH = 4 ;
90
+ const int ENTRY_HEADER_LENGTH = 4 + CONSTANTS . DATA_ARCHIVE_ENTRY_NAME_LENGTH ;
91
+
92
+ var files = Directory . GetFiles ( fromDir ) ;
93
+ var dataStreams = new List < Stream > ( ) ;
94
+
95
+ using var dat = File . Create ( toPath . WithExtension ( ".dat" ) ) ;
96
+ using var writer = new BinaryWriter ( dat , Encoding . Default , true ) ;
97
+
98
+ writer . Write ( files . Length + 1 ) ;
99
+
100
+ //add the file header length
101
+ //plus the entry header length * number of entries
102
+ //plus 4 bytes for the final entry's end address (which could also be considered the total number of bytes)
103
+ var address = HEADER_LENGTH + files . Length * ENTRY_HEADER_LENGTH + 4 ;
104
+
105
+ foreach ( var file in files )
106
+ {
107
+ var nameStr = Path . GetFileName ( file ) ;
108
+
109
+ // ReSharper disable once ConvertIfStatementToSwitchStatement
110
+ if ( nameStr . Length > CONSTANTS . DATA_ARCHIVE_ENTRY_NAME_LENGTH )
111
+ throw new InvalidOperationException ( "Entry name is too long, must be 13 characters or less" ) ;
112
+
113
+ if ( nameStr . Length < CONSTANTS . DATA_ARCHIVE_ENTRY_NAME_LENGTH )
114
+ nameStr = nameStr . PadRight ( CONSTANTS . DATA_ARCHIVE_ENTRY_NAME_LENGTH , '\0 ' ) ;
115
+
116
+ //get bytes for the name field (binaryWriter.Write(string) doesn't work for this)
117
+ var nameStrBytes = Encoding . ASCII . GetBytes ( nameStr ) ;
118
+
119
+ writer . Write ( address ) ;
120
+ writer . Write ( nameStrBytes ) ;
121
+
122
+ var dataStream = File . Open (
123
+ file ,
124
+ new FileStreamOptions
125
+ {
126
+ Access = FileAccess . Read ,
127
+ Mode = FileMode . Open ,
128
+ Options = FileOptions . SequentialScan ,
129
+ Share = FileShare . ReadWrite ,
130
+ BufferSize = 8192
131
+ } ) ;
132
+ dataStreams . Add ( dataStream ) ;
133
+
134
+ address += ( int ) dataStream . Length ;
135
+ }
136
+
137
+ writer . Write ( address ) ;
138
+
139
+ foreach ( var stream in dataStreams )
140
+ {
141
+ stream . CopyTo ( dat ) ;
142
+ stream . Dispose ( ) ;
143
+ }
144
+ }
145
+
78
146
/// <summary>
79
147
/// Extracts the contents of the current archive to the specified directory.
80
148
/// </summary>
@@ -171,8 +239,12 @@ public virtual void Patch(string entryName, ISavable item)
171
239
{
172
240
ThrowIfDisposed ( ) ;
173
241
174
- //if an entry exists with the same name, remove it
175
- Remove ( entryName ) ;
242
+ //if an entry exists with the same name
243
+ //grab its index, so we can replace it and preserve order
244
+ var index = - 1 ;
245
+
246
+ if ( TryGetValue ( entryName , out var existingEntry ) )
247
+ index = IndexOf ( existingEntry ) ;
176
248
177
249
BaseStream . Seek ( 0 , SeekOrigin . End ) ;
178
250
var address = ( int ) BaseStream . Length ;
@@ -188,8 +260,11 @@ public virtual void Patch(string entryName, ISavable item)
188
260
address ,
189
261
length ) ;
190
262
191
- //add entry to archive
192
- Add ( entry ) ;
263
+ //if index is not -1, replace the existing entry
264
+ if ( index != - 1 )
265
+ this [ index ] = entry ;
266
+ else //otherwise add new entry
267
+ Add ( entry ) ;
193
268
}
194
269
195
270
internal void ThrowIfDisposed ( ) => ObjectDisposedException . ThrowIf ( IsDisposed , this ) ;
@@ -253,10 +328,9 @@ public virtual void Save(Stream stream)
253
328
//plus 4 bytes for the final entry's end address (which could also be considered the total number of bytes)
254
329
var address = HEADER_LENGTH + Count * ENTRY_HEADER_LENGTH + 4 ;
255
330
256
- var orderedEntries = this . OrderBy ( entry => entry . EntryName )
257
- . ToList ( ) ;
331
+ var entries = this . ToList ( ) ;
258
332
259
- foreach ( var entry in orderedEntries )
333
+ foreach ( var entry in entries )
260
334
{
261
335
//reconstruct the name field with the required terminator
262
336
var nameStr = entry . EntryName ;
@@ -279,7 +353,7 @@ public virtual void Save(Stream stream)
279
353
280
354
writer . Write ( address ) ;
281
355
282
- foreach ( var entry in orderedEntries )
356
+ foreach ( var entry in entries )
283
357
{
284
358
using var segment = entry . ToStreamSegment ( ) ;
285
359
segment . CopyTo ( stream ) ;
@@ -300,15 +374,21 @@ public virtual void Save(Stream stream)
300
374
/// </returns>
301
375
public static DataArchive FromDirectory ( string dir )
302
376
{
377
+ //create a buffer with a count of 0 entries
303
378
var buffer = new MemoryStream ( ) ;
379
+ buffer . Write ( new byte [ 4 ] ) ;
380
+ buffer . Seek ( 0 , SeekOrigin . Begin ) ;
304
381
305
382
//create a new in-memory archive
383
+ //this will read the stream, finding 0 entries
306
384
var archive = new DataArchive ( buffer ) ;
385
+ buffer . Seek ( 0 , SeekOrigin . End ) ; //just incase
307
386
308
- var address = 0 ;
387
+ //start the address at 4, since the first 4 bytes are the entry count
388
+ var address = 4 ;
309
389
310
390
//enumerate the directory, copying each file into the memory archive
311
- //maintain accurate address offsets for each file so that the archive is useable
391
+ //maintain accurate address offsets for each file so that the archive is usable
312
392
foreach ( var file in Directory . EnumerateFiles ( dir ) )
313
393
{
314
394
using var stream = File . Open (
@@ -338,6 +418,8 @@ public static DataArchive FromDirectory(string dir)
338
418
archive . Add ( entry ) ;
339
419
}
340
420
421
+ buffer . Seek ( 0 , SeekOrigin . Begin ) ;
422
+
341
423
return archive ;
342
424
}
343
425
0 commit comments