Sloppy's Blog

Cocos2d-x文件的压缩

公司最近的游戏上了AppleStore,打开游戏的详细页面。策划突然发现游戏好大啊。怎么显示的是347M,之前上的另外一款游戏,我们没有注意这块,只是光顾着玩了。而实际我们做出来的IPA只有140多M,咨询几个超级资深的IOS开发人员,AppleStore显示的是IPA展开后的大小。但是玩家不这么认为啊。以为下载的游戏这么大。为了减小大小,看来只要压缩原始文件大小了,经过分析,特做出以下的方法:

  • 图片文件,声音文件,不压缩,因为压缩比几乎没有。
  • 压缩相关的文本文件,Plist,Json文件。尤其是动画文件,我们用的是ExportJson格式。大的惊人

在这里,做了一下简单的文件格式定义,文件由三部分构成,如下

  • 在每个压缩的文件头部加eraygames做压缩标记(9字节长),(用来判断文件是否压缩)
  • 然后是四个字节描述压缩前的文件原始长度 (用来解压)
  • 然后是文件压缩后的内容

资源的压缩,可以用Python脚本语言,在项目打包之前做运行一下命令即可,下面主要修改Cocos2d-x的引擎部分CCFileUtils,因为所有的文件加载全部在这个类里。而最终的入口在FileUtils::getContents方法里。当然其他平台,在对应的CCFileUtils-Android,CCFileUtils-win32,里。修改如下,在CCFileUtils.h头文件添加方法如下:

protected:
    enum COMPRESS_CODE
    {
        noCompress = 0,
        unCompressOK,
        unCompressError,
    };
    COMPRESS_CODE checkIsCompressFile(ResizableBuffer* buffer,int size);
private:
    int m_ITagLen;
    int m_ISizeLen;
CCFileUtils.cpp如下:
FileUtils::COMPRESS_CODE FileUtils::checkIsCompressFile(ResizableBuffer* buffer, int size) {
    if (size <= m_itaglen="" +="" m_isizelen)="" {="" return="" nocompress;="" }="" unsigned="" char="" *="" sdata="new" char[m_itaglen="" 1];="" memcpy(sdata,="" buffer-="">buffer(), m_ITagLen);
    sData[m_ITagLen] = '\0';
    std::string tag((char*)sData);
    if (tag.find("eraygames") != std::string::npos) {
        CC_SAFE_DELETE_ARRAY(sData);
        sData = new unsigned char[m_ISizeLen];
        memcpy(sData, (unsigned char*)buffer->buffer() + m_ITagLen, m_ISizeLen);
        std::reverse(&sData[0], &sData[m_ISizeLen]);
        uLong fileLen = 0;
        memcpy(&fileLen, sData, m_ISizeLen);
        CC_SAFE_DELETE_ARRAY(sData);
        int compressSize = size - m_ITagLen - m_ISizeLen;
        sData = new unsigned char[compressSize];
        memcpy(sData, (unsigned char*)buffer->buffer() + m_ITagLen + m_ISizeLen, compressSize);
        unsigned char* fileData = new unsigned char[fileLen];
        bool isCompress = true;
        do
        {
            if (uncompress(fileData, &fileLen, sData, compressSize) != Z_OK) {
                isCompress = false;
                break;
            }
            buffer->setBuffer(fileData, fileLen);
        } while (0);
        CC_SAFE_DELETE_ARRAY(sData);
        CC_SAFE_DELETE_ARRAY(fileData);
        if (!isCompress) {
            CCLOG("UnCompress Error");
            return unCompressError;
        }
        return unCompressOK;
    }
    return noCompress;
}

然后修改对应的getContents方法。在方法最后一句的前面添加,我修改CCFileUtils-win32.cpp

FileUtils::Status FileUtilsWin32::getContents(const std::string& filename, ResizableBuffer* buffer)
{
      // 此处省略其他代码
    if (!successed) {
        CCLOG("Get data from file(%s) failed, error code is %s", filename.data(), std::to_string(::GetLastError()).data());
        buffer->resize(sizeRead);
        return FileUtils::Status::ReadFailed;
    }
    FileUtils::COMPRESS_CODE code = checkIsCompressFile(buffer, size);
    if (code == FileUtils::COMPRESS_CODE::unCompressError) {
        return FileUtils::Status::ReadFailed;
    }
    return FileUtils::Status::OK;
}

最近项目升级引擎到3.13.1,以上所有的研究代码基于此版本