aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Astley2014-09-16 13:49:27 -0500
committerAdrian Astley2014-09-16 13:49:27 -0500
commitc58f7146102be3aac2cef5d69bdceb109da3f09f (patch)
tree4ce7529f3708ebc01d8a19e4f18123e00f03cce8
parent2b9a5ca2b3e4e07f391c4aa0c4ee5223d52b6903 (diff)
downloadscummvm-rg350-c58f7146102be3aac2cef5d69bdceb109da3f09f.tar.gz
scummvm-rg350-c58f7146102be3aac2cef5d69bdceb109da3f09f.tar.bz2
scummvm-rg350-c58f7146102be3aac2cef5d69bdceb109da3f09f.zip
COMMON: Add support for endian-safe reading/writing of int64
-rw-r--r--common/endian.h173
-rw-r--r--common/stream.h66
2 files changed, 239 insertions, 0 deletions
diff --git a/common/endian.h b/common/endian.h
index 6d6563f802..a18e6f088b 100644
--- a/common/endian.h
+++ b/common/endian.h
@@ -49,6 +49,17 @@
# error No endianness defined
#endif
+
+#define SWAP_CONSTANT_64(a) \
+ ((uint64)((((a) >> 56) & 0x000000FF) | \
+ (((a) >> 40) & 0x0000FF00) | \
+ (((a) >> 24) & 0x00FF0000) | \
+ (((a) >> 8) & 0xFF000000) | \
+ (((a) & 0xFF000000) << 8) | \
+ (((a) & 0x00FF0000) << 24) | \
+ (((a) & 0x0000FF00) << 40) | \
+ (((a) & 0x000000FF) << 56) ))
+
#define SWAP_CONSTANT_32(a) \
((uint32)((((a) >> 24) & 0x00FF) | \
(((a) >> 8) & 0xFF00) | \
@@ -60,6 +71,62 @@
(((a) << 8) & 0xFF00) ))
/**
+ * Swap the bytes in a 64 bit word in order to convert LE encoded data to BE
+ * and vice versa.
+ */
+
+// machine/compiler-specific variants come first, fallback last
+
+// Test for GCC and if the target has the MIPS rel.2 instructions (we know the psp does)
+//
+// TODO: Fix this #if statement. It isn't changed from 32 bit. Is there a 64 bit swap instruction?
+#if defined(__GNUC__) && (defined(__psp__) || defined(_MIPS_ARCH_MIPS32R2) || defined(_MIPS_ARCH_MIPS64R2))
+
+ FORCEINLINE uint32 SWAP_BYTES_32(const uint32 a) {
+ if (__builtin_constant_p(a)) {
+ return SWAP_CONSTANT_32(a);
+ } else {
+ uint32 result;
+# if defined(__psp__)
+ // use special allegrex instruction
+ __asm__ ("wsbw %0,%1" : "=r" (result) : "r" (a));
+# else
+ __asm__ ("wsbh %0,%1\n"
+ "rotr %0,%0,16" : "=r" (result) : "r" (a));
+# endif
+ return result;
+ }
+ }
+
+// Test for GCC >= 4.3.0 as this version added the bswap builtin
+#elif GCC_ATLEAST(4, 3)
+
+ FORCEINLINE uint64 SWAP_BYTES_64(uint64 a) {
+ return __builtin_bswap64(a);
+ }
+
+#elif defined(_MSC_VER)
+
+ FORCEINLINE uint64 SWAP_BYTES_64(uint64 a) {
+ return _byteswap_uint64(a);
+ }
+
+// generic fallback
+#else
+
+ inline uint64 SWAP_BYTES_64(uint64 a) {
+ uint32 low = (uint32)a, high = (uint32)(a >> 32);
+ uint16 lowLow = (uint16)low, lowHigh = (uint16)(low >> 16),
+ highLow = (uint16)high, highHigh = (uint16)(high >> 16);
+
+ return ((uint64)(((uint32)(uint16)((lowLow >> 8) | (lowLow << 8)) << 16) |
+ (uint16)((lowHigh >> 8) | (lowHigh << 8))) << 32) |
+ (((uint32)(uint16)((highLow >> 8) | (highLow << 8)) << 16) |
+ (uint16)((highHigh >> 8) | (highHigh << 8)))
+ }
+#endif
+
+/**
* Swap the bytes in a 32 bit word in order to convert LE encoded data to BE
* and vice versa.
*/
@@ -173,6 +240,11 @@
return ((const Unaligned32 *)ptr)->val;
}
+ FORCEINLINE uint64 READ_UINT64(const void *ptr) {
+ struct Unaligned64 { uint64 val; } __attribute__ ((__packed__, __may_alias__));
+ return ((const Unaligned64 *)ptr)->val;
+ }
+
FORCEINLINE void WRITE_UINT16(void *ptr, uint16 value) {
struct Unaligned16 { uint16 val; } __attribute__ ((__packed__, __may_alias__));
((Unaligned16 *)ptr)->val = value;
@@ -183,6 +255,11 @@
((Unaligned32 *)ptr)->val = value;
}
+ FORCEINLINE void WRITE_UINT64(void *ptr, uint64 value) {
+ struct Unaligned64 { uint64 val; } __attribute__((__packed__, __may_alias__));
+ ((Unaligned64 *)ptr)->val = value;
+ }
+
#elif !defined(SCUMM_NEED_ALIGNMENT)
FORCEINLINE uint16 READ_UINT16(const void *ptr) {
@@ -193,6 +270,10 @@
return *(const uint32 *)(ptr);
}
+ FORCEINLINE uint64 READ_UINT64(const void *ptr) {
+ return *(const uint64 *)(ptr);
+ }
+
FORCEINLINE void WRITE_UINT16(void *ptr, uint16 value) {
*(uint16 *)(ptr) = value;
}
@@ -201,6 +282,10 @@
*(uint32 *)(ptr) = value;
}
+ FORCEINLINE void WRITE_UINT64(void *ptr, uint64 value) {
+ *(uint64 *)(ptr) = value;
+ }
+
// use software fallback by loading each byte explicitely
#else
@@ -215,6 +300,10 @@
const uint8 *b = (const uint8 *)ptr;
return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | (b[0]);
}
+ inline uint64 READ_UINT64(const void *ptr) {
+ const uint8 *b = (const uint8 *)ptr;
+ return (b[7] << 56) | (b[6] << 48) | (b[5] << 40) | (b[4] << 32) | (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | (b[0]);
+ }
inline void WRITE_UINT16(void *ptr, uint16 value) {
uint8 *b = (uint8 *)ptr;
b[0] = (uint8)(value >> 0);
@@ -227,6 +316,17 @@
b[2] = (uint8)(value >> 16);
b[3] = (uint8)(value >> 24);
}
+ inline void WRITE_UINT64(void *ptr, uint64 value) {
+ uint8 *b = (uint8 *)ptr;
+ b[0] = (uint8)(value >> 0);
+ b[1] = (uint8)(value >> 8);
+ b[2] = (uint8)(value >> 16);
+ b[3] = (uint8)(value >> 24);
+ b[4] = (uint8)(value >> 32);
+ b[5] = (uint8)(value >> 40);
+ b[6] = (uint8)(value >> 48);
+ b[7] = (uint8)(value >> 56);
+ }
# elif defined(SCUMM_BIG_ENDIAN)
@@ -238,6 +338,10 @@
const uint8 *b = (const uint8 *)ptr;
return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | (b[3]);
}
+ inline uint64 READ_UINT64(const void *ptr) {
+ const uint8 *b = (const uint8 *)ptr;
+ return (b[0] << 56) | (b[1] << 48) | (b[2] << 40) | (b[3] << 32) | (b[4] << 24) | (b[5] << 16) | (b[6] << 8) | (b[7]);
+ }
inline void WRITE_UINT16(void *ptr, uint16 value) {
uint8 *b = (uint8 *)ptr;
b[0] = (uint8)(value >> 8);
@@ -250,6 +354,17 @@
b[2] = (uint8)(value >> 8);
b[3] = (uint8)(value >> 0);
}
+ inline void WRITE_UINT64(void *ptr, uint64 value) {
+ uint8 *b = (uint8 *)ptr;
+ b[0] = (uint8)(value >> 56);
+ b[1] = (uint8)(value >> 48);
+ b[2] = (uint8)(value >> 40);
+ b[3] = (uint8)(value >> 32);
+ b[4] = (uint8)(value >> 24);
+ b[5] = (uint8)(value >> 16);
+ b[6] = (uint8)(value >> 8);
+ b[7] = (uint8)(value >> 0);
+ }
# endif
@@ -261,25 +376,33 @@
#define READ_LE_UINT16(a) READ_UINT16(a)
#define READ_LE_UINT32(a) READ_UINT32(a)
+ #define READ_LE_UINT64(a) READ_UINT64(a)
#define WRITE_LE_UINT16(a, v) WRITE_UINT16(a, v)
#define WRITE_LE_UINT32(a, v) WRITE_UINT32(a, v)
+ #define WRITE_LE_UINT64(a, v) WRITE_UINT64(a, v)
+ #define FROM_LE_64(a) ((uint64)(a))
#define FROM_LE_32(a) ((uint32)(a))
#define FROM_LE_16(a) ((uint16)(a))
+ #define FROM_BE_64(a) SWAP_BYTES_64(a)
#define FROM_BE_32(a) SWAP_BYTES_32(a)
#define FROM_BE_16(a) SWAP_BYTES_16(a)
+ #define TO_LE_64(a) ((uint64)(a))
#define TO_LE_32(a) ((uint32)(a))
#define TO_LE_16(a) ((uint16)(a))
+ #define TO_BE_64(a) SWAP_BYTES_64(a)
#define TO_BE_32(a) SWAP_BYTES_32(a)
#define TO_BE_16(a) SWAP_BYTES_16(a)
+ #define CONSTANT_LE_64(a) ((uint64)(a))
#define CONSTANT_LE_32(a) ((uint32)(a))
#define CONSTANT_LE_16(a) ((uint16)(a))
+ #define CONSTANT_BE_64(a) SWAP_CONSTANT_64(a)
#define CONSTANT_BE_32(a) SWAP_CONSTANT_32(a)
#define CONSTANT_BE_16(a) SWAP_CONSTANT_16(a)
@@ -294,6 +417,10 @@
const uint8 *b = (const uint8 *)ptr;
return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | (b[3]);
}
+ inline uint64 READ_BE_UINT64(const void *ptr) {
+ const uint8 *b = (const uint8 *)ptr;
+ return (b[0] << 56) | b[1] << 48) | b[2] << 40) | b[3] << 32) | b[4] << 24) | (b[5] << 16) | (b[6] << 8) | (b[7]);
+ }
inline void WRITE_BE_UINT16(void *ptr, uint16 value) {
uint8 *b = (uint8 *)ptr;
b[0] = (uint8)(value >> 8);
@@ -306,6 +433,17 @@
b[2] = (uint8)(value >> 8);
b[3] = (uint8)(value >> 0);
}
+ inline void WRITE_BE_UINT64(void *ptr, uint64 value) {
+ uint8 *b = (uint8 *)ptr;
+ b[0] = (uint8)(value >> 56);
+ b[1] = (uint8)(value >> 48);
+ b[2] = (uint8)(value >> 40);
+ b[3] = (uint8)(value >> 32);
+ b[4] = (uint8)(value >> 24);
+ b[5] = (uint8)(value >> 16);
+ b[6] = (uint8)(value >> 8);
+ b[7] = (uint8)(value >> 0);
+ }
# else
inline uint16 READ_BE_UINT16(const void *ptr) {
@@ -314,12 +452,18 @@
inline uint32 READ_BE_UINT32(const void *ptr) {
return SWAP_BYTES_32(READ_UINT32(ptr));
}
+ inline uint32 READ_BE_UINT64(const void *ptr) {
+ return SWAP_BYTES_64(READ_UINT64(ptr));
+ }
inline void WRITE_BE_UINT16(void *ptr, uint16 value) {
WRITE_UINT16(ptr, SWAP_BYTES_16(value));
}
inline void WRITE_BE_UINT32(void *ptr, uint32 value) {
WRITE_UINT32(ptr, SWAP_BYTES_32(value));
}
+ inline void WRITE_BE_UINT64(void *ptr, uint64 value) {
+ WRITE_UINT64(ptr, SWAP_BYTES_64(value));
+ }
# endif // if defined(SCUMM_NEED_ALIGNMENT)
@@ -327,25 +471,33 @@
#define READ_BE_UINT16(a) READ_UINT16(a)
#define READ_BE_UINT32(a) READ_UINT32(a)
+ #define READ_BE_UINT64(a) READ_UINT64(a)
#define WRITE_BE_UINT16(a, v) WRITE_UINT16(a, v)
#define WRITE_BE_UINT32(a, v) WRITE_UINT32(a, v)
+ #define WRITE_BE_UINT64(a, v) WRITE_UINT64(a, v)
+ #define FROM_LE_64(a) SWAP_BYTES_64(a)
#define FROM_LE_32(a) SWAP_BYTES_32(a)
#define FROM_LE_16(a) SWAP_BYTES_16(a)
+ #define FROM_BE_64(a) ((uint64)(a))
#define FROM_BE_32(a) ((uint32)(a))
#define FROM_BE_16(a) ((uint16)(a))
+ #define TO_LE_64(a) SWAP_BYTES_64(a)
#define TO_LE_32(a) SWAP_BYTES_32(a)
#define TO_LE_16(a) SWAP_BYTES_16(a)
+ #define TO_BE_64(a) ((uint64)(a))
#define TO_BE_32(a) ((uint32)(a))
#define TO_BE_16(a) ((uint16)(a))
+ #define CONSTANT_LE_64(a) SWAP_CONSTANT_64(a)
#define CONSTANT_LE_32(a) SWAP_CONSTANT_32(a)
#define CONSTANT_LE_16(a) SWAP_CONSTANT_16(a)
+ #define CONSTANT_BE_64(a) ((uint64)(a))
#define CONSTANT_BE_32(a) ((uint32)(a))
#define CONSTANT_BE_16(a) ((uint16)(a))
@@ -360,6 +512,10 @@
const uint8 *b = (const uint8 *)ptr;
return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | (b[0]);
}
+ inline uint64 READ_LE_UINT64(const void *ptr) {
+ const uint8 *b = (const uint8 *)ptr;
+ return (b[7] << 56) | (b[6] << 48) | (b[5] << 40) | (b[4] << 32) | (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | (b[0]);
+ }
inline void WRITE_LE_UINT16(void *ptr, uint16 value) {
uint8 *b = (uint8 *)ptr;
b[0] = (uint8)(value >> 0);
@@ -372,6 +528,17 @@
b[2] = (uint8)(value >> 16);
b[3] = (uint8)(value >> 24);
}
+ inline void WRITE_LE_UINT64(void *ptr, uint64 value) {
+ uint8 *b = (uint8 *)ptr;
+ b[0] = (uint8)(value >> 0);
+ b[1] = (uint8)(value >> 8);
+ b[2] = (uint8)(value >> 16);
+ b[3] = (uint8)(value >> 24);
+ b[4] = (uint8)(value >> 32);
+ b[5] = (uint8)(value >> 40);
+ b[6] = (uint8)(value >> 48);
+ b[7] = (uint8)(value >> 56);
+ }
# else
inline uint16 READ_LE_UINT16(const void *ptr) {
@@ -380,12 +547,18 @@
inline uint32 READ_LE_UINT32(const void *ptr) {
return SWAP_BYTES_32(READ_UINT32(ptr));
}
+ inline uint64 READ_LE_UINT64(const void *ptr) {
+ return SWAP_BYTES_64(READ_UINT64(ptr));
+ }
inline void WRITE_LE_UINT16(void *ptr, uint16 value) {
WRITE_UINT16(ptr, SWAP_BYTES_16(value));
}
inline void WRITE_LE_UINT32(void *ptr, uint32 value) {
WRITE_UINT32(ptr, SWAP_BYTES_32(value));
}
+ inline void WRITE_LE_UINT64(void *ptr, uint64 value) {
+ WRITE_UINT64(ptr, SWAP_BYTES_64(value));
+ }
# endif // if defined(SCUMM_NEED_ALIGNMENT)
diff --git a/common/stream.h b/common/stream.h
index 2702068cf3..5655f0fcb4 100644
--- a/common/stream.h
+++ b/common/stream.h
@@ -125,6 +125,11 @@ public:
write(&value, 4);
}
+ void writeUint64LE(uint64 value) {
+ value = TO_LE_64(value);
+ write(&value, 8);
+ }
+
void writeUint16BE(uint16 value) {
value = TO_BE_16(value);
write(&value, 2);
@@ -135,6 +140,11 @@ public:
write(&value, 4);
}
+ void writeUint64BE(uint64 value) {
+ value = TO_BE_64(value);
+ write(&value, 8);
+ }
+
FORCEINLINE void writeSint16LE(int16 value) {
writeUint16LE((uint16)value);
}
@@ -143,6 +153,10 @@ public:
writeUint32LE((uint32)value);
}
+ FORCEINLINE void writeSint64LE(int64 value) {
+ writeUint64LE((uint64)value);
+ }
+
FORCEINLINE void writeSint16BE(int16 value) {
writeUint16BE((uint16)value);
}
@@ -151,6 +165,10 @@ public:
writeUint32BE((uint32)value);
}
+ FORCEINLINE void writeSint64BE(int64 value) {
+ writeUint64BE((uint64)value);
+ }
+
/**
* Write the given string to the stream.
* This writes str.size() characters, but no terminating zero byte.
@@ -242,6 +260,19 @@ public:
}
/**
+ * Read an unsigned 64-bit word stored in little endian (LSB first) order
+ * from the stream and return it.
+ * Performs no error checking. The return value is undefined
+ * if a read error occurred (for which client code can check by
+ * calling err() and eos() ).
+ */
+ uint32 readUint64LE() {
+ uint64 val;
+ read(&val, 8);
+ return FROM_LE_64(val);
+ }
+
+ /**
* Read an unsigned 16-bit word stored in big endian (MSB first) order
* from the stream and return it.
* Performs no error checking. The return value is undefined
@@ -268,6 +299,19 @@ public:
}
/**
+ * Read an unsigned 64-bit word stored in big endian (MSB first) order
+ * from the stream and return it.
+ * Performs no error checking. The return value is undefined
+ * if a read error occurred (for which client code can check by
+ * calling err() and eos() ).
+ */
+ uint32 readUint64BE() {
+ uint64 val;
+ read(&val, 8);
+ return FROM_BE_64(val);
+ }
+
+ /**
* Read a signed 16-bit word stored in little endian (LSB first) order
* from the stream and return it.
* Performs no error checking. The return value is undefined
@@ -290,6 +334,17 @@ public:
}
/**
+ * Read a signed 64-bit word stored in little endian (LSB first) order
+ * from the stream and return it.
+ * Performs no error checking. The return value is undefined
+ * if a read error occurred (for which client code can check by
+ * calling err() and eos() ).
+ */
+ FORCEINLINE int64 readSint64LE() {
+ return (int64)readUint64LE();
+ }
+
+ /**
* Read a signed 16-bit word stored in big endian (MSB first) order
* from the stream and return it.
* Performs no error checking. The return value is undefined
@@ -312,6 +367,17 @@ public:
}
/**
+ * Read a signed 64-bit word stored in big endian (MSB first) order
+ * from the stream and return it.
+ * Performs no error checking. The return value is undefined
+ * if a read error occurred (for which client code can check by
+ * calling err() and eos() ).
+ */
+ FORCEINLINE int64 readSint64BE() {
+ return (int64)readUint64BE();
+ }
+
+ /**
* Read the specified amount of data into a malloc'ed buffer
* which then is wrapped into a MemoryReadStream.
* The returned stream might contain less data than requested,