Alientech 0.01
An extendable game engine
C:/Alientech/AlienCHPacker/main.cpp
Go to the documentation of this file.
00001 #include <iostream>
00002 #include <stdio.h>
00003 #include <fstream>
00004 #include <vector>
00005 #include <string>
00006 #include <Windows.h>
00007 #include <stack>
00008 #include <cmath>
00009 using namespace std;
00010 
00011 wstring sToWs(string str)
00012 {
00013         wchar_t* temp = new wchar_t[str.length()+1];
00014         wsprintf(temp,L"%S",str.c_str());
00015         return wstring(temp);
00016 }
00017 
00018 string wsToS(wstring wstr)
00019 {
00020         char* temp = new char[wstr.length()+1];
00021         sprintf(temp,"%S",wstr.c_str());
00022         return string(temp);
00023 }
00024 
00025 wstring prepPathForSearch(wstring path)
00026 {
00027         if (path[path.length()-1] != L'\\')
00028         {
00029                 return (path+L"\\*");
00030         }
00031         return path;
00032 }
00033 
00034 string grabNameFromPath(string path)
00035 {
00036         size_t temp = path.rfind('\\');
00037         if (temp != string::npos)
00038                 return path.substr(path.rfind('\\')+1);
00039         else
00040                 return path;
00041 }
00042 
00043 class File
00044 {
00045 public:
00046         string fullPath;
00047         string name;
00048         streampos offset;
00049         unsigned size;
00050 };
00051 
00052 class Directory : public File
00053 {
00054 public:
00055         bool rootDir;
00056         vector<Directory*> subdirs;
00057         vector<File*> files;
00058 
00059         Directory()
00060         {
00061                 rootDir = false;
00062         }
00063 
00064         void localizeNames()
00065         {
00066                 name = grabNameFromPath(name);
00067                 for (unsigned i=0;i<subdirs.size();i++)
00068                         subdirs[i]->localizeNames();
00069         }
00070 
00071         void print(string pre)
00072         {
00073                 for (unsigned i=0;i<files.size();i++)
00074                         cout << pre << files[i]->name << "[size:" << files[i]->size << "]" << endl;
00075                 for (unsigned i=0;i<subdirs.size();i++)
00076                 {
00077                         cout << pre << subdirs[i]->name << endl;
00078                         subdirs[i]->print(pre+"\t");
00079                 }
00080         }
00081 
00082         void destroy()
00083         {
00084                 // first delete files
00085                 for (unsigned i=0;i<files.size();i++)
00086                         delete files[i];
00087                 // then subdirectories
00088                 for (unsigned i=0;i<subdirs.size();i++)
00089                         subdirs[i]->destroy();
00090                 // lastly delete self
00091                 delete this;
00092         }
00093 };
00094 
00095 Directory* walkDirectory(wstring dirPath);
00096 
00097 void addItemToDirectory(Directory* dir, WIN32_FIND_DATA* fileData)
00098 {
00099         if (wstring(fileData->cFileName).compare(L"..") == 0 ||
00100                 wstring(fileData->cFileName).compare(L".")  == 0)
00101         {
00102                 return; //ignore pointer to the current and previous directory
00103         }
00104         if (fileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
00105         {
00106                 wstring temp = sToWs(dir->name+'\\')+wstring(fileData->cFileName);
00107                 string stemp = wsToS(temp);
00108                 for (unsigned i=0;i<dir->subdirs.size();i++)
00109                         if (dir->subdirs[i]->name.compare(stemp) == 0)
00110                                 return; // don't include directories that have already been added
00111                 dir->subdirs.push_back(walkDirectory(temp));
00112         }
00113         else
00114         {
00115                 File* f = new File();
00116                 f->name = grabNameFromPath(wsToS(wstring(fileData->cFileName)));
00117                 f->fullPath = dir->name+"\\"+wsToS(wstring(fileData->cFileName));
00118                 f->size = fileData->nFileSizeLow; //ignore huge files
00119                 for (unsigned i=0;i<dir->files.size();i++)
00120                         if (dir->files[i]->name.compare(f->name) == 0)
00121                         {
00122                                 delete f;
00123                                 return;
00124                         }
00125                 dir->files.push_back(f);
00126         }
00127 }
00128 
00129 Directory* walkDirectory(wstring dirPath)
00130 {
00131         Directory* dir = new Directory;
00132         dir->name = wsToS(dirPath);
00133         WIN32_FIND_DATA fileData;
00134         HANDLE h = FindFirstFile(prepPathForSearch(dirPath).c_str(),&fileData);
00135         if (h == INVALID_HANDLE_VALUE)
00136         {
00137                 delete dir;
00138                 return NULL;
00139         }
00140         BOOL returnVal = 1;
00141         while (returnVal != 0)
00142         {
00143                 returnVal = FindNextFile(h,&fileData);
00144                 addItemToDirectory(dir,&fileData);
00145         }
00146         
00147         return dir;
00148 }
00149 
00150 void writeUnsigned(unsigned n, ofstream* file)
00151 {
00152         file->write((char*)&n,4);
00153 }
00154 
00155 void writeInt(int n, ofstream* file)
00156 {
00157         file->write((char*)&n,4);
00158 }
00159 
00160 void writeName(string name, ofstream* file)
00161 {
00162         unsigned char nameLength;
00163         nameLength = name.length()+1;
00164         file->write((char*)&nameLength,1);
00165         file->write(name.c_str(),nameLength);
00166 }
00167 
00168 void writeDirStructure(Directory* dir, ofstream* file, stack<Directory*>* traversalStruct)
00169 {
00170         // writes the structrure of a single directory
00171 
00172         // fix offset
00173         if (!dir->rootDir)
00174         {
00175                 streampos currentPos = file->tellp();
00176                 file->seekp(dir->offset);
00177                 writeInt((int)currentPos,file);
00178                 file->seekp(currentPos);
00179         }
00180 
00181         // name
00182         writeName(dir->name,file);
00183         // subdirectories
00184         int subdirs = dir->subdirs.size();
00185         writeInt(subdirs,file);
00186         int dummy = 0;
00187         for (unsigned i=0;i<subdirs;i++)
00188         {
00189                 dir->subdirs[i]->offset = file->tellp();
00190                 writeInt(dummy,file);
00191                 traversalStruct->push(dir->subdirs[i]);
00192         }
00193         // files
00194         int files = dir->files.size();
00195         writeInt(files,file);
00196         for (unsigned i=0;i<files;i++)
00197         {
00198                 dir->files[i]->offset = file->tellp();
00199                 writeInt(dummy,file);
00200         }
00201 }
00202 
00203 void writeStructure(Directory* rootDir, ofstream* file)
00204 {
00205         // writes the file and directory structure to the provided file
00206         // puts the place where the offset would go in the offset field of the files
00207 
00208         file->seekp(fstream::beg);
00209         stack<Directory*> traversal;
00210         traversal.push(rootDir);
00211 
00212         Directory* current;
00213         while (traversal.size()!=0)
00214         {
00215                 current = traversal.top();
00216                 traversal.pop();
00217                 writeDirStructure(current,file,&traversal);
00218         }
00219 }
00220 
00221 unsigned calculateChunks(unsigned size)
00222 {
00223         return (unsigned)ceil(double(size)/1024.0);
00224 }
00225 
00226 void writeFile(File* fileObj, ofstream* file, char* buffer) // buffer has to be a 1024 byte array
00227 {
00228         // writes a file to the cargo hold and updates its offset
00229         // first open the file
00230         ifstream f(fileObj->fullPath.c_str());
00231         if (!f.is_open())
00232         {
00233                 cout << "Error: could not open file [" << fileObj->fullPath << "]. File was written as a 0-size entry." << endl;
00234                 fileObj->size = 0;
00235         }
00236 
00237         // write offset
00238         streampos currentOffset = file->tellp();
00239         file->seekp(fileObj->offset);
00240         writeInt((int)currentOffset,file);
00241         file->seekp(currentOffset);
00242         fileObj->offset = currentOffset;
00243 
00244         // write file information and data
00245         writeName(fileObj->name,file);
00246         writeUnsigned(fileObj->size,file);
00247         // copy data in chunks of 1 kbyte
00248         unsigned chunks = calculateChunks(fileObj->size);
00249         unsigned currentChunkSize;
00250         unsigned size = fileObj->size;
00251         for (unsigned i=0;i<chunks && size <= fileObj->size;i++)
00252         {
00253                 if (size >= 1024)
00254                         currentChunkSize = 1024;
00255                 else
00256                         currentChunkSize = size;
00257                 size -= currentChunkSize;
00258 
00259                 f.read(buffer,currentChunkSize);
00260                 file->write(buffer,currentChunkSize);
00261         }
00262         f.close();
00263         cout << "[" << fileObj->fullPath << "] ";
00264 }
00265 
00266 void writeFiles(Directory* rootDir, ofstream* file, char* buffer) //this should be a 1024 byte array
00267 {
00268         // if I were a good person I would write all the files in the same order that directories were listed, but I'm not so here's a good old depth first traversal
00269         // note this is a recursive function with a base condition that there are no more subdirectories
00270         // own files
00271         for (unsigned i=0;i<rootDir->files.size();i++)
00272         {
00273                 writeFile(rootDir->files[i],file,buffer);
00274         }
00275         // subdirectories
00276         for (unsigned i=0;i<rootDir->subdirs.size();i++)
00277         {
00278                 writeFiles(rootDir->subdirs[i],file,buffer);
00279         }
00280 }
00281 
00282 int main(int argc, char *argv[])
00283 {
00284         cout << "-- AlienCargoHoldPacker v.1.0\n\n" << 
00285                     "   This utility packages directory structures and files into monolith files to be read by the AlienCargoHold library\n" <<
00286                     "   Matching AlienCargoHold library version - v.1.0\n\n";
00287 
00288         // argument handling
00289         if (argc != 3)
00290         {
00291                 cout << "Usage: achpacker [path_to_root_dir] [output_file]" << endl
00292                          << "Example: achpacker \"C:\\Directory\\myRootDir\\\" myRootDir.ach" << endl << endl;
00293                 return 0;
00294         }
00295         
00296         // directory output
00297         bool output = false;
00298         if (argc == 4 && string(argv[3]).compare("-output")==0)
00299         {
00300                 output = true;
00301         }
00302 
00303         string directory(argv[1]);
00304         string filename(argv[2]);
00305 
00306         cout << "Packing directory [" << directory << "] into output file [" << filename << "]:" << endl;
00307         cout << "Reading directory hierarchy and file sizes..." << endl;
00308 
00309         Directory* dir = walkDirectory(sToWs(directory));
00310         dir->rootDir = true;
00311         dir->name = "root";
00312         dir->localizeNames();
00313         if (dir == NULL)
00314         {
00315                 cout << "Error handling root directory." << endl;
00316                 return 0;
00317         }
00318 
00319         if (output)
00320         {
00321                 cout << "Directories and files: " << endl;
00322                 dir->print("");
00323         }
00324 
00325         cout << "Writing directory structure to file..." << endl;
00326 
00327         ofstream file(filename.c_str());
00328         if (!file.is_open())
00329         {
00330                 cout << "Could not open output file!" << endl;
00331                 return 0;
00332         }
00333 
00334         writeStructure(dir,&file);
00335 
00336         cout << "Writing files (this might take a while)..." << endl;
00337 
00338         char* buffer = new char[1024];
00339         writeFiles(dir,&file,buffer);
00340         delete [] buffer;
00341 
00342         cout << endl << "Done!" << endl;
00343         file.close();
00344         dir->destroy();
00345 
00346         return 0;
00347 }