summaryrefslogtreecommitdiff
path: root/contrib/paq.cpp
blob: cd4fb0d68ec4731739941492a5f180e60783f800 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <list>
#include <exception>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/copy.hpp>

using namespace std;

class my_exception : public exception
{
private:
	string cause;

public:
	my_exception(const string& cause) : cause(cause) { }
	virtual const char* what() const { return cause.c_str(); }
};

class paq_builder
{
private:
	struct index_entry
	{
		index_entry(const string& file_name, bool is_dir) : file_name(file_name), is_dir(is_dir) { }

		string file_name;
		bool is_dir;
	};

	list<index_entry> index;
	list<vector<char> > files;

	void process(string path)
	{
		boost::filesystem::directory_iterator end;
		for (boost::filesystem::directory_iterator itr(path.c_str()); itr != end ; ++itr)
		{
			index.push_back(index_entry(itr->filename(), boost::filesystem::is_directory(itr->status())));
			if (index.back().is_dir)
				process(path + "/" + itr->filename());
			else
			{
				ifstream in(itr->string().c_str(), ios::binary);
				if (!in) throw my_exception(string("Can't open ") + itr->string());
				vector<char> v(4);
				*reinterpret_cast<unsigned int*>(&v[0]) = boost::filesystem::file_size(itr->path());
				boost::iostreams::filtering_streambuf<boost::iostreams::output> out;
				out.push(boost::iostreams::gzip_compressor());
				out.push(boost::iostreams::back_inserter(v));
				boost::iostreams::copy(in, out);
				files.push_back(v);
			}
		}
		index.push_back(index_entry(string(""), 0));
	}
	
public:
	paq_builder(const char* path)
	{
		if (!boost::filesystem::exists(path) || !boost::filesystem::is_directory(path))
			throw my_exception(string(path) + " doesn't exist or isn't a directory!");

		process(string(path));
	}

	void write_to_file(const char* path)
	{
		ofstream paq(path, ios::binary);
		if (!paq) throw my_exception(string("Can't open ") + path);

		paq.write("NPAQ", 4);

		list<vector<char> >::iterator f_it = files.begin();
		for (list<index_entry>::iterator i_it = index.begin(); i_it != index.end(); i_it++)
		{
			paq << static_cast<char>(i_it->file_name.size());
			if (i_it->file_name.size())
			{
				paq.write(i_it->file_name.c_str(), i_it->file_name.size());
				unsigned int file_size = i_it->is_dir ? 0 : f_it++->size();
				paq.write(reinterpret_cast<char*>(&file_size), 4);
				paq << static_cast<char>(file_size ? 0 : 1);
			}
		}
		
		for(f_it = files.begin(); f_it != files.end(); f_it++)
			paq.write(&(*f_it)[0], f_it->size());
	}
};

int main(int argc, char** argv)
{
	if (argc < 3)
	{
		cout << "Usage: " << argv[0] << " <in-directory> <out-paq>" << endl;
		return 0;
	}

	try
	{
		paq_builder(argv[1]).write_to_file(argv[2]);
	}
	catch (const exception& e)
	{
		cerr << e.what() << endl;
		return -1;
	}

	return 0;
}