• Welcome to Valhalla Legends Archive.
 
Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - E.T.

#1
General Discussion / Re: Best TV Shows!
February 25, 2007, 08:00 AM
The only show I've really followed over the years is South Park. But I do occasionally download some humor shows:

The Colbert Report - My current favorite actually.
The Daily Show WithOUT Jon Stewart (i.e. most 'correspondents' (Lewis Black, Steve Carell, John Oliver...) are good but he gets on my nerves a little  ;D)

And I have to admit to compulsively watching Lost. It's stretched too long with too much of a reality show aftertaste, but I love psychological experimentation stuff and it's always fun to theorize what they are they are leading the rats to now, what they want them to think and what the goal of the experimentations are. Some episodes are really well written, but you do need to get into the mindset to be able to resort to suspension of disbelief at times... the storyline certainly ain't perfect.

Other than that I do enjoy the Mythbusters when I catch them. Funny and informative / insightful ('edutainment' if you will) is the kind of video I like. I also watch documentaries, but mostly nature, astronomy and history stuff where image is important. For real info or more engaging entertainment I look elsewhere...

P.S.: If you think taking a walk is boring and always need others to entertain you, you have no imagination.
#2
Oh nice, I didn't know about Marshal.GetDelegateForFunctionPointer (in my denfense, it wasn't there in v.1.1  :-\ )

Thanks for the link (I didn't know about beta version of MBNCS either... nice lib BTW, thanks for providing it), the tip and the laugh  ;D
#3
Umm seems you can use PInvoke once you loaded the DLL...
#4
As you say, I don't think you can map an adress to a function in C#... Would a small C++ wrapper dll do? http://www.codeproject.com/csharp/dyninvok.asp

Otherwise, maybe you could hack something with code generation, but I don't know...

Anyhow, here's how to convert str to char* (it must be in a fixed statement to tell GC not to move the string while you're working on it...

I'm not sure for the LPDWORD, but I think this should work too...

int i;
String str;
fixed (char* chars = str)
{
    blah(chars, &i);
}
#5
Oops :o  I hate that word  ;D

All fixed now, thanks.

Yes it's primarily meant to be a bit packed packet reader.
#6
It's not designed for single bits... I called it octet stream reader because it reads from byte arrays and I use it to parse packets.
It's designed like binary reader but unlike it, you can specify the bit length of each field you read from the array. In some packets values are stored on a couple bits to save bandwidth, but most readers assume that all values are at least one full byte in length...

E.g.:

ReadInt32(17) will return an int read from 17 bits in the byte array
ReadBoolean(1) will return a bool from a single bit
ReadString(7, '\0', 20) will return a string from up to 20 7-bit chars or a null
#7
Edit: I optimized Read() and removed the requirement of destination memory being empty, which can lead to hard to debug problems if you forget when adding overloads. But it will still maintain the padding bits in last written  byte...

Also optimized ReadChars, ReadBytes, etc and added ReadString overloads. You can now read a fixed lengh string or terminated string using a max length and a char[] buffer or no max and a List<char> buffer... Now I have still to add encodings support. And endian awareness for that matter. Or parsing floats, dword arrays and ramens. Those things are everywhere...

I find it a bit weird that none of the utility classes I could find support bit unpacking, i.e. reading an arbitrary number of bits from a arbitrary position, in BITS (not bytes!) 'BitConverter' should be renamed ByteConverter and BinaryReader should just be scrapped ^^ Anyhow I bit the bullet and gave a shot at writing one. My primary goal is something flexible to parse packed packets, but fast enough to be used for all packets. I just wrote this and regret not doing it earlier as it makes parsing D2GS 0x9c a breeze. Comments and improvements welcome.


using System;
using System.Collections.Generic;

namespace ETUtils
{
    /// <summary>
    /// Utility class to parse an octet stream from a byte arrays, supporting bit packing.
    /// </summary>
    public class BitReader
    {
        public const int BYTE_MASK = 0xFF;
        public const int BYTE_BITS = 8;
        public const int WORD_BITS = 16;
        public const int DWORD_BITS = 32;
        public const int QWORD_BITS = 64;

        protected byte[] data;
        protected int byteOffset;
        protected int bitOffset;

        #region Constructors
        public BitReader(int length)
        {
            this.data = new byte[length];
        }
        public BitReader(int length, long position)
        {
            this.data = new byte[length];
            this.Position = position;
        }
        public BitReader(int length, int byteOffset)
        {
            this.data = new byte[length];
            this.byteOffset = byteOffset;
        }
        public BitReader(int length, int byteOffset, int bitOffset)
        {
            this.data = new byte[length];
            this.byteOffset = byteOffset;
            this.bitOffset = bitOffset;
        }

        public BitReader(byte[] data)
        {
            this.data = data;
        }
        public BitReader(byte[] data, long position)
        {
            this.data = data;
            this.Position = position;
        }
        public BitReader(byte[] data, int byteOffset)
        {
            this.data = data;
            this.byteOffset = byteOffset;
        }
        public BitReader(byte[] data, int byteOffset, int bitOffset)
        {
            this.data = data;
            this.byteOffset = byteOffset;
            this.bitOffset = bitOffset;
        }

        public BitReader(string data)
        {
            this.data = ByteHelper.GetBytes(data);
        }
        public BitReader(string data, long position)
        {
            this.data = ByteHelper.GetBytes(data);
            this.Position = position;
        }
        public BitReader(string data, int byteOffset)
        {
            this.data = ByteHelper.GetBytes(data);
            this.byteOffset = byteOffset;
        }
        public BitReader(string data, int byteOffset, int bitOffset)
        {
            this.data = ByteHelper.GetBytes(data);
            this.byteOffset = byteOffset;
            this.bitOffset = bitOffset;
        }
        #endregion Constructors

        /// <summary>
        /// Byte array associated with the current instance.
        /// </summary>
        public byte[] Data
        {
            get { return this.data; }
            set
            {
                if (this.data.Length < value.Length)
                {
                    this.byteOffset = 0;
                    this.bitOffset = 0;
                }
                this.data = value;
            }
        }
        /// <summary>
        /// Length of the associated byte array, in bytes.
        /// </summary>
        public int ByteCount
        {
            get { return this.data.Length; }
            set
            {
                byte[] cData = this.data;
                this.data = new byte[value];
                Array.Copy(cData, this.data, Math.Min(this.ByteCount, cData.Length));
                if (this.data.Length < cData.Length)
                {
                    this.byteOffset = 0;
                    this.bitOffset = 0;
                }
            }
        }
        /// <summary>
        /// Length of the associated byte array, in bits.
        /// </summary>
        public long BitCount
        {
            get { return this.data.Length << 3; }
            set
            {
                byte[] cData = this.data;
                this.data = new byte[(int)(value >> 3) + (value & 7) != 0 ? 1 : 0];
                Array.Copy(cData, this.data, Math.Min(this.ByteCount, cData.Length));
                if (this.data.Length < cData.Length)
                {
                    this.byteOffset = 0;
                    this.bitOffset = 0;
                }
            }
        }

        /// <summary>
        /// Offset of the current byte.
        /// </summary>
        public int ByteOffset
        {
            get { return this.byteOffset; }
            set { this.byteOffset = value; }
        }
        /// <summary>
        /// Offset of the current bit, relative to the current byte.
        /// </summary>
        public int BitOffset
        {
            get { return this.bitOffset; }
            set { this.bitOffset = value; }
        }
        /// <summary>
        /// Absolute offset of the current bit (in bits).
        /// </summary>
        public long Position
        {
            get { return ((uint)this.byteOffset << 3) | (uint)this.bitOffset; }
            set
            {
                this.byteOffset = (int)(value >> 3);
                this.bitOffset = (int)value & 7;
            }
        }


        protected void CheckLength(int bits, int maxBits)
        {
            if (bits > maxBits || bits < 1)
                throw new ArgumentOutOfRangeException("bits", bits,
                    String.Concat("Number of bits must be higher than 0 and equal to or less than ", maxBits));
        }
        /*
        protected void CheckOffset(int bits)
        {
            if (bits + this.Offset > this.BitCount)
                throw new OverflowException(String.Format("Less than {0} bits remain! Offset: {1}, Length: {2}",
                    bits, this.Offset, this.BitCount));
        }
        */

        /// <summary>
        /// Increments the byte offset by the specified number.
        /// </summary>
        public void SkipBytes(int bytes)
        {
            this.byteOffset += bytes;
        }
        /// <summary>
        /// Increments the current position in array by the specified number of bits.
        /// </summary>
        public void SkipBits(int bits)
        {
            this.bitOffset += bits & 7;
            if ((this.bitOffset & BYTE_BITS) == BYTE_BITS)
            {
                this.bitOffset ^= BYTE_BITS;
                this.byteOffset++;
            }
            this.byteOffset += bits >> 3;
        }

        /// <summary>
        /// Reads a number of bits, incrementing offsets, and write them to dest.
        /// <para>Be very careful not to overflow when using this function!</para>
        /// <para>Normally you shouldn't have to use it directly...</para>
        /// </summary>
        /// <param name="dest">Pointer to the location where the data will be written.</param>
        /// <param name="bits">Number of bits to read (and copy.)</param>
        unsafe public void Read(byte* dest, int bits)
        {
            int mBits, cBits, destByte = 0, destBit = 0;
            while (true)
            {
                mBits = BYTE_BITS - (destBit > this.bitOffset ? destBit : this.bitOffset);
                if ((cBits = bits < mBits ? bits : mBits) == BYTE_BITS)
                {
                    dest[destByte++] = this.data[this.byteOffset++];
                }
                else
                {
                    dest[destByte] = (byte)(
                        (dest[destByte] & ((BYTE_MASK >> (BYTE_BITS - destBit)) | (BYTE_MASK << (destBit + cBits))))
                        | (((this.data[this.byteOffset] >> this.bitOffset) & (BYTE_MASK >> (BYTE_BITS - cBits)))
                           << destBit)
                        );
                    destBit += cBits;
                    if ((destBit & BYTE_BITS) == BYTE_BITS)
                    {
                        destBit ^= BYTE_BITS;
                        destByte++;
                    }
                    this.bitOffset += cBits;
                    if ((this.bitOffset & BYTE_BITS) == BYTE_BITS)
                    {
                        this.bitOffset ^= BYTE_BITS;
                        this.byteOffset++;
                    }
                }
                if ((bits -= cBits) == 0)
                    break;
            }
        }

        unsafe public bool ReadBoolean()
        {
            byte retVal;
            this.Read((byte*)&retVal, BYTE_BITS);
            return retVal != 0;
        }
        unsafe public bool ReadBoolean(int bits)
        {
            this.CheckLength(bits, DWORD_BITS);
            int retVal;
            this.Read((byte*)&retVal, bits);
            return retVal != 0;
        }

        unsafe public byte ReadByte()
        {
            byte retVal;
            this.Read((byte*)&retVal, BYTE_BITS);
            return retVal;
        }
        unsafe public byte ReadByte(int bits)
        {
            this.CheckLength(bits, BYTE_BITS);
            byte retVal;
            this.Read((byte*)&retVal, bits);
            return retVal;
        }

        unsafe public sbyte ReadSByte()
        {
            sbyte retVal;
            this.Read((byte*)&retVal, BYTE_BITS);
            return retVal;
        }
        unsafe public sbyte ReadSByte(int bits)
        {
            this.CheckLength(bits, BYTE_BITS);
            sbyte retVal;
            this.Read((byte*)&retVal, bits);
            return retVal;
        }

        unsafe public short ReadInt16()
        {
            short retVal;
            this.Read((byte*)&retVal, WORD_BITS);
            return retVal;
        }
        unsafe public short ReadInt16(int bits)
        {
            this.CheckLength(bits, WORD_BITS);
            short retVal;
            this.Read((byte*)&retVal, bits);
            return retVal;
        }

        unsafe public ushort ReadUInt16()
        {
            ushort retVal;
            this.Read((byte*)&retVal, WORD_BITS);
            return retVal;
        }
        unsafe public ushort ReadUInt16(int bits)
        {
            this.CheckLength(bits, WORD_BITS);
            ushort retVal;
            this.Read((byte*)&retVal, bits);
            return retVal;
        }

        unsafe public int ReadInt32()
        {
            int retVal;
            this.Read((byte*)&retVal, DWORD_BITS);
            return retVal;
        }
        unsafe public int ReadInt32(int bits)
        {
            this.CheckLength(bits, DWORD_BITS);
            int retVal;
            this.Read((byte*)&retVal, bits);
            return retVal;
        }

        unsafe public uint ReadUInt32()
        {
            uint retVal;
            this.Read((byte*)&retVal, DWORD_BITS);
            return retVal;
        }
        unsafe public uint ReadUInt32(int bits)
        {
            this.CheckLength(bits, DWORD_BITS);
            uint retVal;
            this.Read((byte*)&retVal, bits);
            return retVal;
        }

        unsafe public long ReadInt64()
        {
            long retVal;
            this.Read((byte*)&retVal, QWORD_BITS);
            return retVal;
        }
        unsafe public long ReadInt64(int bits)
        {
            this.CheckLength(bits, QWORD_BITS);
            long retVal;
            this.Read((byte*)&retVal, bits);
            return retVal;
        }

        unsafe public ulong ReadUInt64()
        {
            ulong retVal;
            this.Read((byte*)&retVal, QWORD_BITS);
            return retVal;
        }
        unsafe public ulong ReadUInt64(int bits)
        {
            this.CheckLength(bits, QWORD_BITS);
            ulong retVal;
            this.Read((byte*)&retVal, bits);
            return retVal;
        }

        unsafe public char ReadChar()
        {
            char retVal;
            this.Read((byte*)&retVal, WORD_BITS);
            return retVal;
        }
        unsafe public char ReadChar(int bits)
        {
            this.CheckLength(bits, WORD_BITS);
            char retVal;
            this.Read((byte*)&retVal, bits);
            return retVal;
        }

        unsafe public byte[] ReadBytes(int bytes)
        {
            byte[] retVal = new byte[bytes];
            fixed (byte* dest = retVal)
            {
                this.Read(dest, BYTE_BITS * bytes);
            }
            return retVal;
        }
        unsafe public byte[] ReadBytes(int bytes, int byteBits)
        {
            this.CheckLength(byteBits, BYTE_BITS);
            byte[] retVal = new byte[bytes];
            fixed (byte* dest = retVal)
            {
                for (int i = 0; i < bytes; i++)
                    this.Read(&dest[i], byteBits);
            }
            return retVal;
        }

        unsafe public char[] ReadChars(int chars)
        {
            char[] retVal = new char[chars];
            fixed (char* dest = retVal)
            {
                for (int i = 0; i < chars; i++)
                    this.Read((byte*)&dest[i], BYTE_BITS);
            }
            return retVal;
        }
        unsafe public char[] ReadChars(int chars, int charBits)
        {
            this.CheckLength(charBits, WORD_BITS);
            char[] retVal = new char[chars];
            fixed (char* dest = retVal)
            {
                for (int i = 0; i < chars; i++)
                    this.Read((byte*)&dest[i], charBits);
            }
            return retVal;
        }

        /// <summary>
        /// Reads the specified number of characters.
        /// </summary>
        /// <param name="chars">Number of chars to read.</param>
        /// <param name="charBits">The number of bits to read per character (max 16.)</param>
        /// <returns>The characters read as a string.</returns>
        unsafe public string ReadString(int chars, int charBits)
        {
            return new String(this.ReadChars(chars, charBits));
        }

        /// <summary>
        /// Reads characters until the specified terminator character or maximum length is reached.
        /// </summary>
        /// <param name="charBits">The number of bits to read per character (max 16.)</param>
        /// <param name="terminator">The character marking the end of the string.</param>
        /// <param name="maxLength">The maximum number of characters to read (also the buffer size...)</param>
        unsafe public string ReadString(int charBits, char terminator, int maxLength)
        {
            this.CheckLength(charBits, WORD_BITS);
            char[] retVal = new char[maxLength];
            int i = 0;
            fixed (char* dest = retVal)
            {
                for (; i < maxLength; i++)
                {
                    this.Read((byte*)&dest[i], charBits);
                    if (dest[i] == terminator)
                        break;
                }
            }
            return new String(retVal, 0, i);
        }

        /// <summary>
        /// Reads 8 bit characters until a null character is reached.
        /// </summary>
        /// <returns>The characters read as a string, excluding the null character.</returns>
        unsafe public string ReadString()
        {
            return this.ReadString(BYTE_BITS, '\0');
        }
        /// <summary>
        /// Reads characters of the specified amount of bits until a null character is reached.
        /// </summary>
        /// <param name="charBits">The number of bits to read per character (max 16.)</param>
        /// <returns>The characters read as a string, excluding the terminator.</returns>
        unsafe public string ReadString(int charBits)
        {
            return this.ReadString(charBits, '\0');
        }
        /// <summary>
        /// Reads 8 bit characters until the specified terminator character is reached.
        /// </summary>
        /// <param name="terminator">The character marking the end of the string.</param>
        /// <returns>The characters read as a string, excluding the terminator.</returns>
        unsafe public string ReadString(char terminator)
        {
            return this.ReadString(BYTE_BITS, terminator);
        }
        /// <summary>
        /// Reads characters of the specified length until the specified terminator character is reached.
        /// </summary>
        /// <param name="charBits">The number of bits to read per character (max 16.)</param>
        /// <param name="terminator">The character marking the end of the string.</param>
        /// <returns>The characters read as a string, excluding the terminator.</returns>
        unsafe public string ReadString(int charBits, char terminator)
        {
            this.CheckLength(charBits, WORD_BITS);
            List<char> retVal = new List<char>();
            char dest;
            while (true)
            {
                this.Read((byte*)&dest, charBits);
                if (dest == terminator)
                    break;
                retVal.Add(dest);
            }
            return new String(retVal.ToArray());
        }

    }
}
#8
I corrected the gold, color and unidentified rare information, cleaned up info some more and added some field descriptions.

Ringo: that's too bad. I guess forums do as well for reference and your research threads helped me a lot to get started anyways...
#9
Oh yes that's it, thanks. Each bit indicates if there are bonuses for the next number of set items worn (starting at 2, up to 6.)

         SetItem:            Tal Rasha's Adjudication
         SetBonuses:         , , (Faster Cast Rate: 10), ,

1 mod for 4 items

         SetItem:            Tal Rasha's Howling Wind
         SetBonuses:         (Faster Cast Rate: 10), , , ,

1 mod for 2 items

         SetItem:            Tal Rasha's Horadric Crest

no mods

         SetItem:            Tal Rasha's Lidless Eye
         SetBonuses:         (+1 To Sorceress Skills), (Passive Fire Pierce: 15), (Passive Lightning Pierce: 15), (Passive Cold Mastery: 15),

1 mod for 2, 3, 4 and 5 items

         SetItem:            Tal Rasha's Fire-Spun Cloth
         SetBonuses:         (Armor Class: 60), (Faster Cast Rate: 10), , ,

1 mod for 2 and 3 items

I'll update the description later...
#10
That may be a problem  :-X

Did they have any claims of copyright infringement or did they actually patent the d2gs protocol, or what?
#11
Haven't seen 0x16 or 0x14 either...

I posted my completed ref in packet discussion forum since you said this thread was too old ^^

I'll update with a better version of bitfield values later...
#12
Unknowns:

  Unknown1
May be part of version... never seen those bits set.

  Runeword ID
The first 12 bits need to be shifted / added by a value depending on last 4 bits...
What is currently got is if second value is 5, subtract 26 is value is under 100, or 25.
Only seen one case where it wasn't 5 so I can't really say, but I tried a lot of runewords with which this works...

Also (more on these later):
A couple unknown action types, flags and other enum values.


(BYTE) Action
(BYTE) Length
(BYTE) Category
(DWORD) Item Unit ID

0x9D only:
  (BYTE) Owner Unit Type
  (DWORD) Owner Unit ID

(DWORD) Flags
(BYTE) Version
(2 bits) Unknown1
(3 bits) Destination

If Destination == 3 (Ground):
  (WORD) X
  (WORD) Y
Else:
  (4 bits) EquipmentLocation
  (4 bits) X
  (3 bits) Y
  (4 bits) Container

If Flags & Ear:
  (3 bits) Character Class
  (7 bits) Level
  (NULLSTRING*) Character Name
  END

(DWORD) Base Item Code

If Base Item == Gold
  (1 bit) Big Pile
  If Big Pile:
    (32 bits) Quantity
  Else:
    (12 bits) Quantity
  END

(3 bits) Used Sockets

If Flags & (SimpleItem | Gamble):
  END

(7 bits) Item Level
(4 bits) Item Quality

(1 bit) Has Graphic
If Has Graphic:
  (3 bits) Graphic

(1 bit) Has Color
If Has Color:
  (11 bits) Color

If Flags & Identified:
  Switch (Quality):
    Inferior:
      (3 bits) Inferior Prefix
    Superior:
      (3 bits) Superior Type
    Magic:
      (11 bits) Magic Prefix
      (11 bits) Magic Suffix
    Rare:
    Crafted:
      (8 bits) Rare Prefix
      (8 bits) Rare Suffix
    Set:
      (12 bits) Set ID
    Unique:
      (12 bits) Unique ID

If Quality == Rare || Crafted:
  For i=0; i<3; i++:
    (1 bit) Has Magic Prefix
    If Has Magic Prefix:
      (11 bits) Magic Prefix
    (1 bit) Has Magic Suffix
    If Has Magic Suffix:
      (11 bits) Magic Suffix
  Done

If Flags & Runeword:
  (16 bits) Runeword ID

If Flags & Personlized:
  (NULLSTRING*) Name

If BaseItem comes from Armor.txt:
  (ArmorClass saveBits) Armor Class + ArmorClass saveAdd

If BaseItem comes from Armor.txt or Weapon.txt:
  (MaxDurability savebits) Max Durability
  If Max Durability != 0:
    (Durability savebits) Durability

If Flags & Socketed:
  (NumSockets saveBits) Sockets

If BaseItem.Stackable:
  If BaseItem.Useable:
    (5 bits) Use
  (9 bits) Quantity

If Flags & Identified == 0:
  END

If Quality == Set:
  (5 bits) Set Bonuses

ReadStats

If Flag & Runeword:
  ReadStats

If Quality == Set:
  For i=0; i < 5; i++
    If SetBonuses & (1 << i):
      ReadStats
  Done


*NULLSTRING: 7 bits per character.


ReadStats:


Repeat:
(9 bits) Stat ID
If Stat Id == 0x1FF:
  BREAK

If SaveParamBits != -1:
  Switch (Stat):
    ChargedSkill:
      (6 bits) Skill Level
      (10 bits) Skill
      (8 bits) Current Charges
      (8 bits) Max Charges
    SkillOnAttack:
    SkillOnKill:
    SkillOnDeath:
    SkillOnStriking:
    SkillOnLevelUp:
    SkillOnGetHit:
      (6 bits) Skill Level
      (10 bits) Skill
      (SaveBits) % Chance
    SkillTabBonus:
      (3 bits) Skill Tab
      (3 bits) Character Class
      (10 bits) Unknown
      (SaveBits) Bonus
    Default:
      (SaveParamBits) Param
      (SaveBits) Value

Else:
  If Stat.OpBase == Level:
    (SaveBits) Value / (1 << stat.OpParam) per Level
  Else:
    Switch (Stat):
      MinDamagePercent:
      MaxDamagePercent:
        (SaveBits) Min
        (SaveBits) Max
      FireMinDamage:
      LightMinDamage:
      MagicMinDamage:
        (SaveBits) Min
        ((Stat.Index+1).SaveBits) Max
      ColdMinDamage:
        (SaveBits) Min
        (ColdMaxDamage.SaveBits) Max
        (ColdLength.SaveBits) Length
      PoisonMinDamage:
        (SaveBits) Min
        (PoisonMaxDamage.SaveBits) Max
        (PoisonLength.SaveBits) Length
      ReplenishDurability:
      ReplenishQuantity:
        (SaveBits) 1 in 100 / Value seconds
      Default:
        (SaveBits) Value - (SaveAdd != -1 ? SaveAdd : 0)
Done



Parsing stats:

Savebits and other "BaseStat" values associated with stat IDs come from itemstatcost.txt.

Another thing you may need to worry about is if the stat is signed or unsigned. For item stats, I don't think it would cause problems if you parse them all as signed, but that is not the case for all stats; some are unsigned and their value can be higher than a 32 signed integer will allow... the signed column in itemstatcost.txt determines if the stat is signed or not.


Fields description:

  Destination
I call it that instead of Location not to be confused with my Equipment Location enum or, more importantly, the Container and X, Y fields, which are not necessarly related. See there is not really a "now" in item action packets, but rather a before and an after: the destination is always where the item will end up, but the rest of the info may be about where it previously was, depending on action type.

E.g. picking an item from inventory means both removing it from there and adding to cursor:
Action:             RemoveFromContainer (5)
Destination:        Cursor (4)
Container:          Inventory (2)
X:                  1
Y:                  1

Values:
Unspecified  = 0
Equipment    = 1
Belt         = 2
Ground       = 3
Cursor       = 4
Item         = 6

If 0, the destination can be determined from the action type / and or container fields instead...
5 and 7 are unknown, if used...

  Base Item Code
This is a DWORD built from the 3 letters item codes, with space as the filler character. One could build it such:
For int i = 0; i < 4; i++:
  id |= (i < code.Length ? code[ i].CharCode : 0x20) << ( i*8 );

  Gold
If amount is larger than 4095, the Big Pile bit will be set and the value will be 32 bits in length instead of 12.
The game will only displays up to 9 characters (999 999 999, 30 bits) but will parse and show the first numbers only if it's longer...
The 32nd bit is the sign; the client will indeed display negative piles of gold.

  Durability
Even items marked NoDurability have one, it just normally isn't used...
0 Max Durability means the item is indestructible and doesn't have a current Durablity.

  Graphic
Set on items having a variable graphic like rings and charms.

  Color
Set on items having a variable color like some class specific items (sorceress orbs...)

  Set Bonuses
This is a bit field, each bit indicating if there are bonus stats for (offset + 2) items of the set worn.


Edit: Corrected set bonuses information.
Edit2: Added extra information and simplified description a bit.
Edit3: Corrected used sockets info: bits are present even if Socketed flag is not set...
Edit4: Corrected the gold, color and unidentified rare information, cleaned up info some more and added some field descriptions.
Edit5: Corrected Item Code and noted that quality specific info should only be read if item is identified.
#13
I don't know Delphi (and don't really want to learn it  :-\), but the project is interesting. I might want to help you guys sometime if you port to C/C++. With the current state of Bnet's servers, I'm sure we could do better  ::) Plus modding is fun, a server supporting extra stuff would be cool to work on. Anyhow in the mean time maybe I can help by helping to document the protocol and expected behavior... I'll take a look at it later. Good luck!
#14
Quote from: ShadowDancer on February 09, 2007, 06:46 AM
excuse my rooughness :( i only tryed to say that it was obsolete compared with the one posted by ringo.

The stuff from EoN may be obsolete compared to the one by Ringo, but the code I posted is my own and I still think it's a better way to parse it, and is not obsolete at all...

Quote from: ShadowDancer on February 09, 2007, 06:46 AM
anymode this thead is too old... Posted on: July 14, 2006, 09:53 AM

Well it's old, but then again most references / packet disscusions threads start in 2005 and still contain useful information... gotta work from what we have.

I'll take a look at your code later... I don't know Delphi and the code looks a bit messy, but I should be able to make some sense of it... I'll post mine later too.
#15
Oh, I misread Ringo's post, the code you quoted was his and he already had a pretty good start  :o
Yes the code posted on EoN had the location stuff wrong.

Anyhow it should still be useful to compare our code and build a reference... here are a few more of my current enum values:

Container:

        Equipment        = 0x00,
        Ground           = 0x01,
        Inventory        = 0x02,
        TraderOffer      = 0x04,
        ForTrade         = 0x06,
        Cube             = 0x08,
        Stash            = 0x0A,


NPC Buffer:

ArmorTab = 0x02,
ArmorTabBottom = 0x03,
WeaponTab1 = 0x04,
WeaponTab1Bottom = 0x05,
WeaponTab2 = 0x06,
MiscTab = 0x08,
MiscTabBottom = 0x09,


NPC Buffer and Container are from the same value which I call container in the above... I assume NPC buffer 0x07 would be WeaponTab2Bottom but I haven't been able to test this yet... Bottom here means the last 2 rows which don't fit in the 16x8 buffers...

ItemAction:

AddToGround = 0,
/// <summary>
  /// Only sent if item goes to cursor (packet 0x0A removes item from ground...)
  /// </summary>
GroundToCursor = 1,
DropToGround = 2,
OnGround = 3,
PutInContainer = 4,
RemoveFromContainer = 5,
Equip = 6,
/// <summary>
/// Sent for the equipped item when changing from a two handed weapon to a single handed weapon or vice versa.
/// The item must be equipped on the "empty" hand or a regular SwapBodyItem will be sent instead.
/// Empty hand meaning left hand if currently wearing a two handed weapon or the empty hand if wearing a single hand item.
/// The result will be the new item being equipped and the old going to cursor.
/// </summary>
IndirectlySwapBodyItem = 7,
Unequip = 8,
SwapBodyItem = 9,
AddQuantity = 0x0A,
AddToShop = 0x0B,
RemoveFromShop = 0x0C,
SwapInContainer = 0x0D,
PutInBelt = 0x0E,
RemoveFromBelt = 0x0F,
SwapInBelt = 0x10,
/// <summary>
/// Sent for the secondary hand's item going to inventory when changing from a dual item setup to a two handed weapon.
/// </summary>
AutoUnequip = 0x11,
  /// <summary>
  /// Sent along with a 0x9d type 0x08 packet...
  /// Also Item on cursor when entering game ?? MiscToCursor??
  /// </summary>
RemoveFromHireling = 0x12,
ItemInSocket = 0x13,
UNKNOWN1 = 0x14,
/// <summary>
  /// When inserting item in socket, for each potion that drops in belt when lower one is removed, etc.
/// </summary>
  UpdateStats = 0x15,
UNKNOWN2 = 0x16,
WeaponSwitch = 0x17,


ItemFlag:

        None              = 0,
        Equipped          = 1,
        // UNKNOWN        = 2,
        // UNKNOWN        = 4,
        InSocket          = 8,
        /// <summary>
        /// Not undentified, really... also set for items that cannot be identified.
        /// </summary>
        Identified        = 0x10,
        /// <summary>
        /// Has to do with aura / state change !?
        /// </summary>
        x20               = 0x20,
        SwitchedIn        = 0x40,
        SwitchedOut       = 0x80,
        Broken            = 0x100,
        // UNKNOWN        = 0x200,
        /// <summary>
        /// Set for Mana, Healing and Rejuvenation potions, but not always !?!
        /// </summary>
        Potion            = 0x400,
        Socketed          = 0x800,
        /// <summary>
        /// Can't be removed? Set for items equipped by Valkyrie...
        /// </summary>
        x1000             = 0x1000,
        /// <summary>
        /// This flag is sometimes set for items in npc buffers, quest items and items equipped by Valkyrie...
        /// </summary>
        x2000             = 0x2000,
        NotInSocket       = 0x4000,     // Illegal Equip ?
        // UNKNOWN        = 0x8000,
        /// <summary>
        /// Is a player's ear. Ear packets have a different structure...
        /// </summary>
        Ear               = 0x10000,
        /// <summary>
        /// Item a character started with (meaning the item worthless to resell.)
        /// </summary>
        StartItem         = 0x20000,
        //UNKNOWN         = 0x40000,
        //UNKNOWN         = 0x80000,
        //UNKNOWN         = 0x100000,
        /// <summary>
        /// Item that doesn't have an ILevel or stats.
        /// </summary>
        SimpleItem        = 0x200000,
        Ethereal          = 0x400000,
        Any               = 0x800000,     // Which means ??
        Personalized      = 0x1000000,
        /// <summary>
        /// Item a town folk is offering for gambling (same purpose as SimpleItem: no ILevel + extra info.)
        /// </summary>
        Gamble            = 0x2000000,
        Runeword          = 0x4000000,
        /// <summary>
        /// InducesTempStatusChange ??
        /// </summary>
        x8000000          = 0x8000000,


ItemQuality:

        NotApplicable = 0,
        Inferior = 1,
        Normal = 2,
        Superior = 3,
        Magic = 4,
        Set = 5,
        Rare = 6,
        Unique = 7,
        Crafted = 8,



Category:

Helm = 0,
Armor = 1,
      /// <summary>
      /// Most weapons, including Crossbows
      /// </summary>
Weapon = 5,
      /// <summary>
      /// Bows (not crossbows), sometimes shield (if equipped in LeftHand?)
      /// </summary>
Weapon2 = 6,
      /// <summary>
      /// Shields can some sometimes be Weapon2...
      /// </summary>
Shield = 7,
      /// <summary>
      /// Class specific items !?
      /// </summary>
      Special = 10,
      /// <summary>
      /// BaseMiscItems and gloves, boots...
      /// </summary>
Misc = 16,


I haven't been able to make much sense of this one...

Any corrections or additional info would be very welcome...