-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathitg_pck.cc
115 lines (105 loc) · 3.53 KB
/
itg_pck.cc
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
#include <boost/filesystem.hpp>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
template <typename It> void test(It it, std::string text) {
if (!std::equal(text.begin(), text.end(), it)) throw std::runtime_error("PCKF header missing");
}
template <typename It> std::string read(It it, unsigned chars) {
std::string ret;
while (chars--) ret += *it++;
return ret.substr(0, ret.find_last_not_of('\0') + 1);
}
template <typename It> unsigned read32(It it) {
unsigned ret = 0;
for (unsigned i = 0; i < 4; ++i) ret |= static_cast<unsigned char>(*it++) << i * 8;
return ret;
}
namespace fs = boost::filesystem;
struct File {
fs::path name;
unsigned size;
unsigned sizeCompressed;
unsigned mode; // 0 = stored, 1 = compressed
unsigned offset;
};
#include <zlib.h>
struct Extract {
std::ifstream& archive;
Extract(std::ifstream& archive): archive(archive) {}
void operator()(File const& file) {
std::cout << " " << file.name << " " << file.size << " at " << file.offset << std::endl;
if (file.mode == 1) std::cout << " " << file.size << "->" << file.sizeCompressed << std::endl;
std::vector<char> buffer(file.sizeCompressed);
archive.seekg(file.offset);
archive.read(&buffer[0], buffer.size());
std::string ext;
if (file.mode == 1) {
std::vector<char> buf2(file.size);
z_stream strm = z_stream();
strm.avail_in = buffer.size();
strm.next_in = reinterpret_cast<Bytef*>(&buffer[0]);
strm.avail_out = buf2.size();
strm.next_out = reinterpret_cast<Bytef*>(&buf2[0]);
inflateInit2(&strm, -15);
int ret = inflate(&strm, Z_SYNC_FLUSH);
inflateEnd(&strm);
std::cout << ret << std::endl;
if (ret == Z_STREAM_END) buf2.swap(buffer);
else {
std::ostringstream oss;
oss << ".comp-" << file.size;
ext = oss.str();
}
if (strm.msg) std::cerr << " Error: " << strm.msg << std::endl;
}
// Make directory
makeDir(file.name);
std::ofstream of((file.name.string() + ext).c_str(), std::ios::binary);
of.write(&buffer[0], buffer.size());
}
void makeDir(fs::path p) {
p.remove_filename();
if (p.empty()) return;
makeDir(p);
fs::create_directory(p);
}
};
int main(int argc, char** argv) {
if (argc != 2) throw std::logic_error("Need pck filename as argument");
std::ifstream f(argv[1], std::ios::binary);
f.exceptions(std::ios::failbit);
std::istreambuf_iterator<char> it(f), end;
// PCKF header:
// 17 bytes "PCKFdefault base "
// Name (title) of the package, padded with zeroes until offset 0x84
// Little-endian u32 file count
test(it, "PCKFdefault base ");
std::string title = read(it, 0x73);
std::cout << "Title: " << title << std::endl;
unsigned filecount = read32(it);
std::cout << filecount << " files:" << std::endl;
std::vector<File> files;
while (files.size() < filecount) {
// File entry: (array of filecount file entries)
// Little-endian u32: size of compressed/stored data
// Little-endian u32: size of uncompressed data
// Little-endian u32: position in pck file (in bytes)
// Little-endian u32: name length (in bytes)
// char[namelen]: file name with path, uses '/' as path separator
// Little-endian u32: compression mode (0 = stored, 1 = deflated)
File file;
file.sizeCompressed = read32(it);
file.size = read32(it);
file.offset = read32(it);
unsigned namelen = read32(it);
file.mode = read32(it);
file.name = fs::path(title) / read(it, namelen);
files.push_back(file);
}
std::cout << "Header ends at " << f.tellg() << std::endl;
std::for_each(files.begin(), files.end(), Extract(f));
}