/**************************************************************************** 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 #include #include #include #if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32) #include #include #include #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()); 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 searchPaths = CCFileUtils::sharedFileUtils()->getSearchPaths(); vector::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; }