 |
Cheat Engine The Official Site of Cheat Engine
|
View previous topic :: View next topic |
Author |
Message |
vesperpallens How do I cheat?
Reputation: 0
Joined: 02 Sep 2024 Posts: 3
|
Posted: Sun Aug 24, 2025 12:33 pm Post subject: Custom LUA type |
|
|
I wanted to create a custom lua type to interpret UTF-32LE strings, but as of now I'm stuck at valuetobytes function.
The issue seems to be on CE side and how it handles writing bytes
For reference:
Code: | function UTF8Codepoints(str)
...
end
function gd4string_valuetobytes(str,address)
local idx = 0
for codePoint in UTF8Codepoints( str ) do
if codePoint < 0 or codePoint > 0x10FFFF or codePoint >= 0xD800 and codePoint <= 0xDFFF then codePoint = 0xFFFD end
writeInteger( address + idx * 0x4, codePoint )
idx = idx + 1
end
-- null terminator
writeInteger(address + idx * 4, 0x0)
return readByte( address ) or 0x0
end
-- and for reference:
registerCustomTypeLua('GD4 String', 1, gd4string_bytestovalue, gd4string_valuetobytes, false, true) |
So let's say I have a XYZ123 string and replace it with ABCDEF where I get AYCDEF. Everything would work fine unless CE would restore the original bytes after the 'callback' (from what I suggest, it keeps a 8byte-ish buffer which then is restored taking return bytes into account). I tried changing the type's bytecount to 8/16 - I still get the old char somewhere.
Before passing that to Dark Byte directly, I just wanted to make sure I'm not retarded. How do I achieve the intended result? |
|
Back to top |
|
 |
ParkourPenguin I post too much
Reputation: 152
Joined: 06 Jul 2014 Posts: 4690
|
Posted: Mon Aug 25, 2025 3:00 am Post subject: |
|
|
You shouldn't write to `address` in the `_valuetobytes` function. As the template indicates, you should return the bytes you want to write. The template shows them returning as multiple values, but it seems you can also return it as a Lua array too. There might be subtle differences, like how trying to write fewer bytes than necessary should leave the remaining bytes unchanged when returning multiple values but should zero the rest when returning a table (if I'm reading the source correctly).
Make sure the `bytecount` is big enough to write whatever string you want. _________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
 |
vesperpallens How do I cheat?
Reputation: 0
Joined: 02 Sep 2024 Posts: 3
|
Posted: Tue Aug 26, 2025 3:52 pm Post subject: |
|
|
I wish strings were more flexible from my perspective, it would be even better.
If I use the function to write bytes manually, it seems more safe, because I have no idea what's gonna happen if memory is not initialized or used by something else when bytecount is huge. So either try that or have no valuetobytes feature for that type at all. |
|
Back to top |
|
 |
Dark Byte Site Admin
Reputation: 470
Joined: 09 May 2003 Posts: 25763 Location: The netherlands
|
Posted: Tue Aug 26, 2025 5:12 pm Post subject: |
|
|
Right now when you return less than the number of strings it writes 0 to the bytes, and there's an issue with the lua stack with huge types which will cause CE to panic/crash
(fixed in next version) _________________
Do not ask me about online cheats. I don't know any and wont help finding them.
Like my help? Join me on Patreon so i can keep helping |
|
Back to top |
|
 |
Dark Byte Site Admin
Reputation: 470
Joined: 09 May 2003 Posts: 25763 Location: The netherlands
|
Posted: Tue Aug 26, 2025 6:03 pm Post subject: |
|
|
But instead of lua, you can also use an auto assembler script for custom type
here's a script that might be of interest to you:
Code: |
{$c}
char TypeName[]="My 32-bit string type";
int ByteSize=200;
char usedFloat=0;
char usesString=1;
char CallMethod=1;
unsigned short MaxStringSize=200;
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
// Decode UTF-8 string into UTF-32LE buffer (preallocated)
// Returns number of codepoints written
size_t utf8_to_utf32le(const char *input, uint32_t *output, size_t max_output) {
if (!input || !output || max_output == 0) return 0;
size_t i = 0;
const unsigned char *p = (const unsigned char *)input;
while (*p && i < max_output - 1) { // reserve space for terminator
uint32_t cp;
if (*p < 0x80) {
cp = *p++;
} else if ((*p & 0xE0) == 0xC0) {
cp = (*p++ & 0x1F) << 6;
if ((*p & 0xC0) == 0x80) cp |= (*p++ & 0x3F);
} else if ((*p & 0xF0) == 0xE0) {
cp = (*p++ & 0x0F) << 12;
if ((*p & 0xC0) == 0x80) cp |= (*p++ & 0x3F) << 6;
if ((*p & 0xC0) == 0x80) cp |= (*p++ & 0x3F);
} else if ((*p & 0xF8) == 0xF0) {
cp = (*p++ & 0x07) << 18;
if ((*p & 0xC0) == 0x80) cp |= (*p++ & 0x3F) << 12;
if ((*p & 0xC0) == 0x80) cp |= (*p++ & 0x3F) << 6;
if ((*p & 0xC0) == 0x80) cp |= (*p++ & 0x3F);
} else {
cp = 0xFFFD; // invalid
p++;
}
output[i++] = cp;
}
output[i] = 0; // null terminate
return i;
}
// UTF-8 encoding of UTF-32 codepoints into preallocated buffer
// Returns number of bytes written (excluding terminator)
size_t utf32le_to_utf8(const uint32_t *input, char *output, size_t max_output) {
if (!input || !output || max_output == 0) return 0;
size_t o = 0;
for (size_t i = 0; input[i] != 0; i++) {
uint32_t cp = input[i];
// replace invalid codepoints
if (cp > 0x10FFFF || (cp >= 0xD800 && cp <= 0xDFFF)) {
cp = 0xFFFD;
}
if (cp < 0x80) {
if (o + 1 >= max_output) break;
output[o++] = (char)cp;
} else if (cp < 0x800) {
if (o + 2 >= max_output) break;
output[o++] = 0xC0 | (cp >> 6);
output[o++] = 0x80 | (cp & 0x3F);
} else if (cp < 0x10000) {
if (o + 3 >= max_output) break;
output[o++] = 0xE0 | (cp >> 12);
output[o++] = 0x80 | ((cp >> 6) & 0x3F);
output[o++] = 0x80 | (cp & 0x3F);
} else {
if (o + 4 >= max_output) break;
output[o++] = 0xF0 | (cp >> 18);
output[o++] = 0x80 | ((cp >> 12) & 0x3F);
output[o++] = 0x80 | ((cp >> 6) & 0x3F);
output[o++] = 0x80 | (cp & 0x3F);
}
}
output[o] = '\0';
return o;
}
//The convert routine should hold a routine that converts the data to a string
//input is a pointer to the data (max buffersize long)
//output is utf8
__cdecl int ConvertRoutine(unsigned char *data, unsigned long long address, unsigned char *output)
{
utf32le_to_utf8(data, output, 100);
}
//The convert back routine should hold a routine that converts the given integer back to a row of bytes (e.g when the user wats to write a new value)
__cdecl void ConvertBackRoutine(unsigned char *input, unsigned long long address, unsigned char *output) //
{
utf8_to_utf32le(input, output, 100);
}
{$asm}
|
and yes, I just blindly copied chatgpt for the conversion routines. You may need to adjust that as I suck at string conversions
(maybe also a max input size as well) _________________
Do not ask me about online cheats. I don't know any and wont help finding them.
Like my help? Join me on Patreon so i can keep helping |
|
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
|
|