Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/express/multifile.cxx

Go to the documentation of this file.
00001 // Filename: multifile.cxx
00002 // Created by:  mike (09Jan97)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "multifile.h"
00020 
00021 #include "config_express.h"
00022 #include "streamWriter.h"
00023 #include "streamReader.h"
00024 #include "datagram.h"
00025 #include "zStream.h"
00026 
00027 #include <algorithm>
00028 
00029 // This sequence of bytes begins each Multifile to identify it as a
00030 // Multifile.
00031 const char Multifile::_header[] = "pmf\0\n\r";
00032 const size_t Multifile::_header_size = 6;
00033 
00034 // These numbers identify the version of the Multifile.  Generally, a
00035 // change in the major version is intolerable; while a Multifile with
00036 // an older minor version may still be read.
00037 const int Multifile::_current_major_ver = 1;
00038 const int Multifile::_current_minor_ver = 0;
00039 
00040 //
00041 // A Multifile consists of the following elements:
00042 //
00043 // (1) A header.  This is always the first n bytes of the Multifile,
00044 // and contains a magic number to identify the file, as well as
00045 // version numbers and any file-specific parameters.
00046 //
00047 //   char[6]    The string Multifile::_header, a magic number.
00048 //   int16      The file's major version number
00049 //   int16      The file's minor version number
00050 //   uint32     Scale factor.  This scales all address references within
00051 //              the file.  Normally 1, this may be set larger to
00052 //              support Multifiles larger than 4GB.
00053 
00054 //
00055 // (2) Zero or more index entries, one for each subfile within the
00056 // Multifile.  These entries are of variable length.  The first one of
00057 // these immediately follows the header, and the first word of each
00058 // index entry contains the address of the next index entry.  A zero
00059 // "next" address marks the end of the chain.  These may appear at any
00060 // point within the Multifile; they do not necessarily appear in
00061 // sequential order at the beginning of the file (although they will
00062 // after the file has been "packed").
00063 //
00064 //   uint32     The address of the next entry.
00065 //   uint32     The address of this subfile's data record.
00066 //   uint32     The length in bytes of this subfile's data record.
00067 //   uint16     The Subfile::_flags member.
00068 //  [uint32]    The original, uncompressed length of the subfile, if it
00069 //               is compressed.  This field is only present if the
00070 //               SF_compressed bit is set in _flags.
00071 //   uint16     The length in bytes of the subfile's name.
00072 //   char[n]    The subfile's name.
00073 //
00074 // (3) Zero or more data entries, one for each subfile.  These may
00075 // appear at any point within the Multifile; they do not necessarily
00076 // follow each index entry, nor are they necessarily all grouped
00077 // together at the end (although they will be all grouped together at
00078 // the end after the file has been "packed").  These are just blocks
00079 // of literal data.
00080 //
00081 
00082 ////////////////////////////////////////////////////////////////////
00083 //     Function: Multifile::Constructor
00084 //       Access: Published
00085 //  Description:
00086 ////////////////////////////////////////////////////////////////////
00087 Multifile::
00088 Multifile() {
00089   _read = (istream *)NULL;
00090   _write = (ostream *)NULL;
00091   _next_index = 0;
00092   _last_index = 0;
00093   _needs_repack = false;
00094   _scale_factor = 1;
00095   _new_scale_factor = 1;
00096   _file_major_ver = 0;
00097   _file_minor_ver = 0;
00098 }
00099 
00100 ////////////////////////////////////////////////////////////////////
00101 //     Function: Multifile::Destructor
00102 //       Access: Published
00103 //  Description:
00104 ////////////////////////////////////////////////////////////////////
00105 Multifile::
00106 ~Multifile() {
00107   close();
00108 }
00109 
00110 ////////////////////////////////////////////////////////////////////
00111 //     Function: Multifile::Copy Constructor
00112 //       Access: Private
00113 //  Description: Don't try to copy Multifiles.
00114 ////////////////////////////////////////////////////////////////////
00115 Multifile::
00116 Multifile(const Multifile &copy) {
00117   nassertv(false);
00118 }
00119 
00120 ////////////////////////////////////////////////////////////////////
00121 //     Function: Multifile::Copy Assignment Operator
00122 //       Access: Private
00123 //  Description: Don't try to copy Multifiles.
00124 ////////////////////////////////////////////////////////////////////
00125 void Multifile::
00126 operator = (const Multifile &copy) {
00127   nassertv(false);
00128 }
00129 
00130 ////////////////////////////////////////////////////////////////////
00131 //     Function: Multifile::open_read
00132 //       Access: Published
00133 //  Description: Opens the named Multifile on disk for reading.  The
00134 //               Multifile index is read in, and the list of subfiles
00135 //               becomes available; individual subfiles may then be
00136 //               extracted or read, but the list of subfiles may not
00137 //               be modified.
00138 //
00139 //               Also see the version of open_read() which accepts an
00140 //               istream.  Returns true on success, false on failure.
00141 ////////////////////////////////////////////////////////////////////
00142 bool Multifile::
00143 open_read(const Filename &multifile_name) {
00144   close();
00145   Filename fname = multifile_name;
00146   fname.set_binary();
00147   if (!fname.open_read(_read_file)) {
00148     return false;
00149   }
00150   _read = &_read_file;
00151   _multifile_name = multifile_name;
00152   return read_index();
00153 }
00154 
00155 ////////////////////////////////////////////////////////////////////
00156 //     Function: Multifile::open_write
00157 //       Access: Published
00158 //  Description: Opens the named Multifile on disk for writing.  If
00159 //               there already exists a file by that name, it is
00160 //               truncated.  The Multifile is then prepared for
00161 //               accepting a brand new set of subfiles, which will be
00162 //               written to the indicated filename.  Individual
00163 //               subfiles may not be extracted or read.
00164 //
00165 //               Also see the version of open_write() which accepts an
00166 //               ostream.  Returns true on success, false on failure.
00167 ////////////////////////////////////////////////////////////////////
00168 bool Multifile::
00169 open_write(const Filename &multifile_name) {
00170   close();
00171   Filename fname = multifile_name;
00172   fname.set_binary();
00173   if (!fname.open_write(_write_file, true)) {
00174     return false;
00175   }
00176   _write = &_write_file;
00177   _multifile_name = multifile_name;
00178   return true;
00179 }
00180 
00181 ////////////////////////////////////////////////////////////////////
00182 //     Function: Multifile::open_read_write
00183 //       Access: Published
00184 //  Description: Opens the named Multifile on disk for reading and
00185 //               writing.  If there already exists a file by that
00186 //               name, its index is read.  Subfiles may be added or
00187 //               removed, and the resulting changes will be written to
00188 //               the named file.
00189 //
00190 //               Also see the version of open_read_write() which
00191 //               accepts an iostream.  Returns true on success, false
00192 //               on failure.
00193 ////////////////////////////////////////////////////////////////////
00194 bool Multifile::
00195 open_read_write(const Filename &multifile_name) {
00196   close();
00197   Filename fname = multifile_name;
00198   fname.set_binary();
00199   bool exists = fname.exists();
00200   if (!fname.open_read_write(_read_write_file)) {
00201     return false;
00202   }
00203   _read = &_read_write_file;
00204   _write = &_read_write_file;
00205   _multifile_name = multifile_name;
00206 
00207   if (exists) {
00208     return read_index();
00209   } else {
00210     return true;
00211   }
00212 }
00213 
00214 ////////////////////////////////////////////////////////////////////
00215 //     Function: Multifile::close
00216 //       Access: Published
00217 //  Description: Closes the Multifile if it is open.  All changes are
00218 //               flushed to disk, and the file becomes invalid for
00219 //               further operations until the next call to open().
00220 ////////////////////////////////////////////////////////////////////
00221 void Multifile::
00222 close() {
00223   if (_new_scale_factor != _scale_factor) {
00224     // If we have changed the scale factor recently, we need to force
00225     // a repack.
00226     repack();
00227   } else {
00228     flush();
00229   }
00230 
00231   _read = (istream *)NULL;
00232   _write = (ostream *)NULL;
00233   _next_index = 0;
00234   _last_index = 0;
00235   _needs_repack = false;
00236   _scale_factor = 1;
00237   _new_scale_factor = 1;
00238   _file_major_ver = 0;
00239   _file_minor_ver = 0;
00240 
00241   _read_file.close();
00242   _write_file.close();
00243   _read_write_file.close();
00244   _multifile_name = Filename();
00245 
00246   clear_subfiles();
00247 }
00248 
00249 ////////////////////////////////////////////////////////////////////
00250 //     Function: Multifile::set_scale_factor
00251 //       Access: Published
00252 //  Description: Changes the internal scale factor for this Multifile.
00253 //
00254 //               This is normally 1, but it may be set to any
00255 //               arbitrary value (greater than zero) to support
00256 //               Multifile archives that exceed 4GB, if necessary.
00257 //               (Individual subfiles may still not exceed 4GB.)
00258 //
00259 //               All addresses within the file are rounded up to the
00260 //               next multiple of _scale_factor, and zeros are written
00261 //               to the file to fill the resulting gaps.  Then the
00262 //               address is divided by _scale_factor and written out
00263 //               as a 32-bit integer.  Thus, setting a scale factor of
00264 //               2 supports up to 8GB files, 3 supports 12GB files,
00265 //               etc.
00266 //
00267 //               Calling this function on an already-existing
00268 //               Multifile will have no immediate effect until a
00269 //               future call to repack() or close() (or until the
00270 //               Multifile is destructed).
00271 ////////////////////////////////////////////////////////////////////
00272 void Multifile::
00273 set_scale_factor(size_t scale_factor) {
00274   nassertv(is_write_valid());
00275   nassertv(scale_factor != (size_t)0);
00276 
00277   if (_next_index == (streampos)0) {
00278     // If it's a brand new Multifile, we can go ahead and set it
00279     // immediately.
00280     _scale_factor = scale_factor;
00281   } else {
00282     // Otherwise, we'd better have read access so we can repack it
00283     // later.
00284     nassertv(is_read_valid());
00285   }
00286 
00287   // Setting the _new_scale_factor different from the _scale_factor
00288   // will force a repack operation on close.
00289   _new_scale_factor = scale_factor;
00290 }
00291 
00292 ////////////////////////////////////////////////////////////////////
00293 //     Function: Multifile::add_subfile
00294 //       Access: Published
00295 //  Description: Adds a file on disk as a subfile to the Multifile.
00296 //               The file named by filename will be read and added to
00297 //               the Multifile at the next call to flush().
00298 //
00299 //               Returns the subfile name on success (it might have
00300 //               been modified slightly), or empty string on failure.
00301 ////////////////////////////////////////////////////////////////////
00302 string Multifile::
00303 add_subfile(const string &subfile_name, const Filename &filename,
00304             int compression_level) {
00305   nassertr(is_write_valid(), string());
00306 
00307   if (!filename.exists()) {
00308     return string();
00309   }
00310   Subfile *subfile = new Subfile;
00311   subfile->_source_filename = filename;
00312   subfile->_source_filename.set_binary();
00313 
00314   return add_new_subfile(subfile_name, subfile, compression_level);
00315 }
00316 
00317 ////////////////////////////////////////////////////////////////////
00318 //     Function: Multifile::flush
00319 //       Access: Published
00320 //  Description: Writes all contents of the Multifile to disk.  Until
00321 //               flush() is called, add_subfile() and remove_subfile()
00322 //               do not actually do anything to disk.  At this point,
00323 //               all of the recently-added subfiles are read and their
00324 //               contents are added to the end of the Multifile, and
00325 //               the recently-removed subfiles are marked gone from
00326 //               the Multifile.
00327 //
00328 //               This may result in a suboptimal index.  To guarantee
00329 //               that the index is written at the beginning of the
00330 //               file, call repack() instead of flush().
00331 //
00332 //               It is not necessary to call flush() explicitly unless
00333 //               you are concerned about reading the recently-added
00334 //               subfiles immediately.
00335 //
00336 //               Returns true on success, false on failure.
00337 ////////////////////////////////////////////////////////////////////
00338 bool Multifile::
00339 flush() {
00340   if (!is_write_valid()) {
00341     return false;
00342   }
00343 
00344   if (_next_index == (streampos)0) {
00345     // If we don't have an index yet, we don't have a header.  Write
00346     // the header.
00347     if (!write_header()) {
00348       return false;
00349     }
00350   }
00351 
00352   nassertr(_write != (ostream *)NULL, false);
00353 
00354   // First, mark out all of the removed subfiles.
00355   PendingSubfiles::iterator pi;
00356   for (pi = _removed_subfiles.begin(); pi != _removed_subfiles.end(); ++pi) {
00357     Subfile *subfile = (*pi);
00358     subfile->rewrite_index_flags(*_write);
00359     delete subfile;
00360   }
00361   _removed_subfiles.clear();
00362 
00363   bool wrote_ok = true;
00364 
00365   if (!_new_subfiles.empty()) {
00366     // Add a few more files to the end.  We always add subfiles at the
00367     // end of the multifile, so go there first.
00368     if (_last_index != (streampos)0) {
00369       _write->seekp(0, ios::end);
00370       if (_write->fail()) {
00371         express_cat.info()
00372           << "Unable to seek Multifile " << _multifile_name << ".\n";
00373         return false;
00374       }
00375       _next_index = _write->tellp();
00376       _next_index = pad_to_streampos(_next_index);
00377 
00378       // And update the forward link from the last_index to point to
00379       // this new index location.
00380       _write->seekp(_last_index);
00381       StreamWriter writer(_write);
00382       writer.add_uint32(streampos_to_word(_next_index));
00383     }
00384 
00385     _write->seekp(_next_index);
00386     nassertr(_next_index == _write->tellp(), false);
00387     
00388     // Ok, here we are at the end of the file.  Write out the
00389     // recently-added subfiles here.  First, count up the index size.
00390     for (pi = _new_subfiles.begin(); pi != _new_subfiles.end(); ++pi) {
00391       Subfile *subfile = (*pi);
00392       _last_index = _next_index;
00393       _next_index = subfile->write_index(*_write, _next_index, this);
00394       _next_index = pad_to_streampos(_next_index);
00395       nassertr(_next_index == _write->tellp(), false);
00396     }
00397     
00398     // Now we're at the end of the index.  Write a 0 here to mark the
00399     // end.
00400     StreamWriter writer(_write);
00401     writer.add_uint32(0);
00402     _next_index += 4;
00403     _next_index = pad_to_streampos(_next_index);
00404 
00405     // All right, now write out each subfile's data.
00406     for (pi = _new_subfiles.begin(); pi != _new_subfiles.end(); ++pi) {
00407       Subfile *subfile = (*pi);
00408       _next_index = subfile->write_data(*_write, _read, _next_index);
00409       _next_index = pad_to_streampos(_next_index);
00410       if (subfile->is_data_invalid()) {
00411         wrote_ok = false;
00412       }
00413       nassertr(_next_index == _write->tellp(), false);
00414     }
00415     
00416     // Now go back and fill in the proper addresses for the data start.
00417     // We didn't do it in the first pass, because we don't really want
00418     // to keep all those pile handles open, and so we didn't have to
00419     // determine each pile's length ahead of time.
00420     for (pi = _new_subfiles.begin(); pi != _new_subfiles.end(); ++pi) {
00421       Subfile *subfile = (*pi);
00422       subfile->rewrite_index_data_start(*_write, this);
00423     }
00424 
00425     _new_subfiles.clear();
00426   }
00427 
00428   _write->flush();
00429   if (!wrote_ok || _write->fail()) {
00430     express_cat.info()
00431       << "Unable to update Multifile " << _multifile_name << ".\n";
00432     close();
00433     return false;
00434   }
00435   return true;
00436 }
00437 
00438 ////////////////////////////////////////////////////////////////////
00439 //     Function: Multifile::repack
00440 //       Access: Published
00441 //  Description: Forces a complete rewrite of the Multifile and all of
00442 //               its contents, so that its index will appear at the
00443 //               beginning of the file with all of the subfiles listed
00444 //               in alphabetical order.  This is considered optimal
00445 //               for reading, and is the standard configuration; but
00446 //               it is not essential to do this.
00447 //
00448 //               It is only valid to call this if the Multifile was
00449 //               opened using open_read_write() and an explicit
00450 //               filename, rather than an iostream.  Also, we must
00451 //               have write permission to the directory containing the
00452 //               Multifile.
00453 //
00454 //               Returns true on success, false on failure.
00455 ////////////////////////////////////////////////////////////////////
00456 bool Multifile::
00457 repack() {
00458   if (_next_index == (streampos)0) {
00459     // If the Multifile hasn't yet been written, this is really just a
00460     // flush operation.
00461     return flush();
00462   }
00463 
00464   nassertr(is_write_valid() && is_read_valid(), false);
00465   nassertr(!_multifile_name.empty(), false);
00466 
00467   // First, we open a temporary filename to copy the Multifile to.
00468   Filename dirname = _multifile_name.get_dirname();
00469   if (dirname.empty()) {
00470     dirname = ".";
00471   }
00472   Filename temp_filename = Filename::temporary(dirname, "mftemp");
00473   temp_filename.set_binary();
00474   ofstream temp;
00475   if (!temp_filename.open_write(temp)) {
00476     express_cat.info()
00477       << "Unable to open temporary file " << temp_filename << "\n";
00478     return false;
00479   }
00480 
00481   // Now we scrub our internal structures so it looks like we're a
00482   // brand new Multifile.
00483   PendingSubfiles::iterator pi;
00484   for (pi = _removed_subfiles.begin(); pi != _removed_subfiles.end(); ++pi) {
00485     Subfile *subfile = (*pi);
00486     delete subfile;
00487   }
00488   _removed_subfiles.clear();
00489   _new_subfiles.clear();
00490   copy(_subfiles.begin(), _subfiles.end(), back_inserter(_new_subfiles));
00491   _next_index = 0;
00492   _last_index = 0;
00493   _scale_factor = _new_scale_factor;
00494 
00495   // And we write our contents to our new temporary file.
00496   _write = &temp;
00497   if (!flush()) {
00498     temp.close();
00499     temp_filename.unlink();
00500     return false;
00501   }
00502 
00503   // Now close everything, and move the temporary file back over our
00504   // original file.
00505   Filename orig_name = _multifile_name;
00506   temp.close();
00507   close();
00508   orig_name.unlink();
00509   if (!temp_filename.rename_to(orig_name)) {
00510     express_cat.info()
00511       << "Unable to rename temporary file " << temp_filename << " to "
00512       << orig_name << ".\n";
00513     return false;
00514   }
00515 
00516   if (!open_read_write(orig_name)) {
00517     express_cat.info()
00518       << "Unable to read newly repacked " << _multifile_name 
00519       << ".\n";
00520     return false;
00521   }
00522 
00523   return true;
00524 }
00525 
00526 ////////////////////////////////////////////////////////////////////
00527 //     Function: Multifile::get_num_subfiles
00528 //       Access: Published
00529 //  Description: Returns the number of subfiles within the Multifile.
00530 //               The subfiles may be accessed in alphabetical order by
00531 //               iterating through [0 .. get_num_subfiles()).
00532 ////////////////////////////////////////////////////////////////////
00533 int Multifile::
00534 get_num_subfiles() const {
00535   return _subfiles.size();
00536 }
00537 
00538 ////////////////////////////////////////////////////////////////////
00539 //     Function: Multifile::find_subfile
00540 //       Access: Published
00541 //  Description: Returns the index of the subfile with the indicated
00542 //               name, or -1 if the named subfile is not within the
00543 //               Multifile.
00544 ////////////////////////////////////////////////////////////////////
00545 int Multifile::
00546 find_subfile(const string &subfile_name) const {
00547   Subfile find_subfile;
00548   find_subfile._name = subfile_name;
00549   Subfiles::const_iterator fi;
00550   fi = _subfiles.find(&find_subfile);
00551   if (fi == _subfiles.end()) {
00552     // Not present.
00553     return -1;
00554   }
00555   return (fi - _subfiles.begin());
00556 }
00557 
00558 ////////////////////////////////////////////////////////////////////
00559 //     Function: Multifile::has_directory
00560 //       Access: Published
00561 //  Description: Returns true if the indicated subfile name is the
00562 //               directory prefix to one or more files within the
00563 //               Multifile.  That is, the Multifile contains at least
00564 //               one file named "subfile_name/...".
00565 ////////////////////////////////////////////////////////////////////
00566 bool Multifile::
00567 has_directory(const string &subfile_name) const {
00568   string prefix = subfile_name;
00569   if (!prefix.empty()) {
00570     prefix += '/';
00571   }
00572   Subfile find_subfile;
00573   find_subfile._name = prefix;
00574   Subfiles::const_iterator fi;
00575   fi = _subfiles.upper_bound(&find_subfile);
00576   if (fi == _subfiles.end()) {
00577     // Not present.
00578     return false;
00579   }
00580 
00581   // At least one subfile exists whose name sorts after prefix.  If it
00582   // contains prefix as the initial substring, then we have a match.
00583   Subfile *subfile = (*fi);
00584   return (subfile->_name.length() > prefix.length() &&
00585           subfile->_name.substr(0, prefix.length()) == prefix);
00586 }
00587 
00588 ////////////////////////////////////////////////////////////////////
00589 //     Function: Multifile::scan_directory
00590 //       Access: Published
00591 //  Description: Considers subfile_name to be the name of a
00592 //               subdirectory within the Multifile, but not a file
00593 //               itself; ills the given vector up with the sorted list
00594 //               of subdirectories or files within the named
00595 //               directory.
00596 //
00597 //               Note that directories do not exist explicitly within
00598 //               a Multifile; this just checks for the existence of
00599 //               files with the given initial prefix.
00600 //
00601 //               Returns true if successful, false otherwise.
00602 ////////////////////////////////////////////////////////////////////
00603 bool Multifile::
00604 scan_directory(vector_string &contents, const string &subfile_name) const {
00605   string prefix = subfile_name;
00606   if (!prefix.empty()) {
00607     prefix += '/';
00608   }
00609   Subfile find_subfile;
00610   find_subfile._name = prefix;
00611   Subfiles::const_iterator fi;
00612   fi = _subfiles.upper_bound(&find_subfile);
00613 
00614   string previous = "";
00615   while (fi != _subfiles.end()) {
00616     Subfile *subfile = (*fi);
00617     if (!(subfile->_name.length() > prefix.length() &&
00618           subfile->_name.substr(0, prefix.length()) == prefix)) {
00619       // We've reached the end of the list of subfiles beneath the
00620       // indicated direcotry prefix.
00621       return true;
00622     }
00623 
00624     size_t slash = subfile->_name.find('/', prefix.length());
00625     string basename = subfile->_name.substr(prefix.length(), slash);
00626     if (basename != previous) {
00627       contents.push_back(basename);
00628       previous = basename;
00629     }
00630     ++fi;
00631   }
00632 
00633   return true;
00634 }
00635 
00636 ////////////////////////////////////////////////////////////////////
00637 //     Function: Multifile::remove_subfile
00638 //       Access: Published
00639 //  Description: Removes the nth subfile from the Multifile.  This
00640 //               will cause all subsequent index numbers to decrease
00641 //               by one.  The file will not actually be removed from
00642 //               the disk until the next call to flush().
00643 //
00644 //               Note that this does not actually remove the data from
00645 //               the indicated subfile; it simply removes it from the
00646 //               index.  The Multifile will not be reduced in size
00647 //               after this operation, until the next call to
00648 //               repack().
00649 ////////////////////////////////////////////////////////////////////
00650 void Multifile::
00651 remove_subfile(int index) {
00652   nassertv(is_write_valid());
00653   nassertv(index >= 0 && index < (int)_subfiles.size());
00654   Subfile *subfile = _subfiles[index];
00655   subfile->_flags |= SF_deleted;
00656   _removed_subfiles.push_back(subfile);
00657   _subfiles.erase(_subfiles.begin() + index);
00658 
00659   _needs_repack = true;
00660 }
00661 
00662 ////////////////////////////////////////////////////////////////////
00663 //     Function: Multifile::get_subfile_name
00664 //       Access: Published
00665 //  Description: Returns the name of the nth subfile.
00666 ////////////////////////////////////////////////////////////////////
00667 const string &Multifile::
00668 get_subfile_name(int index) const {
00669 #ifndef NDEBUG
00670   static string empty_string;
00671   nassertr(index >= 0 && index < (int)_subfiles.size(), empty_string);
00672 #endif
00673   return _subfiles[index]->_name;
00674 }
00675 
00676 ////////////////////////////////////////////////////////////////////
00677 //     Function: Multifile::get_subfile_length
00678 //       Access: Published
00679 //  Description: Returns the uncompressed data length of the nth
00680 //               subfile.  This might return 0 if the subfile has
00681 //               recently been added and flush() has not yet been
00682 //               called.
00683 ////////////////////////////////////////////////////////////////////
00684 size_t Multifile::
00685 get_subfile_length(int index) const {
00686   nassertr(index >= 0 && index < (int)_subfiles.size(), 0);
00687   return _subfiles[index]->_uncompressed_length;
00688 }
00689 
00690 ////////////////////////////////////////////////////////////////////
00691 //     Function: Multifile::is_subfile_compressed
00692 //       Access: Published
00693 //  Description: Returns true if the indicated subfile has been
00694 //               compressed when stored within the archive, false
00695 //               otherwise.
00696 ////////////////////////////////////////////////////////////////////
00697 bool Multifile::
00698 is_subfile_compressed(int index) const {
00699   nassertr(index >= 0 && index < (int)_subfiles.size(), 0);
00700   return (_subfiles[index]->_flags & SF_compressed) != 0;
00701 }
00702 
00703 ////////////////////////////////////////////////////////////////////
00704 //     Function: Multifile::get_subfile_compressed_length
00705 //       Access: Published
00706 //  Description: Returns the number of bytes the indicated subfile
00707 //               consumes within the archive.  For compressed
00708 //               subfiles, this will generally be smaller than
00709 //               get_subfile_length(); for noncompressed subfiles, it
00710 //               will be equal.
00711 ////////////////////////////////////////////////////////////////////
00712 size_t Multifile::
00713 get_subfile_compressed_length(int index) const {
00714   nassertr(index >= 0 && index < (int)_subfiles.size(), 0);
00715   return _subfiles[index]->_data_length;
00716 }
00717 
00718 ////////////////////////////////////////////////////////////////////
00719 //     Function: Multifile::open_read_subfile
00720 //       Access: Published
00721 //  Description: Returns an istream that may be used to read the
00722 //               indicated subfile.  You may seek() within this
00723 //               istream to your heart's content; even though it will
00724 //               be a reference to the already-opened fstream of the
00725 //               Multifile itself, byte 0 appears to be the beginning
00726 //               of the subfile and EOF appears to be the end of the
00727 //               subfile.
00728 //
00729 //               The returned istream will have been allocated via
00730 //               new; you should delete it when you are finished
00731 //               reading the subfile.
00732 //
00733 //               Any future calls to repack() or close() (or the
00734 //               Multifile destructor) will invalidate all currently
00735 //               open subfile pointers.
00736 //
00737 //               The return value will be NULL if the stream cannot be
00738 //               opened for some reason.
00739 ////////////////////////////////////////////////////////////////////
00740 istream *Multifile::
00741 open_read_subfile(int index) {
00742   nassertr(is_read_valid(), NULL);
00743   nassertr(index >= 0 && index < (int)_subfiles.size(), NULL);
00744   Subfile *subfile = _subfiles[index];
00745 
00746   if (subfile->_source != (istream *)NULL ||
00747       !subfile->_source_filename.empty()) {
00748     // The subfile has not yet been copied into the physical
00749     // Multifile.  Force a flush operation to incorporate it.
00750     flush();
00751 
00752     // That shouldn't change the subfile index or delete the subfile
00753     // pointer.
00754     nassertr(subfile == _subfiles[index], NULL);
00755   }
00756 
00757   // Return an ISubStream object that references into the open
00758   // Multifile istream.
00759   nassertr(subfile->_data_start != (streampos)0, NULL);
00760   istream *stream = 
00761     new ISubStream(_read, subfile->_data_start,
00762                    subfile->_data_start + (streampos)subfile->_data_length); 
00763   
00764   if ((subfile->_flags & SF_compressed) != 0) {
00765 #ifndef HAVE_ZLIB
00766     express_cat.error()
00767       << "zlib not compiled in; cannot read compressed multifiles.\n";
00768     delete stream;
00769     return NULL;
00770 #else  // HAVE_ZLIB
00771     // Oops, the subfile is compressed.  So actually, return an
00772     // IDecompressStream that wraps around the ISubStream.
00773     IDecompressStream *wrapper = new IDecompressStream(stream, true);
00774     stream = wrapper;
00775 #endif  // HAVE_ZLIB
00776   }
00777 
00778   if (stream->fail()) {
00779     // Hmm, some inexplicable problem.
00780     delete stream;
00781     return NULL;
00782   }
00783 
00784   return stream;
00785 }
00786 
00787 ////////////////////////////////////////////////////////////////////
00788 //     Function: Multifile::extract_subfile
00789 //       Access: Published
00790 //  Description: Extracts the nth subfile into a file with the given
00791 //               name.
00792 ////////////////////////////////////////////////////////////////////
00793 bool Multifile::
00794 extract_subfile(int index, const Filename &filename) {
00795   nassertr(is_read_valid(), false);
00796   nassertr(index >= 0 && index < (int)_subfiles.size(), false);
00797   
00798   Filename fname = filename;
00799   fname.set_binary();
00800   fname.make_dir();
00801   ofstream out;
00802   if (!fname.open_write(out, true)) {
00803     express_cat.info()
00804       << "Unable to write to file " << filename << "\n";
00805     return false;
00806   }
00807 
00808   return extract_subfile_to(index, out);
00809 }
00810 
00811 ////////////////////////////////////////////////////////////////////
00812 //     Function: Multifile::output
00813 //       Access: Published
00814 //  Description: 
00815 ////////////////////////////////////////////////////////////////////
00816 void Multifile::
00817 output(ostream &out) const {
00818   out << "Multifile " << _multifile_name << ", " << get_num_subfiles()
00819       << " subfiles.\n";
00820 }
00821 
00822 ////////////////////////////////////////////////////////////////////
00823 //     Function: Multifile::ls
00824 //       Access: Published
00825 //  Description: Shows a list of all subfiles within the Multifile.
00826 ////////////////////////////////////////////////////////////////////
00827 void Multifile::
00828 ls(ostream &out) const {
00829   int num_subfiles = get_num_subfiles();
00830   for (int i = 0; i < num_subfiles; i++) {
00831     string subfile_name = get_subfile_name(i);
00832     out << subfile_name << "\n";
00833   }
00834 }
00835 
00836 ////////////////////////////////////////////////////////////////////
00837 //     Function: Multifile::read_subfile
00838 //       Access: Public
00839 //  Description: Fills a string with the entire contents of
00840 //               the indicated subfile.
00841 ////////////////////////////////////////////////////////////////////
00842 bool Multifile::
00843 read_subfile(int index, string &result) {
00844   nassertr(is_read_valid(), false);
00845   nassertr(index >= 0 && index < (int)_subfiles.size(), false);
00846   result = string();
00847 
00848   istream *in = open_read_subfile(index);
00849   if (in == (istream *)NULL) {
00850     return false;
00851   }
00852 
00853   int byte = in->get();
00854   while (!in->eof() && !in->fail()) {
00855     result += (char)byte;
00856     byte = in->get();
00857   }
00858   bool failed = in->fail();
00859   delete in;
00860   nassertr(!failed, false);
00861   return true;
00862 }
00863 
00864 ////////////////////////////////////////////////////////////////////
00865 //     Function: Multifile::open_read
00866 //       Access: Public
00867 //  Description: Opens an anonymous Multifile for reading using an
00868 //               istream.  There must be seek functionality via
00869 //               seekg() and tellg() on the istream.
00870 //
00871 //               This version of open_read() does not close the
00872 //               istream when Multifile.close() is called.
00873 ////////////////////////////////////////////////////////////////////
00874 bool Multifile::
00875 open_read(istream *multifile_stream) {
00876   close();
00877   _read = multifile_stream;
00878   return read_index();
00879 }
00880 
00881 ////////////////////////////////////////////////////////////////////
00882 //     Function: Multifile::open_write
00883 //       Access: Public
00884 //  Description: Opens an anonymous Multifile for writing using an
00885 //               ostream.  There must be seek functionality via
00886 //               seekp() and tellp() on the pstream.
00887 //
00888 //               This version of open_write() does not close the
00889 //               ostream when Multifile.close() is called.
00890 ////////////////////////////////////////////////////////////////////
00891 bool Multifile::
00892 open_write(ostream *multifile_stream) {
00893   close();
00894   _write = multifile_stream;
00895   return true;
00896 }
00897 
00898 ////////////////////////////////////////////////////////////////////
00899 //     Function: Multifile::open_read_write
00900 //       Access: Public
00901 //  Description: Opens an anonymous Multifile for reading and writing
00902 //               using an iostream.  There must be seek functionality
00903 //               via seekg()/seekp() and tellg()/tellp() on the
00904 //               iostream.
00905 //
00906 //               This version of open_read_write() does not close the
00907 //               iostream when Multifile.close() is called.
00908 ////////////////////////////////////////////////////////////////////
00909 bool Multifile::
00910 open_read_write(iostream *multifile_stream) {
00911   close();
00912   _read = multifile_stream;
00913   _write = multifile_stream;
00914 
00915   // Check whether the read stream is empty.
00916   _read->seekg(0, ios::end);
00917   if (_read->tellg() == (streampos)0) {
00918     // The read stream is empty, which is always valid.
00919     return true;
00920   }
00921 
00922   // The read stream is not empty, so we'd better have a valid
00923   // Multifile.
00924   _read->seekg(0);
00925   return read_index();
00926 }
00927 
00928 ////////////////////////////////////////////////////////////////////
00929 //     Function: Multifile::add_subfile
00930 //       Access: Public
00931 //  Description: Adds a file on disk as a subfile to the Multifile.
00932 //               The indicated istream will be read and its contents
00933 //               added to the Multifile at the next call to flush().
00934 //
00935 //               Returns the subfile name on success (it might have
00936 //               been modified slightly), or empty string on failure.
00937 ////////////////////////////////////////////////////////////////////
00938 string Multifile::
00939 add_subfile(const string &subfile_name, istream *subfile_data,
00940             int compression_level) {
00941   nassertr(is_write_valid(), string());
00942 
00943   Subfile *subfile = new Subfile;
00944   subfile->_source = subfile_data;
00945 
00946   return add_new_subfile(subfile_name, subfile, compression_level);
00947 }
00948 
00949 ////////////////////////////////////////////////////////////////////
00950 //     Function: Multifile::extract_subfile_to
00951 //       Access: Public
00952 //  Description: Extracts the nth subfile to the indicated ostream.
00953 ////////////////////////////////////////////////////////////////////
00954 bool Multifile::
00955 extract_subfile_to(int index, ostream &out) {
00956   nassertr(is_read_valid(), false);
00957   nassertr(index >= 0 && index < (int)_subfiles.size(), false);
00958 
00959   istream *in = open_read_subfile(index);
00960   if (in == (istream *)NULL) {
00961     return false;
00962   }
00963 
00964   int byte = in->get();
00965   while (!in->fail() && !in->eof()) {
00966     out.put(byte);
00967     byte = in->get();
00968   }
00969 
00970   bool failed = (in->fail() && !in->eof());
00971   delete in;
00972   nassertr(!failed, false);
00973 
00974   return (!out.fail());
00975 }
00976 
00977 ////////////////////////////////////////////////////////////////////
00978 //     Function: Multifile::pad_to_streampos
00979 //       Access: Private
00980 //  Description: Assumes the _write pointer is at the indicated fpos,
00981 //               rounds the fpos up to the next legitimate address
00982 //               (using normalize_streampos()), and writes enough
00983 //               zeros to the stream to fill the gap.  Returns the new
00984 //               fpos.
00985 ////////////////////////////////////////////////////////////////////
00986 streampos Multifile::
00987 pad_to_streampos(streampos fpos) {
00988   nassertr(_write != (ostream *)NULL, fpos);
00989   nassertr(_write->tellp() == fpos, fpos);
00990   streampos new_fpos = normalize_streampos(fpos);
00991   while (fpos < new_fpos) {
00992     _write->put(0);
00993     fpos += 1; // VC++ doesn't define streampos++ (!)
00994   }
00995   return fpos;
00996 }
00997 
00998 ////////////////////////////////////////////////////////////////////
00999 //     Function: Multifile::add_new_subfile
01000 //       Access: Private
01001 //  Description: Adds a newly-allocated Subfile pointer to the
01002 //               Multifile.
01003 ////////////////////////////////////////////////////////////////////
01004 string Multifile::
01005 add_new_subfile(const string &subfile_name, Subfile *subfile,
01006                 int compression_level) {
01007   if (compression_level != 0) {
01008 #ifndef HAVE_ZLIB
01009     express_cat.warning()
01010       << "zlib not compiled in; cannot generated compressed multifiles.\n";
01011     compression_level = 0;
01012 #else  // HAVE_ZLIB
01013     subfile->_flags |= SF_compressed;
01014     subfile->_compression_level = compression_level;
01015 #endif  // HAVE_ZLIB
01016   }
01017 
01018   if (_next_index != (streampos)0) {
01019     // If we're adding a Subfile to an already-existing Multifile, we
01020     // will eventually need to repack the file.
01021     _needs_repack = true;
01022   }
01023 
01024   // Normalize the Subfile name: eliminate ./, leading slash, etc.
01025   Filename name = subfile_name;
01026   name.standardize();
01027   if (name.empty() || name == "/") {
01028     // Invalid empty name.
01029     return string();
01030   }
01031 
01032   if (name[0] == '/') {
01033     subfile->_name = name.get_fullpath().substr(1);
01034   } else {
01035     subfile->_name = name;
01036   }
01037 
01038   pair<Subfiles::iterator, bool> insert_result = _subfiles.insert(subfile);
01039   if (!insert_result.second) {
01040     // Hmm, unable to insert.  There must already be a subfile by that
01041     // name.  Remove the old one.
01042     Subfile *old_subfile = (*insert_result.first);
01043     old_subfile->_flags |= SF_deleted;
01044     _removed_subfiles.push_back(old_subfile);
01045     (*insert_result.first) = subfile;
01046   }
01047 
01048   _new_subfiles.push_back(subfile);
01049   return subfile->_name;
01050 }
01051 
01052 ////////////////////////////////////////////////////////////////////
01053 //     Function: Multifile::clear_subfiles
01054 //       Access: Private
01055 //  Description: Removes the set of subfiles from the tables and frees
01056 //               their associated memory.
01057 ////////////////////////////////////////////////////////////////////
01058 void Multifile::
01059 clear_subfiles() {
01060   PendingSubfiles::iterator pi;
01061   for (pi = _removed_subfiles.begin(); pi != _removed_subfiles.end(); ++pi) {
01062     Subfile *subfile = (*pi);
01063     subfile->rewrite_index_flags(*_write);
01064     delete subfile;
01065   }
01066   _removed_subfiles.clear();
01067 
01068   // We don't have to delete the ones in _new_subfiles, because these
01069   // also appear in _subfiles.
01070   _new_subfiles.clear();
01071 
01072   Subfiles::iterator fi;
01073   for (fi = _subfiles.begin(); fi != _subfiles.end(); ++fi) {
01074     Subfile *subfile = (*fi);
01075     delete subfile;
01076   }
01077   _subfiles.clear();
01078 }
01079 
01080 ////////////////////////////////////////////////////////////////////
01081 //     Function: Multifile::read_index
01082 //       Access: Private
01083 //  Description: Reads the Multifile header and index.  Returns true
01084 //               if successful, false if the Multifile is not valid.
01085 ////////////////////////////////////////////////////////////////////
01086 bool Multifile::
01087 read_index() {
01088   nassertr(_read != (istream *)NULL, false);
01089 
01090   char this_header[_header_size];
01091   _read->read(this_header, _header_size);
01092   if (_read->fail() || _read->gcount() != (unsigned)_header_size) {
01093     express_cat.info()
01094       << "Unable to read Multifile header " << _multifile_name << ".\n";
01095     close();
01096     return false;
01097   }
01098   if (memcmp(this_header, _header, _header_size) != 0) {
01099     express_cat.info()
01100       << _multifile_name << " is not a Multifile.\n";
01101     return false;
01102   }
01103 
01104   // Now get the version numbers out.
01105   StreamReader reader(_read, false);
01106   _file_major_ver = reader.get_int16();
01107   _file_minor_ver = reader.get_int16();
01108   _scale_factor = reader.get_uint32();
01109   _new_scale_factor = _scale_factor;
01110 
01111   if (_read->eof() || _read->fail()) {
01112     express_cat.info()
01113       << _multifile_name << " header is truncated.\n";
01114     return false;
01115   }
01116 
01117   if (_file_major_ver != _current_major_ver ||
01118       (_file_major_ver == _current_major_ver && 
01119        _file_minor_ver > _current_minor_ver)) {
01120     express_cat.info()
01121       << _multifile_name << " has version " << _file_major_ver << "."
01122       << _file_minor_ver << ", expecting version " 
01123       << _current_major_ver << "." << _current_minor_ver << ".\n";
01124     return false;
01125   }
01126 
01127 
01128   // Now read the index out.
01129   _next_index = _read->tellg();
01130   _next_index = normalize_streampos(_next_index);  
01131   _read->seekg(_next_index);
01132   _last_index = 0;
01133   streampos index_forward;
01134 
01135   Subfile *subfile = new Subfile;
01136   index_forward = subfile->read_index(*_read, _next_index, this);
01137   while (index_forward != (streampos)0) {
01138     _last_index = _next_index;
01139     if (subfile->is_deleted()) {
01140       // Ignore deleted Subfiles in the index.
01141       _needs_repack = true;
01142       delete subfile;
01143     } else {
01144       _subfiles.push_back(subfile);
01145     }
01146     if (index_forward != normalize_streampos(_read->tellg())) {
01147       // If the index entries don't follow exactly sequentially, the
01148       // file ought to be repacked.
01149       _needs_repack = true;
01150     }
01151     _read->seekg(index_forward);
01152     _next_index = index_forward;
01153     subfile = new Subfile;
01154     index_forward = subfile->read_index(*_read, _next_index, this);
01155   }
01156   if (subfile->is_index_invalid()) {
01157     express_cat.info()
01158       << "Error reading index for " << _multifile_name << ".\n";
01159     close();
01160     delete subfile;
01161     return false;
01162   }
01163 
01164   size_t before_size = _subfiles.size();
01165   _subfiles.sort();
01166   size_t after_size = _subfiles.size();
01167 
01168   // If these don't match, the same filename appeared twice in the
01169   // index, which shouldn't be possible.
01170   nassertr(before_size == after_size, true);
01171 
01172   delete subfile;
01173   return true;
01174 }  
01175 
01176 ////////////////////////////////////////////////////////////////////
01177 //     Function: Multifile::write_header
01178 //       Access: Private
01179 //  Description: Writes just the header part of the Multifile, not the
01180 //               index.
01181 ////////////////////////////////////////////////////////////////////
01182 bool Multifile::
01183 write_header() {
01184   nassertr(_write != (ostream *)NULL, false);
01185   nassertr(_write->tellp() == (streampos)0, false);
01186   _write->write(_header, _header_size);
01187   StreamWriter writer(_write);
01188   writer.add_int16(_current_major_ver);
01189   writer.add_int16(_current_minor_ver);
01190   writer.add_uint32(_scale_factor);
01191 
01192   _next_index = _write->tellp();
01193   _next_index = pad_to_streampos(_next_index);
01194   _last_index = 0;
01195 
01196   if (_write->fail()) {
01197     express_cat.info()
01198       << "Unable to write header for " << _multifile_name << ".\n";
01199     close();
01200     return false;
01201   }
01202 
01203   return true;
01204 }
01205 
01206 ////////////////////////////////////////////////////////////////////
01207 //     Function: Multifile::Subfile::read_index
01208 //       Access: Public
01209 //  Description: Reads the index record for the Subfile from the
01210 //               indicated istream.  Assumes the istream has already
01211 //               been positioned to the indicated stream position,
01212 //               fpos, the start of the index record.  Returns the
01213 //               position within the file of the next index record.
01214 ////////////////////////////////////////////////////////////////////
01215 streampos Multifile::Subfile::
01216 read_index(istream &read, streampos fpos, Multifile *multifile) {
01217   nassertr(read.tellg() == fpos, fpos);
01218 
01219   // First, get the next stream position.  We do this separately,
01220   // because if it is zero, we don't get anything else.
01221   StreamReader reader(read);
01222 
01223   streampos next_index = multifile->word_to_streampos(reader.get_uint32());
01224   if (read.eof() || read.fail()) {
01225     _flags |= SF_index_invalid;
01226     return 0;
01227   }
01228 
01229   if (next_index == (streampos)0) {
01230     return 0;
01231   }
01232 
01233   // Now get the rest of the index.
01234 
01235   _index_start = fpos;
01236   
01237   _data_start = multifile->word_to_streampos(reader.get_uint32());
01238   _data_length = reader.get_uint32();
01239   _flags = reader.get_uint16();
01240   if ((_flags & SF_compressed) != 0) {
01241     _uncompressed_length = reader.get_uint32();
01242   } else {
01243     _uncompressed_length = _data_length;
01244   }
01245   size_t name_length = reader.get_uint16();
01246   if (read.eof() || read.fail()) {
01247     _flags |= SF_index_invalid;
01248     return 0;
01249   }
01250 
01251   // And finally, get the rest of the name.
01252   char *name_buffer = new char[name_length];
01253   nassertr(name_buffer != (char *)NULL, next_index);
01254   for (size_t ni = 0; ni < name_length; ni++) {
01255     name_buffer[ni] = read.get() ^ 0xff;
01256   }
01257   _name = string(name_buffer, name_length);
01258   delete[] name_buffer;
01259 
01260   if (read.eof() || read.fail()) {
01261     _flags |= SF_index_invalid;
01262     return 0;
01263   }
01264 
01265   return next_index;
01266 }
01267 
01268 ////////////////////////////////////////////////////////////////////
01269 //     Function: Multifile::Subfile::write_index
01270 //       Access: Public
01271 //  Description: Writes the index record for the Subfile to the
01272 //               indicated ostream.  Assumes the istream has already
01273 //               been positioned to the indicated stream position,
01274 //               fpos, the start of the index record, and that this is
01275 //               the effective end of the file.  Returns the position
01276 //               within the file of the next index record.
01277 //
01278 //               The _index_start member is updated by this operation.
01279 ////////////////////////////////////////////////////////////////////
01280 streampos Multifile::Subfile::
01281 write_index(ostream &write, streampos fpos, Multifile *multifile) {
01282   nassertr(write.tellp() == fpos, fpos);
01283 
01284   _index_start = fpos;
01285 
01286   // This will be the contents of this particular index record.  We
01287   // build it up first since it will be variable length.
01288   Datagram dg;
01289   dg.add_uint32(multifile->streampos_to_word(_data_start));
01290   dg.add_uint32(_data_length);
01291   dg.add_uint16(_flags);
01292   if ((_flags & SF_compressed) != 0) {
01293     dg.add_uint32(_uncompressed_length);
01294   }
01295   dg.add_uint16(_name.length());
01296 
01297   // For no real good reason, we'll invert all the bits in the name.
01298   // The only reason we do this is to make it inconvenient for a
01299   // casual browser of the Multifile to discover the names of the
01300   // files stored within it.  Naturally, this isn't real obfuscation
01301   // or security.
01302   string::iterator ni;
01303   for (ni = _name.begin(); ni != _name.end(); ++ni) {
01304     dg.add_int8((*ni) ^ 0xff);
01305   }
01306 
01307   size_t this_index_size = 4 + dg.get_length();
01308 
01309   // Plus, we will write out the next index address first.
01310   streampos next_index = fpos + (streampos)this_index_size;
01311   Datagram idg;
01312   idg.add_uint32(multifile->streampos_to_word(next_index));
01313 
01314   write.write((const char *)idg.get_data(), idg.get_length());
01315   write.write((const char *)dg.get_data(), dg.get_length());
01316 
01317   return next_index;
01318 }
01319 
01320 ////////////////////////////////////////////////////////////////////
01321 //     Function: Multifile::Subfile::write_index
01322 //       Access: Public
01323 //  Description: Writes the data record for the Subfile to the
01324 //               indicated ostream: the actual contents of the
01325 //               Subfile.  Assumes the istream has already been
01326 //               positioned to the indicated stream position, fpos,
01327 //               the start of the data record, and that this is the
01328 //               effective end of the file.  Returns the position
01329 //               within the file of the next data record.
01330 //
01331 //               The _data_start, _data_length, and
01332 //               _uncompressed_length members are updated by this
01333 //               operation.
01334 //
01335 //               If the "read" pointer is non-NULL, it is the readable
01336 //               istream of a Multifile in which the Subfile might
01337 //               already be packed.  This is used for reading the
01338 //               contents of the Subfile during a repack() operation.
01339 ////////////////////////////////////////////////////////////////////
01340 streampos Multifile::Subfile::
01341 write_data(ostream &write, istream *read, streampos fpos) {
01342   nassertr(write.tellp() == fpos, fpos);
01343 
01344   istream *source = _source;
01345   ifstream source_file;
01346   if (source == (istream *)NULL && !_source_filename.empty()) {
01347     // If we have a filename, open it up and read that.
01348     if (!_source_filename.open_read(source_file)) {
01349       // Unable to open the source file.
01350       express_cat.info()
01351         << "Unable to read " << _source_filename << ".\n";
01352       _flags |= SF_data_invalid;
01353       _data_length = 0;
01354       _uncompressed_length = 0;
01355     } else {
01356       source = &source_file;
01357     }
01358   }
01359 
01360   if (source == (istream *)NULL) {
01361     // We don't have any source data.  Perhaps we're reading from an
01362     // already-packed Subfile (e.g. during repack()).
01363     if (read == (istream *)NULL) {
01364       // No, we're just screwed.
01365       express_cat.info()
01366         << "No source for subfile " << _name << ".\n";
01367       _flags |= SF_data_invalid;
01368     } else {
01369       // Read the data from the original Multifile.
01370       read->seekg(_data_start);
01371       for (size_t p = 0; p < _data_length; p++) {
01372         int byte = read->get();
01373         if (read->eof() || read->fail()) {
01374           // Unexpected EOF or other failure on the source file.
01375           express_cat.info()
01376             << "Unexpected EOF for subfile " << _name << ".\n";
01377           _flags |= SF_data_invalid;
01378           break;
01379         }
01380         write.put(byte);
01381       }
01382     }
01383   } else {
01384     // We do have source data.  Copy it in, and also measure its
01385     // length.
01386 #ifndef HAVE_ZLIB
01387     // Without ZLIB, we can't support compression.  The flag had
01388     // better not be set.
01389     nassertr((_flags & SF_compressed) == 0, fpos);
01390 #else  // HAVE_ZLIB
01391     if ((_flags & SF_compressed) != 0) {
01392       // Write it compressed.
01393       streampos write_start = write.tellp();
01394       _uncompressed_length = 0;
01395       OCompressStream zstream(&write, false, _compression_level);
01396       int byte = source->get();
01397       while (!source->eof() && !source->fail()) {
01398         _uncompressed_length++;
01399         zstream.put(byte);
01400         byte = source->get();
01401       }
01402       zstream.close();
01403       streampos write_end = write.tellp();
01404       _data_length = (size_t)(write_end - write_start);
01405     } else
01406 #endif  // HAVE_ZLIB
01407       {
01408         // Write it uncompressed.
01409         _uncompressed_length = 0;
01410         int byte = source->get();
01411         while (!source->eof() && !source->fail()) {
01412           _uncompressed_length++;
01413           write.put(byte);
01414           byte = source->get();
01415         }
01416         _data_length = _uncompressed_length;
01417       }
01418   }
01419 
01420   // We can't set _data_start until down here, after we have read the
01421   // Subfile.  (In case we are running during repack()).
01422   _data_start = fpos;
01423 
01424   _source = (istream *)NULL;
01425   _source_filename = Filename();
01426   source_file.close();
01427 
01428   return fpos + (streampos)_data_length;
01429 }
01430 
01431 ////////////////////////////////////////////////////////////////////
01432 //     Function: Multifile::Subfile::rewrite_index_data_start
01433 //       Access: Public
01434 //  Description: Seeks within the indicate fstream back to the index
01435 //               record and rewrites just the _data_start and
01436 //               _data_length part of the index record.
01437 ////////////////////////////////////////////////////////////////////
01438 void Multifile::Subfile::
01439 rewrite_index_data_start(ostream &write, Multifile *multifile) {
01440   nassertv(_index_start != (streampos)0);
01441 
01442   static const size_t data_start_offset = 4;
01443   size_t data_start_pos = _index_start + (streampos)data_start_offset;
01444   write.seekp(data_start_pos);
01445   nassertv(!write.fail());
01446 
01447   StreamWriter writer(write);
01448   writer.add_uint32(multifile->streampos_to_word(_data_start));
01449   writer.add_uint32(_data_length);
01450   writer.add_uint16(_flags);
01451   if ((_flags & SF_compressed) != 0) {
01452     writer.add_uint32(_uncompressed_length);
01453   }
01454 }
01455 
01456 ////////////////////////////////////////////////////////////////////
01457 //     Function: Multifile::Subfile::rewrite_index_flags
01458 //       Access: Public
01459 //  Description: Seeks within the indicate fstream back to the index
01460 //               record and rewrites just the _flags part of the
01461 //               index record.
01462 ////////////////////////////////////////////////////////////////////
01463 void Multifile::Subfile::
01464 rewrite_index_flags(ostream &write) {
01465   // If the subfile has never even been recorded to disk, we don't
01466   // need to do anything at all in this function.
01467   if (_index_start != (streampos)0) {
01468     static const size_t flags_offset = 4 + 4 + 4;
01469     size_t flags_pos = _index_start + (streampos)flags_offset;
01470     write.seekp(flags_pos);
01471     nassertv(!write.fail());
01472     
01473     StreamWriter writer(write);
01474     writer.add_uint16(_flags);
01475   }
01476 }

Generated on Fri May 2 00:38:26 2003 for Panda by doxygen1.3