2013-02-22 11:04:09 +08:00
|
|
|
/****************************************************************************
|
|
|
|
Copyright (c) 2013 cocos2d-x.org
|
|
|
|
|
|
|
|
http://www.cocos2d-x.org
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
|
|
in the Software without restriction, including without limitation the rights
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
|
|
all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
THE SOFTWARE.
|
|
|
|
****************************************************************************/
|
|
|
|
#include "AssetsManager.h"
|
|
|
|
#include "cocos2d.h"
|
|
|
|
|
|
|
|
#include <curl/curl.h>
|
|
|
|
#include <curl/easy.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <vector>
|
2013-06-24 13:59:41 +08:00
|
|
|
#include <thread>
|
2013-02-22 11:04:09 +08:00
|
|
|
|
|
|
|
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <errno.h>
|
2013-08-07 13:36:26 +08:00
|
|
|
#include <dirent.h>
|
2013-02-22 11:04:09 +08:00
|
|
|
#endif
|
|
|
|
|
2013-08-07 13:36:26 +08:00
|
|
|
|
2013-10-15 18:00:03 +08:00
|
|
|
#include "unzip.h"
|
2013-02-22 11:04:09 +08:00
|
|
|
|
|
|
|
using namespace cocos2d;
|
|
|
|
using namespace std;
|
|
|
|
|
2013-02-26 16:43:46 +08:00
|
|
|
NS_CC_EXT_BEGIN;
|
|
|
|
|
2013-02-22 11:04:09 +08:00
|
|
|
#define KEY_OF_VERSION "current-version-code"
|
|
|
|
#define KEY_OF_DOWNLOADED_VERSION "downloaded-version-code"
|
|
|
|
#define TEMP_PACKAGE_FILE_NAME "cocos2dx-update-temp-package.zip"
|
|
|
|
#define BUFFER_SIZE 8192
|
|
|
|
#define MAX_FILENAME 512
|
|
|
|
|
2013-12-17 23:09:21 +08:00
|
|
|
#define LOW_SPEED_LIMIT 1L
|
|
|
|
#define LOW_SPEED_TIME 5L
|
|
|
|
|
|
|
|
|
2013-05-15 17:09:28 +08:00
|
|
|
// Message type
|
|
|
|
#define ASSETSMANAGER_MESSAGE_UPDATE_SUCCEED 0
|
|
|
|
#define ASSETSMANAGER_MESSAGE_RECORD_DOWNLOADED_VERSION 1
|
|
|
|
#define ASSETSMANAGER_MESSAGE_PROGRESS 2
|
|
|
|
#define ASSETSMANAGER_MESSAGE_ERROR 3
|
|
|
|
|
|
|
|
// Some data struct for sending messages
|
|
|
|
|
|
|
|
struct ErrorMessage
|
2013-02-22 11:04:09 +08:00
|
|
|
{
|
2013-05-15 17:09:28 +08:00
|
|
|
AssetsManager::ErrorCode code;
|
|
|
|
AssetsManager* manager;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ProgressMessage
|
|
|
|
{
|
|
|
|
int percent;
|
|
|
|
AssetsManager* manager;
|
|
|
|
};
|
2013-02-22 11:04:09 +08:00
|
|
|
|
2013-05-15 17:09:28 +08:00
|
|
|
// Implementation of AssetsManager
|
|
|
|
|
|
|
|
AssetsManager::AssetsManager(const char* packageUrl/* =NULL */, const char* versionFileUrl/* =NULL */, const char* storagePath/* =NULL */)
|
|
|
|
: _storagePath(storagePath)
|
2013-02-22 11:04:09 +08:00
|
|
|
, _version("")
|
2013-05-15 17:09:28 +08:00
|
|
|
, _packageUrl(packageUrl)
|
2013-02-22 11:04:09 +08:00
|
|
|
, _versionFileUrl(versionFileUrl)
|
2013-05-15 17:09:28 +08:00
|
|
|
, _downloadedVersion("")
|
2013-02-22 11:04:09 +08:00
|
|
|
, _curl(NULL)
|
2013-05-15 17:09:28 +08:00
|
|
|
, _connectionTimeout(0)
|
|
|
|
, _delegate(NULL)
|
2013-06-24 13:59:41 +08:00
|
|
|
, _isDownloading(false)
|
2013-08-08 17:36:56 +08:00
|
|
|
, _shouldDeleteDelegateWhenExit(false)
|
2013-02-22 11:04:09 +08:00
|
|
|
{
|
|
|
|
checkStoragePath();
|
|
|
|
}
|
|
|
|
|
2013-05-15 17:09:28 +08:00
|
|
|
AssetsManager::~AssetsManager()
|
2013-02-22 11:04:09 +08:00
|
|
|
{
|
2013-08-08 17:36:56 +08:00
|
|
|
if (_shouldDeleteDelegateWhenExit)
|
|
|
|
{
|
|
|
|
delete _delegate;
|
|
|
|
}
|
2013-02-22 11:04:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void AssetsManager::checkStoragePath()
|
|
|
|
{
|
|
|
|
if (_storagePath.size() > 0 && _storagePath[_storagePath.size() - 1] != '/')
|
|
|
|
{
|
|
|
|
_storagePath.append("/");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-08 16:29:29 +08:00
|
|
|
// Multiple key names
|
2013-09-06 15:10:11 +08:00
|
|
|
static std::string keyWithHash( const char* prefix, const std::string& url )
|
2013-08-08 16:29:29 +08:00
|
|
|
{
|
|
|
|
char buf[256];
|
|
|
|
sprintf(buf,"%s%zd",prefix,std::hash<std::string>()(url));
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
|
|
|
// hashed version
|
2013-08-09 13:39:40 +08:00
|
|
|
std::string AssetsManager::keyOfVersion() const
|
2013-08-08 16:29:29 +08:00
|
|
|
{
|
2013-09-06 15:10:11 +08:00
|
|
|
return keyWithHash(KEY_OF_VERSION,_packageUrl);
|
2013-08-08 16:29:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// hashed version
|
2013-08-09 13:39:40 +08:00
|
|
|
std::string AssetsManager::keyOfDownloadedVersion() const
|
2013-08-08 16:29:29 +08:00
|
|
|
{
|
2013-09-06 15:10:11 +08:00
|
|
|
return keyWithHash(KEY_OF_DOWNLOADED_VERSION,_packageUrl);
|
2013-08-08 16:29:29 +08:00
|
|
|
}
|
|
|
|
|
2013-02-22 11:04:09 +08:00
|
|
|
static size_t getVersionCode(void *ptr, size_t size, size_t nmemb, void *userdata)
|
|
|
|
{
|
|
|
|
string *version = (string*)userdata;
|
|
|
|
version->append((char*)ptr, size * nmemb);
|
|
|
|
|
|
|
|
return (size * nmemb);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AssetsManager::checkUpdate()
|
|
|
|
{
|
|
|
|
if (_versionFileUrl.size() == 0) return false;
|
|
|
|
|
|
|
|
_curl = curl_easy_init();
|
|
|
|
if (! _curl)
|
|
|
|
{
|
|
|
|
CCLOG("can not init curl");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-02-26 10:29:31 +08:00
|
|
|
// Clear _version before assign new value.
|
|
|
|
_version.clear();
|
|
|
|
|
2013-02-22 11:04:09 +08:00
|
|
|
CURLcode res;
|
|
|
|
curl_easy_setopt(_curl, CURLOPT_URL, _versionFileUrl.c_str());
|
2013-02-25 14:33:04 +08:00
|
|
|
curl_easy_setopt(_curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
2013-02-22 11:04:09 +08:00
|
|
|
curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, getVersionCode);
|
|
|
|
curl_easy_setopt(_curl, CURLOPT_WRITEDATA, &_version);
|
2013-05-15 17:09:28 +08:00
|
|
|
if (_connectionTimeout) curl_easy_setopt(_curl, CURLOPT_CONNECTTIMEOUT, _connectionTimeout);
|
2013-12-17 23:09:21 +08:00
|
|
|
curl_easy_setopt(_curl, CURLOPT_NOSIGNAL, 1L);
|
|
|
|
curl_easy_setopt(_curl, CURLOPT_LOW_SPEED_LIMIT, LOW_SPEED_LIMIT);
|
|
|
|
curl_easy_setopt(_curl, CURLOPT_LOW_SPEED_TIME, LOW_SPEED_TIME);
|
2013-02-22 11:04:09 +08:00
|
|
|
res = curl_easy_perform(_curl);
|
|
|
|
|
|
|
|
if (res != 0)
|
|
|
|
{
|
2013-12-06 14:15:01 +08:00
|
|
|
Director::getInstance()->getScheduler()->performFunctionInCocosThread([&, this]{
|
2013-11-28 11:06:08 +08:00
|
|
|
if (this->_delegate)
|
|
|
|
this->_delegate->onError(ErrorCode::NETWORK);
|
|
|
|
});
|
2013-02-26 16:29:52 +08:00
|
|
|
CCLOG("can not get version file content, error code is %d", res);
|
2013-02-22 11:04:09 +08:00
|
|
|
curl_easy_cleanup(_curl);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-08-09 13:39:40 +08:00
|
|
|
string recordedVersion = UserDefault::getInstance()->getStringForKey(keyOfVersion().c_str());
|
2013-02-22 11:04:09 +08:00
|
|
|
if (recordedVersion == _version)
|
|
|
|
{
|
2013-12-06 14:15:01 +08:00
|
|
|
Director::getInstance()->getScheduler()->performFunctionInCocosThread([&, this]{
|
2013-11-28 11:06:08 +08:00
|
|
|
if (this->_delegate)
|
|
|
|
this->_delegate->onError(ErrorCode::NO_NEW_VERSION);
|
|
|
|
});
|
2013-02-22 11:04:09 +08:00
|
|
|
CCLOG("there is not new version");
|
|
|
|
// Set resource search path.
|
|
|
|
setSearchPath();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-02-25 17:50:32 +08:00
|
|
|
CCLOG("there is a new version: %s", _version.c_str());
|
|
|
|
|
2013-02-22 11:04:09 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-06-24 13:59:41 +08:00
|
|
|
void AssetsManager::downloadAndUncompress()
|
2013-05-15 17:09:28 +08:00
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
2013-06-24 13:59:41 +08:00
|
|
|
if (_downloadedVersion != _version)
|
2013-05-15 17:09:28 +08:00
|
|
|
{
|
2013-06-24 13:59:41 +08:00
|
|
|
if (! downLoad()) break;
|
2013-05-15 17:09:28 +08:00
|
|
|
|
2013-12-06 14:15:01 +08:00
|
|
|
Director::getInstance()->getScheduler()->performFunctionInCocosThread([&, this]{
|
2013-11-28 11:06:08 +08:00
|
|
|
UserDefault::getInstance()->setStringForKey(this->keyOfDownloadedVersion().c_str(),
|
|
|
|
this->_version.c_str());
|
|
|
|
UserDefault::getInstance()->flush();
|
|
|
|
});
|
2013-05-15 17:09:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Uncompress zip file.
|
2013-06-24 13:59:41 +08:00
|
|
|
if (! uncompress())
|
2013-05-15 17:09:28 +08:00
|
|
|
{
|
2013-12-06 14:15:01 +08:00
|
|
|
Director::getInstance()->getScheduler()->performFunctionInCocosThread([&, this]{
|
2013-11-28 11:06:08 +08:00
|
|
|
if (this->_delegate)
|
|
|
|
this->_delegate->onError(ErrorCode::UNCOMPRESS);
|
|
|
|
});
|
2013-05-15 17:09:28 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-12-06 14:15:01 +08:00
|
|
|
Director::getInstance()->getScheduler()->performFunctionInCocosThread([&, this] {
|
2013-11-28 11:06:08 +08:00
|
|
|
|
|
|
|
// Record new version code.
|
|
|
|
UserDefault::getInstance()->setStringForKey(this->keyOfVersion().c_str(), this->_version.c_str());
|
|
|
|
|
|
|
|
// Unrecord downloaded version code.
|
|
|
|
UserDefault::getInstance()->setStringForKey(this->keyOfDownloadedVersion().c_str(), "");
|
|
|
|
UserDefault::getInstance()->flush();
|
|
|
|
|
|
|
|
// Set resource search path.
|
|
|
|
this->setSearchPath();
|
|
|
|
|
|
|
|
// Delete unloaded zip file.
|
|
|
|
string zipfileName = this->_storagePath + TEMP_PACKAGE_FILE_NAME;
|
|
|
|
if (remove(zipfileName.c_str()) != 0)
|
|
|
|
{
|
|
|
|
CCLOG("can not remove downloaded zip file %s", zipfileName.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this->_delegate) this->_delegate->onSuccess();
|
|
|
|
});
|
|
|
|
|
2013-05-15 17:09:28 +08:00
|
|
|
} while (0);
|
|
|
|
|
2013-06-24 13:59:41 +08:00
|
|
|
_isDownloading = false;
|
2013-05-15 17:09:28 +08:00
|
|
|
}
|
|
|
|
|
2013-11-11 17:06:32 +08:00
|
|
|
void AssetsManager::update()
|
2013-02-22 11:04:09 +08:00
|
|
|
{
|
2013-06-24 13:59:41 +08:00
|
|
|
if (_isDownloading) return;
|
|
|
|
|
|
|
|
_isDownloading = true;
|
2013-05-15 17:09:28 +08:00
|
|
|
|
2013-02-22 11:04:09 +08:00
|
|
|
// 1. Urls of package and version should be valid;
|
|
|
|
// 2. Package should be a zip file.
|
|
|
|
if (_versionFileUrl.size() == 0 ||
|
|
|
|
_packageUrl.size() == 0 ||
|
|
|
|
std::string::npos == _packageUrl.find(".zip"))
|
|
|
|
{
|
|
|
|
CCLOG("no version file url, or no package url, or the package is not a zip file");
|
2013-06-24 13:59:41 +08:00
|
|
|
_isDownloading = false;
|
2013-02-22 11:04:09 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if there is a new version.
|
2013-07-28 09:53:59 +08:00
|
|
|
if (! checkUpdate())
|
|
|
|
{
|
|
|
|
_isDownloading = false;
|
|
|
|
return;
|
|
|
|
}
|
2013-02-22 11:04:09 +08:00
|
|
|
|
|
|
|
// Is package already downloaded?
|
2013-08-09 13:39:40 +08:00
|
|
|
_downloadedVersion = UserDefault::getInstance()->getStringForKey(keyOfDownloadedVersion().c_str());
|
2013-02-22 11:04:09 +08:00
|
|
|
|
2013-06-24 13:59:41 +08:00
|
|
|
auto t = std::thread(&AssetsManager::downloadAndUncompress, this);
|
|
|
|
t.detach();
|
2013-02-22 11:04:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool AssetsManager::uncompress()
|
|
|
|
{
|
|
|
|
// Open the zip file
|
|
|
|
string outFileName = _storagePath + TEMP_PACKAGE_FILE_NAME;
|
|
|
|
unzFile zipfile = unzOpen(outFileName.c_str());
|
|
|
|
if (! zipfile)
|
|
|
|
{
|
|
|
|
CCLOG("can not open downloaded zip file %s", outFileName.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get info about the zip file
|
|
|
|
unz_global_info global_info;
|
|
|
|
if (unzGetGlobalInfo(zipfile, &global_info) != UNZ_OK)
|
|
|
|
{
|
|
|
|
CCLOG("can not read file global info of %s", outFileName.c_str());
|
|
|
|
unzClose(zipfile);
|
2013-05-15 17:09:28 +08:00
|
|
|
return false;
|
2013-02-22 11:04:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Buffer to hold data read from the zip file
|
|
|
|
char readBuffer[BUFFER_SIZE];
|
|
|
|
|
2013-02-25 17:50:32 +08:00
|
|
|
CCLOG("start uncompressing");
|
|
|
|
|
2013-02-22 11:04:09 +08:00
|
|
|
// Loop to extract all files.
|
|
|
|
uLong i;
|
|
|
|
for (i = 0; i < global_info.number_entry; ++i)
|
|
|
|
{
|
|
|
|
// Get info about current file.
|
|
|
|
unz_file_info fileInfo;
|
|
|
|
char fileName[MAX_FILENAME];
|
|
|
|
if (unzGetCurrentFileInfo(zipfile,
|
|
|
|
&fileInfo,
|
|
|
|
fileName,
|
|
|
|
MAX_FILENAME,
|
|
|
|
NULL,
|
|
|
|
0,
|
|
|
|
NULL,
|
|
|
|
0) != UNZ_OK)
|
|
|
|
{
|
|
|
|
CCLOG("can not read file info");
|
|
|
|
unzClose(zipfile);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
string fullPath = _storagePath + fileName;
|
|
|
|
|
|
|
|
// Check if this entry is a directory or a file.
|
|
|
|
const size_t filenameLength = strlen(fileName);
|
|
|
|
if (fileName[filenameLength-1] == '/')
|
|
|
|
{
|
|
|
|
// Entry is a direcotry, so create it.
|
|
|
|
// If the directory exists, it will failed scilently.
|
|
|
|
if (!createDirectory(fullPath.c_str()))
|
|
|
|
{
|
|
|
|
CCLOG("can not create directory %s", fullPath.c_str());
|
|
|
|
unzClose(zipfile);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-11-08 11:23:06 +08:00
|
|
|
//There are not directory entry in some case.
|
|
|
|
//So we need to test whether the file directory exists when uncompressing file entry
|
|
|
|
//, if does not exist then create directory
|
|
|
|
string fileNameStr(fileName);
|
|
|
|
|
|
|
|
size_t startIndex=0;
|
|
|
|
|
|
|
|
size_t index=fileNameStr.find("/",startIndex);
|
|
|
|
|
|
|
|
while(index!=-1)
|
|
|
|
{
|
|
|
|
string dir=_storagePath+fileNameStr.substr(0,index);
|
|
|
|
|
|
|
|
FILE *out = fopen(dir.c_str(), "r");
|
|
|
|
|
|
|
|
if(!out)
|
|
|
|
{
|
|
|
|
if (!createDirectory(dir.c_str()))
|
|
|
|
{
|
|
|
|
CCLOG("can not create directory %s", dir.c_str());
|
|
|
|
unzClose(zipfile);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CCLOG("create directory %s",dir.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fclose(out);
|
|
|
|
}
|
|
|
|
|
|
|
|
startIndex=index+1;
|
|
|
|
|
|
|
|
index=fileNameStr.find("/",startIndex);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-02-22 11:04:09 +08:00
|
|
|
// Entry is a file, so extract it.
|
|
|
|
|
|
|
|
// Open current file.
|
|
|
|
if (unzOpenCurrentFile(zipfile) != UNZ_OK)
|
|
|
|
{
|
|
|
|
CCLOG("can not open file %s", fileName);
|
|
|
|
unzClose(zipfile);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a file to store current file.
|
|
|
|
FILE *out = fopen(fullPath.c_str(), "wb");
|
|
|
|
if (! out)
|
|
|
|
{
|
|
|
|
CCLOG("can not open destination file %s", fullPath.c_str());
|
|
|
|
unzCloseCurrentFile(zipfile);
|
|
|
|
unzClose(zipfile);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write current file content to destinate file.
|
|
|
|
int error = UNZ_OK;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
error = unzReadCurrentFile(zipfile, readBuffer, BUFFER_SIZE);
|
|
|
|
if (error < 0)
|
|
|
|
{
|
2013-02-26 16:29:52 +08:00
|
|
|
CCLOG("can not read zip file %s, error code is %d", fileName, error);
|
2013-02-22 11:04:09 +08:00
|
|
|
unzCloseCurrentFile(zipfile);
|
|
|
|
unzClose(zipfile);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error > 0)
|
|
|
|
{
|
|
|
|
fwrite(readBuffer, error, 1, out);
|
|
|
|
}
|
|
|
|
} while(error > 0);
|
|
|
|
|
|
|
|
fclose(out);
|
|
|
|
}
|
|
|
|
|
|
|
|
unzCloseCurrentFile(zipfile);
|
|
|
|
|
|
|
|
// Goto next entry listed in the zip file.
|
|
|
|
if ((i+1) < global_info.number_entry)
|
|
|
|
{
|
|
|
|
if (unzGoToNextFile(zipfile) != UNZ_OK)
|
|
|
|
{
|
|
|
|
CCLOG("can not read next file");
|
|
|
|
unzClose(zipfile);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-26 16:29:52 +08:00
|
|
|
CCLOG("end uncompressing");
|
|
|
|
|
2013-02-22 11:04:09 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a direcotry is platform depended.
|
|
|
|
*/
|
|
|
|
bool AssetsManager::createDirectory(const char *path)
|
|
|
|
{
|
|
|
|
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)
|
|
|
|
mode_t processMask = umask(0);
|
|
|
|
int ret = mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO);
|
|
|
|
umask(processMask);
|
|
|
|
if (ret != 0 && (errno != EEXIST))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
#else
|
2013-02-25 18:03:38 +08:00
|
|
|
BOOL ret = CreateDirectoryA(path, NULL);
|
|
|
|
if (!ret && ERROR_ALREADY_EXISTS != GetLastError())
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2013-02-22 11:04:09 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void AssetsManager::setSearchPath()
|
|
|
|
{
|
2013-07-12 06:24:23 +08:00
|
|
|
vector<string> searchPaths = FileUtils::getInstance()->getSearchPaths();
|
2013-02-22 11:04:09 +08:00
|
|
|
vector<string>::iterator iter = searchPaths.begin();
|
|
|
|
searchPaths.insert(iter, _storagePath);
|
2013-07-12 06:24:23 +08:00
|
|
|
FileUtils::getInstance()->setSearchPaths(searchPaths);
|
2013-02-22 11:04:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static size_t downLoadPackage(void *ptr, size_t size, size_t nmemb, void *userdata)
|
|
|
|
{
|
|
|
|
FILE *fp = (FILE*)userdata;
|
|
|
|
size_t written = fwrite(ptr, size, nmemb, fp);
|
|
|
|
return written;
|
|
|
|
}
|
|
|
|
|
2013-05-15 17:09:28 +08:00
|
|
|
int assetsManagerProgressFunc(void *ptr, double totalToDownload, double nowDownloaded, double totalToUpLoad, double nowUpLoaded)
|
2013-02-26 10:29:31 +08:00
|
|
|
{
|
2013-11-28 11:06:08 +08:00
|
|
|
static int percent = 0;
|
|
|
|
int tmp = (int)(nowDownloaded / totalToDownload * 100);
|
2013-05-15 17:09:28 +08:00
|
|
|
|
2013-11-28 11:06:08 +08:00
|
|
|
if (percent != tmp)
|
|
|
|
{
|
|
|
|
percent = tmp;
|
2013-12-06 14:15:01 +08:00
|
|
|
Director::getInstance()->getScheduler()->performFunctionInCocosThread([=]{
|
2013-11-28 11:06:08 +08:00
|
|
|
auto manager = static_cast<AssetsManager*>(ptr);
|
|
|
|
if (manager->_delegate)
|
|
|
|
manager->_delegate->onProgress(percent);
|
|
|
|
});
|
|
|
|
|
|
|
|
CCLOG("downloading... %d%%", percent);
|
|
|
|
}
|
2013-02-26 16:29:52 +08:00
|
|
|
|
2013-02-26 10:29:31 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-02-22 11:04:09 +08:00
|
|
|
bool AssetsManager::downLoad()
|
|
|
|
{
|
2013-02-25 17:50:32 +08:00
|
|
|
// Create a file to save package.
|
2013-02-22 11:04:09 +08:00
|
|
|
string outFileName = _storagePath + TEMP_PACKAGE_FILE_NAME;
|
|
|
|
FILE *fp = fopen(outFileName.c_str(), "wb");
|
|
|
|
if (! fp)
|
|
|
|
{
|
2013-12-06 14:15:01 +08:00
|
|
|
Director::getInstance()->getScheduler()->performFunctionInCocosThread([&, this]{
|
2013-11-28 11:06:08 +08:00
|
|
|
if (this->_delegate)
|
|
|
|
this->_delegate->onError(ErrorCode::CREATE_FILE);
|
|
|
|
});
|
2013-02-22 11:04:09 +08:00
|
|
|
CCLOG("can not create file %s", outFileName.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-02-25 17:50:32 +08:00
|
|
|
// Download pacakge
|
2013-02-22 11:04:09 +08:00
|
|
|
CURLcode res;
|
|
|
|
curl_easy_setopt(_curl, CURLOPT_URL, _packageUrl.c_str());
|
|
|
|
curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, downLoadPackage);
|
|
|
|
curl_easy_setopt(_curl, CURLOPT_WRITEDATA, fp);
|
2013-02-26 10:29:31 +08:00
|
|
|
curl_easy_setopt(_curl, CURLOPT_NOPROGRESS, false);
|
2013-05-15 17:09:28 +08:00
|
|
|
curl_easy_setopt(_curl, CURLOPT_PROGRESSFUNCTION, assetsManagerProgressFunc);
|
|
|
|
curl_easy_setopt(_curl, CURLOPT_PROGRESSDATA, this);
|
2013-12-17 23:09:21 +08:00
|
|
|
curl_easy_setopt(_curl, CURLOPT_NOSIGNAL, 1L);
|
|
|
|
curl_easy_setopt(_curl, CURLOPT_LOW_SPEED_LIMIT, LOW_SPEED_LIMIT);
|
|
|
|
curl_easy_setopt(_curl, CURLOPT_LOW_SPEED_TIME, LOW_SPEED_TIME);
|
|
|
|
|
2013-02-22 11:04:09 +08:00
|
|
|
res = curl_easy_perform(_curl);
|
|
|
|
curl_easy_cleanup(_curl);
|
|
|
|
if (res != 0)
|
|
|
|
{
|
2013-12-06 14:15:01 +08:00
|
|
|
Director::getInstance()->getScheduler()->performFunctionInCocosThread([&, this]{
|
2013-11-28 11:06:08 +08:00
|
|
|
if (this->_delegate)
|
|
|
|
this->_delegate->onError(ErrorCode::NETWORK);
|
|
|
|
});
|
2013-02-22 11:04:09 +08:00
|
|
|
CCLOG("error when download package");
|
|
|
|
fclose(fp);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-02-26 16:29:52 +08:00
|
|
|
CCLOG("succeed downloading package %s", _packageUrl.c_str());
|
|
|
|
|
2013-02-22 11:04:09 +08:00
|
|
|
fclose(fp);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* AssetsManager::getPackageUrl() const
|
|
|
|
{
|
|
|
|
return _packageUrl.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
void AssetsManager::setPackageUrl(const char *packageUrl)
|
|
|
|
{
|
|
|
|
_packageUrl = packageUrl;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* AssetsManager::getStoragePath() const
|
|
|
|
{
|
|
|
|
return _storagePath.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
void AssetsManager::setStoragePath(const char *storagePath)
|
|
|
|
{
|
|
|
|
_storagePath = storagePath;
|
|
|
|
checkStoragePath();
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* AssetsManager::getVersionFileUrl() const
|
|
|
|
{
|
|
|
|
return _versionFileUrl.c_str();
|
|
|
|
}
|
|
|
|
|
|
|
|
void AssetsManager::setVersionFileUrl(const char *versionFileUrl)
|
|
|
|
{
|
|
|
|
_versionFileUrl = versionFileUrl;
|
|
|
|
}
|
2013-02-22 16:02:53 +08:00
|
|
|
|
|
|
|
string AssetsManager::getVersion()
|
|
|
|
{
|
2013-08-09 13:39:40 +08:00
|
|
|
return UserDefault::getInstance()->getStringForKey(keyOfVersion().c_str());
|
2013-02-22 16:02:53 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void AssetsManager::deleteVersion()
|
|
|
|
{
|
2013-08-09 13:39:40 +08:00
|
|
|
UserDefault::getInstance()->setStringForKey(keyOfVersion().c_str(), "");
|
2013-02-22 16:02:53 +08:00
|
|
|
}
|
2013-02-26 16:43:46 +08:00
|
|
|
|
2013-05-15 17:09:28 +08:00
|
|
|
void AssetsManager::setDelegate(AssetsManagerDelegateProtocol *delegate)
|
|
|
|
{
|
|
|
|
_delegate = delegate;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AssetsManager::setConnectionTimeout(unsigned int timeout)
|
|
|
|
{
|
|
|
|
_connectionTimeout = timeout;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int AssetsManager::getConnectionTimeout()
|
|
|
|
{
|
|
|
|
return _connectionTimeout;
|
|
|
|
}
|
|
|
|
|
2013-08-07 13:07:01 +08:00
|
|
|
AssetsManager* AssetsManager::create(const char* packageUrl, const char* versionFileUrl, const char* storagePath, ErrorCallback errorCallback, ProgressCallback progressCallback, SuccessCallback successCallback )
|
|
|
|
{
|
|
|
|
class DelegateProtocolImpl : public AssetsManagerDelegateProtocol
|
|
|
|
{
|
|
|
|
public :
|
2013-11-11 12:49:38 +08:00
|
|
|
DelegateProtocolImpl(ErrorCallback aErrorCallback, ProgressCallback aProgressCallback, SuccessCallback aSuccessCallback)
|
|
|
|
: errorCallback(aErrorCallback), progressCallback(aProgressCallback), successCallback(aSuccessCallback)
|
2013-08-07 13:07:01 +08:00
|
|
|
{}
|
|
|
|
|
|
|
|
virtual void onError(AssetsManager::ErrorCode errorCode) { errorCallback(int(errorCode)); }
|
|
|
|
virtual void onProgress(int percent) { progressCallback(percent); }
|
|
|
|
virtual void onSuccess() { successCallback(); }
|
|
|
|
|
|
|
|
private :
|
|
|
|
ErrorCallback errorCallback;
|
|
|
|
ProgressCallback progressCallback;
|
|
|
|
SuccessCallback successCallback;
|
|
|
|
};
|
|
|
|
|
|
|
|
auto* manager = new AssetsManager(packageUrl,versionFileUrl,storagePath);
|
|
|
|
auto* delegate = new DelegateProtocolImpl(errorCallback,progressCallback,successCallback);
|
|
|
|
manager->setDelegate(delegate);
|
2013-08-08 17:36:56 +08:00
|
|
|
manager->_shouldDeleteDelegateWhenExit = true;
|
2013-08-16 18:03:27 +08:00
|
|
|
manager->autorelease();
|
2013-08-07 13:07:01 +08:00
|
|
|
return manager;
|
|
|
|
}
|
|
|
|
|
2013-08-08 16:29:29 +08:00
|
|
|
void AssetsManager::createStoragePath()
|
2013-08-07 13:36:26 +08:00
|
|
|
{
|
|
|
|
// Remove downloaded files
|
|
|
|
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)
|
2013-08-09 13:39:40 +08:00
|
|
|
DIR *dir = NULL;
|
2013-08-07 13:36:26 +08:00
|
|
|
|
2013-08-09 13:39:40 +08:00
|
|
|
dir = opendir (_storagePath.c_str());
|
|
|
|
if (!dir)
|
2013-08-07 13:36:26 +08:00
|
|
|
{
|
|
|
|
mkdir(_storagePath.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
|
|
|
|
}
|
2013-08-08 16:29:29 +08:00
|
|
|
#else
|
|
|
|
if ((GetFileAttributesA(_storagePath.c_str())) == INVALID_FILE_ATTRIBUTES)
|
|
|
|
{
|
|
|
|
CreateDirectoryA(_storagePath.c_str(), 0);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void AssetsManager::destroyStoragePath()
|
|
|
|
{
|
|
|
|
// Delete recorded version codes.
|
|
|
|
deleteVersion();
|
|
|
|
|
|
|
|
// Remove downloaded files
|
|
|
|
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)
|
|
|
|
string command = "rm -r ";
|
|
|
|
// Path may include space.
|
|
|
|
command += "\"" + _storagePath + "\"";
|
|
|
|
system(command.c_str());
|
2013-08-07 13:36:26 +08:00
|
|
|
#else
|
|
|
|
string command = "rd /s /q ";
|
|
|
|
// Path may include space.
|
|
|
|
command += "\"" + _storagePath + "\"";
|
|
|
|
system(command.c_str());
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-02-26 16:43:46 +08:00
|
|
|
NS_CC_EXT_END;
|