You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
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.
Hello, glad that my work in this repo can be of use to someone. :)
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.
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.
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.
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:
Zone:Windurst Woods
Entity:Two of Spades
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 EventIds array to find the id you are looking for.
Q. What can I do with the event_data bytes?
The EventData array holds the raw bytecode data for all of the events associated with that block.
You can use the information that is stored within the TagOffset table to determine where the bytecode of each event starts for that given block. To determine the size, you would need to make use of the EventDataSize value and the information you get from the TagOffset array. Each event in a given block will have an entry in the TagOffset table to determine where that given events' bytecode starts.
We can use the block you gave as an example. In that example this is how those arrays would connect to each other:
Event Id:-1, TagOffset:0
Event Id:-1, TagOffset:1
Event Id:-1, TagOffset:2
Event Id:271, TagOffset:11
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 EventDataSize value. You would walk the TagOffset array, starting from index 0, upward to determine the size of each events bytecode data. Using this approach we can see that:
Event index 0 starts at data index 0.
Look ahead to the next events start index (1) to determine the length. [1 - 0 = 1]
Event index 1 starts at data index 1.
Look ahead to the next events start index (2) to determine the length. [2 - 1 = 1]
Event index 2 starts at data index 2.
Look ahead to the next events start index (11) to determine the length. [11 - 2 = 9]
Event index 3 starts at data index 11.
Look ahead to the next events start index. This is the last event so no next index. Instead, use the EventDataSize to determine the length of data remaining which will be used for this events data.
EventDataSize for this block is 26. [26 - 11 = 15]
So with this, we can then determine that the bytecode data for each event in this block would be:
Event Index: 0 - Event Id: -1 - Data Length: 1
00
Event Index: 1 - Event Id: -1 - Data Length: 1
00
Event Index: 2 - Event Id: -1 - Data Length: 9
5E 69 64 6C 30 1C 00 80 00
Event Index: 3 - Event Id: 271 - Data Length: 15
1E F0 FF FF 7F 6F 70 1D 01 80 23 20 00 21 00
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: 1E F0 FF FF 7F 6F 70 1D 01 80 23 20 00 21 00
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:
1E F0 FF FF 7F
6F
70 1D 01 80 23 20 00 21 00
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:
1E F0 FF FF 7F - Look at entity and begin talking. [Entity = 7FFFFFF0 -> (Special Id) Local Player]
6F - Wait [Hard coded 16.0f delay time.]
70 - TurnCancel [Stop the target entities movement to talk with the other entity.]
1D 01 80 - NpcTalk [Prints a string as if the NPC said it to chat.][String Index: 0x8001]
23 - MesWait [Waits for the player to progress the event dialog forward.]
20 00 - SetEventUcFlg [Sets the event user control flag to 0.]
21 - Finish [Marks the event as completed and tells the event VM it can cleanup.]
00 - Ends the event and cleans up the current event stack.
I forgot to explain the string handling for this event as well with opcode 0x1D.
The event data you showed in your example is for Two of Spades within Windurst Woods. Using event 271 from that block, we know that it is Two of Spades printing its dialog to chat which is: TwO<star>UN-AblE<star>To<star>Me-MoR-IZe...
In this case, the opcode is 1D 01 80 meaning the read index from the opcode is 0x8001. This value is another special-value used by the event VM. Inside of the getworkofs helper function, we can see that this means the value will make use of the ImidData array. The value 0x8000 will be masked off of this leaving the index into this array that is used. In this case, that means we would get:
index = (0x8001 & 0x7FFF) // index would be 1
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.
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. 🙏🏼
Wait. Sets or updates (decreases) the current ReqStack[RunPos].WaitTime value. When first called, this opcode will check if ReqStack[RunPos].WaitTime is < 0.0. If true, then the WaitTime will be set from the value read from the opcode (e.g., 30 for immed_data[0]?).
Activity
atom0s commentedon Dec 17, 2024
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:
Windurst WoodsTwo of SpadesWhich 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
EventIdsarray to find the id you are looking for.Q. What can I do with the event_data bytes?
The
EventDataarray holds the raw bytecode data for all of the events associated with that block.You can use the information that is stored within the
TagOffsettable to determine where the bytecode of each event starts for that given block. To determine the size, you would need to make use of theEventDataSizevalue and the information you get from theTagOffsetarray. Each event in a given block will have an entry in theTagOffsettable to determine where that given events' bytecode starts.We can use the block you gave as an example. In that example this is how those arrays would connect to each other:
-1, TagOffset:0-1, TagOffset:1-1, TagOffset:2271, TagOffset:11Next, 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
EventDataSizevalue. You would walk theTagOffsetarray, starting from index 0, upward to determine the size of each events bytecode data. Using this approach we can see that:0starts at data index0.1) to determine the length. [1 - 0 = 1]1starts at data index1.2) to determine the length. [2 - 1 = 1]2starts at data index2.11) to determine the length. [11 - 2 = 9]3starts at data index11.EventDataSizeto determine the length of data remaining which will be used for this events data.EventDataSizefor this block is26. [26 - 11 = 15]So with this, we can then determine that the bytecode data for each event in this block would be:
Event Index:
0- Event Id:-1- Data Length:1Event Index:
1- Event Id:-1- Data Length:1Event Index:
2- Event Id:-1- Data Length:9Event Index:
3- Event Id:271- Data Length:15From 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:
1E F0 FF FF 7F 6F 70 1D 01 80 23 20 00 21 00The first byte here is the start of the first instruction
0x1Eso we can go to that page to see what that opcode means:https://github.com/atom0s/XiEvents/blob/main/OpCodes/0x001E.md
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
0x6Fas we took the first 5 bytes for the first instruction. So we again check that page:https://github.com/atom0s/XiEvents/blob/main/OpCodes/0x006F.md
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:
atom0s commentedon Dec 17, 2024
I forgot to explain the string handling for this event as well with opcode
0x1D.The event data you showed in your example is for
Two of SpadeswithinWindurst Woods. Using event 271 from that block, we know that it isTwo of Spadesprinting its dialog to chat which is:TwO<star>UN-AblE<star>To<star>Me-MoR-IZe...That said, the way this works is through the opcode 0x1D. You can read how that opcode works here:
https://github.com/atom0s/XiEvents/blob/main/OpCodes/0x001D.md
This opcode uses its own data to determine which string index is used to be printed. This is done using the
XiEvent::getworkofshelper function, which you can read about here:https://github.com/atom0s/XiEvents/blob/main/Event%20VM%20Functions.md#xieventgetworkofs
In this case, the opcode is
1D 01 80meaning the read index from the opcode is0x8001. This value is another special-value used by the event VM. Inside of thegetworkofshelper function, we can see that this means the value will make use of theImidDataarray. The value 0x8000 will be masked off of this leaving the index into this array that is used. In this case, that means we would get: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.
jamesbradleym commentedon Dec 17, 2024
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. 🙏🏼
jamesbradleym commentedon Dec 17, 2024
So then, for Event Index 2 we would see:
5E 69 64 6C 30 1C 00 80 00
ReqStack[RunPos].WaitTimevalue. When first called, this opcode will check ifReqStack[RunPos].WaitTimeis < 0.0. If true, then theWaitTimewill be set from the value read from the opcode (e.g., 30 forimmed_data[0]?).