Jump to content


Photo

Tutorial: Item Patching


1 reply to this topic

#1 CamDawg

CamDawg

    Executive Delivery Boy

  • Gibberling Poobah
  • 8709 posts
  • Gender:Not Telling

Posted 02 January 2005 - 03:47 PM

A warning: this is nowhere near complete at the moment and I'll adding quite a bit more to it. Every time I think I can knock out something short on the topic pf patching it ends up... err, extremely long-winded such as this. Nonetheless, I do hope to build up to the very big and very ugly patching in stuff like the tweak packs. Eventually.

And whilt the tutorial focuses on items, the principles can be extended to any file type in the game. Every file--spells, stores, even the worldmap--uses the basic format of static block with offsets and numbers for blocks of items/effects/areas/whatever. Item patches appear to be the most common type of patch so I've selected them as the example.

_______
Since I seem to be the most ardent supporter of "80 lines of tp2 patch code > overwriting one item" I've been asked to try and put together a patching tutorial to help others learn how to patch. The code itself is intimidating at first, but once you understand the logic behind it and the common pitfalls it becomes a lot easier to understand.

Pros/Cons of Patching vs Overwriting
Patching is generally superior to overwriting as it goes much further to ensure your work will be compatible with the work of other modders. A properly done patch will ensure you won't have to have a lot of conditional blocks in your install to account for other mods and won't have to worry about install order.

The downside to patching is that item descriptions can not be patched. If you include an item file with your mod, you know exactly what it does and that the description will be accurate. If you patch a file, the potential is there that it has been patched by another mod, meaning that you can either change the description to what you think it will be or not change it at all. Either option can, at best, leave the item standing out as mod content or, at worst, wildly mis-label the item.

The Tools
The goal is to alter the item purely through WeiDU tp2 patching. Depending on what your specific task, this can be immensely difficult or fairly easy. Before proceeding, I would suggest you open an item file with Near Infinity and ensure Tools > Show Offsets is checked or view the itm file format in the IESDP. Patching requires examining and manipulating hex offsets, which means having something that can perform hex calculations would be very useful. If you use Windows, I would suggest using the PowerToys calculator (available for free from Microsoft) as it allows you to perform calculations with hex input and output. I would also suggest you take a look at japheth's tutorial on using READ/WRITE WeiDU commands in the WeiDU documentation.

The General Approach
It's important to understand how an item file is structured before proceeding. There are four basic sections to an item file:
  • Basic info
    Anything in the file prior to hex offset 0x72 contains the basic info for the item. Patching anything in here is very straightforward as the offsets never change and it's simply a matter of reading and writing to the listed offsets. Following the basic info are the...
  • Abilities (aka Extension Headers)
    Following the global effects are item abilities. Abilities are things in an item that the player must select to use--for example, using a sword in melee is an ability, drinking a potion is also an ability, or an item's once per day goodies are abilities. To accompllish their abilities, they also need a set of effects, but the effects for abilities are last in the file. Abilities are 0x38 bytes long in the file.
  • Global Effects (aka Equipping Effects)
    Global effects are next in the item file. Global effects in an item file are always active and generally help define the item beyond the basic, static info. For example, an armor's basic armor class is set by a global effect, as are the colors of a shield. Some magical effects that occur when the item is equipped are also handled by global effects. Effects are 0x30 bytes in length.
  • Extended effects (aka Feature Blocks)
    Following the global effects in the item file are another set of effects. These effects, unlike the global ones, are related to the abilities of the item. It is important to recognize that extended effects are the same as global effects, but referenced in completely different fashion.
Confused yet? Let's try to put this in context by using the example of Celestial Fury. Basic info, such as its weight, who can use it, required STR, etc. are all contained in the static basic info block.

Celestial Fury also has three abilities: it can be used for a melee attack, can cast lightning strike once per day, and can cast blindness once per day. These translate into three abilities in the item file. The actual ability part of the file control how the abilities can be used and how often, but not what they actually do when activated. Basic melee/ranged information is included, such as damage and damage type, but anything beyond this (such as CF's extra lightning damage on a hit) is handled by the effects headers.

CF has four global effects. Three set various item colors (making the hilt red for example) and the fourth makes it glow.

The global effects are followed by the extended effects for the item abilities. In the extended effects you'll find CF's extra melee effects, such as the lightning damage and stun. (The actual melee damage, recall, in one of the few bits of info in the ability itself). The effects needed to actually make CF's two once-per-day abilities work and actually do something are also here in the extended effects.

How It All Comes Together
At this point it's important to understand how the game interprets item files. After 0x72 in the item file, the file could be anything--it could simply end if there are no abilities or effects, there could be 7 abilities, there could be 2 global effects, etc. So understanding how the game discerns the abilities and effects part of the item file is critical.

There are four key fields in the basic info part of the file:
  • 0x64 - Ability Offset
    This field tells the engine at what offset the abilities for the item begin. This value is always 0x72, as the abilities always follow the end of the 'basic info' part of the file. Even if there are no abilities for the item, this will be 0x72. For the sake of being cautious, though, always write your code on the assumption that this can be a non-0x72 value as its possible that a mod or patch may alter this value. The sample code provided with this tutorial will be written assuming this can be any value.
  • 0x68 - Number of Abilities
    This field tells the engine how many abilities an item has. This is important, for if an item has four actual abilities but this field says 3, one of the abilities will be ignored.
  • 0x6a - Effects Offset
    Similar to the abilities offset field, this tells the engine where the effects begin in the item file.
  • 0x70 - Number of Global Effects
    This field tells the engine how many effects are global effects.
You'll notice that the extended effects--the ones used by abilities--are not covered in any of this information. Given these four bits of information, the engine interprets anything beyond the end of the global effects as additional effects used by the abilities. The abilities themselves contain two fields that tell the engine which effects are linked to them. The Effects Index field and the Number of Effects field tell the engine where to look up effects. The Effects Index counts effects from zero and includes the global effects in the count, so the first effect available to an ability in an item with three global effects would be 3, as 0, 1, and 2 are globals. If there are no globals, this index begins at 0.
Don't you worry about Planet Express, let me worry about blank.

#2 CamDawg

CamDawg

    Executive Delivery Boy

  • Gibberling Poobah
  • 8709 posts
  • Gender:Not Telling

Posted 06 January 2005 - 07:33 PM

OK, so why have I rambled at length about the item structure itself? Because you need to understand how changes to the item file affect the rest of the file. For example, adding or deleting an ability means the effects are now pushed back in the file by the length of the abilities you've added. This means you need to adjust the number of abilities and the effects offset in addition to adding the actual abilities.

Adding effects can be just as fun. Adding a global effect means you need to search through the abilities for extended effects--the effects index in particular--and adjust it since new global effects will push the extended effects further down the file.

So let's try some patching, shall we? We'll start with relatively simple examples and work our way up to the more difficult ones.

Example One: Changing Maximum Stack on an item
This is the simplest example to perform, as it involves a simple read/write on the static portion of the file. Opening up the IESDP or an item file in Near Infinity, you can see that the offset 0x38 contains the value for the maximum number in stack, and it's two bytes in length. (I suggest you take a look at japheth's READ BYTE/SHORT/LONG tutorial included in the WeiDU documentation if you need ti figure out when to use the various commands.) We'll use the Amulet of Power, AMUL21.itm, as our example.

COPY_EXISTING ~amul21.itm~ ~override~
  WRITE_SHORT 0x38 10

This will copy the existing item and write the value "10" at offset 0x38--setting the maximum stack to 10 for the item.

Example Two: Altering a Global Effect
For our next example, we'll look at altering the color of Celestial Fury. Colors are set by global effects, so the basic idea is that we're going to read how many global effects exist, and then check each one with a WHILE loop. The basic idea behind a WHILE loop is that it has a condition, and it will continue to execute as long as the condition is true. In this case, we're going to set it is 'as long as the number of effects not checked yet is greater than 0, keep running.' We'll express it a bit more mathematically, of course. :) One of the dangers of WHILE loops is that unless you remember to do something to make the loop close, it can and will run forever.

The other new command you'll see in here is PATCH_IF. A PATCH_IF is nearly identical to a WHILE loop except that it only runs once so you don't need to worry about infinite looping. Since this patch is a bit more complex, we'll do it in sections.

COPY_EXISTING ~sw1h51.itm~ ~override~
READ_LONG 0x6A "effects_offset"
READ_SHORT 0x70 "effects_number"
This step contains the copy command and reads in data we're going to need to set up the WHILE loops. We read in the number of global effects and where they begin. Next we start our WHILE loop and read in data from each existing global effect.

WHILE ("%effects_number%" > 0) BEGIN
SET "effects_number" = ("%effects_number%" - 1)
READ_SHORT ("%effects_offset%" + (0x30 * "%effects_number%")) "opcode"
READ_LONG ("%effects_offset%" + 0x08 + (0x30 * "%effects_number%")) "location"

OK, so it's looking ugly already. We start the WHILE loop with the condition that effects_number, which we got with our READ command, be greater than zero. In the first step inside the WHILE loop. we decrease the value of effects_number by one--this ensures that value will go down every time the WHILE loop runs, which means it will eventally close. Next are a pair of READs, but the location looks very ugly. What we're doing is going to the beginning of the effects in the file--effects_offset which we read in the first step--and then going down by 0x30 (the length of one effect) times the number of effect. (Pull out your hex calc and verify this yourself by looking at a sample file.) This puts at the beginning of whichever effect we're checking. In the first read, we want the opcode, which is at the beginning of the effect. In the second, we want the location of the color code, which is 0x08 from the beginning of the effect--this is why it has the extra 0x08 added in.

Armed with these two pieces of data, it's time to see if we want to patch this effect:
PATCH_IF (("%opcode%" = 7) AND ("%location%" = 21) BEGIN
WRITE_LONG ("%effects_offset%" + 0x04 + (0x30 * "%effects_number%")) 4
END
So IF the effect is using opcode 7 (set color) AND the location is 21 (weapon head) then it executes the WRITE_LONG. The WRITE_LONG sets the color of this effect to 4. Note that we use the same style as the reads--effects_offset + 0x30 * effect_number plus the additional 0x04 that the color is from the beginning of the effect. If we're looking at any other opcode or location, the PATCH_IF is ignored. Finally, the PATCH_IF is closed with an END statement.

So now we just need to wrap up a few details. The WHILE loop also needs to be closed with an END statement. Remember, as long as the condition specified in the WHILE loop is true, WeiDU will jump from here back to the WHILE loop and do it again. In this particular case, the WHILE loop will run as many times as there are effects. We also add one last item: BUT_ONLY_IF_IT_CHANGES. In these examples, BUT_ONLY_IF_IT_CHANGES is not terribly important but it's good practice to get into the habit of always using it, especially when you move up to REGEXPs (don't worry, we'll cover these later). Basically, if the item does not change as the result of your patching it won't get copied, which reduces override folder clutter. So, putting this all together:

COPY_EXISTING ~sw1h51.itm~ ~override~
  READ_LONG 0x6A "effects_offset"
  READ_SHORT 0x70 "effects_number"
  WHILE ("%effects_number%" > 0) BEGIN
    SET "effects_number" = ("%effects_number%" - 1)
    READ_SHORT ("%effects_offset%" + (0x30 * "%effects_number%")) "opcode"
    READ_LONG ("%effects_offset%" + 0x08 + (0x30 * "%effects_number%")) "location"
    PATCH_IF (("%opcode%" = 7) AND ("%location%" = 21) BEGIN
      WRITE_LONG (("%effects_offset%" + 0x04 + (0x30 * "%effects_number%")) 4
    END
  END
  BUT_ONLY_IF_IT_CHANGES

Don't you worry about Planet Express, let me worry about blank.



Reply to this topic



  


0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users