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 


Type help

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

Joined: 19 Jan 2013
Posts: 60

PostPosted: Thu Jan 18, 2024 8:32 am    Post subject: Type help Reply with quote

Sooo... I've asked for new type searches before, and then decided to try to write my own by looking at the code and trying to work out what was done where in the various codes I looked at. It has, needless to say, not gone well.

The game in question measures time in 'ticks', of which there are 60 in an RL second. Many things in the game use this mechanic, but reports things like 'work' or 'seconds' (60 ticks), 'hours' (2500 ticks), 'days' (60,000 ticks), or even 'years' (3,600,000 ticks). Obviously one can always go the long route and just search for values between '(reported value -1) * 60' and '(reported value +1) * 60', but doing this a lot gets annoying fast.

To make things more complicated, one value (work) is stored as a float, while others (seconds, hours, days, years) are 4-byte integers.

I'm willing to either be corrected on my code below (feel free to giggle), or just have someone write out the 'work' one and I'll see if I can struggle through figuring out the others, or (for the insanely generous) have someone write all four. The following is for 'work', which should be the number reported * 60 as a float.

Code:
alloc(ConvertRoutine,1024)
alloc(ConvertBackRoutine,1024)
alloc(TypeName,256)
alloc(ByteSize,4)
alloc(PreferedAlignment,4)
alloc(UsesFloat,1)
alloc(UsesString,1)
alloc(MaxStringSize,2)
alloc(CallMethod,1)

TypeName:
db 'Work',0
ByteSize:
dd 8
PreferedAlignment:
dd 4
UsesFloat:
db 1
UsesString:
db 0
MaxStringSize:
dw #100
CallMethod:
db 1

//cdecl int ConvertRoutine(unsigned char *input, PTR_UINT address);
ConvertRoutine:
[64-bit]
mov ax, 60
div eax
ret
[/64-bit]

//cdecl void ConvertBackRoutine(int i, PTR_UINT address, unsigned char *output);
ConvertBackRoutine:
[64-bit]
mov ax, 60
mul eax
ret
[/64-bit]
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 152

Joined: 06 Jul 2014
Posts: 4719

PostPosted: Thu Jan 18, 2024 1:03 pm    Post subject: Reply with quote

`div` / `mul` are for integers. Use SSE for floating point numbers (floats / doubles).

ByteSize should be 4 if it's just a float.
Code:
label(multiplier)
...
ByteSize:
dd 4
...
//cdecl int ConvertRoutine(unsigned char *input, PTR_UINT address);
ConvertRoutine:
[64-bit]
movss xmm0,[rcx]
divss xmm0,[multiplier]
movd eax,xmm0
ret
[/64-bit]
multiplier:
dd (float)60

//cdecl void ConvertBackRoutine(int i, PTR_UINT address, unsigned char *output);
ConvertBackRoutine:
[64-bit]
movd xmm0,ecx
mulss xmm0,[multiplier]
movss [r8],xmm0
ret
[/64-bit]


oddgamer wrote:
... reports things like 'work' or 'seconds' (60 ticks), 'hours' (2500 ticks), 'days' (60,000 ticks), or even 'years' (3,600,000 ticks).

... others (seconds, hours, days, years) are 4-byte integers.

I don't understand how the game encodes seconds/hours/days/years. I think you mean the game has unique 4-byte integers associated with each of those 4 values, but how exactly do the primitive 4-byte values correspond with the value you see on screen? e.g. for 'days', does it increase/decrease in increments of 60000?
Is this how you mean the game stores it:
Code:
year 1, day 5, hour 0, second 20

 address - value
0344C170 - 3600000 (year 1)
0344C174 - 300000  (day 5)
0344C178 - 0       (hour 0)
0344C17C - 1200    (second 20)
(the addresses needn't be contiguous, but they are unique)
This "simple" encoding scheme could be covered by this custom type:
Code:
//cdecl int ConvertRoutine(unsigned char *input, PTR_UINT address);
ConvertRoutine:
[64-bit]
mov eax,[rcx]
mov ecx,#60000
xor edx,edx
div ecx
ret
[/64-bit]

//cdecl void ConvertBackRoutine(int i, PTR_UINT address, unsigned char *output);
ConvertBackRoutine:
[64-bit]
imul ecx,ecx,#60000
mov [r8],ecx
ret
[/64-bit]

If it's always exactly multiples of the amount, you can cull the number of results from scans like so:
Code:
//cdecl int ConvertRoutine(unsigned char *input, PTR_UINT address);
ConvertRoutine:
[64-bit]
mov eax,[rcx]
mov ecx,#60000
xor edx,edx
div ecx
mov ecx,FFFFFFFF
test edx,edx
cmovnz eax,ecx
ret
[/64-bit]
This treats any number that's not a multiple of the amount as 0xFFFFFFFF.

There was a similar post where several units of time were encoded in the same 4-byte value.
https://forum.cheatengine.org/viewtopic.php?t=621336

_________________
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
oddgamer
Advanced Cheater
Reputation: 0

Joined: 19 Jan 2013
Posts: 60

PostPosted: Thu Jan 18, 2024 4:47 pm    Post subject: Reply with quote

Thanks again for the float one. I tried the movss but had the syntax wrong, didn't move it into a register first (I mean, I read this stuff but somehow things weren't computing in my head), and wouldn't have known the movd to move it back. Works perfectly. Smile

ParkourPenguin wrote:

I don't understand how the game encodes seconds/hours/days/years. I think you mean the game has unique 4-byte integers associated with each of those 4 values, but how exactly do the primitive 4-byte values correspond with the value you see on screen? e.g. for 'days', does it increase/decrease in increments of 60000?


Yeah, that's my bad. The encoding is a single 4-byte value, but what you see on screen varies. The 'seconds' one only comes up in combat where you'll have 'X seconds' left of an effect, and those are real life seconds, so if the game is reporting 4 seconds left, the number in memory is somewhere between 180 and 300 (3 x 60 to 5 x 60). Meanwhile once things get longer, or are not combat-based, it'll show "hours", "days", or "years". You may notice that there's 24 hours in a day (2500 x 24 = 60,000), but only 60 days in a year (60 x 60,000 = 3,600,000). So you'll get things like "Effect lasts 13.4 days" when the number in memory is somewhere between 798,000 (13.3 x 60,000) and 804,000 (13.5 x 60,000). If the time gets low enough, it'll report in hours instead (and yes, this would mess up the search at that point, but if I'm searching for something this comes up so rarely that I don't really care). Technically it's a touch more narrow as the game rounds, so 13.4 is going go be somewhere between 13.35 and 14.45, but still that gets the gist of it. As I say, I'm hoping to avoid having to do those calculations every time and use a 4-byte search for 'value between' and the two numbers, as it's an extra couple of steps that I'm doing repeatedly. Even one that can't handle decimal inputs (so I could only put in 14 days or 13 days, nit 13.4 days) would be quite helpful.
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 152

Joined: 06 Jul 2014
Posts: 4719

PostPosted: Thu Jan 18, 2024 7:06 pm    Post subject: Reply with quote

You could make custom types for all of them. Execute this Lua code in the main Lua script window:
Code:
assert(cheatEngineIs64Bit(), 'These custom types are not implemented for 32-bit CE')

local function make_ct_script(name, base)
  return ([[
alloc(ConvertRoutine,1024)
alloc(ConvertBackRoutine,1024)
alloc(TypeName,256)
alloc(ByteSize,4)
alloc(PreferedAlignment,4)
alloc(UsesFloat,1)
alloc(UsesString,1)
alloc(MaxStringSize,2)
alloc(CallMethod,1)
label(mult)

TypeName:
db '%s',0
ByteSize:
dd 4
PreferedAlignment:
dd 4
UsesFloat:
db 1
UsesString:
db 0
MaxStringSize:
dw #100
CallMethod:
db 1

//cdecl int ConvertRoutine(unsigned char *input, PTR_UINT address);
ConvertRoutine:
  cvtsi2ss xmm0,[rcx]
  divss xmm0,[mult]
  movd eax,xmm0
  ret
mult:
  dd (float)%d

//cdecl void ConvertBackRoutine(int i, PTR_UINT address, unsigned char *output);
ConvertBackRoutine:
  movd xmm0,ecx
  mulss xmm0,[mult]
  cvttss2si eax,xmm0
  mov [r8],eax
  ret
]]):format(name, assert(math.floor(base)))
end

local types = {
  {
    name = "Second",
    base = 60
  },
  {
    name = "Hour",
    base = 2500
  },
  {
    name = "Day",
    base = 60000
  },
  {
    name = "Year",
    base = 3600000
  }
}

for _,t in ipairs(types) do
  local oldCT = getCustomType(t.name)
  if oldCT then
    oldCT.destroy()
  end
  local ct_script = make_ct_script(t.name, t.base)
  registerCustomTypeAutoAssembler(ct_script)
end

There is a string custom type, but the string converted from the bytes in memory must be an exact match for the string input by the user. In this case, that's a bad limitation.

I don't really think custom types should be used here. They're more suited for data encoded in a weird format. Here, it's the input given by the user to scan for that's weird. Unfortunately, custom types can't directly change how the input is handled.

You could use Lua to automate what you're doing. Have some function that takes a string and modifies the current memscan based on that (see MemScan class in celua.txt). e.g. open the Lua engine window (or main Lua script), call that function with some string (e.g. "13.4 days"), and the function does the rest: parsing input, set properties of the current memscan (type, "value between", lower / upper bounds, etc.), and finally scans it. If you're distributing the table to others, maybe make a fancy GUI in an auto assembler script assigned to the table to help.
However, parsing arbitrary user input to a range of values is more annoying than it sounds to do correctly.

_________________
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
oddgamer
Advanced Cheater
Reputation: 0

Joined: 19 Jan 2013
Posts: 60

PostPosted: Fri Jan 19, 2024 2:09 am    Post subject: Reply with quote

ParkourPenguin wrote:
I don't really think custom types should be used here.

Well, I ran your script, had the four types set up for me, and finally tested it... and they work great! Exactly what I wanted. Instead of me having to calculate what '5 days' was, I just switched to 'days', searched for 5, waited a bit, and so on. Found it very, very quickly and it was so much less of a hassle. So thank you very, very much!
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 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