@@ -44,6 +44,81 @@ public void AppendNewRawFile(string zoneFilePath, string filePath, int expectedS
4444 currentZone . ReadHeaderFields ( ) ;
4545 }
4646
47+ /// <inheritdoc/>
48+ public void InjectPlainFile ( string zoneFilePath , string filePath , string gamePath )
49+ {
50+ // Read the plain file content
51+ byte [ ] fileContent = File . ReadAllBytes ( filePath ) ;
52+ int contentSize = fileContent . Length ;
53+
54+ // Build the raw file entry with header:
55+ // - 4 bytes: first marker (0xFFFFFFFF)
56+ // - 4 bytes: data size (big-endian)
57+ // - 4 bytes: second marker (0xFFFFFFFF)
58+ // - N bytes: filename + null terminator
59+ // - M bytes: file content
60+ byte [ ] fileNameBytes = Encoding . ASCII . GetBytes ( gamePath ) ;
61+ int headerSize = 12 + fileNameBytes . Length + 1 ; // 12 bytes markers/size + filename + null
62+ int totalSize = headerSize + contentSize ;
63+
64+ byte [ ] newEntry = new byte [ totalSize ] ;
65+
66+ // Write first marker (0xFFFFFFFF)
67+ newEntry [ 0 ] = 0xFF ;
68+ newEntry [ 1 ] = 0xFF ;
69+ newEntry [ 2 ] = 0xFF ;
70+ newEntry [ 3 ] = 0xFF ;
71+
72+ // Write data size (big-endian)
73+ System . Buffers . Binary . BinaryPrimitives . WriteUInt32BigEndian (
74+ newEntry . AsSpan ( 4 , 4 ) ,
75+ ( uint ) contentSize
76+ ) ;
77+
78+ // Write second marker (0xFFFFFFFF)
79+ newEntry [ 8 ] = 0xFF ;
80+ newEntry [ 9 ] = 0xFF ;
81+ newEntry [ 10 ] = 0xFF ;
82+ newEntry [ 11 ] = 0xFF ;
83+
84+ // Write filename
85+ Array . Copy ( fileNameBytes , 0 , newEntry , 12 , fileNameBytes . Length ) ;
86+ // Null terminator is already 0x00 from array initialization
87+
88+ // Write content
89+ Array . Copy ( fileContent , 0 , newEntry , headerSize , contentSize ) ;
90+
91+ // Now inject the entry into the zone file
92+ ZoneFile currentZone = RawFileNode . CurrentZone ;
93+ int insertPosition = currentZone . AssetPoolEndOffset ;
94+
95+ currentZone . ModifyZoneFile ( fs =>
96+ {
97+ long originalLength = fs . Length ;
98+ // Read tail data from the insertion point.
99+ fs . Seek ( insertPosition , SeekOrigin . Begin ) ;
100+ byte [ ] tailBuffer = new byte [ originalLength - insertPosition ] ;
101+ fs . Read ( tailBuffer , 0 , tailBuffer . Length ) ;
102+ // Extend the file length.
103+ fs . SetLength ( originalLength + newEntry . Length ) ;
104+ // Shift tail data forward.
105+ fs . Seek ( insertPosition + newEntry . Length , SeekOrigin . Begin ) ;
106+ fs . Write ( tailBuffer , 0 , tailBuffer . Length ) ;
107+ // Write the new entry.
108+ fs . Seek ( insertPosition , SeekOrigin . Begin ) ;
109+ fs . Write ( newEntry , 0 , newEntry . Length ) ;
110+ } ) ;
111+
112+ // Update the zone file size header.
113+ uint currentZoneSize = ZoneFileIO . ReadZoneFileSize ( zoneFilePath ) ;
114+ uint newZoneSize = currentZoneSize + ( uint ) newEntry . Length ;
115+ ZoneFileIO . WriteZoneFileSize ( zoneFilePath , newZoneSize ) ;
116+
117+ // Refresh zone data and header.
118+ currentZone . LoadData ( ) ;
119+ currentZone . ReadHeaderFields ( ) ;
120+ }
121+
47122 /// <inheritdoc/>
48123 public void AdjustRawFileNodeSize ( string zoneFilePath , RawFileNode rawFileNode , int newSize )
49124 {
@@ -105,10 +180,14 @@ public void AdjustRawFileNodeSize(string zoneFilePath, RawFileNode rawFileNode,
105180 uint currentZoneSize = ZoneFileIO . ReadZoneFileSize ( zoneFilePath ) ;
106181 uint updatedZoneSize = currentZoneSize + ( uint ) sizeIncrease ;
107182 ZoneFileIO . WriteZoneFileSize ( zoneFilePath , updatedZoneSize ) ;
183+
184+ // Refresh zone header fields after modification.
185+ currentZone . LoadData ( ) ;
186+ currentZone . ReadHeaderFields ( ) ;
108187 }
109188
110189 /// <summary>
111- /// Adjusts a raw file entry read from disk so that its header’ s size field (at offset 4)
190+ /// Adjusts a raw file entry read from disk so that its header' s size field (at offset 4)
112191 /// matches the expected data size. It uses the known header structure:
113192 /// Bytes 0-3: first marker (0xFFFFFFFF)
114193 /// Bytes 4-7: data size (to be updated)
@@ -179,7 +258,7 @@ public void ExportRawFile(RawFileNode exportedRawFile, string fileExtension)
179258 {
180259 using var save = new SaveFileDialog
181260 {
182- Title = "Export File" ,
261+ Title = "Export File (With Header for Re-injection) " ,
183262 FileName = SanitizeFileName ( exportedRawFile . FileName ) ,
184263 Filter = $ "{ fileExtension . TrimStart ( '.' ) . ToUpper ( ) } Files (*{ fileExtension } )|*{ fileExtension } |All Files (*.*)|*.*"
185264 } ;
@@ -197,7 +276,47 @@ public void ExportRawFile(RawFileNode exportedRawFile, string fileExtension)
197276 File . WriteAllBytes ( save . FileName , slice ) ;
198277
199278 MessageBox . Show (
200- $ "File successfully exported to:\n \n { save . FileName } ",
279+ $ "File successfully exported to:\n \n { save . FileName } \n \n " +
280+ "Note: This file includes the zone header and can be re-injected." ,
281+ "Export Complete" ,
282+ MessageBoxButtons . OK ,
283+ MessageBoxIcon . Information
284+ ) ;
285+ }
286+ catch ( Exception ex )
287+ {
288+ MessageBox . Show (
289+ $ "Failed to export file: { ex . Message } ",
290+ "Export Error" ,
291+ MessageBoxButtons . OK ,
292+ MessageBoxIcon . Error
293+ ) ;
294+ }
295+ }
296+
297+ /// <inheritdoc/>
298+ public void ExportRawFileContentOnly ( RawFileNode exportedRawFile , string fileExtension )
299+ {
300+ using var save = new SaveFileDialog
301+ {
302+ Title = "Export Content Only" ,
303+ FileName = SanitizeFileName ( exportedRawFile . FileName ) ,
304+ Filter = $ "{ fileExtension . TrimStart ( '.' ) . ToUpper ( ) } Files (*{ fileExtension } )|*{ fileExtension } |All Files (*.*)|*.*"
305+ } ;
306+
307+ if ( save . ShowDialog ( ) != DialogResult . OK )
308+ return ;
309+
310+ try
311+ {
312+ // Export only the actual content (RawFileBytes), not the header
313+ byte [ ] contentOnly = exportedRawFile . RawFileBytes ;
314+
315+ File . WriteAllBytes ( save . FileName , contentOnly ) ;
316+
317+ MessageBox . Show (
318+ $ "File content successfully exported to:\n \n { save . FileName } \n \n " +
319+ "Note: This file contains only the script content without zone header." ,
201320 "Export Complete" ,
202321 MessageBoxButtons . OK ,
203322 MessageBoxIcon . Information
@@ -242,6 +361,12 @@ public void IncreaseSize(string zoneFilePath, RawFileNode rawFileNode, byte[] ne
242361 }
243362 fs . Seek ( rawFileNode . CodeStartPosition , SeekOrigin . Begin ) ;
244363 fs . Write ( newContent , 0 , newSize ) ;
364+
365+ // Update the size field in the raw file header (4 bytes at StartOfFileHeader + 4)
366+ Span < byte > sizeBuf = stackalloc byte [ 4 ] ;
367+ System . Buffers . Binary . BinaryPrimitives . WriteUInt32BigEndian ( sizeBuf , ( uint ) newSize ) ;
368+ fs . Seek ( rawFileNode . StartOfFileHeader + 4 , SeekOrigin . Begin ) ;
369+ fs . Write ( sizeBuf ) ;
245370 } ) ;
246371
247372 rawFileNode . MaxSize = newSize ;
@@ -251,6 +376,10 @@ public void IncreaseSize(string zoneFilePath, RawFileNode rawFileNode, byte[] ne
251376 uint currentZoneSize = ZoneFileIO . ReadZoneFileSize ( zoneFilePath ) ;
252377 uint newZoneSize = currentZoneSize + ( uint ) sizeIncrease ;
253378 ZoneFileIO . WriteZoneFileSize ( zoneFilePath , newZoneSize ) ;
379+
380+ // Refresh zone header fields after modification
381+ currentZone . LoadData ( ) ;
382+ currentZone . ReadHeaderFields ( ) ;
254383 }
255384
256385 /// <inheritdoc/>
@@ -331,6 +460,18 @@ public void RenameRawFile(TreeView filesTreeView, string ffFilePath, string zone
331460 // Write the modified zone file back to disk.
332461 File . WriteAllBytes ( zoneFilePath , zoneFileData ) ;
333462
463+ // Update the zone file size header if the filename length changed.
464+ if ( byteDifference != 0 )
465+ {
466+ uint currentZoneSize = ZoneFileIO . ReadZoneFileSize ( zoneFilePath ) ;
467+ uint newZoneSize = ( uint ) ( ( int ) currentZoneSize + byteDifference ) ;
468+ ZoneFileIO . WriteZoneFileSize ( zoneFilePath , newZoneSize ) ;
469+
470+ // Refresh zone data and header fields.
471+ RawFileNode . CurrentZone . LoadData ( ) ;
472+ RawFileNode . CurrentZone . ReadHeaderFields ( ) ;
473+ }
474+
334475 // Save the old file name for notification.
335476 string oldFileName = rawFileNode . FileName ;
336477 // Update the renamed file's FileName property.
0 commit comments