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 } }