游戏开发之Zlib的使用

前言

我们知道一个游戏不仅包含了程序代码,而游戏中最主要的是那些资源文件,包括声音,动画,图片甚至还有一些视频文件。如果这些文件不经过压缩,可能需要好 十几张光盘。除了这个原因,另一个不好的原因就是这些资源裸露在玩家面前,也有可能被玩家不经意修改,导致游戏不能继续进行。

因此,在游戏制作好以后,要进行游戏资源的压缩。而我们今天就讲解一下资源文件的压缩。

Zlib

Zlib是一个跨平台的压缩函数库,提供了一套 in-memory 压缩和解压函数,并能检测解压出来的数据的完整性(integrity)。关于zlib的更多信息请访问 http://www.zlib.net

http://www.chinaaspx.com/archive/develop/11324.htm 上有一篇简单地使用Zlib的文章。

上面那篇文章只是讲解了简单的对一些字符串的压缩和解压功能,而我下面将讲解一下如何如何将字符传保存在压缩文件里;如何从压缩文件中读出字符串,这些代码来自于zlib自带的例程中。

在项目设置中添加zlib的库文件,然后在程序中添加下面的头文件。

#include <zlib.h>

在程序开始前,设置下面变量:

    static const char* myVersion=ZLIB_VERSION;

    const char *hello="hello,the world!";

    Byte uncompr[17];

    int uncomprLen=17;  

    int len=strlen(hello);

    int err;

    gzFile file;

    z_off_t pos;                                                  

第一变量是zlib的版本号;hello为我们写入文件的数据。

检查我们所使用的zlib。 

  if(zlibVersion()[0]!=myVersion[0])

    {

      fprintf(stderr,"不相容的zlib版本n");

    }

    else if(strcmp(zlibVersion(),ZLIB_VERSION)!=0)

    {

      fprintf(stderr,"警告:不同的zlib版本n");

    }

    将字符串写入到hello.gz的压缩文件中。

   file=gzopen("hello.gz","rb");

    if(file==NULL)

    {

     fprintf(stderr,"gz文件不能打开。n");

    }

   

    file = gzopen("hello.gz", "wb");

    if (file == NULL) {

         fprintf(stderr, "gzopen errorn");

         exit(1);

     }

    if (gzprintf(file, "%s",hello) != 16) {

         fprintf(stderr, "gzprintf err: %sn", gzerror(file, &err));

         exit(1);

     }

    gzclose(file);

从hello.gz中读去数据。

    strcpy((char*)uncompr,"garbage");

    uncomprLen=gzread(file,uncompr,(unsigned)uncomprLen);

    if(uncomprLen!=len)

    {

     fprintf(stderr,"gz文件读出错误:%sn",gzerror(file,&err));

     printf("%d,%d",uncomprLen,len);

     getch();

     exit(1);

    } 

    if(strcmp((char*)uncompr,hello))

    {

     fprintf(stderr,"读出错误的数据:%sn",(char*)uncompr);

     getch();

     exit(1);

    }

    else

    {

     printf("读出的数据:%sn",(char *)uncompr);

    }

    gzclose(file);                                   

1. 下载zlib,附件是zlib 1.2.3 .
2. 解压代码,打开 ./projects/visualc6/zlib.dsw .
3. Build : zlib Lib Debug / zlib Lib Release ,生成的zlib.lib/zlibd.lib .
4. 在我们的工程中包含头文件 zlib.h / zconf.h ,和连接生成的Lib .
5. 测试代码
Write to file :
char * pchData = "xxx..." ;
gzFile fData = gzopen(pchFile,"wb");
gzwrite(fData,pchData,strlen(pchData));
gzclose(fData);
read from file :
char pchData[1024];
gzFile fData = gzopen(pchFile,"rb");
int n = gzread(fData,pchData,1024);
gzclose(fData);
Buffer test :
//原始数据
unsigned char pchSrc[] = "xxx...." ;
unsigned long nSrcLen = sizeof(pchSrc);

//压缩之后的数据

unsigned char achComp[1024];
unsigned long nCompLen = 1024 ;

//解压缩之后的数据

unsigned char achUncomp[1024];
unsigned long nUncompLen = 1024 ;

//压缩

compress(achComp,&nCompLen, pchSrc,nSrcLen);

//解压缩

uncompress(achUncomp,&nUncompLen, achComp,nCompLen);

//显示原始数据信息

printf("原始数据(%d):/n%s/n/n", nSrcLen,pchSrc);

//显示压缩之后的数据

printf("压缩数据(%d):/n%s/n/n", nCompLen,achComp);

//显示解压缩之后的数据

printf("解压数据(%d):/n%s/n/n", nUncompLen,achUncomp);

zlib是一套公开源代码的压缩,解压缩的函数库,提供了很多文件操作的方法,但是他不是一套类库,所以有兴趣的人都可以把他进行封装,实现自己的类库,和更高层的接口。
具体的介绍可以参考http://www.gzip.org/zlib/主页,这里有详细介绍。
这里简单实现了zlib的最简单的用法,压缩一个文件,通过使用文件映射来实现的。
包含头文件 zlib.h 和 zconf.h 和 zlib.lib
在stdafx.h 中加入:
#ifdef _DEBUG
#pragma comment(lib,"zlibd.lib")
#else
#pragma comment(lib,"zlib.lib")
#endif
#include "zlib.h"
#include "zconf.h"
压缩代码:

HANDLE hFile, hFileToWrite;
CString strFilePath;
m_ctrEdit.GetWindowText(strFilePath);

//打开要进行压缩的文件
hFile = CreateFile(strFilePath, // file name
GENERIC_READ, // open for reading
FILE_SHARE_READ, // share for reading
NULL, // no security
OPEN_EXISTING, // existing file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template

if (hFile == INVALID_HANDLE_VALUE)
{
AfxMessageBox("Could not open file to read"); // process error
return;
}

HANDLE hMapFile, hMapFileToWrite;

//创建一个文件映射
hMapFile = CreateFileMapping(hFile, // Current file handle.
NULL, // Default security.
PAGE_READONLY, // Read/write permission.
0, // Max. object size.
0, // Size of hFile.
"ZipTestMappingObjectForRead"); // Name of mapping object.

if (hMapFile == NULL)
{
AfxMessageBox("Could not create file mapping object");
return;
}

LPVOID lpMapAddress, lpMapAddressToWrite;

//创建一个文件映射的视图用来作为source
lpMapAddress = MapViewOfFile(hMapFile, // Handle to mapping object.
FILE_MAP_READ, // Read/write permission
0, // Max. object size.
0, // Size of hFile.
0); // Map entire file.

if (lpMapAddress == NULL)
{
AfxMessageBox("Could not map view of file");
return;
}

//
DWORD dwFileLength,dwFileLengthToWrite;
dwFileLength = GetFileSize(hFile, NULL);
m_dwSourceFileLength = dwFileLength;
//因为压缩函数的输出缓冲必须比输入大0.1% + 12 然后一个DWORD用来保存压缩前的大小,
// 解压缩的时候用,当然还可以保存更多的信息,这里用不到
dwFileLengthToWrite = (double)dwFileLength*1.001 + 12 + sizeof(DWORD);

//以下是创建一个文件,用来保存压缩后的文件
hFileToWrite = CreateFile("demoFile.rar", // demoFile.rar
GENERIC_WRITE|GENERIC_READ, // open for writing
0, // do not share
NULL, // no security
CREATE_ALWAYS, // overwrite existing
FILE_ATTRIBUTE_NORMAL , // normal file
NULL); // no attr. template

if (hFileToWrite == INVALID_HANDLE_VALUE)
{
AfxMessageBox("Could not open file to write"); // process error
return;
}

hMapFileToWrite = CreateFileMapping(hFileToWrite, // Current file handle.
NULL, // Default security.
PAGE_READWRITE, // Read/write permission.
0, // Max. object size.
dwFileLengthToWrite, // Size of hFile.
"ZipTestMappingObjectForWrite"); // Name of mapping object.

if (hMapFileToWrite == NULL)
{
AfxMessageBox("Could not create file mapping object for write");
return;
}

lpMapAddressToWrite = MapViewOfFile(hMapFileToWrite, // Handle to mapping object.
FILE_MAP_WRITE, // Read/write permission
0, // Max. object size.
0, // Size of hFile.
0); // Map entire file.

if (lpMapAddressToWrite == NULL)
{
AfxMessageBox("Could not map view of file");
return;
}

//这里是将压缩前的大小保存在文件的第一个DWORD里面
LPVOID pBuf = lpMapAddressToWrite;
(*(DWORD*)pBuf) = dwFileLength;
pBuf = (DWORD*)pBuf + 1;


//

//这里就是最重要的,zlib里面提供的一个方法,将源缓存的数据压缩至目的缓存
//原形如下:
//int compress (Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);
//参数destLen返回实际压缩后的文件大小。
compress((Bytef*)pBuf,&dwFileLengthToWrite, (Bytef*)lpMapAddress, dwFileLength);

//

UnmapViewOfFile(lpMapAddress);
CloseHandle(hMapFile);
CloseHandle(hFile);

UnmapViewOfFile(lpMapAddressToWrite);
CloseHandle(hMapFileToWrite);
//这里将文件大小重新设置一下
SetFilePointer(hFileToWrite,dwFileLengthToWrite + sizeof(DWORD) ,NULL,FILE_BEGIN);
SetEndOfFile(hFileToWrite);
CloseHandle(hFileToWrite);

解压缩的方法其他地方都一样,不同的就是使用方法是uncompress而不是compress
原形如下:

int uncompress (Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);
解压缩代码如下:

HANDLE hFile, hFileToWrite;
CString strFilePath;
m_ctrEdit.GetWindowText(strFilePath);

//打开要进行解压缩的文件
hFile = CreateFile(strFilePath, // file name
GENERIC_READ, // open for reading
FILE_SHARE_READ, // share for reading
NULL, // no security
OPEN_EXISTING, // existing file only
FILE_ATTRIBUTE_NORMAL, // normal file
NULL); // no attr. template

if (hFile == INVALID_HANDLE_VALUE)
{
AfxMessageBox("Could not open file to read"); // process error
return;
}

HANDLE hMapFile, hMapFileToWrite;

//创建一个文件映射
hMapFile = CreateFileMapping(hFile, // Current file handle.
NULL, // Default security.
PAGE_READONLY, // Read/write permission.
0, // Max. object size.
0, // Size of hFile.
"ZipTestMappingObjectForRead"); // Name of mapping object.

if (hMapFile == NULL)
{
AfxMessageBox("Could not create file mapping object");
return;
}

LPVOID lpMapAddress, lpMapAddressToWrite;

//创建一个文件映射的视图用来作为source
lpMapAddress = MapViewOfFile(hMapFile, // Handle to mapping object.
FILE_MAP_READ, // Read/write permission
0, // Max. object size.
0, // Size of hFile.
0); // Map entire file.

if (lpMapAddress == NULL)
{
AfxMessageBox("Could not map view of file");
return;
}

//
DWORD dwFileLength,dwFileLengthToWrite;
dwFileLength = GetFileSize(hFile, NULL) - sizeof(DWORD);
//因为压缩函数的输出缓冲必须比输入大0.1% + 12 然后一个DWORD用来保存压缩前的大小,
// 解压缩的时候用,当然还可以保存更多的信息,这里用不到
// dwFileLengthToWrite = (double)dwFileLength*1.001 + 12 + sizeof(DWORD);
dwFileLengthToWrite = (*(DWORD*)lpMapAddress);

LPVOID pSourceBuf = lpMapAddress;
pSourceBuf = (DWORD*)pSourceBuf + 1;

//以下是创建一个文件,用来保存压缩后的文件
hFileToWrite = CreateFile("demoFile.pdf", // create demo.gz
GENERIC_WRITE|GENERIC_READ, // open for writing
0, // do not share
NULL, // no security
CREATE_ALWAYS, // overwrite existing
FILE_ATTRIBUTE_NORMAL , // normal file
NULL); // no attr. template

if (hFileToWrite == INVALID_HANDLE_VALUE)
{
AfxMessageBox("Could not open file to write"); // process error
return;
}

hMapFileToWrite = CreateFileMapping(hFileToWrite, // Current file handle.
NULL, // Default security.
PAGE_READWRITE, // Read/write permission.
0, // Max. object size.
dwFileLengthToWrite, // Size of hFile.
"ZipTestMappingObjectForWrite"); // Name of mapping object.

if (hMapFileToWrite == NULL)
{
AfxMessageBox("Could not create file mapping object for write");
return;
}

lpMapAddressToWrite = MapViewOfFile(hMapFileToWrite, // Handle to mapping object.
FILE_MAP_WRITE, // Read/write permission
0, // Max. object size.
0, // Size of hFile.
0); // Map entire file.

if (lpMapAddressToWrite == NULL)
{
AfxMessageBox("Could not map view of file");
return;
}

//这里是将压缩前的大小保存在文件的第一个DWORD里面
LPVOID pBuf = lpMapAddressToWrite;


//

//这里就是最重要的,zlib里面提供的一个方法,将源缓存的数据压缩至目的缓存
//原形如下:
//int compress (Bytef *dest, uLongf *destLen, const Bytef *source, uLong sourceLen);
//参数destLen返回实际压缩后的文件大小。
uncompress((Bytef*)pBuf,&dwFileLengthToWrite, (Bytef*)pSourceBuf, dwFileLength);

//

UnmapViewOfFile(lpMapAddress);
CloseHandle(hMapFile);
CloseHandle(hFile);

UnmapViewOfFile(lpMapAddressToWrite);
CloseHandle(hMapFileToWrite);
//这里将文件大小重新设置一下
SetFilePointer(hFileToWrite,dwFileLengthToWrite ,NULL,FILE_BEGIN);
SetEndOfFile(hFileToWrite);
CloseHandle(hFileToWrite);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值