Qhimm.com Forums
Miscellaneous Forums => Scripting and Reverse Engineering => Topic started by: Alhexx on 2002-06-04 17:07:33
-
Well, I've written a VC++ DLL for Ultima to swap bytes (especially for the LWO exporter). Swapping integers was no problem, I simply used the << and >> operators. But they don't work with floats...
However, Alhexx went to write a bit of assembler code :D :
// Swaps Bytes in 32-Bit IEEE Float
float APIENTRY SwapFloat(float *Value)
{
float fretVal = float(0);
float* pretVal = &fretVal;
// Uses VC++ Inline Assembler
_asm
{ // Push Registers to Stack (save them)
push eax
push ebx
push ecx
push edx
// Write Address of Values to ECX / EDX Register
mov ecx, Value
mov edx, pretVal
// Read 8-Bit Values from the Float and write them to AX / BX Registers
mov al, byte ptr [ecx]
mov ah, byte ptr [ecx + 1]
mov bl, byte ptr [ecx + 2]
mov bh, byte ptr [ecx + 3]
// Write them to the memory of the return Value
mov byte ptr [edx] , bh
mov byte ptr [edx + 1], bl
mov byte ptr [edx + 2], ah
mov byte ptr [edx + 3], al
// Pop Registers from Stack
pop edx
pop ecx
pop ebx
pop eax
}
return fretVal;
}
Any suggestions or better ideas?
- Alhexx
-
Sure. Use typecasting ;)
Delphi and C (and therefore C++) both support it ...
In C I think (can't say for sure...) it'd be:
float somef;
int temp;
somef = value;
temp = (int)somef;
temp = (temp >> 24) | ( (temp >> 8) & 0xff00) | ( (temp << 8) & 0xff0000) | ( (temp << 24) & 0xff000000;
somef = (float)temp;
Well, you get the idea. Just tell it what type to interpret those values as. Obviously, works best when the source & destination variable are the same size...
In Delphi, two ways:
var
S: Single;
I: Integer;
begin
I := Integer(S);
//do some stuff with I...
S := Single(I);
end;
Or an even more compact way:
var
S: Single
I: Integer absolute S;
With that declaration, variables I and S occupy the same place in memory so you don't even need to typecast ;)
-
float somef;
int temp;
somef = value;
temp = (int)somef;
Im not sure about that, but i think that it will put only integer part of float number into temp ... and that is not what we wanted.
I would do it like this:
float SwapFloat( float fin )
{
union {
float f;
unsigned char c[4];
} normal, swapped;
normal.f = fin;
swapped.c[0] = normal.c[3];
swapped.c[1] = normal.c[2];
swapped.c[2] = normal.c[1];
swapped.c[3] = normal.c[0];
return swapped.f;
}
Union is a good thingie, because all variables in it occupy same place. so c[0] is first byte of f, c[1] is 2nd ...
Or here is a more general function that i use, it is good for reversing anything (to 64bytes in length):
#define MaxSwapBytes 64
void Swap( void* data, int LengthInBytes )
{
assert( ((LengthInBytes > 0 ) && (LengthInBytes <= MaxSwapBytes )) );
if ( ! ((LengthInBytes > 0 ) && (LengthInBytes <= MaxSwapBytes )) )
return;
int i;
unsigned char *normal, swapped[ MaxSwapBytes ];
normal = (unsigned char*) data;
for( i=0; i<LengthInBytes; i++ )
swapped[ i ] = normal[ LengthInBytes - 1 - i ];
memcpy( data, swapped, LengthInBytes );
}
you have to supply a pointer to your variable and length in bytes, like swap( &f, sizeof( float ) ); or swap( &myshort, 2 );
It does the same as the one above, but in a cycle.
-
I'm pretty sure my way would work - that is, after all, the whole point about typecasting.
-
Fice: I'm not sure 'bout that method, too. In my opinion it'll only convert the integer part (as mirex said).
mirex: Ehm ... fine ... however, if I take a look at your second code, I think I'll prefer the ASM method :D
- Alhexx
-
Hmm ... I dunno. I know you /can/ typecast in C, but I'll admit I'm not 100% on the syntax, since I don't really use C/C++ much ;)
I guess the alternative is to use pointers...
float f = somevalue;
int* ipoint = &f;
Then now perform all your bitswapping stuff on (*i) ...
-
ficedula: I agree with the other boys here, (int)float only uses the integer part, that is, the C compiler usually replaces it with a call to the _ftol (float to long) function.
-
How blade .... how /do/ you tell it to reinterpret as another type? Or do you have to use pointers to do that?
-
Pointers, or unions. Pointers may be easier for the most part.
-
I forgot to write it back then. So to retype float to unsigned long (both 4 bytes):
unsigned long *ul;
float f;
ul = (unsigned long*)&f;
//now here it is, but it is in pointer, so usage has to be like this:
//and if you change *ul, you change 'f' too, of course.
*ul = *ul + 1000000;
//or another way, not by retyping, it just moves float to long, easy one, but not too pretty
unsigned long ul;
float f;
memcpy( &ul, &f, 4 );
ul += 10000000;
memcpy( &f, &ul, 4 );
mirex: Ehm ... fine ... however, if I take a look at your second code, I think I'll prefer the ASM method
You know, general functions allways look messy, and it would look even worse in asm.
-
Then again, why not copy it into its own stack space immediately, instead of pointers?
unsigned long ul;
float f;
ul = *(unsigned long*)&f;
// After which, you can do
ul = ul + 10000;
// Note that changing ul does *not* change f
-
Don't pass endian-swapped floats in as floats -- they will get loaded in and out of the floating point registers, and if the swapped bit pattern happens to correspond to a denormal, the FPU will normalize it and trash the value.
void __declspec(naked) __stdcall ByteSwap32(void *pValue) {
__asm {
mov edx, [esp+4]
mov eax, [edx]
bswap eax ;requires 486+
mov [edx], eax
ret 4
}
}
[/code]