mysimulation/server/tso.files/Utils/IoBuffer.cs
Tony Bark 8fec258215 Added FSO.Files for use with the API server
Don't ask me. FreeSO is the prime example of dependency hell.
2024-05-01 04:38:12 -04:00

357 lines
11 KiB
C#
Executable file

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace FSO.Files.Utils
{
/// <summary>
/// The order to read bytes in.
/// </summary>
public enum ByteOrder
{
BIG_ENDIAN,
LITTLE_ENDIAN
}
/// <summary>
/// IOBuffer is a very basic wrapper over System.BinaryReader that inherits from IDisposable.
/// </summary>
public class IoBuffer : IDisposable, BCFReadProxy
{
private Stream Stream;
private BinaryReader Reader;
public ByteOrder ByteOrder = ByteOrder.BIG_ENDIAN;
/// <summary>
/// Creates a new IOBuffer instance from a stream.
/// </summary>
/// <param name="stream"></param>
public IoBuffer(Stream stream)
{
this.Stream = stream;
this.Reader = new BinaryReader(stream);
}
/// <summary>
/// More to read in this stream?
/// </summary>
public bool HasMore
{
get
{
return Stream.Position < Stream.Length - 1;
}
}
/// <summary>
/// Skips a number of bytes in the current stream, starting from the current position.
/// </summary>
/// <param name="numBytes">Number of bytes to skip.</param>
public void Skip(long numBytes)
{
Reader.BaseStream.Seek(numBytes, SeekOrigin.Current);
}
/// <summary>
/// Seeks in the current stream.
/// </summary>
/// <param name="origin">Where to start from.</param>
/// <param name="offset">The offset to seek to.</param>
public void Seek(SeekOrigin origin, long offset)
{
Reader.BaseStream.Seek(offset, origin);
}
public long Position => Stream.Position;
/// <summary>
/// Reads a variable length unsigned integer from the current stream.
/// </summary>
/// <returns>A uint.</returns>
public uint ReadVarLen()
{
uint result = 0;
int shift = 0;
byte read = 0x80;
while ((read&0x80) > 0)
{
read = ReadByte();
result |= (uint)((read & 0x7F) << shift);
shift += 7;
}
return result;
}
/// <summary>
/// Reads an unsigned 16bit integer from the current stream.
/// </summary>
/// <returns>A ushort.</returns>
public ushort ReadUInt16()
{
var value = Reader.ReadUInt16();
if (ByteOrder == ByteOrder.BIG_ENDIAN)
{
value = Endian.SwapUInt16(value);
}
return value;
}
/// <summary>
/// Reads a 16bit integer from the current stream.
/// </summary>
/// <returns>A short.</returns>
public short ReadInt16()
{
var value = Reader.ReadInt16();
if (ByteOrder == ByteOrder.BIG_ENDIAN)
{
value = Endian.SwapInt16(value);
}
return value;
}
/// <summary>
/// Reads a 32bit integer from the current stream.
/// </summary>
/// <returns>An int.</returns>
public int ReadInt32()
{
var value = Reader.ReadInt32();
if (ByteOrder == ByteOrder.BIG_ENDIAN)
{
value = Endian.SwapInt32(value);
}
return value;
}
/// <summary>
/// Reads a 64bit integer from the current stream.
/// </summary>
/// <returns>An int.</returns>
public long ReadInt64()
{
var value = Reader.ReadInt64();
if (ByteOrder == ByteOrder.BIG_ENDIAN)
{
value = Endian.SwapInt64(value);
}
return value;
}
/// <summary>
/// Reads an unsigned 32bit integer from the current stream.
/// </summary>
/// <returns>A uint.</returns>
public uint ReadUInt32()
{
var value = Reader.ReadUInt32();
if (ByteOrder == ByteOrder.BIG_ENDIAN)
{
value = Endian.SwapUInt32(value);
}
return value;
}
/// <summary>
/// Reads a number of ASCII characters from the current stream.
/// </summary>
/// <param name="num">The number of characters to read.</param>
/// <returns>A string, INCLUDING the trailing 0.</returns>
public string ReadCString(int num)
{
return ReadCString(num, false);
}
/// <summary>
/// Reads a number of ASCII characters from the current stream.
/// </summary>
/// <param name="num">The number of characters to read.</param>
/// <param name="trimNull">Trim the trailing 0?</param>
/// <returns>A string, with or without the trailing 0.</returns>
public string ReadCString(int num, bool trimNull)
{
var result = ASCIIEncoding.ASCII.GetString(Reader.ReadBytes(num));
if (trimNull)
{
/** Trim on \0 **/
var io = result.IndexOf('\0');
if (io != -1)
{
result = result.Substring(0, io);
}
}
return result;
}
/// <summary>
/// Reads a byte from the current stream.
/// </summary>
/// <returns>A byte.</returns>
public byte ReadByte()
{
return Reader.ReadByte();
}
/// <summary>
/// Reads a number of bytes from the current stream.
/// </summary>
/// <param name="num">Number of bytes to read.</param>
/// <returns>An byte array.</returns>
public byte[] ReadBytes(uint num)
{
return Reader.ReadBytes((int)num);
}
/// <summary>
/// Reads a number of bytes from the current stream.
/// </summary>
/// <param name="num">Number of bytes to read.</param>
/// <returns>An byte array.</returns>
public byte[] ReadBytes(int num)
{
return Reader.ReadBytes(num);
}
/// <summary>
/// Reads a pascal string from the current stream, which is prefixed by a 16bit short.
/// </summary>
/// <returns>A string.</returns>
public string ReadLongPascalString()
{
var length = ReadInt16();
return Encoding.ASCII.GetString(Reader.ReadBytes(length));
}
/// <summary>
/// Reads a C string from the current stream.
/// </summary>
/// <returns>A string.</returns>
public string ReadNullTerminatedString()
{
var sb = new StringBuilder();
while (true){
char ch = (char)Reader.ReadByte();
if (ch == '\0'){
break;
}
sb.Append(ch);
}
return sb.ToString();
}
public string ReadNullTerminatedUTF8()
{
var sb = new List<byte>();
while (true)
{
var b = Reader.ReadByte();
if (b == 0) break;
sb.Add(b);
}
return Encoding.UTF8.GetString(sb.ToArray());
}
/// <summary>
/// Reads a pascal string from the current stream.
/// </summary>
/// <returns>A string.</returns>
public string ReadVariableLengthPascalString()
{
return Reader.ReadString();
}
/// <summary>
/// Reads a pascal string from the current stream, prefixed by a byte.
/// </summary>
/// <returns>A string.</returns>
public string ReadPascalString()
{
var length = ReadByte();
return Encoding.ASCII.GetString(Reader.ReadBytes(length));
}
/// <summary>
/// Reads a float from the current stream.
/// </summary>
/// <returns>A float.</returns>
[System.Security.SecuritySafeCritical] // auto-generated
public virtual unsafe float ReadFloat()
{
return Reader.ReadSingle();
}
/// <summary>
/// Sets a mark at the current position in the stream.
/// </summary>
private long _Mark;
public void Mark()
{
_Mark = Reader.BaseStream.Position;
}
/// <summary>
/// Seeks in the current stream from the current mark plus the number of bytes.
/// </summary>
/// <param name="numBytes">The number of bytes to add to the offset (mark).</param>
public void SeekFromMark(long numBytes)
{
Reader.BaseStream.Seek(_Mark + numBytes, SeekOrigin.Begin);
}
#region IDisposable Members
public void Dispose()
{
}
#endregion
/// <summary>
/// Creates a new IOBuffer instance from a stream.
/// </summary>
/// <param name="stream">A stream.</param>
/// <returns>A new IOBuffer instance.</returns>
public static IoBuffer FromStream(Stream stream)
{
return new IoBuffer(stream);
}
/// <summary>
/// Creates a new IOBuffer instance from a stream, using a specified byte order.
/// </summary>
/// <param name="stream">A stream.</param>
/// <param name="order">Byte order to use.</param>
/// <returns>A new IOBuffer instance.</returns>
public static IoBuffer FromStream(Stream stream, ByteOrder order)
{
var item = FromStream(stream);
item.ByteOrder = order;
return item;
}
/// <summary>
/// Creates a new IOBuffer instance from a byte array.
/// </summary>
/// <param name="bytes">The byte array to use.</param>
/// <returns>A new IOBuffer instance.</returns>
public static IoBuffer FromBytes(byte[] bytes)
{
return FromStream(new MemoryStream(bytes));
}
/// <summary>
/// Creates a new IOBuffer instance from a byte array, using a specified byte order.
/// </summary>
/// <param name="bytes">The byte array to use.</param>
/// <param name="order">Byte order to use.</param>
/// <returns>A new IOBuffer instance.</returns>
public static IoBuffer FromBytes(byte[] bytes, ByteOrder order)
{
return FromStream(new MemoryStream(bytes), order);
}
}
}