Atmosphere icon indicating copy to clipboard operation
Atmosphere copied to clipboard

Add Support For Float/Double Cheats

Open rydoginator opened this issue 4 years ago • 20 comments

Feature Request

I am requesting to add float/double casting to the dmnt_cheat process.

What feature are you suggesting?

Overview:

  • Currently the supported types are 8 bit,16 bit, 32 bit and 64 bit. Which is nice, but if you're working with a value in memory that is a float or double, then the arithmetic instructions won't work evenly.

Smaller Details:

  • Adding F and D to determine the size of the value, like how 1,2,4,8 are already used.

Nature of Request:

  • Addition of more data types for sizes of cheats.
  • Looks like the size is determined here, so it would be a matter of modifying that switch-case statement.
    I just can't for the life of me figure out how to compile, I get errors all over the place, which is why I'm asking as a feature request :)

What component do you feel this would best fit within?

Why would this feature be useful?

Like I said, implementing float/double types will allow for more better cheats, like moon jump or size modifiers, basically anything that modifies anything that is float or double.

rydoginator avatar Apr 28 '20 04:04 rydoginator

Yes, I know what floats would be in hex. I'm mainly concerned about arithmetics. For example, if you were to add 3F800000 + 3F800000 together, you'd get 7F000000 which is 1.70141e+38 in float. Casting it as a float would cause them to be added evenly so that it would be 40000000 which would be 2.0.

rydoginator avatar Apr 28 '20 06:04 rydoginator

Fine with adding this once C++20 std::bit_cast is available.

SciresM avatar Apr 28 '20 06:04 SciresM

Fine with adding this once C++20 std::bit_cast is available.

Why not a standard cast?

rydoginator avatar Apr 28 '20 06:04 rydoginator

how do you "standard cast" a hex pattern to float/double

SciresM avatar Apr 28 '20 06:04 SciresM

how do you "standard cast" a hex pattern to float/double

You're casting it here, right? If I'm reading the source correct, GetNextVmInt converts the hex pattern to u32, and then it's a matter of casting it to float, right? Float registers would probably need to be implemented, but isn't it just as simple as

(float)first_dword; 
(((double)first_dword) << 32ul) | ((u64)GetNextDword())

rydoginator avatar Apr 28 '20 07:04 rydoginator

that is not at all how casting works

SciresM avatar Apr 28 '20 07:04 SciresM

To be more specific: float <-> u32 bit pattern conversion is more complicated than that.

SciresM avatar Apr 28 '20 07:04 SciresM

Wait, are you thinking of implementing floats/doubles like this?

0F000010 10000000 1.0

I was kind of hoping for it to be like this

0F000010 10000000 3F800000

where the operand (like for the arithmetics opcode's) would be the hexadecimal representation, but it'll be casted as float so that the result would be the hexadecimal representation of said float number.

How would it be more complicated than that?

rydoginator avatar Apr 28 '20 07:04 rydoginator

no, I am thinking of implementing like 0F000010 10000000 3F800000 .

This requires std::bit_cast.

That is not how u32 <-> float casts work.

SciresM avatar Apr 28 '20 07:04 SciresM

Wait I feel stupid. I just read up more on bit_cast and now I see why.

rydoginator avatar Apr 28 '20 07:04 rydoginator

Yes, I know what floats would be in hex. I'm mainly concerned about arithmetics. For example, if you were to add 3F800000 + 3F800000 together, you'd get 7F000000 which is 1.70141e+38 in float. Casting it as a float would cause them to be added evenly so that it would be 40000000 which would be 2.0.

Yeah, I just read about math calculations and deleted answer. Sorry. :P

masagrator avatar Apr 28 '20 08:04 masagrator

can do it with pointer aliasing too, but yeah, bit_cast is better style

misson20000 avatar Apr 28 '20 17:04 misson20000

or do it the safe old fashioned way with a union

RSDuck avatar May 01 '20 23:05 RSDuck

Type punning with unions is undefined behavior in C++.

leoetlino avatar May 02 '20 00:05 leoetlino

I see it's allowed in C but not in C++. Though GNU C++ allows it 😉

RSDuck avatar May 02 '20 12:05 RSDuck

Looks like C++20 was released for devkitA64 today!

rydoginator avatar May 11 '20 20:05 rydoginator

@leoetlino gnu++20 allows type-punning with unions explicitly, which we use.

Since gcc 10 doesn't implement std::bit_cast....

@rydoginator I spent like 30 minutes trying to implement this. It was awkward because the registers are integers and the desired behavior isn't really clear when doing arithmetic.

Could you write a specification for the cheat changes you'd like? I am happy to implement them.

SciresM avatar May 16 '20 22:05 SciresM

I don't have any example offsets currently, but I can think of 2 ways I'd like this to be implemented. 1: enabling a floating point flag, so that both operands are treated as float and double. Treat all instructions as double/single floating point if the flag is enabled. If T is 4 or less, treat it as single. If it's 8, treat it as double. Example:

F1000000 00000001 //enable float flag... Treat all arithmetic operations as float / double
94000100 3F800000 // r0 = r0 + 1.0
F1000000 00000000 // disable float flag... Treat all arithmetic operations as integers.
94000100 3F800000 // r0 = r0 + 0x3f800000;

2: adding float + double registers, so if the user chooses F for the T parameter, it'll use one of the float registers.

9F000100 3F800000 //f0 = f0 + 1.0
9D000100 3ff00000 00000000 // d0 = d0 + 1.0

Does that make sense? I don't mind with either method, whatever way is easier for you.

rydoginator avatar May 16 '20 23:05 rydoginator

@SciresM yes std::bit_cast will be in c++20, but technically you don't have to wait for it. std::bit_cast has been added to deal with type aliasing (type punning) in the C++ language. According to https://en.cppreference.com/w/cpp/numeric/bit_cast std::bit_cast can be implemented like this:

template <class To, class From>
typename std::enable_if_t<
    sizeof(To) == sizeof(From) &&
    std::is_trivially_copyable_v<From> &&
    std::is_trivially_copyable_v<To>,
    To>
bit_cast(const From& src) noexcept
{
    static_assert(std::is_trivially_constructible_v<To>,
        "This implementation additionally requires destination type to be trivially constructible");
 
    To dst;
    std::memcpy(&dst, &src, sizeof(To));
    return dst;
}

This is of course idiot proof. But you can see that the actual function that does the 'work' is std::memcpy, so if you don't want to use this idiot proof 'bit_cast' code you can always just use std::memcpy.

Like so:

#include <iostream>
#include <cstdint>
#include <cstring>

int main()
{
    uint32_t int_value = 0x3f800000; // 1.f in 'raw' form.
    float float_value;
    std::memcpy(&float_value, &int_value, sizeof(float_value));
    std::cout << float_value << "\n"; // this will print "1".
    return 0;
}

See https://godbolt.org/z/9c4Erc for a working example

Captnoord avatar Jul 16 '20 09:07 Captnoord

@Captnoord i don't know why you wrote all that.

bit_cast cannot be constexpr without compiler magic. That compiler magic hasn't been implemented yet.

SciresM avatar Jul 16 '20 17:07 SciresM