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