 |
Cheat Engine The Official Site of Cheat Engine
|
View previous topic :: View next topic |
Author |
Message |
theboy181 Advanced Cheater
Reputation: 0
Joined: 26 Jan 2018 Posts: 91
|
Posted: Sat Jan 27, 2018 12:48 pm Post subject: Ultimate Float Script WANTED |
|
|
I was wondering if someone with the knowledge could help me with a script.
I would like to have two different types of functionality with float searches.
The ability to do a search for floats that are whole numbers only (no decimals)
A range of whole numbered floats, (example 1000 - 5550)
Would pay for this script if its too much work.
PM me.
|
|
Back to top |
|
 |
OldCheatEngineUser Whateven rank
Reputation: 20
Joined: 01 Feb 2016 Posts: 1586
|
Posted: Sat Jan 27, 2018 1:03 pm Post subject: |
|
|
im not sure why would you need a script while CE have it own function of searching ranged value, for sure you might use truncated search too. (you will get 1 or 2 decimals)
_________________
About Me;
I Use CE Since Version 1.X, And Still Learning How To Use It Well!
Jul 26, 2020
STN wrote: | i am a sweetheart. |
|
|
Back to top |
|
 |
TheyCallMeTim13 Wiki Contributor
Reputation: 51
Joined: 24 Feb 2017 Posts: 976 Location: Pluto
|
Posted: Sat Jan 27, 2018 1:19 pm Post subject: |
|
|
I just do #.0 for whole numbers on floats (100.0 -> 500.0), just use a range or exact value scan.
_________________
|
|
Back to top |
|
 |
FreeER Grandmaster Cheater Supreme
Reputation: 53
Joined: 09 Aug 2013 Posts: 1091
|
Posted: Sat Jan 27, 2018 2:21 pm Post subject: |
|
|
As mentioned for a single whole number you can just add .0 (I usually use 3-5 0s lol) but yeah, a value between scan still returns non-whole numbers when using that method....
You can make up something like this easily enough:
Code: | function findWholeValue(min, max, findType, protectionflags, numDigitsAfterDecimal)
-- handle not having required arguments
if not min or not max then return nil end
if tonumber(min) > tonumber(max) then min,max = max, min end
-- default values
findType = findType or vtSingle
protectionflags = protectionflags or "+W-X-C"
numDigitsAfterDecimal = numDigitsAfterDecimal or 0
-- do scan
memscan = createMemScan()
memscan.firstScan(soValueBetween, findType, rtRounded, min, max,
"0", "7fffffffffffffff", protectionflags,
fsmAligned,"4", false, false, false, false)
local done = {}
done.results = {}
done.finished = false
memscan.OnScanDone = function(memscan)
print('done')
local results = done.results
foundlist = createFoundList(memscan)
foundlist.initialize()
local rf = findType == vtSingle and readFloat or readDouble
for i=0,foundlist.Count-1 do
local value = rf(foundlist[i])
local strValue = tostring(value)
local fraction = strValue:match('%.(.+)')
if fraction == '0' then fraction = '' end
if not fraction or #fraction <= numDigitsAfterDecimal then
results[#results+1] = {foundlist[i],value}
print(foundlist[i], value)
end
end
foundlist.deinitialize()
foundlist.destroy()
memscan.destroy()
done.finished = true
end
done.memscan = memscan
memscan.waitTillDone()
print('returning')
return done
end
local info = findWholeValue(100,500, vtDouble, nil, 5)
info.memscan.waitTillDone()
print('displaying')
return info.results | which when I run it results in: Code: | returning
displaying
:table
[
]
done
0168D09C 179.2
016933F0 100.0 | But it should result in something like Code: | done
0168D09C 179.2
016933F0 100.0
returning
displaying
:table
[
1 = table
[
1 = 0168D09C
2 = 179.2
]
2 = table
[
1 = 016933F0
2 = 100.0
]
] |
and I have no idea why it's not actually waiting until the memory scan is complete before continuing on with the code....
|
|
Back to top |
|
 |
theboy181 Advanced Cheater
Reputation: 0
Joined: 26 Jan 2018 Posts: 91
|
Posted: Sat Jan 27, 2018 3:33 pm Post subject: |
|
|
How do I do a range scan with floats? I can't seem to find it anywhere, and the example 100.0 -> 500.0 doesn't seem to work.
|
|
Back to top |
|
 |
TheyCallMeTim13 Wiki Contributor
Reputation: 51
Joined: 24 Feb 2017 Posts: 976 Location: Pluto
|
Posted: Sat Jan 27, 2018 4:45 pm Post subject: |
|
|
Range scan is just a "Value Between" scan, and "100.0 -> 500.0" was just my way of showing the range values, but I guess when I do this I actually use "Exact value" and just undo the scan after adding the addresses to the address list, I just assumed that it works for "Value Between" scans, I should have tested it first I guess.
<-- That's way the heads hanging.
_________________
|
|
Back to top |
|
 |
theboy181 Advanced Cheater
Reputation: 0
Joined: 26 Jan 2018 Posts: 91
|
Posted: Sat Jan 27, 2018 5:55 pm Post subject: |
|
|
Thanks, I figured that is what you meant. It kinda work as expected, but I still got a large amount of decimals..
Thanks you.
I tried running that script above, and I was unable to figure out how to use it,
A cool feature for Cheat Engine would be able to save a preferred cheat list search.
Run a list of common searches.. this would be very helpful when Looking for common areas in N64 Roms.
|
|
Back to top |
|
 |
ParkourPenguin I post too much
Reputation: 152
Joined: 06 Jul 2014 Posts: 4697
|
Posted: Sat Jan 27, 2018 6:52 pm Post subject: |
|
|
Here's a custom type that'll treat every float with a fractional part as a QNaN (i.e. won't be found in searches):
Code: | alloc(ConvertRoutine,1024)
alloc(ConvertBackRoutine,1024)
alloc(TypeName,256)
alloc(ByteSize,4)
alloc(UsesFloat,1)
alloc(CallMethod,1)
TypeName:
db 'Float (Integers)',0
ByteSize:
dd 4
UsesFloat:
db 1
CallMethod:
db 1
//cdecl int ConvertRoutine(unsigned char *input, PTR_UINT address);
ConvertRoutine:
[64-bit]
mov eax,7fc00000
mov edx,[rcx]
mov ecx,edx
shr ecx,17
inc cl
jns @f
mov r8d,edx
and ecx,7F
add ecx,9
and r8d,007fffff
shl r8d,cl
test cl,60
cmovnz eax,edx
test r8d,r8d
cmovz eax,edx
@@:
ret
[/64-bit]
[32-bit]
push ebx
mov eax,[esp+8]
mov edx,[eax]
mov eax,7fc00000
mov ecx,edx
shr ecx,17
inc cl
jns @f
and ecx,7f
mov ebx,edx
and ebx,007fffff
add ecx,09
shl ebx,cl
test cl,60
cmovnz eax,edx
test ebx,ebx
cmovz eax,edx
@@:
pop ebx
ret
[/32-bit]
// cdecl void ConvertBackRoutine(int i, PTR_UINT address, unsigned char *output);
ConvertBackRoutine:
[64-bit]
mov [r8],ecx
ret
[/64-bit]
[32-bit]
mov eax,[esp+4]
mov ecx,[esp+C]
mov [ecx],eax
ret
[/32-bit] |
Right click the value type combobox (e.g. where it says "4 Bytes"), select "Define new custom type (Auto Assembler)", and copy/paste the above code into that window.
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
 |
FreeER Grandmaster Cheater Supreme
Reputation: 53
Joined: 09 Aug 2013 Posts: 1091
|
Posted: Sat Jan 27, 2018 9:29 pm Post subject: |
|
|
Thanks ParkourPenguin though... I don't fully understand how it works lol
I understand the high level concept of it returning a Not A Number value which will fail the compare and not be kept in the search results but how it actually determines whether it's a whole number escapes me in a few key parts lol
Here's how I've commented the x64 code
Code: | // http://steve.hollasch.net/cgindex/coding/ieeefloat.html
// anatomy of a float / real32
// 1 Sign Bit, 8 Exponent bits, 23 mantissa bits
// S EEEEEEEE MMMMMMMM MMMMMMMM MMMMMMM_
// SEEEEEEE EMMMMMMM MMMMMMMM MMMMMMMM
// shr 0x17 = remove mantissa, S EEEEEEEE
mov eax,7fc00000 // QNaN default return value
mov edx,[rcx] // read float into edx
mov ecx,edx // copy float into ecx
shr ecx,0x17 // remove mantissa and put expontent into cl
inc cl // increment exponent
jns @f // return NaN if exponent is less than 127, which is actually 0 because reasons
// since negative exponents = dividing and if you're dividing you're not going to end up with a whole number
// this also excludes infinity and NaN which have an exponent of all 1s, +1 = 0, which is not signed
and ecx,7F // remove original sign bit and exponent sign bit (which was 1 or we'd have jumped ^)
// so 0 = 127, +1 = 128, &7F = 0
// 22 = 127+22 = 149, +1 = 150, &7F = 22 etc.
add ecx,9 // add 9 to exponent... why???
mov r8d,edx // copy original value
and r8d,007fffff // keep only 23 mantissa bits
shl r8d,cl // shift mantissa by exponent (at least 9)
test cl,60 // test exponent for bits 0110 0000 ???
cmovnz eax,edx // if set return original value
test r8d,r8d // if 0
cmovz eax,edx // return original value
@@:
ret |
I almost feel like I understand enough to create one for doubles but...I've spent enough hours trying to wrap my head around this for now (stupid +127 bias caused a lot of it )
oh, and I noticed this treats 0 as non-integer... not sure if that's useful because it could lead to false positives but trivial to change by explicitly checking for 0 if desired.
|
|
Back to top |
|
 |
mgr.inz.Player I post too much
Reputation: 222
Joined: 07 Nov 2008 Posts: 4438 Location: W kraju nad Wisla. UTC+01:00
|
Posted: Sun Jan 28, 2018 3:18 am Post subject: |
|
|
Simpler "Float (Integers)"
Code: | alloc(ConvertRoutine,1024)
alloc(ConvertBackRoutine,1024)
alloc(TypeName,256)
alloc(ByteSize,4)
alloc(UsesFloat,1)
alloc(CallMethod,1)
TypeName:
db 'Float (Integers)',0
ByteSize:
dd 4
UsesFloat:
db 1
CallMethod:
db 1
ConvertRoutine:
[64-bit]
mov eax,[rcx]
[/64-bit]
[32-bit]
mov eax,[esp+4]
mov eax,[eax]
[/32-bit]
movd xmm0,eax
cvtps2dq xmm1,xmm0 // convert single to integer
cvtdq2ps xmm1,xmm1 // convert integer to single
ucomiss xmm0,xmm1
je @f
mov eax,NaN
@@:
ret
ConvertBackRoutine:
// read only
ret
|
EDIT:
"Double (Integers)"
Code: | alloc(ConvertRoutine,1024)
alloc(ConvertBackRoutine,1024)
alloc(TypeName,256)
alloc(ByteSize,4)
alloc(PreferedAlignment,4)
alloc(UsesFloat,1)
alloc(CallMethod,1)
TypeName:
db 'Double (Integers)',0
ByteSize:
dd 8
PreferedAlignment:
dd 4
UsesFloat:
db 1
CallMethod:
db 1
ConvertRoutine:
[64-bit]
movsd xmm0,[rcx]
[/64-bit]
[32-bit]
mov eax,[esp+4]
movsd xmm0,[eax]
[/32-bit]
cvtsd2ss xmm0,xmm0 // convert double to single
movd eax,xmm0
cvtps2dq xmm1,xmm0 // convert single to integer
cvtdq2ps xmm1,xmm1 // convert integer to single
ucomiss xmm0,xmm1
je @f
mov eax,NaN
@@:
ret
ConvertBackRoutine:
// read only
ret
|
_________________
|
|
Back to top |
|
 |
ParkourPenguin I post too much
Reputation: 152
Joined: 06 Jul 2014 Posts: 4697
|
Posted: Sun Jan 28, 2018 10:03 am Post subject: |
|
|
FreeER:
Here's some similar C code with documentation.
Code: | uint32_t modify(uint32_t *n)
{
const uint32_t value = *n; // the value (CE passes a pointer)
const uint32_t bad_value = 0x7FC00000; // QNaN
uint8_t exponent = (value >> 23) & 0xff; // extract the 8 exponent bits
uint32_t mantissa = value & 0x7fffff; // extract the 23 fractional bits (aka mantissa)
// if the exponent is FF or <7F, it's not an integer
// so, increment it and if the most significant bit is 0, it's not an integer
// (note that I forgot about the integer 0, so this fails in that case)
++exponent;
if ((exponent & 0x80) == 0)
return bad_value;
// this is effectively getting the true exponent
exponent &= 0x7f;
// here, the most significant bit of the exponent must be 1, so equivalent code would be:
// exponent -= 128; // -127 for the exponent bias, -1 for the previous increment
// (for brevity, let exp = exponent)
// the value is now in the form value = 2^exp * mantissa
//
// this can be seen as fixed point arithmetic
// the mantissa, as a 32-bit fixed point binary number, is 000000000.MMMMMMMMMMMMMMMMMMMMMMM
// it is being multiplied by 2^exp (0 <= exp <= 7F), so just shift it left by exp
// after that, if any bit to the right of the decimal point is set, there is a fractional part
// those first 9 bits would contribute to the integral part of the value, so they don't matter
// I shifted them away, but they could also be masked away with 0x007fffff
// with regards to C, it's undefined behaviour to shift a 32-bit value by >=32 positions
// check Intel's documentation for what happens with shl; regardless, it must be dealt with
// in this case, there can't be a fractional part for exp >= 23, so assume it's an integer
// in assembly, it's probably faster to check if any of bits 5-7 are set
if (((exponent + 9) & 0xE0) != 0)
return value;
uint32_t frac = mantissa << (exponent + 9);
// at this point, there will only be a fractional part if any bit is set in frac
// if frac == 0, there isn't a fractional part; else, there is, so return QNaN
return frac == 0 ? value : bad_value;
} |
mgr.inz.Player:
SSE / x87 would've been simpler, but my way was more fun
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
 |
FreeER Grandmaster Cheater Supreme
Reputation: 53
Joined: 09 Aug 2013 Posts: 1091
|
Posted: Sun Jan 28, 2018 12:06 pm Post subject: |
|
|
Thanks alot ParkourPenguin, though I'm afraid I don't completely understand the +9 still lol It'd kind of make sense for the mantissa to shift out the sign and exponent bits but... they were already masked out at the start so it seems unnecessary.
Oh and mgr.inz.Player good reminder that you can use existing instructions to do a lot of the work lol
I still enjoyed learning from the more complicated script though
|
|
Back to top |
|
 |
mgr.inz.Player I post too much
Reputation: 222
Joined: 07 Nov 2008 Posts: 4438 Location: W kraju nad Wisla. UTC+01:00
|
Posted: Sun Jan 28, 2018 2:11 pm Post subject: |
|
|
@ParkourPenguin, I know. I also enjoyed making some custom types, for example population count (link to algo) and it worked very well. For 32bit type I used generic CPU registers, for 64bit type I used MMX registers (in 64bit CE I used RAX and RBX).
http://forum.cheatengine.org/viewtopic.php?t=565981
But, modern CPU have SSE4.2 and above, so I just tried POPCNT. I made few speed benchmarks...
Currently, CE Assembler doesn't recognize this instruction, I had to use:
Code: | dd C0B80FF3 // popcnt eax,eax |
Anyway, population count, a neat tool when you want to search for flags. E.g. Dead Space 3 flags like: isVacum, isNoGravity, isPlayerStandingOnPowerPlate, or Metro: Last Light flag: bShowCrosshair.
@FreeER, I agree.
_________________
|
|
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
|
|