aboutsummaryrefslogtreecommitdiff
path: root/externals/g3dlite/BinaryInput.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'externals/g3dlite/BinaryInput.cpp')
-rw-r--r--externals/g3dlite/BinaryInput.cpp568
1 files changed, 568 insertions, 0 deletions
diff --git a/externals/g3dlite/BinaryInput.cpp b/externals/g3dlite/BinaryInput.cpp
new file mode 100644
index 00000000000..65a9976fe04
--- /dev/null
+++ b/externals/g3dlite/BinaryInput.cpp
@@ -0,0 +1,568 @@
+/**
+ @file BinaryInput.cpp
+
+ @author Morgan McGuire, graphics3d.com
+ Copyright 2001-2007, Morgan McGuire. All rights reserved.
+
+ @created 2001-08-09
+ @edited 2005-02-24
+
+
+ <PRE>
+ {
+ BinaryOutput b("c:/tmp/test.b", BinaryOutput::LITTLE_ENDIAN);
+
+ float f = 3.1415926;
+ int i = 1027221;
+ std::string s = "Hello World!";
+
+ b.writeFloat32(f);
+ b.writeInt32(i);
+ b.writeString(s);
+ b.commit();
+
+
+ BinaryInput in("c:/tmp/test.b", BinaryInput::LITTLE_ENDIAN);
+
+ debugAssert(f == in.readFloat32());
+ int ii = in.readInt32();
+ debugAssert(i == ii);
+ debugAssert(s == in.readString());
+ }
+ </PRE>
+ */
+
+#include "G3D/platform.h"
+#include "G3D/BinaryInput.h"
+#include "G3D/Array.h"
+#include "G3D/fileutils.h"
+#include "G3D/Log.h"
+#include <zlib.h>
+
+#include <cstring>
+
+namespace G3D {
+
+void BinaryInput::readBool8(std::vector<bool>& out, int64 n) {
+ out.resize((int)n);
+ // std::vector optimizes bool in a way that prevents fast reading
+ for (int64 i = 0; i < n ; ++i) {
+ out[i] = readBool8();
+ }
+}
+
+
+void BinaryInput::readBool8(Array<bool>& out, int64 n) {
+ out.resize(n);
+ readBool8(out.begin(), n);
+}
+
+
+#define IMPLEMENT_READER(ucase, lcase)\
+void BinaryInput::read##ucase(std::vector<lcase>& out, int64 n) {\
+ out.resize(n);\
+ read##ucase(&out[0], n);\
+}\
+\
+\
+void BinaryInput::read##ucase(Array<lcase>& out, int64 n) {\
+ out.resize(n);\
+ read##ucase(out.begin(), n);\
+}
+
+
+IMPLEMENT_READER(UInt8, uint8)
+IMPLEMENT_READER(Int8, int8)
+IMPLEMENT_READER(UInt16, uint16)
+IMPLEMENT_READER(Int16, int16)
+IMPLEMENT_READER(UInt32, uint32)
+IMPLEMENT_READER(Int32, int32)
+IMPLEMENT_READER(UInt64, uint64)
+IMPLEMENT_READER(Int64, int64)
+IMPLEMENT_READER(Float32, float32)
+IMPLEMENT_READER(Float64, float64)
+
+#undef IMPLEMENT_READER
+
+// Data structures that are one byte per element can be
+// directly copied, regardles of endian-ness.
+#define IMPLEMENT_READER(ucase, lcase)\
+void BinaryInput::read##ucase(lcase* out, int64 n) {\
+ if (sizeof(lcase) == 1) {\
+ readBytes(out, n);\
+ } else {\
+ for (int64 i = 0; i < n ; ++i) {\
+ out[i] = read##ucase();\
+ }\
+ }\
+}
+
+IMPLEMENT_READER(Bool8, bool)
+IMPLEMENT_READER(UInt8, uint8)
+IMPLEMENT_READER(Int8, int8)
+
+#undef IMPLEMENT_READER
+
+
+#define IMPLEMENT_READER(ucase, lcase)\
+void BinaryInput::read##ucase(lcase* out, int64 n) {\
+ if (m_swapBytes) {\
+ for (int64 i = 0; i < n; ++i) {\
+ out[i] = read##ucase();\
+ }\
+ } else {\
+ readBytes(out, sizeof(lcase) * n);\
+ }\
+}
+
+
+IMPLEMENT_READER(UInt16, uint16)
+IMPLEMENT_READER(Int16, int16)
+IMPLEMENT_READER(UInt32, uint32)
+IMPLEMENT_READER(Int32, int32)
+IMPLEMENT_READER(UInt64, uint64)
+IMPLEMENT_READER(Int64, int64)
+IMPLEMENT_READER(Float32, float32)
+IMPLEMENT_READER(Float64, float64)
+
+#undef IMPLEMENT_READER
+
+void BinaryInput::loadIntoMemory(int64 startPosition, int64 minLength) {
+ // Load the next section of the file
+ debugAssertM(m_filename != "<memory>", "Read past end of file.");
+
+ int64 absPos = m_alreadyRead + m_pos;
+
+ if (m_bufferLength < minLength) {
+ // The current buffer isn't big enough to hold the chunk we want to read.
+ // This happens if there was little memory available during the initial constructor
+ // read but more memory has since been freed.
+ m_bufferLength = minLength;
+ debugAssert(m_freeBuffer);
+ m_buffer = (uint8*)System::realloc(m_buffer, m_bufferLength);
+ if (m_buffer == NULL) {
+ throw "Tried to read a larger memory chunk than could fit in memory. (2)";
+ }
+ }
+
+ m_alreadyRead = startPosition;
+
+# ifdef G3D_WIN32
+ FILE* file = fopen(m_filename.c_str(), "rb");
+ debugAssert(file);
+ int ret = fseek(file, (off_t)m_alreadyRead, SEEK_SET);
+ debugAssert(ret == 0);
+ size_t toRead = (size_t)G3D::min(m_bufferLength, m_length - m_alreadyRead);
+ ret = fread(m_buffer, 1, toRead, file);
+ debugAssert(ret == toRead);
+ fclose(file);
+ file = NULL;
+
+# else
+ FILE* file = fopen(m_filename.c_str(), "rb");
+ debugAssert(file);
+ int ret = fseeko(file, (off_t)m_alreadyRead, SEEK_SET);
+ debugAssert(ret == 0);
+ size_t toRead = (size_t)G3D::min<int64>(m_bufferLength, m_length - m_alreadyRead);
+ ret = fread(m_buffer, 1, toRead, file);
+ debugAssert((size_t)ret == (size_t)toRead);
+ fclose(file);
+ file = NULL;
+# endif
+
+ m_pos = absPos - m_alreadyRead;
+ debugAssert(m_pos >= 0);
+}
+
+
+
+const bool BinaryInput::NO_COPY = false;
+
+static bool needSwapBytes(G3DEndian fileEndian) {
+ return (fileEndian != System::machineEndian());
+}
+
+
+/** Helper used by the constructors for decompression */
+static uint32 readUInt32(const uint8* data, bool swapBytes) {
+ if (swapBytes) {
+ uint8 out[4];
+ out[0] = data[3];
+ out[1] = data[2];
+ out[2] = data[1];
+ out[3] = data[0];
+ return *((uint32*)out);
+ } else {
+ return *((uint32*)data);
+ }
+}
+
+
+void BinaryInput::setEndian(G3DEndian e) {
+ m_fileEndian = e;
+ m_swapBytes = needSwapBytes(m_fileEndian);
+}
+
+
+BinaryInput::BinaryInput(
+ const uint8* data,
+ int64 dataLen,
+ G3DEndian dataEndian,
+ bool compressed,
+ bool copyMemory) :
+ m_filename("<memory>"),
+ m_bitPos(0),
+ m_bitString(0),
+ m_beginEndBits(0),
+ m_alreadyRead(0),
+ m_bufferLength(0),
+ m_pos(0) {
+
+ m_freeBuffer = copyMemory || compressed;
+
+ setEndian(dataEndian);
+
+ if (compressed) {
+ // Read the decompressed size from the first 4 bytes
+ m_length = G3D::readUInt32(data, m_swapBytes);
+
+ debugAssert(m_freeBuffer);
+ m_buffer = (uint8*)System::alignedMalloc(m_length, 16);
+
+ unsigned long L = m_length;
+ // Decompress with zlib
+ int64 result = uncompress(m_buffer, (unsigned long*)&L, data + 4, dataLen - 4);
+ m_length = L;
+ m_bufferLength = L;
+ debugAssert(result == Z_OK); (void)result;
+
+ } else {
+ m_length = dataLen;
+ m_bufferLength = m_length;
+ if (! copyMemory) {
+ debugAssert(!m_freeBuffer);
+ m_buffer = const_cast<uint8*>(data);
+ } else {
+ debugAssert(m_freeBuffer);
+ m_buffer = (uint8*)System::alignedMalloc(m_length, 16);
+ System::memcpy(m_buffer, data, dataLen);
+ }
+ }
+}
+
+
+BinaryInput::BinaryInput(
+ const std::string& filename,
+ G3DEndian fileEndian,
+ bool compressed) :
+ m_filename(filename),
+ m_bitPos(0),
+ m_bitString(0),
+ m_beginEndBits(0),
+ m_alreadyRead(0),
+ m_length(0),
+ m_bufferLength(0),
+ m_buffer(NULL),
+ m_pos(0),
+ m_freeBuffer(true) {
+
+ setEndian(fileEndian);
+
+ // Update global file tracker
+ _internal::currentFilesUsed.insert(m_filename);
+
+
+ if (! fileExists(m_filename, false)) {
+ std::string zipfile;
+ std::string internalfile;
+ if (zipfileExists(m_filename, zipfile, internalfile)) {
+ // Load from zipfile
+ void* v;
+ size_t s;
+ zipRead(filename, v, s);
+ m_buffer = reinterpret_cast<uint8*>(v);
+ m_bufferLength = m_length = s;
+ if (compressed) {
+ decompress();
+ }
+ m_freeBuffer = true;
+ } else {
+ Log::common()->printf("Warning: File not found: %s\n", m_filename.c_str());
+ }
+ return;
+ }
+
+ // Figure out how big the file is and verify that it exists.
+ m_length = fileLength(m_filename);
+
+ // Read the file into memory
+ FILE* file = fopen(m_filename.c_str(), "rb");
+
+ if (! file || (m_length == -1)) {
+ throw format("File not found: \"%s\"", m_filename.c_str());
+ return;
+ }
+
+ if (! compressed && (m_length > INITIAL_BUFFER_LENGTH)) {
+ // Read only a subset of the file so we don't consume
+ // all available memory.
+ m_bufferLength = INITIAL_BUFFER_LENGTH;
+ } else {
+ // Either the length is fine or the file is compressed
+ // and requires us to read the whole thing for zlib.
+ m_bufferLength = m_length;
+ }
+
+ debugAssert(m_freeBuffer);
+ m_buffer = (uint8*)System::alignedMalloc(m_bufferLength, 16);
+ if (m_buffer == NULL) {
+ if (compressed) {
+ throw "Not enough memory to load compressed file. (1)";
+ }
+
+ // Try to allocate a small array; not much memory is available.
+ // Give up if we can't allocate even 1k.
+ while ((m_buffer == NULL) && (m_bufferLength > 1024)) {
+ m_bufferLength /= 2;
+ m_buffer = (uint8*)System::alignedMalloc(m_bufferLength, 16);
+ }
+ }
+ debugAssert(m_buffer);
+
+ fread(m_buffer, m_bufferLength, sizeof(int8), file);
+ fclose(file);
+ file = NULL;
+
+ if (compressed) {
+ if (m_bufferLength != m_length) {
+ throw "Not enough memory to load compressed file. (2)";
+ }
+
+ decompress();
+ }
+}
+
+void BinaryInput::decompress() {
+ // Decompress
+ // Use the existing buffer as the source, allocate
+ // a new buffer to use as the destination.
+
+ int64 tempLength = m_length;
+ m_length = G3D::readUInt32(m_buffer, m_swapBytes);
+
+ // The file couldn't have better than 500:1 compression
+ alwaysAssertM(m_length < m_bufferLength * 500, "Compressed file header is corrupted");
+
+ uint8* tempBuffer = m_buffer;
+ m_buffer = (uint8*)System::alignedMalloc(m_length, 16);
+
+ debugAssert(m_buffer);
+ debugAssert(isValidHeapPointer(tempBuffer));
+ debugAssert(isValidHeapPointer(m_buffer));
+
+ unsigned long L = m_length;
+ int64 result = uncompress(m_buffer, &L, tempBuffer + 4, tempLength - 4);
+ m_length = L;
+ m_bufferLength = m_length;
+
+ debugAssertM(result == Z_OK, "BinaryInput/zlib detected corruption in " + m_filename);
+ (void)result;
+
+ System::alignedFree(tempBuffer);
+}
+
+
+void BinaryInput::readBytes(void* bytes, int64 n) {
+ prepareToRead(n);
+ debugAssert(isValidPointer(bytes));
+
+ memcpy(bytes, m_buffer + m_pos, n);
+ m_pos += n;
+}
+
+
+BinaryInput::~BinaryInput() {
+
+ if (m_freeBuffer) {
+ System::alignedFree(m_buffer);
+ }
+ m_buffer = NULL;
+}
+
+
+uint64 BinaryInput::readUInt64() {
+ prepareToRead(8);
+ uint8 out[8];
+
+ if (m_swapBytes) {
+ out[0] = m_buffer[m_pos + 7];
+ out[1] = m_buffer[m_pos + 6];
+ out[2] = m_buffer[m_pos + 5];
+ out[3] = m_buffer[m_pos + 4];
+ out[4] = m_buffer[m_pos + 3];
+ out[5] = m_buffer[m_pos + 2];
+ out[6] = m_buffer[m_pos + 1];
+ out[7] = m_buffer[m_pos + 0];
+ } else {
+ *(uint64*)out = *(uint64*)(m_buffer + m_pos);
+ }
+
+ m_pos += 8;
+ return *(uint64*)out;
+}
+
+
+std::string BinaryInput::readString(int64 n) {
+ prepareToRead(n);
+ debugAssertM((m_pos + n) <= m_length, "Read past end of file");
+
+ char *s = (char*)System::alignedMalloc(n + 1, 16);
+ assert(s != NULL);
+
+ memcpy(s, m_buffer + m_pos, n);
+ // There may not be a null, so make sure
+ // we add one.
+ s[n] = '\0';
+
+ std::string out = s;
+ System::alignedFree(s);
+ s = NULL;
+
+ m_pos += n;
+
+ return out;
+
+}
+
+
+std::string BinaryInput::readString() {
+ int64 n = 0;
+
+ if ((m_pos + m_alreadyRead + n) < (m_length - 1)) {
+ prepareToRead(1);
+ }
+
+ if ( ((m_pos + m_alreadyRead + n) < (m_length - 1)) &&
+ (m_buffer[m_pos + n] != '\0')) {
+
+ ++n;
+ while ( ((m_pos + m_alreadyRead + n) < (m_length - 1)) &&
+ (m_buffer[m_pos + n] != '\0')) {
+
+ prepareToRead(1);
+ ++n;
+ }
+ }
+
+ // Consume NULL
+ ++n;
+
+ return readString(n);
+}
+
+
+std::string BinaryInput::readStringEven() {
+ std::string x = readString();
+ if (hasMore() && (G3D::isOdd(x.length() + 1))) {
+ skip(1);
+ }
+ return x;
+}
+
+
+std::string BinaryInput::readString32() {
+ int len = readUInt32();
+ return readString(len);
+}
+
+
+Vector4 BinaryInput::readVector4() {
+ float x = readFloat32();
+ float y = readFloat32();
+ float z = readFloat32();
+ float w = readFloat32();
+ return Vector4(x, y, z, w);
+}
+
+
+Vector3 BinaryInput::readVector3() {
+ float x = readFloat32();
+ float y = readFloat32();
+ float z = readFloat32();
+ return Vector3(x, y, z);
+}
+
+
+Vector2 BinaryInput::readVector2() {
+ float x = readFloat32();
+ float y = readFloat32();
+ return Vector2(x, y);
+}
+
+
+Color4 BinaryInput::readColor4() {
+ float r = readFloat32();
+ float g = readFloat32();
+ float b = readFloat32();
+ float a = readFloat32();
+ return Color4(r, g, b, a);
+}
+
+
+Color3 BinaryInput::readColor3() {
+ float r = readFloat32();
+ float g = readFloat32();
+ float b = readFloat32();
+ return Color3(r, g, b);
+}
+
+
+void BinaryInput::beginBits() {
+ debugAssert(m_beginEndBits == 0);
+ m_beginEndBits = 1;
+ m_bitPos = 0;
+
+ debugAssertM(hasMore(), "Can't call beginBits when at the end of a file");
+ m_bitString = readUInt8();
+}
+
+
+uint32 BinaryInput::readBits(int numBits) {
+ debugAssert(m_beginEndBits == 1);
+
+ uint32 out = 0;
+
+ const int total = numBits;
+ while (numBits > 0) {
+ if (m_bitPos > 7) {
+ // Consume a new byte for reading. We do this at the beginning
+ // of the loop so that we don't try to read past the end of the file.
+ m_bitPos = 0;
+ m_bitString = readUInt8();
+ }
+
+ // Slide the lowest bit of the bitString into
+ // the correct position.
+ out |= (m_bitString & 1) << (total - numBits);
+
+ // Shift over to the next bit
+ m_bitString = m_bitString >> 1;
+ ++m_bitPos;
+ --numBits;
+ }
+
+ return out;
+}
+
+
+void BinaryInput::endBits() {
+ debugAssert(m_beginEndBits == 1);
+ if (m_bitPos == 0) {
+ // Put back the last byte we read
+ --m_pos;
+ }
+ m_beginEndBits = 0;
+ m_bitPos = 0;
+}
+
+}