在Ogre中实现Mpq文件格式的支持

基于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;
	}

}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值