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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
An in-memory buffer specifically designed for downloading files to memory.
Definition: ramfile.h:25
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
An interface to whatever real-time clock we might have available in the current environment.
Definition: trueClock.h:33
std::string get_extension() const
Returns the file extension.
Definition: filename.I:400
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::string get_fullpath_wo_extension() const
Returns the full filename–directory and basename parts–except for the extension.
Definition: filename.I:377