这几天在尝试使用cocos2d-js解压运行本地zip文件,用的是cocos2d-x 3.13版本,xcode8.2。之前想到的是既然在热更新的时候,已经有从服务端下载zip文件解压运行的接口,那么直接把解压运行的接口提取出来封装成单独一个接口应该也是可行的。因为之前我这边的热更新已经做好了,所以修改直接在我这个热更新demo上继续。热更新教程
1、分析热更新时下载解压zip文件的流程;
首先找到js代码中初始化AssetsManager的地方;
然后按目录打开cocos2d-js-bindings.xcodeproj。/TestJs001/frameworks/cocos2d-x/cocos/scripting/js-bindings/proj.ios_mac;
在此处搜索AssetsManager,定位到jsb_cocos2dx_extension_auto.cpp类,接下来就是在这里做文章了。
通过在几个可能会调到的方法里添加了log发现,会调用到AssetsManager的构造函数。这样就可以直接去C++那边查看构造函数那边是如何做处理的了。
按目录打开cocos2d_libs.xcodeproj。/TestJs001/frameworks/cocos2d-x/build/cocos2d_libs.xcodeproj;
搜索找到AssetsManagerEx.h,及其构造函数。
AssetsManagerEx(const std::string& manifestUrl, const std::string& storagePath);
分析了下,加载过程,会发现在加载成功时会调用onSuccess方法,然后就会调用到这个decompressDownloadedZip(),再进而调用到decompress(),decompress一听就知道是干解压事情的,那么接下来就是在这个方法上动手啦。
2、加入解压zip文件的接口.
1 拷贝一份decompress方法,重新命名为decompressLocalZip,即
bool AssetsManagerEx::decompressLocalZip(const std::string &zip)
,并在.h文件中添加相应头文件,注意要添加到public下,这样js-binding才能调用到。2 修改decompressLocalZip.cpp,
bool AssetsManagerEx::decompressLocalZip(const std::string &zip)
{
CCLOG("AssetsManagerEx : decompressLocalZip zip = %s ..........." , zip.c_str());
FileUtils* fileTtils = FileUtils::getInstance();
//获得可写的具体文件路径
std::string writeablePath = fileTtils->getWritablePath();
size_t pos_0 = zip.find_last_of("/\\");
if (pos_0 == std::string::npos)
{
CCLOG("pos_0.........zip not exit");
return false;
}
std::string zipName = zip.substr(pos_0 + 1, strlen(zip.c_str()));
log("zipName = %s",zipName.c_str());
std::string xmlPath = writeablePath + zipName;
log("xmlPath = %s",xmlPath.c_str());
std::string curFullPath = fileTtils->fullPathForFilename(zip.c_str());
CCLOG("AssetsManagerEx : decompress fullPath = %s ..........." , curFullPath.c_str());
CCLOG("AssetsManagerEx : decompress xmlPath = %s ..........." , xmlPath.c_str());
// Find root path for zip file
size_t pos = xmlPath.find_last_of("/\\");
if (pos == std::string::npos)
{
CCLOG("AssetsManagerEx : no root path specified for curFullPath file %s\n", curFullPath.c_str());
return false;
}
const std::string rootPath = xmlPath.substr(0, pos+1);
CCLOG("AssetsManagerEx : rootPath = %s ...........1111" , rootPath.c_str());
CCLOG("AssetsManagerEx : getSuitableFOpen zip = %s ..........." , FileUtils::getInstance()->getSuitableFOpen(curFullPath).c_str());
// Open the zip file
unzFile zipfile = unzOpen(FileUtils::getInstance()->getSuitableFOpen(curFullPath).c_str());
if (! zipfile)
{
CCLOG("AssetsManagerEx : can not open downloaded zip file %s\n", curFullPath.c_str());
return false;
}
// Get info about the zip file
unz_global_info global_info;
if (unzGetGlobalInfo(zipfile, &global_info) != UNZ_OK)
{
CCLOG("AssetsManagerEx : can not read file global info of %s\n", curFullPath.c_str());
unzClose(zipfile);
return false;
}
// 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("AssetsManagerEx : can not read compressed file info\n");
unzClose(zipfile);
return false;
}
const std::string fullPath = rootPath + fileName;
CCLOG("AssetsManagerEx : create directory fullPath %s\n", fullPath.c_str());
// Check if this entry is a directory or a file.
const size_t filenameLength = strlen(fileName);
if (fileName[filenameLength-1] == '/')
{
//There are not directory entry in some case.
//So we need to create directory when decompressing file entry
if ( !_fileUtils->createDirectory(basename(fullPath)) )
{
// Failed to create directory
CCLOG("AssetsManagerEx : can not create directory %s\n", fullPath.c_str());
unzClose(zipfile);
return false;
}
}
else
{
// jsb depress zip xuyuanteng add 20170324
std::string dir = basename(fullPath);
CCLOG("AssetsManagerEx : basename dir %s\n", dir.c_str());
if(!_fileUtils->isDirectoryExist(dir))
{
CCLOG("AssetsManagerEx : isDirectoryExist no no no");
if(!_fileUtils->createDirectory(dir))
{
// Failed to create directory
CCLOG("AssetsManagerEx : can not create directory %s\n", fullPath.c_str());
unzClose(zipfile);
return false;
}
}
// Entry is a file, so extract it.
// Open current file.
if (unzOpenCurrentFile(zipfile) != UNZ_OK)
{
CCLOG("AssetsManagerEx : can not extract file %s\n", fileName);
unzClose(zipfile);
return false;
}
// Create a file to store current file.
CCLOG("AssetsManagerEx : fullPath = %s\n", fullPath.c_str());
CCLOG("AssetsManagerEx : fopen getSuitableFOpen fullPath = %s\n", FileUtils::getInstance()->getSuitableFOpen(fullPath).c_str());
FILE *out = fopen(FileUtils::getInstance()->getSuitableFOpen(fullPath).c_str(), "wb");
if (!out)
{
CCLOG("AssetsManagerEx : can not create decompress destination file %s\n", 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("AssetsManagerEx : can not read zip file %s, error code is %d\n", fileName, error);
fclose(out);
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("AssetsManagerEx : can not read next file for decompressing\n");
unzClose(zipfile);
return false;
}
}
}
unzClose(zipfile);
return true;
}
- 3 回到jsb_cocos2dx_extension_auto.hpp类,添加头文件
bool js_cocos2dx_extension_AssetsManagerEx_decompressLocalZip(JSContext *cx, uint32_t argc, jsval *vp);
,在jsb_cocos2dx_extension_auto.cpp中加入实现,
//jsb uncompress xuyuanteng add 20170324
bool js_cocos2dx_extension_AssetsManagerEx_decompressLocalZip(JSContext *cx, uint32_t argc, jsval *vp)
{
cocos2d::log("js_cocos2dx_extension_AssetsManagerEx_decompressLocalZip......");
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
bool ok = true;
JS::RootedObject obj(cx, args.thisv().toObjectOrNull());
js_proxy_t *proxy = jsb_get_js_proxy(obj);
cocos2d::extension::AssetsManagerEx* cobj = (cocos2d::extension::AssetsManagerEx *)(proxy ? proxy->ptr : NULL);
JSB_PRECONDITION2( cobj, cx, false, "js_cocos2dx_extension_AssetsManagerEx_decompressLocalZip : Invalid Native Object");
if (argc == 1) {
std::string arg0;
ok &= jsval_to_std_string(cx, args.get(0), &arg0);
JSB_PRECONDITION2(ok, cx, false, "js_cocos2dx_extension_Assetjs_cocos2dx_extension_AssetsManagerEx_decompressLocalZipsManagerEx_decompress : Error processing arguments");
bool ret = cobj->decompressLocalZip(arg0);
jsval jsret = JSVAL_NULL;
jsret = BOOLEAN_TO_JSVAL(ret);
args.rval().set(jsret);
return true;
}
JS_ReportError(cx, "js_cocos2dx_extension_AssetsManagerEx_decompressLocalZip : wrong number of arguments: %d, was expecting %d", argc, 1);
return false;
}
4 在js_register_cocos2dx_extension_AssetsManagerEx方法里进行绑定。
JS_FN("decompressLocalZip", js_cocos2dx_extension_AssetsManagerEx_decompressLocalZip, 1, JSPROP_PERMANENT | JSPROP_ENUMERATE),
5 编译cocos2d_js_bindings工程,生成静态库后,放到cocos2d_libs.xcodeproj工程下,编译生成静态库。
6 js调用代码如下。
this._am = new jsb.AssetsManager("test", "test");
this._am.retain();
var isSucceed = this._am.decompressLocalZip("src/app.zip");
cc.log("isSucceed = " + isSucceed);
var storagePath = (jsb.fileUtils ? jsb.fileUtils.getWritablePath() : "./");
cc.log("storagePath is " + storagePath);
//添加搜索路径
jsb.fileUtils.addSearchPath(storagePath);
//解压成功后运行场景
if (isSucceed) {
cc.loader.loadJs(["src/jsList.js"], function(){
cc.loader.loadJs(jsList, function(){
cc.director.runScene(new HelloWorldScene());
});
});
}