加密就不用说了,看上一篇2.X加密的方式,怎么弄都可以。的保证解密规则就行;
现在重点说3.X解密:
在新的3.X引擎中官方整合了大部分获取资源的方法,最终合成一个getdata;
可以从源码,和堆栈调用中看到:
CCFileUtils.cpp:
Data FileUtils::getDataFromFile(const std::string& filename) { return getData(filename, false); }
getDataFromFile目前只调用getData(filename,false);
Data getData(const std::string& filename, bool forString)
这个函数是一个非类成员静态函数。
forString是用来标识是否是一个文本文件,如果是那么buffer需要多一个字节。
这个其实不重要,因为我们处理的最终buffer是获取完全的
所以直接改代码:
static Data getData(const std::string& filename, bool forString) { if (filename.empty()) { return Data::Null; } Data ret; unsigned char* buffer = nullptr; size_t size = 0; size_t readsize; const char* mode = nullptr; if (forString) mode = "rt"; else mode = "rb"; std::string lastname = FileUtils::getInstance()->fullPathForFilename(filename); lastname = lastname.substr(lastname.length()-5,lastname.length()); do { // Read the file from hardware std::string fullPath = FileUtils::getInstance()->fullPathForFilename(filename); FILE *fp = fopen(fullPath.c_str(), mode); CC_BREAK_IF(!fp); fseek(fp,0,SEEK_END); size = ftell(fp); fseek(fp,0,SEEK_SET); if (forString) { buffer = (unsigned char*)malloc(sizeof(unsigned char) * (size + 1)); buffer[size] = '\0'; } else { buffer = (unsigned char*)malloc(sizeof(unsigned char) * size); } readsize = fread(buffer, sizeof(unsigned char), size, fp); fclose(fp); if (forString && readsize < size) { buffer[readsize] = '\0'; } } while (0); if (nullptr == buffer || 0 == readsize) { std::string msg = "Get data from file("; msg.append(filename).append(") failed!"); CCLOG("%s", msg.c_str()); } else { if(lastname == "_jm.d") { for (int i = 0; i<readsize; i++) { buffer[i]=MD5(buffer[i]); } buffer[readsize]=buffer[readsize]-MD5size; } ret.fastSet(buffer, readsize); } return ret; }
红色代码是我们自定义加密的文件解密,不管你用什么加密,或者修改地址扰码,只要保证加密解密格式相同;
OK,解密就算完成了,但是注意还有资源类型需要判断,在2.X中,处理了EImageFormat的判断,可以定义资源类型
但是还是建议还是不要去大改源码,3.2以上版本已经非常简洁强大了
3.X中整合了Format后,也有资源类型另外一种方式定义_fileType = detectFormat(unpackedData, unpackedLen);
我们解密后的资源类型在3.X中是行不通的,是Format::UNKOWN;
那么就在不大改的情况下重载函数方式解决这个问题:
重载initWithImageData,在CCImage.h中CCImage.cpp中
.h添加:
/* jmflag 加密标识 */ bool initWithImageData(const unsigned char * data, ssize_t dataLen,bool jmflag);
.cpp添加:
bool Image::initWithImageData(const unsigned char * data, ssize_t dataLen,bool jmflag) { bool ret = false; do { CC_BREAK_IF(! data || dataLen <= 0); unsigned char* unpackedData = nullptr; ssize_t unpackedLen = 0; //detecgt and unzip the compress file if (ZipUtils::isCCZBuffer(data, dataLen)) { unpackedLen = ZipUtils::inflateCCZBuffer(data, dataLen, &unpackedData); } else if (ZipUtils::isGZipBuffer(data, dataLen)) { unpackedLen = ZipUtils::inflateMemory(const_cast<unsigned char*>(data), dataLen, &unpackedData); } else { unpackedData = const_cast<unsigned char*>(data); unpackedLen = dataLen; } if(jmflag == true) { _fileType=Format::PNG; } ret = initWithPngData(unpackedData, unpackedLen); if(unpackedData != data) { free(unpackedData); } } while (0); return ret; }
另外在initWithImageFile函数中得修改调用方法
CCImage.cpp:
Data data = FileUtils::getInstance()->getDataFromFile(_filePath); if (!data.isNull()) { std::string lastname = _filePath; lastname = lastname.substr(lastname.length()-5,lastname.length()); if(lastname=="_jm.d") { ret = initWithImageData(data.getBytes(), data.getSize(),true); } else { ret = initWithImageData(data.getBytes(), data.getSize()); } }
OK,自定义类型的资源解密就搞定了,但这只处理了PNG图片类型的加密,其他图片类型资源可以按照这种方式处理。
好了,那么有同学问,那么JS,Lua脚本的解密呢?
看这里
ScirptingCore.cpp:
// Check whether '.jsc' files exist to avoid outputing log which says 'couldn't find .jsc file'. CCLOG("byteCodePath > %s",byteCodePath.c_str()); if (futil->isFileExist(byteCodePath)) { Data data = futil->getDataFromFile(byteCodePath); if (!data.isNull()) { script = JS_DecodeScript(cx, data.getBytes(), static_cast<uint32_t>(data.getSize()), nullptr, nullptr); } }
Data data = futil->getDataFromFile(byteCodePath);
对于脚本语言的加载读取还是上面我们已经改过的getDataFromFile方法噢!
但是还有一点
script = JS_DecodeScript(cx, data.getBytes(), static_cast<uint32_t>(data.getSize()), nullptr, nullptr);
这样是直接拿不到script的,JS_DecodeScript只是处理.jsc的,那么怎么给script复制呢?
代码修改如下:
//只解密scr下面目录文件 if(jmflag==true) { if (futil->isFileExist(jmfullPath)) { Data data = futil->getDataFromFile(jmfullPath,true); if (!data.isNull()) { script = JS::Compile(cx, obj, options, (const char*)data.getBytes(), data.getSize()); } } } else { script = JS::Compile(cx, obj, options, fullPath.c_str()); }
script = JS::Compile(cx, obj, options, (const char*)data.getBytes(), data.getSize());
用Compile的这个重载函数赋值就全部搞定了;Lua和JS的脚本代码解密也一样非常简单!
大功告成,再见!
PS一下广告时间:
我目前在录制cocos2dx-3.X系列的实战项目视频,可以完全来说是商业项目视频讲解,让你爱上更有趣的游戏开发方式;
9秒课堂会第一时间上线我们的实战视频!
可以让大家更快速的掌握cocos2dx-Js的游戏实战开发!现在用脚本做游戏已然成为一个大的趋势;
而且在手游游戏行业飞速发展的今天,如果不用一种快速上手的脚本语言做游戏开发岂不是会Out了
另外希望大家支持我们的游戏开发群:【41131516】