Tutorial: Scripts in Arcanum .proto files using a Hex editor

Discussion in 'Modding and Scripting Support' started by Barbarbrick, Nov 25, 2024.

Remove all ads!
Support Terra-Arcanum:

GOG.com

PayPal - The safer, easier way to pay online!
  1. Barbarbrick

    Barbarbrick New Member

    Messages:
    3
    Likes Received:
    0
    Joined:
    Nov 22, 2024
    This tutorial is based on the work of discord user Jen - The Town Witch, so all credit goes to her.

    Let's open the 6099 Elven Hunter's Bow, which is located in the "006119 - Weapon.pro" file, with a hex editor. For scripts, we want to look at the lines 00000160, 00000170 and 00000180 (offsets may vary for items other than weapons).
    Code:
    00000000 | 77 00 00 00 FF FF 00 00 00 00 00 00 00 00 00 00
    00000010 | 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00
    00000020 | B2 5C 67 00 E7 17 00 00 02 00 00 00 AA 5C 67 00
    00000030 | F7 50 E5 04 05 00 00 00 01 00 E0 A4 03 00 00 00
    00000040 | 54 1B 00 00 B3 EA 13 00 00 02 04 60 00 00 00 00
    00000050 | 00 00 00 00 00 FF FF FF FF 01 04 00 00 00 07 00
    00000060 | 00 00 05 00 00 00 FF FF FF FF FF FF FF FF FF FF
    00000070 | FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
    00000080 | FF FF 02 00 00 00 7F 00 00 00 00 00 00 00 01 04
    00000090 | 00 00 00 07 00 00 00 01 00 00 00 FF FF FF FF FF
    000000a0 | FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
    000000b0 | FF FF FF FF FF FF FF 02 00 00 00 7F 00 00 00 00
    000000c0 | 00 00 00 01 04 00 00 00 04 00 00 00 02 00 00 00
    000000d0 | FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
    000000e0 | 02 00 00 00 0F 00 00 00 00 00 00 00 00 00 00 00
    000000f0 | FF FF FF 00 FF 00 00 00 64 00 00 00 00 00 00 00
    00000100 | FF FF FF FF FF FF FF 00 00 01 04 00 00 00 04 00
    00000110 | 00 00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF
    00000120 | FF FF FF FF FF FF 02 00 00 00 0F 00 00 00 00 00
    00000130 | 00 00 00 34 04 00 00 00 00 00 00 00 00 00 00 D0
    00000140 | 07 00 00 D3 17 00 00 00 02 04 60 FF FF FF FF 00
    00000150 | 00 00 00 50 00 00 00 00 00 00 00 00 00 00 00 02
    00000160 | 00 00 00 00 01 0C 00 00 00 01 00 00 00 03 00 00   <--- This line,
    00000170 | 00 00 00 00 00 00 00 00 00 67 6D 00 00 02 00 00   <--- this line
    00000180 | 00 80 00 00 00 00 00 00 00 DC 0F 00 00 08 00 00   <--- and this line
    00000190 | 00 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00
    000001a0 | A7 00 00 00 00 00 00 00 00 12 04 60 00 00 00 00
    000001b0 | 00 22 04 60 0F 00 00 00 00 00 00 00 D3 17 00 00
    000001c0 | D3 17 00 00 10 27 00 00 10 27 00 00 10 27 00 00
    000001d0 | 10 27 00 00 10 27 00 00 00 00 00 00 00 00 00 00
    000001e0 | 00 00 00 00 00 00 0E 00 00 00 00 22 04 60 00 00
    000001f0 | 00 00 00 00 00 00 01 04 00 00 00 02 00 00 00 06
    00000200 | 00 00 00 01 00 00 00 02 00 00 00 02 00 00 00 11
    00000210 | 00 00 00 00 00 00 00 01 04 00 00 00 02 00 00 00
    00000220 | 04 00 00 00 0A 00 00 00 05 00 00 00 02 00 00 00
    00000230 | 11 00 00 00 00 00 00 00 00 0A 00 00 00 00 00 00
    00000240 | 00 0F 00 00 00 00 00 00 00 08 00 00 00 00 00 00
    00000250 | 00 00 00 00 00 01 00 00 00 00 00 00 40 FF FF FF
    00000260 | FF 02 00 00 00 00 00 00 00 00 00 00 00 04 00 00
    00000270 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    00000280 | 00 00 00
    If the weapon .proto file calls a script, then offset 0x164 will be 01, and the next offset 0x165 will be 0C (items other than weapons may have slightly different offsets). To make things more clear, I'll just paste the relevant bytes starting at offset 0x164, and name some the bytes with letters:
    Code:
    01 0C 00 00 00 01 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 67 6D 00 00 02 00 00 00 80 00 00 00 00 00 00 00
    01 0C -- -- -- 0N -- -- -- 0U -- -- -- FF FF FF FF C0 C1 C2 C3 XX XX XX XX 02 -- -- -- YY YY YY YY YY YY YY YY
    • 01 0C values are always the same.
    • 0N is the number of scripts the .proto file will call, in this case one.
    • 0U is an unknown value, changing it does nothing (as far as we can tell).
    • FF FF FF FF are flags (values 0 or 1 that represent true or false) passed to the script, and they can be accessed in a script with local flag 0, etc. Of the items, only Molochean Hand Amulet uses these, so you'll almost always see the values 00 00 00 00 here. There are 32 available flags and this is a bit mask, just like attachment points described below (encoded / decoded exactly the same way).
    • C0 C1 C2 C3 are counters (numerical arguments) passed to the script and those values can be accessed in a script with Counter 0, etc.
    • XX XX XX XX is the script number that the .proto file calls, but it has to be converted. Reversed order of bytes 67 6D 00 00 is 00 00 6D 67, so the hexadecimal value is 6D67 and that's 28007 in decimal numbers. So this .proto file is calling script 28007 found in the /data/scr/ folder (you have to extract Arcanum5.dat, Arcanum4.dat or Arcanum2.dat files first, using for example dbmaker.
    • YY YY YY YY YY YY YY YY are the attachment points for the scripts. This is a bit mask, more on that below.
    There are 36 script attachment points, described in detail in a file Eventscripts.rtf (part of editdocs.zip) written by Tim Cain himself.

    When there is only one script in the .proto file, the attachment point can be easily decoded using the formula proposed by DKoepp and Otto Krupp (found in PROTOTYPEEDITING.doc, part of ArcanumProtoDocs.zip):
    • 1st byte – 1st bit (1 for Get, 2 for Drop, 4 for Throw, 8 for Hit)
    • 1st byte – 2nd bit (1 for Examine, 2 for Use, 4 for Destroy, 8 for Unlock)
    • 2nd byte – 1st bit (1 for Dying, 2 for Enter Combat, 4 for Exit Combat, 8 for Start Combat)
    • 2nd byte – 2nd bit (1 for Miss, 2 for Dialog, 4 for First Heartbeat, 8 for Catching Thief Pc)
    • 3rd byte – 1st bit (1 for Leader Killing, 2 for Insert Item, 4 for Will Kill on Sight, 8 for Taking Damage)
    • 3rd byte – 2nd bit (1 for End Combat, 2 for Buy Object, 4 for Resurrect, 8 for Heartbeat)
    • 4th byte – 1st bit (1 for Remove Item, 2 for Leader Sleeping, 4 for Bust, 8 for Dialog Override)
    • 4th byte – 2nd bit (1 for Wield On, 2 for Wield Off, 4 for Critter Hits Target, 8 for New Sector)
    • 5th byte – 2nd bit (1 for Transfer, 2 for Caught Thief, 4 for Critical Hit, 8 for Critical Miss)
    In case of 6099 Elven Hunter's Bow we have 80 00 00 00 00 00 00 00, that means 1st byte – 1st bit is 8, 1st byte – 2nd bit is 0 and so on. Therefore, script 28007 has Hit attachment point, which is described in Eventscripts.rtf file like this:

    HIT:
    * when called: when a successful attack is performed by a critter on a target
    * triggerer: critter
    * attachee: item
    * extra object: target (may be NULL if the critter is throwing item at a location)
    * effect of RETURN AND SKIP DEFAULT: no effect. This script is called for informational reasons only. It cannot prevent the hit.
     
  2. Barbarbrick

    Barbarbrick New Member

    Messages:
    3
    Likes Received:
    0
    Joined:
    Nov 22, 2024
    Bit mask

    What happens when there are two or more scripts in one .proto file? Then we have to look at the attachment points as a bit mask. The mask is simple, bit 1 moves from left to right for each attachment point. Because there are only 36 attachment points, we will actually see only 5 bytes used instead of reserved 8.
    Code:
    Attachment Point    Attachment Number    Bit Mask
                            5th byte  | 4th byte  | 3rd byte  | 2nd byte  | 1st byte
    Examine            0    0000 0000 | 0000 0000 | 0000 0000 | 0000 0000 | 0000 0001
    Use                1    0000 0000 | 0000 0000 | 0000 0000 | 0000 0000 | 0000 0010
    Destroy            2    0000 0000 | 0000 0000 | 0000 0000 | 0000 0000 | 0000 0100
    Unlock             3    0000 0000 | 0000 0000 | 0000 0000 | 0000 0000 | 0000 1000
    Get                4    0000 0000 | 0000 0000 | 0000 0000 | 0000 0000 | 0001 0000
    Drop               5    0000 0000 | 0000 0000 | 0000 0000 | 0000 0000 | 0010 0000
    Throw              6    0000 0000 | 0000 0000 | 0000 0000 | 0000 0000 | 0100 0000
    Hit                7    0000 0000 | 0000 0000 | 0000 0000 | 0000 0000 | 1000 0000
    Miss               8    0000 0000 | 0000 0000 | 0000 0000 | 0000 0001 | 0000 0000
    Dialog             9    0000 0000 | 0000 0000 | 0000 0000 | 0000 0010 | 0000 0000
    First Heartbeat    10   0000 0000 | 0000 0000 | 0000 0000 | 0000 0100 | 0000 0000
    Catching Thief Pc  11   0000 0000 | 0000 0000 | 0000 0000 | 0000 1000 | 0000 0000
    Dying              12   0000 0000 | 0000 0000 | 0000 0000 | 0001 0000 | 0000 0000
    Enter Combat       13   0000 0000 | 0000 0000 | 0000 0000 | 0010 0000 | 0000 0000
    Exit Combat        14   0000 0000 | 0000 0000 | 0000 0000 | 0100 0000 | 0000 0000
    Start Combat       15   0000 0000 | 0000 0000 | 0000 0000 | 1000 0000 | 0000 0000
    End Combat         16   0000 0000 | 0000 0000 | 0000 0001 | 0000 0000 | 0000 0000
    Buy Object         17   0000 0000 | 0000 0000 | 0000 0010 | 0000 0000 | 0000 0000
    Resurrect          18   0000 0000 | 0000 0000 | 0000 0100 | 0000 0000 | 0000 0000
    Heartbeat          19   0000 0000 | 0000 0000 | 0000 1000 | 0000 0000 | 0000 0000
    Leader Killing     20   0000 0000 | 0000 0000 | 0001 0000 | 0000 0000 | 0000 0000
    Insert Item        21   0000 0000 | 0000 0000 | 0010 0000 | 0000 0000 | 0000 0000
    Will Kos           22   0000 0000 | 0000 0000 | 0100 0000 | 0000 0000 | 0000 0000
    Taking Damage      23   0000 0000 | 0000 0000 | 1000 0000 | 0000 0000 | 0000 0000
    Wield On           24   0000 0000 | 0000 0001 | 0000 0000 | 0000 0000 | 0000 0000
    Wield Off          25   0000 0000 | 0000 0010 | 0000 0000 | 0000 0000 | 0000 0000
    Critter Hits       26   0000 0000 | 0000 0100 | 0000 0000 | 0000 0000 | 0000 0000
    New Sector         27   0000 0000 | 0000 1000 | 0000 0000 | 0000 0000 | 0000 0000
    Remove Item        28   0000 0000 | 0001 0000 | 0000 0000 | 0000 0000 | 0000 0000
    Leader Sleeping    29   0000 0000 | 0010 0000 | 0000 0000 | 0000 0000 | 0000 0000
    Bust               30   0000 0000 | 0100 0000 | 0000 0000 | 0000 0000 | 0000 0000
    -----              31   0000 0000 | 1000 0000 | 0000 0000 | 0000 0000 | 0000 0000
    Transfer           32   0000 0001 | 0000 0000 | 0000 0000 | 0000 0000 | 0000 0000
    Caught Thief       33   0000 0010 | 0000 0000 | 0000 0000 | 0000 0000 | 0000 0000
    Critical Hit       34   0000 0100 | 0000 0000 | 0000 0000 | 0000 0000 | 0000 0000
    Critical Miss      35   0000 1000 | 0000 0000 | 0000 0000 | 0000 0000 | 0000 0000
    Suppose we want to call two scripts: one that runs when you equip an item, and one that runs when you unequip it. That would be Wield On and Wield Off attachment points. Their combined bit mask has two 1 next to each other:
    Code:
    5th byte  | 4th byte  | 3rd byte  | 2nd byte  | 1st byte
    0000 0000 | 0000 0011 | 0000 0000 | 0000 0000 | 0000 0000
    Now we need to convert this to hexadecimal. We do this by converting every 4 binary digits DDDD to one hex digit H. So we split 0000 0011 into:
    • 0000 which is 0
    • 0011 which is 3
    As a result, we get 03. You can double-check your calculations with any binary-hexadecimal converters available on the web:
    Code:
    5th byte  | 4th byte  | 3rd byte  | 2nd byte  | 1st byte
        00    |     03    |     00    |     00    |     00
    The last thing we need to do is reverse the byte order:
    Code:
    1st byte  | 2nd byte  | 3rd byte  | 4th byte  | 5th byte
        00    |     00    |     00    |     03    |     00
    Our bit mask for two attachment points Wield On and Wield Off should look like this:
    Code:
    00 00 00 03 00
    Like I said, we used only 5 bytes instead of 8 reserved, so let's add 3 unused bytes 00 00 00 at the end:
    Code:
    00 00 00 03 00 00 00 00
    YY YY YY YY YY YY YY YY
    It's important to note that each script must have a different attachment point, and they cannot share the same one. The first script will get the attachment point with the lowest number, the second script will get the attachment point with the second lowest number, and so on.

    Adding a script to a .proto file

    Let's add one script to the 6062 Katana sword, which is located in the "006082 - Weapon.pro" file. We will borrow a script from Aerial Decapitator, that plays a sound when the weapon is equipped. First, we need to add 36 bytes to the .proto file, starting from offset 0x164. This moves value in offset 0x165 to 0x189.

    Then we have to change the added bytes to look like this, starting from the offset 0x164:
    Code:
    01 0C 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 B6 09 00 00 02 00 00 00 00 00 00 01 00 00 00 00
    01 0C -- -- -- 0N -- -- -- 0U -- -- -- FF FF FF FF C0 C1 C2 C3 XX XX XX XX 02 -- -- -- YY YY YY YY YY YY YY YY
    Our "006082 - Weapon.pro" file used to look like this:
    Code:
                           0x165
                           v
    0x160 | 00 00 00 00 00 AA 0F 00 00 02 00 00 00 00 00 00
    And now should look like this:
    Code:
                           0x165
                           v
    0x160 | 00 00 00 00 01 0C 00 00 00 01 00 00 00 00 00 00
    0x170 | 00 00 00 00 00 00 00 00 00 B6 09 00 00 02 00 00
    0x180 | 00 00 00 00 01 00 00 00 00 AA 0F 00 00 02 00 00
                                       ^
                                       0x189
    How about adding a second script? It will be the same one B6 09 for clarity, but this time it will be called when Katana is unequipped. Instead of 36 bytes, we have to add 48 (12 more, this moves the value in offset 0x165 to 0x195), change the number of scripts used 0N to 02 and the attachment points YY to 03:
    Code:
    01 0C 00 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 B6 09 00 00 00 00 00 00 00 00 00 00 B6 09 00 00 02 00 00 00 00 00 00 03 00 00 00 00
    01 0C -- -- -- 0N -- -- -- 0U -- -- -- F1 F1 F1 F1 C0 C1 C2 C3 X1 X1 X1 X1 F2 F2 F2 F2 C0 C1 C2 C3 X2 X2 X2 X2 02 -- -- -- YY YY YY YY YY YY YY YY
    Our new "006082 - Weapon.pro" file with two scripts should look like this:
    Code:
                           0x165
                           v
    0x160 | 00 00 00 00 01 0C 00 00 00 02 00 00 00 00 00 00
    0x170 | 00 00 00 00 00 00 00 00 00 B6 09 00 00 00 00 00
    0x180 | 00 00 00 00 00 B6 09 00 00 02 00 00 00 00 00 00
    0x190 | 03 00 00 00 00 AA 0F 00 00 02 00 00 00 00 00 00
                           ^
                           0x195
    That's all. If you have any questions, feel free to ask. Feedback, corrections and tips are greatly appreciated.
     
Our Host!