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 


How can we improve existing code?

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Lua Scripting
View previous topic :: View next topic  
Author Message
ByTransient
Expert Cheater
Reputation: 5

Joined: 05 Sep 2020
Posts: 240

PostPosted: Sat Jan 30, 2021 5:03 pm    Post subject: How can we improve existing code? Reply with quote

The code design below works fine for a single result.
But it doesn't work when there is more than one result with the same name.
Example; If "sAobs" finds multiple results, it concentrates on the first result and possibly gives "nil".
But if "sAobs" finds a single result, the change of that result is successful.

Code:
function ScanAndReScan(sAob,vDouble,nLength,sOffset)
   if ((type(sAob)=='string' and #sAob > 1) and type(vDouble)=='number' and type(nLength)=='number') then
      local sPattern,vPattern = sAob:gsub("[^%x%?]+",""),doubleToByteTable(vDouble);  -- clean AoB from any unneeded character; converts Double to bytes;
      local sOffset = ((#sPattern % 2 == 0) and (type(sOffset)=='number' and sOffset or (#sPattern // 2)) or error('pattern is incorrect')); -- read bytes starting offset;
      local sAobs = AOBScan(sPattern); -- yes memscan is faster, but I'm lazy.
      if (sAobs) then
         -- local vAobs = AOBScan(readBytes(sAobs[0]..'+'..sOffset,8)); -- New scan for the double value; thought the bytes after the aob represent double, but I see we need to format it;
         local vAobs = AOBScan(unpack(doubleToByteTable(readString(tonumber(sAobs[0],16)+sOffset,nLength)))); -- converts address from base16 to base10 and adds X offset, reads nLength from given address and converts to double (returns table contains double bytes); unpack the table and scans the double bytes;
          --print('sAOB found @:',sAobs[0],' - found targer double count :', (vAobs and vAobs.count or 'nil'));
showMessage('sAOB found @:',sAobs[0],'\nReplace Code Count :', (vAobs and vAobs.count or 'nil'));
         if (vAobs) then
            for i=0,vAobs.count-1 do -- there's might be lot's of results, including of memory areas you maybe wouldn't want to change
               writeBytes(vAobs[i],vPattern); -- writes the double value given after it was converted to bytes...
                --print("Modified address @:",vAobs[i]);

            end
            vAobs.destroy(); -- cleanup
         end
         sAobs.destroy();
      end
   end
end


Is there a solution to search multiple results of "sAobs" (Double) and replace them all in order?
Thanks in advance for the inserts and ideas.
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 152

Joined: 06 Jul 2014
Posts: 4711

PostPosted: Sat Jan 30, 2021 11:39 pm    Post subject: Reply with quote

Iterate over sAobs the same way you do vAobs: wrap it all in a for loop and replace sAobs[0] with sAobs[loopvar].

Other stuff:
  • Lua is a scripting language. It doesn't have strong typing by design. Don't try to make it yourself unless you have good reason to.
  • Short circuiting and/or operators is nice one time. Start nesting it and it quickly becomes an eye sore.
  • Pass a relevant protectionflags argument on both aobscans and relevant alignment arguments on the scan for the double (maybe the string too, if you can spot a pattern). Look up documentation for "AOBScan" in celua.txt.
  • You don't need parenthesis around the conditional expressions in if statements or around and/or expressions.
  • "AOBScan(unpack(doubleToByteTable(readString(tonumber(..." is a little dense. You might want to do some error checking here too (e.g. if readString returns a string Lua can't convert to a number).

_________________
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
ByTransient
Expert Cheater
Reputation: 5

Joined: 05 Sep 2020
Posts: 240

PostPosted: Sun Jan 31, 2021 3:25 am    Post subject: Reply with quote

Again, you explained the situation to the finest detail.
I hope that your disciplined state doesn't tire you.
I stuck with the last point you mentioned and stopped editing the advice code above.

Below I started an incomplete and messy code.
Instead of adding "for", I added 2 "memo" to the code below.
I wanted to see what would happen one by one.

The code works, but I can't make it shorter or remove the extra statements.
For this reason, it seems that I will add the code to Trainer as it is.


Code:
if kform then form.destroy() kform = nil end
kform = createForm()
kform.Position = poDesktopCenter
kform.Width = 310
kform.Height = 250

local km1=createMemo(kform)
km1.Height=230 km1.Width=140 km1.Left=10 km1.Top=10
km1.WordWrap=false km1.ScrollBars="ssAutoBoth"

local km2=createMemo(kform)
km2.Height=230 km2.Width=140 km2.Left=160 km2.Top=10

kmtimer=createTimer(kform) kmtimer.Interval=300 kmtimer.Enabled=false

function getByteString11(address, bytecount)
 local bytes = readBytes(address, bytecount, true)
 if bytes then
 local result = ""
 for i = 1, #bytes do
 if #result > 0 then result = result .. " " end
 result = result .. string.format("%02X", bytes[i]) end
 return result end
end

function string.fromhex133(str1)
 str1 = str1:gsub("%s+", "")
 str1 = string.gsub(str1, "%s+", "")
 return (str1:gsub('..', function (cc)
 return string.char(tonumber(cc, 16)) end))
end

function byteTableToAobString(t)
  for k,v in ipairs(t) do
    t[k] = ('%02X'):format(v)
  end
  return table.concat(t, ' ')
end

function doublers11(str111)
  newvalue = str111
  newvalue = tonumber(newvalue)
  if not newvalue then return end -- if not a number we're done
 newvalue = doubleToByteTable(newvalue) -- alternatively
  newvalue = byteTableToAobString(newvalue)
  str111=('%s'):format(newvalue, newvalue)

 return str111
end
-----------------------
local cnts1=""
local repto=""
local rss3=""
local n2=0
local good=0
local n1=0

function adres11Replace()
cnts1=km2.Lines.Count
if good==cnts1 then
kmtimer.Enabled=false
showMessage("Total "..n2.." code change successful.");
else
rsd1=doublers11(km2.Lines[good])
print("rsd1 - "..rsd1)
good=good+1
kmtimer.Enabled=false
local rs3=AOBScan(rsd1)
if(rs3 == nil) then
showMessage("Double search not found!");
else
for n=0,rs3.Count-1 do
if autoAssemble(rs3[n]..":\ndb "..repto) then kmtimer.Enabled=true end end
n1=rs3.Count - 1
n2=n2 + n1
object_destroy(rs3);
end
end
end
kmtimer.OnTimer=adres11Replace

function getline()
for i = 0, km1.Lines.Count do
local caption = km1.Lines[i]
if caption then
x=km2.Lines.Text
 z = caption
 if string.find(x, "%f[%d]"..z.."%f[%D]") ~= nil then

else
km2.Lines.Add(caption)
if i==km1.Lines.Count then
cnt1=km2.Lines.Count
good=0 adres11Replace() end
end
end
end
 end
-------------------------------
function adres11(from,cnt2,cnt3)
n2=0
good=0
local sl = AOBScan(from);
if(sl == nil) then
showMessage("No code found!");
else
j = stringlist_getCount(sl);
for i = 1, j do
rs1=getByteString11(stringlist_getString(sl,i-1), 60)
str1=string.sub(rs1, cnt2, cnt3) --print("rs2 - "..str1)
rss3=(string.fromhex133(str1))
km1.Lines.Add(rss3)
if km1.Lines.Count==j then
showMessage("Current Code Found:: "..j.."\nClick to OK - Format and Replace..");
getline()
object_destroy(sl); end
end
end
end

function onReplaceStart()
km1.Lines.Text="" km2.Lines.Text=""
adres11("73 61 6C 65 73 4C 6F 67 44 75 72 61 74 69 6F 6E 22 3A ?? ?? ?? ?? ?? ?? ?? ?? ?? ??",54,83)
repto="00 00 00 00 00 C1 82 40"
end

function onReplaceStart()


I think it tells you what I wanted to do, this is complicated, long coding. Smile
Back to top
View user's profile Send private message
daspamer
Grandmaster Cheater Supreme
Reputation: 54

Joined: 13 Sep 2011
Posts: 1588

PostPosted: Mon Feb 01, 2021 11:27 am    Post subject: This post has 1 review(s) Reply with quote

@ParkourPenguin, I wrote the code some time ago, its functionality is to scan for a unique aob, extract a value located at X offset, rescan for that value and replace it with additional given value.
post

Most of the compares, are simply fool-proof just to avoid errors and so..

@ByTransient, If you could elaborate a bit more, would be prefect.

Now if we look at the function
Code:
ScanAndReScan(sAob,vDouble,nLength,sOffset)

We're scanning for a unique array of bytes(in this case, it seems not so unique?)
Extract a value at some offset, then scan for the value and replace it.

So are you suggesting, that the unique aob, may return several results, of each having a different value?

So for instance you have an aob like this
Code:
00 01 02 03 04 05 06 07 09 ?? ?? ?? ?? ?? ?? ?? ??

And your offset is 10h(-> ?? ?? ?? ?? ?? ?? ?? ??)
Each scan result of the given can have a different value (e.g 10.0, 50.0, 100.0 ....)

So question is, do you plan to change all of the possible values with the very same vDouble that is supplied?

If so, you could do, goo through all results, extract value from each and re-scan and replace, better go with memscan.



Code:
function ScanAndReScan(sAob,vDouble,nLength,sOffset)
   if ((type(sAob)=='string' and #sAob > 1) and type(vDouble)=='number' and type(nLength)=='number') then
      local sPattern,vPattern = sAob:gsub("[^%x%?]+",""),doubleToByteTable(vDouble);  -- clean AoB from any unneeded character; converts Double to bytes;
      local sOffset = ((#sPattern % 2 == 0) and (type(sOffset)=='number' and sOffset or (#sPattern // 2)) or error('pattern is incorrect')); -- read bytes starting offset;
      local sAobs = AOBScan(sPattern); -- yes memscan is faster, but I'm lazy.
      local vMark = {} -- store values that were already found...
      if (sAobs) then
         for i=0,vAobs.Count-1 do -- apply to all addresses
            -- converts address from base16 to base10 and adds X offset, reads nLength from given address and converts to double (returns table contains double bytes); unpack the table and scans the double bytes;
            local vStr = readString(tonumber(sAobs[i],16)+sOffset,nLength);
            if (not vMark[vStr]) then -- avoid scanning for previous values
               vMark[vStr] = true; -- mark...
               local vAobs = AOBScan(unpack(doubleToByteTable(vStr)));
               --print('sAOB found @:',sAobs[0],' - found target double count :', (vAobs and vAobs.count or 'nil'));
               if (vAobs) then
                  for i=0,vAobs.count-1 do -- there's might be lot's of results, including of memory areas you maybe wouldn't want to change
                     writeBytes(vAobs[i],vPattern); -- writes the double value given after it was converted to bytes...
                  end
                  vAobs.destroy(); -- cleanup
               end
            end
         end
         sAobs.destroy();
      end
   end
end

And then you can use it like you previously did,
Code:
ScanAndReScan("73 65 6C 65 63 74 5F 49 74 65 6D 5F 31 30 32 3A 22",15221889784,11); -- change double target to 15221889784;

The main drawback I can see while doing so, is that you're basically changing ALL possible (extracted) values into the very same value.


A different solution for this would be, to supply a table as vDouble, for this script.
Code:
function ScanAndReScan(sAob,vDouble,nLength,sOffset)
   if ((type(sAob)=='string' and #sAob > 1) and vDouble and type(nLength)=='number') then
      local sPattern,vPatternIsTable,vPattern = sAob:gsub("[^%x%?]+",""),type(vDouble)=='table';
      if (vPatternIsTable) then -- convert to a table...
         vPattern = {};
         for key,value in pairs(vDouble) do
            vPattern[tostring(key)] = doubleToByteTable(value); -- convert keys to string just in case...
         end
      else -- same value for all possible extracted values
         vPattern = doubleToByteTable(vDouble);
      end
      local sOffset = ((#sPattern % 2 == 0) and (type(sOffset)=='number' and sOffset or (#sPattern // 2)) or error('pattern is incorrect')); -- read bytes starting offset;
      local sAobs = AOBScan(sPattern); -- yes memscan is faster, but I'm lazy.
      local vMark = {} -- store values that were already found...
      if (sAobs) then
      
         for i=0,vAobs.Count-1 do -- apply to all addresses
            local vStr = readString(tonumber(sAobs[i],16)+sOffset,nLength); -- extracted so we could use it to lookup...
            if (not vMark[vStr]) then -- avoid scanning for previous values
               vMark[vStr] = true; -- mark...
               
               local vAobs = AOBScan(unpack(doubleToByteTable(vStr)));
               --print('sAOB found @:',sAobs[0],' - found target double count :', (vAobs and vAobs.count or 'nil'));
               
               local vPattern = vPatternIsTable and vPattern[vStr] or vPattern; -- new local variable.. does not overrides previous one...
               if (vAobs) then
                  if (vPattern) then -- if user has specified a replacement value
                     for i=0,vAobs.count-1 do -- there's might be lot's of results, including of memory areas you maybe wouldn't want to change
                        writeBytes(vAobs[i],vPattern); -- writes the double value given after it was converted to bytes...
                     end
                  end
                  vAobs.destroy(); -- cleanup
               end
            end
         end
         sAobs.destroy();
      end
   end
end

This way, it will change only values that are defined in vDouble table

Usage example
Code:


local myValues = {
   ["1.5"] = 15; -- change value of 1.5 to 15...
   ["100"] = "1000";
   ["5000"] = 0
}
ScanAndReScan("73 65 6C 65 63 74 5F 49 74 65 6D 5F 31 30 32 3A 22",myValues ,11);


This will change any specific double values to a different ones, but doing so makes this whole script logic pointless (as why not just look for that specific value..?).
If there are any other unique identifiers (e.g unique string that can describe the value or object ...), then it may sustain the dynamic that you're requesting.

A small side note:
If for instance if one of the results has a double value of 100, and you change it to 1000.
And the next result (or any afterwards...) has the value of 1000 and you want to change it to 10000, you are going to change the previous results as well to 10000,
100 AND 1000 will turn into 10000.
The order they appear is important, a quick fix would be to not use round numbers (e.g change to 1000.1).

Avoiding this altogther would require re-writing the script logic, fetching all addresses of all (extracted) values upfront, and only then write to them.




I did not test any of the scripts.


EDIT:
Silly me, added a loop and forgot to place a clean up code outside of it.

_________________
I'm rusty and getting older, help me re-learn lua.


Last edited by daspamer on Wed Feb 03, 2021 4:29 am; edited 1 time in total
Back to top
View user's profile Send private message Visit poster's website
ByTransient
Expert Cheater
Reputation: 5

Joined: 05 Sep 2020
Posts: 240

PostPosted: Mon Feb 01, 2021 4:19 pm    Post subject: Reply with quote

Code and "Double value" will not encounter other codes.
Scanned "aob" will find different results.
Example;
Unit code, '"_189_190": 1553881764155,' = double replace: 1400000004155.
Or for Booster; '"powerup_mfc": 1611266787607,' = double replace: 1700000087607
Scanning and change will continue in this order when needed.
But at the end of the change, the code gives an error.
I guess I'll just ignore him.

Code:
sAOB found @: 27FDA71A577  - found target double count : 122
Error:Access violation


Thanks for reworking this code.
To me this is worth more than +1.
Back to top
View user's profile Send private message
daspamer
Grandmaster Cheater Supreme
Reputation: 54

Joined: 13 Sep 2011
Posts: 1588

PostPosted: Wed Feb 03, 2021 4:30 am    Post subject: Reply with quote

Whoops, I see why you get the access violation.
I didn't move the sAobs.destroy() outside the loop.
I've edited the post above with the fix.
It should not throw an error now.

_________________
I'm rusty and getting older, help me re-learn lua.
Back to top
View user's profile Send private message Visit poster's website
ByTransient
Expert Cheater
Reputation: 5

Joined: 05 Sep 2020
Posts: 240

PostPosted: Wed Feb 03, 2021 8:28 am    Post subject: Reply with quote

Code:
if (sAobs) then
     
         for i=0,vAobs.Count-1 do -- <<< I think there's an error here, I changed it. vAobs >> sAobs
            local vStr = readString(tonumber(sAobs[i],16)+sOffset,nLength); -- extracted so we could use it to lookup...
            if (not vMark[vStr]) then -- avoid scanning for previous values
               vMark[vStr] = true; -- mark...
               
               local vAobs = AOBScan(unpack(doubleToByteTable(vStr))); --<<< "vAobs" is defined here.


I see that you understand the general rationale for what I want to do.
But before I used your code, I did "Double" scans and listed them in CE.
I ran your code and when the scan was complete, I saw that some of the "Double" values ​​I listed in CE had changed.

Let me explain below what I want to do with the code I've collected;

1) Numbers will be taken by string code.
2) The relevant value will be extracted from the string code and listed (km1).
3) The list will be compared and the same numbers will be transferred to the other section as a single result (km2)
4) Each digit in km2 will be searched for in "Double" (Note; each digit makes 2-10 results) and changed.
5) The km2 list will repeat the cycle in item 4, respectively.

The code below accomplishes this with a lot of unnecessary functions.
Your code also accomplishes some of it, but it only does it right for a scanned code.
Example; When I activate a different code with the second button it gives a "nil" result.
Everyone's coding logic is based on their own knowledge, I have insufficient knowledge of editing your code.
Here is the code I used before;

Code:
------------------------------------------
if kform then kform.destroy() kform = nil end
kform = createForm()
kform.Position = poDesktopCenter
kform.Width = 310
kform.Height = 250

local km1=createMemo(kform)
km1.Height=230 km1.Width=140 km1.Left=10 km1.Top=10
km1.WordWrap=false km1.ScrollBars="ssAutoBoth"

local km2=createMemo(kform)
km2.Height=230 km2.Width=140 km2.Left=160 km2.Top=10

kmtimer=createTimer(kform) kmtimer.Interval=300 kmtimer.Enabled=false
------------------------- Controls; --------
function getByteString1133(address, bytecount)
 local bytes = readBytes(address, bytecount, true)
 if bytes then
 local result = ""
 for i = 1, #bytes do
 if #result > 0 then result = result .. " " end
 result = result .. string.format("%02X", bytes[i]) end
 return result end
end

function string.fromhex1133(str1)
 str1 = str1:gsub("%s+", "")
 str1 = string.gsub(str1, "%s+", "")
 return (str1:gsub('..', function (cc)
 return string.char(tonumber(cc, 16)) end))
end

function byteTableToAobString(t)
  for k,v in ipairs(t) do
    t[k] = ('%02X'):format(v)
  end
  return table.concat(t, ' ')
end

function doublers1133(str111)
  newvalue = str111
  newvalue = tonumber(newvalue)
  if not newvalue then return end -- if not a number we're done
 newvalue = doubleToByteTable(newvalue) -- alternatively
  newvalue = byteTableToAobString(newvalue)
  str111=('%s'):format(newvalue, newvalue)

 return str111
end
-------------------------------- code -------
-----------------------
local cnts1133=0
local repto1133=""
local rss1133=""
local n3311=0
local n3322=0
local n3333=0
local good1122=0

function adres1133Replace()
if good1122==cnts1133 then
kmtimer1.Enabled=false
showMessage("Total "..n3333.." code change successful.");

else
kmtimer1.Enabled=false
rsd1=doublers1133(km2.Lines[good1122])
kmtimer1.Enabled=false
good1122=good1122+1
--if good1122 then
--UDF1.CELabel1.caption=("Change: "..n3311.." / "..n3322.." - Change Total: "..n3333.." - Found Code:("..good1122.." / "..cnts1133..") ");
--end
local rs3=AOBScan(rsd1)
if rs3==nil then
kmtimer1.Enabled=true
--UDF1.CELabel1.caption=("Change: "..n3311.." / "..n3322.." - Change Total: "..n3333.." - Found Code:("..good1122.." / "..cnts1133..") ");
else
n3322=rs3.Count - 1
n3311=n3322
n3333=n3322 + n3333
--UDF1.CELabel1.caption=("Change: "..n3311.." / "..n3322.." - Change Total: "..n3333.." - Found Code:("..good1122.." / "..cnts1133..") ");
for n=0,rs3.Count-1 do
if autoAssemble(rs3[n]..":\ndb "..repto1133) then  kmtimer1.Enabled=true
n3311=n3311 - n

--UDF1.CELabel1.caption=("Change: "..n3311.." / "..n3322.." - Change Total: "..n3333.." - Found Code:("..good1122.." / "..cnts1133..") ");
   end
 end
 end
object_destroy(rs3);
end
end
kmtimer1.OnTimer=adres1133Replace

function getline1133()
for i = 0, km1.Lines.Count do
local caption = km1.Lines[i]
if caption then
x=km2.Lines.Text
 z = caption
 if string.find(x, "%f[%d]"..z.."%f[%D]") ~= nil then

else
km2.Lines.Add(caption)
good1122=0
cnts1133=km2.Lines.Count - 1
--UDF1.CELabel1.caption=("Change: "..n3311.." / "..n3322.." - Change Total: "..n3333.." - Found Code:("..good1122.." / "..cnts1133..") ");
if i==km1.Lines.Count then
adres1133Replace()
end
end
end
end
 end
-------------------------------
function adres1133(from,t01133,cnt2,cnt3)
n3311=0
good1122=0
n3333=0
n3322=0
repto1133=doublers1133(tonumber(t01133))
local sl = AOBScan(from);
if(sl == nil) then

showMessage("No code found!");
else
j = stringlist_getCount(sl);
for i = 1, j do
rs1=getByteString1133(stringlist_getString(sl,i-1), 60)
str1=string.sub(rs1, cnt2, cnt3) --print("rs2 - "..str1)
rss1133=(string.fromhex1133(str1))
km1.Lines.Add(rss1133)
if km1.Lines.Count==j then
getline1133() end
end
end
object_destroy(sl);
end

-----------------------------------------------------------
--Note: The scanning code is provided as an example.
adres1133("22 5F 65 78 70 69 72 65 73 22 3A ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??",1500000504489,33,71)
--str1=string.sub(rs1, 33, 71) = ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??


adres1133() , Result (25); (km1.Lines.Text)
1640714990000
1640714990000
1609265279101
1610388567000
1609265390000
1610388590000
1640714990000
1609265279000
1640713762000
1612017752000
1612620813000
1612102413000
1614607952000
1643551952000
1643552013000
1643552013000
1614607952000
1612102413000
1643552149000
1612102352000
1643551952000
1612019613000
1612102353000
1643552013000
1643552013000

(Filtering to avoid redialing the same code;)

getline1133(), Result (17); (km2.Lines.Text)
1640714990000
1609265279101
1610388567000
1609265390000
1610388590000
1609265279000
1640713762000
1612017752000
1612620813000
1612102413000
1614607952000
1643551952000
1643552013000
1643552149000
1612102352000
1612019613000
1612102353000

"Adres1133Replace ()" will search for all codes in the "km2" list by formatting them as "Double", replacing them in order and then switching to the next code.

I hope I could explain what I wanted to do.
Your code does this the first time, but ("type") gives the other button and "nil" in another code.

Why such a coding request?
Some items in the game have a time limit (hour or day) assigned against their own code (String), when I extract these codes with text scan and scan them with Double (4-10 results if active, 2-4 results if passive) 2-10 I find results between, and I reduce or increase these time frames as required.

I hope you didn't get confused. Smile
Thanks for your follow up.
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