From ae84b42c282e857dbc6d249fe9e26a44f45d5734 Mon Sep 17 00:00:00 2001 From: Cameron Cawley Date: Sat, 18 Jun 2022 00:24:36 +0100 Subject: [PATCH] COMMON: Fix reading and writing doubles from streams with older ARM toolchains --- common/endian.h | 121 +++++++++++++++++++++++++++++++++++++++++++ common/scummsys.h | 22 ++++++++ common/serializer.h | 5 +- common/stream.h | 54 +++++++------------ test/common/endian.h | 66 +++++++++++++++++++++++ 5 files changed, 230 insertions(+), 38 deletions(-) diff --git a/common/endian.h b/common/endian.h index 0ad9b467ac3..dd8ab24f389 100644 --- a/common/endian.h +++ b/common/endian.h @@ -623,6 +623,127 @@ inline void WRITE_BE_UINT24(void *ptr, uint32 value) { #define WRITE_UINT24(a,b) WRITE_BE_UINT24(a,b) #endif +union SwapFloat { + float f; + uint32 u32; +}; + +STATIC_ASSERT(sizeof(float) == sizeof(uint32), Unexpected_size_of_float); + +inline float READ_LE_FLOAT32(const void *ptr) { + SwapFloat swap; + swap.u32 = READ_LE_UINT32(ptr); + return swap.f; +} + +inline void WRITE_LE_FLOAT32(void *ptr, float value) { + SwapFloat swap; + swap.f = value; + WRITE_LE_UINT32(ptr, swap.u32); +} + +inline float READ_BE_FLOAT32(const void *ptr) { + SwapFloat swap; + swap.u32 = READ_BE_UINT32(ptr); + return swap.f; +} + +inline void WRITE_BE_FLOAT32(void *ptr, float value) { + SwapFloat swap; + swap.f = value; + WRITE_BE_UINT32(ptr, swap.u32); +} + +#ifdef SCUMM_LITTLE_ENDIAN +#define READ_FLOAT32(a) READ_LE_FLOAT32(a) +#define WRITE_FLOAT32(a,b) WRITE_LE_FLOAT32(a,b) +#else +#define READ_FLOAT32(a) READ_BE_FLOAT32(a) +#define WRITE_FLOAT32(a,b) WRITE_BE_FLOAT32(a,b) +#endif + +#ifdef SCUMM_FLOAT_WORD_LITTLE_ENDIAN +union SwapDouble { + double d; + uint64 u64; + struct { + uint32 low, high; + } u32; +}; +#else +union SwapDouble { + double d; + uint64 u64; + struct { + uint32 high, low; + } u32; +}; +#endif + +#ifndef __DC__ +STATIC_ASSERT(sizeof(double) == sizeof(uint64), Unexpected_size_of_double); +#endif + +inline double READ_LE_FLOAT64(const void *ptr) { + SwapDouble swap; + const uint8 *b = (const uint8 *)ptr; + swap.u32.low = READ_LE_UINT32(b); + swap.u32.high = READ_LE_UINT32(b + 4); + return swap.d; +} + +inline void WRITE_LE_FLOAT64(void *ptr, double value) { + SwapDouble swap; + swap.d = value; + uint8 *b = (uint8 *)ptr; + WRITE_LE_UINT32(b, swap.u32.low); + WRITE_LE_UINT32(b + 4, swap.u32.high); +} + +inline double READ_BE_FLOAT64(const void *ptr) { + SwapDouble swap; + const uint8 *b = (const uint8 *)ptr; + swap.u32.high = READ_BE_UINT32(b); + swap.u32.low = READ_BE_UINT32(b + 4); + return swap.d; +} + +inline void WRITE_BE_FLOAT64(void *ptr, double value) { + SwapDouble swap; + swap.d = value; + uint8 *b = (uint8 *)ptr; + WRITE_BE_UINT32(b, swap.u32.high); + WRITE_BE_UINT32(b + 4, swap.u32.low); +} + +inline double READ_FPA_FLOAT64(const void *ptr) { + SwapDouble swap; + const uint8 *b = (const uint8 *)ptr; + swap.u32.high = READ_LE_UINT32(b); + swap.u32.low = READ_LE_UINT32(b + 4); + return swap.d; +} + +inline void WRITE_FPA_FLOAT64(void *ptr, double value) { + SwapDouble swap; + swap.d = value; + uint8 *b = (uint8 *)ptr; + WRITE_LE_UINT32(b, swap.u32.high); + WRITE_LE_UINT32(b + 4, swap.u32.low); +} + +inline double READ_FLOAT64(const void *ptr) { + SwapDouble swap; + swap.u64 = READ_UINT64(ptr); + return swap.d; +} + +inline void WRITE_FLOAT64(void *ptr, double value) { + SwapDouble swap; + swap.d = value; + WRITE_UINT64(ptr, swap.u64); +} + inline int16 READ_LE_INT16(const void *ptr) { return static_cast(READ_LE_UINT16(ptr)); } diff --git a/common/scummsys.h b/common/scummsys.h index 78dcf122e98..bfc3e0dc613 100644 --- a/common/scummsys.h +++ b/common/scummsys.h @@ -300,6 +300,28 @@ #endif #endif +#if !defined(SCUMM_FLOAT_WORD_LITTLE_ENDIAN) && !defined(SCUMM_FLOAT_WORD_BIG_ENDIAN) + + #if defined(__ORDER_LITTLE_ENDIAN__) && defined(__ORDER_BIG_ENDIAN__) && defined(__FLOAT_WORD_ORDER__) + + #if (__FLOAT_WORD_ORDER__ == __ORDER_LITTLE_ENDIAN__) + #define SCUMM_FLOAT_WORD_LITTLE_ENDIAN + #elif (__FLOAT_WORD_ORDER__ == __ORDER_BIG_ENDIAN__) + #define SCUMM_FLOAT_WORD_BIG_ENDIAN + #else + #error Unsupported endianness + #endif + + #else + #ifdef SCUMM_LITTLE_ENDIAN + #define SCUMM_FLOAT_WORD_LITTLE_ENDIAN + #else + #define SCUMM_FLOAT_WORD_BIG_ENDIAN + #endif + + #endif +#endif + // // Some more system specific settings. // TODO/FIXME: All of these should be moved to backend specific files (such as portdefs.h) diff --git a/common/serializer.h b/common/serializer.h index a004148ed97..47cbdabcb15 100644 --- a/common/serializer.h +++ b/common/serializer.h @@ -128,8 +128,9 @@ public: SYNC_AS(Sint32BE, int32, 4) SYNC_AS(FloatLE, float, 4) SYNC_AS(FloatBE, float, 4) - SYNC_AS(DoubleLE, double, 4) - SYNC_AS(DoubleBE, double, 4) + + SYNC_AS(DoubleLE, double, 8) + SYNC_AS(DoubleBE, double, 8) /** * Returns true if an I/O failure occurred. diff --git a/common/stream.h b/common/stream.h index 874b7fd888e..225a78913c6 100644 --- a/common/stream.h +++ b/common/stream.h @@ -565,12 +565,9 @@ public: * calling err() and eos() ). */ FORCEINLINE float readFloatLE() { - uint32 n = readUint32LE(); - float f; - - memcpy(&f, &n, 4); - - return f; + uint8 val[4]; + read(val, 4); + return READ_LE_FLOAT32(val); } /** @@ -582,12 +579,9 @@ public: * calling err() and eos() ). */ FORCEINLINE float readFloatBE() { - uint32 n = readUint32BE(); - float f; - - memcpy(&f, &n, 4); - - return f; + uint8 val[4]; + read(val, 4); + return READ_BE_FLOAT32(val); } @@ -600,12 +594,9 @@ public: * calling err() and eos() ). */ FORCEINLINE double readDoubleLE() { - uint64 n = readUint64LE(); - double d; - - memcpy(&d, &n, 8); - - return d; + uint8 val[8]; + read(val, 8); + return READ_LE_FLOAT64(val); } /** @@ -617,12 +608,9 @@ public: * calling err() and eos() ). */ FORCEINLINE double readDoubleBE() { - uint64 n = readUint64BE(); - double d; - - memcpy(&d, &n, 8); - - return d; + uint8 val[8]; + read(val, 8); + return READ_BE_FLOAT64(val); } /** @@ -840,12 +828,9 @@ public: * and return it in native endianness. */ FORCEINLINE float readFloat() { - uint32 n = readUint32(); - float f; - - memcpy(&f, &n, 4); - - return f; + uint8 val[4]; + read(val, 4); + return (_bigEndian) ? READ_BE_FLOAT32(val) : READ_LE_FLOAT32(val); } /** @@ -853,12 +838,9 @@ public: * and return it in native endianness. */ FORCEINLINE double readDouble() { - uint64 n = readUint64(); - double d; - - memcpy(&d, &n, 8); - - return d; + uint8 val[8]; + read(val, 8); + return (_bigEndian) ? READ_BE_FLOAT64(val) : READ_LE_FLOAT64(val); } }; diff --git a/test/common/endian.h b/test/common/endian.h index 065b6997fc0..9716ebb2ecf 100644 --- a/test/common/endian.h +++ b/test/common/endian.h @@ -45,4 +45,70 @@ class EndianTestSuite : public CxxTest::TestSuite uint32 value = READ_LE_UINT16(data); TS_ASSERT_EQUALS(value, 0x3412UL); } + + void test_READ_BE_FLOAT32() { + const uint8 data[4] = { 0x40, 0x49, 0x0f, 0xdc }; + float value = READ_BE_FLOAT32(data); + TS_ASSERT_EQUALS(value, 3.141593f); + } + + void test_READ_LE_FLOAT32() { + const uint8 data[4] = { 0xdc, 0x0f, 0x49, 0x40 }; + float value = READ_LE_FLOAT32(data); + TS_ASSERT_EQUALS(value, 3.141593f); + } + + void test_READ_BE_FLOAT64() { + const uint8 data[8] = { 0x40, 0x09, 0x21, 0xfb, 0x82, 0xc2, 0xbd, 0x7f }; + double value = READ_BE_FLOAT64(data); + TS_ASSERT_EQUALS(value, 3.141593); + } + + void test_READ_LE_FLOAT64() { + const uint8 data[8] = { 0x7f, 0xbd, 0xc2, 0x82, 0xfb, 0x21, 0x09, 0x40 }; + double value = READ_LE_FLOAT64(data); + TS_ASSERT_EQUALS(value, 3.141593); + } + + void test_READ_FPA_FLOAT64() { + const uint8 data[8] = { 0xfb, 0x21, 0x09, 0x40, 0x7f, 0xbd, 0xc2, 0x82 }; + double value = READ_FPA_FLOAT64(data); + TS_ASSERT_EQUALS(value, 3.141593); + } + + void test_WRITE_BE_FLOAT32() { + const uint8 data[4] = { 0x40, 0x49, 0x0f, 0xdc }; + uint8 out[4]; + WRITE_BE_FLOAT32(out, 3.141593f); + TS_ASSERT_EQUALS(memcmp(data, out, 4), 0); + } + + void test_WRITE_LE_FLOAT32() { + const uint8 data[4] = { 0xdc, 0x0f, 0x49, 0x40 }; + uint8 out[4]; + WRITE_LE_FLOAT32(out, 3.141593f); + TS_ASSERT_EQUALS(memcmp(data, out, 4), 0); + } + + void test_WRITE_BE_FLOAT64() { + const uint8 data[8] = { 0x40, 0x09, 0x21, 0xfb, 0x82, 0xc2, 0xbd, 0x7f }; + uint8 out[8]; + WRITE_BE_FLOAT64(out, 3.141593); + TS_ASSERT_EQUALS(memcmp(data, out, 8), 0); + } + + void test_WRITE_LE_FLOAT64() { + const uint8 data[8] = { 0x7f, 0xbd, 0xc2, 0x82, 0xfb, 0x21, 0x09, 0x40 }; + uint8 out[8]; + WRITE_LE_FLOAT64(out, 3.141593); + TS_ASSERT_EQUALS(memcmp(data, out, 8), 0); + } + + void test_WRITE_FPA_FLOAT64() { + const uint8 data[8] = { 0xfb, 0x21, 0x09, 0x40, 0x7f, 0xbd, 0xc2, 0x82 }; + uint8 out[8]; + WRITE_FPA_FLOAT64(out, 3.141593); + TS_ASSERT_EQUALS(memcmp(data, out, 8), 0); + } + };