HMX Forge (RB4/DCS/Amp16/FME) save game decryptor/encryptor
at master 21 kB view raw
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 &lt;=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}