using System;
using System.Text;
using Ion.Specialized.Encoding;
namespace Ion.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 mID;
///
/// The content of this message as a byte array.
///
private readonly byte[] mContent;
///
/// The current index in the content array, used when reading the message.
///
private int mContentCursor;
#endregion
#region Properties
///
/// Gets the ID of this message as an unsigned 32 bit integer.
///
public uint ID
{
get { return mID; }
}
///
/// Gets theheader of this message, by Base64 encoding the message ID to a 2 byte string.
///
public string Header
{
get
{
return IonEnvironment.GetDefaultTextEncoding().GetString(Base64Encoding.Encodeuint(mID, 2));
}
}
///
/// Gets the method name the client intends to invoke on the server with this message.
///
public string MethodName
{
get
{
return string.Format("TODO:GETMETHOD{0}", mID);
}
}
///
/// Gets the length of the content in this message.
///
public int contentLength
{
get { return mContent.Length; }
}
///
/// Gets the amount of unread content bytes.
///
public int remainingContent
{
get { return (mContent.Length - mContentCursor); }
}
#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];
mID = ID;
mContent = bzContent;
mContentCursor = 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()
{
mContentCursor = 0;
}
///
/// Advances the content cursor by a given amount of bytes.
///
/// The amount of bytes to 'skip'.
public void Advance(int n)
{
mContentCursor += n;
}
///
/// Returns the total content of this message as a string.
///
/// String
public string GetContentString()
{
return IonEnvironment.GetDefaultTextEncoding().GetString(mContent);
}
///
/// 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[]
private byte[] ReadBytes(int numBytes)
{
if (numBytes > this.remainingContent)
numBytes = this.remainingContent;
byte[] bzData = new byte[numBytes];
for (int x = 0; x < numBytes; x++)
{
bzData[x] = mContent[mContentCursor++];
}
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[]
private byte[] ReadBytesFreezeCursor(int numBytes)
{
if (numBytes > this.remainingContent)
numBytes = this.remainingContent;
byte[] bzData = new byte[numBytes];
for (int x = 0, y = mContentCursor; x < numBytes; x++, y++)
{
bzData[x] = mContent[y];
}
return bzData;
}
///
/// Reads a length-prefixed (Base64) value from the message and returns it as a byte array.
///
/// byte[]
private byte[] ReadFixedValue()
{
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.remainingContent > 0 && mContent[mContentCursor++] == Base64Encoding.POSITIVE);
}
public Int32 PopInt32()
{
return Base64Encoding.DecodeInt32(this.ReadBytes(2));
}
public UInt32 PopUInt32()
{
return (UInt32)PopInt32();
}
///
/// Reads a length prefixed string from the message content and encodes it with a given System.Text.Encoding.
///
/// The System.Text.Encoding to encode the string with.
/// String
public String PopFixedString(Encoding pEncoding)
{
if (pEncoding == null)
pEncoding = IonEnvironment.GetDefaultTextEncoding();
return pEncoding.GetString(this.ReadFixedValue());
}
///
/// Reads a length prefixed string from the message content and encodes it with the Ion environment default text encoding.
///
/// String
public String PopFixedString()
{
Encoding pEncoding = IonEnvironment.GetDefaultTextEncoding();
return PopFixedString(pEncoding);
}
///
/// 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 PopFixedInt32()
{
Int32 i;
String s = PopFixedString(Encoding.ASCII);
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
/// PopFixedInt32
public uint PopFixedUInt32()
{
return (uint)PopFixedInt32();
}
///
/// Reads a wire format boolean and returns it. False is returned if there is no remaining content.
///
/// Boolean
public Boolean PopWiredBoolean()
{
return (this.remainingContent > 0 && mContent[mContentCursor++] == 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.remainingContent == 0)
return 0;
byte[] bzData = this.ReadBytesFreezeCursor(WireEncoding.MAX_INTEGER_BYTE_AMOUNT);
Int32 totalBytes = 0;
Int32 i = WireEncoding.DecodeInt32(bzData, out totalBytes);
mContentCursor += 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 PopWireduint()
{
return (uint)PopWiredInt32();
}
public override string ToString()
{
return this.Header + GetContentString();
}
#endregion
}
}