在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;
	}

}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值