Home

Packing app resources

· oth3rside

Jika kita memperhatikan secara seksama semua program-program atau game yang tidak trivial (yang serius) selalu memproteksi resources/sumber daya yang digunakan. Resources disini maksudnya file-file yang berguna untuk game/program, misalnya library, gambar, sound, config file, text file, dan lainnya. Tujuannya jelas, agar tidak mudah diubah/dimodifikasi oleh user, atau setidaknya kalau pun user bisa mengubah , itu tidak dilakukan secara mudah. (ya, tidak ada yang 100 % aman di dunia komputer ). Dari apa yang saya lihat di folder data dari program dan games yang ter-install di komputer, ada macam-macam teknik untuk memproteksinya, ada yang hanya mengubah ekstensi file, (misalnya file : gambar.jpg menjadi blablabla.dll ), melakukan enkripsi dan mengkompress file, dan ada juga yang menjadikan seluruh resources menjadi 1 file besar, misalnya data.pak . Bagaimana kalau ingin membuat proteksi semacam itu? . Saya ingin mencobanya disini, tidak perlu menulis program dari scratch, cukup menggunakan library yang sudah ada. Zlib (library kompres-dekompresi yang popular se-antero internet :-D ) dan C++ Boost adalah pilihan yang menarik. Dengan meng-kompresi data, kita tidak hanya membuat data tersebut tidak mudah di hack tapi juga membuat ukuran menjadi kecil a.k.a menghemat space.

Saya akan membuatnya sederhana. Ada 2 hal yang jelas : [1] program yang melakukan packing, dan [2] data yang terpacking bisa dibaca ulang oleh program yang akan menggunakan data tersebut. Daripada menjadikan beberapa file menjadi 1 file besar, saya lebih memillih mem-packing beberapa file dari sebuah folder (dan sub folder didalam-nya) kedalam 1 folder besar. Untuk mempermudah pembacaan dan peng-organisasian, dari segi pembaca data yang sudah di pack , “data” seolah-olah masih berada di folder sebelum dipack ( tidak membaca dari 1 folder ) . Untuk itu saya akan menyimpan informasi file-file tersebut kedalam 1 file berformat biner, yang nantinya bisa dibaca ulang.

Hmm.. so, here we go, berikut program test-nya: ( klik untuk ekspand source)

[sourcecode collapse=“true” language=“cpp”] // test: packing-unpacking files // @ edi ermawan , 20 Sept 2011 #include #include #include #include “boost/filesystem.hpp”

#include #include “infdef.h”

using std::fstream; using namespace boost::filesystem;

namespace xediconst { const std::string FileNameIdent =“data”; const std::string FileNameIdentExt =".pak"; const int max_path_str = 200; }

using namespace xediconst;

class FileInfo { public: FileInfo() {

}
FileInfo(const std::string& str): mFileID(mLastFileID++)
{
	const char *pC = str.data();
    int length = str.size();
    strncpy( mFileName, pC, length );
	mFileName[length]='\0';
}
~FileInfo()
{
};
void setFileInfo(const int id,const std::string& str)
{
	mFileID=id;

	const char *pC = str.data();
    int length = str.size();
    strncpy( mFileName, pC, length );
	mFileName[length]='\0';
};
int getFileID()
{
	return mFileID;
};
std::string getFileName()
{
	return mFileName;
};

public: static int mLastFileID; private: int mFileID; char mFileName[xediconst::max_path_str]; };

int FileInfo::mLastFileID=0; //use boost bool GetAllFiles(const path& dir_path,std::vector<FileInfo*>& list) { if ( !exists( dir_path ) ) return false; directory_iterator end_itr; for ( directory_iterator itr( dir_path ); itr != end_itr; ++itr ) { if ( is_directory(itr->status()) ) { GetAllFiles( itr->path(),list); } else { list.push_back(new FileInfo(itr->path().string())); } } return true; }

void SaveFile(std::vector<FileInfo*>& listFiles,const std::string& nameFile) { std::string nameFile2=nameFile+"\"+FileNameIdent+FileNameIdentExt; fstream fileOut(nameFile2.c_str(),std::ios::out|std::ios::binary);

std::cout<<" Saving... "<<nameFile<<std::endl;
if(!fileOut)
{
	std::cout<<"Error saving file."<<std::endl;
	return;
}
int i=0;
for(std::vector<FileInfo*>::iterator it=listFiles.begin();
	it!=listFiles.end();it++)
{
	fileOut.seekp( (i++) * sizeof(FileInfo) );
	const char* c=reinterpret_cast<const char*>(*it);
	fileOut.write(c,sizeof(FileInfo));
}
fileOut.close();

}

void ReadFiles(std::vector<FileInfo*>& listFiles,const std::string& nameFile) { std::string nameFile2=nameFile+"\"+FileNameIdent+FileNameIdentExt; fstream fileIn(nameFile2.c_str(),std::ios::in | std::ios::binary);

std::cout<<"Reading... "<<nameFile<<std::endl;
if(!fileIn)
{
	std::cout<<"Error reading file."<<std::endl;
	return;
}
FileInfo* temp=new FileInfo();
fileIn.read(reinterpret_cast<char*>(temp),sizeof(FileInfo));
while(fileIn && !fileIn.eof())
{
	listFiles.push_back(temp);
	temp=new FileInfo();

	fileIn.read(reinterpret_cast<char*>(temp),sizeof(FileInfo));
}
fileIn.close();

} //compress-decompress : use zlib void CompressFiles(std::vector<FileInfo*>& listFiles,const std::string& nameFile) { FILE *file_in; FILE *file_out;

for(std::vector<FileInfo*>::iterator it=listFiles.begin();
	it!=listFiles.end();it++)
{
		std::string nameIn=(*it)->getFileName();
		char c[30];
		itoa((*it)->getFileID(),c,10);
		std::string nameOut=nameFile+"\\"+FileNameIdent+c+FileNameIdentExt;

		std::cout<<"File: "<<nameIn<<"  Compressed to :"<<nameOut<<std::endl;
		file_in=fopen(nameIn.c_str(),"r+b");
		file_out=fopen(nameOut.c_str(),"w+b");
		int ret = def(file_in, file_out, Z_DEFAULT_COMPRESSION);
		if (ret != Z_OK)
		{
			zerr(ret);
		}
		fclose(file_in);
		fclose(file_out);
}

}

void DecompressFiles(std::vector<FileInfo*>& listFiles,const std::string& nameFile) { FILE *file_in; FILE *file_out;

for(std::vector<FileInfo*>::iterator it=listFiles.begin();
	it!=listFiles.end();it++)
{
		std::string nameOut=(*it)->getFileName();
		char c[30];
		itoa((*it)->getFileID(),c,10);
		std::string nameIn=nameFile+"\\"+FileNameIdent+c+FileNameIdentExt;

		std::cout<<"File: "<<nameIn<<"  Extracted to :"<<nameOut<<std::endl;
		file_in=fopen(nameIn.c_str(),"r+b");
		file_out=fopen(nameOut.c_str(),"w+b");
		int ret = inf(file_in, file_out);
		if (ret != Z_OK)
		{
			zerr(ret);
		}
		fclose(file_in);
		fclose(file_out);
}

} int main(int argc,char** argv) { if(argc<3) { std::cout«“Usage: xedicompress -P RelativePathSrc RelativePathDest”«std::endl; std::cout«“Usage: xedicompress -UP RelativePathDest”«std::endl; std::cout«“Relative path from this app."«std::endl; return 0; }

if (strcmp(argv[1], "-UP") == 0)
{
	std::cout<<" Unpacking Files . . ."<<std::endl;
	///path presult;
	std::vector<FileInfo*>listFiles_2;
	//read list of file from data.pak
	ReadFiles(listFiles_2,argv[2]);
	//after get list of files, then extract them.
	DecompressFiles(listFiles_2,argv[2]);
}
else if (strcmp(argv[1], "-P") == 0)
{
	std::cout<<" Packing Files . . ."<<std::endl;

	path presult;
	std::vector<FileInfo*>listFiles;
	//get all files from specified path, save it into vector
	GetAllFiles(argv[2],listFiles);
	//save list of files in data.pak, as future reference for extracting
	SaveFile(listFiles,argv[3]);
	//compress all files
	CompressFiles(listFiles,argv[3]);

}
else
{
	std::cout<<"Usage: xedicompress -P RelativePathSrc RelativePathDest"<<std::endl;
	std::cout<<"Usage: xedicompress -UP RelativePathDest"<<std::endl;
	std::cout<<"Relative path from this app."<<std::endl;
	return 0;
}

} [/sourcecode]

format penggunaan: untuk Packing : xedicompress -P RelativePathSrc RelativePathDest untuk Unpacking : xedicompress -UP RelativePathDest contoh: xedicompress -P ..\test ..\debug\data akan mengkompress seluruh file didalam folder test (dan subfolder di dalam test) ke folder \debug\data. Relative path terhadap folder dimana aplikasi berada. xedicompress -UP ..\debug\data Akan mendekompress seluruh file di debug\data ke folder asalnya.

#just_for_fun :-)

boost ver 1.42 zlib ver 1.2.5