Jump to content

Unreal Script Jump code


twinj

Recommended Posts

I have been reading old forum topics and noted some of the older problems which were causing roadblocks. One in particular was jumps. The solution mostly being to swap the bytes either 0x06 or 0x07 with each other to show what type of jump logic to use (not or normal)

 

The code looked at in the topic which I cant find now was.

 

93-AF-00-00-47-55-00-00-00-00-00-00-79-AF-00-00
00-00-00-00-00-00-00-00-7E-AF-00-00-00-00-00-00
AD-01-00-00-08-44-00-00-80-00-00-00-54-00-00-00
0F-00-7B-AF-00-00-25-07-6F-00-96-00-7B-AF-00-00
36-00-7D-AF-00-00-16-07-61-00-72-00-7E-AF-00-00
35-07-30-00-00-08-30-00-00-00-00-10-00-7B-AF-00
00-00-7D-AF-00-00-16-04-00-7B-AF-00-00-A5-00-7B
AF-00-00-16-06-0B-00-04-1D-FF-FF-FF-FF-04-3A-7C
AF-00-00-53-00-00-00-02-00-02-00-23-59-00-00-00
00-00-00

 

XComGame.KGunit

 

function int PerkStampFind(XGUnit kUnit, array<PerkStamp> stamps)
{
   local int I;

   I = 0;
   J0x0b:
   // End:0x6f [While If]
   if(I < stamps.Length)
   {
       // End:0x61
       if(kUnit == stamps[i].m_kUnit)
       {
           return I;
       }
       ++ I;
       // This is an implied JumpToken; Continue!
       goto J0x0b;
   }
   return -1;
}

 

After doing trial and error with Squad sizes, in a previous post, I found that the jumps were always out in DWORD sizes, being 4 bytes. e.g: 0, 4, 8, 12 and so on.

 

This holds true everywhere I looked. This prompted me to find out more as I need to figure out what the script size does to a script when calls to an INDEX are increased in a script. I am thinking this increases the required size or the script will fail. Note that when I am talking about 'size' I am NOT modifying the actual size. This has something to do with the size of the script when loaded. Actual file sizes are not reported in the header as far as I can tell. I need to know the size numbers so we can mod further.

 

Anyway I found this statement online.

 

Any offset is in Script units, that is, taking into account that any INDEX value in thetokens adds four bytes instead of the real serialized size.
At scribd http://www.scribd.com/doc/54572848/UT-Package-File-Format

 

For the script above you will see the J0x0b. This value points to the right location as there are no INDEX reads.

 

The next jump at 0x07 is at Little Endian WORD 0x6f00 or Big endian 0x006f which should jump to location 0x77 in the file to be correct.

 

// End:0x6f [While If]
   if(I < stamps.Length)

 

0x77 - 0x6f = 8. Effectively 2 INDEX reads.

 

The next jump at 0x07 to location 0x61 should go to location 6D to be correct.

 

// End:0x61
       if(kUnit == stamps[i].m_kUnit)

 

0x6D - 0x61 = C = 12d; 3 INDEX READS

 

I am not exactly sure which reads are counted but it seems like the ones that exist inside the conditional itself.

 

For the first example there are 2 reads being, I and stamps.

 

For the second it seems like three being, I, kUnit and stamps.

 

Looking for a more precise way to cover jumps. Might have to examine larger code bases with many jumps to get a proper feel.

 

Now need to understand the first 48 bytes in the files a bit more...

Edited by twinj
Link to comment
Share on other sites

Jumps are fun. When I worked on CanBeSold, I counted all bytes including 07 and the offset. B0 and D4 has the same offset size. Every offset counts with the size of previous one. But don't know where 8C counting starts. It could start at 00000030, but there would have to be 5 indexes.

 

XComStrategyGame.upk, class XGItemTree

function bool CanBeSold(int iItem)
{
   local TItem kItem;

   kItem = Item(iItem);
   // End:0x8c
   if(kItem.iCategory < 0 || kItem.iCategory == 3 || kItem.iCategory == 4)
   {
       return false;
   }
   // End:0xb0
   if(iItem >= 13 && iItem <= 19)
   {
       return false;
   }
   // End:0xd4
   if(iItem >= 77 && iItem <= 80)
   {
       return false;
   }
   return m_arrItems[iItem].iCash != -1;
}

SF: 07 8C 00 84 84 9A 35 B9 02 00 00 C8 02 00 00 00 00 00 D8 35 00 00 26 16 18 21 00 9A 35 B9 02 00 00 C8 02 00 00 00 00 00 D8 35 00 00 2C 02 16 16 18 21 00 9A 35 B9 02 00 00 C8 02 00 00 00 00 00 D8 35 00 00 2C 03 16 16 04 28 07 D4 00 84 84 84 9A 00 DA 35 00 00 2C C0 16 18 0E 00 9A 00 DA 35 00 00 2C 87 16 16 18 0E 00 9A 00 DA 35 00 00 2C 85 16 16 18 0E 00 9A 00 DA 35 00 00 2C 86 16 16 04 28 04 9B 35 C4 02 00 00 C8 02 00 00 00 00 10 00 DA 35 00 00 01 63 35 00 00 1D FF FF FF FF 16
RW: 07 8C 00 84 84 96 35 B9 02 00 00 C8 02 00 00 00 00 00 D8 35 00 00 25 16 18 21 00 9A 35 B9 02 00 00 C8 02 00 00 00 00 00 D8 35 00 00 2C 03 16 16 18 21 00 9A 35 B9 02 00 00 C8 02 00 00 00 00 00 D8 35 00 00 2C 04 16 16 04 28 07 B0 00 82 99 00 DA 35 00 00 2C 0D 16 18 0E 00 98 00 DA 35 00 00 2C 13 16 16 04 28 07 D4 00 82 99 00 DA 35 00 00 2C 4D 16 18 0E 00 98 00 DA 35 00 00 2C 50 16 16 04 28 04 9B 35 C4 02 00 00 C8 02 00 00 00 00 10 00 DA 35 00 00 01 63 35 00 00 1D FF FF FF FF 16

07 8C (140) 00 ... byte count 4A (74) ... 140 - 74 = 66 / 4 = 16,5 indexes? Weird. I can see 6, 3x kItem and 3x iCategory, leaving 42 to count before 07 is defined.

07 B0 (176) 00 ... byte count 1C (28) ... B0 - 8C = 24 (36) ... 36 - 28 = 8 / 4 = 2 ... 2x iItem

07 D4 (212) 00 ... byte count C1 (28) ... D4 - B0 = 24 (36) ... 36 - 28 = 8 / 4 = 2 ... 2x iItem

 

Got following reply from Eliot to one of my questions, but it is something you already know:

2B 03 is the position where the “if” ends, although this size is not identical to bytes. For example for every occurrance of object index it’ll consider it as 8 bytes even though only 4 are written there.
Link to comment
Share on other sites

Counting from...? I kinda see how they can be related that way. It is a pity that code has changed in the upk. Id like to see it in UE ex.

 

function bool CanBeSold(int iItem)
{
   local TItem kItem;

   kItem = Item(iItem);
   // End:0x8c
   if(kItem.iCategory == 1 || kItem.iCategory == 2 || kItem.iCategory == 3)
   {
       return false;
   }
   // End:0xd4
   if(iItem == 192 || iItem == 135 || iItem == 133 || iItem == 134)
   {
       return false;
   }
   return m_arrItems[iItem].iCash != -1;
}

 

I note that in what you posted the offset for 8c is 4 bytes behind the needed location. There are three reads from kitem essentially being one read being that all three condition have the same address?

 

Each script has a header of 48 bytes. The last dword of which is also the size of the header or the position to the end of script token. Maybe start from there. Generally I use the lteral address and try to adjust according to the index reads it will always be dword aligned.

 

But still I would like a precise mathematical formala. Which is what I am trying to figure out. I can still do guesswork for now... Did you re write the jumps in that code? Did you see my poor attempt at SquadSizes?

 

.. Still dont fully understand it. Will have to devote some more time to anaysing different file I guess.

Link to comment
Share on other sites

2B 03 is the position where the “if” ends, although this size is not identical to bytes. For example for every occurrance of object index it’ll consider it as 8 bytes even though only 4 are written there.

 

This is the information I am attempting to run from. There are several tokens which generate these extra bytes.

 

Some examples:


  1.  
  2. EX_LocalVariable 0x00 INDEX Object
  3. EX_InstanceVariable 0x01 INDEX Object
  4. EX_DefaultVariable 0x02 INDEX Object

 

But in my checks they do not all seem to add bytes. It seemed to be only ones involved in the conditional check... but yeah... still working it.

Edited by twinj
Link to comment
Share on other sites

That is why I posted search for replace with code, so you can see it in UE Explorer. It's working in the game.

 

What do you mean by "4 bytes behind the needed location"? The first jump's offset was set to 8C by Firaxis and I let it that way, as I changed only operators/numbers :) What's kitem? How do you see reads? What? 3 in 1?

 

I think jumps share the same address, when it comes to counting of jump's end.

 

Clever :) tokens tell it, but with 00 may be problem they are used in object itself. Like 00 DA 35 00 00.

 

Yes I did rewrite those jumps - trial and error :) I added 26 at the end and increased/decreased (by 4, and then by 1) the offset until it got hold of the one (26) I added. Than I just decreased it by one and put valid code back. And as D4 was the same as B0, I just did D4+24(hex).

 

I think I saw your SquadSizes, but haven't dig into the code and haven't compared it to the original. Major issue with SquadSize is soldier selection screen and spawn points.

 

Yeah, math will definitely help, but until you explain it to me, it would be easier to learn rocket science!

Edited by Drakous79
Link to comment
Share on other sites

Check XComStrategyGame.upk, class XGItemTree.

function bool CanFacilityBeBuilt(int iFacility)
{
   local TFacility kFacility;

   // End:0x35
   if(iFacility == 0 || iFacility == 20 || iFacility == 21)
   {
       return false;
   }
...

[hex] Length 29 + 4 + 4 + 4 = 35 (3x iFacility) ... started counting right after the header's end.

Edited by Drakous79
Link to comment
Share on other sites

dllimport function Init(XComPlayerController _controller, UIFxsMovie _manager, UI_FxsScreen _screen, XGMission kMission, bool bNav)
{
   local XGShip_Dropship kSkyranger;

   m_iMaxSlots = 0;
   J0x0b:
   // End:0x3f [While If]
   if(5 > m_iMaxSlots)
   {
       m_arrFillOrderIndex.AddItem(m_iMaxSlots);
       ++ m_iMaxSlots;
       // This is an implied JumpToken; Continue!
       goto J0x0b;
   }
   m_arrFillOrderIndex.AddItem(5);
   m_arrFillOrderIndex.AddItem(6);
   PanelInit(_controller, _manager, _screen);
   bCanNavigate = bNav;
   kSkyranger = kMission.GetAssignedSkyranger();
   m_iMaxSlots = kSkyranger.GetCapacity();
   m_iCurrentSelection = ((m_iMaxSlots == 6) ? 0 : 4);
   m_arrUIOptions.Add(6);
}

 

I cant figure this out the jump here is 28! bytes different from the location it should go to. I also do not understand why it changes to dllimport from simulated function either... do you have any idea why it might do that? I really want to get this one to work. It is a massive rewrite.. I couldnt add a local variable so I have re-used the instance variable m_iMaxSlots to work in a whil loop against a const.

 

To fill the space correctly I just shortened the loops and kept 2 of the array adds. I kept the (add) numbers the same as I just want the code to work for now. Then I want to increase squad size.. but the dll import... wtf. Maybe I should just restart and try to see which changes screw it.

 

Any idea?

 

Edited Code:

 

D4 16 00 00 A9 1F 00 00 00 00 00 00 CE 16 00 00
00 00 00 00 00 00 00 00 D4 16 00 00 00 00 00 00
39 00 00 00 24 07 00 00 15 01 00 00 C5 00 00 00
0F 01 C4 16 00 00 25 07 3F 00 97 2C 05 01 C4 16
00 00 16 55 01 C7 16 00 00 0A 00 01 C4 16 00 00
16 A5 01 C4 16 00 00 16 06 0B 00 55 01 C7 16 00
00 03 00 2C 05 16 55 01 C7 16 00 00 03 00 2C 06
16 1B 55 21 00 00 00 00 00 00 00 D4 16 00 00 00
D3 16 00 00 00 D2 16 00 00 4A 16 14 2D 01 C6 16
00 00 2D 00 D0 16 00 00 0F 00 CF 16 00 00 19 00
D1 16 00 00 0A 00 50 37 00 00 00 1B 8B 0E 00 00
00 00 00 00 16 0F 01 C4 16 00 00 19 00 CF 16 00
00 0A 00 E2 3D 00 00 00 1B AF 0E 00 00 00 00 00
00 16 0F 01 C9 16 00 00 45 9A 01 C4 16 00 00 2C
06 16 01 00 25 02 00 2C 04 54 01 C8 16 00 00 2C
06 16 04 0B 53 00 00 00 02 01 02 00 FA 12 00 00
00 00 00 00

 

 

Original:

D4 16 00 00 A9 1F 00 00 00 00 00 00 CE 16 00 00
00 00 00 00 00 00 00 00 D4 16 00 00 00 00 00 00
39 00 00 00 24 07 00 00 15 01 00 00 C5 00 00 00
1B 55 21 00 00 00 00 00 00 00 D4 16 00 00 00 D3
16 00 00 00 D2 16 00 00 4A 16 14 2D 01 C6 16 00
00 2D 00 D0 16 00 00 0F 00 CF 16 00 00 19 00 D1
16 00 00 0A 00 50 37 00 00 00 1B 8B 0E 00 00 00
00 00 00 16 0F 01 C4 16 00 00 19 00 CF 16 00 00
0A 00 E2 3D 00 00 00 1B AF 0E 00 00 00 00 00 00
16 55 01 C7 16 00 00 03 00 2C 03 16 55 01 C7 16
00 00 03 00 2C 02 16 55 01 C7 16 00 00 03 00 2C
04 16 55 01 C7 16 00 00 02 00 26 16 55 01 C7 16
00 00 03 00 2C 05 16 55 01 C7 16 00 00 02 00 25
16 0F 01 C9 16 00 00 45 9A 01 C4 16 00 00 2C 06
16 02 00 2C 05 02 00 2C 04 54 01 C8 16 00 00 2C
06 16 04 0B 53 00 00 00 02 01 02 00 FA 12 00 00
00 00 00 00

Edited by twinj
Link to comment
Share on other sites

Check XComStrategyGame.upk, class XGItemTree.

function bool CanFacilityBeBuilt(int iFacility)
{
   local TFacility kFacility;

   // End:0x35
   if(iFacility == 0 || iFacility == 20 || iFacility == 21)
   {
       return false;
   }
...

[hex] Length 29 + 4 + 4 + 4 = 35 (3x iFacility) ... started counting right after the header's end.

 

Yes! So to simplify the formula is:

 

H = header size in hex. 0x30

FJ = offset to which it should jump to in file in hex. e.g: 0x59 in the above example

I = number of Index objects read.

DWORD = 4 bytes

O = offset for jump

 

We see four bytes on the script but the loader adds 4 more essentially making it 64 bits rather than 32.

FJ - H + (I * DWORD) = O

 

In the code above this would be:

 

0x59 - 0x30 + (3 * 4) = 35.

 

 

You will also note that if you ever need to use something like DynamicArrayAddItemToken where for example you wish to add a value to an array: 55 01 C7 16 00 00 03 00 2C 06 16 Which adds an int constant value of 6 to the array at index 16c7 and the 3 is the number of btyes to the end of the expression

 

If the value is another index you must add the 4 bytes again. e.g: 55 01 C7 16 00 00 0A 00 01 C4 16 00 00 16. One would expect the 0A to be a 6 as the end is 6 bytes away. But the loader expands the index to 8 bytes! So we need to tell it to read 10 bytes!

 

Any way in my jump for the loop the address is 16 bytes out! That is four i's. With the formula.

 

0x5B - 0x30 + (4 * 4) = 3B I have 3F... if I put 3B the code does not decompile correctly... It seems there is 5 indexs?

Edited by twinj
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...