Panda3D
extractor.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 extractor.cxx
10  * @author mike
11  * @date 1997-01-09
12  */
13 
14 #include "extractor.h"
15 #include "config_downloader.h"
16 #include "trueClock.h"
17 #include "filename.h"
18 #include "error_utils.h"
19 
20 
21 /**
22  *
23  */
24 Extractor::
25 Extractor() {
26  _initiated = false;
27  _multifile = new Multifile;
28 }
29 
30 /**
31  *
32  */
33 Extractor::
34 ~Extractor() {
35  reset();
36 }
37 
38 /**
39  * Specifies the filename of the Multifile that the Extractor will read.
40  * Returns true on success, false if the mulifile name is invalid.
41  */
43 set_multifile(const Filename &multifile_name) {
44  reset();
45  _multifile_name = multifile_name;
46  return _multifile->open_read(multifile_name);
47 }
48 
49 /**
50  * Specifies the directory into which all extracted subfiles will be written.
51  * Relative paths of subfiles within the Multifile will be written as relative
52  * paths to this directory.
53  */
55 set_extract_dir(const Filename &extract_dir) {
56  _extract_dir = extract_dir;
57 }
58 
59 /**
60  * Interrupts the Extractor in the middle of its business and makes it ready
61  * to accept a new list of subfiles to extract.
62  */
64 reset() {
65  if (_initiated) {
66  if (_read != nullptr) {
68  _read = nullptr;
69  }
70  _write.close();
71  _initiated = false;
72  }
73 
74  _requests.clear();
75  _requests_total_length = 0;
76 }
77 
78 /**
79  * Requests a particular subfile to be extracted when step() or run() is
80  * called. Returns true if the subfile exists, false otherwise.
81  */
83 request_subfile(const Filename &subfile_name) {
84  int index = _multifile->find_subfile(subfile_name);
85  if (index < 0) {
86  return false;
87  }
88  _requests.push_back(index);
89  _requests_total_length += _multifile->get_subfile_length(index);
90  return true;
91 }
92 
93 /**
94  * Requests all subfiles in the Multifile to be extracted. Returns the number
95  * requested.
96  */
99  _requests.clear();
100  _requests_total_length = 0;
101  int num_subfiles = _multifile->get_num_subfiles();
102  for (int i = 0; i < num_subfiles; i++) {
103  _requests.push_back(i);
104  _requests_total_length += _multifile->get_subfile_length(i);
105  }
106  return num_subfiles;
107 }
108 
109 /**
110  * After all of the requests have been made via request_file() or
111  * request_all_subfiles(), call step() repeatedly until it stops returning
112  * EU_ok.
113  *
114  * step() extracts the next small unit of data from the Multifile. Returns
115  * EU_ok if progress is continuing, EU_error_abort if there is a problem, or
116  * EU_success when the last piece has been extracted.
117  *
118  * Also see run().
119  */
121 step() {
122  if (!_initiated) {
123  _request_index = 0;
124  _subfile_index = 0;
125  _subfile_pos = 0;
126  _subfile_length = 0;
127  _total_bytes_extracted = 0;
128  _read = nullptr;
129  _initiated = true;
130  }
131 
133  double now = clock->get_short_time();
134  double finish = now + extractor_step_time;
135 
136  do {
137  if (_read == nullptr) {
138  // Time to open the next subfile.
139  if (_request_index >= (int)_requests.size()) {
140  // All done!
141  if (downloader_cat.is_debug()) {
142  downloader_cat.debug()
143  << "Finished extracting.\n";
144  }
145  reset();
146  return EU_success;
147  }
148 
149  _subfile_index = _requests[_request_index];
150  _subfile_filename = Filename(_extract_dir,
151  _multifile->get_subfile_name(_subfile_index));
152 
153  if (downloader_cat.is_debug()) {
154  downloader_cat.debug()
155  << "Extracting " << _subfile_filename << ".\n";
156  }
157 
158  _subfile_filename.set_binary();
159  _subfile_filename.make_dir();
160  if (!_subfile_filename.open_write(_write, true)) {
161  downloader_cat.error()
162  << "Unable to write to " << _subfile_filename << ".\n";
163  reset();
164  return EU_error_abort;
165  }
166 
167  _subfile_length = _multifile->get_subfile_length(_subfile_index);
168  _subfile_pos = 0;
169  _read = _multifile->open_read_subfile(_subfile_index);
170  if (_read == nullptr) {
171  downloader_cat.error()
172  << "Unable to read subfile "
173  << _multifile->get_subfile_name(_subfile_index) << ".\n";
174  reset();
175  return EU_error_abort;
176  }
177 
178  } else if (_subfile_pos >= _subfile_length) {
179  // Time to close this subfile.
180 
181  if (downloader_cat.is_debug()) {
182  downloader_cat.debug()
183  << "Finished current subfile.\n";
184  }
186  _read = nullptr;
187  _write.close();
188  _request_index++;
189 
190  } else {
191  // Read a number of bytes from the subfile and write them to the output.
192  static const size_t buffer_size = 1024;
193  char buffer[buffer_size];
194 
195  size_t max_bytes = std::min(buffer_size, _subfile_length - _subfile_pos);
196  _read->read(buffer, max_bytes);
197  size_t count = _read->gcount();
198  while (count != 0) {
199  if (downloader_cat.is_spam()) {
200  downloader_cat.spam()
201  << " . . . read " << count << " bytes.\n";
202  }
203  _write.write(buffer, count);
204  if (!_write) {
205  downloader_cat.error()
206  << "Error writing to " << _subfile_filename << ".\n";
207  reset();
208  return EU_error_abort;
209  }
210 
211  _subfile_pos += count;
212  _total_bytes_extracted += count;
213 
214  now = clock->get_short_time();
215  if (now >= finish) {
216  // That's enough for now.
217  return EU_ok;
218  }
219 
220  max_bytes = std::min(buffer_size, _subfile_length - _subfile_pos);
221  _read->read(buffer, max_bytes);
222  count = _read->gcount();
223  }
224 
225  if (max_bytes != 0) {
226  downloader_cat.error()
227  << "Unexpected EOF on multifile " << _multifile_name << ".\n";
228  reset();
229  return EU_error_abort;
230  }
231  }
232 
233  now = clock->get_short_time();
234  } while (now < finish);
235 
236  // That's enough for now.
237  return EU_ok;
238 }
239 
240 /**
241  * Returns the fraction of the Multifile extracted so far.
242  */
243 PN_stdfloat Extractor::
244 get_progress() const {
245  if (!_initiated) {
246  return 0.0f;
247  }
248  if (_requests_total_length == 0) {
249  return 1.0f;
250  }
251 
252  return (PN_stdfloat)_total_bytes_extracted / (PN_stdfloat)_requests_total_length;
253 }
254 
255 /**
256  * A convenience function to extract the Multifile all at once, when you don't
257  * care about doing it in the background.
258  *
259  * First, call request_file() or request_all_files() to specify the files you
260  * would like to extract, then call run() to do the extraction. Also see
261  * step() for when you would like the extraction to happen as a background
262  * task.
263  */
265 run() {
266  while (true) {
267  int ret = step();
268  if (ret == EU_success) {
269  return true;
270  }
271  if (ret < 0) {
272  return false;
273  }
274  }
275 }
void reset()
Interrupts the Extractor in the middle of its business and makes it ready to accept a new list of sub...
Definition: extractor.cxx:64
bool run()
A convenience function to extract the Multifile all at once, when you don't care about doing it in th...
Definition: extractor.cxx:265
void set_extract_dir(const Filename &extract_dir)
Specifies the directory into which all extracted subfiles will be written.
Definition: extractor.cxx:55
bool set_multifile(const Filename &multifile_name)
Specifies the filename of the Multifile that the Extractor will read.
Definition: extractor.cxx:43
bool request_subfile(const Filename &subfile_name)
Requests a particular subfile to be extracted when step() or run() is called.
Definition: extractor.cxx:83
get_progress
Returns the fraction of the Multifile extracted so far.
Definition: extractor.h:54
int request_all_subfiles()
Requests all subfiles in the Multifile to be extracted.
Definition: extractor.cxx:98
int step()
After all of the requests have been made via request_file() or request_all_subfiles(),...
Definition: extractor.cxx:121
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
bool open_read(std::ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
Definition: filename.cxx:1863
void set_binary()
Indicates that the filename represents a binary file.
Definition: filename.I:414
bool make_dir() const
Creates all the directories in the path to the file specified in the filename, except for the basenam...
Definition: filename.cxx:2484
bool open_write(std::ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
Definition: filename.cxx:1899
A file that contains a set of files.
Definition: multifile.h:37
static void close_read_subfile(std::istream *stream)
Closes a file opened by a previous call to open_read_subfile().
Definition: multifile.cxx:1628
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.