Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


Visual Novel: kirikiri Engine & SpoilerAL

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> General Gamehacking
View previous topic :: View next topic  
Author Message
happyTugs
Cheater
Reputation: 0

Joined: 23 Apr 2020
Posts: 26

PostPosted: Fri Jul 16, 2021 11:06 am    Post subject: Visual Novel: kirikiri Engine & SpoilerAL Reply with quote

Hello.

I am currently reversing the kirikiri/2 Engine, and I am a bit stuck.


    a.) Using conventional pointer-scanning in CE will always result in zero finds, unless my settings are incorrect.
    b.) Trying to find what accesses the address will always result with an instruction accessing hundreds of addresses.


Therefore, I have resorted to using SpoilerAL, a Japanese memory-editor that uses a format different from CE.
Instead of .xml, it uses .ssg.

Below is a code excerpt that I have used to try and understand the engine, and the game itself (dualtail VenusBlood Frontier International).

Code:
[group]ADDRESS_DAY
[:MName::VBFI.exe+0x003DFE7C:]+0x1C=>base;
0x48058FEE=>hash;
[:$base:]=>base;
[:$base+0x10:]=>tmp1;
[:$base+0x18:]=>tmp2;
($tmp1&$hash)*0x20=>tmp1;
$tmp2+$tmp1=>ad0;
[:$ad0+0x1C:]=>ad1;


Converting the entire code snippet to lua led me to below...

Code:
[ENABLE]

local baseAddy = readPointer("VBFI.exe+0x003DFE7C") + 0x1C
local hash0 = 0x48058FEE  --string key 'f'
local hash1 = 0x518E5C1C  --string key 'used'
local hash2 = 0x4E9A9417  --?
local hash3 = 0x4D12DEE9  --string key 'resource.name'
local hash4 = 0x00BBECBA  --string key 'now'

local function str2hex(str)
      return string.format('%X',str)
end

local function returnAddressFromHash(base,hash)
      local temp1 = readPointer(base+0x10)
      local temp2 = readPointer(base+0x18)

      local temp1 = (bAnd(temp1,hash)) * 0x20
      local addressStart = temp1 + temp2

      table1 = {addressStart} -- addresses

      local addressNext = addressStart
      for i =2,500,1 do -- 500 is an arbitrary number I used for the iterator
          addressNext = readPointer(addressNext + 0x1C) or 0
          table1[i] = addressNext
      end

      table2 = {} -- valid addresses

      for i =1,500,1 do
          addressNext = readPointer(table1[i] + 0x04)
          if addressNext == hash then
             return table1[i] + 0x10
          end
      end
end

local result1 = returnAddressFromHash(readPointer(baseAddy),hash0)
local result2 = returnAddressFromHash(readPointer(result1),hash1)
local result3 = returnAddressFromHash(readPointer(result2),hash2)
local result4 = returnAddressFromHash(readPointer(result3),hash3)
local result5 = returnAddressFromHash(readPointer(result4),hash4)
print(str2hex(result5))

[DISABLE]



Firstly, my goal is to find a reliable way to access my character data (gold, hp, resources, etc) directly from CE and not SpoilerAL. I accomplished this by writing the lua script above. All that is needed is to input the 'base' and the needed 'hash(es)'.

However, I don't understand how the 'base' and 'hash' were found.

To find out more on this, I searched for relevant topics in the forum, and it led me here: https://forum.cheatengine.org/viewtopic.php?t=571274&highlight=

If I am not mistaken, these 'hashes' were found via string keys (f, used, resource.name, now, etc.).

If that is the case, does anyone know, or can suggest, how I can find these string keys along with the base and hash?

Thank you.

_________________
This is a block of text that can be added to posts you make. There is a 300 character limit
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 138

Joined: 06 Jul 2014
Posts: 4275

PostPosted: Fri Jul 16, 2021 11:52 am    Post subject: Reply with quote

If the data values can easily be found, I'd look up the callstack from accesses to those values and follow the pointer path as I go along.

You'd need to know how to read assembly as well as basic reverse engineering (e.g. identifying hash tables) to do that.

_________________
I don't know where I'm going, but I'll figure it out when I get there.
Back to top
View user's profile Send private message
happyTugs
Cheater
Reputation: 0

Joined: 23 Apr 2020
Posts: 26

PostPosted: Sat Jul 24, 2021 5:11 am    Post subject: To ParkourPenguin Reply with quote

Hello, sorry for the late response.

I have been looking more into this, and I have found out a few things.
    Firstly, I simplified my LUA code (I should probably use recursion).

    Code:
    local function returnAddressFromHash(baseArray,hash)
          local bitKey = readPointer(baseArray+0x10)  --some hex key (0x7F, etc.)
          local subnodeHash = readPointer(baseArray+0x18)  --some address (0x01B66638, etc.)

          local arrayOffset = (bAnd(bitKey,hash)) * 0x20  --algo to find index in dictionary/array
          local subnodeArray = arrayOffset + subnodeHash  -- add offset to subnode

          local linkLimit = 0 -- arbitrary limit of 500
          while (subnodeArray and readInteger(subnodeArray + 0x04) ~= hash and linkLimit < 500) do
                subnodeArray = readPointer(subnodeArray + 0x1C) or 0
                linkLimit = linkLimit + 1
          end

          return subnodeArray + 0x10
    end

    Secondly, I found the answer to my first question (i.e. How were these hashes found?).
    Krkr stores its data into hash tables, and they contain the name of the data it represents.

    In most krkr games, you can access a hash table by finding an object (gold, energy, etc.), and accessing its this pointer.
    The name can be found at offset 0x8 and its hash can be found at offset 0x3C.

    Some hashes below...
    Code:
    stat | 4E9A9417 
    gold | 4D12DEE9
    energy | E8269420
    resource | 541F84B8
    force | 2B56B5F7
    population | D7DA0CC0
    Tilca | DAAC821D
    Ferna | 50E7D0C1
    Thor | 38F2A99A
    Fenrir | 8766694B
    Hel | 1EDDD5C0
    Ferna | 50E7D0C1
    Odin | 51A8AE73
    Regret | C22A91DB
    Thor | 38F2A99A
    Freya | 63E995FD
    Menia | B16ACEBF
    Jormu | A754C23F

    Then input them like so...
    Code:
    local hashT = {
    ['f'] = 0x48058FEE,
    ['used'] = 0x518E5C1C,
    ['game'] = 0x4E9A9417,
    ['stat'] = {
                ['ENERGY'] = 0xE8269420,
                ['FOOD'] = 0xA34B1939,
                ['GOLD'] = 0x4D12DEE9,
                ['RESOURCE'] = 0x541F84B8,
               },
    ['now'] = 0x00BBECBA,
    }

    local function addResources()
          for k,v in pairs(hashT['stat']) do
              local f = returnAddressFromHash(readPointer(baseAddy),hashT['f'])
              local used = returnAddressFromHash(readPointer(f),hashT['used'])
              local game = returnAddressFromHash(readPointer(used),hashT['game'])
              local d = returnAddressFromHash(readPointer(game),hashT['stat'][k])
              local now = returnAddressFromHash(readPointer(d),hashT['now'])
              registerAddress(k,now)
          end
    end

    I know it's ugly, but it will work for now.
Now, I am confused as to how the sequence of the structure was found (i.e. f -> used -> game -> gold -> now).
One way is to unpack the .xp3 files and sift through the .tjs and.ks files piece by piece (.tjs and .ks are modified versions of JavaScript).

But
, suppose you don't do all of that.

How then would you find these particular sequences, and back-trace a piece of data (now) to its beginning (f)?

A few ideas..
    a. Loop through the data's hash table(now) to see if it links back to the primary hash table(f) or its secondary (used, game, stat, etc.).
    b. Finding out what instructions accesses the hash table and work from there (I am doing binary analysis with IDA, along with ClassInformer).
    c. Pray for thyself to reach an epiphany.
What do you think?

As always, thank you ParkourPenguin!

In the meantime, I will do some more research.

_________________
This is a block of text that can be added to posts you make. There is a 300 character limit
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 138

Joined: 06 Jul 2014
Posts: 4275

PostPosted: Sat Jul 24, 2021 12:14 pm    Post subject: Reply with quote

If the source code is available (GPL, so probably), you could simply look at that. There's some fork of kirikiri2 called kirikiriZ I found on github which implements some type of hash table here:
https://github.com/krkrz/krkrz/blob/48c1055852ec948fe13d760bf8482d0b7c6b6265/tjs2/tjsHashSearch.h#L128
Might be what you're looking for. I don't know.

If source code isn't available, resort to basic reverse engineering. I'm neither capable nor willing to teach software reverse engineering over a few forum posts.

_________________
I don't know where I'm going, but I'll figure it out when I get there.
Back to top
View user's profile Send private message
atom0s
Moderator
Reputation: 198

Joined: 25 Jan 2006
Posts: 8516
Location: 127.0.0.1

PostPosted: Sun Jul 25, 2021 10:51 pm    Post subject: This post has 1 review(s) Reply with quote

That hash algorithm is JOAAT.
_________________
- Retired.
Back to top
View user's profile Send private message Visit poster's website
happyTugs
Cheater
Reputation: 0

Joined: 23 Apr 2020
Posts: 26

PostPosted: Sun Aug 15, 2021 5:07 am    Post subject: To ParkourPenguin and atom0s Reply with quote

I have been busy with work; sorry for the late response.

A few updates.
    Firstly, I scoured through the source; the JOAAT algorithm does indeed hash the element's strings.

    Below is my LUA 5.3 implementation.
    Code:
    function joaatHash(str)
          local i,hash,length = 1,0,#str
          while (i ~= length + 1) do
                hash = (hash + string.byte(str,i)) & 0xFFFFFFFF
                hash = (hash + (hash << 10)) & 0xFFFFFFFF
                hash = (hash ~ (hash >> 6)) & 0xFFFFFFFF
                i = i + 1
          end
          hash = (hash + (hash << 3)) & 0xFFFFFFFF
          hash = (hash ~ (hash >> 11)) & 0xFFFFFFFF
          hash = (hash + (hash << 15)) & 0xFFFFFFFF
          return hash
    end

    Furthermore, below is a snippet of the class of the tJSHashTable.
    Code:
    private:
       struct element
       {
          tjs_uint32 Hash;
          tjs_uint32 Flags; // management flag
          char Key[sizeof(KeyT)];
          char Value[sizeof(ValueT)];
          element *Prev; // previous chain item
          element *Next; // next chain item
          element *NPrev; // previous item in the additional order
          element *NNext; // next item in the additional order
       } Elms[HashSize];

       tjs_uint Count;

       element *NFirst; // first item in the additional order
       element *NLast;  // last item in the additional order

    The krkr2 engine uses chaining to resolve collisions, which can explain why some elements point to other elements.
    I'll have to research more on hash-tables to find out how the sequence of buckets are initialized, and how elements are inserted.

    Secondly, I figured out how to find the top-level (global) object: tTjs.

    Since krkr2 is based off the TJS2 Script Engine (VBFI being based off the 'Aug 10 2016' build), all classes are underneath the namespace of TJS.

    There are at least two ways to find the tTJS object: vfTable or static address.
    If the binary is compiled with MS Visual C++, and ships with RTTI, you can use IDA, along with ClassInformer, to find the TJS::tTJS::vfTable!
    From there, memory scan for the vfTable address (ensure writable scans), and you find your tTJS object.

    If not, there are several UNICODE strings used inside the tTJS constructor as shown below.
    Code:
    "version"
    "the global object" (this x-ref is generally unique)
    "Array"
    "Dictionary"

    The address for the tTJS vfTable is found in the prologue of the tTJS constructor, as shown below.
    Code:
    v40 = &v26;
    v1 = this;
    v38 = this;
    *this = &TJS::tTJS::`vftable';
    this[3] = 0;
    this[4] = 0;
    this[5] = 0;
    v41 = 0;
    this[1] = 1;
    this[6] = 0;
    this[2] = 0;

    Of course, there may be other, similar implementations, so please check yourself.

    On the other hand, there is a static address that points to the tTJS object, and is x-ref'd numerous times.

    However, there are krkr games compiled with Borland C++, as such, these techniques may not apply; I will investigate more when I have time.
Some other notable information.
Kirikiri is a scripting engine, and utilizes the KAG (KiriKiri Adventure Game System) framework and and an object-oriented scripting language called TJS (Kirikiri TPV JavaScript).

VBFI is based off KiriKiri2 (krkr2) in conjunction with the KAG '3.32 stable rev. 2' framework, and the TJS2 scripting language.
Moreover, there are other implmentations of KiriKiri, such as in C#: https://github.com/fantasydr/krkr-cs

Thank you atom0s for mentioning the JOAAT hash algorithm!
I would not have made that connection, since I am not familar with hashes and hash-tables.

Also, thank you ParkourPenguin for sending me a link to the source code!
I learned significantly on krkr2's hash-table implementation, and made meaningful connections from source to binary.

In the meantime, I will do some more research.

_________________
This is a block of text that can be added to posts you make. There is a 300 character limit
Back to top
View user's profile Send private message
happyTugs
Cheater
Reputation: 0

Joined: 23 Apr 2020
Posts: 26

PostPosted: Sun Aug 22, 2021 9:30 am    Post subject: My Obsevations Reply with quote

Hello again.

I finally figured out how to find the correct sequence of hashes to locate a certain element.
Because I have been busy, my write-up was delayed.
This will be a long post; hopefully, this will be helpful for those concerned in the future.
    Firstly, the krkr engine has a top-level object that contains a global hash-table to other objects, functions, data, etc.
    Respectively, they are called TJS::tTJS and the TJS::tTJSDictionaryObject.
    In a previous post, I covered how to find the static address of tTJS by finding the address of TJS::tJS::vfTable.

    All objects instantiated from the TJS::tTJS class have an address that points to the TJS::tJS::vfTable, generally found at its 'this' pointer.
    By finding the TJS::tJS::vfTable address, and searching in memory which object contains that address, you effectively find a tTJS object (keep in mind, there is usually only one tTJS object).

    Finding the tTJS object, you can then search in memory what static address points to the tTJS object.
    Once you find the static address that points to tTJS, you can then read from this address and
    add an offset to find tTJSDictionaryObject (tTJSDict for short).

    Depending on the version of the game engine (krkr or krkr2), the offset for tTJSDict may vary.
    From what I have dealt with, the offset for tTJSDict for krkr is at offset 0x30; for krkr2, tTJSDict is at offset 0x18.

    Now, tTJSDictionaryObject is a hash-table: a data structure that maps keys to values, and is primarily defined by its hash algorithm.
    Both krkr, and krkr2, use the Jenkins-One-At-A-Time (JOAAT) hash algorithm to calculate the index of a tTJSElement, or value, given a string.

    If you are so inclined, a kind freelance programmer by the username of jin1016, posted the source code for krkr2, and is also developing krkrz (a fork of krkr), at github: https://github.com/krkrz/krkr2
    Below are the class information and functions to retrieve a tTJSElement from a tTJSDictionaryObject for krkr.
    Code:
    ---------
    --CLASS--
    ---------

    local tTJSElement = {
        This = 0x0,
        Hash = 0x4,
        Value = 0x10,
        Type = 0x18,
        Next = 0x1C,
    }

    local tTJSDictionaryObject = {
          Size = 0x10,
          Max = 0x14,
          Array = 0x18,
    }

    local tNameArray = {
          Name = 0x8,
    }

    -------------
    --FUNCTIONS--
    -------------

    --[[
        Function Overview: Hash a sequence of strings, delimited by a period(.)
                           with JOAAT, and use that hash to index a
                           TJSDictionaryObject to retrieve the respective
                           TJSElement.

         a. joaatHash(str)
         b. split(inputstr, sep)
         c. hashString(str)
         d. rAFH(TJSDictionaryObject,hashT) a.k.a returnAddressFromHash
    ]]

    --https://en.wikipedia.org/wiki/Jenkins_hash_function
    local function joaatHash(str)
          local i,hash,length = 1,0,#str
          while (i ~= length + 1) do
                hash = (hash + string.byte(str,i)) & 0xFFFFFFFF
                hash = (hash + (hash << 10)) & 0xFFFFFFFF
                hash = (hash ~ (hash >> 6)) & 0xFFFFFFFF
                i = i + 1
          end
          hash = (hash + (hash << 3)) & 0xFFFFFFFF
          hash = (hash ~ (hash >> 11)) & 0xFFFFFFFF
          hash = (hash + (hash << 15)) & 0xFFFFFFFF
          return hash
    end

    --https://stackoverflow.com/questions/1426954/split-string-in-lua
    --thank you, bart.
    local function split(inputstr, sep)
          sep=sep or '%s'
          local t={}
          for field,s in string.gmatch(inputstr, "([^"..sep.."]*)("..sep.."?)") do
              table.insert(t,field)
              if s=="" then
                 return t
              end
          end
    end

    local function hashString(str,sep)
          local stringHash = {}
          for k,v in pairs(split(str,sep)) do
              stringHash[k] = joaatHash(v)
          end
          return stringHash
    end

    --given a table of hashes, return the value of a tTJSelement from a tTJSDictionaryObject
    local function rAFH(TJSDictionaryObject,hashT)
          local sz = readInteger(TJSDictionaryObject+tTJSDictionaryObject.Size)
          local bucketArray = readPointer(TJSDictionaryObject+tTJSDictionaryObject.Array)
          local arrayOffset = (bAnd(sz,hashT[1])) * 0x20
          local TJSElement = bucketArray + arrayOffset

          local counter = 0
          while (TJSElement and
                readInteger(TJSElement+ tTJSElement.Hash) ~= hashT[1] and
                counter ~= sz) do
                TJSElement = readPointer(TJSElement + tTJSElement.Next)
                counter = counter + 1
          end
          if TJSElement == nil then
             printf('The string hash (%08X) is not indexed in the dictionary (%08X).',hashT[1],TJSDictionaryObject)
             return 0
          end
          if #hashT ~= 1 then
             table.remove(hashT,1)
             return rAFH(readPointer(TJSElement + tTJSElement.Value),hashT)
          end
          return TJSElement + tTJSElement.Value
    end

    Below are some usage examples.
    Code:
    local tTJS = readPointer('game.exe+123456')
    local tTJSDictionary = readPointer(tTJS+0x30)
    local kingLifeAddress = rAFH(tTJSDictionary,hashString('game.country.person._life','.'))
    printf('%X',kingLifeAddress)

    Secondly, how does one find the sequence of a hash (i.e. 'game.country.person._life')?
    Since we know that tTJS is the top-level object that contains a tTJSDictionary, or hash-table, to other tTJSElements, we can loop through its hash-table, store the elements into a table, check if those elements contain another hash-table, and repeat the process for a user-defined amount.
    I was able to find 'game.country.person._life' through this method.
    I got this idea since I have dumped Unreal Engine 3 before, and decided to apply that knowledge here (which was rather exciting).
    Code:
    --------
    --DATA--
    --------

    local FOLDER_PATH = 'C:\\Users\\'..os.getenv('USERNAME')..'\\Desktop\\'..process

    -------------
    --FUNCTIONS--
    -------------

    --[[
        Function Overview: Process a TJSDictionaryObject, and write its
                           results (i.e. TJSElements contained inside the TJSDictionaryObject)
                           onto a .txt file.

         a. makeFile(FOLDER_PATH,fileName)
         b. closeFile(handle)
         c. writeStrToFile(handle,str)
         d. checkForTJSDictionary(TJSElement,TJSElementValue)
         e. TJSDictionaryDumper(TJSDictionaryObject,parentOf)
         f. cycleElementBucket(fileHandle,TJSElementBucket,level,limit,tabStr)
            (1)cycles through a dictionary for elements
            (2)writes the attributes of an element to a .txt file
            (3)check if the element has a dictionary
            (4)cycle that dictionary
         g. TJSDictionaryDumperMain(TJSDictionaryObject,parentName,loopLimit)
    ]]

    local function makeFile(FOLDER_PATH,fileName)
          os.execute('mkdir '..FOLDER_PATH)
          local file,err = io.open(FOLDER_PATH..'\\'..fileName..'.txt', 'w+')
          assert(file,err)
          return file
    end

    local function closeFile(handle)
          handle:close()
          return
    end

    local function writeStrToFile(handle,str)
          handle:write(str)
          return
    end

    local function checkForTJSDictionary(TJSElement,TJSElementValue)
          if readPointer(TJSElementValue) ~= nil then
            local dictHasSize = readInteger(TJSElementValue +tTJSDictionaryObject.Size) ==
                  readInteger(TJSElementValue + tTJSDictionaryObject.Max)-1
            if dictHasSize then return true end
          end
          return false
    end

    --return a table of elements contained in a TJSDictionaryObject
    local function TJSDictionaryDumper(TJSDictionaryObject,parentOf)
          if TJSDictionaryObject == nil then return nil end
          local sz = readInteger(TJSDictionaryObject+tTJSDictionaryObject.Size)
          local BucketArray = readPointer(TJSDictionaryObject+tTJSDictionaryObject.Array)
          local elementBucket = {}
          for index=0x0,sz,0x1 do
              local ArrayOffset = index * 0x20
              local TJSElement = ArrayOffset + BucketArray
              local TJSElementValue = readPointer(TJSElement+tTJSElement.Value)
              local TJSElementThis = readPointer(TJSElement + tTJSElement.This) or 0
              local name = readString(TJSElementThis + tNameArray.Name,50,true)
              if name ~= nil then
                 table.insert(elementBucket,{
                 ['ParentOf'] = parentOf or '',
                 ['Name'] = name,
                 ['Address'] = TJSElement,
                 ['Value'] = TJSElementValue,
                 ['HasDictionary'] = checkForTJSDictionary(TJSElement,TJSElementValue)})
              end
          end
          return elementBucket
    end

    --recursive function that cycles through an element bucket for dictionaries and
    --writes them to a .txt file
    local function cycleElementBucket(fileHandle,TJSElementBucket,level,limit,tabStr)
          if level == limit then return end
          for k,v in pairs(TJSElementBucket) do
              local formatStr = string.format('%s%X -> %08X : %s\n',tabStr,v['Address'],v['Value'],v['ParentOf']..'.'..v['Name'])
              writeStrToFile(fileHandle,formatStr)
              if v['HasDictionary'] == true then
                 local nextDictionary = TJSDictionaryDumper(v['Value'],v['ParentOf']..'.'..v['Name'])
                 cycleElementBucket(fileHandle,nextDictionary,level+1,limit,tabStr..'\t')
              end
          end
          return
    end

    local function TJSDictionaryDumperMain(TJSDictionaryObject,parentName,loopLimit)
          local fileHandle = makeFile(FOLDER_PATH,parentName)
          local elementBucket = TJSDictionaryDumper(TJSDictionaryObject,parentName)
          local recursionLevel = 0 --default level for recursion
          local emptyString = '' --used to concatenate tabs
          cycleElementBucket(fileHandle,elementBucket,recursionLevel,loopLimit,emptyString)
          closeFile(fileHandle)
          return
    end

    Below are some usage examples.
    Code:
    local tTJS = readPointer('game.exe+123456')
    local tTJSDictionary = readPointer(tTJS+0x30)
    local name = '' -- name is an empty string since I am dumping from the tTJS object
    local recursionLimit = 2 --cycle amount
    TJSDictionaryDumperMain(tTJSDictionary,name,recursionLimit)

    Executing the above code results in the below snippet of the produced text file.
    Code:
    B87EAF8 -> 0A0F7610 : .game
       18FF4220 -> 0A013BFC : .game.prisonCmdOpened
       18FF4240 -> 086E0DE8 : .game.routeCostReset
       18FF4280 -> 00000000 : .game.curChara
       18FF43C0 -> 030048AC : .game.onStableStateChanged
       18FF44E0 -> 00000000 : .game.winner
       18FF4580 -> 0877A800 : .game.layersReset
       18FF45A0 -> 0C69C704 : .game.troopList

    For the parent name of tTJS, I used an empty string because the tTJS object is the basis for all elements. If you are trying to find a hash, the actual hash string would be...
    Code:
    .game.winner - > game.winner
    If I am dumping the tTJSDictionaryObject from the 'game' element, then I would use the code below.
    Code:
    local tTJS = readPointer('game.exe+123456')
    local gameAddressValue = rAFH(readPointer(baseAddress+0x30),hashString('game'))
    local tTJSDictionary = readPointer(gameAddressValue)
    local name = 'game'
    local recursionLimit = 2 --cycle amount
    TJSDictionaryDumperMain(tTJSDictionary,name,recursionLimit)

    Thirdly, krkr2 is slightly different.
    While krkr2 does store hash-tables within elements, krkr2 stores the next element by chaining (i.e. a pointer to another tTJSElement, not a tTJSDictionaryObject).
    I will have to investigate more on this later on when I have some time.
    But, the overall process of calculating hashes and retrieving elements through a hashed string are still the same.

This brings me to the end; I will post more on krkr and krkr2 when I have some more time.
If you have any suggestions where I can improve my LUA, please let me know!

Thank you. Smile

_________________
This is a block of text that can be added to posts you make. There is a 300 character limit
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> General Gamehacking All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites