Files
Cpp2IL/Cpp2IL.Core/Extensions/StreamExtensions.cs
2024-11-15 16:43:31 +00:00

106 lines
2.9 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.IO.Compression;
namespace Cpp2IL.Core.Extensions;
public static class StreamExtensions
{
public static uint ReadUnityCompressedUint(this Stream stream)
{
if (stream.Position == stream.Length)
throw new EndOfStreamException();
var b = stream.ReadByte();
if (b < 128)
return (uint)b;
if (b == 240)
{
//Full Uint
#if NET7_0_OR_GREATER
Span<byte> buffer = stackalloc byte[4];
stream.ReadExactly(buffer);
return BitConverter.ToUInt32(buffer);
#elif NET5_0_OR_GREATER
Span<byte> buffer = stackalloc byte[4];
stream.Read(buffer);
return BitConverter.ToUInt32(buffer);
#else
var buffer = new byte[4];
stream.Read(buffer, 0, 4);
return BitConverter.ToUInt32(buffer, 0);
#endif
}
//Special constant values
if (b == byte.MaxValue)
return uint.MaxValue;
if (b == 254)
return uint.MaxValue - 1;
if ((b & 192) == 192)
{
//3 more to read
return (uint)((b & ~192U) << 24 | (uint)(stream.ReadByte() << 16) | (uint)(stream.ReadByte() << 8) | (uint)stream.ReadByte());
}
if ((b & 128) == 128)
{
//1 more to read
return (uint)((b & ~128U) << 8 | (uint)stream.ReadByte());
}
throw new Exception($"How did we even get here? Invalid compressed int first byte {b}");
}
public static int ReadUnityCompressedInt(this Stream stream)
{
//Ref libil2cpp, il2cpp\utils\ReadCompressedInt32
var unsigned = stream.ReadUnityCompressedUint();
if (unsigned == uint.MaxValue)
return int.MinValue;
var isNegative = (unsigned & 1) == 1;
unsigned >>= 1;
if (isNegative)
return -(int)(unsigned + 1);
return (int)unsigned;
}
public static string ReadUnicodeString(this BinaryReader reader)
{
List<byte> bytes = [];
var continueReading = true;
var lastWasNull = false;
while (continueReading)
{
var b = reader.ReadByte();
if (b == 0 && lastWasNull)
//Double null is a terminator for unicode strings
continueReading = false;
lastWasNull = b == 0;
bytes.Add(b);
}
bytes.Add(reader.ReadByte()); //Last byte of null terminator will always be skipped - unskip it
return Encoding.Unicode.GetString(bytes.ToArray()).TrimEnd('\0');
}
public static byte[] ReadBytes(this Stream stream)
{
using var ms = new MemoryStream();
stream.CopyTo(ms);
return ms.ToArray();
}
public static byte[] ReadBytes(this ZipArchiveEntry entry) => entry.Open().ReadBytes();
}