Panda3D
Loading...
Searching...
No Matches
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 */
35Decompressor::
36Decompressor() {
37 _source = nullptr;
38 _decompress = nullptr;
39 _dest = nullptr;
40}
41
42/**
43 *
44 */
45Decompressor::
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 */
55int Decompressor::
56initiate(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 */
76int Decompressor::
77initiate(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 */
138int Decompressor::
139run() {
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 */
179bool Decompressor::
180decompress(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 */
202bool Decompressor::
203decompress(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 */
223PN_stdfloat Decompressor::
224get_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 */
241void Decompressor::
242cleanup() {
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:44
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.