• Welcome to Valhalla Legends Archive.
 

9c // 9d packet breakdown

Started by ShadowDancer, July 14, 2006, 09:53 AM

Previous topic - Next topic

ShadowDancer

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



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

Quote from: MyndFyre-vL
No. We help people who help themselves.
http://sourceforge.net/projects/d2gs
http://assembla.com/space/d2gs

teK

You forgot an important part


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

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
Quote from: MyndFyre-vL
No. We help people who help themselves.
http://sourceforge.net/projects/d2gs
http://assembla.com/space/d2gs

teK

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

ShadowDancer

ahhhhh you are RaMz? :D cool

I allways merge the info from long theads like it for my ref library :P
Quote from: MyndFyre-vL
No. We help people who help themselves.
http://sourceforge.net/projects/d2gs
http://assembla.com/space/d2gs

UserLoser

Excellent.  I think they banned herzog from here though over some stupid C++ discussion.  Not sure though...

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:

    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

E.T.

If you still need this, here's how I do it (this comes right after the version byte):


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



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

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 from: MyndFyre-vL
No. We help people who help themselves.
http://sourceforge.net/projects/d2gs
http://assembla.com/space/d2gs

E.T.

#9
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:

Container    = 0,
Equipment    = 1,
Belt         = 2,
Ground       = 3,
Cursor       = 4,
Item         = 6,
   

EquipmentLocation:

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,


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.

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:

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

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

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:

[bold]Sure :D [/bold]


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;
Quote from: MyndFyre-vL
No. We help people who help themselves.
http://sourceforge.net/projects/d2gs
http://assembla.com/space/d2gs

E.T.

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.

Ringo

Quote
            Exit Sub <<< that is a problem
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:

    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


ShadowDancer

Quote from: RingoWhy? :)

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.-
Quote from: MyndFyre-vL
No. We help people who help themselves.
http://sourceforge.net/projects/d2gs
http://assembla.com/space/d2gs