 |
Cheat Engine The Official Site of Cheat Engine
|
| View previous topic :: View next topic |
| Author |
Message |
oddgamer Advanced Cheater
Reputation: 0
Joined: 19 Jan 2013 Posts: 60
|
Posted: Thu Jan 18, 2024 8:32 am Post subject: Type help |
|
|
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 |
|
 |
ParkourPenguin I post too much
Reputation: 152
Joined: 06 Jul 2014 Posts: 4719
|
Posted: Thu Jan 18, 2024 1:03 pm Post subject: |
|
|
`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 |
|
 |
oddgamer Advanced Cheater
Reputation: 0
Joined: 19 Jan 2013 Posts: 60
|
Posted: Thu Jan 18, 2024 4:47 pm Post subject: |
|
|
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.
| 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 |
|
 |
ParkourPenguin I post too much
Reputation: 152
Joined: 06 Jul 2014 Posts: 4719
|
Posted: Thu Jan 18, 2024 7:06 pm Post subject: |
|
|
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 |
|
 |
oddgamer Advanced Cheater
Reputation: 0
Joined: 19 Jan 2013 Posts: 60
|
Posted: Fri Jan 19, 2024 2:09 am Post subject: |
|
|
| 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 |
|
 |
|
|
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
|
|