cocos2dx-2.x CCFileUtils文件管理类分析

前端之家收集整理的这篇文章主要介绍了cocos2dx-2.x CCFileUtils文件管理类分析前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

cocos2dx文件管理类是一个很重要的类,这里对这个类进行一下分析:
CCFileUtils是文件管理类的基类,不同平台下android,ios,win32都有
继承于这个类的子类,如android
class CC_DLL CCFileUtilsAndroid : public CCFileUtils

1、单例类:
static CCFileUtils* sharedFileUtils()
而实现却在CCFileUtilsAndroid.cpp文件中:并且创建的是各个平台下的子类实例
CCFileUtils* CCFileUtils::sharedFileUtils()
{
    if (s_sharedFileUtils == NULL)
    {
        s_sharedFileUtils = new CCFileUtilsAndroid();
        s_sharedFileUtils->init();

	//获取apk包的路径,这里是java端设置的。
        std::string resourcePath = getApkPath();

	// record the zip on the resource path
        // static ZipFile *s_pZipFile = NULL;
	// 因为android的很多资源是放在安装包里的assets文件里,
	//所以获取资源是需要从包里解压,这就用到了ZipFile类
        s_pZipFile = new ZipFile(resourcePath,"assets/");
    }
    return s_sharedFileUtils;
}

2、初始化:
bool CCFileUtilsAndroid::init()
{
    m_strDefaultResRootPath = "assets/"; //默认的资源路径,默认是安装包
    return CCFileUtils::init(); -->> 1
}
1 -->> 
bool CCFileUtils::init()
{
    //m_searchPathArray -- 资源搜索路径数组
    //m_searchResolutionsOrderArray -- 资源分辨率数组
    m_searchPathArray.push_back(m_strDefaultResRootPath);
    m_searchResolutionsOrderArray.push_back("");
    return true;
}




在1中,我只是针对整体结构进行了分析,那么在2中,我将会对一些我们常用的函数进行分析。

    //获取给定文件名的全路径
    //下面这很长一段注释,通过举例子,像我们说明cocos2dx获取文件全路径的规则。
    //这段我就不翻译了,直接通过代码来看。
    /** Returns the fullpath for a given filename.
     
     First it will try to get a new filename from the "filenameLookup" dictionary.
     If a new filename can't be found on the dictionary,it will use the original filename.
     Then it will try to obtain the full path of the filename using the CCFileUtils search rules: resolutions,and search paths.
     The file search is based on the array element order of search paths and resolution directories.
     
     For instance:

     	We set two elements("/mnt/sdcard/","internal_dir/") to search paths vector by setSearchPaths,and set three elements("resources-ipadhd/","resources-ipad/","resources-iphonehd")
     	to resolutions vector by setSearchResolutionsOrder. The "internal_dir" is relative to "Resources/".

		If we have a file named 'sprite.png',the mapping in fileLookup dictionary contains `key: sprite.png -> value: sprite.pvr.gz`.
     	Firstly,it will replace 'sprite.png' with 'sprite.pvr.gz',then searching the file sprite.pvr.gz as follows:

     	    /mnt/sdcard/resources-ipadhd/sprite.pvr.gz      (if not found,search next)
     	    /mnt/sdcard/resources-ipad/sprite.pvr.gz        (if not found,search next)
     	    /mnt/sdcard/resources-iphonehd/sprite.pvr.gz    (if not found,search next)
     	    /mnt/sdcard/sprite.pvr.gz                       (if not found,search next)
     	    internal_dir/resources-ipadhd/sprite.pvr.gz     (if not found,search next)
     	    internal_dir/resources-ipad/sprite.pvr.gz       (if not found,search next)
     	    internal_dir/resources-iphonehd/sprite.pvr.gz   (if not found,search next)
     	    internal_dir/sprite.pvr.gz                      (if not found,return "sprite.png")

        If the filename contains relative path like "gamescene/uilayer/sprite.png",and the mapping in fileLookup dictionary contains `key: gamescene/uilayer/sprite.png -> value: gamescene/uilayer/sprite.pvr.gz`.
        The file search order will be:

     	    /mnt/sdcard/gamescene/uilayer/resources-ipadhd/sprite.pvr.gz      (if not found,search next)
     	    /mnt/sdcard/gamescene/uilayer/resources-ipad/sprite.pvr.gz        (if not found,search next)
     	    /mnt/sdcard/gamescene/uilayer/resources-iphonehd/sprite.pvr.gz    (if not found,search next)
     	    /mnt/sdcard/gamescene/uilayer/sprite.pvr.gz                       (if not found,search next)
     	    internal_dir/gamescene/uilayer/resources-ipadhd/sprite.pvr.gz     (if not found,search next)
     	    internal_dir/gamescene/uilayer/resources-ipad/sprite.pvr.gz       (if not found,search next)
     	    internal_dir/gamescene/uilayer/resources-iphonehd/sprite.pvr.gz   (if not found,search next)
     	    internal_dir/gamescene/uilayer/sprite.pvr.gz                      (if not found,return "gamescene/uilayer/sprite.png")

     If the new file can't be found on the file system,it will return the parameter pszFileName directly.

     @since v2.1
     */
    virtual std::string fullPathForFilename(const char* pszFileName);

    -->>
    std::string CCFileUtils::fullPathForFilename(const char* pszFileName)
{
    CCAssert(pszFileName != NULL,"CCFileUtils: Invalid path");
    
    //判断是否是绝对路径,如果是绝对路径就直接返回。
    /*
        //android下 判断依据就是是否以'/'开头或者以assets/开头。下面这个函数,注释的很清楚。
	//你可以做个实验:
	//例: Get data from file(/second_bg.png) Failed! 我在创建精灵时传递/second_bg.png路径
	bool CCFileUtilsAndroid::isAbsolutePath(const std::string& strPath)
	{
	    // On Android,there are two situations for full path. 
	    // 1) Files in APK,e.g. assets/path/path/file.png
	    // 2) Files not in APK,e.g. /data/data/org.cocos2dx.hellocpp/cache/path/path/file.png,or /sdcard/path/path/file.png.
	    // So these two situations need to be checked on Android.
	    if (strPath[0] == '/' || strPath.find(m_strDefaultResRootPath) == 0)
	    {
		return true;
	    }
	    return false;
	}
    */
    std::string strFileName = pszFileName;
    if (isAbsolutePath(pszFileName))
    {
        //CCLOG("Return absolute path( %s ) directly.",pszFileName);
        return pszFileName;
    }
    
    // Already Cached ?
    //是否已经缓存,如果缓存过,直接返回
    std::map<std::string,std::string>::iterator cacheIter = m_fullPathCache.find(pszFileName);
    if (cacheIter != m_fullPathCache.end())
    {
        //CCLOG("Return full path from cache: %s",cacheIter->second.c_str());
        return cacheIter->second;
    }
    
    /*
	std::string CCFileUtils::getNewFilename(const char* pszFileName)
	{
	    const char* pszNewFileName = NULL;
	    // in Lookup Filename dictionary ?
	    //可以把这个m_pFilenameLookupDict(默认为NULL)字典理解为一种查找
	    //比如这个字典里存了一个"fish.png(key)" --> "big_fish.png(value)"
	    //那么我们传入fish.png是,就会给我们转化为big_fish.png。如果没有,则返回我们传入的。
	    CCString* fileNameFound = m_pFilenameLookupDict ? (CCString*)m_pFilenameLookupDict->objectForKey(pszFileName) : NULL;
	    if( NULL == fileNameFound || fileNameFound->length() == 0) {
		pszNewFileName = pszFileName;
	    }
	    else {
		pszNewFileName = fileNameFound->getCString();
		//CCLOG("FOUND NEW FILE NAME: %s.",pszNewFileName);
	    }
	    return pszNewFileName;
	}
    */
    // Get the new file name.
    std::string newFilename = getNewFilename(pszFileName);
    
    string fullpath = "";
    
    //下面这一段很关键:
    //m_searchPathArray 前面介绍过搜索路径数组,需要我们手动设置。android的初始话会添加一个默认值为
    //m_searchPathArray.push_back(m_strDefaultResRootPath)即,"assets/"。

    /* m_searchResolutionsOrderArray 可以理解为分辨率搜索顺序,就按开头注释说明的那样
        //m_searchPathArray
	We set two elements("/mnt/sdcard/",//m_searchResolutionsOrderArray
     	and set three elements("resources-ipadhd/","resources-iphonehd")
     	to resolutions vector by setSearchResolutionsOrder. 

        //组合后的路径
	/mnt/sdcard/resources-ipadhd/sprite.pvr.gz      (if not found,search next)
     	/mnt/sdcard/resources-ipad/sprite.pvr.gz        (if not found,search next)
     	/mnt/sdcard/resources-iphonehd/sprite.pvr.gz    (if not found,search next)

	总结:从这里可以看出,m_searchPathArray在前面的路径,会优先搜索,m_searchResolutionsOrderArray也一样。
    */
    for (std::vector<std::string>::iterator searchPathsIter = m_searchPathArray.begin();
         searchPathsIter != m_searchPathArray.end(); ++searchPathsIter) {
        for (std::vector<std::string>::iterator resOrderIter = m_searchResolutionsOrderArray.begin();
             resOrderIter != m_searchResolutionsOrderArray.end(); ++resOrderIter) {
            
            //CCLOG("\n\nSEARCHING: %s,%s,%s",newFilename.c_str(),resOrderIter->c_str(),searchPathsIter->c_str());
            
	    //下面我分析一下这个函数:-->> 2
            fullpath = this->getPathForFilename(newFilename,*resOrderIter,*searchPathsIter);
            
	    //这里会对找到的路径,进行缓存
            if (fullpath.length() > 0)
            {
                // Using the filename passed in as key.
                m_fullPathCache.insert(std::pair<std::string,std::string>(pszFileName,fullpath));
                //CCLOG("Returning path: %s",fullpath.c_str());
                return fullpath;
            }
        }
    }
    
    //CCLOG("cocos2d: fullPathForFilename: No file found at %s. Possible missing file.",pszFileName);

    // The file wasn't found,return the file name passed in.
    return pszFileName;
}

--> 2
//filename -- 传入的文件名
//searchPath -- 搜索路径
//resolutionDirectory -- 资源分辨率路径
std::string CCFileUtils::getPathForFilename(const std::string& filename,const std::string& resolutionDirectory,const std::string& searchPath)
{
    std::string file = filename;
    std::string file_path = "";
    size_t pos = filename.find_last_of("/");
    if (pos != std::string::npos)
    {
        file_path = filename.substr(0,pos+1);
        file = filename.substr(pos+1);
    }
    
    //如果传入的"gamescene/uilayer/sprite.png"是这样的路径,那么进行一定的处理,
    //处理成:path = searchPath + gamescene/uilayer/ + resourceDirectory
              file = sprite.png
    ///mnt/sdcard/ gamescene/uilayer/ resources-ipadhd/sprite.pvr.gz     
    // searchPath + file_path + resourceDirectory
    std::string path = searchPath;
    path += file_path;
    path += resolutionDirectory;
    
    path = getFullPathForDirectoryAndFilename(path,file);
    
    //CCLOG("getPathForFilename,fullPath = %s",path.c_str());
    return path;
}

-->>
std::string CCFileUtils::getFullPathForDirectoryAndFilename(const std::string& strDirectory,const std::string& strFilename)
{
    std::string ret = strDirectory+strFilename;
    //如果文件存在,就把文件的路径返回,这个路径可能是绝对路径,也可能是包里的路径
    if (!isFileExist(ret)) {
        ret = "";
    }
    return ret;
}

-->>

//把上面合成的整个文件路径传进去,判断文件是否存在
bool CCFileUtilsAndroid::isFileExist(const std::string& strFilePath)
{
    if (0 == strFilePath.length())
    {
        return false;
    }

    bool bFound = false;
    
    // Check whether file exists in apk.
    //如果不是以'/'开头,就在android包里查找
    if (strFilePath[0] != '/')
    {
	//如果不是以"assets/"开头,则插入
        std::string strPath = strFilePath;
        if (strPath.find(m_strDefaultResRootPath) != 0)
        {// Didn't find "assets/" at the beginning of the path,adding it.
            strPath.insert(0,m_strDefaultResRootPath);
        }
        
	//在安装包里查找,看是否存在
        if (s_pZipFile->fileExists(strPath))
        {
            bFound = true;
        } 
    }
    else
    {
        //如果是绝对路径,看否打开成功,如果成功,则证明文件存在。
        FILE *fp = fopen(strFilePath.c_str(),"r");
        if(fp)
        {
            bFound = true;
            fclose(fp);
        }
    }
    return bFound;
}

总结:这里需要知道一点,就是先加载搜索路径的路径,会优先搜索到。
      比如热更新,我们只要把更新路径设置在前面即可。

在2中,我们分析了几个函数,在这一篇中我们继续分析其他一些函数。
1、
在2中,多次用到了m_searchPathArray(搜索路径),那这个搜索路径怎么来的呢?
我们可以通过setSearchPaths这个函数来设置搜索路径
void CCFileUtils::setSearchPaths(const std::vector<std::string>& searchPaths)
{
    bool bExistDefaultRootPath = false;

    //先把以前的清空,包括缓存路径
    m_fullPathCache.clear();
    m_searchPathArray.clear(); 

    //逐个加入到m_searchPathArray
    for (std::vector<std::string>::const_iterator iter = searchPaths.begin(); iter != searchPaths.end(); ++iter)
    {
        std::string strPrefix;
        std::string path;

	//如果不是绝对路径,android的则加上"assets/"前缀,表明需要去安装包里找
        if (!isAbsolutePath(*iter))
        { // Not an absolute path
            strPrefix = m_strDefaultResRootPath;
        }

	//如果路径不是以'/'结尾,则在结尾加上'/'
        path = strPrefix+(*iter);
        if (path.length() > 0 && path[path.length()-1] != '/')
        {
            path += "/";
        }
        if (!bExistDefaultRootPath && path == m_strDefaultResRootPath)
        {
            bExistDefaultRootPath = true;
        }
        m_searchPathArray.push_back(path);
    }
    
    if (!bExistDefaultRootPath)
    {
        //如果m_strDefaultResRootPath默认路径不在m_searchPathArray,则加入进来
        //CCLOG("Default root path doesn't exist,adding it.");
        m_searchPathArray.push_back(m_strDefaultResRootPath);
    }
}

-->>
void CCFileUtils::addSearchPath(const char* path_)
{
    std::string strPrefix;
    std::string path(path_);
    if (!isAbsolutePath(path))
    { // Not an absolute path
        strPrefix = m_strDefaultResRootPath;
    }
    path = strPrefix + path;
    if (path.length() > 0 && path[path.length()-1] != '/')
    {
        path += "/";
    }
    m_searchPathArray.push_back(path);
}

//移除一个搜索路径:
void CCFileUtils::removeSearchPath(const char *path_)
{
	std::string strPrefix;
	std::string path(path_);
	if (!isAbsolutePath(path))
	{ // Not an absolute path
		strPrefix = m_strDefaultResRootPath;
	}
	path = strPrefix + path;
	if (path.length() > 0 && path[path.length()-1] != '/')
	{
		path += "/";
	}
	std::vector<std::string>::iterator iter = std::find(m_searchPathArray.begin(),m_searchPathArray.end(),path);
	m_searchPathArray.erase(iter);
}

//移除全部
void CCFileUtils::removeAllPaths()
{
	m_searchPathArray.clear();
}

2、
m_searchResolutionsOrderArray资源路径和上面的一样处理方式,就不说了。

3、
从pszRelativeFile这个文件的相对路径中,得到pszFilename文件的全路径,
其实就是找到pszRelativeFile文件的最后一个'/',然后去这个'/'前的所有字符 + pszFilename即可。
const char* CCFileUtils::fullPathFromRelativeFile(const char *pszFilename,const char *pszRelativeFile)
{
    std::string relativeFile = pszRelativeFile;
    CCString *pRet = CCString::create("");
    pRet->m_sString = relativeFile.substr(0,relativeFile.rfind('/')+1);
    pRet->m_sString += getNewFilename(pszFilename);
    return pRet->getCString();
}

4、
//android下的可读写路径
string CCFileUtilsAndroid::getWritablePath()
{
    // Fix for Nexus 10 (Android 4.2 multi-user environment)
    // the path is retrieved through Java Context.getCacheDir() method
    string dir("");
    //pContext.getFilesDir().getAbsolutePath() java端
    string tmp = getFileDirectoryJNI(); //pContext.getFilesDir().getAbsolutePath()

    if (tmp.length() > 0)
    {
        dir.append(tmp).append("/");

        return dir;
    }
    else
    {
        return "";
    }
}

在3中,我们又分析了几个函数,在这一篇中我们继续分析其他一些函数。

1、android平台
unsigned char* CCFileUtilsAndroid::getFileData(const char* pszFileName,const char* pszMode,unsigned long * pSize)
{    
    return doGetFileData(pszFileName,pszMode,pSize,false);
}

-->>
//pszFileName 文件名
//pszMode 读取模式,只有对绝对路径有用,参数就是C API的类型
//pSize 读出的字节大小
//forAsync 同步还是异步
unsigned char* CCFileUtilsAndroid::doGetFileData(const char* pszFileName,unsigned long * pSize,bool forAsync)
{
    unsigned char * pData = 0;
    
    if ((! pszFileName) || (! pszMode) || 0 == strlen(pszFileName))
    {
        return 0;
    }
    
    //先获取文件的全路径
    string fullPath = fullPathForFilename(pszFileName);
    
    if (fullPath[0] != '/')
    {
        //如果以"assets/"开头,即文件在安装包里,那么通过压缩文件类从压缩包中读取,
	//其实android的安装包就是压缩包。
        if (forAsync)
        {
            pData = s_pZipFile->getFileData(fullPath.c_str(),s_pZipFile->_dataThread);
        }
        else
        {
            pData = s_pZipFile->getFileData(fullPath.c_str(),pSize);
        }
    }
    else
    {
        do
        {   
	     //如果是绝对路径,则通过C API读取。
            // read rrom other path than user set it
	        //CCLOG("GETTING FILE ABSOLUTE DATA: %s",pszFileName);
            FILE *fp = fopen(fullPath.c_str(),pszMode);
            CC_BREAK_IF(!fp);
            
            unsigned long size;
            fseek(fp,SEEK_END);
            size = ftell(fp);
            fseek(fp,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;
}

总结:到此为止,cocos2dx-2.X android平台的文件读出我已经通过源码的形式分析完了,做个记录,
以防忘记。

猜你在找的Cocos2d-x相关文章