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>
|
|
|
|
|
|
|
|
#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "support/zip_support/unzip.h"
|
|
|
|
|
|
|
|
using namespace cocos2d;
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
#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
|
|
|
|
|
|
|
|
AssetsManager::AssetsManager()
|
|
|
|
: _packageUrl("")
|
|
|
|
, _versionFileUrl("")
|
|
|
|
, _version("")
|
|
|
|
, _curl(NULL)
|
|
|
|
, _delegate(NULL)
|
|
|
|
{
|
|
|
|
_storagePath = CCFileUtils::sharedFileUtils()->getWritablePath();
|
|
|
|
checkStoragePath();
|
|
|
|
}
|
|
|
|
|
|
|
|
AssetsManager::AssetsManager(const char* packageUrl, const char* versionFileUrl)
|
|
|
|
: _packageUrl(packageUrl)
|
|
|
|
, _version("")
|
|
|
|
, _versionFileUrl(versionFileUrl)
|
|
|
|
, _delegate(NULL)
|
|
|
|
, _curl(NULL)
|
|
|
|
{
|
|
|
|
_storagePath = CCFileUtils::sharedFileUtils()->getWritablePath();
|
|
|
|
checkStoragePath();
|
|
|
|
}
|
|
|
|
|
|
|
|
AssetsManager::AssetsManager(const char* packageUrl, const char* versionFileUrl, const char* storagePath)
|
|
|
|
: _packageUrl(packageUrl)
|
|
|
|
, _version("")
|
|
|
|
, _versionFileUrl(versionFileUrl)
|
|
|
|
, _storagePath(storagePath)
|
|
|
|
, _delegate(NULL)
|
|
|
|
, _curl(NULL)
|
|
|
|
{
|
|
|
|
checkStoragePath();
|
|
|
|
}
|
|
|
|
|
|
|
|
void AssetsManager::checkStoragePath()
|
|
|
|
{
|
|
|
|
if (_storagePath.size() > 0 && _storagePath[_storagePath.size() - 1] != '/')
|
|
|
|
{
|
|
|
|
_storagePath.append("/");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
res = curl_easy_perform(_curl);
|
|
|
|
|
|
|
|
if (res != 0)
|
|
|
|
{
|
|
|
|
CCLOG("can not get version file content");
|
|
|
|
curl_easy_cleanup(_curl);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
string recordedVersion = CCUserDefault::sharedUserDefault()->getStringForKey(KEY_OF_VERSION);
|
|
|
|
if (recordedVersion == _version)
|
|
|
|
{
|
|
|
|
CCLOG("there is not new version");
|
|
|
|
// Set resource search path.
|
|
|
|
setSearchPath();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AssetsManager::update()
|
|
|
|
{
|
|
|
|
// 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");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if there is a new version.
|
|
|
|
if (! checkUpdate()) return;
|
|
|
|
|
|
|
|
// Is package already downloaded?
|
|
|
|
string downloadedVersion = CCUserDefault::sharedUserDefault()->getStringForKey(KEY_OF_DOWNLOADED_VERSION);
|
|
|
|
if (downloadedVersion != _version)
|
|
|
|
{
|
|
|
|
if (! downLoad()) return;
|
|
|
|
|
|
|
|
// Record downloaded version.
|
|
|
|
CCUserDefault::sharedUserDefault()->setStringForKey(KEY_OF_DOWNLOADED_VERSION, _version.c_str());
|
|
|
|
CCUserDefault::sharedUserDefault()->flush();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Uncompress zip file.
|
|
|
|
if (! uncompress()) return;
|
|
|
|
|
|
|
|
// Record new version code.
|
|
|
|
CCUserDefault::sharedUserDefault()->setStringForKey(KEY_OF_VERSION, _version.c_str());
|
|
|
|
|
|
|
|
// Unrecord downloaded version code.
|
|
|
|
CCUserDefault::sharedUserDefault()->setStringForKey(KEY_OF_DOWNLOADED_VERSION, "");
|
|
|
|
|
|
|
|
CCUserDefault::sharedUserDefault()->flush();
|
|
|
|
|
|
|
|
// Set resource search path.
|
|
|
|
setSearchPath();
|
|
|
|
|
|
|
|
// Delete unloaded zip file.
|
|
|
|
string zipfileName = _storagePath + TEMP_PACKAGE_FILE_NAME;
|
|
|
|
if (remove(zipfileName.c_str()) != 0)
|
|
|
|
{
|
|
|
|
CCLOG("can not remove downloaded zip file");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Buffer to hold data read from the zip file
|
|
|
|
char readBuffer[BUFFER_SIZE];
|
|
|
|
|
|
|
|
// 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
|
|
|
|
{
|
|
|
|
// 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)
|
|
|
|
{
|
|
|
|
CCLOG("error when read zip file %s, error code is %d", fileName, error);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
//@todo create a direcotry on win32
|
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void AssetsManager::setSearchPath()
|
|
|
|
{
|
|
|
|
vector<string> searchPaths = CCFileUtils::sharedFileUtils()->getSearchPaths();
|
|
|
|
vector<string>::iterator iter = searchPaths.begin();
|
|
|
|
searchPaths.insert(iter, _storagePath);
|
|
|
|
CCFileUtils::sharedFileUtils()->setSearchPaths(searchPaths);
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AssetsManager::downLoad()
|
|
|
|
{
|
|
|
|
// Create file to save package.
|
|
|
|
string outFileName = _storagePath + TEMP_PACKAGE_FILE_NAME;
|
|
|
|
FILE *fp = fopen(outFileName.c_str(), "wb");
|
|
|
|
if (! fp)
|
|
|
|
{
|
|
|
|
CCLOG("can not create file %s", outFileName.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// download pacakge
|
|
|
|
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);
|
|
|
|
res = curl_easy_perform(_curl);
|
|
|
|
curl_easy_cleanup(_curl);
|
|
|
|
if (res != 0)
|
|
|
|
{
|
|
|
|
CCLOG("error when download package");
|
|
|
|
fclose(fp);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
|
|
|
{
|
|
|
|
return CCUserDefault::sharedUserDefault()->getStringForKey(KEY_OF_VERSION);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AssetsManager::deleteVersion()
|
|
|
|
{
|
|
|
|
CCUserDefault::sharedUserDefault()->setStringForKey(KEY_OF_VERSION, "");
|
|
|
|
}
|