]> git.tdb.fi Git - libs/datafile.git/blob - tool/packer.cpp
d54d333e3affc244e80ce9653cbc4867f66f43fe
[libs/datafile.git] / tool / packer.cpp
1 #include <msp/datafile/parser.h>
2 #include <msp/datafile/statement.h>
3 #include <msp/datafile/writer.h>
4 #include <msp/fs/utils.h>
5 #include <msp/io/memory.h>
6 #include <msp/strings/format.h>
7 #include "packer.h"
8 #include "tool.h"
9
10 using namespace std;
11 using namespace Msp;
12
13 Packer::Packer(DataTool &t):
14         tool(t),
15         tmp_file(tempfile()),
16         dir_alloc(0)
17 { }
18
19 IO::BufferedFile *Packer::tempfile()
20 {
21         FS::Path tmpdir;
22         const char *tmp_env = getenv("TMPDIR");
23         if(tmp_env)
24                 tmpdir = tmp_env;
25         else
26                 tmpdir = "/tmp";
27
28         for(unsigned i=0;; ++i)
29         {
30                 try
31                 {
32                         FS::Path filename = tmpdir/format("mspdatatool.%d", i);
33                         IO::BufferedFile *file = new IO::BufferedFile(filename.str(), IO::M_RDWR, IO::File::C_NEW);
34                         FS::unlink(filename.str());
35                         return file;
36                 }
37                 catch(const IO::file_already_exists &)
38                 {
39                         continue;
40                 }
41         }
42 }
43
44 Packer::~Packer()
45 {
46         delete tmp_file;
47 }
48
49 void Packer::pack_file(const string &fn)
50 {
51         if(!tmp_file)
52                 throw logic_error("Packer::pack_file");
53
54         IO::SeekOffset offset = tmp_file->tell();
55
56         IO::BufferedFile in(fn);
57         ObjectList objects;
58         bool raw = detect_raw(in);
59         if(raw)
60                 transfer_raw(in, *tmp_file);
61         else
62                 transfer_datafile(in, fn, *tmp_file, objects);
63
64         unsigned length = tmp_file->tell()-offset;
65
66         DataFile::Statement st("file");
67         st.append(FS::basename(fn));
68         st.sub.push_back((DataFile::Statement("slice"), offset, length));
69         for(list<Object>::const_iterator i=objects.begin(); i!=objects.end(); ++i)
70                 st.sub.push_back((DataFile::Statement("object"), i->name, i->keyword));
71         directory.push_back(st);
72         dir_alloc += 100+objects.size()*100;
73 }
74
75 bool Packer::detect_raw(IO::Seekable &in)
76 {
77         IO::SeekOffset offset = in.tell();
78         bool raw = false;
79         DataFile::Parser parser(in, "-");
80         while(!raw && parser && in.tell()<10240)
81         {
82                 try
83                 {
84                         parser.parse();
85                 }
86                 catch(...)
87                 {
88                         raw = true;
89                 }
90         }
91         in.seek(offset, IO::S_BEG);
92         return raw;
93 }
94
95 void Packer::transfer_datafile(IO::Base &in, const string &fn, IO::Base &out, ObjectList &objects)
96 {
97         DataFile::Parser parser(in, fn);
98
99         DataFile::Writer *writer = tool.create_writer(out);
100
101         bool collection = FS::extpart(fn)==".mdc";
102         while(parser)
103         {
104                 DataFile::Statement st = parser.parse(true);
105                 if(st.valid)
106                 {
107                         if(collection && !st.control)
108                         {
109                                 if(st.get_signature()=="s")
110                                 {
111                                         Object obj;
112                                         obj.name = st.args[0].get<string>();
113                                         obj.keyword = st.keyword;
114                                         objects.push_back(obj);
115                                 }
116                                 else
117                                         collection = false;
118                         }
119                         if(!st.control || st.keyword=="__src")
120                                 writer->write(st);
121                 }
122         }
123         writer->write(DataFile::Statement("__end"));
124         delete writer;
125 }
126
127 void Packer::transfer_raw(IO::Base &in, IO::Base &out)
128 {
129         while(!in.eof())
130         {
131                 char buf[4096];
132                 unsigned len = in.read(buf, sizeof(buf));
133                 if(len==0)
134                         break;
135                 out.write(buf, len);
136         }
137 }
138
139 void Packer::create_pack(const string &fn)
140 {
141         vector<char> dir_buffer(dir_alloc);
142         IO::Memory mem(&dir_buffer[0], dir_buffer.size(), IO::M_WRITE);
143
144         unsigned base_offset = 0;
145         while(1)
146         {
147                 mem.seek(0, IO::S_BEG);
148
149                 DataFile::Writer *writer = tool.create_writer(mem);
150
151                 for(list<DataFile::Statement>::const_iterator i=directory.begin(); i!=directory.end(); ++i)
152                         writer->write(*i);
153                 if(base_offset==0)
154                         base_offset = mem.tell();
155                 writer->write((DataFile::Statement("base_offset"), base_offset));
156                 writer->write(DataFile::Statement("__end"));
157                 delete writer;
158
159                 unsigned dir_size = mem.tell();
160                 if(dir_size<=base_offset)
161                         break;
162                 base_offset = dir_size;
163         }
164
165         IO::File out(fn, IO::M_WRITE);
166         out.write(&dir_buffer[0], base_offset);
167         tmp_file->seek(0, IO::S_BEG);
168         unsigned bufsize = 1048576;
169         char *buf = new char[bufsize];
170         while(!tmp_file->eof())
171         {
172                 unsigned len = tmp_file->read(buf, bufsize);
173                 if(!len)
174                         break;
175                 out.write(buf, len);
176         }
177         delete[] buf;
178 }