Valhalla Legends Forums Archive | Battle.net Bot Development | 9c // 9d packet breakdown

AuthorMessageTime
ShadowDancer
It comes from the first good discusion in edgeofnowhere about d2 hacking:

[code]

//common bits//
Bit(8) Packet ID (0x9c) (0x9D)
bit(8) Action ID
bit(8) Packet size
bit(8) Item Type
bit(32) Item ID

//9D only, owner ID / ownerAction//
bit(8) Owner Action
bit(32) Owner ID


// item flags //
bit(1) isQuestItem
bit(3) Unknown
bit(1) isIdentified
bit(3) Unknown*
bit(1) Illegal Inventory?*
bit(1) Unknown*
bit(1) isDuplicated?*
bit(1) isSocketed
bit(2) Unknown*
bit(1) Illegal Equip
bit(1) Unknown*
bit(1) Ear Structure
bit(1) Starter Item
bit(2) Unknown*
bit(1) Unknown*
bit(1) Simple Structure
bit(1) isEthereal
bit(1) Unknown*
bit(1) isInscribed
bit(1) Unknown*
bit(1) isRuneword


// item version //
bit(5) unknown*
bit(8) Item version
bit(2) unknown*


// item location //
bit(3) Item Location

if Item location <> 0x03
  bit(4) Position on body
  bit(4) Grid Column
  bit(4) Grid Row
  bit(3) Stored In
else
  bit(16) Location X
  bit(16) Location Y


// Item code
bit(32) Item Code

if Item code == "ear"
  bit(3) Class
  bit(7) Level
  bit(18) Name
end
else ......

* = seems to be true but not 100% sure

Items are usually encoded along the lines of: origin, id, [player info,] flags, location, item code, item info (sockets, quality, image, etc), item quality data, runeword data, personalization data, more item info (defense, durability, sockets, etc), mods.

The exceptions to the scheme seems to be:
1) Gambled items should stop decoding after reading the item code
2) Ears are decoded differently and is already known

After reading the item code, for items with the misc bit not set in the flags:
1) Read in 3 bits. If it is socketed, that is the total number of sockets
2) Read in 7 bits for the item level
3) Read in 4 bits for the item quality
4) Read in 1 bit. This tells you if the item has an image. If it has an image, read in 3 bits for the image number.
5) Read in 1 bit. This tells you if it has class specific data. If it does, read in 11 bits to get that info.

The above should get you synced up to read in data about the quality.

The 2nd section on item info is decoded as such:
1) If it comes from the armor file, read in 10 bits and subtract 10 to get the base defense.
2) If the item does not come from the misc item file (*), read in 8 bits for the base durability. If this is 0, it is indestructible. If it is not 0, read in 8 bits to get the current durability. Bows and throwing items have durability and WILL break if durability becomes 0 (try it out with ethereal javelins if you want).
3) If it is socketed, read 4 bits for the number of used sockets
4) If it is a set item, read in 5 bits to get the number of set bonuses associated with the set.
5) If it is stackable (from mpq file (*)):
5a) If it is useable (from mpq file) (*), read in 5 bits (I didn't check the value to find out what it might be)
5b) Read in 9 bits to get the quantity

4 and 5 might be reversed, but I don't think there are any set stackable items to test it with. This should get you synced up to start reading in the mods.

Mods are decoded along the lines of:
1) Read stat
1a) If stat is 0x1ff, go to 3. If not, read info related to stat
2) Go to 1.
3) If it is a set, repeat from 1 for the count indicated in the set bonuses field

That should get you to the end of the packet with less than 8 bits left in the packet.

Special stats:
STATS_ITEM_MAXDAMAGE_PERCENT (and probably STATS_ITEM_MINDAMAGE_PERCENT): encoded in 2 pairs of length "SaveBits". Probably for left/ right hand, primary/secondary weapon or something along those lines

Replenish stats: rate is given as 1 per 100/value seconds

Cold/poison duration: duration is given as value * 0.04 (*) seconds

Posion damage: rate is given as value / 10.25 (*) damage per second

STATS_FIREMINDAM, STATS_LIGHTMINDAM, STATS_MAGICMINDAM, STATS_COLDMINDAM, STATS_COLDMAXDAM, STATS_POISONMINDAM, STATS_POISONMAXDAM: require reading looping back to 1a using stat + 1 as the current stat. I'm guessing this was done to save 9 bits each stat.

Per level: ((level * value) >> PerLevelShift) - SaveAdd (better done with floats instead of integers so use division instead of shifts)

By time: "max value" : "min value" : "best time" (x : 10 : 2). Max/min values subtracted by 0x100. Times: 0 = daytime, 1 = dusk, 2 = nighttime, 3 = dawn. Best time = max value, worst time = min value, others = average of max + min

+Skill tab: encoded as "# of skills" : "skill tab" (x : 5) (x = remaining bits, calculated as SaveBits - used up bits (in this case, 5))

+Skill: encoded as "# of skills" : "skill" (x : 9)

+Skill on attack/striking/struck: "% chance" : "skill level" : "skill" (x : 5 : 9)

+Charges: "total charges" : "current charges" : "skill level" : "skill" (x : 8 : 5 : 9)

  /*************** Tables ***************/
  /***** Item actions *****
  0x00 - Lying on the ground (just dropped) (9c)
  0x01 - Picked up from ground to cursor (9c)
  0x02 - Dropped by Player (9c)
  0x03 - Lying on the ground (been lying there) (9c)
  0x04 - Moved in Cube/Inventory (9c)
  0x05 - Put onto cursor (9d)
  0x06 - Item on cursor was equipped (9d)
  0x08 - Removed from equipment slot (9d)
  0x09 - Item on cursor was swapped with item in equipment slot (9d)
  0x0b - Added to Shop/Gamble buffer (9c)
  0x0c - Removed from Shop/Gamble buffer (9c)
  0x0d - Item on Cursor was swapped with item in inventory (9c)
  0x0e - Put into belt (9c)
  0x0f - Removed from Belt (9c)
  0x10 - Item on Cursor was switched with item in belt (9c)
  0x12 - Item on cursor when entering game (9c)
  0x13 - Item is socketed into another (9d)
  0x15 - An item was just inserted into socket (9d)
  0x17 - Item on equipment slot was swapped with one from the second set of slots (9d)

  ***** Buffer IDs *****
  0x00 - Belt,equipment slots and merc
  0x0a - Stash
  0x10 - Ground
  0x20 - Inventory (there is also a part of this buffer which is used by NPCs (shop+gamble))
  0x30 - NPC Buffer (Shop + Gamble)
  0x40 - NPC Buffer (Shop + Gamble)
  0x50 - NPC Buffer (Shop + Gamble)
  0x60 - NPC Buffer (Shop + Gamble)
  0x80 - Cube (same thing here as in 0x20)

  ***** Item Qualities *****
  1 - Inferior
  2 - Normal
  3 - Superior
  4 - Magic
  5 - Set
  6 - Rare
  7 - Unique
  8 - Crafted

[/code]
July 14, 2006, 2:53 PM
Infamous
You forgot an important part

[code]
// Item code
bit(32) Item Code

if Item code == "ear"
  bit(3) Class
  bit(7) Level
  bit(18) Name
      end
  else

bit(3) Number of gems
bit(7) Drop level
bit(4) Quality
bit(1) Variable Graphic flag
bit(1) Class Info


//case Quality
  0x00 //crafted
      bit(8) RarePrefix
      bit(8) RareSuffix
  0x01 //inferior
      bit(3) iQualityType
  0x02 //Normal

  0x03 //Superior
      bit(3) iQualityType
  0x04 //Magic
      bit(3) unknown?
      bit(11) Magic Prefix
      bit(11) Magic Suffix
  0x05 //Set
      bit(12) Set ID
  0x06 //Rare
      bit(8) RarePrefix
      bit(8) RareSuffix
  0x07 //Unique
      bit(12) Unique ID
  //end case


if isRuneWord
  bit(16) Runeword ID

if isInscribed
  bit(15) Name

// item base
bit(11) Defence
bit(8) Max Durability
bit(9) Durability
bit(4) Number of sockets
bit(9) Quantity // arrows, throwing axe etc..

// item mods
bit(>) Property list
[/code]
July 14, 2006, 3:44 PM
ShadowDancer
no, it is there but in text format bcoz herzog_zwei have writed it better conditionated and explained... soo if u can edit and remove to not cause confusion :P

// excuse my spelling, i know it is really bad
July 14, 2006, 3:55 PM
Infamous
Pretty funny how you manged to take my code from EoN and herzog_zwei and placed it under one.. you should have kept the it the same..less confusing imo

btw heres the thread in EoN.

http://www.edgeofnowhere.cc/viewtopic.php?t=312208&start=15
July 14, 2006, 4:01 PM
ShadowDancer
ahhhhh you are RaMz? :D cool

I allways merge the info from long theads like it for my ref library :P
July 14, 2006, 4:06 PM
UserLoser
Excellent.  I think they banned herzog from here though over some stupid C++ discussion.  Not sure though...
July 15, 2006, 5:46 AM
Ringo
Hm, iv been doing alota research on this packet over the last few days, and i cant see how the item location is parseable from your code.
Depending on the event, depends on format of the location area of the bit fields, example:
[code]
    ItemFlag = Reader.ReadAsLong(32)
    Call Reader.SkipBits(10) 'unknown
    Select Case ItemEvent
        Case &H0, &H2, &H3 'FLOOR ITEM
            Position = Reader.ReadAsByte(3)
            LocalX = Reader.ReadAsInteger(16)
            LocalY = Reader.ReadAsInteger(16)
        Case &H4, &H5, &HD, &HE, &HF, &H10, &H15 'Stash item and belt item
            Call Reader.SkipBits(7)  'unknown 0x64/0x65
            LocalX = Reader.ReadAsByte(4) 'If belt then Belt slot
            LocalY = Reader.ReadAsByte(3)
            Position = Reader.ReadAsByte(4)
        Case &H6, &H8, &H9 'body item add/del/switch
            Position = Reader.ReadAsByte(3)
            Call Reader.SkipBits(3)
            LocalX = Reader.ReadAsByte(4)
            LocalY = Reader.ReadAsByte(4)
            Call Reader.SkipBits(4)
        Case Else
            Exit Sub
    End Select
[/code]
July 15, 2006, 6:35 AM
E.T.
If you still need this, here's how I do it (this comes right after the version byte):

[code]
            this.destination = (ItemDestination)ByteConverter.GetBits(data, ref pOffset, 5);

            if (this.destination == ItemDestination.Ground)
            {
                this.x = (ushort)ByteConverter.GetBits(data, ref pOffset, 16);
                this.y = (ushort)ByteConverter.GetBits(data, ref pOffset, 16);
                this.container = ItemContainer.Ground;
            }
            else
            {
                this.location = (EquipmentLocation)ByteConverter.GetBits(data, ref pOffset, 4);
                this.x = (ushort)ByteConverter.GetBits(data, ref pOffset, 4);
                this.y = (ushort)ByteConverter.GetBits(data, ref pOffset, 3);
                this.container = (ItemContainer)ByteConverter.GetBits(data, ref pOffset, 4);
            }
[/code]

[code]
    public enum ItemDestination
    {
        /// <summary>
        /// The item is going in the specified container
        /// </summary>
        Container    = 0,
        Equipment    = 4,
        Belt        = 8,
        Ground      = 0x0C,
        Cursor      = 0x10,
        Item        = 0x18,
    }
[/code]
February 9, 2007, 4:02 AM
ShadowDancer
Posted on: July 15, 2006, 01:35 AM

[QOUTE]
            if (this.destination == ItemDestination.Ground)
            {
                this.x = (ushort)ByteConverter.GetBits(data, ref pOffset, 16);
                this.y = (ushort)ByteConverter.GetBits(data, ref pOffset, 16);
                this.container = ItemContainer.Ground;
            }
            else
            {
                this.location = (EquipmentLocation)ByteConverter.GetBits(data, ref pOffset, 4);
                this.x = (ushort)ByteConverter.GetBits(data, ref pOffset, 4);
                this.y = (ushort)ByteConverter.GetBits(data, ref pOffset, 3);
                this.container = (ItemContainer)ByteConverter.GetBits(data, ref pOffset, 4);
            }
[/QUOTE]
imho useless...

[QUOTE]
    ItemFlag = Reader.ReadAsLong(32)
    Call Reader.SkipBits(10) 'unknown
    Select Case ItemEvent
        Case &H0, &H2, &H3 'FLOOR ITEM
            Position = Reader.ReadAsByte(3)
            LocalX = Reader.ReadAsInteger(16)
            LocalY = Reader.ReadAsInteger(16)
        Case &H4, &H5, &HD, &HE, &HF, &H10, &H15 'Stash item and belt item
            Call Reader.SkipBits(7)  'unknown 0x64/0x65
            LocalX = Reader.ReadAsByte(4) 'If belt then Belt slot
            LocalY = Reader.ReadAsByte(3)
            Position = Reader.ReadAsByte(4)
        Case &H6, &H8, &H9 'body item add/del/switch
            Position = Reader.ReadAsByte(3)
            Call Reader.SkipBits(3)
            LocalX = Reader.ReadAsByte(4)
            LocalY = Reader.ReadAsByte(4)
            Call Reader.SkipBits(4)
        Case Else
            Exit Sub <<< that is a problem
    End Select
[/QUOTE]

February 9, 2007, 7:29 AM
E.T.
Useless? I'd like to know why it's useless... you're doing almost the same as me but ignoring more bits. You're probably right about the first 5 bits being 2 separate info (or first 2 bits being part of version maybe?), but other than that... If you mean useless because it's in a different language, I'm sure Ringo can extract the offsets from my simple code...

I do a little post processing for convenience after that, but the important info is there.

I'll investigate the first bits and build a plain text reference to make it clearer.

Edit: here's first part of ref with the ItemDestination corrected:

(BYTE) Action
(BYTE) Length
(BYTE) Category
(DWORD) Item UID

For 0x9D only:
(BYTE) Owner UnitType
(DWORD) Owner UID

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

If Destination == 3 (Ground):
(WORD) X
(WORD) Y

Else:
(4 bits) EquipmentLocation
(4 bits) X
(3 bits) Y
(4 bits) Container


Destination:
[code]
Container    = 0,
Equipment    = 1,
Belt        = 2,
Ground      = 3,
Cursor      = 4,
Item        = 6,
[/code]

EquipmentLocation:
[code]
NotApplicable = 0,
Helm = 1,
Amulet = 2,
Armor = 3,
RightHand = 4,
LeftHand = 5,
RightHandRing = 6,
LeftHandRing = 7,
Belt = 8,
Boots = 9,
Gloves = 10,
RightHandSwitch = 11,
LeftHandSwitch = 12,
[/code]

EquipmentLocation will only be non-0 if applicable, in which case X will be the same value. But X is also defined for other actions so better get it from first spot to know without having to check action type...
Really it's just simpler like that, avoids relying on known action types and works for all I tested with...

But I'd appreciate constructive criticism. Just saying it's useless is not very nice or useful though.
February 9, 2007, 8:46 AM
E.T.
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:
[code]
        Equipment        = 0x00,
        Ground          = 0x01,
        Inventory        = 0x02,
        TraderOffer      = 0x04,
        ForTrade        = 0x06,
        Cube            = 0x08,
        Stash            = 0x0A,
[/code]

NPC Buffer:
[code]
ArmorTab = 0x02,
ArmorTabBottom = 0x03,
WeaponTab1 = 0x04,
WeaponTab1Bottom = 0x05,
WeaponTab2 = 0x06,
MiscTab = 0x08,
MiscTabBottom = 0x09,
[/code]

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:
[code]
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,
[/code]

ItemFlag:
[code]
        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,
[/code]

ItemQuality:
[code]
        NotApplicable = 0,
        Inferior = 1,
        Normal = 2,
        Superior = 3,
        Magic = 4,
        Set = 5,
        Rare = 6,
        Unique = 7,
        Crafted = 8,
[/code]


Category:
[code]
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,
[/code]

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

Any corrections or additional info would be very welcome...
February 9, 2007, 10:26 AM
ShadowDancer
[QUOTE]
Useless? I'd like to know why it's useless... you're doing almost the same as me but ignoring more bits. You're probably right about the first 5 bits being 2 separate info (or first 2 bits being part of version maybe?), but other than that... If you mean useless because it's in a different language, I'm sure Ringo can extract the offsets from my simple code...
[/QUOTE]

excuse my rooughness :( i only tryed to say that it was obsolete compared with the one posted by ringo.

anymode this thead is too old... Posted on: July 14, 2006, 09:53 AM

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

[bold]Sure :D [/bold]

[code]
procedure process_0x9C_0x9D();
type
    titemstats = record
    start : integer;
    itemstat:integer;
    param:string;
    paramint:integer;
    addparam:string;
    addparamint:integer;
    end;
    titemdec = array of titemstats;
    titem = array of titemdec;
var
    i,j,k,l:integer;
    s:string;
    tstr:array[0..10] of string;
    tinteger:array[0..10] of integer;
    lastreaded:integer;
    t:ttreenode;
    rcode:integer;

    keep1, keep2:boolean;
    mods:array[0..50] of dword;
    mpqitem:tvirtuallistitem;
    itype:integer;
    gitemlist:tvirtuallist;
    dontadd:boolean;
//    item:tvirtuallist;
    li,li2,li1:tvirtuallistitem;
    prefix, sufix:string;
    //when comes a +min
    readthemax:boolean;
    tfile:textfile;
    iwidth, iheight:integer;
    tmpstr:array[0..10] of string;
    icodeat:integer;

    item2:titem;

    gt:ttreeview;

    r,v:integer;
    itemmustbesaved:boolean;
    savetitle:string;
function inttobin(a:integer):string;
var
    w:longword;
    s:string;
    i:integer;
begin
w:=$01;
for i:=1 to 8 do
begin
  if a and w = w then
  begin
  s:=s+'1';
  end
  else
  s:=s+'0';
  w:=w * 2;
end;
result:=s;
end;

function readbits(bitstring:string; var from:integer; amount:integer):string;
begin
result:=copy(bitstring,from,amount);
inc(from,amount);
end;

function strbittostr(bstr:string; bitsaling:integer):string;
var j,i,k,q,a:integer;
    s:string;
begin
k:=0;
for i:=1 to (length(bstr) div bitsaling) do
begin
  q:=1;
  a:=0;
  for j:=1 to bitsaling do
  begin
  inc(k);
  if bstr[k]='1' then
    a:=a+q;
  q:=q*2;
  end;
  s:=s+chr(a);
end;
result:=s;
end;

function strbittoint(bstr:string):integer;
var j,i,k,q,a:integer;
begin
if length(bstr) > 32 then
  exit;
k:=0;
  q:=1;
  a:=0;
  for j:=1 to length(bstr) do
  begin
  inc(k);
  if bstr[k]='1' then
    a:=a+q;
  q:=q*2;
  end;
result:=a;
end;

var itemread:tvirtuallist;
    xli:tlistitem;

function strtoint(a:string):integer;
var
    c,d:integer;
begin
val(a,c,d);
if d = 0 then
result:=c
else
result:=0;
end;

function decquality(s:String):integer;
begin
result:=0;
s:=lowercase(s);
if s='crafted' then result:=0;
if s='inferior' then result:=1;
if s='normal' then result:=2;
if s='superior' then result:=3;
if s='magic' then result:=4;
if s='set' then result:=5;
if s='rare' then result:=6;
if s='unique' then result:=7;
end;

procedure add2(s,g,h:string);
var li:tvirtuallistitem;
begin
li:= itemread.find(0,s);
if li = nil then
li:=itemread.add;
li.strings[0]:=s;
li.strings[1]:=g;
li.strings[2]:=h;
end;

function rprop(s:string):string;
var li:tvirtuallistitem;
begin
li:=itemread.find(0,s);
if li<>nil then result:=li.strings[1];
end;

procedure readmod(id:integer);
var
    savebits:integer;
    saveparambits:integer;
    saveadd:integer;
    sendother:integer;
    callback:integer;
    extraparams:integer;
    op, opparam:integer;
    eparam, eparam2:integer;
    li2,li1:tvirtuallistitem;
begin
  li:=itemstats.find('ID',inttostr(id));
  if li <> nil then
  begin
    savebits:=strtoint(li.strings[itemstats.findcolumn(0,'Save Bits')]);
    saveparambits:=strtoint(li.strings[itemstats.findcolumn(0,'Save Param Bits')]);
    saveadd:=strtoint(li.strings[itemstats.findcolumn(0,'Save Add')]);
    sendother:=strtoint(li.strings[itemstats.findcolumn(0,'Send Other')]);
    callback:=strtoint(li.strings[itemstats.findcolumn(0,'fCallback')]);
    op:=strtoint(li.strings[itemstats.findcolumn(0,'op')]);
    opparam:=strtoint(li.strings[itemstats.findcolumn(0,'op param')]);

    if li.strings[itemstats.findcolumn(0,'read next')] = '1' then
    readthemax:=true
    else
    readthemax:=false;

    tmpstr[1]:=readbits(s,lastreaded,savebits);

    if saveparambits > 0 then
    begin
    readbits(s,lastreaded,saveparambits);
    end;
  end;
end;

procedure readmod2(id:integer);
var
    savebits:integer;
    saveparambits:integer;
    saveadd:integer;
    sendother:integer;
    callback:integer;
    extraparams:integer;
    op, opparam:integer;
    eparam, eparam2:integer;
    li2,li1:tvirtuallistitem;
begin
  li:=itemstats.find('ID',inttostr(id));
  if li <> nil then
  begin
    savebits:=strtoint(li.strings[itemstats.findcolumn(0,'Save Bits')]);
    saveparambits:=strtoint(li.strings[itemstats.findcolumn(0,'Save Param Bits')]);
    saveadd:=strtoint(li.strings[itemstats.findcolumn(0,'Save Add')]);
    sendother:=strtoint(li.strings[itemstats.findcolumn(0,'Send Other')]);
    callback:=strtoint(li.strings[itemstats.findcolumn(0,'fCallback')]);
    op:=strtoint(li.strings[itemstats.findcolumn(0,'op')]);
    opparam:=strtoint(li.strings[itemstats.findcolumn(0,'op param')]);

    if li.strings[itemstats.findcolumn(0,'read max')] = '1' then
    readthemax:=true
    else
    readthemax:=false;

//    if sendother > 0 then
  //  tmpstr[2]:=readbits(s,lastreaded,sendother);

    tmpstr[1]:=readbits(s,lastreaded,savebits);

    //add2('- mod '+li.strings[0]+' value ',inttostr(strbittoint(tmpstr[1])- saveadd),tmpstr[1]);

  item2[high(item2)][high(item2[high(item2)])].param:=tmpstr[1];
  item2[high(item2)][high(item2[high(item2)])].paramint:=strbittoint(tmpstr[1])- saveadd;
//    if sendother > 0 then
//    add2('- sendother',tmpstr[2],'');

    if saveparambits > 0 then
    begin
    tmpstr[2]:=readbits(s,lastreaded,saveparambits);
    item2[high(item2)][high(item2[high(item2)])].addparam:=tmpstr[2];
    item2[high(item2)][high(item2[high(item2)])].addparamint:=strbittoint(tmpstr[2]);
    //add2('- param  ',tmpstr[2],'');
    end;
  end;
end;

function checkmods(i:integer):boolean;
var pop:integer;
    g:String;
begin
result:=false;
pop:=lastreaded;
lastreaded:=i;
repeat
  readthemax:=false;
  g:=readbits(s,lastreaded,9);
  if ($1FF = strbittoint(g)) then
  if (lastreaded+8 >= length(s)) then
  begin
  result:=true;
  lastreaded:=pop;
  exit;
  end;
  if $1FF <> strbittoint(g) then
  begin
  readmod(strbittoint(g));
  end;
  if readthemax = true then
  begin
  readmod(strbittoint(g)+1);
  end;
until (lastreaded-1+8 >= length(s));
lastreaded:=pop;
end;

procedure addmods(lr:integer);
var pop:integer;
    k,l,i:integer;
begin
pop:=lastreaded;
lastreaded:=lr;
  l:=-1;
  for i:=0 to high(item2) do
  for k:=0 to high(item2[i]) do
  if item2[i][k].start=lr then
  begin
    l:=k;
    break;
  end;
  if l=-1 then
  begin
  add2('Optional mod list',inttostr(j),'');
  setlength(item2,high(item2)+2);
repeat
  readthemax:=false;
  tmpstr[0]:=readbits(s,lastreaded,9);
  setlength(item2[high(item2)],high(item2[high(item2)])+2);
  item2[high(item2)][high(item2[high(item2)])].start:=lastreaded;
  item2[high(item2)][high(item2[high(item2)])].itemstat:=strbittoint(tmpstr[0]);
  li:=nil;

  if $1FF <> strbittoint(tmpstr[0]) then
  readmod2(strbittoint(tmpstr[0]));

  if readthemax = true then
  readmod2(strbittoint(tmpstr[0])+1);

  if li<>nil then
  add2('Mod '+li.strings[0]+' ('+tmpstr[0]+')',item2[high(item2)][high(item2[high(item2)])].param+' '+item2[high(item2)][high(item2[high(item2)])].addparam,'');
  if (strbittoint(tmpstr[0]) = $1FF) then
  break;

until (lastreaded+8 >= length(s));
end;

lastreaded:=pop;
end;

procedure make_name();
var
    pre,suf:integer;
    prefix, suffix:string;
    i,j,k:integer;
    s:string;
begin
if mpqitem<>nil then
  s:=mpqitem.strings[0]
else
  exit;

  case strbittoint(rprop('Quality')) of
  0: //crafted
  begin
  end;
  01: //inferior
  begin
  end;
  02: //Normal
  begin
  end;
  03: //Superior
  begin
    s:='Superior '+s+'';
  end;
  04: //Magic
  begin
    if (strbittoint(rprop('Magic Prefix')) <> 0) and (strbittoint(rprop('Magic Prefix')) < magicprefix.count) then
    begin
    li:=magicprefix.items[strbittoint(rprop('Magic Prefix'))+1];
    prefix:=li.strings[0]+' ';
    end;

    if (strbittoint(rprop('Magic Suffix')) <> 0) and (strbittoint(rprop('Magic Suffix')) < magicsuffix.count) then
    begin
    li:=magicsuffix.items[strbittoint(rprop('Magic Suffix'))+1];
    sufix:=' '+li.strings[0];
    end;
    s:='Magic '+prefix+s+sufix;
  end;
  05: //Set
  begin
    s:='Set '+s+'';
  end;
  06: //Rare
  begin
    s:='Rare '+s+'';
  end;
  07: //Unique
  begin
    if (strbittoint(rprop('Unique ID')) <> 0) and (strbittoint(rprop('Unique ID')) < uniqueitems_txt.count) then
    begin
    li:=UniqueItems_ex_txt.items[strbittoint(rprop('Unique ID'))];
    li:=uniqueitems_txt.find('index',li.strings[0]);
    prefix:=li.strings[0]+' ';
    //get more data about this unique...
    end;
    s:='Unique '+prefix+' ('+s+') ';
  end;
  end;

if rprop('isIdentified') = '0' then
  s:=s+' [unidentified]';

if rprop('isEthereal') = '1' then
  s:='ETH >> '+s;

if debuglvl > 6 then
  s:=s+' {'+strbittostr(rprop('Item Code'),8)+'}';

add2('Item Name',s,'');
end;

procedure graphitem(gt:ttreeview);
var
    pre,suf:integer;
    prefix, suffix:string;
    i,j,k:integer;
    s:string;
begin
if bots[vlink].winpointer2 <>nil then
if (strbittoint(rprop('Action')) <> 4) and
    (strbittoint(rprop('Action')) <> 3) and
    (strbittoint(rprop('Action')) <> 2) and
    (strbittoint(rprop('Action')) <> 0)
    then
begin
    exit;
end;

if (strbittoint(rprop('Action')) = 4) and
    (strbittoint(rprop('Position')) <> 2) and
    (strbittoint(rprop('Position')) <> 4)
    then
  begin
  exit;
end;

t:=gt.Items.Add(nil,rprop('Item Name'));

gt.Items.AddChild(t,rprop('Action'));
//    t.strings[0]:='Magic '+prefix+' '+item.items[0].strings[0]+' '+sufix;
{
if high(item2)>0 then
begin
  for i:=0 to high(item2) do
  for j:=0 to high(item2[i]) do
  gt.Items.AddChild(t,inttostr(item2[i][j].itemstat));
end;
}
end;

procedure checksave();
var v,i,r,l,j:integer;
    li:tvirtuallistitem;
    s,g:string;
begin
if high(item2)>=0 then
begin
  for v:=0 to pick_by_mod.count-1 do
  begin
  li:=pick_by_mod.items[v];
  if (strbittoint(rprop('Quality')) = decquality(li.strings[pick_by_mod.findcolumn(0,'quality')])) then
  if (mpqitem.strings[gitemlist.findcolumn(0,'type')] = li.strings[pick_by_mod.findcolumn(0,'itype')]) or
      (li.strings[pick_by_mod.findcolumn(0,'itype')] = '')  then
  begin
    for i:=0 to high(item2) do
    begin
    l:=0;
    for r :=1 to 8 do
    begin
      s:=li.strings[pick_by_mod.findcolumn(0,'mod '+inttostr(r))];
      g:=li.strings[pick_by_mod.findcolumn(0,'mod param '+inttostr(r))];
      if s = '' then
      inc(l)
      else
      for j:=0 to high(item2[i]) do
      begin
      if (item2[i][j].itemstat=strtoint(s)) then
      begin
        if (g = '') or
        (item2[i][j].paramint=strtoint(g))
        then
        begin
        inc(l);
        break;
        end;
      end;
      end;
    end;
    if l = 8 then
    begin
      add2('Item must be saved','','');
      itemmustbesaved:=true;
      break;
    end;
    end;
  end;
  end;
end;
end;

var gstats:tstats;
    m1:integer;
begin
j:=dlen;

itemread:=tvirtuallist.create(3);

s:='';
tstr[0]:='';
for i:=0 to j-1 do
begin
  s:=s+inttobin(cbuf[i]);
end;

for i:=0 to high(mods) do
  mods[i]:=0;

for i:=1 to (j+1)*8 do
begin
  tstr[0]:=s[i]+tstr[0];
end;

if debuglevel = 9 then
begin
memo1.lines.add('+ <- 0');
memo1.lines.add(tstr[0]);
memo1.lines.add('0 -> +');
memo1.lines.add(s);

end;

lastreaded:=1;

add2('Packet message',readbits(s,lastreaded,8),'');
add2('Action',readbits(s,lastreaded,8),'');
add2('Item type',readbits(s,lastreaded,8),'');
add2('Data size',readbits(s,lastreaded,8),'');
add2('Item ID',readbits(s,lastreaded,32),'');

if strbittoint(rprop('Packet message')) = $9d then
begin
  add2('Action2',readbits(s,lastreaded,8),'');
  add2('User ID',readbits(s,lastreaded,32),'');
end;

dontadd:=false;
{
if strbittoint(rprop('Action')) = 11 then
dontadd:=true; //in trade
}
add2('isQuestItem',readbits(s,lastreaded,1),'');
add2('Unknow',readbits(s,lastreaded,3),'');
add2('isIdentified',readbits(s,lastreaded,1),'');
add2('Unknow2',readbits(s,lastreaded,3),'');
add2('IlegalInventory',readbits(s,lastreaded,1),'');
add2('Unknow3',readbits(s,lastreaded,1),'');
add2('isDuplicated',readbits(s,lastreaded,1),'');
add2('isSocketed',readbits(s,lastreaded,1),'');
add2('Unknow4',readbits(s,lastreaded,2),'');
add2('Illegal Equip',readbits(s,lastreaded,1),'');
add2('Unknow5',readbits(s,lastreaded,1),'');
add2('Ear Structure',readbits(s,lastreaded,1),'');
add2('Starter Item',readbits(s,lastreaded,1),'');
add2('Unknow6',readbits(s,lastreaded,2),'');
add2('Unknow7',readbits(s,lastreaded,1),'');
add2('Simple Structure',readbits(s,lastreaded,1),'');
add2('isEthereal',readbits(s,lastreaded,1),'');
add2('Unknow8',readbits(s,lastreaded,1),'');
add2('isInscribed',readbits(s,lastreaded,1),'');
add2('Unknow9',readbits(s,lastreaded,1),'');
add2('isRuneword',readbits(s,lastreaded,1),'');
add2('Unknow10',readbits(s,lastreaded,5),'');

//flags end here 32 bits

add2('Item version',readbits(s,lastreaded,8),'');
add2('Unknow11',readbits(s,lastreaded,2),'');

if bots[vlink].ig_currentstatus=$fb then
if strbittoint(rprop('Item ID')) = strtoint('$0'+bots[vlink].paramstack[0]) then
case strbittoint(rprop('Action'))  of
  $00, $02, $03:begin end;
  else
  begin
  bots[vlink].queue_step(1);
  end;
end;

case strbittoint(rprop('Action'))  of
  //FLOOR ITEM
  $00, $02, $03:
  begin
    add2('Position',readbits(s,lastreaded,3),'');
    add2('LocalX',readbits(s,lastreaded,16),'');
    add2('LocalY',readbits(s,lastreaded,16),'');
  end;
  //Stash item
  $04,
  $05,
  $0D,
  $0E,
  $0F,
  $10,
  $12, //item at cursor when beggin
  $13,
  $15,
  $17:
  begin
    add2('Unknow12',readbits(s,lastreaded,7),'');
    add2('LocalX',readbits(s,lastreaded,4),'');
    add2('LocalY',readbits(s,lastreaded,3),'');
    add2('Position',readbits(s,lastreaded,4),'');
  end;
  //body item add/del/switch
  $06, $08, $09:
  begin
    add2('Position',readbits(s,lastreaded,3),'');
    add2('Unknow13',readbits(s,lastreaded,3),'');
    add2('LocalX',readbits(s,lastreaded,4),'');
    add2('LocalY',readbits(s,lastreaded,4),'');
    add2('Unknow14',readbits(s,lastreaded,4),'');
  end;
  $0b:begin
    add2('Unknow12',readbits(s,lastreaded,7),'');
    add2('LocalX',readbits(s,lastreaded,4),'');
    add2('LocalY',readbits(s,lastreaded,3),'');
    add2('Position',readbits(s,lastreaded,4),'');
  end;
  else
  begin
  assignfile(tfile,'logs\failed.txt');
  if fileexists('logs\failed.txt') then
    append(tfile)
  else
    rewrite(tfile);
  writeln(tfile,s);
for i:=0 to itemread.count-1 do
begin
  write(tfile,itemread.items[i].strings[0]+' ');
  write(tfile,itemread.items[i].strings[1]+' ');
  write(tfile,inttostr(strbittoint(itemread.items[i].strings[1]))+' ');
  write(tfile,inttohex(strbittoint(itemread.items[i].strings[1]),4)+' ');
  writeln(tfile,strbittostr(itemread.items[i].strings[1],8)+' ');
end;
  closefile(tfile);
  memo1.lines.add('why end ?'+inttohex(strbittoint(rprop('Action')),2));
  exit;
  end;
end;

add2('Item Code',readbits(s,lastreaded,32),'');

icodeat:=lastreaded;

itype:=0;

mpqitem:=misc.find(misc.findcolumn(0,'code'),zstring(strbittostr(rprop('Item Code'),8),1));
  if mpqitem<>nil then
  begin
  itype:=1;
  gitemlist:=misc;
  end;

if mpqitem=nil then
begin
  mpqitem:=weapons.find(weapons.findcolumn(0,'code'),zstring(strbittostr(rprop('Item Code'),8),1));
  if mpqitem<>nil then
  begin
  itype:=2;
  gitemlist:=weapons;
  end;
end;
if mpqitem=nil then
begin
  mpqitem:=armors.find(armors.findcolumn(0,'code'),zstring(strbittostr(rprop('Item Code'),8),1));
  if mpqitem<>nil then
  begin
  itype:=3;
  gitemlist:=armors;
  end;
end;

if mpqitem=nil then
begin
  memo1.lines.add('******************** item is unknow!!!!');
  exit;
end;

add2('InvFile',mpqitem.strings[gitemlist.findcolumn(0,'invfile')],'');
{
if fileexists('S:\Diablo II\D2SysBot 2.5\PCXFiles\c_inv'+zstring(strbittostr(rprop('Item Code'),8),1)+'.pcx') then
add2('InvFile','S:\Diablo II\D2SysBot 2.5\PCXFiles\c_inv'+zstring(strbittostr(rprop('Item Code'),8),1)+'.pcx','');
}

// if mpqitem<>nil then
//  item.items[0].strings[0]:=mpqitem.strings[0];

if zstring(strbittostr(rprop('Item Code'),8),1) = 'ear' then
begin
//  bit(3) Class
//  bit(7) Level
//  bit(18) Name
memo1.lines.add('ear ... ');
exit;
end;

add2('Number of gems',readbits(s,lastreaded,3),'');
add2('Drop level',readbits(s,lastreaded,7),'');
add2('Quality',readbits(s,lastreaded,4),'');
add2('is Graphic Variable',readbits(s,lastreaded,1),'');

if rprop('is Graphic Variable') = '1' then
  add2('Image',readbits(s,lastreaded,3),'');

li1:=tables[tbl_itemtypes].find('Code',mpqitem.strings[gitemlist.findcolumn(0,'type')]);
if li1 = nil then
begin
  li1:=tables[tbl_itemtypes].find('Equiv1',mpqitem.strings[gitemlist.findcolumn(0,'type')]);
  if li1 = nil then
  begin
  li1:=tables[tbl_itemtypes].find('Equiv2',mpqitem.strings[gitemlist.findcolumn(0,'type')]);
  if li1 = nil then
  begin
    memo1.lines.add('******************** item type is unknow!!!!');
    exit;
  end;
  end;
end;

if strtoint(li1.strings[tables[tbl_itemtypes].findcolumn(0,'VarInvGfx')]) > 0 then
begin
  add2('InvFile',li1.strings[tables[tbl_itemtypes].findcolumn(0,'InvGfx'+inttostr(strbittoint(rprop('Image'))+1))],'');
end;

//¿? skip classinfo if misc
add2('have Class Info?',readbits(s,lastreaded,1),'');
if rprop('have Class Info?') = '1' then
  add2('Class info',readbits(s,lastreaded,11),'');

  case strbittoint(rprop('Quality')) of
  0: //crafted
  begin
    add2('RarePrefix',readbits(s,lastreaded,8),'');
    add2('RareSuffix',readbits(s,lastreaded,8),'');
  end;
  01: //inferior
  begin
    add2('iQualityType',readbits(s,lastreaded,3),'');
  end;
  02: //Normal
  begin
  end;
  03: //Superior
  begin
    add2('iQualityType',readbits(s,lastreaded,3),'');
  end;
  04: //Magic
  begin
    add2('Magic Prefix',readbits(s,lastreaded,11),'');
    add2('Magic Suffix',readbits(s,lastreaded,11),'');
  end;
  05: //Set
  begin
    add2('Set ID',readbits(s,lastreaded,12),'');
  end;
  06: //Rare
  begin
    add2('RarePrefix',readbits(s,lastreaded,8),'');
    add2('RareSuffix',readbits(s,lastreaded,8),'');

    for i:=1 to 3 do
    begin
    add2('Prefix '+inttostr(i)+' flag',readbits(s,lastreaded,1),'');
    if rprop('Prefix '+inttostr(i)+' flag') = '1' then
    add2('Prefix '+inttostr(i),readbits(s,lastreaded,11),'');

    add2('Suffix '+inttostr(i)+' flag',readbits(s,lastreaded,1),'');
    if rprop('Suffix '+inttostr(i)+' flag') = '1' then
    add2('Suffix '+inttostr(i),readbits(s,lastreaded,11),'');
    end;
  end;
  07: //Unique
  begin
    add2('Unique ID',readbits(s,lastreaded,12),'');

    if (strbittoint(rprop('Unique ID')) <> 0) and (strbittoint(rprop('Unique ID')) < uniqueitems_txt.count) then
    begin
    li1:=UniqueItems_ex_txt.items[strbittoint(rprop('Unique ID'))];
    li1:=uniqueitems_txt.find('index',li1.strings[0]);
    if li1.strings[uniqueitems_txt.findcolumn(0,'invfile')]<>'' then
    add2('InvFile',li1.strings[uniqueitems_txt.findcolumn(0,'invfile')],'');
    //get more data about this unique...
    end;

  end;
  end;
//fixme: If it is a set item, read in 5 bits to get the number of set bonuses associated with the set.

if rprop('isRuneword')='1' then
  add2('Runeword ID',readbits(s,lastreaded,16),'');

if itype = 3 then
  add2('Defense',readbits(s,lastreaded,11),'');

//If the item does not come from the misc item file, read in 8 bits for the base durability. If this is 0, it is indestructible. If it is not 0, read in 8 bits to get the current durability. Bows and throwing items have durability and WILL break if durability becomes 0 (try it out with ethereal javelins if you want).
if itype <> 1 then
begin
  //if mpqitem.strings[gitemlist.findcolumn(0,'nodurability')] <> '1' then
  begin
  add2('Duravility',readbits(s,lastreaded,8),'');
  if strbittoint(rprop('Duravility')) <> 0 then
  begin
  add2('r Duravility',readbits(s,lastreaded,8),'');
  add2('Unknow Flag',readbits(s,lastreaded,1),'');
  end
  else
  //item.add.strings[0]:=('Duravility: Indestructible');
  end;
end;

// add2('Unknow Flag',readbits(s,lastreaded,1),'');

//  If it is stackable (from mpq file (*)):
if (mpqitem.strings[gitemlist.findcolumn(0,'stackable')] = '1') then
begin
  //fixme:  If it is useable (from mpq file) (*), read in 5 bits (I didn't check the value to find out what it might be)
  //add2('Stack ',readbits(s,lastreaded,5),'');

  add2('Stack ',readbits(s,lastreaded,9),'');
end;

//If it is socketed, read 4 bits for the number of used sockets
if rprop('isSocketed') = '1' then
begin
  add2('Sockets Amount',readbits(s,lastreaded,4),'');
end;

if rprop('isInscribed')='1' then
  add2('Inscribed Name',readbits(s,lastreaded,15),'');


//runewords must skip first break
j:=0;
if rprop('isRuneword')='1' then j:=1;

if checkmods(lastreaded) then
begin
  add2('Checkmods '+inttostr(lastreaded),booltostr(checkmods(lastreaded),true),'');
  addmods(lastreaded);
end;

if checkmods(lastreaded-1) then
begin
  add2('Checkmods '+inttostr(lastreaded-1),booltostr(checkmods(lastreaded-1),true),'');
  addmods(lastreaded-1);
end;


if (strbittoint(rprop('Quality'))<>0) and
    (strbittoint(rprop('Quality'))<>2) and
    (high(item2)<0)
  then
begin
  if length(s)-icodeat < 200 then
  k:=length(s)-icodeat
  else
  k:=200;
  for j:=icodeat to icodeat+k do
  begin
  if checkmods(j) then
    addmods(j);
  end;
end;

if (strbittoint(rprop('Quality'))<>0) and (strbittoint(rprop('Quality'))<>2) and (high(item2)<=0) then
begin
{  assignfile(tfile,'logs\failed.txt');
  if fileexists('logs\failed.txt') then
    append(tfile)
  else
    rewrite(tfile);
  writeln(tfile,s);
for i:=0 to itemread.count-1 do
begin
  write(tfile,itemread.items[i].strings[0]+' ');
  write(tfile,itemread.items[i].strings[1]+' ');
  write(tfile,inttostr(strbittoint(itemread.items[i].strings[1]))+' ');
  write(tfile,inttohex(strbittoint(itemread.items[i].strings[1]),4)+' ');
  writeln(tfile,strbittostr(itemread.items[i].strings[1],8)+' ');
end;
  closefile(tfile);
  }
end;

{
    assignfile(tfile,'logs\failed.txt');
  if fileexists('logs\failed.txt') then
    append(tfile)
  else
    rewrite(tfile);
  writeln(tfile,s);
for i:=0 to itemread.count-1 do
begin
  write(tfile,itemread.items[i].strings[0]+' ');
  write(tfile,itemread.items[i].strings[1]+' ');
  write(tfile,inttostr(strbittoint(itemread.items[i].strings[1]))+' ');
  write(tfile,inttohex(strbittoint(itemread.items[i].strings[1]),4)+' ');
  writeln(tfile,strbittostr(itemread.items[i].strings[1],8)+' ');
end;
  closefile(tfile);
}

make_name();

if strbittoint(rprop('Action'))=$06 then
begin
  if (strbittoint(rprop('Duravility')) <> 0) and
    (strbittoint(rprop('r Duravility')) = 0) then
  bots[vlink].setvar('broken_body','1');
//  memo1.lines.add('body item '+rprop('Item Name')+' '+inttostr(strbittoint(rprop('Duravility')))+' '+inttostr(strbittoint(rprop('r Duravility'))));
end;

case strbittoint(rprop('Action'))  of
  //FLOOR ITEM
  $00, $02, $03:
  begin
  end;
  //Stash item
  $04, $0D, $0F, $010, $015:
  begin
  if high(item2) > -1 then
  begin
  m1:=high(item2[0])+1;
  setlength(gstats,m1);

for i:=0 to high(item2[0]) do
begin
  gstats[i].statid:=item2[0][i].itemstat;
  gstats[i].stat:=item2[0][i].paramint;
  gstats[i].stat_add:=item2[0][i].addparamint;
end;
  end;
    bots[vlink].inv_add(
    strbittostr(rprop('Item Code'),8),
    rprop('InvFile'),
    strtoint('0'+mpqitem.strings[gitemlist.findcolumn(0,'invwidth')]),
    strtoint('0'+mpqitem.strings[gitemlist.findcolumn(0,'invheight')]),
    strbittoint(rprop('LocalX')),
    strbittoint(rprop('LocalY')),
    strbittoint(rprop('Action')),
    strbittoint(rprop('Position')),
    strbittoint(rprop('Image')),
    strbittoint(rprop('Item ID')),
    rprop('Item Name'),
    gstats);

    case strbittoint(rprop('Position')) of
    10:begin
    end;
    2:begin

    iwidth:=strtoint('0'+mpqitem.strings[gitemlist.findcolumn(0,'invwidth')]);
    iheight:=strtoint('0'+mpqitem.strings[gitemlist.findcolumn(0,'invheight')]);

    for i:=0 to iwidth-1 do
    for j:=0 to iheight-1 do
      bots[vlink].back[strbittoint(rprop('LocalX'))+i,strbittoint(rprop('LocalY'))+j]:=strbittoint(rprop('Item ID'));

    if (zstring(strbittostr(rprop('Item Code'),8),1)='rvl') or
      (zstring(strbittostr(rprop('Item Code'),8),1)='mp4') or
      (zstring(strbittostr(rprop('Item Code'),8),1)='mp5')
    then
    begin
    //...
    end;
    end; //case 2 end
    end; //case  strbittoint(itemdecoded[29]) position in body
  end; // case $04, $0D, $0F, $010, $015 stash item

  $05:begin // unestash item
    for i:=0 to high(bots[vlink].back) do
    for j:=0 to high(bots[vlink].back[i]) do
      if strbittoint(rprop('Item ID')) = bots[vlink].back[i,j] then
      bots[vlink].back[i,j]:=0;
  end;

  $0E: // belt item
  begin
  end;
  //body item add/del/switch
  $06, $08, $09:
  begin
  end;
  $0b:begin
  end;
  else
  begin
  exit;
  end;
end;

///////////// bothing //////////////
if strbittoint(rprop('Action'))=$15 then
if strbittoint(rprop('isIdentified'))=1 then
begin
  itemmustbesaved:=false;

{
  tlist:=tvirtuallist.create(1);

  for i:=1 to actionsqueue.count-1 do
    tlist.add.strings[0]:=actionsqueue.items[i].strings[0];

  for i:=1 to actionsqueue.count-1 do
    actionsqueue.delete(1);
}

    case strbittoint(rprop('Quality')) of
  0: //crafted
  begin
    itemmustbesaved:=true;
  end;
  01: //inferior
  begin
    itemmustbesaved:=true;
  end;
  02: //Normal
  begin
    itemmustbesaved:=true;
  end;
  03: //Superior
  begin
    itemmustbesaved:=true;
  end;
  04: //Magic
  begin

    li1:=Saved_MagicPrefix.items[strbittoint(rprop('Magic Prefix'))+1];
    k:=0;
    for j:=1 to 7 do
    if li1.strings[saved_magicprefix.findcolumn(0,'itype'+inttostr(j))] = mpqitem.strings[gitemlist.findcolumn(0,'type')] then
      k:=1;
    if (k = 1) and
      (li1.strings[saved_magicprefix.findcolumn(0,'Save')] = '1')
      then
    begin
    itemmustbesaved:=true;
    end;

    li1:=Saved_MagicSuffix.items[strbittoint(rprop('Magic Suffix'))+1];
    k:=0;
    for j:=1 to 7 do
    if li1.strings[saved_MagicSuffix.findcolumn(0,'itype'+inttostr(j))] = mpqitem.strings[gitemlist.findcolumn(0,'type')] then
      k:=1;
    if (k = 1) and
      (li1.strings[saved_MagicSuffix.findcolumn(0,'Save')] = '1')
      then
    begin
    itemmustbesaved:=true;
    end;

  end; // case 0x04 ends

  5:begin //set items
    itemmustbesaved:=true; //if set items are collected then....
  end; //case 0x05 ends

  7:begin // unique items
    itemmustbesaved:=true;
    if (strbittoint(rprop('Unique ID')) <> 0) and (strbittoint(rprop('Unique ID')) < uniqueitems_txt.count) then
    begin
    li:=UniqueItems_ex_txt.items[strbittoint(rprop('Unique ID'))];
    li:=uniqueitems_autodrop.find('index',li.strings[0]);
    if li.strings[UniqueItems_autodrop.findcolumn(0,'Drop')] = '1' then
    begin
      itemmustbesaved:=false;
    end;
    end;
  end;
  end; // end of case

  checksave();

  s:=mpqitem.strings[0];
  case strbittoint(rprop('Quality')) of
  0: //crafted
  begin
  end;
  01: //inferior
  begin
  end;
  02: //Normal
  begin
  end;
  03: //Superior
  begin
    s:='Superior '+s+'';
  end;
  04: //Magic
  begin
    if (strbittoint(rprop('Magic Prefix')) <> 0) and (strbittoint(rprop('Magic Prefix')) < magicprefix.count) then
    begin
    li:=magicprefix.items[strbittoint(rprop('Magic Prefix'))+1];
    prefix:=li.strings[0]+' ';
    end;

    if (strbittoint(rprop('Magic Suffix')) <> 0) and (strbittoint(rprop('Magic Suffix')) < magicsuffix.count) then
    begin
    li:=magicsuffix.items[strbittoint(rprop('Magic Suffix'))+1];
    sufix:=' '+li.strings[0];
    end;
    s:='Magic '+prefix+' '+s+' '+sufix+' ('+inttostr(strbittoint(rprop('Magic Prefix')))+','+inttostr(strbittoint(rprop('Magic Suffix')))+')';
  end;
  05: //Set
  begin
    s:='Set '+s+'';
  end;
  06: //Rare
  begin
    s:='Rare '+s+'';
  end;
  07: //Unique
  begin
    if (strbittoint(rprop('Unique ID')) <> 0) and (strbittoint(rprop('Unique ID')) < uniqueitems_txt.count) then
    begin
    li:=UniqueItems_ex_txt.items[strbittoint(rprop('Unique ID'))];
    li:=uniqueitems_txt.find('index',li.strings[0]);
    prefix:=li.strings[0]+' ';
    end;

    s:='Unique '+prefix+' ('+s+') ';
  end;
  end;

  if itemmustbesaved = false then
  begin
  assignfile(tfile,'logs\dropeds.txt');
  if fileexists('logs\dropeds.txt') then
    append(tfile)
  else
    rewrite(tfile);


  writeln(tfile,s);

  if strbittoint(rprop('Quality')) =6 then
if high(item2)>=0 then
begin
  s:=inttohex(cbuf[0],2);
  for i:=1 to dlen-1 do
  s:=s+' '+inttohex(cbuf[i],2);
  writeln(tfile,'packet: '+s);
  for i:=0 to high(item2) do
  begin
  writeln(tfile,'----- mods -----');
  for j:=0 to high(item2[i]) do
    writeln(tfile,inttostr(item2[i][j].itemstat)+' '+item2[i][j].param);
  end;
end;

  closefile(tfile);
  if bots[vlink].getvar('sell_garbage') = 'true' then
  begin
    bots[vlink].interruptsqueue.add.strings[0]:='setvar last_picked_id '+inttohex(strbittoint(rprop('Item ID')),8);
    bots[vlink].interruptsqueue.add.strings[0]:='pick_to_cursor '+inttohex(strbittoint(rprop('Item ID')),8);
    bots[vlink].interruptsqueue.add.strings[0]:='sell_item last_picked_id';
    bots[vlink].interruptsqueue.add.strings[0]:='delay 250';
  end
  else
  begin
    bots[vlink].interruptsqueue.add.strings[0]:='setvar last_picked_id '+inttohex(strbittoint(rprop('Item ID')),8);
    bots[vlink].interruptsqueue.add.strings[0]:='pick_to_cursor '+inttohex(strbittoint(rprop('Item ID')),8);
    bots[vlink].interruptsqueue.add.strings[0]:='item_at_cursor_to_floor last_picked_id';
    bots[vlink].interruptsqueue.add.strings[0]:='delay 250';
  end;
  end
  else
  begin
  memo1.lines.add('item must be saved: '+s);
  if strbittoint(rprop('Position')) = 2 then
    dxwavelist1.Items.Find('ICEQuery.wav').Play(false);
  end;
{
  for i:=0 to tlist.count-1 do
    actionsqueue.add.strings[0]:=tlist.items[i].strings[0];

  tlist.destroy;
}
end;

//pickit
s:=rprop('Action');
case strbittoint(s) of
  0, 2, 3:begin
  s:=zstring(strbittostr(rprop('Item Code'),8),1);
  j:=gitemlist.findcolumn(0,'type');
for i:=0 to pickup_txt.count - 1 do
begin
  li2:=pickup_txt.items[i];
  if ((pickup_txt.items[i].strings[1]=s) and (pickup_txt.items[i].strings[1]<>'')) or
    ((li2.strings[4] = mpqitem.strings[j]) and
    (li2.strings[4] <> '')) then
  begin

  k:=1;

  //quality
  if (li2.strings[5] <> '') and
      (decquality(li2.strings[5]) <> strbittoint(rprop('Quality'))) then
    k:=0;
      //ethereal
  if (strtoint('$0'+li2.strings[8]) <> strbittoint(rprop('isEthereal'))) then
    k:=0;
      //identified
  if (strtoint('$0'+li2.strings[3]) <> strbittoint(rprop('isIdentified'))) then
    k:=0;

      //socketed items
  if ((li2.strings[7] <> '') and
      (strtoint('$0'+li2.strings[7]) <> strbittoint(rprop('Sockets Amount')))) then
      k:=0;

    if k = 1 then
    begin

  if strtoint('0'+li2.strings[2]) <> 0 then
  begin
  assignfile(tfile,'logs\itemslog.txt');
  if fileexists('logs\itemslog.txt') then
    append(tfile)
  else
    rewrite(tfile);
  writeln(tfile,pickup_txt.items[i].strings[0]);
  closefile(tfile);
  memo1.lines.add(li2.strings[0]);
  end;
    s:=rprop('Item ID');
    k:=strbittoint(s);

    if bots[vlink].getvar('instant_pick') = 'true' then
    begin
      bots[vlink].run_command('telehackt to location '+inttohex(strbittoint(rprop('LocalX')),4)+' '+inttohex(strbittoint(rprop('LocalY')),4));
      bots[vlink].run_command('pick id '+inttohex(k,8));
    end
    else
    begin
      bots[vlink].interruptsqueue.add.strings[0]:='walk to location '+inttohex(strbittoint(rprop('LocalX')),4)+' '+inttohex(strbittoint(rprop('LocalY')),4);
      bots[vlink].interruptsqueue.add.strings[0]:='pick id '+inttohex(k,8);
    end;
    break;
    end;
  end;
end;
  end;
end;

if (itemslist.find(0,inttohex(strbittoint(rprop('Item ID')),8)) = nil) then
if (bots[vlink].ig_currentstatus <> 5) and
    (bots[vlink].ig_currentstatus <> 6) and
    (strbittoint(rprop('Action')) <> 11) then
    begin
    if bots[vlink].winpointer2 <>nil then
      gt:=bots[vlink].winpointer2.treeview1
    else
      gt:=treeview1;
    graphitem(gt);
    end;
s:=rprop('Action');
if (strbittoint(s) = 11) then
begin
  if (strbittoint(rprop('Magic Prefix')) <> 0) and (strbittoint(rprop('Magic Prefix')) < magicprefix.count) then
  begin
    li:=magicprefix.items[strbittoint(rprop('Magic Prefix'))+1];
    prefix:=li.strings[0]+' ';
  end;

  if (strbittoint(rprop('Magic Suffix')) <> 0) and (strbittoint(rprop('Magic Suffix')) < magicsuffix.count) then
  begin
    li:=magicsuffix.items[strbittoint(rprop('Magic Suffix'))+1];
    sufix:=' '+li.strings[0];
  end;

  assignfile(tfile,'logs\itemslog.txt');
  if fileexists('logs\itemslog.txt') then
    append(tfile)
  else
    rewrite(tfile);

  writeln(tfile,
    inttohex(strbittoint(rprop('Item ID')),4)+' '+
    prefix+
    mpqitem.strings[0]+
    sufix+' ('+inttostr(strbittoint(rprop('Magic Prefix')))+','+inttostr(strbittoint(rprop('Magic Suffix')))+')');
  closefile(tfile);

  inc(seenitems);
  label15.caption:='items seen: '+inttostr(seenitems);
// if zstring(strbittostr(rprop('Item Code'),8),1) = edit9.Text then
  for j:=0 to listbox1.Items.Count-1 do
  begin
  stringstack[0]:=listbox1.items.strings[j];
  stringstack[1]:=zstring(stringstack[0],1);
  stringstack[2]:=zstring(stringstack[0],2);
  val(stringstack[1],i,rcode);
  if (rcode=0) and
      (strbittoint(rprop('Magic Prefix')) = i) then
  begin
    val(stringstack[2],i,rcode);
    if (stringstack[2] = '*') or
      ((rcode=0) and (strbittoint(rprop('Magic Suffix')) = i))
    then
    begin
{
    memo4.Clear;

    for i:=0 to item.count-1 do
      memo4.Lines.Add(item.items[i].strings[0]);
}
    dec_ig_actionsstack(vlink,bots[vlink].actionsqueue.count);
    dxwavelist1.Items.Find('ICEQuery.wav').Play(false);
    end;
  end;
  end;
end;

if bots[vlink].ig_currentstatus = $F1 then
begin
  if ((strbittoint(rprop('Item ID')) = strtoint('$0'+bots[vlink].paramstack[0])))
    and (strbittoint(rprop('Action')) = 05) then
  begin
  assignfile(tfile,'logs\study1.txt');
  if fileexists('logs\study1.txt') then
    append(tfile)
  else
    rewrite(tfile);
  writeln(tfile,pickup_txt.items[i].strings[0]);
  closefile(tfile);
  end;
  if (strbittoint(rprop('Action')) = 05) then
  begin
  //item to cursor
  bots[vlink].queue_step(1);
  end;
end;

if (strbittoint(rprop('Action')) = 08) then
  if bots[vlink].ig_currentstatus = $F6 then
  bots[vlink].queue_step(1);

if (strbittoint(rprop('Action')) = 02) then
  if bots[vlink].ig_currentstatus = $Fc then
  if (strbittoint(rprop('Item ID')) = strtoint('$0'+bots[vlink].paramstack[0])) then
    bots[vlink].queue_step(1);

if (strbittoint(rprop('Action')) = 04) then
begin
  //item to cursor
  if bots[vlink].ig_currentstatus = $F3 then
  bots[vlink].queue_step(1);

  //do_cube
  if bots[vlink].ig_currentstatus = $F4 then
  begin
  bots[vlink].ig_lastcubed:=strbittoint(rprop('Item ID'));
  bots[vlink].queue_step(1);
  end;
end;

if (strbittoint(rprop('Action')) = 11) then
begin
  //do_cube
  if bots[vlink].ig_currentstatus = $F4 then
  bots[vlink].queue_step(1);

if (bots[vlink].ig_currentstatus = 5) or
    (bots[vlink].ig_currentstatus = 6) then
begin //is in cube

  if (strbittoint(rprop('Quality')) = 7) then
  begin //is unique

      dwordstack[0]:=strbittoint(rprop('Item ID'));
      bots[vlink].ig_currentstatus:=9;
      bots[vlink].ig_state_9_p[1]:=dwordstack[0];
      d2g_sendpacket_0x19(socket,dwordstack[0]);
      d2g_sendpacket_0x4f(socket, $17, $00);

      dxwavelist1.Items.find('Beep.wav').Play(false);
  end;

  if (strbittoint(rprop('Quality')) = 4) then
  begin //is magic
    if (bots[vlink].ig_currentstatus = 5) then
    begin
    keep1:=false; keep2:=false;
    for i:=0 to high(bots[vlink].ig_findprefix) do
      if (bots[vlink].ig_findprefix[i] = strbittoint(rprop('Magic Prefix'))) then
        keep1:=true;
    for i:=0 to high(bots[vlink].ig_findsufix) do
      if (bots[vlink].ig_findsufix[i] = strbittoint(rprop('Magic Suffix'))) then
        keep2:=true;
    if mods[7] <> 50 then
      keep2:=false;
    if keep1 and keep2= false then
      d2g_sendpacket_0x4f(socket,$18,$00)
    else
    begin
      dwordstack[0]:=strbittoint(rprop('Item ID'));
      bots[vlink].ig_currentstatus:=9;
      bots[vlink].ig_state_9_p[1]:=dwordstack[0];
      d2g_sendpacket_0x19(socket,dwordstack[0]);
      d2g_sendpacket_0x4f(socket, $17, $00);

      dxwavelist1.Items.find('Beep.wav').Play(false);
    end;
    end;

    if bots[vlink].ig_currentstatus = 6 then
    begin
    li1:=itemslist.find(0,'box');
    if li1<>nil then
      dwordstack[3]:=strtoint('$'+li1.strings[1]);

    li1:=playerslist.Find(0,inttohex(bots[vlink].ig_id,8));
    if li1 <> nil then
    begin
      wordstack[0]:=strtoint('$'+zfield(li1.Strings[3],',',1,false));
      wordstack[1]:=strtoint('$'+zfield(li1.Strings[3],',',2,false));
    end;

    d2g_sendpacket_0x20(socket, dwordstack[3], wordstack[0], wordstack[1]);

    bots[vlink].ig_currentstatus:=7;

    // d2g_sendpacket_0x4F(socket, $18, $00);

    // bots[vlink].ig_currentstatus:=5; //rerolling for a prefix
    end;
  end; //is magic end
  end; //status is 4 or 5
end; //is in cube end if

  li1:=itemslist.find(1,inttohex(strbittoint(rprop('Item ID')),8));
  if (li1 = nil) then
    li1:=itemslist.add;

  li1.strings[0]:=zstring(strbittostr(rprop('Item Code'),8),1); // code
  li1.strings[1]:=inttohex(strbittoint(rprop('Item ID')),8); // item id
  li1.strings[2]:=inttohex(strbittoint(rprop('Position')),2); // item id
  li1.strings[4]:=s;
  li1.strings[5]:=inttohex(strbittoint(rprop('Action')),2); // action
  li1.strings[6]:=inttohex(strbittoint(rprop('isIdentified')),2); //identified
  li1.strings[7]:=inttohex(strbittoint(rprop('Quality')),2); //quality
  li1.strings[8]:=mpqitem.strings[gitemlist.findcolumn(0,'type')]; //quality
  //9 is used ^^
  li1.strings[10]:=savetitle;
  case strbittoint(rprop('Quality')) of
  0: //crafted
  begin
  end;
  01: //inferior
  begin
  end;
  02: //Normal
  begin
  end;
  03: //Superior
  begin
  end;
  04: //Magic
  begin
    li1.strings[9]:=inttohex(strbittoint(rprop('Magic Prefix')),4)+' '+inttohex(strbittoint(rprop('Magic Suffix')),4);
  end;
  05: //Set
  begin
  end;
  06: //Rare
  begin
  end;
  07: //Unique
  begin
    li1.strings[9]:=inttohex(strbittoint(rprop('Unique ID')),4);
  end;
  end;

setlength(item2,0);
end;
[/code]
February 9, 2007, 12:46 PM
E.T.
[quote author=ShadowDancer link=topic=15398.msg164401#msg164401 date=1171025191]
excuse my rooughness :( i only tryed to say that it was obsolete compared with the one posted by ringo.
[/quote]

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 author=ShadowDancer link=topic=15398.msg164401#msg164401 date=1171025191]
anymode this thead is too old... Posted on: July 14, 2006, 09:53 AM
[/quote]

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.
February 9, 2007, 3:17 PM
Ringo
[quote]
            Exit Sub <<< that is a problem
[/quote]
Why? :)


Its been awhile, and I have made so many d2 bots todate, so im not sure what project the code I posted before came from, but the first item location parser I came to seems more compleat:
[code]
    Select Case ItemEvent
        Case &H0, &H2, &H3 'FLOOR ITEM
            Position = Reader.ReadAsByte(3)
            LocalX = Reader.ReadAsInteger(16)
            LocalY = Reader.ReadAsInteger(16)
        Case &H4, &H5, &HD, &HB, &HC 'Stash item/NPC Item add-rem
            UnKnBin(0) = Reader.ReadAsBinary(2) 'unknown
            ItemToMouse = Reader.ReadAsByte(1) 'boolean, the item's destination is to mouse
            UnKnBin(1) = Reader.ReadAsBinary(4)
            LocalX = Reader.ReadAsByte(4)
            LocalY = Reader.ReadAsByte(3)
            Position = Reader.ReadAsByte(4)
        Case &HE, &HF, &H10, &H15 'Belt item add/del/switch/swap
            UnKnBin(0) = Reader.ReadAsBinary(2) 'unknown
            ItemToMouse = Reader.ReadAsByte(1) 'boolean, the item's destination is to mouse
            UnKnBin(1) = Reader.ReadAsBinary(4)
            LocalX = Reader.ReadAsByte(4)
            LocalY = Reader.ReadAsByte(3)
            Position = Reader.ReadAsByte(4)
        Case &H6, &H8, &H9, &H17 'body item add/del/switch/swap
            Position = Reader.ReadAsByte(3)
            UnKnBin(0) = Reader.ReadAsBinary(3)
            LocalX = Reader.ReadAsByte(4)
            LocalY = Reader.ReadAsByte(4)
            UnKnBin(1) = Reader.ReadAsBinary(4)
        Case &H1, &H12 'ground item to buffer/in buffer on join
            Position = Reader.ReadAsByte(4)
            LocalX = Reader.ReadAsByte(4) 'null
            LocalY = Reader.ReadAsByte(4) 'null
            UnKnBin(0) = Reader.ReadAsBinary(6) 'unknown (null)
        Case &H13 'item in a socket
            Position = Reader.ReadAsByte(4)
            UnKnBin(0) = Reader.ReadAsBinary(3)
            LocalX = Reader.ReadAsByte(4)
            LocalY = Reader.ReadAsByte(4)
            UnKnBin(1) = Reader.ReadAsBinary(3)
        Case Else
            UnKnBin(0) = Reader.ReadAsBinary(200)
            ParseItemLocation = False
            Exit Function
    End Select
    ParseItemLocation = True
[/code]
February 9, 2007, 4:49 PM
ShadowDancer
[QUOTE=Ringo]Why? :)[/QUOTE]

B'coz i don't just pick the items.

I know that nobody will read that code, it was a joke... I will document it later. ;D


Shadow Dancer.-
February 9, 2007, 8:04 PM
Ringo
[quote author=ShadowDancer link=topic=15398.msg164412#msg164412 date=1171051488]
[QUOTE=Ringo]Why? :)[/QUOTE]

B'coz i don't just pick the items.
[/quote]
Well, I posted it for the item loction bitfield format. But even so, its still a good idea to stop the parseing from continueing on unresearched/unsupported events, so you can analyze the data. :P
February 9, 2007, 8:35 PM
ShadowDancer
[quote author=Ringo link=topic=15398.msg164414#msg164414 date=1171053344]
[quote author=ShadowDancer link=topic=15398.msg164412#msg164412 date=1171051488]
[QUOTE=Ringo]Why? :)[/QUOTE]

B'coz i don't just pick the items.
[/quote]
Well, I posted it for the item loction bitfield format. But even so, its still a good idea to stop the parseing from continueing on unresearched/unsupported events, so you can analyze the data. :P

[/quote]

Yes, and is better if you save the code to a file. (?) xD

My bot haven't seen the 0x16 action, do you know what is it for?
February 9, 2007, 10:20 PM
E.T.
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...
February 13, 2007, 12:18 AM

Search