11using D2SSharp . Data ;
2+ using D2SSharp . Enums ;
23using D2SSharp . IO ;
34
45namespace D2SSharp . Model ;
@@ -14,8 +15,11 @@ public partial class D2StashTab
1415 /// <summary>Tab magic value: 0xAA55AA55</summary>
1516 public const uint Magic = 0xAA55AA55 ;
1617
17- /// <summary>Expected header value for D2R stash tabs.</summary>
18- public const ulong Header = 0x6300000001L ;
18+ /// <summary>Stash format.</summary>
19+ public uint StashFormat { get ; set ; }
20+
21+ /// <summary>Item format in the stash.</summary>
22+ public uint ItemFormat { get ; set ; }
1923
2024 /// <summary>Gold amount stored in the stash tab.</summary>
2125 public uint Gold { get ; set ; }
@@ -34,9 +38,8 @@ public partial class D2StashTab
3438 /// </summary>
3539 /// <param name="reader">The bit reader.</param>
3640 /// <param name="externalData">External game data for parsing items.</param>
37- /// <param name="saveVersion">Save file version (97+ for D2R format).</param>
3841 /// <returns>The parsed stash tab.</returns>
39- public static D2StashTab Read ( ref BitReader reader , IExternalData externalData , uint saveVersion )
42+ public static D2StashTab Read ( ref BitReader reader , IExternalData externalData )
4043 {
4144 var tab = new D2StashTab ( ) ;
4245 int startPosition = reader . BytePosition ; // Record start
@@ -45,15 +48,13 @@ public static D2StashTab Read(ref BitReader reader, IExternalData externalData,
4548 if ( magic != Magic )
4649 throw new InvalidDataException ( $ "Invalid stash tab magic: 0x{ magic : X8} , expected 0x{ Magic : X8} ") ;
4750
48- ulong header = reader . ReadUInt64 ( ) ;
49- if ( header != Header )
50- throw new InvalidDataException ( $ "Invalid stash tab header: 0x{ header : X16} , expected 0x{ Header : X16} ") ;
51-
51+ tab . StashFormat = reader . ReadUInt32 ( ) ;
52+ tab . ItemFormat = reader . ReadUInt32 ( ) ;
5253 tab . Gold = reader . ReadUInt32 ( ) ;
5354 ushort size = reader . ReadUInt16 ( ) ; // Size field (not stored, used for validation)
5455 tab . Season = reader . ReadUInt16 ( ) ;
5556 reader . ReadBytes ( tab . Reserved ) ;
56- tab . Items = ItemsSection . Read ( ref reader , externalData , saveVersion ) ;
57+ tab . Items = ItemsSection . Read ( ref reader , externalData , tab . ItemFormat ) ;
5758
5859 // Seek to the correct position for the next tab
5960 reader . SetBytePosition ( startPosition + size ) ;
@@ -66,13 +67,22 @@ public static D2StashTab Read(ref BitReader reader, IExternalData externalData,
6667 /// </summary>
6768 /// <param name="writer">The bit writer.</param>
6869 /// <param name="externalData">External game data for writing items.</param>
69- /// <param name="saveVersion">Save file version (97+ for D2R format) .</param>
70- public void Write ( ref BitWriter writer , IExternalData externalData , uint saveVersion )
70+ /// <param name="targetVersion">Optional target version for format conversion. If null, uses current Version .</param>
71+ public void Write ( ref BitWriter writer , IExternalData externalData , uint ? targetVersion = null )
7172 {
73+ uint writeVersion = targetVersion ?? ItemFormat ;
74+
75+ // Prepare for version conversion if needed (mutates data for new format)
76+ PrepareForVersion ( ItemFormat , writeVersion ) ;
77+
78+ // Update Version permanently (data is now in the new format)
79+ ItemFormat = writeVersion ;
80+
7281 int startPosition = writer . BytesWritten ;
7382
7483 writer . WriteUInt32 ( Magic ) ;
75- writer . WriteUInt64 ( Header ) ;
84+ writer . WriteUInt32 ( StashFormat ) ;
85+ writer . WriteUInt32 ( ItemFormat ) ;
7686 writer . WriteUInt32 ( Gold ) ;
7787
7888 // Write placeholder for size, will update at end
@@ -81,7 +91,7 @@ public void Write(ref BitWriter writer, IExternalData externalData, uint saveVer
8191 writer . WriteUInt16 ( Season ) ;
8292 writer . WriteBytes ( Reserved ) ;
8393
84- Items . Write ( ref writer , externalData , saveVersion ) ;
94+ Items . Write ( ref writer , externalData , writeVersion ) ;
8595
8696 // Calculate this tab's size and write it back
8797 int tabSize = writer . BytesWritten - startPosition ;
@@ -90,4 +100,31 @@ public void Write(ref BitWriter writer, IExternalData externalData, uint saveVer
90100 writer . WriteUInt16 ( ( ushort ) tabSize ) ;
91101 writer . SetBitPosition ( currentPosition ) ;
92102 }
103+
104+ /// <summary>
105+ /// Prepares the save data for writing to a specific version format.
106+ /// Handles conversion between 1.14 (version 96) and D2R (version 97+) formats.
107+ /// </summary>
108+ private void PrepareForVersion ( uint sourceVersion , uint targetVersion )
109+ {
110+ bool sourceIsD2R = sourceVersion > 96 ;
111+ bool targetIsD2R = targetVersion > 96 ;
112+
113+ // Only convert if source and target formats differ
114+ if ( ! sourceIsD2R && targetIsD2R )
115+ Convert114ToD2R ( ) ;
116+ }
117+
118+ /// <summary>
119+ /// Converts save data from 1.14 format to D2R format.
120+ /// </summary>
121+ private void Convert114ToD2R ( )
122+ {
123+ // 3. Zero BodyLocation for stored items (D2R fails otherwise)
124+ foreach ( var item in Items )
125+ {
126+ if ( item . Position . Mode != ItemMode . Equipped )
127+ item . Position . BodyLocation = BodyLocation . None ;
128+ }
129+ }
93130}
0 commit comments