Browse Source

Merge branch 'dev-continue-download' into new-render

# Conflicts:
#	oxygine/src/oxygine/AsyncTask.cpp
#	oxygine/src/oxygine/AsyncTask.h
#	oxygine/src/oxygine/HttpRequestTask.cpp
#	oxygine/src/oxygine/HttpRequestTask.h
#	oxygine/src/oxygine/core/ZipFileSystem.h
#	oxygine/src/oxygine/core/curl/HttpRequestCurlTask.cpp
dmuratshin 8 years ago
parent
commit
b8611bcb74

+ 1 - 1
examples/Demo/src/test.cpp

@@ -245,4 +245,4 @@ void Test::notifyDone(Event* ev)
 {
     size_t N = size_t(ev->target->getUserData());
     _notifies[N] -= 1;
-}
+}

+ 1 - 1
oxygine/SDL/android/lib/genNative.bat

@@ -1,2 +1,2 @@
-javah -force -jni -classpath bin/classes -o f.h org.oxygine.lib.HttpRequest.nativeHttpRequestResponseSuccess
+javah -force -jni -classpath build\intermediates\classes\debug\org\oxygine\lib\ -o f.h org.oxygine.lib.HttpRequest.nativeHttpRequestResponseSuccess
 PAUSE

BIN
oxygine/SDL/android/lib/gradle/wrapper/gradle-wrapper.jar


+ 6 - 0
oxygine/SDL/android/lib/gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,6 @@
+#Fri Feb 03 15:44:28 YEKT 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-bin.zip

+ 90 - 0
oxygine/SDL/android/lib/gradlew.bat

@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 1 - 1
oxygine/SDL/android/lib/javapp.bat

@@ -1,3 +1,3 @@
 
-javap -s -p -classpath bin\classes\org\oxygine\lib\ HttpRequests
+javap -s -p -classpath build\intermediates\classes\debug\org\oxygine\lib\ HttpRequests
 PAUSE

+ 33 - 124
oxygine/SDL/android/lib/src/org/oxygine/lib/HttpRequests.java

@@ -21,9 +21,10 @@ import java.net.URL;
 
 class RequestDetails {
     public String url;
-    public String fileName;
     public byte[] postData;
     public long handle;
+    public String[] headerKeys;
+    public String[] headerValues;
 };
 
 
@@ -65,11 +66,11 @@ class HttpRequest extends AsyncTask<RequestDetails, Integer, String> {
     public HttpRequest() {
     }
 
-    public static native void nativeHttpRequestResponseSuccess(long handle, byte[] data, int code);
+    public static native void nativeHttpRequestSuccess(long handle);
+    public static native void nativeHttpRequestError(long handle);
+    public static native void nativeHttpRequestGotHeader(long handle, int code, int contentLength);
+    public static native void nativeHttpRequestWrite(long handle, byte[] data, int size);
 
-    public static native void nativeHttpRequestResponseProgress(long handle, int loaded, int total);
-
-    public static native void nativeHttpRequestResponseError(long handle, int code);
 
     private Proxy detectProxy() {
         try {
@@ -91,14 +92,14 @@ class HttpRequest extends AsyncTask<RequestDetails, Integer, String> {
     @Override
     protected String doInBackground(RequestDetails... details_) {
         InputStream input = null;
-        OutputStream output = null;
+
         HttpURLConnection connection = null;
         RequestDetails details = details_[0];
 
-        int respCode = 0;
 
         try {
             URL url = new URL(details.url);
+
             Proxy proxy = detectProxy();
             if (proxy != null)
                 connection = (HttpURLConnection) url.openConnection(proxy);
@@ -106,108 +107,48 @@ class HttpRequest extends AsyncTask<RequestDetails, Integer, String> {
                 connection = (HttpURLConnection) url.openConnection();
 
             connection.setInstanceFollowRedirects(true);
-            
+
+            for (int i = 0; i < details.headerKeys.length; ++i)
+            {
+                String key = details.headerKeys[i];
+                String value = details.headerValues[i];
+
+                connection.setRequestProperty(key, value);
+            }
+
             if (details.postData != null) {
                 connection.setDoOutput(true);
-                //connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                 connection.setRequestMethod("POST");
                 connection.getOutputStream().write(details.postData);
             }
 
-            //connection.connect();
-
-            /*
-
-            boolean redirect = false;
-
-            int status = conn.getResponseCode();
-            if (status != HttpURLConnection.HTTP_OK) {
-                if (status == HttpURLConnection.HTTP_MOVED_TEMP || status == HttpURLConnection.HTTP_MOVED_PERM || status == HttpURLConnection.HTTP_SEE_OTHER)
-                    redirect = true;
-            }
-
-            if (redirect) {
 
-                // get redirect url from "location" header field
-                String newUrl = conn.getHeaderField("Location");
 
-                // get the cookie if need, for login
-                String cookies = conn.getHeaderField("Set-Cookie");
+            int respCode = connection.getResponseCode();
+            nativeHttpRequestGotHeader(details.handle, respCode, connection.getContentLength());
 
-                // open the new connnection again
-                conn = (HttpURLConnection) new URL(newUrl).openConnection();
-                conn.setRequestProperty("Cookie", cookies);
-                conn.addRequestProperty("Accept-Language", "en-US,en;q=0.8");
-                conn.addRequestProperty("User-Agent", "Mozilla");
-                conn.addRequestProperty("Referer", "google.com");
 
-                System.out.println("Redirect to URL : " + newUrl);
-            }
-            */
-
-            // expect HTTP 200 OK, so we don't mistakenly save error report
-            // instead of the file
-            respCode = connection.getResponseCode();
-            //if (respCode != HttpURLConnection.HTTP_OK) {
-            //    nativeHttpRequestResponseError(details.handle, respCode);
-            //    return "Server returned HTTP " + respCode + " " + connection.getResponseMessage();
-            //}
-
-            // this will be useful to display download percentage
-            // might be -1: server did not report the length
-            int fileLength = connection.getContentLength();
-
-            // download the file
             if (respCode >= 400 && respCode <= 599)
                 input = connection.getErrorStream();
             else
                 input = connection.getInputStream();
 
 
-            ByteArrayOutputStream bt = null;
-
-
-            if (details.fileName != null)
-                output = new FileOutputStream(OxygineActivity.instance.getFilesDir() + "/" + details.fileName);
-            else {
-                bt = new ByteArrayOutputStream();
-                output = bt;
-            }
-
             byte data[] = new byte[4096];
-            int total = 0;
             int count;
 
-            nativeHttpRequestResponseProgress(details.handle, 0, fileLength);
+            while ((count = input.read(data)) != -1)
+                nativeHttpRequestWrite(details.handle, data, count);
 
-            while ((count = input.read(data)) != -1) {
-                // allow canceling with back button
-                /*
-                if (isCancelled()) {
-                    input.close();
-                    return null;
-                }
-                */
-                total += count;
-                // publishing the progress....
-                //if (fileLength > 0) // only if total length is known
-                //    publishProgress((int) (total * 100 / fileLength));
-                output.write(data, 0, count);
-                nativeHttpRequestResponseProgress(details.handle, total, fileLength);
-            }
-            if (bt != null)
-                nativeHttpRequestResponseSuccess(details.handle, bt.toByteArray(), respCode);
-            else
-                nativeHttpRequestResponseSuccess(details.handle, null, respCode);
+
+            nativeHttpRequestSuccess(details.handle);
 
         } catch (Exception e) {
-            nativeHttpRequestResponseError(details.handle, respCode);
+            nativeHttpRequestError(details.handle);
             Log.v("HttpRequest", "error: " + e.toString());
             return e.toString();
         } finally {
             try {
-                if (output != null)
-                    output.close();
                 if (input != null)
                     input.close();
             } catch (IOException ignored) {
@@ -231,68 +172,36 @@ class HttpRequest extends AsyncTask<RequestDetails, Integer, String> {
         //mProgressDialog.show();
     }
 
-    @Override
-    protected void onProgressUpdate(Integer... progress) {
-        super.onProgressUpdate(progress);
-        // if we get here, length is known, now set indeterminate to false
-        /*
-        mProgressDialog.setIndeterminate(false);
-        mProgressDialog.setMax(100);
-        mProgressDialog.setProgress(progress[0]);
-        */
-    }
 
     @Override
     protected void onPostExecute(String result) {
         mWakeLock.release();
-        /*
-        mProgressDialog.dismiss();
-        if (result != null)
-            Toast.makeText(context,"Download error: "+result, Toast.LENGTH_LONG).show();
-        else
-            Toast.makeText(context,"File downloaded", Toast.LENGTH_SHORT).show();
-            */
-    }
-}
-
-class HttpHandler {
-    //private RequestQueue _queue;
-    public HttpHandler() {
-        //_queue = Volley.newRequestQueue(OxygineActivity.instance);
-    }
-
-    public HttpRequestHolder createRequest(final RequestDetails details) {
-
-        //HttpRequest r = new HttpRequest(url, fname, post, handle);
-        //_queue.add(r);
-
-        HttpRequestHolder downloadTask = new HttpRequestHolder();
-        downloadTask.run(details);
-
-        return downloadTask;
     }
 }
 
 
 public class HttpRequests {
-    static private HttpHandler _handler;
 
     static public void init() {
-        _handler = new HttpHandler();
+
     }
 
     static public void release() {
-        _handler = null;
+
     }
 
-    static public HttpRequestHolder createRequest(String url, String fname, byte[] post, long handle) {
+    static public HttpRequestHolder createRequest(String url, String[] headerKeys, String[] headerValues, byte[] post, long handle) {
 
         RequestDetails details = new RequestDetails();
-        details.fileName = fname;
         details.url = url;
         details.postData = post;
         details.handle = handle;
+        details.headerKeys = headerKeys;
+        details.headerValues = headerValues;
 
-        return _handler.createRequest(details);
+        HttpRequestHolder downloadTask = new HttpRequestHolder();
+        downloadTask.run(details);
+
+        return downloadTask;
     }
 }

+ 4 - 8
oxygine/src/oxygine/AnimationFrame.cpp

@@ -54,16 +54,10 @@ namespace oxygine
     {
         AnimationFrame f = *this;
         if (vertical)
-        {
-            f._srcRect.setY(_srcRect.getBottom());
-            f._srcRect.setHeight(-_srcRect.getHeight());
-        }
+            f.flipY();
 
         if (horizontal)
-        {
-            f._srcRect.setX(_srcRect.getRight());
-            f._srcRect.setWidth(-_srcRect.getWidth());
-        }
+            f.flipX();
 
         return f;
     }
@@ -72,12 +66,14 @@ namespace oxygine
     {
         _srcRect.setX(_srcRect.getRight());
         _srcRect.setWidth(-_srcRect.getWidth());
+        _destRect.pos.x = _frameSize.x - _destRect.getRight();
     }
 
     void AnimationFrame::flipY()
     {
         _srcRect.setY(_srcRect.getBottom());
         _srcRect.setHeight(-_srcRect.getHeight());
+        _destRect.pos.y = _frameSize.y - _destRect.getBottom();
     }
 
     AnimationFrame::AnimationFrame(spNativeTexture t)

+ 9 - 5
oxygine/src/oxygine/AsyncTask.cpp

@@ -3,7 +3,8 @@
 #include <typeinfo>
 namespace oxygine
 {
-    AsyncTask::AsyncTask() : _status(status_not_started), _mainThreadSync(true), _uiThreadSync(false)
+
+    AsyncTask::AsyncTask() : _status(status_not_started), _mainThreadSync(false)
     {
 
     }
@@ -25,15 +26,18 @@ namespace oxygine
 
     void AsyncTask::run()
     {
-        _prerun();
-        log::messageln("AsyncTask::run %d - %s", getObjectID(), typeid(*this).name());
-
         OX_ASSERT(_status == status_not_started);
         _status = status_inprogress;
 
+        bool ok = _prerun();
+        log::messageln("AsyncTask::run %d - %s", getObjectID(), typeid(*this).name());
+
         sync([ = ]()
         {
-            _run();
+            if (ok)
+                _run();
+            else
+                _error();
         });
     }
 

+ 20 - 9
oxygine/src/oxygine/AsyncTask.h

@@ -3,7 +3,7 @@
 #include "core/ThreadDispatcher.h"
 #include "EventDispatcher.h"
 #include "Event.h"
-
+#include "core/oxygine.h"
 #ifdef ERROR
 #undef ERROR
 #endif
@@ -58,24 +58,35 @@ namespace oxygine
         friend class AsyncTaskManager;
 
         bool _mainThreadSync;
-        bool _uiThreadSync;
 
-        virtual void _prerun() {}
+        virtual bool _prerun() { return true; }
         virtual void _run() = 0;
         virtual void _onError() {}
         virtual void _onComplete() {}
         virtual void _onFinal(bool error) {}
-        virtual void _onCustom(const ThreadDispatcher::message&) {};
         virtual void _finalize(bool error) {}
         virtual void _dispatchComplete();
 
+
+        template <class F>
+        void sync(const F& f)
+        {
+            if (_mainThreadSync)
+            {
+                addRef();
+                core::getMainThreadDispatcher().postCallback([ = ]()
+                {
+                    f();
+                    releaseRef();
+                });
+                return;
+            }
+            f();
+        }
+
     private:
 
         void _complete();
         void _error();
-
-
-        static void threadCB(const ThreadDispatcher::message&);
-        void _threadCB(const ThreadDispatcher::message&);
     };
-}
+}

+ 99 - 20
oxygine/src/oxygine/HttpRequestTask.cpp

@@ -1,6 +1,7 @@
 #include "HttpRequestTask.h"
 #include "core/file.h"
 #include "core/oxygine.h"
+#include "utils/stringUtils.h"
 
 namespace oxygine
 {
@@ -20,13 +21,36 @@ namespace oxygine
     void HttpRequestTask::init() {}
     void HttpRequestTask::release() {}
 #endif
-    HttpRequestTask::HttpRequestTask() : _loaded(0), _cacheEnabled(true), _successOnAnyResponceCode(false)
+
+    static HttpRequestTask::responseCodeChecker _defaultCheckerAny = [](int code)
+    {
+        return true;
+    };
+
+
+    static HttpRequestTask::responseCodeChecker _defaultChecker200 = [](int code)
     {
+        return code == 200 || code == 206;
+    };
+
+    HttpRequestTask::HttpRequestTask() :
+        _cacheEnabled(true),
+        _continueDownload(false),
+        _expectedContentSize(0),
+        _receivedContentSize(0),
+        _fhandle(0),
+        _suitableResponse(false),
+        _responseCodeChecker(_defaultChecker200)
+    {
+        _mainThreadSync = true;
 
     }
+
     HttpRequestTask::~HttpRequestTask()
     {
         log::messageln("~HttpRequestTask");
+        if (_fhandle)
+            file::close(_fhandle);
     }
 
     void HttpRequestTask::setCustomRequests(createHttpRequestCallback cb)
@@ -46,8 +70,9 @@ namespace oxygine
         _setUrl(url);
     }
 
-    void HttpRequestTask::setFileName(const std::string& name)
+    void HttpRequestTask::setFileName(const std::string& name, bool continueDownload)
     {
+        _continueDownload = continueDownload;
         _fname = name;
         _setFileName(name);
     }
@@ -58,6 +83,11 @@ namespace oxygine
         _setCacheEnabled(enabled);
     }
 
+    void HttpRequestTask::setSuccessOnAnyResponseCode(bool any)
+    {
+        _responseCodeChecker = any ? _defaultCheckerAny : _defaultChecker200;
+    }
+
     void HttpRequestTask::addHeader(const std::string& key, const std::string& value)
     {
         OX_ASSERT(!key.empty());
@@ -86,35 +116,54 @@ namespace oxygine
         return _fname;
     }
 
-    void HttpRequestTask::_prerun()
+    bool HttpRequestTask::_prerun()
     {
+        _suitableResponse = false;
+        _receivedContentSize = 0;
+        _expectedContentSize = 0;
         _responseCode = 0;
-        _loaded = 0;
         _response.clear();
+        if (_fhandle)
+            file::close(_fhandle);
+        _fhandle = 0;
+
         if (!_fname.empty())
         {
-            file::deleteFile(_fname, ep_ignore_error);
+            const char* mode = _continueDownload ? "ab" : "wb";
+            _fhandle = file::open(_fname, mode, ep_ignore_error);
+            OX_ASSERT(_fhandle);
+
+            if (!_fhandle)
+            {
+                return false;
+            }
+
+            if (_continueDownload)
+            {
+                file::seek(_fhandle, 0, SEEK_END);
+                unsigned int size = file::tell(_fhandle);
+
+                char str[255];
+                safe_sprintf(str, "bytes=%d-", size);
+                addHeader("Range", str);
+
+                _receivedContentSize = size;
+            }
         }
+        return true;
     }
 
-    void HttpRequestTask::dispatchProgress(int loaded, int total)
+    void HttpRequestTask::dispatchProgress(int delta, int loaded, int total)
     {
-        int delta = loaded - _loaded;
-        _loaded = loaded;
         ProgressEvent event(delta, loaded, total);
         dispatchEvent(&event);
     }
 
-    void HttpRequestTask::_onCustom(const ThreadDispatcher::message& msg)
-    {
-        dispatchProgress((int)(size_t)msg.arg1, (int)(size_t)msg.arg2);
-    }
-
-    void HttpRequestTask::progress(int loaded, int total)
+    void HttpRequestTask::asyncProgress(int delta, int loaded, int total)
     {
         sync([ = ]()
         {
-            dispatchProgress(loaded, total);
+            dispatchProgress(delta, loaded, total);
         });
     }
 
@@ -130,16 +179,46 @@ namespace oxygine
 
     void HttpRequestTask::_dispatchComplete()
     {
-        if (_responseCode == 200 || _successOnAnyResponceCode)
+        Event ev(_suitableResponse ? COMPLETE : ERROR);
+        dispatchEvent(&ev);
+    }
+
+    void HttpRequestTask::_finalize(bool error)
+    {
+        if (_fhandle)
         {
-            Event ev(COMPLETE);
-            dispatchEvent(&ev);
+            file::close(_fhandle);
+            _fhandle = 0;
+
+            if (error && !_continueDownload)
+                file::deleteFile(_fname);
         }
+        _fhandle = 0;
+    }
+
+    void HttpRequestTask::gotHeaders()
+    {
+        _suitableResponse = _responseCodeChecker(_responseCode);
+
+        if (_continueDownload)
+            asyncProgress(_receivedContentSize, _receivedContentSize, _expectedContentSize);
+    }
+
+    void HttpRequestTask::write(const void* data, unsigned int size)
+    {
+        if (!_suitableResponse)
+            return;
+
+        if (_fhandle)
+            file::write(_fhandle, data, size);
         else
         {
-            Event ev(ERROR);
-            dispatchEvent(&ev);
+            const char* p = (const char*)data;
+            _response.insert(_response.end(), p, p + size);
         }
+
+        _receivedContentSize += size;
+        asyncProgress(size, _receivedContentSize, _expectedContentSize);
     }
 
 }

+ 25 - 10
oxygine/src/oxygine/HttpRequestTask.h

@@ -3,6 +3,7 @@
 #include "AsyncTask.h"
 #include <vector>
 #include <string>
+#include "core/file.h"
 
 namespace oxygine
 {
@@ -12,6 +13,7 @@ namespace oxygine
     public:
         static spHttpRequestTask create();
         typedef HttpRequestTask* (*createHttpRequestCallback)();
+        typedef std::function< bool(int) > responseCodeChecker;
         static void setCustomRequests(createHttpRequestCallback);
         static void init();
         static void release();
@@ -47,26 +49,32 @@ namespace oxygine
         /**swap version of getResponse if you want to modify result buffer inplace*/
         void getResponseSwap(std::vector<unsigned char>&);
         int  getResponseCode() const { return _responseCode; }
+        const responseCodeChecker& getResponseCodeChecker() const {return _responseCodeChecker;}
         void addHeader(const std::string& key, const std::string& value);
 
         void setPostData(const std::vector<unsigned char>& data);
         void setUrl(const std::string& url);
-        void setFileName(const std::string& name);
+        void setFileName(const std::string& name, bool continueDownload = false);
         void setCacheEnabled(bool enabled);
-        /**by default only response code == 200 is succeded, other codes are dispatching Event::ERROR*/
-        void setSuccessOnAnyResponseCode(bool en) { _successOnAnyResponceCode = en; }
+
+        void setResponseCodeChecker(const responseCodeChecker& f) {_responseCodeChecker = f;}
+		/**by default only response code == 200 is succeded, other codes are dispatching Event::ERROR*/
+        void setSuccessOnAnyResponseCode(bool en);
 
     protected:
-        void _prerun() override;
+        bool _prerun() override;
         void _onError() override;
         void _onComplete() override;
         void _dispatchComplete() override;
+        void _finalize(bool error) override;
+
+        void gotHeaders();
+        void write(const void* data, unsigned int size);
 
         //async
-        void progress(int loaded, int total);
+        void asyncProgress(int delta, int loaded, int total);
 
-        void _onCustom(const ThreadDispatcher::message& msg) override;
-        void dispatchProgress(int loaded, int total);
+        void dispatchProgress(int delta, int loaded, int total);
 
         virtual void _setFileName(const std::string& name) {}
         virtual void _setUrl(const std::string& url) {}
@@ -74,17 +82,24 @@ namespace oxygine
         virtual void _setCacheEnabled(bool enabled) {}
         virtual void _addHeader(const std::string& key, const std::string& value) {}
 
-        int _loaded;
         std::string _url;
         std::string _fname;
+        file::handle _fhandle;
         bool _cacheEnabled;
         std::vector<unsigned char> _response;
         std::vector<unsigned char> _postData;
 
+        responseCodeChecker _responseCodeChecker;
+
+        bool _suitableResponse;
+
         int _responseCode;
-        bool _successOnAnyResponceCode;
+        size_t _expectedContentSize;
+        size_t _receivedContentSize;
+
+        bool _continueDownload;
 
         typedef std::vector< std::pair<std::string, std::string> >  headers;
         headers _headers;
     };
-}
+}

+ 4 - 0
oxygine/src/oxygine/core/FileSystem.h

@@ -7,16 +7,20 @@ namespace oxygine
     namespace file
     {
         class buffer;
+        class FileSystem;
 
         class fileHandle: public ObjectBase
         {
         public:
+            fileHandle(FileSystem* fs): _fs(fs) {}
             virtual void release() = 0;
             virtual unsigned int read(void* dest, unsigned int size) = 0;
             virtual unsigned int write(const void* src, unsigned int size) = 0;
             virtual unsigned int getSize() const = 0;
             virtual int          seek(unsigned int offset, int whence) = 0;
             virtual unsigned int tell() const = 0;
+
+            FileSystem* _fs;
         };
 
 

+ 2 - 2
oxygine/src/oxygine/core/STDFileSystem.cpp

@@ -124,7 +124,7 @@ namespace oxygine
         class fileHandleSTD: public fileHandle
         {
         public:
-            fileHandleSTD(oxHandle* fh): _handle(fh)
+            fileHandleSTD(FileSystem* fs, oxHandle* fh): fileHandle(fs), _handle(fh)
             {
             }
 
@@ -224,7 +224,7 @@ namespace oxygine
             if (!h)
                 return status_error;
 
-            fh = new fileHandleSTD(h);
+            fh = new fileHandleSTD(this, h);
             return status_ok;
         }
 

+ 81 - 48
oxygine/src/oxygine/core/ZipFileSystem.cpp

@@ -40,7 +40,7 @@ namespace oxygine
             return true;
         }
 
-        Zips::Zips(): _sort(false), _lock(true)
+        Zips::Zips(): _lock(true)
         {
 
         }
@@ -61,15 +61,22 @@ namespace oxygine
 
                 file_entry entry;
                 unzGetCurrentFileInfo(zp, 0, entry.name, sizeof(entry.name) - 1, 0, 0, 0, 0);
+                entry.refs = 0;
                 entry.pos = pos;
                 entry.zp = zp;
+                
+                char *str = entry.name;
+                for (int i = 0; str[i]; i++)
+                    str[i] = tolower(str[i]);
 
-                _files.push_back(entry);
+
+                OX_ASSERT(_files.find(entry.name) == _files.end());
+
+                _files[entry.name] = entry;
 
             }
             while (unzGoToNextFile(zp) != UNZ_END_OF_LIST_OF_FILE);
 
-            _sort = true;
         }
 
         void Zips::add(const unsigned char* data, unsigned int size)
@@ -159,7 +166,34 @@ namespace oxygine
             return 0;
         }
 
+        void Zips::remove(const char* name)
+        {
+            MutexAutoLock al(_lock);
 
+            for (size_t i = 0; i < _zps.size(); ++i)
+            {
+                zpitem& item = _zps[i];
+                if (!strcmp(item.name, name))
+                {
+                    for (files::iterator it = _files.begin(); it != _files.end();)
+                    {
+                        file_entry& entry = it->second;
+                        if (entry.zp == item.handle)
+                        {
+                            OX_ASSERT(entry.refs == 0);
+                            it = _files.erase(it);
+                        }
+                        else
+                            ++it;
+                    }
+
+                    unzClose(item.handle);
+
+                    _zps.erase(_zps.begin() + i);
+                    break;
+                }
+            }
+        }
 
         void Zips::add(const char* name)
         {
@@ -191,18 +225,6 @@ namespace oxygine
             read(zp);
         }
 
-        void Zips::update()
-        {
-            std::sort(_files.begin(), _files.end(), sortFiles);
-#ifdef OX_DEBUG
-            for (int i = 0; i < (int)_files.size() - 1; ++i)
-            {
-                OX_ASSERT(strcmp(_files[i].name, _files[i + 1].name) != 0);
-            }
-#endif
-            log::messageln("ZipFS, total files: %d", _files.size());
-        }
-
         bool Zips::isExists(const char* name)
         {
             MutexAutoLock al(_lock);
@@ -234,43 +256,40 @@ namespace oxygine
                 unzClose(f.handle);
             }
 
-            _zps.clear();
-            _files.clear();
-        }
 
+#ifdef OX_DEBUG
+            for (files::iterator i = _files.begin(); i != _files.end(); ++i)
+            {
+                OX_ASSERT(i->second.refs == 0);
+            }
 
-        bool findEntry(const file_entry& g, const char* name)
-        {
-            return strcmp_cns(g.name, name) < 0;
+#endif
+            _zps.clear();
+            _files.clear();
         }
 
-        const file_entry* Zips::getEntryByName(const char* name)
+        file_entry* Zips::getEntryByName(const char* name)
         {
-            if (_sort)
+            char str[255];
+            char *p = str;
+            while (*name)
             {
-                update();
-                _sort = false;
+                *p = tolower(*name);
+                name++;
+                ++p;
             }
+            *p = 0;
 
-            files::const_iterator it = std::lower_bound(_files.begin(), _files.end(), name, findEntry);
-            if (it != _files.end())
-            {
-                const file_entry& g = *it;
-                if (!strcmp_cns(g.name, name))
-                    return &g;
-            }
+            files::iterator it = _files.find(str);
+            if (it != _files.end())            
+                return &it->second;
 
             return 0;
         }
 
+        /*
         const file_entry* Zips::getEntry(int index)
         {
-            if (_sort)
-            {
-                update();
-                _sort = false;
-            }
-
             return &_files[index];
         }
 
@@ -278,25 +297,30 @@ namespace oxygine
         {
             return _files.size();
         }
+        */
 
 
         class fileHandleZip: public fileHandle
         {
         public:
 
-            fileHandleZip(const file_entry* entry): _entry(entry)
+            fileHandleZip(FileSystem* fs, file_entry* entry): fileHandle(fs), _entry(entry)
             {
-                int r = 0;
-                r = unzGoToFilePos(entry->zp, const_cast<unz_file_pos*>(&entry->pos));
+                int r = unzGoToFilePos(entry->zp, const_cast<unz_file_pos*>(&entry->pos));
                 OX_ASSERT(r == UNZ_OK);
                 r = unzOpenCurrentFile(entry->zp);
                 OX_ASSERT(r == UNZ_OK);
+                _entry->refs++;
             }
 
             void release()
             {
+                ZipFileSystem *zfs = static_cast<ZipFileSystem *>(_fs);
+                MutexAutoLock lock(zfs->_zips.getMutex());
+                _entry->refs--;
+
                 int r = unzCloseCurrentFile(_entry->zp);
-                OX_ASSERT(r == UNZ_OK);
+                OX_ASSERT(r == UNZ_OK);                
                 delete this;
             }
 
@@ -329,7 +353,7 @@ namespace oxygine
                 return (unsigned int) file_info.uncompressed_size;
             }
 
-            const file_entry* _entry;
+            file_entry* _entry;
         };
 
         class fileHandleZipStreaming : public fileHandle
@@ -339,8 +363,9 @@ namespace oxygine
             z_off_t _pos;
             z_off_t _size;
             long _cpos;
+            file_entry* _entry;
 
-            fileHandleZipStreaming(const file_entry* entry, const Zips& z): _cpos(0)
+            fileHandleZipStreaming(FileSystem* fs, file_entry* entry, const Zips& z): fileHandle(fs), _cpos(0), _entry(entry)
             {
                 int r = 0;
                 r = unzGoToFilePos(entry->zp, const_cast<unz_file_pos*>(&entry->pos));
@@ -361,11 +386,14 @@ namespace oxygine
                 file::seek(_h, static_cast<unsigned int>(_pos), SEEK_SET);
 
                 unzCloseCurrentFile(entry->zp);
+                entry->refs++;
             }
 
             ~fileHandleZipStreaming()
             {
-                int q = 0;
+                ZipFileSystem *zfs = static_cast<ZipFileSystem *>(_fs);
+                MutexAutoLock lock(zfs->_zips.getMutex());
+                _entry->refs--;
             }
 
             void release()
@@ -441,6 +469,11 @@ namespace oxygine
         }
 
 
+        void ZipFileSystem::remove(const char* zip)
+        {
+            _zips.remove(zip);
+        }
+
         void ZipFileSystem::reset()
         {
             _zips.reset();
@@ -458,13 +491,13 @@ namespace oxygine
         {
             MutexAutoLock lock(_zips._lock);
 
-            const file_entry* entry = _zips.getEntryByName(file);
+            file_entry* entry = _zips.getEntryByName(file);
             if (entry)
             {
                 if (*mode == 's')
-                    fh = new fileHandleZipStreaming(entry, _zips);
+                    fh = new fileHandleZipStreaming(this, entry, _zips);
                 else
-                    fh = new fileHandleZip(entry);
+                    fh = new fileHandleZip(this, entry);
                 return status_ok;
             }
 

+ 11 - 10
oxygine/src/oxygine/core/ZipFileSystem.h

@@ -5,6 +5,7 @@
 #include "../minizip/unzip.h"
 #include "../core/file.h"
 #include "../core/Mutex.h"
+#include <unordered_map>
 
 namespace oxygine
 {
@@ -15,6 +16,7 @@ namespace oxygine
             unzFile zp;
             char name[128];
             unz_file_pos pos;
+            int refs;
         };
 
         class Zips
@@ -28,26 +30,21 @@ namespace oxygine
             void add(const char* name);
             void add(const unsigned char* data, unsigned int size);
             void add(std::vector<char>& data);
-
-            void update();
+            void remove(const char* name);
 
             bool read(const char* name, file::buffer& bf);
             bool read(const file_entry* entry, file::buffer& bf);
             bool isExists(const char* name);
 
-            const file_entry*   getEntryByName(const char* name);
-            const file_entry*   getEntry(int index);
-            size_t              getNumEntries() const;
-
-            const char* getZipFileName(int i) const { return _zps[i].name; }
+            file_entry*         getEntryByName(const char* name);            
+            const char*         getZipFileName(int i) const { return _zps[i].name; }
+            Mutex&              getMutex() { return _lock; }
 
         private:
             friend class ZipFileSystem;
             void read(unzFile zp);
 
-            bool _sort;
-
-            typedef std::vector<file_entry> files;
+            typedef std::unordered_map<std::string, file_entry> files;
             files _files;
 
             struct zpitem
@@ -74,6 +71,7 @@ namespace oxygine
 
             /**add zip from file*/
             void add(const char* zip);
+            void remove(const char* zip);
             //add zip from memory, data should not be deleted
             void add(const unsigned char* data, unsigned int size);
             //add zip from memory, vector would be swapped (emptied)
@@ -82,6 +80,9 @@ namespace oxygine
 
         protected:
 
+            friend class fileHandleZip;
+            friend class fileHandleZipStreaming;
+
             Zips _zips;
 
             status _read(const char* file, file::buffer&, error_policy ep);

+ 56 - 26
oxygine/src/oxygine/core/android/HttpRequestJavaTask.cpp

@@ -20,7 +20,9 @@ namespace oxygine
         _jHttpRequestsClass = (jclass) env->NewGlobalRef(env->FindClass("org/oxygine/lib/HttpRequests"));
         JNI_NOT_NULL(_jHttpRequestsClass);
 
-        _jCreateRequestMethod = env->GetStaticMethodID(_jHttpRequestsClass, "createRequest", "(Ljava/lang/String;Ljava/lang/String;[BJ)Lorg/oxygine/lib/HttpRequestHolder;");
+        _jCreateRequestMethod = env->GetStaticMethodID(_jHttpRequestsClass, "createRequest",
+                                "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;[BJ)Lorg/oxygine/lib/HttpRequestHolder;");
+
         JNI_NOT_NULL(_jCreateRequestMethod);
 
         jmethodID jInit = env->GetStaticMethodID(_jHttpRequestsClass, "init", "()V");
@@ -56,13 +58,11 @@ namespace oxygine
 
     void HttpRequestJavaTask::_run()
     {
-        addRef();
-        _mainThreadSync = true;
         JNIEnv* env = jniGetEnv();
         LOCAL_REF_HOLDER(env);
 
-        jstring jurl = jniGetEnv()->NewStringUTF(_url.c_str());
-        jstring jfname = _fname.empty() ? 0 : jniGetEnv()->NewStringUTF(_fname.c_str());
+        jstring jurl = env->NewStringUTF(_url.c_str());
+
         jbyteArray  jpost = 0;
         if (!_postData.empty())
         {
@@ -70,33 +70,49 @@ namespace oxygine
             env->SetByteArrayRegion(jpost, 0, _postData.size(), (jbyte*)&_postData.front());
         }
 
-        _handle = env->NewGlobalRef(env->CallStaticObjectMethod(_jHttpRequestsClass, _jCreateRequestMethod, jurl, jfname, jpost, (jlong)this));
+        jobjectArray jkeys   = (jobjectArray)env->NewObjectArray(_headers.size(), env->FindClass("java/lang/String"), 0);
+        jobjectArray jvalues = (jobjectArray)env->NewObjectArray(_headers.size(), env->FindClass("java/lang/String"), 0);
+
+        for (size_t i = 0; i < _headers.size(); ++i)
+        {
+            jstring jkey   = env->NewStringUTF(_headers[i].first.c_str());
+            jstring jvalue = env->NewStringUTF(_headers[i].second.c_str());
+
+            env->SetObjectArrayElement(jkeys,   i, jkey);
+            env->SetObjectArrayElement(jvalues, i, jvalue);
+        }
+
+
+        addRef();
+        _handle = env->NewGlobalRef(env->CallStaticObjectMethod(_jHttpRequestsClass, _jCreateRequestMethod,
+                                    jurl, jkeys, jvalues, jpost, (jlong)this));
     }
 
-    void HttpRequestJavaTask::error_(int respCode)
+    void HttpRequestJavaTask::error_()
     {
-        _responseCode = respCode;
         onError();
         releaseRef();
     }
 
-    void HttpRequestJavaTask::progress_(int loaded, int total)
+    void HttpRequestJavaTask::gotHeader_(int respCode, int contentLen)
     {
-        progress(loaded, total);
+        _responseCode = respCode;
+        _expectedContentSize = contentLen;
+        gotHeaders();
     }
 
-    void HttpRequestJavaTask::complete_(jbyteArray ar, int respCode)
+    void HttpRequestJavaTask::write_(jbyteArray jdata, int size)
     {
-        _responseCode = respCode;
-        if (ar)
-        {
-            JNIEnv* env = jniGetEnv();
-            jbyte* data = env->GetByteArrayElements(ar, 0);
-            jint len = env->GetArrayLength(ar);
-            _response.assign((char*)data, (char*)data + len);
-            env->ReleaseByteArrayElements(ar, data, 0);
-        }
+        JNIEnv* env = jniGetEnv();
+        jbyte* data = env->GetByteArrayElements(jdata, 0);
+
+        write(data, size);
+
+        env->ReleaseByteArrayElements(jdata, data, 0);
+    }
 
+    void HttpRequestJavaTask::complete_()
+    {
         onComplete();
         releaseRef();
     }
@@ -111,23 +127,37 @@ namespace oxygine
 
 extern "C"
 {
+    /*
+    public static native void nativeHttpRequestSuccess(long handle);
+    public static native void nativeHttpRequestError(long handle);
+    public static native void nativeHttpRequestGotHeader(long handle, int code, int contentLength);
+    public static native void nativeHttpRequestWrite(long handle, byte[] data, int size);
+    */
     JNIEnv* Android_JNI_GetEnv(void);
 
-    JNIEXPORT void JNICALL Java_org_oxygine_lib_HttpRequest_nativeHttpRequestResponseSuccess(JNIEnv* env, jclass, jlong handle, jbyteArray array, jint respCode)
+    JNIEXPORT void JNICALL Java_org_oxygine_lib_HttpRequest_nativeHttpRequestSuccess(JNIEnv* env, jclass, jlong handle)
+    {
+        oxygine::HttpRequestJavaTask* task = (oxygine::HttpRequestJavaTask*)handle;
+        task->complete_();
+    }
+
+    JNIEXPORT void JNICALL Java_org_oxygine_lib_HttpRequest_nativeHttpRequestError(JNIEnv* env, jclass, jlong handle)
     {
         oxygine::HttpRequestJavaTask* task = (oxygine::HttpRequestJavaTask*)handle;
-        task->complete_(array, respCode);
+        task->error_();
     }
 
-    JNIEXPORT void JNICALL Java_org_oxygine_lib_HttpRequest_nativeHttpRequestResponseProgress(JNIEnv* env, jclass, jlong handle, jint loaded, jint total)
+    JNIEXPORT void JNICALL Java_org_oxygine_lib_HttpRequest_nativeHttpRequestWrite(JNIEnv* env, jclass,
+            jlong handle, jbyteArray array, jint size)
     {
         oxygine::HttpRequestJavaTask* task = (oxygine::HttpRequestJavaTask*)handle;
-        task->progress_(loaded, total);
+        task->write_(array, size);
     }
 
-    JNIEXPORT void JNICALL Java_org_oxygine_lib_HttpRequest_nativeHttpRequestResponseError(JNIEnv* env, jclass, jlong handle, jint respCode)
+    JNIEXPORT void JNICALL Java_org_oxygine_lib_HttpRequest_nativeHttpRequestGotHeader(JNIEnv* env, jclass,
+            jlong handle, jint respCode, jint contentLen)
     {
         oxygine::HttpRequestJavaTask* task = (oxygine::HttpRequestJavaTask*)handle;
-        task->error_(respCode);
+        task->gotHeader_(respCode, contentLen);
     }
 }

+ 4 - 2
oxygine/src/oxygine/core/android/HttpRequestJavaTask.h

@@ -19,9 +19,11 @@ namespace oxygine
         HttpRequestJavaTask();
         ~HttpRequestJavaTask();
 
-        void complete_(jbyteArray, int respCode);
+        void complete_();
         void progress_(int loaded, int total);
-        void error_(int respCode);
+        void gotHeader_(int respCode, int contentLen);
+        void write_(jbyteArray, int size);
+        void error_();
 
 
     protected:

+ 69 - 69
oxygine/src/oxygine/core/curl/HttpRequestCurlTask.cpp

@@ -1,6 +1,6 @@
 #include "HttpRequestCurlTask.h"
-#include "../oxygine.h"
-#include "../ThreadDispatcher.h"
+#include "core/oxygine.h"
+#include "core/ThreadDispatcher.h"
 #include "SDL.h"
 #include "pthread.h"
 
@@ -17,9 +17,34 @@ namespace oxygine
         return new HttpRequestTaskCURL;
     }
 
-    const unsigned int ID_FINISH = 3423;
+    const unsigned int ID_DONE = sysEventID('C', 'D', 'N');
 
-    void* curlThread(void*)
+
+    void mainThreadFunc(const ThreadDispatcher::message& msg)
+    {
+        switch (msg.msgid)
+        {
+            case ID_DONE:
+            {
+                CURL* easy = (CURL*)msg.arg1;
+
+                HttpRequestTaskCURL* task = 0;
+                curl_easy_getinfo(easy, CURLINFO_PRIVATE, &task);
+
+                bool ok = (size_t)msg.arg2 == CURLE_OK;
+
+                if (ok)
+                    task->onComplete();
+                else
+                    task->onError();
+
+                task->releaseRef();
+            } break;
+        }
+
+    }
+
+    void* thread(void*)
     {
         while (true)
         {
@@ -32,7 +57,7 @@ namespace oxygine
                 ThreadDispatcher::peekMessage tmsg;
                 if (_messages.peek(tmsg, true))
                 {
-                    if (tmsg.msgid == ID_FINISH)
+                    if (tmsg.msgid == 1)
                         return 0;
                     curl_multi_add_handle(multi_handle, (CURL*)tmsg.arg1);
                 }
@@ -78,27 +103,10 @@ namespace oxygine
                     {
                         if (msg->msg == CURLMSG_DONE)
                         {
+
 #ifdef OX_HAS_CPP11 //msg broken in VS2010
                             curl_multi_remove_handle(multi_handle, msg->easy_handle);
-
-
-                            HttpRequestTaskCURL* task = 0;
-                            curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &task);
-
-                            bool ok = msg->data.result == CURLE_OK;
-                            if (ok)
-                            {
-                                int response = 0;
-                                curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &response);
-                                task->_responseCode = response;
-                            }
-
-                            if (ok)
-                                task->onComplete();
-                            else
-                                task->onError();
-
-                            task->releaseRef();
+                            core::getMainThreadDispatcher().postCallback(ID_DONE, msg->easy_handle, (void*)msg->data.result, mainThreadFunc, 0);
 #endif
                         }
                     }
@@ -116,12 +124,12 @@ namespace oxygine
             return;
         setCustomRequests(createCurl);
         multi_handle = curl_multi_init();
-        pthread_create(&_thread, 0, curlThread, 0);
+        pthread_create(&_thread, 0, thread, 0);
     }
 
     void HttpRequestTask::release()
     {
-        _messages.post(ID_FINISH, 0, 0);
+        _messages.post(1, 0, 0);
         pthread_join(_thread, 0);
 
         if (multi_handle)
@@ -129,56 +137,63 @@ namespace oxygine
         multi_handle = 0;
     }
 
-    size_t HttpRequestTaskCURL::cbXRefInfoFunction(void* userData, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
-    {
-        return ((HttpRequestTaskCURL*)userData)->_cbXRefInfoFunction(dltotal, dlnow);
-    }
 
-    size_t HttpRequestTaskCURL::_cbXRefInfoFunction(curl_off_t dltotal, curl_off_t dlnow)
+    size_t HttpRequestTaskCURL::cbWriteFunction(char* d, size_t n, size_t l, HttpRequestTaskCURL* This)
     {
-        progress((int)dlnow, (int)dltotal);
-
-        return 0;
+        return This->_cbWriteFunction(d, n, l);
     }
 
 
-    int HttpRequestTaskCURL::cbProgressFunction(void* userData, double dltotal, double dlnow, double ultotal, double ulnow)
+    size_t HttpRequestTaskCURL::_cbWriteFunction(char* d, size_t n, size_t l)
     {
-        return ((HttpRequestTaskCURL*)userData)->_cbXRefInfoFunction((curl_off_t) dltotal, (curl_off_t)dlnow);
+        size_t size = n * l;
+
+        write(d, size);
+
+        return size;
     }
 
-    size_t HttpRequestTaskCURL::cbWriteFunction(char* d, size_t n, size_t l, void* userData)
+    size_t HttpRequestTaskCURL::cbHeaderFunction(char* d, size_t n, size_t l, HttpRequestTaskCURL* This)
     {
-        return ((HttpRequestTaskCURL*)userData)->_cbWriteFunction(d, n, l);
+        return This->_cbHeaderFunction(d, n, l);
     }
 
 
-    size_t HttpRequestTaskCURL::_cbWriteFunction(char* d, size_t n, size_t l)
+    size_t HttpRequestTaskCURL::_cbHeaderFunction(char* d, size_t n, size_t l)
     {
-        if (!_handle && !_fname.empty())
+        size_t s = n * l;
+        if (s > 255)//ignore unknown headers
+            return s;
+
+
+        int contentLength = 0;
+        if (sscanf(d, "Content-Length: %d", &contentLength) == 1)
         {
-            _handle = file::open(_fname, "wb");
+            _expectedContentSize = contentLength;
         }
 
-        size_t size = n * l;
-        if (!_fname.empty())
-            file::write(_handle, d, (unsigned int)size);
-        else
-            _response.insert(_response.end(), d, d + size);
+        int responseCode = 0;
+        char ver[32];
+        if (sscanf(d, "HTTP/%s %d ", ver, &responseCode) == 2)
+        {
+            _responseCode = responseCode;
+        }
 
-        return size;
+        if (d[0] == '\r' && d[1] == '\n')
+        {
+            gotHeaders();
+        }
+
+        return s;
     }
 
-    HttpRequestTaskCURL::HttpRequestTaskCURL() : _easy(0), _handle(0), _httpHeaders(0)
+    HttpRequestTaskCURL::HttpRequestTaskCURL() : _easy(0), _httpHeaders(0)
     {
         _easy = curl_easy_init();
     }
 
     HttpRequestTaskCURL::~HttpRequestTaskCURL()
     {
-        if (_handle)
-            file::close(_handle);
-        _handle = 0;
 
         if (_easy)
             curl_easy_cleanup(_easy);
@@ -196,17 +211,13 @@ namespace oxygine
         curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, HttpRequestTaskCURL::cbWriteFunction);
         curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this);
 
+        curl_easy_setopt(_easy, CURLOPT_HEADERFUNCTION, HttpRequestTaskCURL::cbHeaderFunction);
+        curl_easy_setopt(_easy, CURLOPT_HEADERDATA, this);
+
 
         curl_easy_setopt(_easy, CURLOPT_NOPROGRESS, 0);
 
-#ifdef CURLOPT_XFERINFOFUNCTION
-        curl_easy_setopt(_easy, CURLOPT_XFERINFOFUNCTION, HttpRequestTaskCURL::cbXRefInfoFunction);
-        curl_easy_setopt(_easy, CURLOPT_XFERINFODATA, this);
-#else
 
-        curl_easy_setopt(_easy, CURLOPT_PROGRESSFUNCTION, HttpRequestTaskCURL::cbProgressFunction);
-        curl_easy_setopt(_easy, CURLOPT_PROGRESSDATA, this);
-#endif
         curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, true);
 
 
@@ -228,15 +239,4 @@ namespace oxygine
         addRef();
         _messages.post(0, _easy, 0);
     }
-
-    void HttpRequestTaskCURL::_finalize(bool error)
-    {
-        if (_handle)
-        {
-            file::close(_handle);
-            if (error)
-                file::deleteFile(_fname);
-        }
-        _handle = 0;
-    }
 }

+ 3 - 7
oxygine/src/oxygine/core/curl/HttpRequestCurlTask.h

@@ -23,19 +23,15 @@ namespace oxygine
         friend void* curlThread(void*);
         friend void mainThreadFunc(const ThreadDispatcher::message& msg);
 
-        static size_t cbWriteFunction(char* d, size_t n, size_t l, void* p);
+        static size_t cbWriteFunction(char* d, size_t n, size_t l, HttpRequestTaskCURL* p);
         size_t _cbWriteFunction(char* d, size_t n, size_t l);
 
-        static size_t cbXRefInfoFunction(void* clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow);
-        size_t _cbXRefInfoFunction(curl_off_t dltotal, curl_off_t dlnow);
-
-        static int cbProgressFunction(void* clientp, double dltotal, double dlnow, double ultotal, double ulnow);
+        static size_t cbHeaderFunction(char* d, size_t n, size_t l, HttpRequestTaskCURL* p);
+        size_t _cbHeaderFunction(char* d, size_t n, size_t l);
 
 
         void _run();
-        void _finalize(bool error);
 
-        file::handle _handle;
         void* _easy;
         curl_slist* _httpHeaders;
     };

+ 3 - 2
oxygine/src/oxygine/core/ios/HttpRequestCocoaTask.h

@@ -16,8 +16,9 @@ namespace oxygine
         HttpRequestCocoaTask();
         ~HttpRequestCocoaTask();
 
-        void complete_(NSData* data, bool error, int respCode);
-        void progress_(int loaded, int total);
+        void write(NSData* data);
+        void complete_(bool error);
+        void gotResponse(int resp, size_t expectedSize);
 
     protected:
         void _run();

+ 66 - 127
oxygine/src/oxygine/core/ios/HttpRequestCocoaTask.mm

@@ -13,7 +13,7 @@ static HttpRequestTask *createTask()
     return new HttpRequestCocoaTask;
 }
 
-@interface HttpRequests:NSObject<NSURLSessionDownloadDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
+@interface HttpRequests:NSObject<NSURLSessionTaskDelegate, NSURLSessionDataDelegate>
 {
 }
 @end
@@ -34,98 +34,67 @@ static HttpRequestTask *createTask()
     oxygine::HttpRequestCocoaTask* task = (oxygine::HttpRequestCocoaTask*)taskValue.pointerValue;
     if (remove)
         objc_removeAssociatedObjects(object);
-    if (!task)
-        int q=0;
     return task;
 }
 
-#pragma mark - NSURLSessionDownloadDelegate
+#pragma mark - NSURLSessionTaskDelegate
 
--(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
+- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error
 {
-    oxygine::HttpRequestCocoaTask* task = [self getTask:downloadTask remove:false];
-    task->progress_((int) totalBytesWritten, (int) totalBytesExpectedToWrite);
-}
-
--(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
-    
-    oxygine::HttpRequestCocoaTask* task = [self getTask:downloadTask remove:true];
-    
-    NSHTTPURLResponse *resp = (NSHTTPURLResponse*)[downloadTask response];
-    
-    long code = [resp statusCode];
-    
-    
-    NSFileManager *fileManager = [NSFileManager defaultManager];
-    
-    std::string dest = oxygine::file::wfs().getFullPath(task->getFileName().c_str());
-    NSURL *destUrl = [NSURL fileURLWithPath:[NSString stringWithUTF8String:dest.c_str()]];
+    NSLog(@"Session %@ download task %@ finished downloading with error %@\n",session, task, error);
     
-    NSError *fileManagerError;
     
-    [fileManager removeItemAtURL:destUrl error:&fileManagerError];
+    oxygine::HttpRequestCocoaTask* httpRequestTask = [self getTask:task remove:true];
     
-    /*
-    if (code == 200)
+    if (error)
     {
-        [fileManager copyItemAtURL:location toURL:destUrl error:&fileManagerError];
-    
-        task->complete_(nil,  false);
+        httpRequestTask->complete_(true);
     }
     else
     {
-        task->complete_(nil, true);
-        
+        httpRequestTask->complete_(false);
     }
-    */
-
-    task->complete_(nil, false, (int)code);
 }
 
-#pragma mark - NSURLSessionTaskDelegate
 
-- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error
+#pragma mark - NSURLSessionDataDelegate
+
+// Not used yet (using completion handler for data tasks)
+- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask  didReceiveData:(NSData *)data
 {
-    NSLog(@"Session %@ download task %@ finished downloading with error %@\n",session, task, error);
-    
-    
-    oxygine::HttpRequestCocoaTask* httpRequestTask = [self getTask:task remove:true];
-    
-    if (error) {
-        NSData* resumeData = [error.userInfo objectForKey:NSURLSessionDownloadTaskResumeData];
-        if (resumeData) {
-            NSURLSessionTask *rt = [session downloadTaskWithResumeData:resumeData];
-            [rt resume];
-            
-            NSValue *taskValue = [NSValue valueWithPointer:httpRequestTask];
-            objc_setAssociatedObject(rt, &taskKey, taskValue, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
-        }
-        else
+    oxygine::HttpRequestCocoaTask* task = [self getTask:dataTask remove:false];
+    if (!task)
+        return;
+    task->write(data);
+}
+
+- (void)URLSession:(NSURLSession *)session
+          dataTask:(NSURLSessionDataTask *)dataTask
+didReceiveResponse:(NSURLResponse *)response
+ completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler
+{
+    if ([response isKindOfClass:[NSHTTPURLResponse class]])
+    {
+        NSHTTPURLResponse *httpResponse = ((NSHTTPURLResponse *)response);
+        
+        long long size = [httpResponse expectedContentLength];
+        
+        int resp = (int)httpResponse.statusCode;
+        
+        oxygine::HttpRequestCocoaTask* task = [self getTask:dataTask remove:false];
+        if (task)
         {
-            httpRequestTask->complete_(/* data */ nil, /* error */ true, 0);
+            task->gotResponse(resp, size);
+            bool ok = task->getResponseCodeChecker()(resp);
+            completionHandler(ok ? NSURLSessionResponseAllow : NSURLSessionResponseCancel);
+            return;
         }
     }
+
     
-    if (!error) {
-        // didFinishDownloadingToURL will be called in this case,
-        // which will save the file and signal completion.
-    } else {
-        
-    }
+    completionHandler(NSURLSessionResponseAllow);
 }
 
-
-#pragma mark - NSURLSessionDataDelegate
-
-// Not used yet (using completion handler for data tasks)
-//- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask  didReceiveData:(NSData *)data{
-//    oxygine::HttpRequestCocoaTask* task = [self getTask:dataTask remove:true];
-//    log::messageln("nssessiond complete %x", dataTask);
-//    //if (!task)
-//    //    return;
-//    task->complete_(data);
-//}
-
 #pragma mark -
 
 @end
@@ -199,88 +168,58 @@ namespace oxygine
         
     }
     
-    void HttpRequestCocoaTask::progress_(int loaded, int total)
+    
+    void HttpRequestCocoaTask::write(NSData *data)
     {
-        progress(loaded, total);
+        const void *ptr = [data bytes];
+        unsigned int len = [data length];
+        HttpRequestTask::write(ptr, len);
     }
     
-    void HttpRequestCocoaTask::complete_(NSData *data, bool error, int respCode)
+    void HttpRequestCocoaTask::complete_(bool error)
     {
-        _responseCode = respCode;
-        
         if (error)
             onError();
         else
-        {
-            if (data)
-            {
-                const void *ptr = [data bytes];
-                size_t len = [data length];
-                _response.assign((const char*)ptr, (const char*)ptr + len);
-            }
-            
             onComplete();
-        }
         
         releaseRef();
     }
     
+    void HttpRequestCocoaTask::gotResponse(int resp, size_t expectedSize)
+    {
+        _responseCode = resp;
+        _expectedContentSize = _receivedContentSize + expectedSize;
+        gotHeaders();
+    }
+    
     void HttpRequestCocoaTask::_run()
     {
-        _mainThreadSync = true;
-        
         addRef();
+        
         NSString *urlString = [NSString stringWithUTF8String:_url.c_str()];
-        NSURL *url =[NSURL URLWithString:urlString];
+        NSURL *url = [NSURL URLWithString:urlString];
         
         NSURLSession *session = _cacheEnabled ? _getDefaultSession() : _getEphemeralSession();
         
         NSURLSessionTask *task = 0;
-        if (_fname.empty())
+        
+        NSMutableURLRequest *request = [NSMutableURLRequest	requestWithURL:url];
+        for (const auto& h:_headers)
         {
-            NSMutableURLRequest *request = [NSMutableURLRequest	requestWithURL:url];
-            if (!_postData.empty())
-            {
-                request.HTTPBody = [NSData dataWithBytes:_postData.data() length:_postData.size()];
-                request.HTTPMethod = @"POST";
-            }
+            NSString *key = [NSString stringWithUTF8String:h.first.c_str()];
+            NSString *value = [NSString stringWithUTF8String:h.second.c_str()];
             
-            task = [session dataTaskWithRequest:request
-                              completionHandler:^(NSData *data,
-                                                  NSURLResponse *response,
-                                                  NSError *error) {
-                                  // handle response
-                                  
-                                  
-                                  if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
-                                      NSHTTPURLResponse *httpResponse = ((NSHTTPURLResponse *)response);
-                                      _responseCode = (int)httpResponse.statusCode;
-                                      //if (statusCode != 200)
-                                       //   httpError = true;
-                                  }
-                                  
-                                  if (error)
-                                  {
-                                      onError();
-                                  }
-                                  else
-                                  {
-                                      if (data)
-                                      {
-                                          const void *ptr = data.bytes;
-                                          size_t len = data.length;
-                                          _response.assign((const char*)ptr, (const char*)ptr + len);
-                                      }
-                                      
-                                      onComplete();
-                                  }
-                                  releaseRef();
-                              }];
+            [request setValue:value forHTTPHeaderField:key];
         }
-        else
+        
+        if (!_postData.empty())
         {
-            task = [session downloadTaskWithURL:url];
+            request.HTTPBody = [NSData dataWithBytes:_postData.data() length:_postData.size()];
+            request.HTTPMethod = @"POST";
         }
+            
+        task = [session dataTaskWithRequest:request];
         
         NSValue *taskValue = [NSValue valueWithPointer:this];
         objc_setAssociatedObject(task, &taskKey, taskValue, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

+ 8 - 1
oxygine/src/oxygine/core/oxygine.cpp

@@ -348,6 +348,8 @@ namespace oxygine
             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
             SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
 
+            SDL_GL_SetAttribute(SDL_GL_RETAINED_BACKING, 0);
+
             if (desc.force_gles)
             {
                 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
@@ -383,7 +385,12 @@ namespace oxygine
 
 #if TARGET_OS_IPHONE
             //ios bug workaround
-            flags &= ~SDL_WINDOW_FULLSCREEN;
+            //flags &= ~SDL_WINDOW_FULLSCREEN;
+#endif
+
+#if TARGET_OS_IPHONE || defined(__ANDROID__)
+            desc.w = -1;
+            desc.h = -1;
 #endif
 
             log::messageln("creating window %d %d", desc.w, desc.h);

+ 1 - 1
oxygine/src/oxygine/oxygine-include.h

@@ -113,7 +113,7 @@ namespace oxygine { namespace log { void error(const char* format, ...); } }
 
 #define OXYGINE_RENDERER 5
 
-#define OXYGINE_VERSION 5
+#define OXYGINE_VERSION 6
 
 #ifdef __GNUC__
 #   define OXYGINE_DEPRECATED __attribute__((deprecated))