SDL2 游戏开发日记(五) 资源打包

SDL2 游戏开发日记(五) 资源打包

游戏中的各种各样的资源,如果不想直接的给别人看到,那就需要对资源进行打包。

打包的时候可以加入加密解密算法对数据进行加密,防止别人直接拿来使用。

打包可以使文件夹看起来更简洁。

SDL提供了SDL_RWops来让我们直接从内存或者二进制流中加载各种游戏素材。

资源打包解包类

#pragma once 
#include <string>
#include <map>
#include <iostream>
#include <fstream>
#include <SDL.h>
using namespace std;
//文件信息
struct FileInfo{
	//文件在资源包中的位置
	int offset;
	//文件大小
	int fileSize;
	//文件名字长度	
	int nameSize;
	//文件名
	char*fileName;
	//文件输入流,打包时用到
	fstream fin;
	~FileInfo(){
		if(fileName != NULL)
			delete[]fileName;
	}	
};
typedef map<string,FileInfo*> FileList;
//
class PackageRes{
private:
	//文件列表,存储要打包的文件或者解包的文件
	FileList mFileList;
	//文件输入流,解包和读取资源时用
	fstream mPackIn;
	//文件索引长度
	int mIndexLen;	
public:
	PackageRes(){		
	}
	~PackageRes(){
		FileList::iterator iter = mFileList.begin();
		while (iter != mFileList.end()){
			delete (iter->second);
			iter++;
		}
		mFileList.clear();
		if (mPackIn.is_open()){
			mPackIn.close();
		}	
	}
	//添加文件到列表
	void AddFile(string fileName){
		int size = fileName.size()+1;
		if (size > 0){
			char *name = new char[size];
			memset(name, 0, size);
			
			strcpy_s(name,size, fileName.c_str());
			FileInfo *info = new FileInfo();
			info->fileName = name;
			info->nameSize = size;
			AddFile(fileName, info);	
		}
	}
	void AddFile(string fileName, FileInfo *info){
		FileList::iterator iter = mFileList.find(fileName);
		if (iter != mFileList.end()){
			delete iter->second;
			iter->second = info;
		}
		else{
			mFileList.insert(make_pair(fileName, info));
		}
	}
	//从列表中移除文件
	void RemoveFile(string fileName){
		FileList::iterator iter = mFileList.find(fileName);
		if (iter != mFileList.end()){
			delete (iter->second);
			mFileList.erase(iter);
		}
	}
	//打包
	void Package(string fileName);	
	//解包和获取资源
	void Unpack(string fileName);
	SDL_RWops* GetResource(string fileName);
};

//PackageRes.cpp

	
#include "stdafx.h"	
#include "PackageRes.h"
#include "SDLGame.h"
//SDL_RWops释放时释放new申请的内存
int RWFreeBuffer(SDL_RWops *context){
	if (context != NULL){
		//printf("free sdl rwops.\n");	
		printf_s("free: %p\n", context->hidden.unknown.data1);
		delete[] context->hidden.unknown.data1;
		SDL_FreeRW(context);
	}
	return 0;
}
//打包
void PackageRes::Package(string fileName){
	int fileCount = mFileList.size();
	if (fileCount <= 0)
		return;
	FileList::iterator iter = mFileList.begin();
	//文件偏移量
	int offset = 0;
	//索引偏移量/
	int indexLen = 0;
	while (iter != mFileList.end()){
		fstream fin;
		FileInfo *info = iter->second;
		fin.open(theGame.GetResourcePath() + info->fileName, ios::binary | ios::in);	
		fin.seekg(0, ios::end);
		info->fileSize = (int)fin.tellg();
		info->offset = offset;
		offset += info->fileSize;
		indexLen += sizeof(int)*3 + info->nameSize;
		//fstream没有重载=,所以把fin转为右值
		info->fin = std::move(fin);
		iter++;
	}	
	fstream fout;
	fout.open(theGame.GetResourcePath() + fileName, ios::out | ios::binary);
	//如果有需要,可以在这里接入加密算法。把数据加密了再写入文件
	fout.write((char*)&fileCount, sizeof(int));
	fout.write((char*)&indexLen, sizeof(int));
	//写索引
	iter = mFileList.begin();
	while (iter != mFileList.end()){
		FileInfo *info = iter->second;
		fout.write((char*)&(info->offset), sizeof(int));
		fout.write((char*)&(info->fileSize), sizeof(int));
		fout.write((char*)&(info->nameSize), sizeof(int));
		fout.write(info->fileName, info->nameSize);
		iter++;
	}
	//写入文件内容
	iter = mFileList.begin();
	char buff[512];
	while (iter != mFileList.end()){
		FileInfo *info = iter->second;
		info->fin.seekg(0, ios::beg);
		while (!info->fin.eof()){	
			memset(buff, 0, sizeof(buff));
			info->fin.read(buff, sizeof(buff));
			int len = (int)info->fin.gcount();
			if (len > 0){
				fout.write(buff, len);
			}
		}
		info->fin.close();
		iter++;
	}
	fout.close();
}
//解包
void PackageRes::Unpack(string fileName){
	if (mPackIn.is_open())
		mPackIn.close();
	mPackIn.open(theGame.GetResourcePath() + fileName, ios::in | ios::binary);
	mPackIn.seekg(0, ios::beg);
	int fileCount = 0;
	int indexLen = 0;
	//读取文件数量
	mPackIn.read((char*)&fileCount, sizeof(int));
	mPackIn.read((char*)&indexLen, sizeof(int));
	mIndexLen = indexLen + 8;
	//读取索引中的文件信息
	for (int i = 0; i < fileCount; i++){
		FileInfo *info = new FileInfo();
		mPackIn.read((char*)&info->offset, sizeof(int));
		mPackIn.read((char*)&info->fileSize, sizeof(int));
		mPackIn.read((char*)&info->nameSize, sizeof(int));	
		if (info->nameSize <= 0 || info->fileSize <= 0 || info->offset < 0){
			printf_s("unpack error. nameSize <= 0\n");
			return;
		}
		char *fn = new char[info->nameSize];
		memset(fn, 0, info->nameSize);
		mPackIn.read(fn, info->nameSize);
		info->fileName = fn;
		//把文件信息添加到文件列表
		AddFile(string(fn), info);
	}
}
//读取资源
SDL_RWops* PackageRes::GetResource(string fileName){
	SDL_RWops * rwOps = NULL;
	FileList::iterator iter = mFileList.find(fileName);
	if (iter != mFileList.end()){
		FileInfo *info = iter->second;
		char *buffer = new char[info->fileSize];
		//输出内存所在的地址,检查内存释放。
		printf_s("alloc: %p\n", buffer);
		//从指定位置读取指定长度的数据
		mPackIn.seekg(info->offset + mIndexLen, ios::beg);
		mPackIn.read(buffer, info->fileSize);
		int realRead = (int)mPackIn.gcount();
		if (realRead != info->fileSize){
			printf("%s,read error.%d/%d\n",fileName.c_str(),realRead,info->fileSize);
			delete[] buffer;
			return rwOps;
		}		
		
		rwOps = SDL_RWFromMem(buffer, info->fileSize);
		//rwOps释放时调用RWFreeBuffer释放new申请的buffer			
		rwOps->close = RWFreeBuffer;
	}	
	return rwOps;
}

更新相关的类,从SDL_RWops中加载资源

图片加载:IMG_Load_RW

字体加载:TTF_OpenFontRW

音乐加载:Mix_LoadMUS_RW,Mix_LoadWAV_RW

更新GameSounds类,从SDL_RWops中读取声音

#define theSounds GameSound::Instance()

class GameSound{
	//省略之前........
	//加载音乐
	//Mix_Music* LoadMusic(string fileName);
	////加载音效
	//Mix_Chunk* LoadSound(string fileName);
	//新函数
	Mix_Music* LoadMusic(string fileName, SDL_RWops *ops = NULL);
	Mix_Chunk* LoadSound(string fileName, SDL_RWops *ops = NULL);
	//省略之后.......		
};

//cpp	
Mix_Music* GameSound::LoadMusic(string fileName,SDL_RWops *ops){
	
	Mix_Music *music = NULL;
	MusicsMap::iterator iter = mMusics.find(fileName);
	if (iter != mMusics.end()){
		music = iter->second;
		return music;
	}
	else{
		if (ops != NULL){
			//从SDL_RWops中加载,第二个参数表示SDL_RWops在不适用时自动释放
			music = Mix_LoadMUS_RW(ops,  1);			
		}
		else{
			music = Mix_LoadMUS((theGame.GetResourcePath() + fileName).c_str());
		}
		if (music != NULL){
			mMusics.insert(std::make_pair(fileName, music));
		}
		else{
			return NULL;
		}
	}
	return music;
}
//
Mix_Chunk* GameSound::LoadSound(string fileName,SDL_RWops *ops){
	Mix_Chunk *chunk = NULL;
	SoundsMap::iterator iter = mSounds.find(fileName);
	if (iter != mSounds.end()){
		chunk = iter->second;
		return chunk;
	}	
	if (ops != NULL){
		//从SDL_RWops中加载,第二个参数表示SDL_RWops在不适用时自动释放
		chunk = Mix_LoadWAV_RW(ops, 1);
	}
	else
	{
		chunk = Mix_LoadWAV((theGame.GetResourcePath() + fileName).c_str());
	}
	if (chunk != NULL){
		mSounds.insert(std::make_pair(fileName, chunk));
	}
	else{
		return NULL;
	}
	return chunk;
}

测试

资源打包

PackageRes package;	
package.AddFile("background.wav");
package.AddFile("background.mp3");
package.AddFile("simkai.ttf");
package.AddFile("delete.wav");
package.AddFile("tetris.png");
package.AddFile("loading.png");
package.Package("resource.pak");

资源解包和加载

PackageRes package;	
package.Unpack("resource.pak");
SDL_RWops *music = package.GetResource("background.mp3");
SDL_RWops *del = package.GetResource("delete.wav");
if (music != NULL){
	printf("load music success.\n");		
	theSounds.LoadMusic("background.mp3",music);
	theSounds.PlayMusic("background.mp3", -1);	
}
if (del != NULL){
	printf("load sound success.\n");		
	theSounds.LoadSound("delete.wav", del);		
	theSounds.PlaySoundEffect("delete.wav",0);
}
发布了9 篇原创文章 · 获赞 0 · 访问量 1028

猜你喜欢

转载自blog.csdn.net/drizzlemao/article/details/103546724