玩儿转Cocos 文件系统

#如何玩转 Cocos 文件系统

1.资源是如何被找到的

   没做开发以前一直在想一个问题,文件如何被计算机找到并且执行? 当知道计算机在执行 0101 的二进制文件时候就又在想,我们的图片、声音等文件应该是变成了机器代码之后才能被读出来和执行。于是抱着这种想法开始了codeing。偶然有一次发现,Android 的 apk 包竟然可以用zip 解压工具解开,里面竟然找到了一些未经过压缩的图片和其他资源。在好奇心的驱使下就想要搞明白计算机是如何做到的。在玩儿 Cocos 的过程中,接触到了FileUtils这个类文件。顺着源码部分我们去看,发现在经过引擎一系列优化以后,查找文件代码最终调用了系统方法,根据文件的名字,在APP的指定路径(包里面或者可读写路径下)返回资源文件的全路径。

   似乎经过上面一说,整片文章就可以结束了。以Cocos lua 为例子 我们尝试列一下流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//先说一下 luafile  部分
// cocos lua 部分入口是在c++ 初始化 LuaEngine 以后执行lua 代码
if (engine->executeScriptFile("src/main.lua"))
// LuaEngine 之后做了如下操作
LuaStack 去执行了 executeScriptFile(filename);
// 判断结尾,确认是 lua 还是 luac
std::string fullPath = utils->fullPathForFilename(buf);
Data data = utils->getDataFromFile(fullPath);

// data 存在 执行
if (luaLoadBuffer(_state, (const char*)data.getBytes(), (int)data.getSize(), fullPath.c_str()) == 0)
{
rn = executeFunction(0);
}

   脚本文件在执行之前,需要通过引擎代码 FileUtils 进行定位文件具体位置,然后获取文件buffer 内容和长度以后进行执行。

   我们于是可能会好奇图片文件是如何被载入和执行的,上面的流程是否满足图片资源的需求 ?

1
2
3
4
5
6
7
8
9
10
11
12
// Talk is cheap , show me the code
Sprite::create(fileName)
// 跟踪fileName
if (sprite && sprite->initWithFile(filename))
//Cocos 使用了 Texure2d
Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(filename);
//继续关心fileName 是如何最后变成 Texture2D 对象的
std::string fullpath = FileUtils::getInstance()->fullPathForFilename(path);
bool bRet = image->initWithImageFile(fullpath);
// 于是继续向下查找
_filePath = FileUtils::getInstance()->fullPathForFilename(path);
Data data = FileUtils::getInstance()->getDataFromFile(_filePath);

   似乎图片资源从file到 机器可识别的Data 也需要关键的两步,findPath ,getData

   我们可以继续去验证其他后缀结尾的文件,大多数都在执行同样的流程。为甚叫做大多数呢 ? 当你实验到音频文件的时候,例如 mp3 ,最后执行的貌似并不是当前的流程。读代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 感兴趣的点在哪里
AudioEngine::play2d
//屡一下源码部分 容错处理
if ( !FileUtils::getInstance()->isFileExist(filePath)){
break;
}
ret = _audioEngineImpl->play2d(filePath, loop, volume);
//在ios一侧 使用了 SimpleAudioEngine
-(void) playBackgroundMusic:(NSString*) filePath
{
[am playBackgroundMusic:filePath loop:TRUE];
}
// 看到OC 代码的时候 就会发现,对于 SimpleAudioEngine 库来讲,需要的仅仅是文件的绝对地址 ,这个音频库会找到 文件然后进行播放

   我们希望引擎中代码部分和整体的设计流程保持一致,大多数文件是保持了先经过 FileUtils 获取到文件在设备存放的位置,然后读取file Buffer 之后再次加工处理。

   设计结构的时候,我们经常会对内容进行归类,找出共同点,然后进行粗略处理,最后不同之处做区分。现实情况中我们总会发现Cocos原生打包内容资源部分容易被盗,脚本代码也很容易被还原,安全方面几乎无法保证,虽然逻辑部分能正常work 。

   代码的主要逻辑在于如何获取文本 BufferLength 面对上面的常见问题, 所以具体原始文件是什么形式我们不是特别关心,只是要给到 fileName 我们正确的返回 BufferLength 就可以了。 加入文件名字是被算法加密了呢?假如文本内容是被高效率解析加密函数加密了呢?很多事情貌似可以直接上手做。设想和做是两回事儿。

   万一文件名字和内容都被一个加密算法加密了呢 ?比如ZIP

   为什么是zip ? 在读FileUtils 类的时候有一个函数很吸引人 FileUtils::getFileDataFromZip

   选择ZIP会有一些优势,最简单常见的是可以给资源进行压缩和加密,如果是需要进行热更操作的资源呢?连续性大文件下载的优势就有了,而且具有压缩的特点,资源或者代码的热更时间会大大减少。有没有了解过zip文件的结构?考虑到每一个文件,你会关心哪些数据?简单理解ZIP文件为一大片连续的数据段?

   我们在读Cocos 提供的unzip文件的时候会发现几个比较有意思的函数

1
2
3
4
5
6
7
8
9
unzOpen
unzGoToFirstFile64
unzGetFilePos
unzGoToNextFile64


unzGoToFilePos
unzOpenCurrentFilePassword
unzReadCurrentFile

如果理解ZIP 文件为长串儿连续压缩文件的话也并不是不可以,只不过每一个文件头部都有包含一些信息:1.文件名字,无压缩文件大小,起始文件地址等。依靠这些文件已经可以把压缩文件解压缩读出。

Demo 暂时不列出,思路可以提供。