[Question] XIEvent Type #3
Replies: 7 comments 7 replies
-
Hello, glad that my work in this repo can be of use to someone. :)
The manner in which data is used by the event VM is dependent on the opcode(s) that are referencing it and what additional calls, if any, are used when handling said data. It is possible that a value is used in multiple ways through casting or lookups with other things and so on.
The way the data is used will be dependent on the opcode(s) that are using it at the given moment in the event. It is possible for data to be reused as well allowing it to have more than one type/purpose. Since you asked multiple questions here, I'll answer them individually. Q. For example, how might I know what immed_data 30 is below, or that 8033 is a string id?You can't determine what they are or how they are used simply by looking at them. You need to decompile the bytecode data back into readable opcode instructions to determine how they are being used and what they represent. But again, data can be reused by multiple things and not always have a single specific use. It can also be manipulated after being read to become some other value for another purpose as well. For example, that 30 could represent a string index, a time delay, a flag, a value to be used in an equation, a value used as a compare, and so on. There are a ton of ways the data can be used. Q. Why are index 0, 1, and 2 event_exec_nums the max value of unsigned 16-bit integer?These are generally used as special condition values throughout the event VM and can be used for different purposes. In some cases, it can denote an empty event block to tell the event system to reset, or it can be dead/old event entries that are just left over and have had their ids removed. I don't think -1 (65535) has a specific purpose itself, but -2 (65534), for example, does and causes the event id loop checks to exit early if hit. Q. Where should I be looking for event 271?There is no specific event with the id '271'. Every zone can have an event with the id of 271. They are not unique ids across the whole game. If you are referring to the block you have dumped, based on the information you shared that would be for:
Which does have the entry for event '271'. To determine which event block in the zone has the desired event id, you would have to parse the full file, loop the blocks and look at each Q. What can I do with the event_data bytes?The You can use the information that is stored within the We can use the block you gave as an example. In that example this is how those arrays would connect to each other:
Next, you can use this information to determine the bytecode data and size of data for each event in the block based on the offsets and the
So with this, we can then determine that the bytecode data for each event in this block would be: Event Index:
Event Index:
Event Index:
Event Index:
From here, you would need to then disassemble the bytecode back into its opcode form to determine how things are being used. Each of the opcodes pages contain information regarding their raw bytecode layout and sizes based on the various sub-conditions that can change how the data is used. I'll use the last event (271) as an example to show you how this would look. We now know the bytecode for this event is: The first byte here is the start of the first instruction Here we can see the length is always 5 bytes long. This opcode is used to look at a given entity to begin the talking animation. So then we have:
The next opcode starts with This is a delay opcode telling the event to 'wait' an amount of time. This kind of delay uses a hardcoded delay of 16 and is only a single byte long. So then we have:
We repeat this for each starting byte of the opcode to fully disassemble what this event is doing. Once done, you should end up with:
|
Beta Was this translation helpful? Give feedback.
-
I forgot to explain the string handling for this event as well with opcode The event data you showed in your example is for That said, the way this works is through the opcode 0x1D. You can read how that opcode works here: This opcode uses its own data to determine which string index is used to be printed. This is done using the In this case, the opcode is
That means that this would use the value 8033 as the string index. For Windurst Woods, you would then use the string DAT file for that zones event strings and dump the string at that index to be printed. |
Beta Was this translation helpful? Give feedback.
-
Wow, this is absolutely awesome. Thank you for taking the time to step through this! This gives me more than enough to work with - apologies for completely glazing over all the opCodes md files you laid out. 🙏🏼 |
Beta Was this translation helpful? Give feedback.
-
So then, for Event Index 2 we would see: 5E 69 64 6C 30 1C 00 80 00
|
Beta Was this translation helpful? Give feedback.
-
I've converted your Issue into a Discussion since it is just questions about the repo. And yes, that looks correct for your last post/question. |
Beta Was this translation helpful? Give feedback.
-
Following up on this, I've noticed several opcodes have multiple size options, and the logic you've discerned for these opcodes indicates what position the pointer moves to. This logic seems to be a mix of actual game conditions as well as straight forward value checking from upcoming bytes from the opcode. So, would that mean without a client and active game it isn't possible to break down these events? Or am I thinking about these the wrong way? 0xD4, 0xA7, 0x75, 0x72, 0x66, and 0x5B are some examples of this. For instance 5B is looking at an input variable param3 to determine if it should be size 15 or 17 🤷♂️. If determining the true size for these opcodes is (at least not yet) plausible, what would be the right way to deconstruct as much of the event data as possible? decode up to the point of uncertainty and then spit out the raw bytes from then on for the event series we're currently in? (Event Series being the events within an event block for an actor, aka 271 above) |
Beta Was this translation helpful? Give feedback.
-
Hello again. Everything can be determined offline without running the client. There are some conditional opcodes that will wait for certain conditions before progressing through other paths, but their conditions are all deterministic so the flow of instructions is able to be determined while statically disassembling. The event VM is written in an old school async manner. When there is an active event on the client, the client will call the main VM executor every frame. The current For the opcodes you asked about:
For example, with opcode switch (this->EventData[this->ExecPointer + 1]) This is reading the next byte from the instruction after the initial opcode
Keeping in mind that some of these sub-kinds can also reset the |
Beta Was this translation helpful? Give feedback.
-
Hello! First off I want to say thank you for the work you've done here in establishing a structure for the XIEvent Dats, It's been a tremendous (read: I could not have done this without you 😂) help! I've been tinkering with xi-tinkerer and made a fork that implements a round trip from .dat to .yml for events.
Now I'm at the point where I'm trying to determine how to make this useful, such as having the .yml include human-readable info, but to do so I'd need to know that the immid data is of a certain type.
For example, how might I know what immed_data 30 is below, or that 8033 is a string id? Why are index 0, 1, and 2 event_exec_nums the max value of unsigned 16-bit integer? Where should I be looking for event 271? What can I do with the event_data bytes? This is my first foray into decoding and investigating .dat files so I may be missing something obvious for how to decipher this but figured I'd ask first before going into fruitless rabbit holes.
Thanks again!
https://github.com/jamesbradleym/xi-tinkerer/tree/event
Beta Was this translation helpful? Give feedback.
All reactions