特别感谢: Deepsky 帮忙解答
♫⊹香草奶昔⌒ 提供话题在cocos2dx中,提供了CCFileUitl来进行文件操作,但是大家一般习惯的方式还是仅仅通过这个类获取路径,然后用fopen相关的函数来操作,大概如下:
string fullPath = fullPathForFilename(pszFileName);
FILE *fp = fopen(fullPath.c_str(), pszMode);
今天追了之后,发现,在android下,CCFileUtil有独立实现,在cocos2d-x-2.2.3/cocos2dx/platform/android/下面(引擎版本2.2.3),CCFileUtilsAndroid。其中getFileData的实现如下:
unsigned char* CCFileUtilsAndroid::getFileData(const char* pszFileName, const char* pszMode, unsigned long * pSize)
{
return doGetFileData(pszFileName, pszMode, pSize, false);
}
unsigned char* CCFileUtilsAndroid::getFileDataForAsync(const char* pszFileName, const char* pszMode, unsigned long * pSize)
{
return doGetFileData(pszFileName, pszMode, pSize, true);
}
unsigned char* CCFileUtilsAndroid::doGetFileData(const char* pszFileName, const char* pszMode, unsigned long * pSize, bool forAsync)
{
unsigned char * pData = 0;
if ((! pszFileName) || (! pszMode) || 0 == strlen(pszFileName))
{
return 0;
}
string fullPath = fullPathForFilename(pszFileName);
if (fullPath[0] != '/')
{
if (forAsync)
{ /********** !!!注意啊 ***********/
pData = s_pZipFile->getFileData(fullPath.c_str(), pSize, s_pZipFile->_dataThread);
}
else
{
pData = s_pZipFile->getFileData(fullPath.c_str(), pSize);
}
}
else
{
do
{
FILE *fp = fopen(fullPath.c_str(), pszMode);
CC_BREAK_IF(!fp);
unsigned long size;
fseek(fp,0,SEEK_END);
size = ftell(fp);
fseek(fp,0,SEEK_SET);
pData = new unsigned char[size];
size = fread(pData,sizeof(unsigned char), size,fp);
fclose(fp);
if (pSize)
{
*pSize = size;
}
} while (0);
}
if (! pData)
{
std::string msg = "Get data from file(";
msg.append(pszFileName).append(") failed!");
CCLOG("%s", msg.c_str());
}
return pData;
}
注意上面代码里面我标记注释的地方,
有个东西叫s_pZipFile,之前看到这里,没注意这个东西。仔细一看,前面的fullpath的返回值,是一个相对于asset/的文件,也就是说,基本上都会走这个if,那么这个s_pZipFile又是怎么定义的。
#include "support/zip_support/ZipUtils.h"
// record the zip on the resource path
static ZipFile *s_pZipFile = NULL;
CCFileUtils* CCFileUtils::sharedFileUtils()
{
if (s_sharedFileUtils == NULL)
{
s_sharedFileUtils = new CCFileUtilsAndroid();
s_sharedFileUtils->init();
std::string resourcePath = getApkPath();
s_pZipFile = new ZipFile(resourcePath, "assets/");
}
return s_sharedFileUtils;
}
重要的头文件我都留在上面了,所以其实上面那个getFileData的实现是这样的
unsigned char *ZipFile::getFileData(const std::string &fileName, unsigned long *pSize, ZipFilePrivate *data)
{
unsigned char * pBuffer = NULL;
if (pSize)
{
*pSize = 0;
}
do
{
CC_BREAK_IF(!data->zipFile);
CC_BREAK_IF(fileName.empty());
ZipFilePrivate::FileListContainer::const_iterator it = data->fileList.find(fileName);
CC_BREAK_IF(it == data->fileList.end());
ZipEntryInfo fileInfo = it->second;
int nRet = unzGoToFilePos(data->zipFile, &fileInfo.pos);
CC_BREAK_IF(UNZ_OK != nRet);
nRet = unzOpenCurrentFile(data->zipFile);
CC_BREAK_IF(UNZ_OK != nRet);
pBuffer = new unsigned char[fileInfo.uncompressed_size];
int CC_UNUSED nSize = unzReadCurrentFile(data->zipFile, pBuffer, fileInfo.uncompressed_size);
CCAssert(nSize == 0 || nSize == (int)fileInfo.uncompressed_size, "the file size is wrong");
if (pSize)
{
*pSize = fileInfo.uncompressed_size;
}
unzCloseCurrentFile(data->zipFile);
} while (0);
return pBuffer;
}
基本上到这,问题就清楚了,
总结起来就是2点:
一、assets其实是一个zip压缩文件,直接读取里面的内容是不行的。
二、android的实现和其他2个平台不一样。
解决办法:
最简单的,直接用CCFileUtil的getFileData实现文件读取。
不然就从上面提到的文件去改。。。
其他方法,参考:1、 http://www.cppblog.com/johndragon/archive/2012/12/28/196754.html
2、 http://blog.csdn.net/happyhell/article/details/7414110