基于StormLib,实现的一个MpqFileSystem,没有写入功能,因为游戏的资源包通常没有读取的需求,打包解包工具网上可以找到很多,就不再推荐了。
#ifndef __Mpq_H__
#define __Mpq_H__
#include "OgrePrerequisites.h"
#include "OgreArchive.h"
#include "OgreArchiveFactory.h"
#include "stormlib/StormLib.h"
// Forward declaration for zziplib to avoid header file dependency.
namespace Ogre {
/** Specialisation of the Archive class to allow reading of files from a zip
format source archive.
@remarks
This archive format supports all archives compressed in the standard
zip format, including iD pk3 files.
*/
class _OgreExport MpqArchive : public Archive
{
protected:
/// Handle to root zip file
HANDLE m_hMpq;
/// Handle any errors from zzip
void checkZzipError(int zzipError, const String& operation) const;
/// File list (since zziplib seems to only allow scanning of dir tree once)
FileInfoList mFileList;
public:
MpqArchive(const String& name, const String& archType );
~MpqArchive();
/// @copydoc Archive::isCaseSensitive
bool isCaseSensitive(void) const { return false; }
/// @copydoc Archive::load
void load();
/// @copydoc Archive::unload
void unload();
/// @copydoc Archive::open
DataStreamPtr open(const String& filename) const;
/// @copydoc Archive::list
StringVectorPtr list(bool recursive = true, bool dirs = false);
/// @copydoc Archive::listFileInfo
FileInfoListPtr listFileInfo(bool recursive = true, bool dirs = false);
/// @copydoc Archive::find
StringVectorPtr find(const String& pattern, bool recursive = true,
bool dirs = false);
/// @copydoc Archive::findFileInfo
FileInfoListPtr findFileInfo(const String& pattern, bool recursive = true,
bool dirs = false);
/// new add by lingbo \ to /
void stringRightToLeft(String& inputStr) const;
/// new add by lingbo / to \
void stringLeftToRight(String& inputStr) const;
/// @copydoc Archive::exists
bool exists(const String& filename);
};
/** Specialisation of ArchiveFactory for Zip files. */
class _OgrePrivate MpqArchiveFactory : public ArchiveFactory
{
public:
virtual ~MpqArchiveFactory() {}
/// @copydoc FactoryObj::getType
const String& getType(void) const;
/// @copydoc FactoryObj::createInstance
Archive *createInstance( const String& name )
{
return new MpqArchive(name, "Mpq");
}
/// @copydoc FactoryObj::destroyInstance
void destroyInstance( Archive* arch) { delete arch; }
};
/** Specialisation of DataStream to handle streaming data from zip archives. */
class _OgrePrivate MpqDataStream : public DataStream
{
protected:
HANDLE m_hFile;
public:
/// Unnamed constructor
MpqDataStream(HANDLE hFile, size_t uncompressedSize);
/// Constructor for creating named streams
MpqDataStream(const String& name, HANDLE hFile, size_t uncompressedSize);
~MpqDataStream();
/// @copydoc DataStream::read
size_t read(void* buf, size_t count);
/// @copydoc DataStream::skip
void skip(long count);
/// @copydoc DataStream::seek
void seek( size_t pos );
/// @copydoc DataStream::seek
size_t tell(void) const;
/// @copydoc DataStream::eof
bool eof(void) const;
/// @copydoc DataStream::close
void close(void);
};
}
#endif
#include "OgreStableHeaders.h"
#include "OgreMpq.h"
#include "OgreLogManager.h"
#include "OgreException.h"
#include "OgreStringVector.h"
#include "OgreRoot.h"
#include <zzip/zzip.h>
namespace Ogre {
//-----------------------------------------------------------------------
MpqArchive::MpqArchive(const String& name, const String& archType )
: Archive(name, archType), m_hMpq(0)
{
}
//-----------------------------------------------------------------------
MpqArchive::~MpqArchive()
{
unload();
}
//-----------------------------------------------------------------------
void MpqArchive::stringLeftToRight(String& inputStr) const
{
for (String::iterator it = inputStr.begin();it!=inputStr.end(); it++)
{
if ((*it) == '/')
{
(*it) = '\\';
}
}
}
//-----------------------------------------------------------------------
void MpqArchive::stringRightToLeft(String& inputStr) const
{
for (String::iterator it = inputStr.begin();it!=inputStr.end(); it++)
{
if ((*it) == '\\')
{
(*it) = '/';
}
}
}
//-----------------------------------------------------------------------
void MpqArchive::load()
{
if (!m_hMpq)
{
//open mpq Archive into HandleMpq
if (SFileOpenArchive(mName.c_str(),0,0,&m_hMpq))
{
SFILE_FIND_DATA sfd;
//PARAM2 NULL表示用MPQ内置的filelist,Param3 "*" find any file
HANDLE hFind = SFileFindFirstFile(m_hMpq,"*",&sfd,NULL);
if (hFind)
{
FileInfo info;
info.archive = this;
//这一步是把转义字符'\\'换成'/'
String tempStr = sfd.cFileName;
stringRightToLeft(tempStr);
StringUtil::splitFilename(tempStr,info.basename,info.path);
info.filename = tempStr;
info.compressedSize = sfd.dwCompSize;
info.uncompressedSize = sfd.dwFileSize;
mFileList.push_back(info);
while(1)
{
SFILE_FIND_DATA sfdTmp;
if (SFileFindNextFile(hFind,&sfdTmp))
{
FileInfo info;
info.archive = this;
String tempStr = sfdTmp.cFileName;
stringRightToLeft(tempStr);
StringUtil::splitFilename(tempStr,info.basename,info.path);
info.filename = tempStr;
info.compressedSize = sfdTmp.dwCompSize;
info.uncompressedSize = sfdTmp.dwFileSize;
mFileList.push_back(info);
}
else
{
SFileFindClose(hFind);
break;
}
}
}
}
}
}
//-----------------------------------------------------------------------
void MpqArchive::unload()
{
if (m_hMpq)
{
SFileCloseArchive(m_hMpq);
m_hMpq = 0;
mFileList.clear();
}
}
//-----------------------------------------------------------------------
DataStreamPtr MpqArchive::open(const String& filename) const
{
String tempStr = filename;
stringLeftToRight(tempStr);
//打开MPQ中指定的文件名的文件(第3个参数是从指定从MPQ文件中找)
HANDLE hFile;
if(!SFileOpenFileEx(m_hMpq,tempStr.c_str(),SFILE_OPEN_FROM_MPQ,&hFile))
{
//如果打开失败则输出到LOG,且返回个空指针
LogManager::getSingleton().logMessage(mName + " - Unable to open file " + tempStr + ", error was '" + "'" );
return DataStreamPtr();
}
//得到文件解压缩之后的SIZE
DWORD size = SFileGetFileSize(hFile);
if (SFILE_INVALID_SIZE ==size)
{
LogManager::getSingleton().logMessage(mName + " - Unable to open file " + tempStr + ", error was '" +"size"+ "'" );
SFileCloseFile(hFile);
return DataStreamPtr();
}
//如果得到正确
return DataStreamPtr(new MpqDataStream(filename.c_str(),hFile,static_cast<size_t>(size)));
}
//-----------------------------------------------------------------------
StringVectorPtr MpqArchive::list(bool recursive, bool dirs)
{
StringVectorPtr ret = StringVectorPtr(new StringVector());
FileInfoList::iterator i, iend;
iend = mFileList.end();
for (i = mFileList.begin(); i != iend; ++i)
{
if ((dirs == (i->compressedSize == size_t (-1))) &&(recursive || i->path.empty()))
{
ret->push_back(i->filename);
}
}
return ret;
}
//-----------------------------------------------------------------------
FileInfoListPtr MpqArchive::listFileInfo(bool recursive, bool dirs)
{
FileInfoList* fil = new FileInfoList();
FileInfoList::const_iterator i, iend;
iend = mFileList.end();
for (i = mFileList.begin(); i != iend; ++i)
{
if ((dirs == (i->compressedSize == size_t (-1))) &&(recursive || i->path.empty()))
{
fil->push_back(*i);
}
}
return FileInfoListPtr(fil);
}
//-----------------------------------------------------------------------
StringVectorPtr MpqArchive::find(const String& pattern, bool recursive, bool dirs)
{
StringVectorPtr ret = StringVectorPtr(new StringVector());
// If pattern contains a directory name, do a full match
bool full_match = (pattern.find ('/') != String::npos) ||
(pattern.find ('\\') != String::npos);
FileInfoList::iterator i, iend;
iend = mFileList.end();
for (i = mFileList.begin(); i != iend; ++i)
{
if ((dirs == (i->compressedSize == size_t (-1))) &&(recursive || full_match || i->path.empty()))
{
// Check basename matches pattern (zip is case insensitive)
if (StringUtil::match(full_match ? i->filename : i->basename, pattern, false))
{
ret->push_back(i->filename);
}
}
}
return ret;
}
//-----------------------------------------------------------------------
FileInfoListPtr MpqArchive::findFileInfo(const String& pattern,
bool recursive, bool dirs)
{
FileInfoListPtr ret = FileInfoListPtr(new FileInfoList());
// If pattern contains a directory name, do a full match
bool full_match = (pattern.find ('/') != String::npos) ||
(pattern.find ('\\') != String::npos);
FileInfoList::iterator i, iend;
iend = mFileList.end();
for (i = mFileList.begin(); i != iend; ++i)
{
if ((dirs == (i->compressedSize == size_t (-1))) &&(recursive || full_match || i->path.empty()))
{
// Check name matches pattern (zip is case insensitive)
if (StringUtil::match(full_match ? i->filename : i->basename, pattern, false))
{
ret->push_back(*i);
}
}
}
return ret;
}
//-----------------------------------------------------------------------
bool MpqArchive::exists(const String& filename)
{
//ZZIP_STAT zstat;
//int res = zzip_dir_stat(mZzipDir, filename.c_str(), &zstat, ZZIP_CASEINSENSITIVE);
//return (res == ZZIP_NO_ERROR);
//目的是如果输入的目录符号是'/'转化成'\\'
String tempStr = filename;
stringLeftToRight(tempStr);
return SFileHasFile(m_hMpq,tempStr.c_str());
}
//-----------------------------------------------------------------------
void MpqArchive::checkZzipError(int zzipError, const String& operation) const
{
//if (zzipError != ZZIP_NO_ERROR)
//{
// String errorMsg = getZzipErrorDescription(static_cast<zzip_error_t>(zzipError));
// OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
// mName + " - error whilst " + operation + ": " + errorMsg,
// "MpqArchive::checkZzipError");
//}
}
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
MpqDataStream::MpqDataStream(HANDLE hFile, size_t uncompressedSize)
: m_hFile(hFile)
{
mSize = uncompressedSize;
}
//-----------------------------------------------------------------------
MpqDataStream::MpqDataStream(const String& name, HANDLE hFile, size_t uncompressedSize)
:DataStream(name), m_hFile(hFile)
{
mSize = uncompressedSize;
}
//-----------------------------------------------------------------------
MpqDataStream::~MpqDataStream()
{
close();
}
//-----------------------------------------------------------------------
size_t MpqDataStream::read(void* buf, size_t count)
{
//return zzip_file_read(mZzipFile, (char*)buf, count);
if(SFileReadFile(m_hFile,buf,static_cast<DWORD>(count)))
{
return count;
}
else
{
return 0;
}
}
//-----------------------------------------------------------------------
void MpqDataStream::skip(long count)
{
SFileSetFilePointer(m_hFile,count,NULL,FILE_CURRENT);
}
//-----------------------------------------------------------------------
void MpqDataStream::seek( size_t pos )
{
SFileSetFilePointer(m_hFile,static_cast<long>(pos),NULL,FILE_BEGIN);
}
//-----------------------------------------------------------------------
size_t MpqDataStream::tell(void) const
{
return (size_t)SFileSetFilePointer(m_hFile, 0, NULL, FILE_CURRENT);
}
//-----------------------------------------------------------------------
bool MpqDataStream::eof(void) const
{
DWORD pos = SFileSetFilePointer(m_hFile, 0, NULL, FILE_CURRENT);
DWORD size = SFileGetFileSize(m_hFile);
return pos>=size;
}
//-----------------------------------------------------------------------
void MpqDataStream::close(void)
{
SFileCloseFile(m_hFile);
}
//-----------------------------------------------------------------------
const String& MpqArchiveFactory::getType(void) const
{
static String name = "Mpq";
return name;
}
}