@@ -16,6 +16,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
16
17
17
using System ;
18
18
using System . Collections ;
19
+ using System . Collections . Generic ;
19
20
using System . IO ;
20
21
using System . Linq ;
21
22
using System . Runtime . CompilerServices ;
@@ -33,7 +34,7 @@ public class WzBinaryWriter : BinaryWriter
33
34
#region Properties
34
35
public WzMutableKey WzKey { get ; set ; }
35
36
public uint Hash { get ; set ; }
36
- public Hashtable StringCache { get ; set ; }
37
+ public Dictionary < string , int > StringCache { get ; set ; }
37
38
public WzHeader Header { get ; set ; }
38
39
public bool LeaveOpen { get ; internal set ; }
39
40
#endregion
@@ -54,8 +55,8 @@ public WzBinaryWriter(Stream output, byte[] WzIv, bool leaveOpen)
54
55
: base ( output )
55
56
{
56
57
WzKey = WzKeyGenerator . GenerateWzKey ( WzIv ) ;
57
- StringCache = new Hashtable ( ) ;
58
- this . LeaveOpen = leaveOpen ;
58
+ StringCache = new Dictionary < string , int > ( ) ;
59
+ this . LeaveOpen = leaveOpen ;
59
60
}
60
61
#endregion
61
62
@@ -112,146 +113,142 @@ public bool WriteWzObjectValue(string stringObjectValue, WzDirectoryType type)
112
113
int sOffset = ( int ) ( this . BaseStream . Position - Header . FStart ) ;
113
114
Write ( ( byte ) type ) ;
114
115
Write ( stringObjectValue ) ;
115
- if ( ! StringCache . ContainsKey ( storeName ) )
116
- {
117
- StringCache [ storeName ] = sOffset ;
118
- }
119
- }
116
+ StringCache [ storeName ] = sOffset ;
117
+ }
120
118
return false ;
121
119
}
122
120
123
121
public override void Write ( string value )
124
122
{
125
- if ( value . Length == 0 )
123
+ if ( string . IsNullOrEmpty ( value ) )
126
124
{
127
125
Write ( ( byte ) 0 ) ;
126
+ return ;
128
127
}
129
- else
130
- {
131
- bool unicode = value . Any ( c => c > sbyte . MaxValue ) ;
128
+ bool unicode = value . Any ( c => c > sbyte . MaxValue ) ;
129
+
130
+ if ( unicode )
131
+ WriteUnicodeString ( value ) ;
132
+ else // ASCII
133
+ WriteAsciiString ( value ) ;
134
+ }
132
135
133
- if ( unicode )
134
- {
135
- ushort mask = 0xAAAA ;
136
+ /// <summary>
137
+ /// Encodes unicode string
138
+ /// </summary>
139
+ /// <param name="value"></param>
140
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
141
+ private void WriteUnicodeString ( string value )
142
+ {
143
+ ushort mask = 0xAAAA ;
136
144
137
- if ( value . Length >= sbyte . MaxValue ) // Bugfix - >= because if value.Length = MaxValue, MaxValue will be written and then treated as a long-length marker
138
- {
139
- Write ( sbyte . MaxValue ) ;
140
- Write ( value . Length ) ;
141
- }
142
- else
143
- {
144
- Write ( ( sbyte ) value . Length ) ;
145
- }
145
+ WriteStringLength ( value . Length , false ) ;
146
146
147
- int i = 0 ;
148
- foreach ( var character in value )
149
- {
150
- ushort encryptedChar = ( ushort ) character ;
151
- encryptedChar ^= ( ushort ) ( ( WzKey [ i * 2 + 1 ] << 8 ) + WzKey [ i * 2 ] ) ;
152
- encryptedChar ^= mask ;
153
- mask ++ ;
154
- Write ( encryptedChar ) ;
147
+ for ( int i = 0 ; i < value . Length ; i ++ )
148
+ {
149
+ ushort encryptedChar = ( ushort ) value [ i ] ;
150
+ encryptedChar ^= ( ushort ) ( ( WzKey [ i * 2 + 1 ] << 8 ) + WzKey [ i * 2 ] ) ;
151
+ encryptedChar ^= mask ;
152
+ mask ++ ;
153
+ Write ( encryptedChar ) ;
154
+ }
155
+ }
155
156
156
- i ++ ;
157
- }
158
- }
159
- else // ASCII
160
- {
161
- byte mask = 0xAA ;
157
+ /// <summary>
158
+ /// Encodes Ascii string
159
+ /// </summary>
160
+ /// <param name="value"></param>
161
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
162
+ private void WriteAsciiString ( string value )
163
+ {
164
+ byte mask = 0xAA ;
162
165
163
- if ( value . Length > sbyte . MaxValue ) // Note - no need for >= here because of 2's complement (MinValue == -(MaxValue + 1))
164
- {
165
- Write ( sbyte . MinValue ) ;
166
- Write ( value . Length ) ;
167
- }
168
- else
169
- {
170
- Write ( ( sbyte ) ( - value . Length ) ) ;
171
- }
166
+ WriteStringLength ( value . Length , true ) ;
172
167
173
- int i = 0 ;
174
- foreach ( char c in value )
175
- {
176
- byte encryptedChar = ( byte ) c ;
177
- encryptedChar ^= WzKey [ i ] ;
178
- encryptedChar ^= mask ;
179
- mask ++ ;
180
- Write ( encryptedChar ) ;
168
+ for ( int i = 0 ; i < value . Length ; i ++ )
169
+ {
170
+ byte encryptedChar = ( byte ) value [ i ] ;
171
+ encryptedChar ^= WzKey [ i ] ;
172
+ encryptedChar ^= mask ;
173
+ mask ++ ;
174
+ Write ( encryptedChar ) ;
175
+ }
176
+ }
181
177
182
- i ++ ;
183
- }
184
- }
185
- }
186
- }
178
+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
179
+ private void WriteStringLength ( int length , bool isAscii )
180
+ {
181
+ // Bugfix - >= because if value.Length = MaxValue, MaxValue will be written and then treated as a long-length marker
182
+ // Note - no need for >= here because of 2's complement (MinValue == -(MaxValue + 1))
183
+ if ( length > sbyte . MaxValue )
184
+ {
185
+ Write ( sbyte . MinValue ) ;
186
+ Write ( length ) ;
187
+ }
188
+ else
189
+ {
190
+ if ( isAscii )
191
+ Write ( ( sbyte ) ( - length ) ) ;
192
+ else
193
+ Write ( ( sbyte ) length ) ;
194
+ }
195
+ }
187
196
188
- public void Write ( string value , int length )
189
- {
190
- for ( int i = 0 ; i < length ; i ++ )
191
- {
192
- if ( i < value . Length )
193
- {
194
- Write ( value [ i ] ) ;
195
- }
196
- else
197
- {
198
- Write ( ( byte ) 0 ) ;
199
- }
200
- }
201
- }
197
+ public void Write ( string value , int length )
198
+ {
199
+ int writeLength = Math . Min ( value . Length , length ) ;
200
+ Write ( value . AsSpan ( 0 , writeLength ) ) ;
201
+ for ( int i = writeLength ; i < length ; i ++ )
202
+ {
203
+ Write ( ( byte ) 0 ) ;
204
+ }
205
+ }
202
206
203
- public char [ ] EncryptString ( string stringToDecrypt )
207
+ public char [ ] EncryptString ( string stringToEncrypt )
204
208
{
205
- char [ ] outputChars = new char [ stringToDecrypt . Length ] ;
206
- for ( int i = 0 ; i < stringToDecrypt . Length ; i ++ )
207
- outputChars [ i ] = ( char ) ( stringToDecrypt [ i ] ^ ( ( char ) ( ( WzKey [ i * 2 + 1 ] << 8 ) + WzKey [ i * 2 ] ) ) ) ;
208
- return outputChars ;
209
+ return stringToEncrypt . Select ( ( c , i ) => ( char ) ( c ^ ( ( WzKey [ i * 2 + 1 ] << 8 ) + WzKey [ i * 2 ] ) ) ) . ToArray ( ) ;
209
210
}
210
211
211
- public char [ ] EncryptNonUnicodeString ( string stringToDecrypt )
212
+ public char [ ] EncryptNonUnicodeString ( string stringToEncrypt )
212
213
{
213
- char [ ] outputChars = new char [ stringToDecrypt . Length ] ;
214
- for ( int i = 0 ; i < stringToDecrypt . Length ; i ++ )
215
- outputChars [ i ] = ( char ) ( stringToDecrypt [ i ] ^ WzKey [ i ] ) ;
216
- return outputChars ;
214
+ return stringToEncrypt . Select ( ( c , i ) => ( char ) ( c ^ WzKey [ i ] ) ) . ToArray ( ) ;
217
215
}
218
216
219
- public void WriteNullTerminatedString ( string value )
220
- {
221
- for ( int i = 0 ; i < value . Length ; i ++ )
222
- {
223
- Write ( ( byte ) value [ i ] ) ;
224
- }
225
- Write ( ( byte ) 0 ) ;
226
- }
217
+ public void WriteNullTerminatedString ( string value )
218
+ {
219
+ Write ( value . AsSpan ( ) ) ;
220
+ Write ( ( byte ) 0 ) ;
221
+ }
227
222
228
- public void WriteCompressedInt ( int value )
229
- {
230
- if ( value > sbyte . MaxValue || value <= sbyte . MinValue )
231
- {
232
- Write ( sbyte . MinValue ) ;
233
- Write ( value ) ;
234
- }
235
- else
236
- {
237
- Write ( ( sbyte ) value ) ;
238
- }
239
- }
223
+ public void WriteCompressedInt ( int value ) => WriteCompressed ( value ) ;
224
+
225
+ public void WriteCompressedLong ( long value ) => WriteCompressed ( value ) ;
240
226
241
- public void WriteCompressedLong ( long value )
227
+ private void WriteCompressed < T > ( T value ) where T : IConvertible
242
228
{
243
- if ( value > sbyte . MaxValue || value <= sbyte . MinValue )
229
+ long longValue = value . ToInt64 ( null ) ;
230
+ if ( longValue > sbyte . MaxValue || longValue <= sbyte . MinValue )
244
231
{
245
232
Write ( sbyte . MinValue ) ;
246
- Write ( value ) ;
233
+ switch ( Type . GetTypeCode ( typeof ( T ) ) )
234
+ {
235
+ case TypeCode . Int32 :
236
+ Write ( value . ToInt32 ( null ) ) ;
237
+ break ;
238
+ case TypeCode . Int64 :
239
+ Write ( value . ToInt64 ( null ) ) ;
240
+ break ;
241
+ default :
242
+ throw new ArgumentException ( $ "Unsupported type for compressed writing: { typeof ( T ) } ") ;
243
+ }
247
244
}
248
245
else
249
246
{
250
- Write ( ( sbyte ) value ) ;
247
+ Write ( value . ToSByte ( null ) ) ;
251
248
}
252
249
}
253
250
254
- public void WriteOffset ( long value )
251
+ public void WriteOffset ( long value )
255
252
{
256
253
uint encOffset = ( uint ) BaseStream . Position ;
257
254
encOffset = ( encOffset - Header . FStart ) ^ 0xFFFFFFFF ;
0 commit comments