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 


Using AA symbol as parameter in Lua function

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Lua Scripting
View previous topic :: View next topic  
Author Message
LostInTransliteration
How do I cheat?
Reputation: 0

Joined: 25 Aug 2021
Posts: 9

PostPosted: Wed Aug 25, 2021 10:43 am    Post subject: Using AA symbol as parameter in Lua function Reply with quote

Hello everyone, I've been dipping my noob toes into Lua functions to streamline my code a bit and I've run into a problem. I want to be able to store some variable bytes in a Lua table so that when I deactivate the script that overwrites them they can be placed back where they originally were.

The function saves the original address, number of bytes to be saved, and the bytes themselves in a table. It has two parameters where I enter the original address in the form of an AA symbol from an aobscan, and the number of bytes to be saved.

Code:
-- new codesafe function (wip)
codesafet = { } --establishing empty table 'codesafet' to store saved bytes in

codesafef = function (savedaddress, savedlength)
-----------------
--globals and locals
local n
n = 1 --space in codesafet table currently being checked/filled

local csloop
csloop = 0

--converting strings to numbers
savedlength = tonumber(savedlength)

--loop to find empty space in table
while (codesafet[n] ~= nil and codesafet[n] ~= 0) do
  n = n + 2 + codesafet[n+1] --next potentially free space in codesafet table is current position + 2 + length of bytes stored at current position
  end

--saving values in table
codesafet[n] = savedaddress
codesafet[n+1] = savedlength
while (csloop < savedlength) do --this saves each byte as an individual table element
  codesafet[n + 2 + csloop] = readBytes((savedaddress + csloop),1)
  csloop = csloop + 1
end

-----------------
end --end of codesafef function



Here is what it looks like when I use it in a different script:

Code:
{$lua}
autoAssemble([[
//this is just an example, camStructMem was defined and registered in a different script already
define(testaddress,camStructMem+20)
registersymbol(testaddress)

testaddress:
  db AA BB CC DD EE 01 02 03 04
]])
codesafef('testaddress','5')
{$asm}


In theory codesafet should now look like "testaddress, 5, AA, BB, CC, DD, EE".

But executing it brings up the error "attempt to perform arithmetic on a string value (local 'savedaddress')". I think it's because the parameter with the address is stored as a string and that then messes with the symbol parsing. Trying to convert savedaddress to a number using tonumber() just returns nil. Any solutions?
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 138

Joined: 06 Jul 2014
Posts: 4275

PostPosted: Wed Aug 25, 2021 11:52 am    Post subject: Reply with quote

That Lua code is poorly formatted and has way too many comments.

Storing data in a table like that is at best a premature optimization you won't ever notice and at worst an excessively complicated implementation detail you won't recognize in a week.

Calling readBytes one time for every byte you read is ridiculous.

In an AA script, if you have a {$lua} block that calls autoAssemble, you're usually using it wrong.

{$lua} blocks that don't return a string (i.e. only used for side effects) should usually have "if syntaxcheck then return end" at the top.

You're getting that error because 'testaddress' is a string and you're trying to treat it like a number. Use the CE Lua function getAddress to convert a string to an address.


You could just use readmem in AA to backup and restore unknown bytes:
Code:
[ENABLE]
aobscan(INJECT,...)
alloc(backup,8)
registersymbol(INJECT)
registersymbol(backup)

backup:
  readmem(INJECT+2A,8)

INJECT+2A:
  db ...

[DISABLE]

INJECT+2A:
  readmem(backup,8)

dealloc(backup)
unregistersymbol(backup)
unregistersymbol(INJECT)

_________________
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
LostInTransliteration
How do I cheat?
Reputation: 0

Joined: 25 Aug 2021
Posts: 9

PostPosted: Wed Aug 25, 2021 1:06 pm    Post subject: Reply with quote

Thanks for the reply! My original code was actually exactly what you posted in your reply, but I found that it was too inflexible for how many times I was using it - having to manually type out new "backupA", "backupB" and so on every time to make sure the correct bytes were stored in the correct place just meant a lot of room for error when I was switching around code etc.

I tried to just have all needed bytes put into one entry in the table so I could avoid the clunky "one readBytes for every byte" method but just couldn't get it to work properly, the readBytes command never read beyond the first byte in the entry. I'm sure there's a way to go around that but I couldn't figure out how.

The AA block in the Lua code is because I read Lua always gets executed before AA so I thought that was the way to solve that issue? What should I have done instead there?

Will experiment with getAddress, thank you!

And,
>That Lua code is poorly formatted and has way too many comments.
Agree, like I said I'm a noob. Razz Commenting on everything to help me understand as I'm learning.

Edit: getAddress worked, thank you so much! Will be experimenting with 'collapsing' the seperate individual readBytes into one singular read into one singular entry in the table now to hopefully make the code a bit less ridiculous Very Happy .
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 138

Joined: 06 Jul 2014
Posts: 4275

PostPosted: Wed Aug 25, 2021 3:16 pm    Post subject: Reply with quote

Do you really need to backup and restore bytes that often? The only time I do it is when the game is being updated often and offsets in instructions are changing with each update.

I'd make custom AA commands. Put this in the main Lua script or the autorun directory (but don't forget to include it if you release a table to the public):
Code:
do
  local backuptable = {}

  registerAutoAssemblerCommand('backup', function(params, syntaxcheckonly)
    -- split at last ',' in params string
    local address, size = params:match('(.*),([^,]*)$')
    if not address or not size then
      return nil, 'Bad arguments to backup()'
    end

    local realaddress = getAddressSafe(address)
    local realsize = tonumber(size,16)
    if not realaddress or not realsize then
      return nil, 'Bad arguments to backup()'
    end

    if syntaxcheckonly then return '' end

    local t = readBytes(realaddress, realsize, true)
    if not t then
      return nil,('Could not read bytes at address %s (%08X)'):format(address, realaddress)
    end
    backuptable[realaddress] = t
    return ''
  end)

  registerAutoAssemblerCommand('restore', function(params, syntaxcheckonly)
    local address = getAddressSafe(params)
    if not address then
      return nil, 'Bad arguments to restore()'
    end

    if syntaxcheckonly then return '' end

    local t = backuptable[address]
    if not t then
      return nil,('No backup exists for address %s (%08X)'):format(address, params)
    end
    backuptable[address] = nil
    writeBytes(address, t)
    return ''
  end)
end
The enclosing do...end block limits the scope and encapsulates the backuptable variable so nothing else can modify it. You don't need it if you put it in a .lua file in the autorun directory.
I haven't extensively tested this, but it works more or less.

This makes backing up and restoring bytes as easy as this:
Code:
[ENABLE]
globalalloc(foo,4096)
backup(foo+2C,8)

foo+2C:
  db 01 23 45 67 89 AB CD EF

[DISABLE]
restore(foo+2C)

_________________
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
LostInTransliteration
How do I cheat?
Reputation: 0

Joined: 25 Aug 2021
Posts: 9

PostPosted: Fri Aug 27, 2021 1:09 pm    Post subject: Reply with quote

Awesome, thank you! I didn't realize custom AA commands were even possible. I just tested out your code and it works perfectly (as far as I can tell) so I'll be studying your code to see where mine went wrong. If you don't mind me asking, what does the "syntaxcheckonly" parameter and this line in particular do?

Code:
if syntaxcheckonly then return '' end


I read that the AA command function can be called twice, once to check the syntax and once (if applicable) to actually execute it, and I'm assuming it has to do with that but I don't understand how it works.

Also, how would you also let the backup/restore functions be called as Lua functions? I tried just moving the functions "up a layer" like this, hoping it'd turn them into global functions I'd be able to call, but that didn't work:
Code:

function backup (params, syntaxcheckonly)
    -- split at last ',' in params string
    local address, size = params:match('(.*),([^,]*)$')
......................
    backuptable[realaddress] = t
    return ''
end

registerSymbol(backup)
registerAutoAssemblerCommand('backup', backup)
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 Aug 27, 2021 2:50 pm    Post subject: Reply with quote

The AutoAssembler will sometimes perform a dry run of scripts: it'll execute most of what the script does without actually doing anything to the game. Memory won't get allocated, aobscans won't happen, values and instructions won't be written to memory, symbols won't get registered, etc., but the AA will try to pretend like it happened as best it can to make sure the script is correct when you execute it. Among other times, this happens when editing a script in the cheat table and you click "ok" to save changes. If there are any syntax errors the AA detects when performing this dry run, you'll get a warning about it before CE actually saves your changes to the cheat table.

With custom AA commands, {$lua} blocks, or anything else that modifies what the AA does, you should account for this syntax check yourself. The second argument passed to the callback function (i.e. syntaxcheckonly) is used for this purpose. Check it and exit early before you do anything that modifies the game and after you've checked to make sure the syntax is correct (more or less).


registerSymbol is entirely unnecessary there. In Lua, to make a global variable, simply don't use the local keyword:
Code:
function backup (params, syntaxcheckonly)
  ...
end
registerAutoAssemblerCommand('backup', backup)
Using the callback functions as they are now from Lua is awkward because the first parameter is a string of argument(s).
_________________
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
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Lua Scripting 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