using System;
using System.Text;
using Aleeda.Specialized.Encoding;
namespace Aleeda.Net.Messages
{
///
/// Represents a Habbo client > server protocol structured message, providing methods to identify and 'read' the message.
///
public class ClientMessage : IHabboMessage
{
#region Fields
///
/// The ID of this message as an unsigned 32 bit integer.
///
private readonly uint fID;
///
/// The content of this message as a byte array.
///
private readonly byte[] fContent;
///
/// The current index in the content array, used when reading the message.
///
private int fContentCursor;
#endregion
#region Properties
///
/// Gets the ID of this message as an unsigned 32 bit integer.
///
public uint ID
{
get
{
return fID;
}
}
///
/// Gets the header of this message, by Base64 encoding the message ID to a 2 byte string.
///
public string GetHeader()
{
return AleedaEnvironment.GetDefaultEncoding().GetString(Base64Encoding.Encodeuint(fID, 2));
}
///
/// Gets the length of the content in this message.
///
public int GetContentLength()
{
return fContent.Length;
}
///
/// Gets the amount of unread content bytes.
///
public int GetRemainingContent()
{
return (fContent.Length - fContentCursor);
}
public int contentLength
{
get
{
return fContent.Length - fContentCursor;
}
}
#endregion
#region Constructors
///
/// Constructs a ClientMessage object for a given message ID and a given content byte array.
///
/// The ID of the message as an unsigned 32 bit integer.
/// The content as a byte array. If null is supplied, an empty byte array will be created.
public ClientMessage(uint ID, byte[] bzContent)
{
if (bzContent == null)
bzContent = new byte[0];
fID = ID;
fContent = bzContent;
fContentCursor = 0;
}
#endregion
#region Methods
///
/// Resets the client message to it's state when it was constructed by resetting the content reader cursor. This allows to re-read read data.
///
public void Reset()
{
fContentCursor = 0;
}
///
/// Advances the content cursor by a given amount of bytes.
///
/// The amount of bytes to 'skip'.
public void Advance(int n)
{
fContentCursor += n;
}
///
/// Returns the total content of this message as a string.
///
/// String
public string GetContentString()
{
return Encoding.Default.GetString(fContent);
}
///
/// Returns the header and total content of this message as a string.
///
public string GetFullString()
{
return this.GetHeader() + GetContentString();
}
///
/// Reads a given amount of bytes from the remaining message content and returns it in a byte array. The reader cursor is incremented during reading.
///
/// The amount of bytes to read, advance and return. If there is less remaining data than this value, all remaining data will be read.
/// byte[]
public byte[] ReadBytes(int numBytes)
{
if (numBytes > this.GetRemainingContent())
numBytes = this.GetRemainingContent();
byte[] bzData = new byte[numBytes];
for (int x = 0; x < numBytes; x++)
{
bzData[x] = fContent[fContentCursor++];
}
return bzData;
}
///
/// Reads a given amount of bytes from the remaining message content and returns it in a byte array. The reader cursor does not increment during reading.
///
/// The amount of bytes to read, advance and return. If there is less remaining data than this value, all remaining data will be read.
/// byte[]
public byte[] ReadBytesFreezeCursor(int numBytes)
{
if (numBytes > this.GetRemainingContent())
numBytes = this.GetRemainingContent();
byte[] bzData = new byte[numBytes];
for (int x = 0, y = fContentCursor; x < numBytes; x++, y++)
{
bzData[x] = fContent[y];
}
return bzData;
}
///
/// Reads a length-prefixed (Base64) value from the message and returns it as a byte array.
///
/// byte[]
public byte[] ReadPrefixedValue()
{
Int32 Length = Base64Encoding.DecodeInt32(this.ReadBytes(2));
return this.ReadBytes(Length);
}
///
/// Reads a Base64 boolean and returns it. False is returned if there is no remaining content.
///
/// Boolean
public Boolean PopBase64Boolean()
{
return (this.GetRemainingContent() > 0 && fContent[fContentCursor++] == Base64Encoding.POSITIVE);
}
public Int32 PopInt32()
{
return Base64Encoding.DecodeInt32(this.ReadBytes(2));
}
public UInt32 PopUInt32()
{
return (UInt32)PopInt32();
}
public string PopFixedString()
{
return PopFixedString(AleedaEnvironment.GetDefaultEncoding());
}
public string PopFixedString(Encoding encoding)
{
return encoding.GetString(ReadPrefixedValue()).Replace(Convert.ToChar(1), ' ');
}
///
/// Reads a length prefixed string 32 bit integer from the message content and tries to parse it to integer. No exceptions are thrown if parsing fails.
///
/// Int32
public Int32 PopPrefixedInt32()
{
Int32 i;
String s = PopFixedString(Encoding.UTF8);
Int32.TryParse(s, out i);
return i;
}
///
/// Reads a length prefixed string 32 bit unsigned integer from the message content and tries to parse it to integer. No exceptions are thrown if parsing fails.
///
/// Int32
/// PopPrefixedInt32
public uint PopPrefixedUInt32()
{
return (uint)PopPrefixedInt32();
}
///
/// Reads a wire format boolean and returns it. False is returned if there is no remaining content.
///
/// Boolean
public Boolean PopWiredBoolean()
{
return (this.GetRemainingContent() > 0 && fContent[fContentCursor++] == WireEncoding.POSITIVE);
}
///
/// Reads the next wire encoded 32 bit integer from the message content and advances the reader cursor.
///
/// Int32
public Int32 PopWiredInt32()
{
if (this.GetRemainingContent() == 0)
return 0;
byte[] bzData = ReadBytesFreezeCursor(WireEncoding.MAX_INTEGER_BYTE_AMOUNT);
Int32 totalBytes = 0;
Int32 i = WireEncoding.DecodeInt32(bzData, out totalBytes);
fContentCursor += totalBytes;
return i;
}
///
/// Reads the next wire encoded unsigned 32 bit integer from the message content and advances the reader cursor.
///
/// Int32
/// PopWiredInt32()
public uint PopWiredUInt32()
{
return (uint)PopWiredInt32();
}
public string PopEncodeInt32()
{
return wireEncoding.encodeVL64(PopWiredInt32());
}
#endregion
}
}