HMX Forge (RB4/DCS/Amp16/FME) save game decryptor/encryptor
1using System.Text;
2
3static class StreamExtensions
4{
5 /// <summary>
6 /// Read a signed 8-bit integer from the stream.
7 /// </summary>
8 /// <param name="s"></param>
9 /// <returns></returns>
10 public static sbyte ReadInt8(this Stream s) => unchecked((sbyte)s.ReadUInt8());
11
12 /// <summary>
13 /// Read an unsigned 8-bit integer from the stream.
14 /// </summary>
15 /// <param name="s"></param>
16 /// <returns></returns>
17 public static byte ReadUInt8(this Stream s)
18 {
19 byte ret;
20 byte[] tmp = new byte[1];
21 s.Read(tmp, 0, 1);
22 ret = tmp[0];
23 return ret;
24 }
25
26 /// <summary>
27 /// Read an unsigned 16-bit little-endian integer from the stream.
28 /// </summary>
29 /// <param name="s"></param>
30 /// <returns></returns>
31 public static ushort ReadUInt16LE(this Stream s) => unchecked((ushort)s.ReadInt16LE());
32
33 /// <summary>
34 /// Read a signed 16-bit little-endian integer from the stream.
35 /// </summary>
36 /// <param name="s"></param>
37 /// <returns></returns>
38 public static short ReadInt16LE(this Stream s)
39 {
40 int ret;
41 byte[] tmp = new byte[2];
42 s.Read(tmp, 0, 2);
43 ret = tmp[0] & 0x00FF;
44 ret |= (tmp[1] << 8) & 0xFF00;
45 return (short)ret;
46 }
47
48 public static void WriteInt16LE(this Stream s, short i)
49 {
50 s.WriteUInt16LE(unchecked((ushort)i));
51 }
52
53 public static void WriteUInt16LE(this Stream s, ushort i)
54 {
55 byte[] tmp = new byte[2];
56 tmp[0] = (byte)(i & 0xFF);
57 tmp[1] = (byte)((i >> 8) & 0xFF);
58 s.Write(tmp, 0, 2);
59 }
60
61 public static void WriteUInt16BE(this Stream s, ushort i)
62 {
63 byte[] tmp = new byte[2];
64 tmp[0] = (byte)((i >> 8) & 0xFF);
65 tmp[1] = (byte)(i & 0xFF);
66 s.Write(tmp, 0, 2);
67 }
68
69 /// <summary>
70 /// Read an unsigned 16-bit Big-endian integer from the stream.
71 /// </summary>
72 /// <param name="s"></param>
73 /// <returns></returns>
74 public static ushort ReadUInt16BE(this Stream s) => unchecked((ushort)s.ReadInt16BE());
75
76 /// <summary>
77 /// Read a signed 16-bit Big-endian integer from the stream.
78 /// </summary>
79 /// <param name="s"></param>
80 /// <returns></returns>
81 public static short ReadInt16BE(this Stream s)
82 {
83 int ret;
84 byte[] tmp = new byte[2];
85 s.Read(tmp, 0, 2);
86 ret = (tmp[0] << 8) & 0xFF00;
87 ret |= tmp[1] & 0x00FF;
88 return (short)ret;
89 }
90
91 public static void WriteUInt24LE(this Stream s, uint i)
92 {
93 byte[] tmp = new byte[3];
94 tmp[0] = (byte)(i & 0xFF);
95 tmp[1] = (byte)((i >> 8) & 0xFF);
96 tmp[2] = (byte)((i >> 16) & 0xFF);
97 s.Write(tmp, 0, 3);
98 }
99 public static void WriteUInt24BE(this Stream s, uint i)
100 {
101 byte[] tmp = new byte[3];
102 tmp[2] = (byte)(i & 0xFF);
103 tmp[1] = (byte)((i >> 8) & 0xFF);
104 tmp[0] = (byte)((i >> 16) & 0xFF);
105 s.Write(tmp, 0, 3);
106 }
107
108 /// <summary>
109 /// Read an unsigned 24-bit little-endian integer from the stream.
110 /// </summary>
111 /// <param name="s"></param>
112 /// <returns></returns>
113 public static uint ReadUInt24LE(this Stream s)
114 {
115 int ret;
116 byte[] tmp = new byte[3];
117 s.Read(tmp, 0, 3);
118 ret = tmp[0] & 0x0000FF;
119 ret |= (tmp[1] << 8) & 0x00FF00;
120 ret |= (tmp[2] << 16) & 0xFF0000;
121 return unchecked((uint)ret);
122 }
123
124 /// <summary>
125 /// Read a signed 24-bit little-endian integer from the stream.
126 /// </summary>
127 /// <param name="s"></param>
128 /// <returns></returns>
129 public static int ReadInt24LE(this Stream s)
130 {
131 int ret;
132 byte[] tmp = new byte[3];
133 s.Read(tmp, 0, 3);
134 ret = tmp[0] & 0x0000FF;
135 ret |= (tmp[1] << 8) & 0x00FF00;
136 ret |= (tmp[2] << 16) & 0xFF0000;
137 if ((tmp[2] & 0x80) == 0x80)
138 {
139 ret |= 0xFF << 24;
140 }
141 return ret;
142 }
143
144 /// <summary>
145 /// Read an unsigned 24-bit Big-endian integer from the stream.
146 /// </summary>
147 /// <param name="s"></param>
148 /// <returns></returns>
149 public static uint ReadUInt24BE(this Stream s)
150 {
151 int ret;
152 byte[] tmp = new byte[3];
153 s.Read(tmp, 0, 3);
154 ret = tmp[2] & 0x0000FF;
155 ret |= (tmp[1] << 8) & 0x00FF00;
156 ret |= (tmp[0] << 16) & 0xFF0000;
157 return (uint)ret;
158 }
159
160 /// <summary>
161 /// Read a signed 24-bit Big-endian integer from the stream.
162 /// </summary>
163 /// <param name="s"></param>
164 /// <returns></returns>
165 public static int ReadInt24BE(this Stream s)
166 {
167 int ret;
168 byte[] tmp = new byte[3];
169 s.Read(tmp, 0, 3);
170 ret = tmp[2] & 0x0000FF;
171 ret |= (tmp[1] << 8) & 0x00FF00;
172 ret |= (tmp[0] << 16) & 0xFF0000;
173 if ((tmp[0] & 0x80) == 0x80)
174 {
175 ret |= 0xFF << 24; // sign-extend
176 }
177 return ret;
178 }
179
180 /// <summary>
181 /// Read an unsigned 32-bit little-endian integer from the stream.
182 /// </summary>
183 /// <param name="s"></param>
184 /// <returns></returns>
185 public static uint ReadUInt32LE(this Stream s) => unchecked((uint)s.ReadInt32LE());
186
187 /// <summary>
188 /// Read a signed 32-bit little-endian integer from the stream.
189 /// </summary>
190 /// <param name="s"></param>
191 /// <returns></returns>
192 public static int ReadInt32LE(this Stream s)
193 {
194 int ret;
195 byte[] tmp = new byte[4];
196 s.Read(tmp, 0, 4);
197 ret = tmp[0] & 0x000000FF;
198 ret |= (tmp[1] << 8) & 0x0000FF00;
199 ret |= (tmp[2] << 16) & 0x00FF0000;
200 ret |= (tmp[3] << 24);
201 return ret;
202 }
203
204 public static void WriteInt32LE(this Stream s, int i)
205 {
206 s.WriteUInt32LE(unchecked((uint)i));
207 }
208
209 public static void WriteUInt32LE(this Stream s, uint i)
210 {
211 byte[] tmp = new byte[4];
212 tmp[0] = (byte)(i & 0xFF);
213 tmp[1] = (byte)((i >> 8) & 0xFF);
214 tmp[2] = (byte)((i >> 16) & 0xFF);
215 tmp[3] = (byte)((i >> 24) & 0xFF);
216 s.Write(tmp, 0, 4);
217 }
218
219 public static void WriteUInt32BE(this Stream s, uint i)
220 {
221 byte[] tmp = new byte[4];
222 tmp[3] = (byte)(i & 0xFF);
223 tmp[2] = (byte)((i >> 8) & 0xFF);
224 tmp[1] = (byte)((i >> 16) & 0xFF);
225 tmp[0] = (byte)((i >> 24) & 0xFF);
226 s.Write(tmp, 0, 4);
227 }
228
229 /// <summary>
230 /// Read an unsigned 32-bit Big-endian integer from the stream.
231 /// </summary>
232 /// <param name="s"></param>
233 /// <returns></returns>
234 public static uint ReadUInt32BE(this Stream s) => unchecked((uint)s.ReadInt32BE());
235
236 /// <summary>
237 /// Read a signed 32-bit Big-endian integer from the stream.
238 /// </summary>
239 /// <param name="s"></param>
240 /// <returns></returns>
241 public static int ReadInt32BE(this Stream s)
242 {
243 int ret;
244 byte[] tmp = new byte[4];
245 s.Read(tmp, 0, 4);
246 ret = (tmp[0] << 24);
247 ret |= (tmp[1] << 16) & 0x00FF0000;
248 ret |= (tmp[2] << 8) & 0x0000FF00;
249 ret |= tmp[3] & 0x000000FF;
250 return ret;
251 }
252
253 /// <summary>
254 /// Read an unsigned 64-bit little-endian integer from the stream.
255 /// </summary>
256 /// <param name="s"></param>
257 /// <returns></returns>
258 public static ulong ReadUInt64LE(this Stream s) => unchecked((ulong)s.ReadInt64LE());
259
260 /// <summary>
261 /// Read a signed 64-bit little-endian integer from the stream.
262 /// </summary>
263 /// <param name="s"></param>
264 /// <returns></returns>
265 public static long ReadInt64LE(this Stream s)
266 {
267 long ret;
268 byte[] tmp = new byte[8];
269 s.Read(tmp, 0, 8);
270 ret = tmp[4] & 0x000000FFL;
271 ret |= (tmp[5] << 8) & 0x0000FF00L;
272 ret |= (tmp[6] << 16) & 0x00FF0000L;
273 ret |= (tmp[7] << 24) & 0xFF000000L;
274 ret <<= 32;
275 ret |= tmp[0] & 0x000000FFL;
276 ret |= (tmp[1] << 8) & 0x0000FF00L;
277 ret |= (tmp[2] << 16) & 0x00FF0000L;
278 ret |= (tmp[3] << 24) & 0xFF000000L;
279 return ret;
280 }
281
282 public static void WriteInt64LE(this Stream s, long i)
283 {
284 s.WriteUInt64LE(unchecked((ulong)i));
285 }
286
287 public static void WriteUInt64LE(this Stream s, ulong i)
288 {
289 byte[] tmp = new byte[8];
290 tmp[0] = (byte)(i & 0xFF);
291 tmp[1] = (byte)((i >> 8) & 0xFF);
292 tmp[2] = (byte)((i >> 16) & 0xFF);
293 tmp[3] = (byte)((i >> 24) & 0xFF);
294 i >>= 32;
295 tmp[4] = (byte)(i & 0xFF);
296 tmp[5] = (byte)((i >> 8) & 0xFF);
297 tmp[6] = (byte)((i >> 16) & 0xFF);
298 tmp[7] = (byte)((i >> 24) & 0xFF);
299 s.Write(tmp, 0, 8);
300 }
301
302 /// <summary>
303 /// Read an unsigned 64-bit big-endian integer from the stream.
304 /// </summary>
305 /// <param name="s"></param>
306 /// <returns></returns>
307 public static ulong ReadUInt64BE(this Stream s) => unchecked((ulong)s.ReadInt64BE());
308
309 /// <summary>
310 /// Read a signed 64-bit big-endian integer from the stream.
311 /// </summary>
312 /// <param name="s"></param>
313 /// <returns></returns>
314 public static long ReadInt64BE(this Stream s)
315 {
316 long ret;
317 byte[] tmp = new byte[8];
318 s.Read(tmp, 0, 8);
319 ret = tmp[3] & 0x000000FFL;
320 ret |= (tmp[2] << 8) & 0x0000FF00L;
321 ret |= (tmp[1] << 16) & 0x00FF0000L;
322 ret |= (tmp[0] << 24) & 0xFF000000L;
323 ret <<= 32;
324 ret |= tmp[7] & 0x000000FFL;
325 ret |= (tmp[6] << 8) & 0x0000FF00L;
326 ret |= (tmp[5] << 16) & 0x00FF0000L;
327 ret |= (tmp[4] << 24) & 0xFF000000L;
328 return ret;
329 }
330
331 /// <summary>
332 /// Reads a multibyte value of the specified length from the stream.
333 /// </summary>
334 /// <param name="s">The stream</param>
335 /// <param name="bytes">Must be less than or equal to 8</param>
336 /// <returns></returns>
337 public static long ReadMultibyteBE(this Stream s, byte bytes)
338 {
339 if (bytes > 8) return 0;
340 long ret = 0;
341 var b = s.ReadBytes(bytes);
342 for (uint i = 0; i < b.Length; i++)
343 {
344 ret <<= 8;
345 ret |= b[i];
346 }
347 return ret;
348 }
349
350 /// <summary>
351 /// Read a single-precision (4-byte) floating-point value from the stream.
352 /// </summary>
353 /// <param name="s"></param>
354 /// <returns></returns>
355 public static float ReadFloat(this Stream s)
356 {
357 byte[] tmp = new byte[4];
358 s.Read(tmp, 0, 4);
359 return BitConverter.ToSingle(tmp, 0);
360 }
361
362 /// <summary>
363 /// Read a half-precision (2-byte) floating point value from the stream.
364 /// Return value is aliased to a single precision float because C# does not support half floats.
365 /// </summary>
366 /// <param name="s"></param>
367 /// <returns></returns>
368 public static float ReadHalfFloat(this Stream s) => ParseHalfFloat(s.ReadUInt16LE());
369
370 unsafe public static float ParseHalfFloat(int half)
371 {
372 int sign = half >> 15;
373 int exponent = ((half >> 10) & 0x1F);
374 int mantissa = half & 0x03FF;
375 int single;
376 if (exponent == 0)
377 {
378 // Subnormal
379 if (mantissa == 0) return 0;
380 int exp = -15;
381 int mask = 0x3FF;
382 // Find the first leading 1.
383 while (mantissa == (mantissa & mask))
384 {
385 mask >>= 1;
386 exp--;
387 }
388 // AND the mantissa with the mask because the SP float is *not* subnormal and has an implied "1."
389 single = (sign << 31) | (((128 + exp) & 0xFF) << 23) | ((mantissa & mask) << (30 + exp));
390 }
391 else
392 {
393 single = exponent == 31 ?
394 // Infinity
395 (mantissa == 0 ? (sign << 31) | (0xFF << 23)
396 // NaN
397 : (sign << 31) | (0xFF << 23) | 1)
398 // Normal
399 : (sign << 31) | (((exponent + 112) & 0xFF) << 23) | (mantissa << 13);
400 }
401 // TODO: Any other option besides unsafe code or allocating an unnecessary byte array?
402 return *(float*)(&single);
403 /* Eek, unsafe, but BitConverter.ToSingle is also unsafe according to reference source,
404 and it requires allocating another byte array and multiple method calls... */
405 // return BitConverter.ToSingle(BitConverter.GetBytes(single), 0);
406 }
407
408
409 /// <summary>
410 /// Read a null-terminated ASCII string from the given stream.
411 /// </summary>
412 /// <param name="s"></param>
413 /// <returns></returns>
414 public static string ReadASCIINullTerminated(this Stream s, int limit = -1)
415 {
416 StringBuilder sb = new StringBuilder(255);
417 char cur;
418 while ((limit == -1 || sb.Length < limit) && (cur = (char)s.ReadByte()) != 0)
419 {
420 sb.Append(cur);
421 }
422 return sb.ToString();
423 }
424
425 /// <summary>
426 /// Read a null-terminated UTF8 string from the given stream.
427 /// </summary>
428 /// <param name="s"></param>
429 /// <returns></returns>
430 public static string ReadFixedLengthNullTerminatedString(this Stream s, int length)
431 {
432 var stringBytes = s.ReadBytes(length);
433 var endIdx = 0;
434 for (int i = 0; i < stringBytes.Length; i++)
435 {
436 endIdx = i;
437 if (stringBytes[i] == 0)
438 break;
439 }
440 return Encoding.UTF8.GetString(stringBytes, 0, endIdx);
441 }
442
443 public static string ReadFixedLengthString(this Stream s, int length)
444 {
445 var stringBytes = s.ReadBytes(length);
446 return Encoding.UTF8.GetString(stringBytes, 0, stringBytes.Length);
447 }
448
449 /// <summary>
450 /// Read a length-prefixed string of the specified encoding type from the file.
451 /// The length is a 32-bit little endian integer.
452 /// </summary>
453 /// <param name="s"></param>
454 /// <param name="e">The encoding to use to decode the string.</param>
455 /// <returns></returns>
456 public static string ReadLengthPrefixedString(this Stream s, Encoding e, bool bigEdn = false)
457 {
458 int length = bigEdn ? s.ReadInt32BE() : s.ReadInt32LE();
459 byte[] chars = new byte[length];
460 s.Read(chars, 0, length);
461 return e.GetString(chars);
462 }
463
464 /// <summary>
465 /// Read a length-prefixed UTF-8 string from the given stream.
466 /// </summary>
467 /// <param name="s"></param>
468 /// <returns></returns>
469 public static string ReadLengthUTF8(this Stream s, bool bigEdn = false)
470 {
471 return s.ReadLengthPrefixedString(Encoding.UTF8, bigEdn);
472 }
473
474 /// <summary>
475 /// Read a given number of bytes from a stream into a new byte array.
476 /// </summary>
477 /// <param name="s"></param>
478 /// <param name="count">Number of bytes to read (maximum)</param>
479 /// <returns>New byte array of size <=count.</returns>
480 public static byte[] ReadBytes(this Stream s, int count)
481 {
482 // Size of returned array at most count, at least difference between position and length.
483 int realCount = (int)((s.Position + count > s.Length) ? (s.Length - s.Position) : count);
484 byte[] ret = new byte[realCount];
485 s.Read(ret, 0, realCount);
486 return ret;
487 }
488
489 /// <summary>
490 /// Read a variable-length integral value as found in MIDI messages.
491 /// </summary>
492 /// <param name="s"></param>
493 /// <returns></returns>
494 public static uint ReadMidiMultiByte(this Stream s)
495 {
496 int ret = 0;
497 byte b = (byte)(s.ReadByte());
498 ret += b & 0x7f;
499 if (0x80 == (b & 0x80))
500 {
501 ret <<= 7;
502 b = (byte)(s.ReadByte());
503 ret += b & 0x7f;
504 if (0x80 == (b & 0x80))
505 {
506 ret <<= 7;
507 b = (byte)(s.ReadByte());
508 ret += b & 0x7f;
509 if (0x80 == (b & 0x80))
510 {
511 ret <<= 7;
512 b = (byte)(s.ReadByte());
513 ret += b & 0x7f;
514 if (0x80 == (b & 0x80))
515 throw new InvalidDataException("Variable-length MIDI number > 4 bytes");
516 }
517 }
518 }
519 return (uint)ret;
520 }
521
522 public static void WriteMidiMultiByte(this Stream s, uint i)
523 {
524 if (i > 0x7FU)
525 {
526 int max = 7;
527 while ((i >> max) > 0x7FU) max += 7;
528 while (max > 0)
529 {
530 s.WriteByte((byte)(((i >> max) & 0x7FU) | 0x80));
531 max -= 7;
532 }
533 }
534 s.WriteByte((byte)(i & 0x7FU));
535 }
536
537 public static void WriteLE(this Stream s, ushort i) => s.WriteUInt16LE(i);
538 public static void WriteLE(this Stream s, uint i) => s.WriteUInt32LE(i);
539 public static void WriteLE(this Stream s, ulong i) => s.WriteUInt64LE(i);
540 public static void WriteLE(this Stream s, short i) => s.WriteInt16LE(i);
541 public static void WriteLE(this Stream s, int i) => s.WriteInt32LE(i);
542 public static void WriteLE(this Stream s, long i) => s.WriteInt64LE(i);
543 public static ushort FlipEndian(this ushort i) => (ushort)((i & 0xFFU) << 8 | (i & 0xFF00U) >> 8);
544 public static uint FlipEndian(this uint i) => (i & 0x000000FFU) << 24 | (i & 0x0000FF00U) << 8 |
545 (i & 0x00FF0000U) >> 8 | (i & 0xFF000000U) >> 24;
546 public static ulong FlipEndian(this ulong i) => (i & 0x00000000000000FFUL) << 56 | (i & 0x000000000000FF00UL) << 40 |
547 (i & 0x0000000000FF0000UL) << 24 | (i & 0x00000000FF000000UL) << 8 |
548 (i & 0x000000FF00000000UL) >> 8 | (i & 0x0000FF0000000000UL) >> 24 |
549 (i & 0x00FF000000000000UL) >> 40 | (i & 0xFF00000000000000UL) >> 56;
550 public static short FlipEndian(this short i) => unchecked((short)((ushort)i).FlipEndian());
551 public static int FlipEndian(this int i) => unchecked((int)((uint)i).FlipEndian());
552 public static long FlipEndian(this long i) => unchecked((long)((ulong)i).FlipEndian());
553 public static void WriteBE(this Stream s, ushort i) => s.WriteUInt16LE(i.FlipEndian());
554 public static void WriteBE(this Stream s, uint i) => s.WriteUInt32LE(i.FlipEndian());
555 public static void WriteBE(this Stream s, ulong i) => s.WriteUInt64LE(i.FlipEndian());
556 public static void WriteBE(this Stream s, short i) => s.WriteInt16LE(i.FlipEndian());
557 public static void WriteBE(this Stream s, int i) => s.WriteInt32LE(i.FlipEndian());
558 public static void WriteBE(this Stream s, long i) => s.WriteInt64LE(i.FlipEndian());
559
560
561 /// <summary>
562 /// Write a signed 8-bit integer to the stream.
563 /// </summary>
564 /// <param name="s"></param>
565 /// <param name="int8">The integer to write.</param>
566 public static void WriteInt8(this Stream s, sbyte int8) => s.WriteUInt8((byte)int8);
567
568 /// <summary>
569 /// Write an unsigned 8-bit integer to the stream.
570 /// </summary>
571 /// <param name="s"></param>
572 /// <param name="uint8">The integer to write.</param>
573 public static void WriteUInt8(this Stream s, byte uint8)
574 {
575 byte[] tmp = new byte[1] { uint8 };
576 s.Write(tmp, 0, 1);
577 }
578
579 /// <summary>
580 /// Write a length-prefixed string of the specified encoding type to the file.
581 /// The length is a 32-bit little endian integer.
582 /// </summary>
583 /// <param name="s"></param>
584 /// <param name="e">The encoding to use to decode the string.</param>
585 /// <param name="str">The string to write.</param>
586 public static void WriteLengthPrefixedString(this Stream s, Encoding e, string str, bool bigEdn = false)
587 {
588 int byteCount = e.GetByteCount(str);
589 if (bigEdn)
590 s.WriteUInt32BE((uint)byteCount);
591 else
592 s.WriteInt32LE(byteCount);
593 byte[] chars = e.GetBytes(str);
594 s.Write(chars, 0, byteCount);
595 }
596
597 /// <summary>
598 /// Write a length-prefixed UTF-8 string to the given stream.
599 /// </summary>
600 /// <param name="s"></param>
601 /// <param name="str">The string to write.</param>
602 public static void WriteLengthUTF8(this Stream s, string str, bool bigEdn = false)
603 {
604 s.WriteLengthPrefixedString(Encoding.UTF8, str, bigEdn);
605 }
606
607 /// <summary>
608 /// Write a single-precision (4-byte) floating-point value to the stream.
609 /// </summary>
610 /// <param name="s"></param>
611 /// <param name="flt">The floating point value to write.</param>
612 public static void WriteFloat(this Stream s, float flt)
613 {
614 byte[] tmp = BitConverter.GetBytes(flt);
615 s.Write(tmp, 0, 4);
616 }
617
618}