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 


read specific string

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

Joined: 17 Jan 2018
Posts: 205

PostPosted: Mon Jan 13, 2020 4:05 pm    Post subject: read specific string Reply with quote

I want to read game's specific text (items description). Game stores strings as ASCII values offset - 0x20. But strings contains specific value F9 function. F9: This is an encoding technique designed to make the raw data smaller. A byte following this value will tell the game's memory the location of, and how much, text to read. The byte is set up like this:
In binary:
YYXXXXXX
Where YY is the number of bytes to read and XXXXXX is how far back from the F9 byte to look.

The number of bytes to read uses the formula:
no_of_bytes = (YY) * 2 + 4 [Where YY is binary]
The location starts at the position that the data read the byte F9 and proceeds backwards XXXXXX + 1 times.

Example:
34 48 52 4F 57 00 49 54 45 4D 53 00 44 41 4D 41 47 49 4E 47 00 4F 50 50 4F 4E 45 4E 54 FF 34 52 41 4E 53 46 4F 52 4D F9 92 54 00 49 4E 54 4F 00 41 4E F9 2C 4D FF
- T - h - r - o - w ---- i - t - e - m - s ---- d - a - m - a - g - i - n - g ---- o - p - p - o - n - e - n - t ---- T - r - a - n - s - f - o - r - m - ? - ? - t ------ i - n - t - o ---- a - n - ? - ? - m

where: 0x00: Space, 0xFF: end of text.
Game displays description: Throw items damaging opponent ---- Transform opponent into an item

In the example we have two F9 functions. The first one references the " opponen" in the word " opponent" earlier, in the previous item description and the second one references the " ite" in the " items". The bytes following the F9 describe how to get to those texts:

in hex: 0x92
in binary: 10 010010 - go back (binary:010010=18 +1) 19 bytes and display (binary:10=2; 2 * 2 + 4) eight characters
in hex: 0x2C
in binary: 00 101100 - go back 45 bytes and display four characters

I have function that can read text without F9, but if text contains F9 it does not work (CE error: bad argument #1 to 'char' (value out of range)). And also need to skip the next byte after the F9, and do not read it.

Code:
  local function readDescription(addr, len)
  len = len or 92 -- defaults to 92
  local bytes = readBytes(addr, len, true)
  local chars = {}
  for k,v in ipairs(bytes) do
    if v == 0xF9 then
    local a = readBytes(addr+k) --if v == 0xF9 then read next byte value
    local no_of_bytes = (a >> 6) * 2 + 4 --The number of bytes to read: no_of_bytes = (YY) * 2 + 4  [Where YY is binary]
    local b = (bAnd(0x3F, a)) + 1 --from F9: proceed backward XXXXXX + 1 times
      local bytesss = readBytes(addr+k-1-b, no_of_bytes, true) --from F9: proceed backward
      for ke,ve in ipairs(bytesss) do
          if ve == 0xFF then break end
      chars[#chars+1] = string.char(ve+0x20) -- or: table.insert(chars, string.char(ve+0x20))
      end
    elseif v == 0xFF then break -- Is it allowed to do this without 'end'?
    else
    chars[#chars+1] = string.char(v+0x20)
    end
  end
  return table.concat(chars,'')
  end


Can you point where the error is?
Error is found, and function reads string with F9, but how to skip next byte after F9, and not read it?
Also need to know how to change value to word: if byte value == F8, then add the word 'Box:' to the table and read the next byte after F8, if byte == 0x02, then add the word 'Red' to table, so in total it should add words 'Box: Red' to table. And again need to skip next byte after F8, and not read it.


Last edited by Razi on Tue Jan 14, 2020 8:13 pm; edited 3 times in total
Back to top
View user's profile Send private message
panraven
Grandmaster Cheater
Reputation: 62

Joined: 01 Oct 2008
Posts: 959

PostPosted: Tue Jan 14, 2020 12:37 am    Post subject: Reply with quote

CE blocked me for the original text, replied here
https://pastebin.com/fLryitjd
Quote:
I would separate memory read (slow if byte by byte) and decoding (fast on a piece of string). So the following only suggest a more easier way to do different special code (f8/f9/ff) handling on string.

The point here is to separate the handling into smaller function that specialize itself.
...


Sorry, missing the part you asked.

To fix your for-loop code you probably need some flag/counter to indicate that next loop/byte has to be SKIPPED, for instance, 0xf9 process 2 bytes each time, while 0xff and literal 1 byte, and, let's say, 0xf8 need 8 bytes, then make some SkipCounter as 0xf9->1; 0xff / literal-> 0 ; 0xf8 -> 7 then first test
Code:

  if SkipCounter>0 then SkipCounter = SkipCounter - 1 else
  ....  your other 0xf9/0xff ... processing

_________________
- Retarded.
Back to top
View user's profile Send private message
Razi
Expert Cheater
Reputation: 1

Joined: 17 Jan 2018
Posts: 205

PostPosted: Tue Jan 14, 2020 7:34 pm    Post subject: Reply with quote

panraven wrote:
CE blocked me for the original text, replied here
https://pastebin.com/fLryitjd
Quote:
I would separate memory read (slow if byte by byte) and decoding (fast on a piece of string). So the following only suggest a more easier way to do different special code (f8/f9/ff) handling on string.

The point here is to separate the handling into smaller function that specialize itself.
...


I'm a complete newbie with strings, haven't worked with them before. What method is needed to pass bytes to a function (which bytes to process), readBytes?

Code:
local txt = readBytes(0x009AF6B2, 72, true)
unlzo(txt)
But, of course this does not work.
Back to top
View user's profile Send private message
panraven
Grandmaster Cheater
Reputation: 62

Joined: 01 Oct 2008
Posts: 959

PostPosted: Wed Jan 15, 2020 3:24 am    Post subject: Reply with quote

I guess you can do it in a for loop on txt, or use CE function byteTableToString
Code:

local txt = byteTableToString(readBytes(0x009AF6B2, 72, true))


I make another version more like your code, different is it is in a while loop, and addr has to be update according to the 1st byte read in each loop iteration.
Code:

autoAssemble[[
  globalalloc(__,$1000)
  __:
  db 34 48 52 4F 57 00 49 54 45 4D 53 00 44 41 4D 41 47 49 4E 47 00 4F 50 50 4F 4E 45 4E 54 FF 34 52 41 4E 53 46 4F 52 4D F9 92 54 00 49 4E 54 4F 00 41 4E F9 2C 4D ff
  db fe // our endByte for test
]]
 
local chr, sub, cat = string.char, string.sub, table.concat
function readDesc(addr, endByte)
  addr = GetAddressSafe(addr) -- convert to numeric address
  local now = os.clock() -- prevent inf loop
  local acc = '' -- our to be return string
  while true do
    if os.clock()-now>10 then break end -- prevent inf loop
    local b = readBytes(addr)
    if b==endByte then -- ok done
      return acc, addr
    elseif b==0xff then  --- multiple elseif on same variable is like SWITCH-CASE
      -- may same as endByte, but if not the same, 0xff
      -- can make output like new-line, and continue scan
      -- for new line
      addr, acc = addr+1, acc .. '\r\n'
    elseif b==0xf9 then
      b = readBytes(addr+1) --  the encoded len,ofs byte
      local l, o = (b >> 6)*2+4, (b & 0x3f)+1
      b = readBytes(addr-o, l, true)
      for i=1,l do -- convert byte to valid string
        b[i] = chr(b[i]+0x20)
      end
      addr, acc = addr+2, acc .. cat(b)
--  elseif b==0xf8 then
--    process 0xf8 ...
    else -- is literal
      addr, acc = addr + 1, acc .. chr(b + 0x20)
    end
  end
end
 
print(readDesc("__", 0xfe))
 
print(readDesc("__", 0xff)) -- endByte is 0xff, will work like your original code

-> https://pastebin.com/RMXRXjC9

there will still be error like (CE error: bad argument #1 to 'char' (value out of range)) if the addr input is not right.

_________________
- Retarded.
Back to top
View user's profile Send private message
Razi
Expert Cheater
Reputation: 1

Joined: 17 Jan 2018
Posts: 205

PostPosted: Thu Jan 16, 2020 12:42 am    Post subject: Reply with quote

panraven wrote:
I make another version more like your code, different is it is in a while loop, and addr has to be update according to the 1st byte read in each loop iteration.
-> https://pastebin.com/RMXRXjC9

there will still be error like (CE error: bad argument #1 to 'char' (value out of range)) if the addr input is not right.


Code:

local chr, sub, cat = string.char, string.sub, table.concat
function readDesc(addr, endByte)
  addr = GetAddressSafe(addr) -- convert to numeric address
  local now = os.clock() -- prevent inf loop
  local acc = '' -- our to be return string
  while true do
    if os.clock()-now>1 then break end -- prevent inf loop
    local b = readBytes(addr)
    if b==endByte then-- ok done
    return acc
    elseif b==0xf9 then
      b = readBytes(addr+1) --  the encoded len,ofs byte
      local l, o = (b >> 6)*2+4, (b & 0x3f)+1
      b = readBytes(addr-o, l, true)
      for i=1,l do -- convert byte to valid string
        if b[i] == 0xFF then break -- this line prevents from CE error: bad argument #1 to 'char' (value out of range)
        else
        b[i] = chr(b[i]+0x20)
        end
      end
      addr, acc = addr+2, acc .. cat(b)
--  elseif b==0xf8 then
--    process 0xf8 ...
    else -- is literal
      addr, acc = addr + 1, acc .. chr(b + 0x20)
    end
  end
end

  UDF1.CEEdit106.Text = readDesc(baseaddr+0x5AF5C0+val, 0xFF)

Now it works almost as it should, but if there are two 0xFF in the byte string, then 255 is displayed in the edit box (ex.: 'Steal item255', but should display: 'Steal item'). Example
45 00 49 54 45 4D FF 33 54 45 41 4C F9 4A FF
- e ---- i - t - e - m ----- S - t - e - a - l - ? - ?

0x4A= 01 001010 - go back 11 bytes and display 6 characters
from F9 it goes back and reads bytes (00 49 54 45 4D FF == word ' item' with 0xFF), then returns back after 0x4A and reads another 0xFF. But the description should be: Steal item. How to fix this?
Back to top
View user's profile Send private message
panraven
Grandmaster Cheater
Reputation: 62

Joined: 01 Oct 2008
Posts: 959

PostPosted: Thu Jan 16, 2020 1:04 am    Post subject: Reply with quote

A quick fix may be (not test)
Code:

-- change
        if b[i] == 0xFF then break -- this line prevents from CE error: bad argument #1 to 'char' (value out of range)
        else
        b[i] = chr(b[i]+0x20)
        end
-- to
  b[i] = b[i]>0xc9 and '' or chr(b[i]+0x20)

this collect an empty string instead if the byte will be more than 0xff after +0x20, and not break the loop.

_________________
- Retarded.
Back to top
View user's profile Send private message
Razi
Expert Cheater
Reputation: 1

Joined: 17 Jan 2018
Posts: 205

PostPosted: Thu Jan 16, 2020 2:48 am    Post subject: Reply with quote

panraven wrote:
A quick fix may be (not test)
Code:
  b[i] = b[i]>0xc9 and '' or chr(b[i]+0x20)

this collect an empty string instead if the byte will be more than 0xff after +0x20, and not break the loop.


It works, thanks a lot for the help.
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