@@ -125,6 +125,19 @@ public static D2Save Read(ReadOnlySpan<byte> data, IExternalData externalData)
125125 return save ;
126126 }
127127
128+ /// <summary>
129+ /// Writes a save file to a new byte array.
130+ /// </summary>
131+ /// <param name="externalData">External game data for writing stats/items. Uses built-in data if null.</param>
132+ /// <param name="targetVersion">Optional target version for format conversion.</param>
133+ /// <returns>The serialized save file bytes.</returns>
134+ public byte [ ] ToBytes ( IExternalData ? externalData = null , uint ? targetVersion = null )
135+ {
136+ byte [ ] buffer = new byte [ EstimateSize ( ) ] ;
137+ int bytesWritten = Write ( buffer , externalData ?? TxtFileExternalData . Default , targetVersion ) ;
138+ return buffer . AsSpan ( 0 , bytesWritten ) . ToArray ( ) ;
139+ }
140+
128141 /// <summary>
129142 /// Writes a save file to a byte span using the default external data.
130143 /// </summary>
@@ -215,29 +228,26 @@ public int EstimateSize()
215228 // Skills: 2 + numSkills
216229 size += 2 + Character . NumSkills ;
217230
218- // Items: very variable, estimate based on item count including socketed items
219- // Average item is ~50-200 bytes
220- size += 4 + CountItemsRecursive ( Items ) * 200 ;
231+ // Items
232+ size += Items . EstimateSize ( ) ;
221233
222234 // Corpse: header + corpses with items
223235 size += 4 ;
224236 foreach ( var corpse in Corpses )
225237 {
226- size += 12 + CountItemsRecursive ( corpse . Items ) * 200 ;
238+ size += 12 + corpse . Items . EstimateSize ( ) ;
227239 }
228240
229241 // Expansion sections
230242 if ( IsExpansion ( Version ) )
231243 {
232- // Merc items
233- size += 4 + CountItemsRecursive ( MercItems ? . Items ) * 200 ;
244+ // Merc items: magic(2) + items section
245+ size += 2 + ( MercItems ? . Items ?? new ItemsSection ( ) ) . EstimateSize ( ) ;
234246
235247 // Iron golem
236248 size += 3 ;
237249 if ( IronGolem ? . GolemItem != null )
238- {
239- size += CountItemsRecursive ( [ IronGolem . GolemItem ] ) * 200 ;
240- }
250+ size += 200 ; // Single item estimate
241251
242252 // Demon section
243253 if ( Character . Preview . GameVersion == GameVersion . ReignOfTheWarlock && Demon != null )
@@ -258,23 +268,6 @@ public int EstimateSize()
258268 return ( int ) ( size * 1.2 ) ;
259269 }
260270
261- /// <summary>
262- /// Counts items recursively, including socketed items.
263- /// </summary>
264- private static int CountItemsRecursive ( IEnumerable < Item ? > ? items )
265- {
266- if ( items == null ) return 0 ;
267-
268- int count = 0 ;
269- foreach ( var item in items )
270- {
271- if ( item == null ) continue ;
272- count ++ ; // Count this item
273- count += CountItemsRecursive ( item . Sockets ) ; // Count socketed items
274- }
275- return count ;
276- }
277-
278271 /// <summary>
279272 /// Verifies the checksum of save file data.
280273 /// </summary>
0 commit comments