Panda3D
decompressor.cxx
1 // Filename: decompressor.cxx
2 // Created by: mike (09Jan97)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "pandabase.h"
16 
17 #ifdef HAVE_ZLIB
18 
19 #include "config_downloader.h"
20 
21 #include "error_utils.h"
22 #include "filename.h"
23 #include "ramfile.h"
24 #include "zStream.h"
25 #include "config_express.h"
26 #include "trueClock.h"
27 
28 #include "decompressor.h"
29 
30 #include <stdio.h>
31 #include <errno.h>
32 
33 ////////////////////////////////////////////////////////////////////
34 // Function: Decompressor::Constructor
35 // Access: Public
36 // Description:
37 ////////////////////////////////////////////////////////////////////
38 Decompressor::
39 Decompressor() {
40  _source = NULL;
41  _decompress = NULL;
42  _dest = NULL;
43 }
44 
45 ////////////////////////////////////////////////////////////////////
46 // Function: Decompressor::Destructor
47 // Access: Public
48 // Description:
49 ////////////////////////////////////////////////////////////////////
50 Decompressor::
51 ~Decompressor() {
52  cleanup();
53 }
54 
55 ////////////////////////////////////////////////////////////////////
56 // Function: Decompressor::initiate
57 // Access: Public
58 // Description: Begins a background decompression of the named file
59 // (whose filename must end in ".pz") to a new file
60 // without the .pz extension. The source file is
61 // removed after successful completion.
62 ////////////////////////////////////////////////////////////////////
63 int Decompressor::
64 initiate(const Filename &source_file) {
65  string extension = source_file.get_extension();
66  if (extension == "pz") {
67  Filename dest_file = source_file;
68  dest_file = source_file.get_fullpath_wo_extension();
69  return initiate(source_file, dest_file);
70  }
71 
72  if (downloader_cat.is_debug()) {
73  downloader_cat.debug()
74  << "Unknown file extension for decompressor: ."
75  << extension << endl;
76  }
77  return EU_error_abort;
78 }
79 
80 ////////////////////////////////////////////////////////////////////
81 // Function: Decompressor::initiate
82 // Access: Public
83 // Description: Begins a background decompression from the named
84 // source file to the named destination file. The
85 // source file is removed after successful completion.
86 ////////////////////////////////////////////////////////////////////
87 int Decompressor::
88 initiate(const Filename &source_file, const Filename &dest_file) {
89  cleanup();
90 
91  // Open source file
92  _source_filename = Filename(source_file);
93  _source_filename.set_binary();
94 
95  pifstream *source_pfstream = new pifstream;
96  _source = source_pfstream;
97  if (!_source_filename.open_read(*source_pfstream)) {
98  downloader_cat.error()
99  << "Unable to read " << _source_filename << "\n";
100  return get_write_error();
101  }
102 
103  // Determine source file length
104  source_pfstream->seekg(0, ios::end);
105  _source_length = source_pfstream->tellg();
106  if (_source_length == 0) {
107  downloader_cat.warning()
108  << "Zero length file: " << source_file << "\n";
109  return EU_error_file_empty;
110  }
111  source_pfstream->seekg(0, ios::beg);
112 
113  // Open destination file
114  Filename dest_filename(dest_file);
115  dest_filename.set_binary();
116 
117  pofstream *dest_pfstream = new pofstream;
118  _dest = dest_pfstream;
119  if (dest_filename.exists()) {
120  downloader_cat.info()
121  << dest_filename << " already exists, removing.\n";
122  if (!dest_filename.unlink()) {
123  downloader_cat.error()
124  << "Unable to remove old " << dest_filename << "\n";
125  return get_write_error();
126  }
127  } else {
128  if (downloader_cat.is_debug()) {
129  downloader_cat.debug()
130  << dest_filename << " does not already exist.\n";
131  }
132  }
133  if (!dest_filename.open_write(*dest_pfstream, true)) {
134  downloader_cat.error()
135  << "Unable to write to " << dest_filename << "\n";
136  return get_write_error();
137  }
138 
139  // Now create the decompressor stream.
140  _decompress = new IDecompressStream(_source, false);
141  return EU_success;
142 }
143 
144 ////////////////////////////////////////////////////////////////////
145 // Function: Decompressor::run
146 // Access: Public
147 // Description: Called each frame to do the next bit of work in the
148 // background task. Returns EU_ok if a chunk is
149 // completed but there is more to go, or EU_success when
150 // we're all done. Any other return value indicates an
151 // error.
152 ////////////////////////////////////////////////////////////////////
153 int Decompressor::
154 run() {
155  if (_decompress == (istream *)NULL) {
156  // Hmm, we were already done.
157  return EU_success;
158  }
159 
161  double now = clock->get_short_time();
162  double finish = now + decompressor_step_time;
163 
164  static const size_t buffer_size = 1024;
165  char buffer[buffer_size];
166 
167  _decompress->read(buffer, buffer_size);
168  size_t count = _decompress->gcount();
169  while (count != 0) {
170  _dest->write(buffer, count);
171 
172  now = clock->get_short_time();
173  if (now >= finish) {
174  // That's enough for now.
175  return EU_ok;
176  }
177 
178  _decompress->read(buffer, buffer_size);
179  count = _decompress->gcount();
180  }
181 
182  // All done!
183  cleanup();
184  if (!keep_temporary_files) {
185  _source_filename.unlink();
186  }
187  return EU_success;
188 }
189 
190 ////////////////////////////////////////////////////////////////////
191 // Function: Decompressor::decompress
192 // Access: Public
193 // Description: Performs a foreground decompression of the named
194 // file; does not return until the decompression is
195 // complete.
196 ////////////////////////////////////////////////////////////////////
197 bool Decompressor::
198 decompress(const Filename &source_file) {
199  int ret = initiate(source_file);
200  if (ret < 0)
201  return false;
202 
203  int ch = _decompress->get();
204  while (!_decompress->eof() && !_decompress->fail()) {
205  _dest->put(ch);
206  ch = _decompress->get();
207  }
208 
209  cleanup();
210  if (!keep_temporary_files) {
211  _source_filename.unlink();
212  }
213  return true;
214 }
215 
216 ////////////////////////////////////////////////////////////////////
217 // Function: Decompressor::decompress
218 // Access: Public
219 // Description: Does an in-memory decompression of the indicated
220 // Ramfile. The decompressed contents are written back
221 // into the same Ramfile on completion.
222 ////////////////////////////////////////////////////////////////////
223 bool Decompressor::
224 decompress(Ramfile &source_and_dest_file) {
225  istringstream source(source_and_dest_file._data);
226  ostringstream dest;
227 
228  IDecompressStream decompress(&source, false);
229 
230  int ch = decompress.get();
231  while (!decompress.eof() && !decompress.fail()) {
232  dest.put(ch);
233  ch = decompress.get();
234  }
235 
236  source_and_dest_file._pos = 0;
237  source_and_dest_file._data = dest.str();
238  return true;
239 }
240 
241 ////////////////////////////////////////////////////////////////////
242 // Function: Decompressor::get_progress
243 // Access: Public
244 // Description: Returns the ratio through the decompression step
245 // in the background.
246 ////////////////////////////////////////////////////////////////////
247 PN_stdfloat Decompressor::
248 get_progress() const {
249  if (_decompress == (istream *)NULL) {
250  // Hmm, we were already done.
251  return 1.0f;
252  }
253 
254  nassertr(_source_length > 0, 0.0);
255  size_t source_pos = _source->tellg();
256 
257  // We stop the scale at 0.99 because there may be a little bit more
258  // to do even after the decompressor has read all of the source.
259  return (0.99f * (PN_stdfloat)source_pos / (PN_stdfloat)_source_length);
260 }
261 
262 ////////////////////////////////////////////////////////////////////
263 // Function: Decompressor::cleanup
264 // Access: Private
265 // Description: Called to reset a previous decompressor state and
266 // clean up properly.
267 ////////////////////////////////////////////////////////////////////
268 void Decompressor::
269 cleanup() {
270  if (_source != (istream *)NULL) {
271  delete _source;
272  _source = NULL;
273  }
274  if (_dest != (ostream *)NULL) {
275  delete _dest;
276  _dest = NULL;
277  }
278  if (_decompress != (istream *)NULL) {
279  delete _decompress;
280  _decompress = NULL;
281  }
282 }
283 
284 #endif // HAVE_ZLIB
static TrueClock * get_global_ptr()
Returns a pointer to the one TrueClock object in the world.
Definition: trueClock.I:81
string get_extension() const
Returns the file extension.
Definition: filename.I:477
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
An in-memory buffer specifically designed for downloading files to memory.
Definition: ramfile.h:27
An interface to whatever real-time clock we might have available in the current environment.
Definition: trueClock.h:38
string get_fullpath_wo_extension() const
Returns the full filename–directory and basename parts–except for the extension.
Definition: filename.I:448