Преглед изворни кода

Merge pull request #605 from dgough/next

Fixed DebugMem and added Stream interface
Sean Paul Taylor пре 13 година
родитељ
комит
f73e7013e1

+ 1 - 0
gameplay/gameplay.vcxproj

@@ -573,6 +573,7 @@
     <ClInclude Include="src\ScriptTarget.h" />
     <ClInclude Include="src\Slider.h" />
     <ClInclude Include="src\SpriteBatch.h" />
+    <ClInclude Include="src\Stream.h" />
     <ClInclude Include="src\Technique.h" />
     <ClInclude Include="src\TextBox.h" />
     <ClInclude Include="src\Texture.h" />

+ 3 - 0
gameplay/gameplay.vcxproj.filters

@@ -1622,6 +1622,9 @@
     <ClInclude Include="src\lua\lua_LoggerLevel.h">
       <Filter>lua</Filter>
     </ClInclude>
+    <ClInclude Include="src\Stream.h">
+      <Filter>src</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <None Include="src\Game.inl">

+ 7 - 1
gameplay/gameplay.xcodeproj/project.pbxproj

@@ -1701,6 +1701,8 @@
 		5BD52674150F8258004C9099 /* PhysicsCollisionObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5BD5266D150F8257004C9099 /* PhysicsCollisionObject.cpp */; };
 		5BD52675150F8258004C9099 /* PhysicsCollisionObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BD5266E150F8258004C9099 /* PhysicsCollisionObject.h */; };
 		5BD52676150F8258004C9099 /* PhysicsCollisionObject.h in Headers */ = {isa = PBXBuildFile; fileRef = 5BD5266E150F8258004C9099 /* PhysicsCollisionObject.h */; };
+		9FC6EE731665304F00F39955 /* Stream.h in Headers */ = {isa = PBXBuildFile; fileRef = 9FC6EE721665304F00F39955 /* Stream.h */; };
+		9FC6EE741665304F00F39955 /* Stream.h in Headers */ = {isa = PBXBuildFile; fileRef = 9FC6EE721665304F00F39955 /* Stream.h */; };
 		B67EC8EB161DFC8E000B4D12 /* lua_Logger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B67EC8E7161DFC8E000B4D12 /* lua_Logger.cpp */; };
 		B67EC8EC161DFC8E000B4D12 /* lua_Logger.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B67EC8E7161DFC8E000B4D12 /* lua_Logger.cpp */; };
 		B67EC8ED161DFC8E000B4D12 /* lua_Logger.h in Headers */ = {isa = PBXBuildFile; fileRef = B67EC8E8161DFC8E000B4D12 /* lua_Logger.h */; };
@@ -1715,7 +1717,7 @@
 		B67EC8F9161DFCA8000B4D12 /* Logger.h in Headers */ = {isa = PBXBuildFile; fileRef = B67EC8F5161DFCA8000B4D12 /* Logger.h */; };
 		F1616ABC1614E24B008DD8B7 /* MathUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F1616ABB1614E24B008DD8B7 /* MathUtil.cpp */; };
 		F1616ABD1614E24B008DD8B7 /* MathUtil.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F1616ABB1614E24B008DD8B7 /* MathUtil.cpp */; };
-		F18024A51627000D001BFF87 /* gameplay-ios.mm in Sources */ = {isa = PBXBuildFile; fileRef = F18024A31627000D001BFF87 /* gameplay-main-ios.mm */; };
+		F18024A51627000D001BFF87 /* gameplay-main-ios.mm in Sources */ = {isa = PBXBuildFile; fileRef = F18024A31627000D001BFF87 /* gameplay-main-ios.mm */; };
 		F18024A61627000D001BFF87 /* gameplay-main-ios.mm in Sources */ = {isa = PBXBuildFile; fileRef = F18024A31627000D001BFF87 /* gameplay-main-ios.mm */; };
 		F18024A71627000D001BFF87 /* gameplay-main-macosx.mm in Sources */ = {isa = PBXBuildFile; fileRef = F18024A41627000D001BFF87 /* gameplay-main-macosx.mm */; };
 		F18024A81627000D001BFF87 /* gameplay-main-macosx.mm in Sources */ = {isa = PBXBuildFile; fileRef = F18024A41627000D001BFF87 /* gameplay-main-macosx.mm */; };
@@ -2610,6 +2612,7 @@
 		5BD5266C150F8257004C9099 /* PhysicsCharacter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PhysicsCharacter.h; path = src/PhysicsCharacter.h; sourceTree = SOURCE_ROOT; };
 		5BD5266D150F8257004C9099 /* PhysicsCollisionObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PhysicsCollisionObject.cpp; path = src/PhysicsCollisionObject.cpp; sourceTree = SOURCE_ROOT; };
 		5BD5266E150F8258004C9099 /* PhysicsCollisionObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PhysicsCollisionObject.h; path = src/PhysicsCollisionObject.h; sourceTree = SOURCE_ROOT; };
+		9FC6EE721665304F00F39955 /* Stream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Stream.h; path = src/Stream.h; sourceTree = SOURCE_ROOT; };
 		B67EC8E7161DFC8E000B4D12 /* lua_Logger.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lua_Logger.cpp; sourceTree = "<group>"; };
 		B67EC8E8161DFC8E000B4D12 /* lua_Logger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lua_Logger.h; sourceTree = "<group>"; };
 		B67EC8E9161DFC8E000B4D12 /* lua_LoggerLevel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lua_LoggerLevel.cpp; sourceTree = "<group>"; };
@@ -2889,6 +2892,7 @@
 				42CD0E2F147D8FF50000361E /* SpriteBatch.cpp */,
 				42CD0E30147D8FF50000361E /* SpriteBatch.h */,
 				42CD0E31147D8FF50000361E /* Technique.cpp */,
+				9FC6EE721665304F00F39955 /* Stream.h */,
 				42CD0E32147D8FF50000361E /* Technique.h */,
 				42CD0E33147D8FF50000361E /* Texture.cpp */,
 				42CD0E34147D8FF50000361E /* Texture.h */,
@@ -4083,6 +4087,7 @@
 				B67EC8ED161DFC8E000B4D12 /* lua_Logger.h in Headers */,
 				B67EC8F1161DFC8E000B4D12 /* lua_LoggerLevel.h in Headers */,
 				B67EC8F8161DFCA8000B4D12 /* Logger.h in Headers */,
+				9FC6EE731665304F00F39955 /* Stream.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -4513,6 +4518,7 @@
 				B67EC8EE161DFC8E000B4D12 /* lua_Logger.h in Headers */,
 				B67EC8F2161DFC8E000B4D12 /* lua_LoggerLevel.h in Headers */,
 				B67EC8F9161DFCA8000B4D12 /* Logger.h in Headers */,
+				9FC6EE741665304F00F39955 /* Stream.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

+ 1 - 4
gameplay/res/shaders/lib/lighting-point.vert

@@ -34,14 +34,11 @@ void applyLight(vec4 position)
     // Compute the light direction.
     vec3 lightDirection = u_pointLightPosition - positionWorldViewSpace.xyz;
    
-    vec4 vertexToPointLightDirection;
-    vertexToPointLightDirection.xyz = lightDirection;
-   
     // Attenuation
     v_pointLightAttenuation = 1.0 - dot(lightDirection * u_pointLightRangeInverse, lightDirection * u_pointLightRangeInverse);
 
     // Output light direction.
-    v_vertexToPointLightDirection =  vertexToPointLightDirection;
+    v_vertexToPointLightDirection =  lightDirection;
    
     #if defined (SPECULAR)
    

+ 81 - 55
gameplay/src/AudioBuffer.cpp

@@ -8,6 +8,36 @@ namespace gameplay
 // Audio buffer cache
 static std::vector<AudioBuffer*> __buffers;
 
+// Callbacks for loading an ogg file using Stream
+static size_t readStream(void *ptr, size_t size, size_t nmemb, void *datasource)
+{
+    GP_ASSERT(datasource);
+    Stream* stream = reinterpret_cast<Stream*>(datasource);
+    return stream->read(ptr, size, nmemb);
+}
+
+static int seekStream(void *datasource, ogg_int64_t offset, int whence)
+{
+    GP_ASSERT(datasource);
+    Stream* stream = reinterpret_cast<Stream*>(datasource);
+    return !stream->seek(offset, whence);
+}
+
+static int closeStream(void *datasource)
+{
+    GP_ASSERT(datasource);
+    Stream* stream = reinterpret_cast<Stream*>(datasource);
+    stream->close();
+    return 0;
+}
+
+static long tellStream(void *datasource)
+{
+    GP_ASSERT(datasource);
+    Stream* stream = reinterpret_cast<Stream*>(datasource);
+    return stream->position();
+}
+
 AudioBuffer::AudioBuffer(const char* path, ALuint buffer)
     : _filePath(path), _alBuffer(buffer)
 {
@@ -63,8 +93,8 @@ AudioBuffer* AudioBuffer::create(const char* path)
     }
     
     // Load sound file.
-    FILE* file = FileSystem::openFile(path, "rb");
-    if (!file)
+    std::auto_ptr<Stream> stream(FileSystem::open(path));
+    if (stream.get() == NULL || !stream->canRead())
     {
         GP_ERROR("Failed to load audio file %s.", path);
         goto cleanup;
@@ -72,7 +102,7 @@ AudioBuffer* AudioBuffer::create(const char* path)
     
     // Read the file header
     char header[12];
-    if (fread(header, 1, 12, file) != 12)
+    if (stream->read(header, 1, 12) != 12)
     {
         GP_ERROR("Invalid header for audio file %s.", path);
         goto cleanup;
@@ -81,7 +111,7 @@ AudioBuffer* AudioBuffer::create(const char* path)
     // Check the file format
     if (memcmp(header, "RIFF", 4) == 0)
     {
-        if (!AudioBuffer::loadWav(file, alBuffer))
+        if (!AudioBuffer::loadWav(stream.get(), alBuffer))
         {
             GP_ERROR("Invalid wave file: %s", path);
             goto cleanup;
@@ -89,7 +119,7 @@ AudioBuffer* AudioBuffer::create(const char* path)
     }
     else if (memcmp(header, "OggS", 4) == 0)
     {
-        if (!AudioBuffer::loadOgg(file, alBuffer))
+        if (!AudioBuffer::loadOgg(stream.get(), alBuffer))
         {
             GP_ERROR("Invalid ogg file: %s", path);
             goto cleanup;
@@ -101,10 +131,6 @@ AudioBuffer* AudioBuffer::create(const char* path)
         goto cleanup;
     }
 
-    //NOTE: loadOgg actually sets this null, so it is expected
-    if (file)    
-        fclose(file);
-
     buffer = new AudioBuffer(path, alBuffer);
 
     // Add the buffer to the cache.
@@ -113,34 +139,32 @@ AudioBuffer* AudioBuffer::create(const char* path)
     return buffer;
     
 cleanup:
-    
-    if (file)
-        fclose(file);
     if (alBuffer)
         AL_CHECK( alDeleteBuffers(1, &alBuffer) );
     return NULL;
 }
 
-bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
+bool AudioBuffer::loadWav(Stream* stream, ALuint buffer)
 {
-    GP_ASSERT(file);
-    unsigned char stream[12];
+    GP_ASSERT(stream);
+
+    unsigned char data[12];
     
     // Verify the wave fmt magic value meaning format.
-    if (fread(stream, 1, 8, file) != 8 || memcmp(stream, "fmt ", 4) != 0 )
+    if (stream->read(data, 1, 8) != 8 || memcmp(data, "fmt ", 4) != 0 )
     {
         GP_ERROR("Failed to verify the magic value for the wave file format.");
         return false;
     }
     
     unsigned int section_size;
-    section_size  = stream[7]<<24;
-    section_size |= stream[6]<<16;
-    section_size |= stream[5]<<8;
-    section_size |= stream[4];
+    section_size  = data[7]<<24;
+    section_size |= data[6]<<16;
+    section_size |= data[5]<<8;
+    section_size |= data[4];
 
     // Check for a valid pcm format.
-    if (fread(stream, 1, 2, file) != 2 || stream[1] != 0 || stream[0] != 1)
+    if (stream->read(data, 1, 2) != 2 || data[1] != 0 || data[0] != 1)
     {
         GP_ERROR("Unsupported audio file format (must be a valid PCM format).");
         return false;
@@ -148,31 +172,31 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
     
     // Get the channel count (16-bit little-endian).
     int channels;
-    if (fread(stream, 1, 2, file) != 2)
+    if (stream->read(data, 1, 2) != 2)
     {
         GP_ERROR("Failed to read the wave file's channel count.");
         return false;
     }
-    channels  = stream[1]<<8;
-    channels |= stream[0];
+    channels  = data[1]<<8;
+    channels |= data[0];
     
     // Get the sample frequency (32-bit little-endian).
     ALuint frequency;
-    if (fread(stream, 1, 4, file) != 4)
+    if (stream->read(data, 1, 4) != 4)
     {
         GP_ERROR("Failed to read the wave file's sample frequency.");
         return false;
     }
 
-    frequency  = stream[3]<<24;
-    frequency |= stream[2]<<16;
-    frequency |= stream[1]<<8;
-    frequency |= stream[0];
+    frequency  = data[3]<<24;
+    frequency |= data[2]<<16;
+    frequency |= data[1]<<8;
+    frequency |= data[0];
     
     // The next 6 bytes hold the block size and bytes-per-second. 
     // We don't need that info, so just read and ignore it. 
     // We could use this later if we need to know the duration.
-    if (fread(stream, 1, 6, file) != 6)
+    if (stream->read(data, 1, 6) != 6)
     {
         GP_ERROR("Failed to read past the wave file's block size and bytes-per-second.");
         return false;
@@ -180,13 +204,13 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
     
     // Get the bit depth (16-bit little-endian).
     int bits;
-    if (fread(stream, 1, 2, file) != 2)
+    if (stream->read(data, 1, 2) != 2)
     {
         GP_ERROR("Failed to read the wave file's bit depth.");
         return false;
     }
-    bits  = stream[1]<<8;
-    bits |= stream[0];
+    bits  = data[1]<<8;
+    bits |= data[0];
     
     // Now convert the given channel count and bit depth into an OpenAL format. 
     ALuint format = 0;
@@ -216,7 +240,7 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
         unsigned int length = section_size - 16;
 
         // Extension size is 2 bytes.
-        if (fread(stream, 1, length, file) != length)
+        if (stream->read(data, 1, length) != length)
         {
             GP_ERROR("Failed to read extension size from wave file.");
             return false;
@@ -227,32 +251,32 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
     while (true)
     {
         // Check if we are at the end of the file without reading the data.
-        if (feof(file))
+        if (stream->eof())
         {
             GP_ERROR("Failed to load wave file; file appears to have no data.");
             return false;
         }
 
         // Read in the type of the next section of the file.
-        if (fread(stream, 1, 4, file) != 4)
+        if (stream->read(data, 1, 4) != 4)
         {
             GP_ERROR("Failed to read next section type from wave file.");
             return false;
         }
 
         // Data chunk.
-        if (memcmp(stream, "data", 4) == 0)
+        if (memcmp(data, "data", 4) == 0)
         {
             // Read how much data is remaining and buffer it up.
             unsigned int dataSize;
-            if (fread(&dataSize, sizeof(int), 1, file) != 1)
+            if (stream->read(&dataSize, sizeof(int), 1) != 1)
             {
                 GP_ERROR("Failed to read size of data section from wave file.");
                 return false;
             }
 
             char* data = new char[dataSize];
-            if (fread(data, sizeof(char), dataSize, file) != dataSize)
+            if (stream->read(data, sizeof(char), dataSize) != dataSize)
             {
                 GP_ERROR("Failed to load wave file; file is missing data.");
                 SAFE_DELETE_ARRAY(data);
@@ -281,22 +305,22 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
         {
             // Store the name of the chunk so we can report errors informatively.
             char chunk[5] = { 0 };
-            memcpy(chunk, stream, 4);
+            memcpy(chunk, data, 4);
 
             // Read the chunk size.
-            if (fread(stream, 1, 4, file) != 4)
+            if (stream->read(data, 1, 4) != 4)
             {
                 GP_ERROR("Failed to read size of '%s' chunk from wave file.", chunk);
                 return false;
             }
 
-            section_size  = stream[3]<<24;
-            section_size |= stream[2]<<16;
-            section_size |= stream[1]<<8;
-            section_size |= stream[0];
+            section_size  = data[3]<<24;
+            section_size |= data[2]<<16;
+            section_size |= data[1]<<8;
+            section_size |= data[0];
 
             // Seek past the chunk.
-            if (fseek(file, section_size, SEEK_CUR) != 0)
+            if (stream->seek(section_size, SEEK_CUR) == false)
             {
                 GP_ERROR("Failed to seek past '%s' chunk in wave file.", chunk);
                 return false;
@@ -304,10 +328,10 @@ bool AudioBuffer::loadWav(FILE* file, ALuint buffer)
         }
     }
 }
-    
-bool AudioBuffer::loadOgg(FILE*& file, ALuint buffer)
+
+bool AudioBuffer::loadOgg(Stream* stream, ALuint buffer)
 {
-    GP_ASSERT(file);
+    GP_ASSERT(stream);
 
     OggVorbis_File ogg_file;
     vorbis_info* info;
@@ -316,11 +340,16 @@ bool AudioBuffer::loadOgg(FILE*& file, ALuint buffer)
     int section;
     long size = 0;
 
-    rewind(file);
+    stream->rewind();
 
-    if ((result = ov_open(file, &ogg_file, NULL, 0)) < 0)
+    ov_callbacks callbacks;
+    callbacks.read_func = readStream;
+    callbacks.seek_func = seekStream;
+    callbacks.close_func = closeStream;
+    callbacks.tell_func = tellStream;
+
+    if ((result = ov_open_callbacks(stream, &ogg_file, NULL, 0, callbacks)) < 0)
     {
-        fclose(file);
         GP_ERROR("Failed to open ogg file.");
         return false;
     }
@@ -367,9 +396,6 @@ bool AudioBuffer::loadOgg(FILE*& file, ALuint buffer)
     SAFE_DELETE_ARRAY(data);
     ov_clear(&ogg_file);
 
-    // ov_clear actually closes the file pointer as well.
-    file = 0;
-
     return true;
 }
 

+ 3 - 2
gameplay/src/AudioBuffer.h

@@ -2,6 +2,7 @@
 #define AUDIOBUFFER_H_
 
 #include "Ref.h"
+#include "Stream.h"
 
 namespace gameplay
 {
@@ -43,9 +44,9 @@ private:
      */
     static AudioBuffer* create(const char* path);
     
-    static bool loadWav(FILE* file, ALuint buffer);
+    static bool loadWav(Stream* stream, ALuint buffer);
     
-    static bool loadOgg(FILE*& file, ALuint buffer);
+    static bool loadOgg(Stream* stream, ALuint buffer);
 
     std::string _filePath;
     ALuint _alBuffer;

+ 83 - 99
gameplay/src/Bundle.cpp

@@ -32,7 +32,7 @@ namespace gameplay
 static std::vector<Bundle*> __bundleCache;
 
 Bundle::Bundle(const char* path) :
-    _path(path), _referenceCount(0), _references(NULL), _file(NULL), _trackedNodes(NULL)
+    _path(path), _referenceCount(0), _references(NULL), _stream(NULL), _trackedNodes(NULL)
 {
 }
 
@@ -49,10 +49,9 @@ Bundle::~Bundle()
 
     SAFE_DELETE_ARRAY(_references);
 
-    if (_file)
+    if (_stream)
     {
-        fclose(_file);
-        _file = NULL;
+        SAFE_DELETE(_stream);
     }
 }
 
@@ -61,7 +60,7 @@ bool Bundle::readArray(unsigned int* length, T** ptr)
 {
     GP_ASSERT(length);
     GP_ASSERT(ptr);
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
 
     if (!read(length))
     {
@@ -71,7 +70,7 @@ bool Bundle::readArray(unsigned int* length, T** ptr)
     if (*length > 0)
     {
         *ptr = new T[*length];
-        if (fread(*ptr, sizeof(T), *length, _file) != *length)
+        if (_stream->read(*ptr, sizeof(T), *length) != *length)
         {
             GP_ERROR("Failed to read an array of data from bundle (into an array).");
             SAFE_DELETE_ARRAY(*ptr);
@@ -85,7 +84,7 @@ template <class T>
 bool Bundle::readArray(unsigned int* length, std::vector<T>* values)
 {
     GP_ASSERT(length);
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
 
     if (!read(length))
     {
@@ -95,7 +94,7 @@ bool Bundle::readArray(unsigned int* length, std::vector<T>* values)
     if (*length > 0 && values)
     {
         values->resize(*length);
-        if (fread(&(*values)[0], sizeof(T), *length, _file) != *length)
+        if (_stream->read(&(*values)[0], sizeof(T), *length) != *length)
         {
             GP_ERROR("Failed to read an array of data from bundle (into a std::vector).");
             return false;
@@ -108,7 +107,7 @@ template <class T>
 bool Bundle::readArray(unsigned int* length, std::vector<T>* values, unsigned int readSize)
 {
     GP_ASSERT(length);
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
     GP_ASSERT(sizeof(T) >= readSize);
 
     if (!read(length))
@@ -119,7 +118,7 @@ bool Bundle::readArray(unsigned int* length, std::vector<T>* values, unsigned in
     if (*length > 0 && values)
     {
         values->resize(*length);
-        if (fread(&(*values)[0], readSize, *length, _file) != *length)
+        if (_stream->read(&(*values)[0], readSize, *length) != *length)
         {
             GP_ERROR("Failed to read an array of data from bundle (into a std::vector with a specified single element read size).");
             return false;
@@ -128,12 +127,12 @@ bool Bundle::readArray(unsigned int* length, std::vector<T>* values, unsigned in
     return true;
 }
 
-static std::string readString(FILE* fp)
+static std::string readString(Stream* stream)
 {
-    GP_ASSERT(fp);
+    GP_ASSERT(stream);
 
     unsigned int length;
-    if (fread(&length, 4, 1, fp) != 1)
+    if (stream->read(&length, 4, 1) != 1)
     {
         GP_ERROR("Failed to read the length of a string from a bundle.");
         return std::string();
@@ -146,7 +145,7 @@ static std::string readString(FILE* fp)
     if (length > 0)
     {
         str.resize(length);
-        if (fread(&str[0], 1, length, fp) != length)
+        if (stream->read(&str[0], 1, length) != length)
         {
             GP_ERROR("Failed to read string from bundle.");
             return std::string();
@@ -173,8 +172,8 @@ Bundle* Bundle::create(const char* path)
     }
 
     // Open the bundle.
-    FILE* fp = FileSystem::openFile(path, "rb");
-    if (!fp)
+    Stream* stream = FileSystem::open(path);
+    if (!stream)
     {
         GP_ERROR("Failed to open file '%s'.", path);
         return NULL;
@@ -182,46 +181,34 @@ Bundle* Bundle::create(const char* path)
 
     // Read the GPB header info.
     char sig[9];
-    if (fread(sig, 1, 9, fp) != 9 || memcmp(sig, "\xABGPB\xBB\r\n\x1A\n", 9) != 0)
+    if (stream->read(sig, 1, 9) != 9 || memcmp(sig, "\xABGPB\xBB\r\n\x1A\n", 9) != 0)
     {
+        SAFE_DELETE(stream);
         GP_ERROR("Invalid GPB header for bundle '%s'.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         return NULL;
     }
 
     // Read version.
     unsigned char ver[2];
-    if (fread(ver, 1, 2, fp) != 2)
+    if (stream->read(ver, 1, 2) != 2)
     {
+        SAFE_DELETE(stream);
         GP_ERROR("Failed to read GPB version for bundle '%s'.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         return NULL;
     }
     if (ver[0] != BUNDLE_VERSION_MAJOR || ver[1] != BUNDLE_VERSION_MINOR)
     {
+        SAFE_DELETE(stream);
         GP_ERROR("Unsupported version (%d.%d) for bundle '%s' (expected %d.%d).", (int)ver[0], (int)ver[1], path, BUNDLE_VERSION_MAJOR, BUNDLE_VERSION_MINOR);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         return NULL;
     }
 
     // Read ref table.
     unsigned int refCount;
-    if (fread(&refCount, 4, 1, fp) != 1)
+    if (stream->read(&refCount, 4, 1) != 1)
     {
+        SAFE_DELETE(stream);
         GP_ERROR("Failed to read ref table for bundle '%s'.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         return NULL;
     }
 
@@ -229,15 +216,12 @@ Bundle* Bundle::create(const char* path)
     Reference* refs = new Reference[refCount];
     for (unsigned int i = 0; i < refCount; ++i)
     {
-        if ((refs[i].id = readString(fp)).empty() ||
-            fread(&refs[i].type, 4, 1, fp) != 1 ||
-            fread(&refs[i].offset, 4, 1, fp) != 1)
+        if ((refs[i].id = readString(stream)).empty() ||
+            stream->read(&refs[i].type, 4, 1) != 1 ||
+            stream->read(&refs[i].offset, 4, 1) != 1)
         {
+            SAFE_DELETE(stream);
             GP_ERROR("Failed to read ref number %d for bundle '%s'.", i, path);
-            if (fclose(fp) != 0)
-            {
-                GP_ERROR("Failed to close file '%s'.", path);
-            }
             SAFE_DELETE_ARRAY(refs);
             return NULL;
         }
@@ -247,7 +231,7 @@ Bundle* Bundle::create(const char* path)
     Bundle* bundle = new Bundle(path);
     bundle->_referenceCount = refCount;
     bundle->_references = refs;
-    bundle->_file = fp;
+    bundle->_stream = stream;
 
     return bundle;
 }
@@ -281,8 +265,8 @@ void Bundle::clearLoadSession()
 
 const char* Bundle::getIdFromOffset() const
 {
-    GP_ASSERT(_file);
-    return getIdFromOffset((unsigned int) ftell(_file));
+    GP_ASSERT(_stream);
+    return getIdFromOffset((unsigned int) _stream->position());
 }
 
 const char* Bundle::getIdFromOffset(unsigned int offset) const
@@ -318,8 +302,8 @@ Bundle::Reference* Bundle::seekTo(const char* id, unsigned int type)
     }
 
     // Seek to the offset of this object.
-    GP_ASSERT(_file);
-    if (fseek(_file, ref->offset, SEEK_SET) != 0)
+    GP_ASSERT(_stream);
+    if (_stream->seek(ref->offset, SEEK_SET) == false)
     {
         GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", id, _path.c_str());
         return NULL;
@@ -331,7 +315,7 @@ Bundle::Reference* Bundle::seekTo(const char* id, unsigned int type)
 Bundle::Reference* Bundle::seekToFirstType(unsigned int type)
 {
     GP_ASSERT(_references);
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
 
     for (unsigned int i = 0; i < _referenceCount; ++i)
     {
@@ -339,7 +323,7 @@ Bundle::Reference* Bundle::seekToFirstType(unsigned int type)
         if (ref->type == type)
         {
             // Found a match.
-            if (fseek(_file, ref->offset, SEEK_SET) != 0)
+            if (_stream->seek(ref->offset, SEEK_SET) == false)
             {
                 GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", ref->id.c_str(), _path.c_str());
                 return NULL;
@@ -352,22 +336,22 @@ Bundle::Reference* Bundle::seekToFirstType(unsigned int type)
 
 bool Bundle::read(unsigned int* ptr)
 {
-    return fread(ptr, sizeof(unsigned int), 1, _file) == 1;
+    return _stream->read(ptr, sizeof(unsigned int), 1) == 1;
 }
 
 bool Bundle::read(unsigned char* ptr)
 {
-    return fread(ptr, sizeof(unsigned char), 1, _file) == 1;
+    return _stream->read(ptr, sizeof(unsigned char), 1) == 1;
 }
 
 bool Bundle::read(float* ptr)
 {
-    return fread(ptr, sizeof(float), 1, _file) == 1;
+    return _stream->read(ptr, sizeof(float), 1) == 1;
 }
 
 bool Bundle::readMatrix(float* m)
 {
-    return fread(m, sizeof(float), 16, _file) == 16;
+    return _stream->read(m, sizeof(float), 16) == 16;
 }
 
 Scene* Bundle::loadScene(const char* id)
@@ -419,7 +403,7 @@ Scene* Bundle::loadScene(const char* id)
         }
     }
     // Read active camera.
-    std::string xref = readString(_file);
+    std::string xref = readString(_stream);
     if (xref.length() > 1 && xref[0] == '#') // TODO: Handle full xrefs
     {
         Node* node = scene->findNode(xref.c_str() + 1, true);
@@ -453,14 +437,14 @@ Scene* Bundle::loadScene(const char* id)
 
     // Parse animations.
     GP_ASSERT(_references);
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
     for (unsigned int i = 0; i < _referenceCount; ++i)
     {
         Reference* ref = &_references[i];
         if (ref->type == BUNDLE_TYPE_ANIMATIONS)
         {
             // Found a match.
-            if (fseek(_file, ref->offset, SEEK_SET) != 0)
+            if (_stream->seek(ref->offset, SEEK_SET) == false)
             {
                 GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", ref->id.c_str(), _path.c_str());
                 return NULL;
@@ -483,7 +467,7 @@ Node* Bundle::loadNode(const char* id, Scene* sceneContext)
 {
     GP_ASSERT(id);
     GP_ASSERT(_references);
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
 
     clearLoadSession();
 
@@ -499,7 +483,7 @@ Node* Bundle::loadNode(const char* id, Scene* sceneContext)
         Reference* ref = &_references[i];
         if (ref->type == BUNDLE_TYPE_ANIMATIONS)
         {
-            if (fseek(_file, ref->offset, SEEK_SET) != 0)
+            if (_stream->seek(ref->offset, SEEK_SET) == false)
             {
                 GP_ERROR("Failed to seek to object '%s' in bundle '%s'.", ref->id.c_str(), _path.c_str());
                 SAFE_DELETE(_trackedNodes);
@@ -517,7 +501,7 @@ Node* Bundle::loadNode(const char* id, Scene* sceneContext)
 
             for (unsigned int j = 0; j < animationCount; j++)
             {
-                const std::string id = readString(_file);
+                const std::string id = readString(_stream);
 
                 // Read the number of animation channels in this animation.
                 unsigned int animationChannelCount;
@@ -532,7 +516,7 @@ Node* Bundle::loadNode(const char* id, Scene* sceneContext)
                 for (unsigned int k = 0; k < animationChannelCount; k++)
                 {
                     // Read target id.
-                    std::string targetId = readString(_file);
+                    std::string targetId = readString(_stream);
                     if (targetId.empty())
                     {
                         GP_ERROR("Failed to read target id for animation '%s'.", id.c_str());
@@ -627,7 +611,7 @@ bool Bundle::skipNode()
 {
     const char* id = getIdFromOffset();
     GP_ASSERT(id);
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
 
     // Skip the node's type.
     unsigned int nodeType;
@@ -638,12 +622,12 @@ bool Bundle::skipNode()
     }
 
     // Skip over the node's transform and parent ID.
-    if (fseek(_file, sizeof(float) * 16, SEEK_CUR) != 0)
+    if (_stream->seek(sizeof(float) * 16, SEEK_CUR) == false)
     {
         GP_ERROR("Failed to skip over node transform for node '%s'.", id);
         return false;
     }
-    readString(_file);
+    readString(_stream);
 
     // Skip over the node's children.
     unsigned int childrenCount;
@@ -673,7 +657,7 @@ Node* Bundle::readNode(Scene* sceneContext, Node* nodeContext)
 {
     const char* id = getIdFromOffset();
     GP_ASSERT(id);
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
 
     // If we are tracking nodes and it's not in the set yet, add it.
     if (_trackedNodes)
@@ -725,7 +709,7 @@ Node* Bundle::readNode(Scene* sceneContext, Node* nodeContext)
 
     // Read transform.
     float transform[16];
-    if (fread(transform, sizeof(float), 16, _file) != 16)
+    if (_stream->read(transform, sizeof(float), 16) != 16)
     {
         GP_ERROR("Failed to read transform for node '%s'.", id);
         SAFE_RELEASE(node);
@@ -734,7 +718,7 @@ Node* Bundle::readNode(Scene* sceneContext, Node* nodeContext)
     setTransform(transform, node);
 
     // Skip the parent ID.
-    readString(_file);
+    readString(_stream);
 
     // Read children.
     unsigned int childrenCount;
@@ -954,7 +938,7 @@ Model* Bundle::readModel(const char* nodeId)
 {
     // Read mesh.
     Mesh* mesh = NULL;
-    std::string xref = readString(_file);
+    std::string xref = readString(_stream);
     if (xref.length() > 1 && xref[0] == '#') // TODO: Handle full xrefs
     {
         mesh = loadMesh(xref.c_str() + 1, nodeId);
@@ -1035,7 +1019,7 @@ MeshSkin* Bundle::readMeshSkin()
     // Read joint xref strings for all joints in the list.
     for (unsigned int i = 0; i < jointCount; i++)
     {
-        skinData->joints.push_back(readString(_file));
+        skinData->joints.push_back(readString(_stream));
     }
 
     // Read bind poses.
@@ -1072,7 +1056,7 @@ MeshSkin* Bundle::readMeshSkin()
 
 void Bundle::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
 {
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
 
     for (size_t i = 0, skinCount = _meshSkins.size(); i < skinCount; ++i)
     {
@@ -1144,12 +1128,12 @@ void Bundle::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
                         seekTo(nodeId.c_str(), ref->type);
 
                         // Skip over the node type (1 unsigned int) and transform (16 floats) and read the parent id.
-                        if (fseek(_file, sizeof(unsigned int) + sizeof(float)*16, SEEK_CUR) != 0)
+                        if (_stream->seek(sizeof(unsigned int) + sizeof(float)*16, SEEK_CUR) == false)
                         {
                             GP_ERROR("Failed to skip over node type and transform for node '%s' in bundle '%s'.", nodeId.c_str(), _path.c_str());
                             return;
                         }
-                        std::string parentID = readString(_file);
+                        std::string parentID = readString(_stream);
 
                         if (!parentID.empty())
                             nodeId = parentID;
@@ -1185,7 +1169,7 @@ void Bundle::resolveJointReferences(Scene* sceneContext, Node* nodeContext)
 
 void Bundle::readAnimation(Scene* scene)
 {
-    const std::string animationId = readString(_file);
+    const std::string animationId = readString(_stream);
 
     // Read the number of animation channels in this animation.
     unsigned int animationChannelCount;
@@ -1223,7 +1207,7 @@ Animation* Bundle::readAnimationChannel(Scene* scene, Animation* animation, cons
     GP_ASSERT(animationId);
 
     // Read target id.
-    std::string targetId = readString(_file);
+    std::string targetId = readString(_stream);
     if (targetId.empty())
     {
         GP_ERROR("Failed to read target id for animation '%s'.", animationId);
@@ -1331,11 +1315,11 @@ Mesh* Bundle::loadMesh(const char* id)
 
 Mesh* Bundle::loadMesh(const char* id, const char* nodeId)
 {
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
     GP_ASSERT(id);
 
     // Save the file position.
-    long position = ftell(_file);
+    long position = _stream->position();
     if (position == -1L)
     {
         GP_ERROR("Failed to save the current file position before loading mesh '%s'.", id);
@@ -1395,7 +1379,7 @@ Mesh* Bundle::loadMesh(const char* id, const char* nodeId)
     SAFE_DELETE(meshData);
 
     // Restore file pointer.
-    if (fseek(_file, position, SEEK_SET) != 0)
+    if (_stream->seek(position, SEEK_SET) == false)
     {
         GP_ERROR("Failed to restore file pointer after loading mesh '%s'.", id);
         return NULL;
@@ -1408,7 +1392,7 @@ Bundle::MeshData* Bundle::readMeshData()
 {
     // Read vertex format/elements.
     unsigned int vertexElementCount;
-    if (fread(&vertexElementCount, 4, 1, _file) != 1)
+    if (_stream->read(&vertexElementCount, 4, 1) != 1)
     {
         GP_ERROR("Failed to load vertex element count.");
         return NULL;
@@ -1423,13 +1407,13 @@ Bundle::MeshData* Bundle::readMeshData()
     for (unsigned int i = 0; i < vertexElementCount; ++i)
     {
         unsigned int vUsage, vSize;
-        if (fread(&vUsage, 4, 1, _file) != 1)
+        if (_stream->read(&vUsage, 4, 1) != 1)
         {
             GP_ERROR("Failed to load vertex usage.");
             SAFE_DELETE_ARRAY(vertexElements);
             return NULL;
         }
-        if (fread(&vSize, 4, 1, _file) != 1)
+        if (_stream->read(&vSize, 4, 1) != 1)
         {
             GP_ERROR("Failed to load vertex size.");
             SAFE_DELETE_ARRAY(vertexElements);
@@ -1445,7 +1429,7 @@ Bundle::MeshData* Bundle::readMeshData()
 
     // Read vertex data.
     unsigned int vertexByteCount;
-    if (fread(&vertexByteCount, 4, 1, _file) != 1)
+    if (_stream->read(&vertexByteCount, 4, 1) != 1)
     {
         GP_ERROR("Failed to load vertex byte count.");
         SAFE_DELETE(meshData);
@@ -1461,7 +1445,7 @@ Bundle::MeshData* Bundle::readMeshData()
     GP_ASSERT(meshData->vertexFormat.getVertexSize());
     meshData->vertexCount = vertexByteCount / meshData->vertexFormat.getVertexSize();
     meshData->vertexData = new unsigned char[vertexByteCount];
-    if (fread(meshData->vertexData, 1, vertexByteCount, _file) != vertexByteCount)
+    if (_stream->read(meshData->vertexData, 1, vertexByteCount) != vertexByteCount)
     {
         GP_ERROR("Failed to load vertex data.");
         SAFE_DELETE(meshData);
@@ -1469,13 +1453,13 @@ Bundle::MeshData* Bundle::readMeshData()
     }
 
     // Read mesh bounds (bounding box and bounding sphere).
-    if (fread(&meshData->boundingBox.min.x, 4, 3, _file) != 3 || fread(&meshData->boundingBox.max.x, 4, 3, _file) != 3)
+    if (_stream->read(&meshData->boundingBox.min.x, 4, 3) != 3 || _stream->read(&meshData->boundingBox.max.x, 4, 3) != 3)
     {
         GP_ERROR("Failed to load mesh bounding box.");
         SAFE_DELETE(meshData);
         return NULL;
     }
-    if (fread(&meshData->boundingSphere.center.x, 4, 3, _file) != 3 || fread(&meshData->boundingSphere.radius, 4, 1, _file) != 1)
+    if (_stream->read(&meshData->boundingSphere.center.x, 4, 3) != 3 || _stream->read(&meshData->boundingSphere.radius, 4, 1) != 1)
     {
         GP_ERROR("Failed to load mesh bounding sphere.");
         SAFE_DELETE(meshData);
@@ -1484,7 +1468,7 @@ Bundle::MeshData* Bundle::readMeshData()
 
     // Read mesh parts.
     unsigned int meshPartCount;
-    if (fread(&meshPartCount, 4, 1, _file) != 1)
+    if (_stream->read(&meshPartCount, 4, 1) != 1)
     {
         GP_ERROR("Failed to load mesh part count.");
         SAFE_DELETE(meshData);
@@ -1494,19 +1478,19 @@ Bundle::MeshData* Bundle::readMeshData()
     {
         // Read primitive type, index format and index count.
         unsigned int pType, iFormat, iByteCount;
-        if (fread(&pType, 4, 1, _file) != 1)
+        if (_stream->read(&pType, 4, 1) != 1)
         {
             GP_ERROR("Failed to load primitive type for mesh part with index %d.", i);
             SAFE_DELETE(meshData);
             return NULL;
         }
-        if (fread(&iFormat, 4, 1, _file) != 1)
+        if (_stream->read(&iFormat, 4, 1) != 1)
         {
             GP_ERROR("Failed to load index format for mesh part with index %d.", i);
             SAFE_DELETE(meshData);
             return NULL;
         }
-        if (fread(&iByteCount, 4, 1, _file) != 1)
+        if (_stream->read(&iByteCount, 4, 1) != 1)
         {
             GP_ERROR("Failed to load index byte count for mesh part with index %d.", i);
             SAFE_DELETE(meshData);
@@ -1540,7 +1524,7 @@ Bundle::MeshData* Bundle::readMeshData()
         partData->indexCount = iByteCount / indexSize;
 
         partData->indexData = new unsigned char[iByteCount];
-        if (fread(partData->indexData, 1, iByteCount, _file) != iByteCount)
+        if (_stream->read(partData->indexData, 1, iByteCount) != iByteCount)
         {
             GP_ERROR("Failed to read index data for mesh part with index %d.", i);
             SAFE_DELETE(meshData);
@@ -1601,7 +1585,7 @@ Bundle::MeshData* Bundle::readMeshData(const char* url)
 Font* Bundle::loadFont(const char* id)
 {
     GP_ASSERT(id);
-    GP_ASSERT(_file);
+    GP_ASSERT(_stream);
 
     // Seek to the specified font.
     Reference* ref = seekTo(id, BUNDLE_TYPE_FONT);
@@ -1612,7 +1596,7 @@ Font* Bundle::loadFont(const char* id)
     }
 
     // Read font family.
-    std::string family = readString(_file);
+    std::string family = readString(_stream);
     if (family.empty())
     {
         GP_ERROR("Failed to read font family for font '%s'.", id);
@@ -1621,23 +1605,23 @@ Font* Bundle::loadFont(const char* id)
 
     // Read font style and size.
     unsigned int style, size;
-    if (fread(&style, 4, 1, _file) != 1)
+    if (_stream->read(&style, 4, 1) != 1)
     {
         GP_ERROR("Failed to read style for font '%s'.", id);
         return NULL;
     }
-    if (fread(&size, 4, 1, _file) != 1)
+    if (_stream->read(&size, 4, 1) != 1)
     {
         GP_ERROR("Failed to read size for font '%s'.", id);
         return NULL;
     }
 
     // Read character set.
-    std::string charset = readString(_file);
+    std::string charset = readString(_stream);
 
     // Read font glyphs.
     unsigned int glyphCount;
-    if (fread(&glyphCount, 4, 1, _file) != 1)
+    if (_stream->read(&glyphCount, 4, 1) != 1)
     {
         GP_ERROR("Failed to read glyph count for font '%s'.", id);
         return NULL;
@@ -1649,7 +1633,7 @@ Font* Bundle::loadFont(const char* id)
     }
 
     Font::Glyph* glyphs = new Font::Glyph[glyphCount];
-    if (fread(glyphs, sizeof(Font::Glyph), glyphCount, _file) != glyphCount)
+    if (_stream->read(glyphs, sizeof(Font::Glyph), glyphCount) != glyphCount)
     {
         GP_ERROR("Failed to read glyphs for font '%s'.", id);
         SAFE_DELETE_ARRAY(glyphs);
@@ -1658,19 +1642,19 @@ Font* Bundle::loadFont(const char* id)
 
     // Read texture attributes.
     unsigned int width, height, textureByteCount;
-    if (fread(&width, 4, 1, _file) != 1)
+    if (_stream->read(&width, 4, 1) != 1)
     {
         GP_ERROR("Failed to read texture width for font '%s'.", id);
         SAFE_DELETE_ARRAY(glyphs);
         return NULL;
     }
-    if (fread(&height, 4, 1, _file) != 1)
+    if (_stream->read(&height, 4, 1) != 1)
     {
         GP_ERROR("Failed to read texture height for font '%s'.", id);
         SAFE_DELETE_ARRAY(glyphs);
         return NULL;
     }
-    if (fread(&textureByteCount, 4, 1, _file) != 1)
+    if (_stream->read(&textureByteCount, 4, 1) != 1)
     {
         GP_ERROR("Failed to read texture byte count for font '%s'.", id);
         SAFE_DELETE_ARRAY(glyphs);
@@ -1685,7 +1669,7 @@ Font* Bundle::loadFont(const char* id)
 
     // Read texture data.
     unsigned char* textureData = new unsigned char[textureByteCount];
-    if (fread(textureData, 1, textureByteCount, _file) != textureByteCount)
+    if (_stream->read(textureData, 1, textureByteCount) != textureByteCount)
     {
         GP_ERROR("Failed to read texture data for font '%s'.", id);
         SAFE_DELETE_ARRAY(glyphs);

+ 1 - 1
gameplay/src/Bundle.h

@@ -429,7 +429,7 @@ private:
     std::string _path;
     unsigned int _referenceCount;
     Reference* _references;
-    FILE* _file;
+    Stream* _stream;
 
     std::vector<MeshSkinData*> _meshSkins;
     std::map<std::string, Node*>* _trackedNodes;

+ 103 - 0
gameplay/src/DebugNew.h

@@ -76,6 +76,109 @@ template<typename T, typename T1, typename T2> T* bullet_new(const T1& t1, const
 #endif
 }
 
+template<typename T, typename T1, typename T2> T* bullet_new(T1& t1, const T2& t2)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+    T* t = new T(t1, t2);
+#define new DEBUG_NEW
+    return t;
+#else
+    return new T(t1, t2);
+#endif
+}
+
+template<typename T, typename T1, typename T2, typename T3> 
+T* bullet_new(const T1& t1, const T2& t2, const T3& t3)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+    T* t = new T(t1, t2, t3);
+#define new DEBUG_NEW
+    return t;
+#else
+    return new T(t1, t2, t3);
+#endif
+}
+
+template<typename T, typename T1, typename T2, typename T3> 
+T* bullet_new(T1& t1, const T2& t2, const T3& t3)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+    T* t = new T(t1, t2, t3);
+#define new DEBUG_NEW
+    return t;
+#else
+    return new T(t1, t2, t3);
+#endif
+}
+
+template<typename T, typename T1, typename T2, typename T3, typename T4> 
+T* bullet_new(const T1& t1, const T2& t2, const T3& t3, const T4& t4)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+    T* t = new T(t1, t2, t3, t4);
+#define new DEBUG_NEW
+    return t;
+#else
+    return new T(t1, t2, t3, t4);
+#endif
+}
+
+template<typename T, typename T1, typename T2, typename T3, typename T4> 
+T* bullet_new(T1& t1, const T2& t2, const T3& t3, const T4& t4)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+    T* t = new T(t1, t2, t3, t4);
+#define new DEBUG_NEW
+    return t;
+#else
+    return new T(t1, t2, t3, t4);
+#endif
+}
+
+template<typename T, typename T1, typename T2, typename T3, typename T4> 
+T* bullet_new(T1& t1, T2& t2, const T3& t3, const T4& t4)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+    T* t = new T(t1, t2, t3, t4);
+#define new DEBUG_NEW
+    return t;
+#else
+    return new T(t1, t2, t3, t4);
+#endif
+}
+
+template<typename T, typename T1, typename T2, typename T3, typename T4, typename T5> 
+T* bullet_new(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+    T* t = new T(t1, t2, t3, t4, t5);
+#define new DEBUG_NEW
+    return t;
+#else
+    return new T(t1, t2, t3, t4, t5);
+#endif
+}
+
+template<typename T, typename T1, typename T2, typename T3, typename T4, typename T5> 
+T* bullet_new(T1& t1, T2& t2, const T3& t3, const T4& t4, const T5& t5)
+{
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+    T* t = new T(t1, t2, t3, t4, t5);
+#define new DEBUG_NEW
+    return t;
+#else
+    return new T(t1, t2, t3, t4, t5);
+#endif
+}
+
 template<typename T, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename T9> 
 T* bullet_new(const T1& t1, const T2& t2, const T3& t3, const T4& t4, const T5& t5, const T6& t6, const T7& t7, const T8& t8, const T9& t9)
 {

+ 5 - 4
gameplay/src/Effect.cpp

@@ -200,10 +200,11 @@ static void writeShaderToErrorFile(const char* filePath, const char* source)
 {
     std::string path = filePath;
     path += ".err";
-    FILE* file = FileSystem::openFile(path.c_str(), "wb");
-    int err = ferror(file);
-    fwrite(source, 1, strlen(source), file);
-    fclose(file);
+    std::auto_ptr<Stream> stream(FileSystem::open(path.c_str(), FileSystem::WRITE));
+    if (stream.get() != NULL && stream->canWrite())
+    {
+        stream->write(source, 1, strlen(source));
+    }
 }
 
 Effect* Effect::createFromSource(const char* vshPath, const char* vshSource, const char* fshPath, const char* fshSource, const char* defines)

+ 397 - 30
gameplay/src/FileSystem.cpp

@@ -1,6 +1,7 @@
 #include "Base.h"
 #include "FileSystem.h"
 #include "Properties.h"
+#include "Stream.h"
 
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -69,6 +70,78 @@ void makepath(std::string path, int mode)
 static std::string __resourcePath("./");
 static std::map<std::string, std::string> __aliases;
 
+/**
+ * 
+ * @script{ignore}
+ */
+class FileStream : public Stream
+{
+public:
+    friend class FileSystem;
+    
+    ~FileStream();
+    virtual bool canRead();
+    virtual bool canWrite();
+    virtual bool canSeek();
+    virtual void close();
+    virtual size_t read(void* ptr, size_t size, size_t count);
+    virtual char* readLine(char* str, int num);
+    virtual size_t write(const void* ptr, size_t size, size_t count);
+    virtual bool eof();
+    virtual size_t length();
+    virtual long int position();
+    virtual bool seek(long int offset, int origin);
+    virtual bool rewind();
+
+    static FileStream* create(const char* filePath, const char* mode);
+
+private:
+    FileStream(FILE* file);
+
+private:
+    FILE* _file;
+    bool _canRead;
+    bool _canWrite;
+};
+
+#ifdef __ANDROID__
+
+/**
+ * 
+ * @script{ignore}
+ */
+class FileStreamAndroid : public Stream
+{
+public:
+    friend class FileSystem;
+    
+    ~FileStreamAndroid();
+    virtual bool canRead();
+    virtual bool canWrite();
+    virtual bool canSeek();
+    virtual void close();
+    virtual size_t read(void* ptr, size_t size, size_t count);
+    virtual char* readLine(char* str, int num);
+    virtual size_t write(const void* ptr, size_t size, size_t count);
+    virtual bool eof();
+    virtual size_t length();
+    virtual long int position();
+    virtual bool seek(long int offset, int origin);
+    virtual bool rewind();
+
+    static FileStreamAndroid* create(const char* filePath, const char* mode);
+
+private:
+    FileStreamAndroid(AAsset* asset);
+
+private:
+    AAsset* _asset;
+};
+
+#endif
+
+/////////////////////////////
+
 FileSystem::FileSystem()
 {
 }
@@ -230,15 +303,52 @@ bool FileSystem::fileExists(const char* filePath)
 #endif
 }
 
-FILE* FileSystem::openFile(const char* path, const char* mode)
+Stream* FileSystem::open(const char* path, size_t mode)
 {
-    GP_ASSERT(path);
+    char modeStr[] = "rb";
+    if ((mode & WRITE) != 0)
+        modeStr[0] = 'w';
+#ifdef __ANDROID__
+    return FileStreamAndroid::create(resolvePath(path), modeStr);
+#else
+    std::string fullPath(__resourcePath);
+    fullPath += resolvePath(path);
+    
+#ifdef WIN32
+    gp_stat_struct s;
+    if (stat(fullPath.c_str(), &s) != 0)
+    {
+        fullPath = __resourcePath;
+        fullPath += "../../gameplay/";
+        fullPath += path;
+        
+        int result = stat(fullPath.c_str(), &s);
+        if (result != 0)
+        {
+            fullPath = __resourcePath;
+            fullPath += "../gameplay/";
+            fullPath += path;
+            if (stat(fullPath.c_str(), &s) != 0)
+            {
+                return NULL;
+            }
+        }
+    }
+#endif
+    FileStream* stream = FileStream::create(fullPath.c_str(), modeStr);
+    return stream;
+#endif
+}
+
+FILE* FileSystem::openFile(const char* filePath, const char* mode)
+{
+    GP_ASSERT(filePath);
     GP_ASSERT(mode);
 
     std::string fullPath(__resourcePath);
-    fullPath += resolvePath(path);
+    fullPath += resolvePath(filePath);
 
-    createFileFromAsset(path);
+    createFileFromAsset(filePath);
     
     FILE* fp = fopen(fullPath.c_str(), mode);
     
@@ -247,14 +357,14 @@ FILE* FileSystem::openFile(const char* path, const char* mode)
     {
         fullPath = __resourcePath;
         fullPath += "../../gameplay/";
-        fullPath += path;
+        fullPath += filePath;
         
         fp = fopen(fullPath.c_str(), mode);
         if (!fp)
         {
             fullPath = __resourcePath;
             fullPath += "../gameplay/";
-            fullPath += path;
+            fullPath += filePath;
             fp = fopen(fullPath.c_str(), mode);
         }
     }
@@ -268,32 +378,20 @@ char* FileSystem::readAll(const char* filePath, int* fileSize)
     GP_ASSERT(filePath);
 
     // Open file for reading.
-    FILE* file = openFile(filePath, "rb");
-    if (file == NULL)
+    std::auto_ptr<Stream> stream(open(filePath));
+    if (stream.get() == NULL)
     {
         GP_ERROR("Failed to load file: %s", filePath);
         return NULL;
     }
-
-    // Obtain file length.
-    if (fseek(file, 0, SEEK_END) != 0)
-    {
-        GP_ERROR("Failed to seek to the end of the file '%s' to obtain the file length.", filePath);
-        return NULL;
-    }
-    int size = (int)ftell(file);
-    if (fseek(file, 0, SEEK_SET) != 0)
-    {
-        GP_ERROR("Failed to seek to beginning of the file '%s' to begin reading in the entire file.", filePath);
-        return NULL;
-    }
+    size_t size = stream->length();
 
     // Read entire file contents.
     char* buffer = new char[size + 1];
-    int read = (int)fread(buffer, 1, size, file);
+    size_t read = stream->read(buffer, 1, size);
     if (read != size)
     {
-        GP_ERROR("Failed to read complete contents of file '%s' (amount read vs. file size: %d < %d).", filePath, (int)read, (int)size);
+        GP_ERROR("Failed to read complete contents of file '%s' (amount read vs. file size: %u < %u).", filePath, read, size);
         SAFE_DELETE_ARRAY(buffer);
         return NULL;
     }
@@ -301,15 +399,9 @@ char* FileSystem::readAll(const char* filePath, int* fileSize)
     // Force the character buffer to be NULL-terminated.
     buffer[size] = '\0';
 
-    // Close file and return.
-    if (fclose(file) != 0)
-    {
-        GP_ERROR("Failed to close file '%s'.", filePath);
-    }
-
     if (fileSize)
     {
-        *fileSize = size; 
+        *fileSize = (int)size; 
     }
     return buffer;
 }
@@ -383,4 +475,279 @@ void FileSystem::createFileFromAsset(const char* path)
 #endif
 }
 
+//////////////////
+
+FileStream::FileStream(FILE* file)
+    : _file(file), _canRead(false), _canWrite(false)
+{
+    
+}
+
+FileStream::~FileStream()
+{
+    if (_file)
+    {
+        close();
+    }
+}
+
+FileStream* FileStream::create(const char* filePath, const char* mode)
+{
+    FILE* file = fopen(filePath, mode);
+    if (file)
+    {
+        FileStream* stream = new FileStream(file);
+        const char* s = mode;
+        while (s != NULL && *s != '\0')
+        {
+            if (*s == 'r')
+                stream->_canRead = true;
+            else if (*s == 'w')
+                stream->_canWrite = true;
+            ++s;
+        }
+
+        return stream;
+    }
+    return NULL;
+}
+
+bool FileStream::canRead()
+{
+    return _file && _canRead;
+}
+
+bool FileStream::canWrite()
+{
+    return _file && _canWrite;
+}
+
+bool FileStream::canSeek()
+{
+    return _file != NULL;
+}
+
+void FileStream::close()
+{
+    if (_file)
+        fclose(_file);
+    _file = NULL;
+}
+
+size_t FileStream::read(void* ptr, size_t size, size_t count)
+{
+    if (!_file)
+        return 0;
+    return fread(ptr, size, count, _file);
+}
+
+char* FileStream::readLine(char* str, int num)
+{
+    if (!_file)
+        return 0;
+    return fgets(str, num, _file);
+}
+
+size_t FileStream::write(const void* ptr, size_t size, size_t count)
+{
+    if (!_file)
+        return 0;
+    return fwrite(ptr, size, count, _file);
+}
+
+bool FileStream::eof()
+{
+    if (!_file || feof(_file))
+        return true;
+    return ((size_t)position()) >= length();
+}
+
+size_t FileStream::length()
+{
+    size_t len = 0;
+    if (canSeek())
+    {
+        long int pos = position();
+        if (seek(0, SEEK_END))
+        {
+            len = position();
+        }
+        seek(pos, SEEK_SET);
+    }
+    return len;
+}
+
+long int FileStream::position()
+{
+    if (!_file)
+        return -1;
+    return ftell(_file);
+}
+
+bool FileStream::seek(long int offset, int origin)
+{
+    if (!_file)
+        return false;
+    return fseek(_file, offset, origin) == 0;
+}
+
+bool FileStream::rewind()
+{
+    if (canSeek())
+    {
+        ::rewind(_file);
+        return true;
+    }
+    return false;
+}
+
+////////////////////////////////
+
+#ifdef __ANDROID__
+
+FileStreamAndroid::FileStreamAndroid(AAsset* asset)
+    : _asset(asset)
+{
+}
+
+FileStreamAndroid::~FileStreamAndroid()
+{
+    if (_asset)
+        close();
+}
+
+FileStreamAndroid* FileStreamAndroid::create(const char* filePath, const char* mode)
+{
+    AAsset* asset = AAssetManager_open(__assetManager, filePath, AASSET_MODE_RANDOM);
+    if (asset)
+    {
+        FileStreamAndroid* stream = new FileStreamAndroid(asset);
+        return stream;
+    }
+    return NULL;
+}
+
+bool FileStreamAndroid::canRead()
+{
+    return true;
+}
+
+bool FileStreamAndroid::canWrite()
+{
+    return false;
+}
+
+bool FileStreamAndroid::canSeek()
+{
+    return true;
+}
+
+void FileStreamAndroid::close()
+{
+    if (_asset)
+        AAsset_close(_asset);
+    _asset = NULL;
+}
+
+size_t FileStreamAndroid::read(void* ptr, size_t size, size_t count)
+{
+    int result = AAsset_read(_asset, ptr, size * count);
+    return result > 0 ? ((size_t)result) / size : 0;
+}
+
+char* FileStreamAndroid::readLine(char* str, int num)
+{
+    if (num <= 0)
+        return NULL;
+    char c = 0;
+    size_t maxCharsToRead = num - 1;
+    for (size_t i = 0; i < maxCharsToRead; ++i)
+    {
+        size_t result = read(&c, 1, 1);
+        if (result != 1)
+        {
+            str[i] = '\0';
+            break;
+        }
+        if (c == '\n')
+        {
+            str[i] = c;
+            str[i + 1] = '\0';
+            break;
+        }
+        else if(c == '\r')
+        {
+            str[i] = c;
+            // next may be '\n'
+            size_t pos = position();
+
+            char nextChar = 0;
+            if (read(&nextChar, 1, 1) != 1)
+            {
+                // no more characters
+                str[i + 1] = '\0';
+                break;
+            }
+            if (nextChar == '\n')
+            {
+                if (i == maxCharsToRead - 1)
+                {
+                    str[i + 1] = '\0';
+                    break;
+                }
+                else
+                {
+                    str[i + 1] = nextChar;
+                    str[i + 2] = '\0';
+                    break;
+                }
+            }
+            else
+            {
+                seek(pos, SEEK_SET);
+                str[i + 1] = '\0';
+                break;
+            }
+        }
+        str[i] = c;
+    }
+    return str; // what if first read failed?
+}
+
+size_t FileStreamAndroid::write(const void* ptr, size_t size, size_t count)
+{
+    return 0;
+}
+
+bool FileStreamAndroid::eof()
+{
+    return position() >= length();
+}
+
+size_t FileStreamAndroid::length()
+{
+    return (size_t)AAsset_getLength(_asset);
+}
+
+long int FileStreamAndroid::position()
+{
+    return AAsset_getLength(_asset) - AAsset_getRemainingLength(_asset);
+}
+
+bool FileStreamAndroid::seek(long int offset, int origin)
+{
+    return AAsset_seek(_asset, offset, origin) != -1;
+}
+
+bool FileStreamAndroid::rewind()
+{
+    if (canSeek())
+    {
+        return AAsset_seek(_asset, 0, SEEK_SET) != -1;
+    }
+    return false;
+}
+
+#endif
+
 }

+ 24 - 0
gameplay/src/FileSystem.h

@@ -1,6 +1,8 @@
 #ifndef FILESYSTEM_H_
 #define FILESYSTEM_H_
 
+#include "Stream.h"
+
 namespace gameplay
 {
 
@@ -13,6 +15,12 @@ class FileSystem
 {
 public:
 
+    enum StreamMode
+    {
+        READ = 1,
+        WRITE = 2
+    };
+
     /**
      * Destructor.
      */
@@ -107,6 +115,22 @@ public:
      */
     static bool fileExists(const char* filePath);
 
+    /**
+     * Opens a byte stream for the given resource path.
+     *
+     * If <code>path</code> is a file path, the file at the specified location is opened relative to the currently set
+     * resource path.
+     *
+     * @param path The path to the resource to be opened, relative to the currently set resource path.
+     * @param mode The mode used to open the file.
+     * 
+     * @return A stream that can be used to read or write to the file depending on the mode.
+     *         Returns NULL if there was an error. (Request mode not supported).
+     * 
+     * @script{ignore}
+     */
+    static Stream* open(const char* path, size_t mode = READ);
+
     /**
      * Opens the specified file.
      *

+ 13 - 28
gameplay/src/Image.cpp

@@ -4,14 +4,23 @@
 
 namespace gameplay
 {
+// Callback for reading a png image using Stream
+static void readStream(png_structp png, png_bytep data, png_size_t length)
+{
+    Stream* stream = reinterpret_cast<Stream*>(png_get_io_ptr(png));
+    if (stream == NULL || stream->read(data, 1, length) != length)
+    {
+        png_error(png, "Error reading PNG.");
+    }
+}
 
 Image* Image::create(const char* path)
 {
     GP_ASSERT(path);
 
     // Open the file.
-    FILE* fp = FileSystem::openFile(path, "rb");
-    if (fp == NULL)
+    std::auto_ptr<Stream> stream(FileSystem::open(path));
+    if (stream.get() == NULL || !stream->canRead())
     {
         GP_ERROR("Failed to open image file '%s'.", path);
         return NULL;
@@ -19,13 +28,9 @@ Image* Image::create(const char* path)
 
     // Verify PNG signature.
     unsigned char sig[8];
-    if (fread(sig, 1, 8, fp) != 8 || png_sig_cmp(sig, 0, 8) != 0)
+    if (stream->read(sig, 1, 8) != 8 || png_sig_cmp(sig, 0, 8) != 0)
     {
         GP_ERROR("Failed to load file '%s'; not a valid PNG.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close image file '%s'.", path);
-        }
         return NULL;
     }
 
@@ -34,10 +39,6 @@ Image* Image::create(const char* path)
     if (png == NULL)
     {
         GP_ERROR("Failed to create PNG structure for reading PNG file '%s'.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close image file '%s'.", path);
-        }
         return NULL;
     }
 
@@ -46,10 +47,6 @@ Image* Image::create(const char* path)
     if (info == NULL)
     {
         GP_ERROR("Failed to create PNG info structure for PNG file '%s'.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close image file '%s'.", path);
-        }
         png_destroy_read_struct(&png, NULL, NULL);
         return NULL;
     }
@@ -58,16 +55,12 @@ Image* Image::create(const char* path)
     if (setjmp(png_jmpbuf(png)))
     {
         GP_ERROR("Failed to set up error handling for reading PNG file '%s'.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close image file '%s'.", path);
-        }
         png_destroy_read_struct(&png, &info, NULL);
         return NULL;
     }
 
     // Initialize file io.
-    png_init_io(png, fp);
+    png_set_read_fn(png, stream.get(), readStream);
 
     // Indicate that we already read the first 8 bytes (signature).
     png_set_sig_bytes(png, 8);
@@ -92,10 +85,6 @@ Image* Image::create(const char* path)
 
     default:
         GP_ERROR("Unsupported PNG color type (%d) for image file '%s'.", (int)colorType, path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close image file '%s'.", path);
-        }
         png_destroy_read_struct(&png, &info, NULL);
         return NULL;
     }
@@ -114,10 +103,6 @@ Image* Image::create(const char* path)
 
     // Clean up.
     png_destroy_read_struct(&png, &info, NULL);
-    if (fclose(fp) != 0)
-    {
-        GP_ERROR("Failed to close image file '%s'.", path);
-    }
 
     return image;
 }

+ 11 - 5
gameplay/src/PhysicsController.cpp

@@ -6,7 +6,13 @@
 #include "MeshPart.h"
 #include "Bundle.h"
 
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#undef new
+#endif
 #include "BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h"
+#ifdef GAMEPLAY_MEM_LEAK_DETECTION
+#define new DEBUG_NEW
+#endif
 
 // The initial capacity of the Bullet debug drawer's vertex batch.
 #define INITIAL_CAPACITY 280
@@ -422,13 +428,13 @@ btScalar PhysicsController::CollisionCallback::addSingleResult(btManifoldPoint&
 
 void PhysicsController::initialize()
 {
-    _collisionConfiguration = new btDefaultCollisionConfiguration();
-    _dispatcher = new btCollisionDispatcher(_collisionConfiguration);
-    _overlappingPairCache = new btDbvtBroadphase();
-    _solver = new btSequentialImpulseConstraintSolver();
+    _collisionConfiguration = bullet_new<btDefaultCollisionConfiguration>();
+    _dispatcher = bullet_new<btCollisionDispatcher>(_collisionConfiguration);
+    _overlappingPairCache = bullet_new<btDbvtBroadphase>();
+    _solver = bullet_new<btSequentialImpulseConstraintSolver>();
 
     // Create the world.
-    _world = new btDiscreteDynamicsWorld(_dispatcher, _overlappingPairCache, _solver, _collisionConfiguration);
+    _world = bullet_new<btDiscreteDynamicsWorld>(_dispatcher, _overlappingPairCache, _solver, _collisionConfiguration);
     _world->setGravity(BV(_gravity));
 
     // Register ghost pair callback so bullet detects collisions with ghost objects (used for character collisions).

+ 4 - 4
gameplay/src/PhysicsGenericConstraint.cpp

@@ -23,11 +23,11 @@ PhysicsGenericConstraint::PhysicsGenericConstraint(PhysicsRigidBody* a, PhysicsR
     {
         GP_ASSERT(b->_body && b->getNode());
         Vector3 origin = centerOfMassMidpoint(a->getNode(), b->getNode());
-        _constraint = new btGeneric6DofConstraint(*a->_body, *b->_body, getTransformOffset(a->getNode(), origin), getTransformOffset(b->getNode(), origin), true);
+        _constraint = bullet_new<btGeneric6DofConstraint>(*a->_body, *b->_body, getTransformOffset(a->getNode(), origin), getTransformOffset(b->getNode(), origin), true);
     }
     else
     {
-        _constraint = new btGeneric6DofConstraint(*a->_body, btTransform::getIdentity(), true);
+        _constraint = bullet_new<btGeneric6DofConstraint>(*a->_body, btTransform::getIdentity(), true);
     }
 }
 
@@ -53,12 +53,12 @@ PhysicsGenericConstraint::PhysicsGenericConstraint(PhysicsRigidBody* a, const Qu
 
         btTransform frameInA(BQ(rotationOffsetA), BV(tA));
         btTransform frameInB(BQ(rotationOffsetB), BV(tB));
-        _constraint = new btGeneric6DofConstraint(*a->_body, *b->_body, frameInA, frameInB, true);
+        _constraint = bullet_new<btGeneric6DofConstraint>(*a->_body, *b->_body, frameInA, frameInB, true);
     }
     else
     {
         btTransform frameInA(BQ(rotationOffsetA), BV(tA));
-        _constraint = new btGeneric6DofConstraint(*a->_body, frameInA, true);
+        _constraint = bullet_new<btGeneric6DofConstraint>(*a->_body, frameInA, true);
     }
 }
 

+ 2 - 2
gameplay/src/PhysicsHingeConstraint.cpp

@@ -34,12 +34,12 @@ PhysicsHingeConstraint::PhysicsHingeConstraint(PhysicsRigidBody* a, const Quater
 
         btTransform frameInA(BQ(rotationOffsetA), BV(tA));
         btTransform frameInB(BQ(rotationOffsetB), BV(tB));
-        _constraint = new btHingeConstraint(*a->_body, *b->_body, frameInA, frameInB);
+        _constraint = bullet_new<btHingeConstraint>(*a->_body, *b->_body, frameInA, frameInB);
     }
     else
     {
         btTransform frameInA(BQ(rotationOffsetA), BV(tA));
-        _constraint = new btHingeConstraint(*a->_body, frameInA);
+        _constraint = bullet_new<btHingeConstraint>(*a->_body, frameInA);
     }
 }
 

+ 4 - 4
gameplay/src/PhysicsSocketConstraint.cpp

@@ -16,11 +16,11 @@ PhysicsSocketConstraint::PhysicsSocketConstraint(PhysicsRigidBody* a, PhysicsRig
         btTransform frameInA = getTransformOffset(a->getNode(), origin);
         btTransform frameInB = getTransformOffset(b->getNode(), origin);
 
-        _constraint = new btPoint2PointConstraint(*a->_body, *b->_body, frameInA.getOrigin(), frameInB.getOrigin());
+        _constraint = bullet_new<btPoint2PointConstraint>(*a->_body, *b->_body, frameInA.getOrigin(), frameInB.getOrigin());
     }
     else
     {
-        _constraint = new btPoint2PointConstraint(*a->_body, btVector3(0.0f, 0.0f, 0.0f));
+        _constraint = bullet_new<btPoint2PointConstraint>(*a->_body, btVector3(0.0f, 0.0f, 0.0f));
     }
 }
 
@@ -44,11 +44,11 @@ PhysicsSocketConstraint::PhysicsSocketConstraint(PhysicsRigidBody* a, const Vect
         b->getNode()->getWorldMatrix().getScale(&sB);
         Vector3 tB(translationOffsetB.x * sB.x, translationOffsetB.y * sB.y, translationOffsetB.z * sB.z);
 
-        _constraint = new btPoint2PointConstraint(*a->_body, *b->_body, BV(tA), BV(tB));
+        _constraint = bullet_new<btPoint2PointConstraint>(*a->_body, *b->_body, BV(tA), BV(tB));
     }
     else
     {
-        _constraint = new btPoint2PointConstraint(*a->_body, BV(tA));
+        _constraint = bullet_new<btPoint2PointConstraint>(*a->_body, BV(tA));
     }
 }
 

+ 2 - 2
gameplay/src/PhysicsSpringConstraint.cpp

@@ -16,7 +16,7 @@ PhysicsSpringConstraint::PhysicsSpringConstraint(PhysicsRigidBody* a, PhysicsRig
     _b = b;
 
     Vector3 origin = centerOfMassMidpoint(a->getNode(), b->getNode());
-    _constraint = new btGeneric6DofSpringConstraint(*a->_body, *b->_body, getTransformOffset(a->getNode(), origin), getTransformOffset(b->getNode(), origin), true);
+    _constraint = bullet_new<btGeneric6DofSpringConstraint>(*a->_body, *b->_body, getTransformOffset(a->getNode(), origin), getTransformOffset(b->getNode(), origin), true);
 }
 
 PhysicsSpringConstraint::PhysicsSpringConstraint(PhysicsRigidBody* a, const Quaternion& rotationOffsetA, const Vector3& translationOffsetA,
@@ -40,7 +40,7 @@ PhysicsSpringConstraint::PhysicsSpringConstraint(PhysicsRigidBody* a, const Quat
 
     btTransform frameInA(BQ(rotationOffsetA), BV(tA));
     btTransform frameInB(BQ(rotationOffsetB), BV(tB));
-    _constraint = new btGeneric6DofSpringConstraint(*a->_body, *b->_body, frameInA, frameInB, true);
+    _constraint = bullet_new<btGeneric6DofSpringConstraint>(*a->_body, *b->_body, frameInA, frameInB, true);
 }
 
 PhysicsSpringConstraint::~PhysicsSpringConstraint()

+ 1 - 1
gameplay/src/PhysicsVehicle.cpp

@@ -184,7 +184,7 @@ void PhysicsVehicle::initialize()
     btRigidBody* body = static_cast<btRigidBody*>(_rigidBody->getCollisionObject());
     btDynamicsWorld* dynamicsWorld = Game::getInstance()->getPhysicsController()->_world;
     _vehicleRaycaster = new VehicleNotMeRaycaster(dynamicsWorld, body);
-    _vehicle = new btRaycastVehicle(_vehicleTuning, body, _vehicleRaycaster);
+    _vehicle = bullet_new<btRaycastVehicle>(_vehicleTuning, body, _vehicleRaycaster);
     body->setActivationState(DISABLE_DEACTIVATION);
     dynamicsWorld->addVehicle(_vehicle);
     _vehicle->setCoordinateSystem(0, 1, 2);

+ 48 - 35
gameplay/src/Properties.cpp

@@ -6,6 +6,19 @@
 namespace gameplay
 {
 
+/**
+ * Reads the next character from the stream. Returns EOF if the end of the stream is reached.
+ */
+static signed char readChar(Stream* stream)
+{
+    if (stream->eof())
+        return EOF;
+    signed char c;
+    if (stream->read(&c, 1, 1) != 1)
+        return EOF;
+    return c;
+}
+
 // Utility functions (shared with SceneLoader).
 /** @script{ignore} */
 void calculateNamespacePath(const std::string& urlString, std::string& fileString, std::vector<std::string>& namespacePath);
@@ -29,13 +42,14 @@ Properties::Properties(const Properties& copy)
     rewind();
 }
 
-Properties::Properties(FILE* file)
+
+Properties::Properties(Stream* stream)
 {
-    readProperties(file);
+    readProperties(stream);
     rewind();
 }
 
-Properties::Properties(FILE* file, const char* name, const char* id, const char* parentID) : _namespace(name)
+Properties::Properties(Stream* stream, const char* name, const char* id, const char* parentID) : _namespace(name)
 {
     if (id)
     {
@@ -45,7 +59,7 @@ Properties::Properties(FILE* file, const char* name, const char* id, const char*
     {
         _parentID = parentID;
     }
-    readProperties(file);
+    readProperties(stream);
     rewind();
 }
 
@@ -63,16 +77,16 @@ Properties* Properties::create(const char* url)
     std::vector<std::string> namespacePath;
     calculateNamespacePath(urlString, fileString, namespacePath);
 
-    FILE* file = FileSystem::openFile(fileString.c_str(), "rb");
-    if (!file)
+    std::auto_ptr<Stream> stream(FileSystem::open(fileString.c_str()));
+    if (stream.get() == NULL)
     {
         GP_ERROR("Failed to open file '%s'.", fileString.c_str());
         return NULL;
     }
 
-    Properties* properties = new Properties(file);
+    Properties* properties = new Properties(stream.get());
     properties->resolveInheritance();
-    fclose(file);
+    stream->close();
 
     // Get the specified properties object.
     Properties* p = getPropertiesFromNamespacePath(properties, namespacePath);
@@ -93,9 +107,9 @@ Properties* Properties::create(const char* url)
     return p;
 }
 
-void Properties::readProperties(FILE* file)
+void Properties::readProperties(Stream* stream)
 {
-    GP_ASSERT(file);
+    GP_ASSERT(stream);
 
     char line[2048];
     int c;
@@ -108,14 +122,14 @@ void Properties::readProperties(FILE* file)
 
     while (true)
     {
-        skipWhiteSpace(file);
+        skipWhiteSpace(stream);
 
         // Stop when we have reached the end of the file.
-        if (feof(file))
+        if (stream->eof())
             break;
 
         // Read the next line.
-        rc = fgets(line, 2048, file);
+        rc = stream->readLine(line, 2048);
         if (rc == NULL)
         {
             GP_ERROR("Error reading line from file.");
@@ -213,20 +227,20 @@ void Properties::readProperties(FILE* file)
                     // If the namespace ends on this line, seek back to right before the '}' character.
                     if (rccc && rccc == lineEnd)
                     {
-                        if (fseek(file, -1, SEEK_CUR) != 0)
+                        if (stream->seek(-1, SEEK_CUR) == false)
                         {
                             GP_ERROR("Failed to seek back to before a '}' character in properties file.");
                             return;
                         }
-                        while (fgetc(file) != '}')
+                        while (readChar(stream) != '}')
                         {
-                            if (fseek(file, -2, SEEK_CUR) != 0)
+                            if (stream->seek(-2, SEEK_CUR) == false)
                             {
                                 GP_ERROR("Failed to seek back to before a '}' character in properties file.");
                                 return;
                             }
                         }
-                        if (fseek(file, -1, SEEK_CUR) != 0)
+                        if (stream->seek(-1, SEEK_CUR) == false)
                         {
                             GP_ERROR("Failed to seek back to before a '}' character in properties file.");
                             return;
@@ -234,13 +248,13 @@ void Properties::readProperties(FILE* file)
                     }
 
                     // New namespace without an ID.
-                    Properties* space = new Properties(file, name, NULL, parentID);
+                    Properties* space = new Properties(stream, name, NULL, parentID);
                     _namespaces.push_back(space);
 
                     // If the namespace ends on this line, seek to right after the '}' character.
                     if (rccc && rccc == lineEnd)
                     {
-                        if (fseek(file, 1, SEEK_CUR) != 0)
+                        if (stream->seek(1, SEEK_CUR) == false)
                         {
                             GP_ERROR("Failed to seek to immediately after a '}' character in properties file.");
                             return;
@@ -255,20 +269,20 @@ void Properties::readProperties(FILE* file)
                         // If the namespace ends on this line, seek back to right before the '}' character.
                         if (rccc && rccc == lineEnd)
                         {
-                            if (fseek(file, -1, SEEK_CUR) != 0)
+                            if (stream->seek(-1, SEEK_CUR) == false)
                             {
                                 GP_ERROR("Failed to seek back to before a '}' character in properties file.");
                                 return;
                             }
-                            while (fgetc(file) != '}')
+                            while (readChar(stream) != '}')
                             {
-                                if (fseek(file, -2, SEEK_CUR) != 0)
+                                if (stream->seek(-2, SEEK_CUR) == false)
                                 {
                                     GP_ERROR("Failed to seek back to before a '}' character in properties file.");
                                     return;
                                 }
                             }
-                            if (fseek(file, -1, SEEK_CUR) != 0)
+                            if (stream->seek(-1, SEEK_CUR) == false)
                             {
                                 GP_ERROR("Failed to seek back to before a '}' character in properties file.");
                                 return;
@@ -276,13 +290,13 @@ void Properties::readProperties(FILE* file)
                         }
 
                         // Create new namespace.
-                        Properties* space = new Properties(file, name, value, parentID);
+                        Properties* space = new Properties(stream, name, value, parentID);
                         _namespaces.push_back(space);
 
                         // If the namespace ends on this line, seek to right after the '}' character.
                         if (rccc && rccc == lineEnd)
                         {
-                            if (fseek(file, 1, SEEK_CUR) != 0)
+                            if (stream->seek(1, SEEK_CUR) == false)
                             {
                                 GP_ERROR("Failed to seek to immediately after a '}' character in properties file.");
                                 return;
@@ -292,18 +306,18 @@ void Properties::readProperties(FILE* file)
                     else
                     {
                         // Find out if the next line starts with "{"
-                        skipWhiteSpace(file);
-                        c = fgetc(file);
+                        skipWhiteSpace(stream);
+                        c = readChar(stream);
                         if (c == '{')
                         {
                             // Create new namespace.
-                            Properties* space = new Properties(file, name, value, parentID);
+                            Properties* space = new Properties(stream, name, value, parentID);
                             _namespaces.push_back(space);
                         }
                         else
                         {
                             // Back up from fgetc()
-                            if (fseek(file, -1, SEEK_CUR) != 0)
+                            if (stream->seek(-1, SEEK_CUR) == false)
                                 GP_ERROR("Failed to seek backwards a single character after testing if the next line starts with '{'.");
 
                             // Store "name value" as a name/value pair, or even just "name".
@@ -331,20 +345,19 @@ Properties::~Properties()
     }
 }
 
-void Properties::skipWhiteSpace(FILE* file)
+void Properties::skipWhiteSpace(Stream* stream)
 {
-    int c;
-
+    signed char c;
     do
     {
-        c = fgetc(file);
-    } while (isspace(c));
+        c = readChar(stream);
+    } while (isspace(c) && c != EOF);
 
     // If we are not at the end of the file, then since we found a
     // non-whitespace character, we put the cursor back in front of it.
     if (c != EOF)
     {
-        if (fseek(file, -1, SEEK_CUR) != 0)
+        if (stream->seek(-1, SEEK_CUR) == false)
         {
             GP_ERROR("Failed to seek backwards one character after skipping whitespace.");
         }

+ 5 - 4
gameplay/src/Properties.h

@@ -4,6 +4,7 @@
 #include "Base.h"
 #include "Matrix.h"
 #include "Vector2.h"
+#include "Stream.h"
 
 namespace gameplay
 {
@@ -383,17 +384,17 @@ private:
      * Constructors.
      */
     Properties();
-    Properties(FILE* file);
+    Properties(Stream* stream);
     Properties(const Properties& copy);
 
     /**
      * Constructor. Read from the beginning of namespace specified
      */
-    Properties(FILE* file, const char* name, const char* id = NULL, const char* parentID = NULL);
+    Properties(Stream* stream, const char* name, const char* id = NULL, const char* parentID = NULL);
 
-    void readProperties(FILE* file);
+    void readProperties(Stream* stream);
 
-    void skipWhiteSpace(FILE* file);
+    void skipWhiteSpace(Stream* stream);
 
     char* trimWhiteSpace(char* str);
 

+ 167 - 0
gameplay/src/Stream.h

@@ -0,0 +1,167 @@
+#ifndef Stream_H_
+#define Stream_H_
+
+namespace gameplay
+{
+
+/**
+ * Stream is an interface for reading and writing a sequence of bytes.
+ * 
+ * Use FileSystem::open() to create a stream.
+ * 
+ * @script{ignore}
+ */
+class Stream
+{
+public:
+
+    /**
+     * Destructor. The stream should be closed when it is destroyed.
+     */
+    virtual ~Stream() {};
+
+    /**
+     * Returns true if this stream can preform read operations.
+     * 
+     * @return True if the stream can read, false otherwise.
+     */
+    virtual bool canRead() = 0;
+
+    /**
+     * Returns true if this stream can perform write operations.
+     * 
+     * @return True if the stream can write, false otherwise.
+     */
+    virtual bool canWrite() = 0;
+
+    /**
+     * Returns true if this stream can seek.
+     * 
+     * @return True if the stream can seek, false otherwise.
+     */
+    virtual bool canSeek() = 0;
+
+    /**
+     * Closes this stream.
+     */
+    virtual void close() = 0;
+    
+    /**
+     * Reads an array of <code>count</code> elements, each of size <code>size</code>.
+     * 
+     * \code
+     * int numbers[3];
+     * if (stream->read(numbers, sizeof(int), 3) != 3)
+     *     print("Error reading from file");
+     * \endcode
+     * 
+     * @param ptr   The pointer to the memory to copy into.
+     *              The available size should be at least (<code>size * count</code>) bytes.
+     * @param size  The size of each element to be read, in bytes.
+     * @param count The number of elements to read.
+     * 
+     * @return The number of elements read.
+     * 
+     * @see canRead()
+     */
+    virtual size_t read(void* ptr, size_t size, size_t count) = 0;
+
+    /**
+     * Reads a line from the stream.
+     * 
+     * A new line is denoted by by either "\n", "\r" or "\r\n".
+     * The line break character is included in the string.
+     * The terminating null character is added to the end of the string.
+     * 
+     * @param str The array of chars to copy the string to.
+     * @param num The maximum number of characters to be copied.
+     * 
+     * @return On success, str is returned. On error, NULL is returned.
+     * 
+     * @see canRead()
+     */
+    virtual char* readLine(char* str, int num) = 0;
+    
+    /**
+     * Writes an array of <code>count</code> elements, each of size <code>size</code>.
+     * 
+     * \code
+     * int numbers[] = {1, 2, 3};
+     * if (stream->write(numbers, sizeof(int), 3) != 3)
+     *     print("Error writing to file");
+     * \endcode
+     * 
+     * @param ptr   The poiner to the array of elements to be written.
+     * @param size  The size of each element to be written, in bytes.
+     * @param count The number of elements to write.
+     * 
+     * @return The number of elements written.
+     * 
+     * @see canWrite()
+     */
+    virtual size_t write(const void* ptr, size_t size, size_t count) = 0;
+
+    /**
+     * Returns true if the end of the stream has been reached.
+     * 
+     * @return True if end of stream reached, false otherwise.
+     */
+    virtual bool eof() = 0;
+
+    /**
+     * Returns the length of the stream in bytes.
+     * 
+     * Zero is returned if the length of the stream is unknown and/or it cannot be seeked. 
+     * 
+     * Example: The length of a network stream is unknown and cannot be seeked.
+     * 
+     * @return The length of the stream in bytes.
+     */
+    virtual size_t length() = 0;
+
+    /**
+     * Returns the position of the file pointer. Zero is the start of the stream.
+     * 
+     * @return The file indicator offset in bytes. 
+     */
+    virtual long int position() = 0;
+
+    /**
+     * Sets the position of the file pointer.
+     * 
+     * Use canSeek() to determine if this method is supported.
+     * 
+     * @param offset The number of bytes to offset from origin.
+     * @param origin The position used as a reference for offset.
+     *               The supported vaules are the same as fseek().
+     *                - <code>SEEK_SET</code> relative to the beginning of the file.
+     *                - <code>SEEK_CUR</code> relative to the current position of the file pointer.
+     *                - <code>SEEK_END</code> relative to the end of file.
+     * 
+     * @return True if successful, false otherwise.
+     * 
+     * @see canSeek()
+     */
+    virtual bool seek(long int offset, int origin) = 0;
+
+    /**
+     * Moves the file pointer to the start of the file.
+     * 
+     * Use canSeek() to determine if this method is supported.
+     * 
+     * @return True if successful, false otherwise.
+     * 
+     * @see canSeek()
+     */
+    virtual bool rewind() = 0;
+
+protected:
+    Stream() {};
+private:
+    Stream(const Stream&);            // Hidden copy constructor.
+    Stream& operator=(const Stream&); // Hidden copy assignment operator.
+};
+
+}
+
+#endif

+ 21 - 89
gameplay/src/Texture.cpp

@@ -220,8 +220,8 @@ static unsigned int computePVRTCDataSize(int width, int height, int bpp)
 
 Texture* Texture::createCompressedPVRTC(const char* path)
 {
-    FILE* file = FileSystem::openFile(path, "rb");
-    if (file == NULL)
+    std::auto_ptr<Stream> stream(FileSystem::open(path));
+    if (stream.get() == NULL || !stream->canRead())
     {
         GP_ERROR("Failed to load file '%s'.", path);
         return NULL;
@@ -230,27 +230,17 @@ Texture* Texture::createCompressedPVRTC(const char* path)
     // Read first 4 bytes to determine PVRTC format.
     size_t read;
     unsigned int version;
-    read = fread(&version, sizeof(unsigned int), 1, file);
+    read = stream->read(&version, sizeof(unsigned int), 1);
     if (read != 1)
     {
         GP_ERROR("Failed to read PVR version.");
-        if (fclose(file) != 0)
-        {
-            GP_ERROR("Failed to close PVR file '%s'.", path);
-            return NULL;
-        }
         return NULL;
     }
 
     // Rewind to start of header.
-    if (fseek(file, 0, SEEK_SET) != 0)
+    if (stream->seek(0, SEEK_SET) == false)
     {
         GP_ERROR("Failed to seek backwards to beginning of file after reading PVR version.");
-        if (fclose(file) != 0)
-        {
-            GP_ERROR("Failed to close PVR file '%s'.", path);
-            return NULL;
-        }
         return NULL;
     }
 
@@ -263,29 +253,19 @@ Texture* Texture::createCompressedPVRTC(const char* path)
     if (version == 0x03525650)
     {
         // Modern PVR file format.
-        data = readCompressedPVRTC(path, file, &width, &height, &format, &mipMapCount);
+        data = readCompressedPVRTC(path, stream.get(), &width, &height, &format, &mipMapCount);
     }
     else
     {
         // Legacy PVR file format.
-        data = readCompressedPVRTCLegacy(path, file, &width, &height, &format, &mipMapCount);
+        data = readCompressedPVRTCLegacy(path, stream.get(), &width, &height, &format, &mipMapCount);
     }
     if (data == NULL)
     {
         GP_ERROR("Failed to read texture data from PVR file '%s'.", path);
-        if (fclose(file) != 0)
-        {
-            GP_ERROR("Failed to close PVR file '%s'.", path);
-            return NULL;
-        }
-        return NULL;
-    }
-
-    if (fclose(file) != 0)
-    {
-        GP_ERROR("Failed to close PVR file '%s'.", path);
         return NULL;
     }
+    stream->close();
 
     int bpp = (format == GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG || format == GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG) ? 2 : 4;
 
@@ -322,9 +302,9 @@ Texture* Texture::createCompressedPVRTC(const char* path)
     return texture;
 }
 
-GLubyte* Texture::readCompressedPVRTC(const char* path, FILE* file, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount)
+GLubyte* Texture::readCompressedPVRTC(const char* path, Stream* stream, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount)
 {
-    GP_ASSERT(file);
+    GP_ASSERT(stream);
     GP_ASSERT(path);
     GP_ASSERT(width);
     GP_ASSERT(height);
@@ -351,7 +331,7 @@ GLubyte* Texture::readCompressedPVRTC(const char* path, FILE* file, GLsizei* wid
 
     // Read header data.
     pvrtc_file_header header;
-    read = fread(&header, sizeof(pvrtc_file_header), 1, file);
+    read = stream->read(&header, sizeof(pvrtc_file_header), 1);
     if (read != 1)
     {
         GP_ERROR("Failed to read PVR header data for file '%s'.", path);
@@ -395,7 +375,7 @@ GLubyte* Texture::readCompressedPVRTC(const char* path, FILE* file, GLsizei* wid
     *mipMapCount = header.mipMapCount;
 
     // Skip meta-data.
-    if (fseek(file, header.metaDataSize, SEEK_CUR) != 0)
+    if (stream->seek(header.metaDataSize, SEEK_CUR) == false)
     {
         GP_ERROR("Failed to seek past header meta data in PVR file '%s'.", path);
         return NULL;
@@ -414,7 +394,7 @@ GLubyte* Texture::readCompressedPVRTC(const char* path, FILE* file, GLsizei* wid
 
     // Read data.
     GLubyte* data = new GLubyte[dataSize];
-    read = fread(data, 1, dataSize, file);
+    read = stream->read(data, 1, dataSize);
     if (read != dataSize)
     {
         SAFE_DELETE_ARRAY(data);
@@ -425,7 +405,7 @@ GLubyte* Texture::readCompressedPVRTC(const char* path, FILE* file, GLsizei* wid
     return data;
 }
 
-GLubyte* Texture::readCompressedPVRTCLegacy(const char* path, FILE* file, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount)
+GLubyte* Texture::readCompressedPVRTCLegacy(const char* path, Stream* stream, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount)
 {
     char PVRTCIdentifier[] = "PVR!";
 
@@ -449,14 +429,10 @@ GLubyte* Texture::readCompressedPVRTCLegacy(const char* path, FILE* file, GLsize
     // Read the file header.
     unsigned int size = sizeof(pvrtc_file_header_legacy);
     pvrtc_file_header_legacy header;
-    unsigned int read = (int)fread(&header, 1, size, file);
+    unsigned int read = (int)stream->read(&header, 1, size);
     if (read != size)
     {
         GP_ERROR("Failed to read file header for pvrtc file '%s'.", path);
-        if (fclose(file) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         return NULL;
     }
 
@@ -467,10 +443,6 @@ GLubyte* Texture::readCompressedPVRTCLegacy(const char* path, FILE* file, GLsize
         PVRTCIdentifier[3] != (char)((header.pvrtcTag >> 24) & 0xff))
      {
         GP_ERROR("Failed to load pvrtc file '%s': invalid header.", path);
-        if (fclose(file) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         return NULL;
     }
 
@@ -486,10 +458,6 @@ GLubyte* Texture::readCompressedPVRTCLegacy(const char* path, FILE* file, GLsize
     else
     {
         GP_ERROR("Failed to load pvrtc file '%s': invalid pvrtc compressed texture format flags.", path);
-        if (fclose(file) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         return NULL;
     }
 
@@ -498,14 +466,10 @@ GLubyte* Texture::readCompressedPVRTCLegacy(const char* path, FILE* file, GLsize
     *mipMapCount = header.mipmapCount + 1; // +1 because mipmapCount does not include the base level
 
     GLubyte* data = new GLubyte[header.dataSize];
-    read = (int)fread(data, 1, header.dataSize, file);
+    read = (int)stream->read(data, 1, header.dataSize);
     if (read != header.dataSize)
     {
         GP_ERROR("Failed to load texture data for pvrtc file '%s'.", path);
-        if (fclose(file) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         SAFE_DELETE_ARRAY(data);
         return NULL;
     }
@@ -559,8 +523,8 @@ Texture* Texture::createCompressedDDS(const char* path)
     Texture* texture = NULL;
 
     // Read DDS file.
-    FILE* fp = FileSystem::openFile(path, "rb");
-    if (fp == NULL)
+    std::auto_ptr<Stream> stream(FileSystem::open(path));
+    if (stream.get() == NULL || !stream->canRead())
     {
         GP_ERROR("Failed to open file '%s'.", path);
         return NULL;
@@ -568,25 +532,17 @@ Texture* Texture::createCompressedDDS(const char* path)
 
     // Validate DDS magic number.
     char code[4];
-    if (fread(code, 1, 4, fp) != 4 || strncmp(code, "DDS ", 4) != 0)
+    if (stream->read(code, 1, 4) != 4 || strncmp(code, "DDS ", 4) != 0)
     {
         GP_ERROR("Failed to read DDS file '%s': invalid DDS magic number.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         return NULL;
     }
 
     // Read DDS header.
     dds_header header;
-    if (fread(&header, sizeof(dds_header), 1, fp) != 1)
+    if (stream->read(&header, sizeof(dds_header), 1) != 1)
     {
         GP_ERROR("Failed to read header for DDS file '%s'.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         return NULL;
     }
 
@@ -639,10 +595,6 @@ Texture* Texture::createCompressedDDS(const char* path)
             break;
         default:
             GP_ERROR("Unsupported compressed texture format (%d) for DDS file '%s'.", header.ddspf.dwFourCC, path);
-            if (fclose(fp) != 0)
-            {
-                GP_ERROR("Failed to close file '%s'.", path);
-            }
             SAFE_DELETE_ARRAY(mipLevels);
             return NULL;
         }
@@ -654,7 +606,7 @@ Texture* Texture::createCompressedDDS(const char* path)
             mipLevels[i].size =  std::max(1, (width+3) >> 2) * std::max(1, (height+3) >> 2) * bytesPerBlock;
             mipLevels[i].data = new GLubyte[mipLevels[i].size];
 
-            if (fread(mipLevels[i].data, 1, mipLevels[i].size, fp) != (unsigned int)mipLevels[i].size)
+            if (stream->read(mipLevels[i].data, 1, mipLevels[i].size) != (unsigned int)mipLevels[i].size)
             {
                 GP_ERROR("Failed to load dds compressed texture bytes for texture: %s", path);
                 
@@ -662,11 +614,6 @@ Texture* Texture::createCompressedDDS(const char* path)
                 for (unsigned int i = 0; i < header.dwMipMapCount; ++i)
                     SAFE_DELETE_ARRAY(mipLevels[i].data);
                 SAFE_DELETE_ARRAY(mipLevels);
-
-                if (fclose(fp) != 0)
-                {
-                    GP_ERROR("Failed to close file '%s'.", path);
-                }
                 return texture;
             }
 
@@ -679,10 +626,6 @@ Texture* Texture::createCompressedDDS(const char* path)
         // RGB (uncompressed)
         // Note: Use GL_BGR as internal format to flip bytes.
         GP_ERROR("Failed to create texture from DDS file '%s': uncompressed RGB format is not supported.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         SAFE_DELETE_ARRAY(mipLevels);
         return NULL;
     }
@@ -691,10 +634,6 @@ Texture* Texture::createCompressedDDS(const char* path)
         // RGBA (uncompressed)
         // Note: Use GL_BGRA as internal format to flip bytes.
         GP_ERROR("Failed to create texture from DDS file '%s': uncompressed RGBA format is not supported.", path);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         SAFE_DELETE_ARRAY(mipLevels);
         return NULL;
     }
@@ -702,19 +641,12 @@ Texture* Texture::createCompressedDDS(const char* path)
     {
         // Unsupported.
         GP_ERROR("Failed to create texture from DDS file '%s': unsupported flags (%d).", path, header.ddspf.dwFlags);
-        if (fclose(fp) != 0)
-        {
-            GP_ERROR("Failed to close file '%s'.", path);
-        }
         SAFE_DELETE_ARRAY(mipLevels);
         return NULL;
     }
     
     // Close file.
-    if (fclose(fp) != 0)
-    {
-        GP_ERROR("Failed to close file '%s'.", path);
-    }
+    stream->close();
 
     // Generate GL texture.
     GLuint textureId;

+ 3 - 2
gameplay/src/Texture.h

@@ -2,6 +2,7 @@
 #define TEXTURE_H_
 
 #include "Ref.h"
+#include "Stream.h"
 
 namespace gameplay
 {
@@ -285,9 +286,9 @@ private:
 
     static Texture* createCompressedDDS(const char* path);
 
-    static GLubyte* readCompressedPVRTC(const char* path, FILE* file, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount);
+    static GLubyte* readCompressedPVRTC(const char* path, Stream* stream, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount);
 
-    static GLubyte* readCompressedPVRTCLegacy(const char* path, FILE* file, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount);
+    static GLubyte* readCompressedPVRTCLegacy(const char* path, Stream* stream, GLsizei* width, GLsizei* height, GLenum* format, unsigned int* mipMapCount);
 
     std::string _path;
     TextureHandle _handle;