summaryrefslogtreecommitdiff
path: root/plugins/adplug/libbinio/binio.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/adplug/libbinio/binio.cpp')
-rw-r--r--plugins/adplug/libbinio/binio.cpp640
1 files changed, 640 insertions, 0 deletions
diff --git a/plugins/adplug/libbinio/binio.cpp b/plugins/adplug/libbinio/binio.cpp
new file mode 100644
index 00000000..75489788
--- /dev/null
+++ b/plugins/adplug/libbinio/binio.cpp
@@ -0,0 +1,640 @@
+/*
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * binio.cpp - Binary stream I/O classes
+ * Copyright (C) 2002, 2003 Simon Peter <dn.tlp@gmx.net>
+ */
+
+#include <string.h>
+
+#include "binio.h"
+
+#if BINIO_WITH_MATH
+
+#include <math.h>
+
+#ifdef __QNXNTO__
+ #define pow std::powf
+#endif // __QNXNTO__
+
+// If 'math.h' doesn't define HUGE_VAL, we try to use HUGE instead.
+#ifndef HUGE_VAL
+# define HUGE_VAL HUGE
+#endif
+
+#endif
+
+/***** Defines *****/
+
+#if BINIO_ENABLE_STRING
+// String buffer size for std::string readString() method
+#define STRINGBUFSIZE 256
+#endif
+
+/***** binio *****/
+
+const binio::Flags binio::system_flags = binio::detect_system_flags();
+
+const binio::Flags binio::detect_system_flags()
+{
+ Flags f = 0;
+
+ // Endian test
+ union {
+ int word;
+ Byte byte;
+ } endian_test;
+
+ endian_test.word = 1;
+ if(endian_test.byte != 1) f |= BigEndian;
+
+ // IEEE-754 floating-point test
+ float fl = 6.5;
+ Byte *dat = (Byte *)&fl;
+
+ if(sizeof(float) == 4 && sizeof(double) == 8)
+ if(f & BigEndian) {
+ if(dat[0] == 0x40 && dat[1] == 0xD0 && !dat[2] && !dat[3])
+ f |= FloatIEEE;
+ } else
+ if(dat[3] == 0x40 && dat[2] == 0xD0 && !dat[1] && !dat[0])
+ f |= FloatIEEE;
+
+ return f;
+}
+
+binio::binio()
+ : my_flags(system_flags), err(NoError)
+{
+}
+
+binio::~binio()
+{
+}
+
+void binio::setFlag(Flag f, bool set)
+{
+ if(set)
+ my_flags |= f;
+ else
+ my_flags &= !f;
+}
+
+bool binio::getFlag(Flag f)
+{
+ return (my_flags & f ? true : false);
+}
+
+binio::Error binio::error()
+{
+ Error e = err;
+
+ err = NoError;
+ return e;
+}
+
+bool binio::eof()
+{
+ return (err & Eof ? true : false);
+}
+
+/***** binistream *****/
+
+binistream::binistream()
+{
+}
+
+binistream::~binistream()
+{
+}
+
+binistream::Int binistream::readInt(unsigned int size)
+{
+ unsigned int i;
+ Int val = 0, in;
+
+ // Check if 'size' doesn't exceed our system's biggest type.
+ if(size > sizeof(Int)) {
+ err |= Unsupported;
+ return 0;
+ }
+
+ for(i = 0; i < size; i++) {
+ in = getByte();
+ if(getFlag(BigEndian))
+ val <<= 8;
+ else
+ in <<= i * 8;
+ val |= in;
+ }
+
+ return val;
+}
+
+binistream::Float binistream::readFloat(FType ft)
+{
+ if(getFlag(FloatIEEE)) { // Read IEEE-754 floating-point value
+ unsigned int i, size;
+ Byte in[8];
+ bool swap;
+
+ // Determine appropriate size for given type.
+ switch(ft) {
+ case Single: size = 4; break; // 32 bits
+ case Double: size = 8; break; // 64 bits
+ }
+
+ // Determine byte ordering, depending on what we do next
+ if(system_flags & FloatIEEE)
+ swap = getFlag(BigEndian) ^ (system_flags & BigEndian);
+ else
+ swap = !getFlag(BigEndian);
+
+ // Read the float byte by byte, converting endianess
+ for(i = 0; i < size; i++)
+ if(swap)
+ in[size - i - 1] = getByte();
+ else
+ in[i] = getByte();
+
+ if(system_flags & FloatIEEE) {
+ // Compatible system, let the hardware do the conversion
+ switch(ft) {
+ case Single: return *(float *)in;
+ case Double: return *(double *)in;
+ }
+ } else { // Incompatible system, convert manually
+ switch(ft) {
+ case Single: return ieee_single2float(in);
+ case Double: return ieee_double2float(in);
+ }
+ }
+ }
+
+ // User tried to read a (yet) unsupported floating-point type. Bail out.
+ err |= Unsupported; return 0.0;
+}
+
+binistream::Float binistream::ieee_single2float(Byte *data)
+{
+ signed int sign = data[0] >> 7 ? -1 : 1;
+ unsigned int exp = ((data[0] << 1) & 0xff) | ((data[1] >> 7) & 1),
+ fracthi7 = data[1] & 0x7f;
+ Float fract = fracthi7 * 65536.0 + data[2] * 256.0 + data[3];
+
+ // Signed and unsigned zero
+ if(!exp && !fracthi7 && !data[2] && !data[3]) return sign * 0.0;
+
+ // Signed and unsigned infinity (maybe unsupported on non-IEEE systems)
+ if(exp == 255)
+ if(!fracthi7 && !data[2] && !data[3]) {
+#ifdef HUGE_VAL
+ if(sign == -1) return -HUGE_VAL; else return HUGE_VAL;
+#else
+ err |= Unsupported;
+ if(sign == -1) return -1.0; else return 1.0;
+#endif
+ } else { // Not a number (maybe unsupported on non-IEEE systems)
+#ifdef NAN
+ return NAN;
+#else
+ err |= Unsupported; return 0.0;
+#endif
+ }
+
+ if(!exp) // Unnormalized float values
+ return sign * pow(2, -126) * fract * pow(2, -23);
+ else // Normalized float values
+ return sign * pow(2, exp - 127) * (fract * pow(2, -23) + 1);
+
+ err |= Fatal; return 0.0;
+}
+
+binistream::Float binistream::ieee_double2float(Byte *data)
+{
+ signed int sign = data[0] >> 7 ? -1 : 1;
+ unsigned int exp = ((unsigned int)(data[0] & 0x7f) << 4) | (data[1] >> 4),
+ fracthi4 = data[1] & 0xf;
+ Float fract = fracthi4 * pow(2, 48) + data[2] * pow(2, 40) + data[3] *
+ pow(2, 32) + data[4] * pow(2, 24) + data[5] * pow(2, 16) + data[6] *
+ pow(2, 8) + data[7];
+
+ // Signed and unsigned zero
+ if(!exp && !fracthi4 && !data[2] && !data[3] && !data[4] && !data[5] &&
+ !data[6] && !data[7]) return sign * 0.0;
+
+ // Signed and unsigned infinity (maybe unsupported on non-IEEE systems)
+ if(exp == 2047)
+ if(!fracthi4 && !data[2] && !data[3] && !data[4] && !data[5] && !data[6] &&
+ !data[7]) {
+#ifdef HUGE_VAL
+ if(sign == -1) return -HUGE_VAL; else return HUGE_VAL;
+#else
+ err |= Unsupported;
+ if(sign == -1) return -1.0; else return 1.0;
+#endif
+ } else { // Not a number (maybe unsupported on non-IEEE systems)
+#ifdef NAN
+ return NAN;
+#else
+ err |= Unsupported; return 0.0;
+#endif
+ }
+
+ if(!exp) // Unnormalized float values
+ return sign * pow(2, -1022) * fract * pow(2, -52);
+ else // Normalized float values
+ return sign * pow(2, exp - 1023) * (fract * pow(2, -52) + 1);
+
+ err |= Fatal; return 0.0;
+}
+
+#if !BINIO_WITH_MATH
+binio::Float binio::pow(Float base, signed int exp)
+/* Our own, stripped-down version of pow() for not having to depend on 'math.h'.
+ * This one handles float values for the base and an integer exponent, both
+ * positive and negative.
+ */
+{
+ int i;
+ Float val = base;
+
+ if(!exp) return 1.0;
+
+ for(i = 1; i < (exp < 0 ? -exp : exp); i++)
+ val *= base;
+
+ if(exp < 0) val = 1.0 / val;
+
+ return val;
+}
+#endif
+
+unsigned long binistream::readString(char *str, unsigned long maxlen)
+{
+ unsigned long i;
+
+ for(i = 0; i < maxlen; i++) {
+ str[i] = (char)getByte();
+ if(err) { str[i] = '\0'; return i; }
+ }
+
+ return maxlen;
+}
+
+unsigned long binistream::readString(char *str, unsigned long maxlen,
+ const char delim)
+{
+ unsigned long i;
+
+ for(i = 0; i < maxlen; i++) {
+ str[i] = (char)getByte();
+ if(str[i] == delim || err) { str[i] = '\0'; return i; }
+ }
+
+ str[maxlen] = '\0';
+ return maxlen;
+}
+
+#if BINIO_ENABLE_STRING
+std::string binistream::readString(const char delim)
+{
+ char buf[STRINGBUFSIZE + 1];
+ std::string tempstr;
+ unsigned long read;
+
+ do {
+ read = readString(buf, STRINGBUFSIZE, delim);
+ tempstr.append(buf, read);
+ } while(read == STRINGBUFSIZE);
+
+ return tempstr;
+}
+#endif
+
+binistream::Int binistream::peekInt(unsigned int size)
+{
+ Int val = readInt(size);
+ if(!err) seek(-(long)size, Add);
+ return val;
+}
+
+binistream::Float binistream::peekFloat(FType ft)
+{
+ Float val = readFloat(ft);
+
+ if(!err)
+ switch(ft) {
+ case Single: seek(-4, Add); break;
+ case Double: seek(-8, Add); break;
+ }
+
+ return val;
+}
+
+bool binistream::ateof()
+{
+ Error olderr = err; // Save current error state
+ bool eof_then;
+
+ peekInt(1);
+ eof_then = eof(); // Get error state of next byte
+ err = olderr; // Restore original error state
+ return eof_then;
+}
+
+void binistream::ignore(unsigned long amount)
+{
+ unsigned long i;
+
+ for(i = 0; i < amount; i++)
+ getByte();
+}
+
+/***** binostream *****/
+
+binostream::binostream()
+{
+}
+
+binostream::~binostream()
+{
+}
+
+void binostream::writeInt(Int val, unsigned int size)
+{
+ unsigned int i;
+
+ // Check if 'size' doesn't exceed our system's biggest type.
+ if(size > sizeof(Int)) { err |= Unsupported; return; }
+
+ for(i = 0; i < size; i++) {
+ if(getFlag(BigEndian))
+ putByte((val >> (size - i - 1) * 8) & 0xff);
+ else {
+ putByte(val & 0xff);
+ val >>= 8;
+ }
+ }
+}
+
+void binostream::writeFloat(Float f, FType ft)
+{
+ if(getFlag(FloatIEEE)) { // Write IEEE-754 floating-point value
+ unsigned int i, size;
+ Byte *out;
+ bool swap;
+
+ if(system_flags & FloatIEEE) {
+ // compatible system, let the hardware do the conversion
+ float outf = f;
+ double outd = f;
+
+ // Hardware could be big or little endian, convert appropriately
+ swap = getFlag(BigEndian) ^ (system_flags & BigEndian);
+
+ // Determine appropriate size for given type and convert by hardware
+ switch(ft) {
+ case Single: size = 4; out = (Byte *)&outf; break; // 32 bits
+ case Double: size = 8; out = (Byte *)&outd; break; // 64 bits
+ }
+ } else {
+#if BINIO_WITH_MATH
+ // incompatible system, do the conversion manually
+ Byte buf[8];
+
+ // Our own value is always big endian, just check whether we have to
+ // convert for a different stream format.
+ swap = !getFlag(BigEndian);
+
+ // Convert system's float to requested IEEE-754 float
+ switch(ft) {
+ case Single: size = 4; float2ieee_single(f, buf); break;
+ case Double: size = 8; float2ieee_double(f, buf); break;
+ }
+
+ out = buf; // Make the value ready for writing
+#else
+ // No necessary support routines to do the conversion, bail out!
+ err |= Unsupported; return;
+#endif
+ }
+
+ // Write the float byte by byte, converting endianess
+ if(swap) out += size - 1;
+ for(i = 0; i < size; i++) {
+ putByte(*out);
+ if(swap) out--; else out++;
+ }
+
+ return; // We're done.
+ }
+
+ // User tried to write an unsupported floating-point type. Bail out.
+ err |= Unsupported;
+}
+
+#ifdef BINIO_WITH_MATH
+
+/*
+ * Single and double floating-point to IEEE-754 equivalent conversion functions
+ * courtesy of Ken Turkowski.
+ *
+ * Copyright (C) 1989-1991 Ken Turkowski. <turk@computer.org>
+ *
+ * All rights reserved.
+ *
+ * Warranty Information
+ * Even though I have reviewed this software, I make no warranty
+ * or representation, either express or implied, with respect to this
+ * software, its quality, accuracy, merchantability, or fitness for a
+ * particular purpose. As a result, this software is provided "as is,"
+ * and you, its user, are assuming the entire risk as to its quality
+ * and accuracy.
+ *
+ * This code may be used and freely distributed as long as it includes
+ * this copyright notice and the above warranty information.
+ */
+
+/****************************************************************
+ * The following two routines make up for deficiencies in many
+ * compilers to convert properly between unsigned integers and
+ * floating-point. Some compilers which have this bug are the
+ * THINK_C compiler for the Macintosh and the C compiler for the
+ * Silicon Graphics MIPS-based Iris.
+ ****************************************************************/
+
+#ifdef applec /* The Apple C compiler works */
+# define FloatToUnsigned(f) ((unsigned long)(f))
+#else
+# define FloatToUnsigned(f) ((unsigned long)(((long)((f) - 2147483648.0)) + 2147483647L + 1))
+#endif
+
+#define SEXP_MAX 255
+#define SEXP_OFFSET 127
+#define SEXP_SIZE 8
+#define SEXP_POSITION (32-SEXP_SIZE-1)
+
+void binostream::float2ieee_single(Float num, Byte *bytes)
+{
+ long sign;
+ register long bits;
+
+ if (num < 0) { /* Can't distinguish a negative zero */
+ sign = 0x80000000;
+ num *= -1;
+ } else {
+ sign = 0;
+ }
+
+ if (num == 0) {
+ bits = 0;
+ } else {
+ Float fMant;
+ int expon;
+
+ fMant = frexp(num, &expon);
+
+ if ((expon > (SEXP_MAX-SEXP_OFFSET+1)) || !(fMant < 1)) {
+ /* NaN's and infinities fail second test */
+ bits = sign | 0x7F800000; /* +/- infinity */
+ }
+
+ else {
+ long mantissa;
+
+ if (expon < -(SEXP_OFFSET-2)) { /* Smaller than normalized */
+ int shift = (SEXP_POSITION+1) + (SEXP_OFFSET-2) + expon;
+ if (shift < 0) { /* Way too small: flush to zero */
+ bits = sign;
+ }
+ else { /* Nonzero denormalized number */
+ mantissa = (long)(fMant * (1L << shift));
+ bits = sign | mantissa;
+ }
+ }
+
+ else { /* Normalized number */
+ mantissa = (long)floor(fMant * (1L << (SEXP_POSITION+1)));
+ mantissa -= (1L << SEXP_POSITION); /* Hide MSB */
+ bits = sign | ((long)((expon + SEXP_OFFSET - 1)) << SEXP_POSITION) | mantissa;
+ }
+ }
+ }
+
+ bytes[0] = bits >> 24; /* Copy to byte string */
+ bytes[1] = bits >> 16;
+ bytes[2] = bits >> 8;
+ bytes[3] = bits;
+}
+
+#define DEXP_MAX 2047
+#define DEXP_OFFSET 1023
+#define DEXP_SIZE 11
+#define DEXP_POSITION (32-DEXP_SIZE-1)
+
+void binostream::float2ieee_double(Float num, Byte *bytes)
+{
+ long sign;
+ long first, second;
+
+ if (num < 0) { /* Can't distinguish a negative zero */
+ sign = 0x80000000;
+ num *= -1;
+ } else {
+ sign = 0;
+ }
+
+ if (num == 0) {
+ first = 0;
+ second = 0;
+ } else {
+ Float fMant, fsMant;
+ int expon;
+
+ fMant = frexp(num, &expon);
+
+ if ((expon > (DEXP_MAX-DEXP_OFFSET+1)) || !(fMant < 1)) {
+ /* NaN's and infinities fail second test */
+ first = sign | 0x7FF00000; /* +/- infinity */
+ second = 0;
+ }
+
+ else {
+ long mantissa;
+
+ if (expon < -(DEXP_OFFSET-2)) { /* Smaller than normalized */
+ int shift = (DEXP_POSITION+1) + (DEXP_OFFSET-2) + expon;
+ if (shift < 0) { /* Too small for something in the MS word */
+ first = sign;
+ shift += 32;
+ if (shift < 0) { /* Way too small: flush to zero */
+ second = 0;
+ }
+ else { /* Pretty small demorn */
+ second = FloatToUnsigned(floor(ldexp(fMant, shift)));
+ }
+ }
+ else { /* Nonzero denormalized number */
+ fsMant = ldexp(fMant, shift);
+ mantissa = (long)floor(fsMant);
+ first = sign | mantissa;
+ second = FloatToUnsigned(floor(ldexp(fsMant - mantissa, 32)));
+ }
+ }
+
+ else { /* Normalized number */
+ fsMant = ldexp(fMant, DEXP_POSITION+1);
+ mantissa = (long)floor(fsMant);
+ mantissa -= (1L << DEXP_POSITION); /* Hide MSB */
+ fsMant -= (1L << DEXP_POSITION);
+ first = sign | ((long)((expon + DEXP_OFFSET - 1)) << DEXP_POSITION) | mantissa;
+ second = FloatToUnsigned(floor(ldexp(fsMant - mantissa, 32)));
+ }
+ }
+ }
+
+ bytes[0] = first >> 24;
+ bytes[1] = first >> 16;
+ bytes[2] = first >> 8;
+ bytes[3] = first;
+ bytes[4] = second >> 24;
+ bytes[5] = second >> 16;
+ bytes[6] = second >> 8;
+ bytes[7] = second;
+}
+
+#endif // BINIO_WITH_MATH
+
+unsigned long binostream::writeString(const char *str, unsigned long amount)
+{
+ unsigned int i;
+
+ if(!amount) amount = strlen(str);
+
+ for(i = 0; i < amount; i++) {
+ putByte(str[i]);
+ if(err) return i;
+ }
+
+ return amount;
+}
+
+#if BINIO_ENABLE_STRING
+unsigned long binostream::writeString(const std::string &str)
+{
+ return writeString(str.c_str());
+}
+#endif