mirror of https://github.com/axmolengine/axmol.git
Android: Access to files from APK is boosted
Open APK file just once and cache a file list information (name and position inside archive). Also a general ZipFile helper class is created to read multiple files from Zip archives - might be used for another platforms if needed. Previously it was so for each separate file access operation: - Open zip archive, initially parse its structure (unzOpen) - Locate the particular file there by a linear search through full archive (unzLocateFile). So if file does not exist - still an archive has to be handled fully, even slower than a file exists. - Read file. - Close zip archive After this commit: - Once - open zip/apk file and collect file list information (average consumed time - the same like a search for 2 different files by unzLocateFile) - When needed - directly retrieve file position inside the archive, setting it up to the zip reader and reading file ZipFile class is located in support/zip_support/ZipUtils.h and .cpp to prevent creation of a new files and adding them to multiple project files.
This commit is contained in:
parent
168cde4099
commit
071d264cc5
|
@ -24,6 +24,7 @@ THE SOFTWARE.
|
||||||
|
|
||||||
#define __CC_PLATFORM_FILEUTILS_CPP__
|
#define __CC_PLATFORM_FILEUTILS_CPP__
|
||||||
#include "platform/CCFileUtilsCommon_cpp.h"
|
#include "platform/CCFileUtilsCommon_cpp.h"
|
||||||
|
#include "support/zip_support/ZipUtils.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
@ -36,6 +37,7 @@ NS_CC_BEGIN
|
||||||
static string s_strResourcePath = "";
|
static string s_strResourcePath = "";
|
||||||
|
|
||||||
static CCFileUtils* s_pFileUtils = NULL;
|
static CCFileUtils* s_pFileUtils = NULL;
|
||||||
|
static ZipFile *s_pZipFile = NULL;
|
||||||
|
|
||||||
CCFileUtils* CCFileUtils::sharedFileUtils()
|
CCFileUtils* CCFileUtils::sharedFileUtils()
|
||||||
{
|
{
|
||||||
|
@ -43,6 +45,7 @@ CCFileUtils* CCFileUtils::sharedFileUtils()
|
||||||
{
|
{
|
||||||
s_pFileUtils = new CCFileUtils();
|
s_pFileUtils = new CCFileUtils();
|
||||||
s_strResourcePath = getApkPath();
|
s_strResourcePath = getApkPath();
|
||||||
|
s_pZipFile = new ZipFile(s_strResourcePath, "assets/");
|
||||||
}
|
}
|
||||||
return s_pFileUtils;
|
return s_pFileUtils;
|
||||||
}
|
}
|
||||||
|
@ -94,13 +97,13 @@ unsigned char* CCFileUtils::getFileData(const char* pszFileName, const char* psz
|
||||||
|
|
||||||
fullPath.insert(0, m_obDirectory.c_str());
|
fullPath.insert(0, m_obDirectory.c_str());
|
||||||
fullPath.insert(0, "assets/");
|
fullPath.insert(0, "assets/");
|
||||||
pData = CCFileUtils::getFileDataFromZip(s_strResourcePath.c_str(), fullPath.c_str(), pSize);
|
pData = s_pZipFile->getFileData(fullPath, pSize);
|
||||||
|
|
||||||
if (! pData && m_obDirectory.size() > 0)
|
if (! pData && m_obDirectory.size() > 0)
|
||||||
{
|
{
|
||||||
// search from root
|
// search from root
|
||||||
pathWithoutDirectory.insert(0, "assets/");
|
pathWithoutDirectory.insert(0, "assets/");
|
||||||
pData = CCFileUtils::getFileDataFromZip(s_strResourcePath.c_str(), pathWithoutDirectory.c_str(), pSize);
|
pData = s_pZipFile->getFileData(pathWithoutDirectory, pSize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -28,9 +28,13 @@ THE SOFTWARE.
|
||||||
#include "ZipUtils.h"
|
#include "ZipUtils.h"
|
||||||
#include "ccMacros.h"
|
#include "ccMacros.h"
|
||||||
#include "platform/CCFileUtils.h"
|
#include "platform/CCFileUtils.h"
|
||||||
|
#include "unzip.h"
|
||||||
|
#include <map>
|
||||||
|
|
||||||
NS_CC_BEGIN
|
NS_CC_BEGIN
|
||||||
|
|
||||||
|
// --------------------- ZipUtils ---------------------
|
||||||
|
|
||||||
// memory in iPhone is precious
|
// memory in iPhone is precious
|
||||||
// Should buffer factor be 1.5 instead of 2 ?
|
// Should buffer factor be 1.5 instead of 2 ?
|
||||||
#define BUFFER_INC_FACTOR (2)
|
#define BUFFER_INC_FACTOR (2)
|
||||||
|
@ -280,4 +284,139 @@ int ZipUtils::ccInflateCCZFile(const char *path, unsigned char **out)
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --------------------- ZipFile ---------------------
|
||||||
|
// from unzip.cpp
|
||||||
|
#define UNZ_MAXFILENAMEINZIP 256
|
||||||
|
|
||||||
|
class ZipFilePrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
unzFile zipFile;
|
||||||
|
|
||||||
|
// std::unordered_map is faster if available on the platform
|
||||||
|
typedef std::map<std::string, unz_file_pos> FileListContainer;
|
||||||
|
FileListContainer fileList;
|
||||||
|
};
|
||||||
|
|
||||||
|
ZipFile::ZipFile(const std::string &zipFile, const std::string &filter)
|
||||||
|
: m_data(new ZipFilePrivate)
|
||||||
|
{
|
||||||
|
m_data->zipFile = unzOpen(zipFile.c_str());
|
||||||
|
if (m_data->zipFile)
|
||||||
|
{
|
||||||
|
setFilter(filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ZipFile::~ZipFile()
|
||||||
|
{
|
||||||
|
if (m_data && m_data->zipFile)
|
||||||
|
{
|
||||||
|
unzClose(m_data->zipFile);
|
||||||
|
}
|
||||||
|
CC_SAFE_DELETE(m_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZipFile::setFilter(const std::string &filter)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
CC_BREAK_IF(!m_data);
|
||||||
|
CC_BREAK_IF(!m_data->zipFile);
|
||||||
|
|
||||||
|
// clear existing file list
|
||||||
|
m_data->fileList.clear();
|
||||||
|
|
||||||
|
// go through all files and store position information about the required files
|
||||||
|
int err = unzGoToFirstFile(m_data->zipFile);
|
||||||
|
while (err == UNZ_OK)
|
||||||
|
{
|
||||||
|
unz_file_pos posInfo;
|
||||||
|
int posErr = unzGetFilePos(m_data->zipFile, &posInfo);
|
||||||
|
if (posErr == UNZ_OK)
|
||||||
|
{
|
||||||
|
// UNZ_MAXFILENAMEINZIP + 1 - it is done so in unzLocateFile
|
||||||
|
char szCurrentFileName[UNZ_MAXFILENAMEINZIP + 1];
|
||||||
|
int nameErr = unzGetCurrentFileInfo64(m_data->zipFile, NULL,
|
||||||
|
szCurrentFileName, sizeof(szCurrentFileName) - 1,
|
||||||
|
NULL, 0, NULL, 0);
|
||||||
|
std::string currentFileName = szCurrentFileName;
|
||||||
|
if (nameErr == UNZ_OK)
|
||||||
|
{
|
||||||
|
// cache info about filtered files only (like 'assets/')
|
||||||
|
if (filter.empty()
|
||||||
|
|| currentFileName.substr(0, filter.length()) == filter)
|
||||||
|
{
|
||||||
|
m_data->fileList[currentFileName] = posInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = unzGoToNextFile(m_data->zipFile);
|
||||||
|
}
|
||||||
|
ret = true;
|
||||||
|
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ZipFile::fileExists(const std::string &fileName) const
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
CC_BREAK_IF(!m_data);
|
||||||
|
|
||||||
|
ret = m_data->fileList.find(fileName) != m_data->fileList.end();
|
||||||
|
} while(false);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *ZipFile::getFileData(const std::string &fileName, unsigned long *pSize)
|
||||||
|
{
|
||||||
|
unsigned char * pBuffer = NULL;
|
||||||
|
if (pSize)
|
||||||
|
{
|
||||||
|
*pSize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
CC_BREAK_IF(!m_data->zipFile);
|
||||||
|
CC_BREAK_IF(fileName.empty());
|
||||||
|
|
||||||
|
ZipFilePrivate::FileListContainer::const_iterator it = m_data->fileList.find(fileName);
|
||||||
|
CC_BREAK_IF(it == m_data->fileList.end());
|
||||||
|
|
||||||
|
unz_file_pos filePos = it->second;
|
||||||
|
|
||||||
|
int nRet = unzGoToFilePos(m_data->zipFile, &filePos);
|
||||||
|
CC_BREAK_IF(UNZ_OK != nRet);
|
||||||
|
|
||||||
|
char szFilePathA[UNZ_MAXFILENAMEINZIP + 1];
|
||||||
|
unz_file_info FileInfo;
|
||||||
|
nRet = unzGetCurrentFileInfo(m_data->zipFile, &FileInfo,
|
||||||
|
szFilePathA, sizeof(szFilePathA) - 1, NULL, 0, NULL, 0);
|
||||||
|
CC_BREAK_IF(UNZ_OK != nRet);
|
||||||
|
|
||||||
|
nRet = unzOpenCurrentFile(m_data->zipFile);
|
||||||
|
CC_BREAK_IF(UNZ_OK != nRet);
|
||||||
|
|
||||||
|
pBuffer = new unsigned char[FileInfo.uncompressed_size];
|
||||||
|
int nSize = 0;
|
||||||
|
nSize = unzReadCurrentFile(m_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(m_data->zipFile);
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
return pBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
NS_CC_END
|
NS_CC_END
|
||||||
|
|
|
@ -24,6 +24,8 @@ THE SOFTWARE.
|
||||||
#ifndef __SUPPORT_ZIPUTILS_H__
|
#ifndef __SUPPORT_ZIPUTILS_H__
|
||||||
#define __SUPPORT_ZIPUTILS_H__
|
#define __SUPPORT_ZIPUTILS_H__
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace cocos2d
|
namespace cocos2d
|
||||||
{
|
{
|
||||||
/* XXX: pragma pack ??? */
|
/* XXX: pragma pack ??? */
|
||||||
|
@ -91,6 +93,68 @@ namespace cocos2d
|
||||||
unsigned int outLenghtHint);
|
unsigned int outLenghtHint);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end of namespace cocos2d
|
// forward declaration
|
||||||
#endif // __PLATFORM_WOPHONE_ZIPUTILS_H__
|
class ZipFilePrivate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zip file - reader helper class.
|
||||||
|
*
|
||||||
|
* It will cache the file list of a particular zip file with positions inside an archive,
|
||||||
|
* so it would be much faster to read some particular files or to check their existance.
|
||||||
|
*
|
||||||
|
* @since v2.0.5
|
||||||
|
*/
|
||||||
|
class ZipFile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Constructor, open zip file and store file list.
|
||||||
|
*
|
||||||
|
* @param zipFile Zip file name
|
||||||
|
* @param filter The first part of file names, which should be accessible.
|
||||||
|
* For example, "assets/". Other files will be missed.
|
||||||
|
*
|
||||||
|
* @since v2.0.5
|
||||||
|
*/
|
||||||
|
ZipFile(const std::string &zipFile, const std::string &filter = std::string());
|
||||||
|
virtual ~ZipFile();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regenerate accessible file list based on a new filter string.
|
||||||
|
*
|
||||||
|
* @param filter New filter string (first part of files names)
|
||||||
|
* @return true whenever zip file is open successfully and it is possible to locate
|
||||||
|
* at least the first file, false otherwise
|
||||||
|
*
|
||||||
|
* @since v2.0.5
|
||||||
|
*/
|
||||||
|
bool setFilter(const std::string &filter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check does a file exists or not in zip file
|
||||||
|
*
|
||||||
|
* @param fileName File to be checked on existance
|
||||||
|
* @return true whenever file exists, false otherwise
|
||||||
|
*
|
||||||
|
* @since v2.0.5
|
||||||
|
*/
|
||||||
|
bool fileExists(const std::string &fileName) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get resource file data from a zip file.
|
||||||
|
* @param fileName File name
|
||||||
|
* @param[out] pSize If the file read operation succeeds, it will be the data size, otherwise 0.
|
||||||
|
* @return Upon success, a pointer to the data is returned, otherwise NULL.
|
||||||
|
* @warning Recall: you are responsible for calling delete[] on any Non-NULL pointer returned.
|
||||||
|
*
|
||||||
|
* @since v2.0.5
|
||||||
|
*/
|
||||||
|
unsigned char *getFileData(const std::string &fileName, unsigned long *pSize);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** Internal data like zip file pointer / file list array and so on */
|
||||||
|
ZipFilePrivate *m_data;
|
||||||
|
};
|
||||||
|
} // end of namespace cocos2d
|
||||||
|
#endif // __SUPPORT_ZIPUTILS_H__
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue