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
) 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)
// test: packing-unpacking files
// @ edi ermawan , 20 Sept 2011
#include <iostream>
#include <vector>
#include <string>
#include "boost/filesystem.hpp"
#include <fstream>
#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;
}
}
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