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