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  */
42 bool Extractor::
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  */
54 void Extractor::
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  */
63 void Extractor::
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  */
82 bool Extractor::
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  */
97 int Extractor::
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  */
120 int Extractor::
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  */
264 bool Extractor::
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 }
bool open_write(std::ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
Definition: filename.cxx:1899
static TrueClock * get_global_ptr()
Returns a pointer to the one TrueClock object in the world.
Definition: trueClock.I:68
void set_extract_dir(const Filename &extract_dir)
Specifies the directory into which all extracted subfiles will be written.
Definition: extractor.cxx:55
void set_binary()
Indicates that the filename represents a binary file.
Definition: filename.I:414
bool open_read(std::ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
Definition: filename.cxx:1863
bool request_subfile(const Filename &subfile_name)
Requests a particular subfile to be extracted when step() or run() is called.
Definition: extractor.cxx:83
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int request_all_subfiles()
Requests all subfiles in the Multifile to be extracted.
Definition: extractor.cxx:98
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
static void close_read_subfile(std::istream *stream)
Closes a file opened by a previous call to open_read_subfile().
Definition: multifile.cxx:1628
int step()
After all of the requests have been made via request_file() or request_all_subfiles(),...
Definition: extractor.cxx:121
bool set_multifile(const Filename &multifile_name)
Specifies the filename of the Multifile that the Extractor will read.
Definition: extractor.cxx:43
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 interface to whatever real-time clock we might have available in the current environment.
Definition: trueClock.h:33
A file that contains a set of files.
Definition: multifile.h:37
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.