KoA: Reckoning - KoreVM (HavokScript) Information

Information and research into the KoA: Reckoning game files and main game executable.
Post Reply
User avatar
atom0s
Site Admin
Posts: 33
Joined: Sun Apr 08, 2018 2:54 pm
Location: 127.0.0.1
Contact:

KoA: Reckoning - KoreVM (HavokScript) Information

Post by atom0s » Thu Apr 12, 2018 8:22 pm

Kingdoms of Amalur: Reckoning uses a custom version of Lua called KoreVM (aka HavokScript). The exact version used by KoA:R is 2.5 r7786. This is a custom version of Lua, based on Lua 5.1, but heavily rewritten into a class / OOP style setup. This also causes A LOT of the Lua API to be either rewritten, altered, removed, or force-inlined into other functions. Because of this, some direct calls, such as lua_gettop do not exist as normal API calls, but are instead inlined into the function referencing them.
Here is a list of Lua functions and the KoA:R equivalent address to them. This list is what I deem required / useful for modding things in the Lua state itself. Without some of these, very basic Lua usage is near impossible.

This is based on the client exe:

Code: Select all

    Reckoning.exe - Steam Version - v1.0.0.2 (1.0.0.1) 
        - CRC32 : DC256122
        - MD5   : 9863F5D6754DAC28CBB96808015E2089
        - SHA1  : 91CC28B28AE6E4926A89DF5EAD8DB2B7D974AE46
KoA: Reckoning - KoreVM Lua Types

Code: Select all

00 - nil
01 - boolean
02 - userdata | luserdata
03 - number
04 - string
05 - table
06 - function | invalid function
07 - userdata
08 - thread
09 - function | ifunction
10 - function | cfunction
11 - ui64
12 - struct
Site Owner

Want to donate to say thanks? Donate via Paypal
https://www.paypal.me/atom0s
User avatar
atom0s
Site Admin
Posts: 33
Joined: Sun Apr 08, 2018 2:54 pm
Location: 127.0.0.1
Contact:

Re: KoA: Reckoning - KoreVM (HavokScript) Information

Post by atom0s » Thu Apr 12, 2018 8:22 pm

  1. /*
  2. ----------------------------------------------------------------------------------------------------
  3. Kingdoms of Amalur: Reckoning - Lua (KoreVM) Information
  4. Research Paper by atom0s [atom0s@live.com]
  5. Version: 1.0.0
  6. ----------------------------------------------------------------------------------------------------
  7.  
  8.     Some entries are not valid for certain things, such as specific versions of the file!
  9.     Please read the notes carefully!
  10.  
  11.     This data is based on the following game executable:
  12.     Reckoning.exe - Steam Version - v1.0.0.2 (1.0.0.1)
  13.         - CRC32 : DC256122
  14.         - MD5   : 9863F5D6754DAC28CBB96808015E2089
  15.         - SHA1  : 91CC28B28AE6E4926A89DF5EAD8DB2B7D974AE46
  16.    
  17. */
  18.  
  19. /*
  20. lua_call                        // sub_906310
  21. lua_close                       //
  22. lua_createtable                 // sub_8F61C0
  23. lua_gc                          //
  24. lua_getfield                    //
  25. lua_getmetatable                // sub_8FB8C0 (?) (non-important)
  26. lua_gettop                      // (sub_906CE0) result = (*(_DWORD *)(a1 + 36) - *(_DWORD *)(a1 + 40)) >> 3;
  27. lua_isstring                    // (see notes below 'ttisstring')
  28. lua_newstate                    // sub_913C50
  29. lua_next                        //
  30. lua_pcall                       // sub_911560
  31. lua_pushboolean                 //
  32. lua_pushcclosure                // sub_8F8B20 - Thanks Kender
  33. lua_pushfstring                 // sub_8FD8F0
  34. lua_pushlightuserdata           //
  35. lua_pushlstring                 // sub_466120
  36. lua_pushnil                     //
  37. lua_pushnumber                  //
  38. lua_pushstring                  // sub_466360
  39. lua_pushvalue                   // sub_467460 (?)
  40. lua_rawset                      //
  41. lua_insert                      //
  42. lua_remove                      //
  43. lua_replace                     //
  44. lua_setfield                    // sub_467550
  45. lua_setmetatable                //
  46. lua_settop                      //
  47. lua_toboolean                   //
  48. lua_tonumber                    //
  49. lua_type                        // sub_8F35C0 (?)
  50. lua_tolstring                   // sub_8FE840 (?)
  51.  
  52. luaL_argerror                   // sub_8F5530
  53. luaL_checklstring               // sub_9042B0
  54. luaL_checkoption                // sub_906530
  55. luaL_checktype                  // sub_8FDAA0 (?)
  56. luaL_getmetafield               // sub_9145C0 (non-important)
  57. luaL_loadbuffer                 //
  58. luaL_error                      // sub_8F26D0
  59. luaL_findtable                  // sub_914400
  60. luaL_loadfile                   //
  61. luaL_loadstring                 //
  62. luaL_openlibs                   // sub_9118A0 (?)
  63. luaL_openlib                    // sub_9186B0 - Thanks Kender
  64. */
  65. ----------------------------------------------------------------------------------------------------
  66. Inlined Function Notes
  67. ----------------------------------------------------------------------------------------------------
  68.  
  69. /**
  70.  * #define ttisstring(o)    (ttype(o) == LUA_TSTRING)
  71.  */
  72. v3 = *(_DWORD *)(a1 + 40);
  73. if ( (*(_BYTE *)v3 & 0xF) == 4 )
  74. { ... }
  75.  
  76. /**
  77.  * #define ttistable(o) (ttype(o) == LUA_TTABLE)
  78.  */
  79. v3 = *(_DWORD *)(a1 + 40);
  80. if ( (*(_BYTE *)v3 & 0xF) == 5 )
  81. { ... }
  82.  
  83. /**
  84.  * sub_9199F0 - package.seeall
  85.  *
  86.  * The first chunk is inlined version of:
  87.  * lua_pushvalue(L, LUA_GLOBALSINDEX);
  88.  *
  89.  */
  90. v12 = *(_DWORD *)(a1 + 36);                     // gets the current top..
  91. *(_DWORD *)v12 = *(_DWORD *)(a1 + 56);          // 5 (table)
  92. *(_DWORD *)(v12 + 4) = *(_DWORD *)(a1 + 60);    // LUA_GLOBALSINDEX table object is held in state + 60
  93. *(_DWORD *)(a1 + 36) = v12 + 8;                 // sets the new top..
  94. sub_467550("__index", a1, -2);
  95.  
  96. /**
  97.  * The above inlines lua_pushvalue, which inlines all of its features too.
  98.  *
  99.  * When api_incr_top is called, it appears that our state value sizes are considered 8 bytes wide.
  100.  * This should only be top += 1; but instead its incrementing by 8 (divide by 2 as we see in other inlines.)
  101.  */
  102. LUA_API void lua_pushvalue (lua_State *L, int idx) {
  103.   lua_lock(L);                              // unused
  104.   setobj2s(L, L->top, index2adr(L, idx));   // first 3 lines of the above code..
  105.   api_incr_top(L);                          // last line before the __index call line..
  106.   lua_unlock(L);                            // unused
  107. }
  108.  
  109. /**/
Site Owner

Want to donate to say thanks? Donate via Paypal
https://www.paypal.me/atom0s
User avatar
atom0s
Site Admin
Posts: 33
Joined: Sun Apr 08, 2018 2:54 pm
Location: 127.0.0.1
Contact:

Re: KoA: Reckoning - KoreVM (HavokScript) Information

Post by atom0s » Thu Apr 12, 2018 8:23 pm

Misc Constants

Code: Select all

Constants.xp_to_level_table:
K: 1 -- V:500
K: 2 -- V:1600
K: 3 -- V:3400
K: 4 -- V:6000
K: 5 -- V:9500
K: 6 -- V:14000
K: 7 -- V:19700
K: 8 -- V:26600
K: 9 -- V:34900
K: 10 -- V:44600
K: 11 -- V:55900
K: 12 -- V:68800
K: 13 -- V:83500
K: 14 -- V:100000
K: 15 -- V:118500
K: 16 -- V:139000
K: 17 -- V:161500
K: 18 -- V:186000
K: 19 -- V:213500
K: 20 -- V:244000
K: 21 -- V:277500
K: 22 -- V:314000
K: 23 -- V:354500
K: 24 -- V:399000
K: 25 -- V:447500
K: 26 -- V:500000
K: 27 -- V:557500
K: 28 -- V:620000
K: 29 -- V:687500
K: 30 -- V:760000
K: 31 -- V:839500
K: 32 -- V:926000
K: 33 -- V:1019500
K: 34 -- V:1120000
K: 35 -- V:1230500
K: 36 -- V:1351000
K: 37 -- V:1481500
K: 38 -- V:1622000
K: 39 -- V:1777500
K: 40 -- V:1948000
K: 41 -- V:2133500
K: 42 -- V:2334000
K: 43 -- V:2554500
K: 44 -- V:2795000
K: 45 -- V:3055500
Site Owner

Want to donate to say thanks? Donate via Paypal
https://www.paypal.me/atom0s
User avatar
Kender
Posts: 9
Joined: Fri Apr 13, 2018 8:40 am

Re: KoA: Reckoning - KoreVM (HavokScript) Information

Post by Kender » Fri Apr 13, 2018 11:46 am

I think lua_type is sub_42D4C0
I have pretty much the same lua functions identified, and a few more:

Code: Select all

luaL_loadstring	sub_91DF70
luaL_loadfile	sub_91E1C0
lua_tonumber	sub_904E30
lua_tostring	sub_917960
lua_remove	sub_904A40
lua_rawset	sub_906C30
See also https://github.com/Kender2/amalur/blob/ ... ctions.txt which lists all the functions in the order they are registered.
User avatar
atom0s
Site Admin
Posts: 33
Joined: Sun Apr 08, 2018 2:54 pm
Location: 127.0.0.1
Contact:

Re: KoA: Reckoning - KoreVM (HavokScript) Information

Post by atom0s » Fri Apr 13, 2018 8:40 pm

The issue with the function dump you posted is that those are the Lua sided exposures and not the internal usage from the C API calls. While they are helpful to find things through tracing backward, the calls themselves are not the lua_ or luaL_ API calls specifically.

For example, lua_tonumber that you have is actually just the 'tonumber' Lua sided call. This points backward to:
- luaB_tonumber

Inside of Lua from the C side of things, lua_tonumber actually calls:

Code: Select all

LUA_API lua_Number lua_tonumber (lua_State *L, int idx) {
  TValue n;
  const TValue *o = index2adr(L, idx);
  if (tonumber(o, &n))
    return nvalue(o);
  else
    return 0;
}

//..
#define tonumber(o,n)	(ttype(o) == LUA_TNUMBER || \
                         (((o) = luaV_tonumber(o,n)) != NULL))

//..

const TValue *luaV_tonumber (const TValue *obj, TValue *n) {
  lua_Number num;
  if (ttisnumber(obj)) return obj;
  if (ttisstring(obj) && luaO_str2d(svalue(obj), &num)) {
    setnvalue(n, num);
    return n;
  }
  else
    return NULL;
}
And so on. Basically, this means that calling your lua_tonumber wont properly handle the C side of the API if used as if it were the real lua_tonumber.
Site Owner

Want to donate to say thanks? Donate via Paypal
https://www.paypal.me/atom0s
User avatar
Kender
Posts: 9
Joined: Fri Apr 13, 2018 8:40 am

Re: KoA: Reckoning - KoreVM (HavokScript) Information

Post by Kender » Fri Apr 13, 2018 10:34 pm

Ah yes, that makes sense.

Apart from that list I do have:

Code: Select all

lua_pushcclosure	008F8B20 
luaL_openlib	9186B0
luaL_where	8FD9B0
luaM_malloc	47AFB7
luaO_log2	8F09F0
luaV_execute	472850
lua_call	906310
lua_getinfo	90E0E0
lua_type	42D4C0
But I don't know how useful that is.
User avatar
atom0s
Site Admin
Posts: 33
Joined: Sun Apr 08, 2018 2:54 pm
Location: 127.0.0.1
Contact:

Re: KoA: Reckoning - KoreVM (HavokScript) Information

Post by atom0s » Fri Apr 13, 2018 10:43 pm

I can take a look and cross-check them to see if they are valid for the C side of things.

As for things to work on, anything that you are interested in really. Nothing really set in stone at this point. rMod opens up ease of keeping things in a central project and allows for rapid development using addons and such. If you are interested in building things for it feel free to dig in and ask any questions you have.
Site Owner

Want to donate to say thanks? Donate via Paypal
https://www.paypal.me/atom0s
User avatar
atom0s
Site Admin
Posts: 33
Joined: Sun Apr 08, 2018 2:54 pm
Location: 127.0.0.1
Contact:

Re: KoA: Reckoning - KoreVM (HavokScript) Information

Post by atom0s » Fri Apr 13, 2018 10:56 pm

For your lua_pushcclosure, an easy way to find it is to locate lua_setfield via referencing the normal Lua 5.1 code base. You can find several strings that will align to it. In KoreVM inside of Reckoning.exe, lua_setfield is: 'sub_467550'

One of the main uses of lua_setfield is within 'luaI_openlib'. Which you can find inside of Reckoning via its strings that it uses:
- _LOADED
- name conflict for module

From there you can compare the code between the real Lua output and IDA's hexrays pseudo generation. The last loop within the function includes using setfield and pushcclosure:

Code: Select all

  for (; l->name; l++) {
    int i;
    for (i=0; i<nup; i++)  /* copy upvalues to the top */
      lua_pushvalue(L, -nup);
    lua_pushcclosure(L, l->func, nup);
    lua_setfield(L, -(nup+2), l->name);
  }
Your address for pushcclosure is here too:

Code: Select all

      v24 = sub_8F8B20(v18, a1, *(_DWORD *)(v5 + 4), a5, 0);
      v25 = (_DWORD *)(*(_DWORD *)(a1 + 36) - 8 * v18);
      v25[1] = v24;
The only issue I see though is that it looks like it is used in a different manner with KoreVM. Instead of pushing onto the stack and continuing, its instead returning a value that is then used afterward. This, to me, looks like sub_8F8B20 is creating some type of object that is returned into v24, then the next line is determining the stack position to push it onto. (a1 + 36) is part of the stack top usage.

v25[1] here is placing into the 2nd DWORD of what v25 equals. So I do feel yours is correct just that the C usage is going to differ than stock Lua.
Site Owner

Want to donate to say thanks? Donate via Paypal
https://www.paypal.me/atom0s
Post Reply