Panda3D

downloadDb.cxx

00001 // Filename: downloadDb.cxx
00002 // Created by:  shochet (08Sep00)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "config_downloader.h"
00016 #include "downloadDb.h"
00017 #include "streamReader.h"
00018 #include "streamWriter.h"
00019 #include "ramfile.h"
00020 #include "virtualFileSystem.h"
00021 
00022 #include <algorithm>
00023 
00024 ////////////////////////////////////////////////////////////////////
00025 // Defines
00026 ////////////////////////////////////////////////////////////////////
00027 
00028 // Written at the top of the file so we know this is a downloadDb
00029 PN_uint32 DownloadDb::_magic_number = 0xfeedfeed;
00030 
00031 // Written at the top of the file to signify we are not done
00032 // writing to the file yet. If you load a db with this magic
00033 // number that means the previous time it got written out was
00034 // probably interrupted in the middle of the write.
00035 PN_uint32 DownloadDb::_bogus_magic_number = 0x11111111;
00036 
00037 
00038 static string
00039 back_to_front_slash(const string &str) {
00040   string result = str;
00041   string::iterator si;
00042   for (si = result.begin(); si != result.end(); ++si) {
00043     if ((*si) == '\\') {
00044       (*si) = '/';
00045     }
00046   }
00047 
00048   return result;
00049 }
00050 
00051 
00052 ////////////////////////////////////////////////////////////////////
00053 //     Function: DownloadDb::Constructor
00054 //       Access: Public
00055 //  Description: Create a download db with these client and server dbs
00056 ////////////////////////////////////////////////////////////////////
00057 DownloadDb::
00058 DownloadDb(Ramfile &server_file, Filename &client_file) {
00059   if (downloader_cat.is_debug())
00060     downloader_cat.debug()
00061       << "DownloadDb constructor called" << endl;
00062   _client_db = read_db(client_file, 0);
00063   _client_db._filename = client_file;
00064   _server_db = read_db(server_file, 1);
00065 }
00066 
00067 ////////////////////////////////////////////////////////////////////
00068 //     Function: DownloadDb::Constructor
00069 //       Access: Public
00070 //  Description: Create a download db with these client and server dbs
00071 ////////////////////////////////////////////////////////////////////
00072 DownloadDb::
00073 DownloadDb(Filename &server_file, Filename &client_file) {
00074   if (downloader_cat.is_debug())
00075     downloader_cat.debug()
00076       << "DownloadDb constructor called" << endl;
00077   _client_db = read_db(client_file, 0);
00078   _client_db._filename = client_file;
00079   _server_db = read_db(server_file, 1);
00080   _server_db._filename = server_file;
00081 }
00082 
00083 ////////////////////////////////////////////////////////////////////
00084 //     Function: DownloadDb::Constructor
00085 //       Access: Public
00086 //  Description: Primarily used for testing.
00087 ////////////////////////////////////////////////////////////////////
00088 DownloadDb::
00089 DownloadDb() {
00090   _client_db = Db();
00091   _server_db = Db();
00092 }
00093 
00094 ////////////////////////////////////////////////////////////////////
00095 //     Function: DownloadDb::Destructor
00096 //       Access: Public
00097 //  Description:
00098 ////////////////////////////////////////////////////////////////////
00099 DownloadDb::
00100 ~DownloadDb() {
00101   if (downloader_cat.is_debug())
00102     downloader_cat.debug()
00103       << "DownloadDb destructor called" << endl;    
00104 }
00105 
00106 
00107 ////////////////////////////////////////////////////////////////////
00108 //     Function: DownloadDb::output
00109 //       Access: Public
00110 //  Description:
00111 ////////////////////////////////////////////////////////////////////
00112 void DownloadDb::
00113 output(ostream &out) const {
00114   out << "[" << _server_db._filename << " " << _client_db._filename << "]";
00115 }
00116 
00117 ////////////////////////////////////////////////////////////////////
00118 //     Function: DownloadDb::write
00119 //       Access: Public
00120 //  Description:
00121 ////////////////////////////////////////////////////////////////////
00122 void DownloadDb::
00123 write(ostream &out) const {
00124   out << "DownloadDb" << endl;
00125   out << "============================================================" << endl;
00126   out << "  Client DB file: " << _client_db._filename << endl;
00127   out << "============================================================" << endl;
00128   _client_db.write(out);
00129   out << endl;
00130   out << "============================================================" << endl;
00131   out << "  Server DB file: " << _server_db._filename << endl;
00132   out << "============================================================" << endl;
00133   _server_db.write(out);
00134   write_version_map(out);
00135   out << endl;
00136 }
00137 
00138 
00139 ////////////////////////////////////////////////////////////////////
00140 //     Function: DownloadDb::
00141 //       Access: Public
00142 //  Description:
00143 ////////////////////////////////////////////////////////////////////
00144 bool DownloadDb::
00145 write_client_db(Filename &file) {
00146   return write_db(file, _client_db, 0);
00147 }
00148 
00149 
00150 ////////////////////////////////////////////////////////////////////
00151 //     Function: DownloadDb::
00152 //       Access: Public
00153 //  Description:
00154 ////////////////////////////////////////////////////////////////////
00155 bool DownloadDb::
00156 write_server_db(Filename &file) {
00157   return write_db(file, _server_db, 1);
00158 }
00159 
00160 ////////////////////////////////////////////////////////////////////
00161 //     Function: DownloadDb::
00162 //       Access: Public
00163 //  Description:
00164 ////////////////////////////////////////////////////////////////////
00165 bool DownloadDb::
00166 client_multifile_exists(string mfname) const {
00167   return (_client_db.multifile_exists(mfname));
00168 }
00169 
00170 ////////////////////////////////////////////////////////////////////
00171 //     Function: DownloadDb::
00172 //       Access: Public
00173 //  Description: A multifile is complete when it is completely
00174 //               downloaded. Note: it may already be decompressed
00175 //               or extracted and it is still complete
00176 ////////////////////////////////////////////////////////////////////
00177 bool DownloadDb::
00178 client_multifile_complete(string mfname) const {
00179   int client_status = _client_db.get_multifile_record_named(mfname)->_status;
00180   return (client_status >= Status_complete);
00181 }
00182 
00183 ////////////////////////////////////////////////////////////////////
00184 //     Function: DownloadDb::
00185 //       Access: Public
00186 //  Description:
00187 ////////////////////////////////////////////////////////////////////
00188 bool DownloadDb::
00189 client_multifile_decompressed(string mfname) const {
00190   int client_status = _client_db.get_multifile_record_named(mfname)->_status;
00191   return (client_status >= Status_decompressed);
00192 }
00193 
00194 ////////////////////////////////////////////////////////////////////
00195 //     Function: DownloadDb::
00196 //       Access: Public
00197 //  Description:
00198 ////////////////////////////////////////////////////////////////////
00199 bool DownloadDb::
00200 client_multifile_extracted(string mfname) const {
00201   int client_status = _client_db.get_multifile_record_named(mfname)->_status;
00202   return (client_status >= Status_extracted);
00203 }
00204 
00205 
00206 ////////////////////////////////////////////////////////////////////
00207 //     Function: DownloadDb::
00208 //       Access: Public
00209 //  Description: Return the hash value of the file we are working on
00210 ////////////////////////////////////////////////////////////////////
00211 HashVal DownloadDb::
00212 get_client_multifile_hash(string mfname) const {
00213   return _client_db.get_multifile_record_named(mfname)->_hash;
00214 }
00215 
00216 
00217 ////////////////////////////////////////////////////////////////////
00218 //     Function: DownloadDb::
00219 //       Access: Public
00220 //  Description: Return the hash value of the server file
00221 ////////////////////////////////////////////////////////////////////
00222 HashVal DownloadDb::
00223 get_server_multifile_hash(string mfname) const {
00224   return _server_db.get_multifile_record_named(mfname)->_hash;
00225 }
00226 
00227 
00228 ////////////////////////////////////////////////////////////////////
00229 //     Function: DownloadDb::
00230 //       Access: Public
00231 //  Description: Set the hash value of file we are working on
00232 ////////////////////////////////////////////////////////////////////
00233 void DownloadDb::
00234 set_client_multifile_hash(string mfname, HashVal val) {
00235   _client_db.get_multifile_record_named(mfname)->_hash = val;
00236   write_client_db(_client_db._filename);
00237 }
00238 
00239 
00240 ////////////////////////////////////////////////////////////////////
00241 //     Function: DownloadDb::
00242 //       Access: Public
00243 //  Description: Set the hash value of file we are working on
00244 ////////////////////////////////////////////////////////////////////
00245 void DownloadDb::
00246 set_server_multifile_hash(string mfname, HashVal val) {
00247   _server_db.get_multifile_record_named(mfname)->_hash = val;
00248 }
00249 
00250 // Operations on multifiles
00251 
00252 ////////////////////////////////////////////////////////////////////
00253 //     Function: DownloadDb::
00254 //       Access: Public
00255 //  Description:
00256 ////////////////////////////////////////////////////////////////////
00257 void DownloadDb::
00258 delete_client_multifile(string mfname) {
00259 }
00260 
00261 ////////////////////////////////////////////////////////////////////
00262 //     Function: DownloadDb::
00263 //       Access: Public
00264 //  Description:
00265 ////////////////////////////////////////////////////////////////////
00266 void DownloadDb::
00267 add_client_multifile(string server_mfname) {
00268   PT(MultifileRecord) server_mfr = _server_db.get_multifile_record_named(server_mfname);
00269   PT(MultifileRecord) client_mfr = new MultifileRecord;
00270   client_mfr->_name = server_mfr->_name;
00271   client_mfr->_phase = server_mfr->_phase;
00272   _client_db.add_multifile_record(client_mfr);
00273 }
00274 
00275 
00276 ////////////////////////////////////////////////////////////////////
00277 //     Function: DownloadDb::
00278 //       Access: Public
00279 //  Description:
00280 ////////////////////////////////////////////////////////////////////
00281 void DownloadDb::
00282 expand_client_multifile(string mfname) {
00283 }
00284 
00285 
00286 ////////////////////////////////////////////////////////////////////
00287 //     Function: DownloadDb::read_db
00288 //       Access: Published
00289 //  Description:
00290 ////////////////////////////////////////////////////////////////////
00291 DownloadDb::Db DownloadDb::
00292 read_db(Filename &file, bool want_server_info) {
00293   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00294   Db db;
00295 
00296   // Open the multifile for reading
00297   file.set_binary();
00298   istream *read_stream = vfs->open_read_file(file, true);
00299 
00300   if (read_stream == (istream *)NULL) {
00301     downloader_cat.error()
00302       << "failed to open input file: "
00303       << file << endl;
00304     return db;
00305   }
00306 
00307   StreamReader sr(*read_stream);
00308   if (!db.read(sr, want_server_info)) {
00309     downloader_cat.error()
00310       << "read failed: "
00311       << file << endl;
00312     vfs->close_read_file(read_stream);
00313     return db;
00314   }
00315 
00316   if (want_server_info) {
00317     if (!read_version_map(sr)) {
00318       downloader_cat.error()
00319         << "read_version_map() failed: "
00320         << file << endl;
00321     }
00322   }
00323 
00324   vfs->close_read_file(read_stream);
00325 
00326   return db;
00327 }
00328 
00329 ////////////////////////////////////////////////////////////////////
00330 //     Function: DownloadDb::read_db
00331 //       Access: Published
00332 //  Description:
00333 ////////////////////////////////////////////////////////////////////
00334 DownloadDb::Db DownloadDb::
00335 read_db(Ramfile &file, bool want_server_info) {
00336   // Open the multifile for reading
00337   istringstream read_stream(file._data);
00338   Db db;
00339 
00340   StreamReader sr(read_stream);
00341 
00342   if (!db.read(sr, want_server_info)) {
00343     downloader_cat.error()
00344       << "read failed" << endl;
00345     return db;
00346   }
00347   if (want_server_info) {
00348     if (!read_version_map(sr)) {
00349       downloader_cat.error()
00350         << "read_version_map() failed" << endl;
00351     }
00352   }
00353 
00354   return db;
00355 }
00356 
00357 
00358 ////////////////////////////////////////////////////////////////////
00359 //     Function: DownloadDb::write_db
00360 //       Access: Published
00361 //  Description:
00362 ////////////////////////////////////////////////////////////////////
00363 bool DownloadDb::
00364 write_db(Filename &file, Db db, bool want_server_info) {
00365   pofstream write_stream;
00366   file.set_binary();
00367   if (!file.open_write(write_stream)) {
00368     downloader_cat.error()
00369       << "DownloadDb::write_db() - Failed to open output file: "
00370       << file << endl;
00371     return false;
00372   }
00373 
00374   downloader_cat.spam()
00375     << "Writing to file: " << file << endl;
00376 
00377   StreamWriter sw(write_stream);
00378 
00379   // Initialize the write stream with a bogus header
00380   db.write_bogus_header(sw);
00381   db.write(sw, want_server_info);
00382   if (want_server_info) {
00383     write_version_map(sw);
00384   }
00385   // Now write the real header
00386   db.write_header(write_stream);
00387   write_stream.close();
00388   return true;
00389 }
00390 
00391 
00392 ////////////////////////////////////////////////////////////////////
00393 //     Function: DownloadDb::create_new_server_db
00394 //       Access: Public
00395 //  Description: Used on the server side makefiles to create a
00396 //               new clean server db
00397 ////////////////////////////////////////////////////////////////////
00398 void DownloadDb::
00399 create_new_server_db() {
00400   _server_db = Db();
00401 }
00402 
00403 
00404 ////////////////////////////////////////////////////////////////////
00405 //     Function: DownloadDb::
00406 //       Access: Public
00407 //  Description:
00408 ////////////////////////////////////////////////////////////////////
00409 void DownloadDb::
00410 server_add_multifile(string mfname, Phase phase, int size, int status) {
00411   PT(MultifileRecord) mfr = new MultifileRecord(mfname, phase, size, status);
00412   _server_db.add_multifile_record(mfr);
00413 }
00414 
00415 
00416 ////////////////////////////////////////////////////////////////////
00417 //     Function: DownloadDb::
00418 //       Access: Public
00419 //  Description:
00420 ////////////////////////////////////////////////////////////////////
00421 void DownloadDb::
00422 server_add_file(string mfname, string fname) {
00423   // Make the new file record
00424   PT(FileRecord) fr = new FileRecord(fname);
00425 
00426   // Find the multifile with mfname
00427   pvector< PT(MultifileRecord) >::iterator i = _server_db._mfile_records.begin();
00428   for (; i != _server_db._mfile_records.end(); ++i) {
00429     if (mfname == (*i)->_name) {
00430       (*i)->add_file_record(fr);
00431       return;
00432     }
00433   }
00434 
00435   // Uh-oh, did not find it
00436   downloader_cat.error() << "Could not find record named "
00437                          << mfname << " in database " << endl;
00438   nassertv(false);
00439   return;
00440 }
00441 
00442 
00443 ////////////////////////////////////////////////////////////////////
00444 // Multifile methods
00445 ////////////////////////////////////////////////////////////////////
00446 
00447 
00448 ////////////////////////////////////////////////////////////////////
00449 //     Function: DownloadDb::MultifileRecord::Constructor
00450 //       Access: Public
00451 //  Description:
00452 ////////////////////////////////////////////////////////////////////
00453 DownloadDb::MultifileRecord::
00454 MultifileRecord() {
00455   _name = "";
00456   _phase = 0;
00457   _size = 0;
00458   _status = Status_incomplete;
00459 }
00460 
00461 
00462 ////////////////////////////////////////////////////////////////////
00463 //     Function: DownloadDb::MultifileRecord::Constructor
00464 //       Access: Public
00465 //  Description:
00466 ////////////////////////////////////////////////////////////////////
00467 DownloadDb::MultifileRecord::
00468 MultifileRecord(string name, Phase phase, int size, int status) {
00469   _name = name;
00470   _phase = phase;
00471   _size = size;
00472   _status = status;
00473 }
00474 
00475 
00476 ////////////////////////////////////////////////////////////////////
00477 //     Function: DownloadDb::MultifileRecord::write
00478 //       Access: Public
00479 //  Description:
00480 ////////////////////////////////////////////////////////////////////
00481 void DownloadDb::MultifileRecord::
00482 write(ostream &out) const {
00483   out << "==================================================" << endl;
00484   out << "MultifileRecord: " << _name    << endl
00485       << "    phase: " << _phase   << endl
00486       << "     size: " << _size    << endl
00487       << "   status: " << _status  << endl
00488       << "     hash: " << _hash.as_dec() << endl;
00489   out << "--------------------------------------------------" << endl;
00490   pvector< PT(FileRecord) >::const_iterator i = _file_records.begin();
00491   for(; i != _file_records.end(); ++i) {
00492     (*i)->write(out);
00493   }
00494 }
00495 
00496 
00497 
00498 ////////////////////////////////////////////////////////////////////
00499 //     Function: DownloadDb::MultifileRecord::
00500 //       Access: Public
00501 //  Description:
00502 ////////////////////////////////////////////////////////////////////
00503 int DownloadDb::MultifileRecord::
00504 get_num_files() const {
00505   return _file_records.size();
00506 }
00507 
00508 ////////////////////////////////////////////////////////////////////
00509 //     Function: DownloadDb::MultifileRecord::
00510 //       Access: Public
00511 //  Description:
00512 ////////////////////////////////////////////////////////////////////
00513 string DownloadDb::MultifileRecord::
00514 get_file_name(int index) const {
00515   return _file_records[index]->_name;
00516 }
00517 
00518 
00519 ////////////////////////////////////////////////////////////////////
00520 //     Function: DownloadDb::MultifileRecord::
00521 //       Access: Public
00522 //  Description:
00523 ////////////////////////////////////////////////////////////////////
00524 bool DownloadDb::MultifileRecord::
00525 file_exists(string fname) const {
00526   pvector< PT(FileRecord) >::const_iterator i = _file_records.begin();
00527   for(; i != _file_records.end(); ++i) {
00528     if (fname == (*i)->_name) {
00529       return true;
00530     }
00531   }
00532   return false;
00533 }
00534 
00535 
00536 ////////////////////////////////////////////////////////////////////
00537 //     Function: DownloadDb::MultifileRecord::
00538 //       Access: Public
00539 //  Description:
00540 ////////////////////////////////////////////////////////////////////
00541 PT(DownloadDb::FileRecord) DownloadDb::MultifileRecord::
00542 get_file_record_named(string fname) const {
00543   pvector< PT(FileRecord) >::const_iterator i = _file_records.begin();
00544   for(; i != _file_records.end(); ++i) {
00545     if (fname == (*i)->_name) {
00546       return (*i);
00547     }
00548   }
00549   // Did not find it, just return an empty version
00550   downloader_cat.error() << "Could not find record named "
00551                          << fname << " in multifile " << _name << endl;
00552   PT(FileRecord) foo = new FileRecord;
00553   nassertr(false, foo);
00554   return foo;
00555 }
00556 
00557 
00558 ////////////////////////////////////////////////////////////////////
00559 //     Function: DownloadDb::MultifileRecord::
00560 //       Access: Public
00561 //  Description:
00562 ////////////////////////////////////////////////////////////////////
00563 void DownloadDb::MultifileRecord::
00564 add_file_record(PT(FileRecord) fr) {
00565   _file_records.push_back(fr);
00566 }
00567 
00568 
00569 
00570 ////////////////////////////////////////////////////////////////////
00571 // Db methods
00572 ////////////////////////////////////////////////////////////////////
00573 
00574 
00575 
00576 
00577 ////////////////////////////////////////////////////////////////////
00578 //     Function: DownloadDb::Db::constructor
00579 //       Access: Public
00580 //  Description:
00581 ////////////////////////////////////////////////////////////////////
00582 DownloadDb::Db::
00583 Db() {
00584   // The head is a magic number and the number of multifiles in the db
00585   _header_length = sizeof(_magic_number) + sizeof(PN_int32);
00586 }
00587 
00588 
00589 ////////////////////////////////////////////////////////////////////
00590 //     Function: DownloadDb::Db::output
00591 //       Access: Public
00592 //  Description:
00593 ////////////////////////////////////////////////////////////////////
00594 void DownloadDb::Db::
00595 write(ostream &out) const {
00596   pvector< PT(MultifileRecord) >::const_iterator i = _mfile_records.begin();
00597   for(; i != _mfile_records.end(); ++i) {
00598     (*i)->write(out);
00599   }
00600 }
00601 
00602 
00603 ////////////////////////////////////////////////////////////////////
00604 //     Function: DownloadDb::Db::
00605 //       Access: Public
00606 //  Description:
00607 ////////////////////////////////////////////////////////////////////
00608 int DownloadDb::Db::
00609 get_num_multifiles() const {
00610   return _mfile_records.size();
00611 }
00612 
00613 ////////////////////////////////////////////////////////////////////
00614 //     Function: DownloadDb::Db::
00615 //       Access: Public
00616 //  Description:
00617 ////////////////////////////////////////////////////////////////////
00618 string DownloadDb::Db::
00619 get_multifile_name(int index) const {
00620   return _mfile_records[index]->_name;
00621 }
00622 
00623 ////////////////////////////////////////////////////////////////////
00624 //     Function: DownloadDb::Db::
00625 //       Access: Public
00626 //  Description:
00627 ////////////////////////////////////////////////////////////////////
00628 bool DownloadDb::Db::
00629 multifile_exists(string mfname) const {
00630   pvector< PT(MultifileRecord) >::const_iterator i = _mfile_records.begin();
00631   for(; i != _mfile_records.end(); ++i) {
00632     if (mfname == (*i)->_name) {
00633       return true;
00634     }
00635   }
00636   return false;
00637 }
00638 
00639 ////////////////////////////////////////////////////////////////////
00640 //     Function: DownloadDb::Db::
00641 //       Access: Public
00642 //  Description:
00643 ////////////////////////////////////////////////////////////////////
00644 PT(DownloadDb::MultifileRecord) DownloadDb::Db::
00645 get_multifile_record_named(string mfname) const {
00646   pvector< PT(MultifileRecord) >::const_iterator i = _mfile_records.begin();
00647   for(; i != _mfile_records.end(); ++i) {
00648     if (mfname == (*i)->_name) {
00649       return (*i);
00650     }
00651   }
00652   // Did not find it, just return an empty version
00653   downloader_cat.error() << "Could not find record named "
00654                          << mfname << " in database " << endl;
00655   PT(MultifileRecord) foo = new MultifileRecord;
00656   nassertr(false, foo);
00657   return foo;
00658 }
00659 
00660 ////////////////////////////////////////////////////////////////////
00661 //     Function: DownloadDb::Db::
00662 //       Access: Public
00663 //  Description:
00664 ////////////////////////////////////////////////////////////////////
00665 void DownloadDb::Db::
00666 add_multifile_record(PT(MultifileRecord) mfr) {
00667   _mfile_records.push_back(mfr);
00668 }
00669 
00670 
00671 ////////////////////////////////////////////////////////////////////
00672 //     Function: DownloadDb::Db::parse_header
00673 //       Access: Private
00674 //  Description: Verifies magic number, returns the number of
00675 //               multifiles or -1 if invalid
00676 ////////////////////////////////////////////////////////////////////
00677 int DownloadDb::Db::
00678 parse_header(const string &data) {
00679   Datagram dg(data);
00680 
00681   // Make sure we have a good header
00682   DatagramIterator di(dg);
00683   PN_uint32 magic_number = di.get_uint32();
00684   downloader_cat.debug()
00685     << "Parsed magic number: " << magic_number << endl;
00686   // If the magic number is equal to the bogus magic number
00687   // it signifies that the previous write was interrupted
00688   if (magic_number == _bogus_magic_number) {
00689     downloader_cat.error()
00690       << "DownloadDb::parse_header() - "
00691       << "Bogus magic number, previous write incomplete: "
00692       << magic_number << " expected: " << _magic_number << endl;
00693     return -1;
00694   }
00695   // If the magic number does not match at all, something is
00696   // really wrong
00697   else if (magic_number != _magic_number) {
00698     downloader_cat.error()
00699       << "DownloadDb::parse_header() - Invalid magic number: "
00700       << magic_number << " expected: " << _magic_number << endl;
00701     return -1;
00702   }
00703 
00704   PN_int32 num_multifiles = di.get_int32();
00705   downloader_cat.debug()
00706     << "Parsed number of multifiles: " << num_multifiles << endl;
00707 
00708   // If we got all the way here, must be done
00709   return num_multifiles;
00710 }
00711 
00712 
00713 
00714 ////////////////////////////////////////////////////////////////////
00715 //     Function: DownloadDb::Db::parse_fr_header
00716 //       Access: Private
00717 //  Description: Parses a file record (fr) header and returns
00718 //               the length of the next file record
00719 ////////////////////////////////////////////////////////////////////
00720 int DownloadDb::Db::
00721 parse_record_header(const string &data) {
00722   Datagram dg(data);
00723   DatagramIterator di(dg);
00724   PN_int32 record_length = di.get_int32();
00725   downloader_cat.spam()
00726     << "Parsed record header length: " << record_length << endl;
00727 
00728   // If we got all the way here, must be done
00729   return record_length;
00730 }
00731 
00732 
00733 ////////////////////////////////////////////////////////////////////
00734 //     Function: DownloadDb::Db::parse_mfr
00735 //       Access: Private
00736 //  Description: Parses a multifile record (mfr) and returns one
00737 ////////////////////////////////////////////////////////////////////
00738 PT(DownloadDb::MultifileRecord) DownloadDb::Db::
00739 parse_mfr(const string &data) {
00740 
00741   PT(DownloadDb::MultifileRecord) mfr = new DownloadDb::MultifileRecord;
00742 
00743   Datagram dg(data);
00744   DatagramIterator di(dg);
00745   PN_int32 mfr_name_length = di.get_int32();
00746   mfr->_name = di.extract_bytes(mfr_name_length);
00747   mfr->_phase = di.get_float64();
00748   mfr->_size = di.get_int32();
00749   mfr->_status = di.get_int32();
00750   mfr->_num_files = di.get_int32();
00751 
00752   // At one time, we stored files in the database with a backslash
00753   // separator.  Nowadays we use a forward slash, but we should make
00754   // sure we properly convert any old records we might read.
00755   mfr->_name = back_to_front_slash(mfr->_name);
00756   
00757   // Read the hash value
00758   mfr->_hash.read_datagram(di);
00759 
00760   downloader_cat.debug()
00761     << "Parsed multifile record: " << mfr->_name << " phase: " << mfr->_phase
00762      << " size: " << mfr->_size
00763     << " status: " << mfr->_status << " num_files: " << mfr->_num_files << endl;
00764 
00765   // Return the new MultifileRecord
00766   return mfr;
00767 }
00768 
00769 
00770 
00771 
00772 ////////////////////////////////////////////////////////////////////
00773 //     Function: DownloadDb::Db::parse_fr
00774 //       Access: Private
00775 //  Description: Parses a file record (fr) and returns one
00776 ////////////////////////////////////////////////////////////////////
00777 PT(DownloadDb::FileRecord) DownloadDb::Db::
00778 parse_fr(const string &data) {
00779 
00780   PT(DownloadDb::FileRecord) fr = new DownloadDb::FileRecord;
00781 
00782   Datagram dg(data);
00783   DatagramIterator di(dg);
00784   PN_int32 fr_name_length = di.get_int32();
00785   fr->_name = di.extract_bytes(fr_name_length);
00786 
00787   // At one time, we stored files in the database with a backslash
00788   // separator.  Nowadays we use a forward slash, but we should make
00789   // sure we properly convert any old records we might read.
00790   fr->_name = back_to_front_slash(fr->_name);
00791 
00792   downloader_cat.spam()
00793     << "Parsed file record: " << fr->_name << endl;
00794 
00795   // Return the new MultifileRecord
00796   return fr;
00797 }
00798 
00799 
00800 
00801 
00802 ////////////////////////////////////////////////////////////////////
00803 //     Function: DownloadDb::Db::read
00804 //       Access: Private
00805 //  Description:
00806 ////////////////////////////////////////////////////////////////////
00807 bool DownloadDb::Db::
00808 read(StreamReader &sr, bool want_server_info) {
00809   // Read the header
00810   string header;
00811   header = sr.extract_bytes(_header_length);
00812   if (header.size() != (size_t)_header_length) {
00813     downloader_cat.error() << "truncated db file" << endl;
00814     return false;
00815   }
00816 
00817   // Parse the header
00818   int num_multifiles = parse_header(header);
00819   if (num_multifiles < 0) {
00820     downloader_cat.error() << "invalid db header" << endl;
00821     return false;
00822   }
00823 
00824   // Now that we know how many multifiles this db has, we can iterate
00825   // reading them off one by one
00826   for (int i = 0; i < num_multifiles; i++) {
00827     // The multifile record header is just one int which
00828     // represents the size of the record
00829     int mfr_header_length = sizeof(PN_int32);
00830 
00831     string mfr_header = sr.extract_bytes(mfr_header_length);
00832     if (mfr_header.size() != (size_t)mfr_header_length) {
00833       downloader_cat.error() << "invalid mfr header" << endl;
00834       return false;
00835     }
00836 
00837     // Parse the header
00838     int mfr_length = parse_record_header(mfr_header);
00839 
00840     // Ok, now that we know the size of the mfr, read it in
00841     // Make a buffer to read the multifile record into
00842     // do not count the header length twice
00843     int read_length = (mfr_length - mfr_header_length);
00844     string mfr_record = sr.extract_bytes(read_length);
00845     if (mfr_record.size() != (size_t)read_length) {
00846       downloader_cat.error() << "invalid mfr record" << endl;
00847       return false;
00848     }
00849 
00850     // Parse the mfr
00851     PT(DownloadDb::MultifileRecord) mfr = parse_mfr(mfr_record);
00852 
00853     // Only read in the individual file info if you are the server
00854     if (want_server_info) {
00855 
00856       // Read off all the file records this multifile has
00857       for (int j = 0; j < mfr->_num_files; j++) {
00858         // The file record header is just one int which
00859         // represents the size of the record
00860         int fr_header_length = sizeof(PN_int32);
00861 
00862         // Read the header
00863         string fr_header = sr.extract_bytes(fr_header_length);
00864         if (fr_header.size() != (size_t)fr_header_length) {
00865           downloader_cat.error() << "invalid fr header" << endl;
00866           return false;
00867         }
00868 
00869         // Parse the header
00870         int fr_length = parse_record_header(fr_header);
00871 
00872         // Ok, now that we know the size of the mfr, read it in
00873         // do not count the header length twice
00874         int read_length = (fr_length - fr_header_length);
00875 
00876         string fr_record = sr.extract_bytes(read_length);
00877         if (fr_record.size() != (size_t)read_length) {
00878           downloader_cat.error() << "invalid fr record" << endl;
00879           return false;
00880         }
00881 
00882         // Parse the file record
00883         PT(DownloadDb::FileRecord) fr = parse_fr(fr_record);
00884 
00885         // Add this file record to the current multifilerecord
00886         mfr->add_file_record(fr);
00887       }
00888     }
00889 
00890     // Add the current multifilerecord to our database
00891     add_multifile_record(mfr);
00892   }
00893 
00894   return true;
00895 }
00896 
00897 
00898 
00899 ////////////////////////////////////////////////////////////////////
00900 //     Function: DownloadDb::Db::write
00901 //       Access: Private
00902 //  Description:
00903 ////////////////////////////////////////////////////////////////////
00904 bool DownloadDb::Db::
00905 write(StreamWriter &sw, bool want_server_info) {
00906   // Declare these outside the loop so we do not keep creating
00907   // and deleting them
00908   PN_float64 phase;
00909   PN_int32 size;
00910   PN_int32 status;
00911   PN_int32 num_files;
00912   PN_int32 name_length;
00913   PN_int32 header_length;
00914 
00915   // Iterate over the multifiles writing them to the stream
00916   pvector< PT(MultifileRecord) >::const_iterator i = _mfile_records.begin();
00917   for(; i != _mfile_records.end(); ++i) {
00918     // Cache some properties so we do not have to keep asking for them
00919     phase = (*i)->_phase;
00920     size = (*i)->_size;
00921     status = (*i)->_status;
00922     num_files = (*i)->get_num_files();
00923     name_length = (*i)->_name.length();
00924 
00925     // Compute the length of this datagram
00926     header_length =
00927       sizeof(header_length) +  // Size of this header length
00928       sizeof(name_length) +    // Size of the size of the name string
00929       (*i)->_name.length() +      // Size of the name string
00930       sizeof(phase) + sizeof(size) +
00931       sizeof(status) + sizeof(num_files) +
00932       sizeof(PN_uint32)*4; // Size of hash value
00933 
00934     // Add the length of this entire datagram
00935     sw.add_int32(header_length);
00936 
00937     // Add the length of the name
00938     sw.add_int32(name_length);
00939     // Add the name
00940     sw.append_data((*i)->_name);
00941 
00942     // Add all the properties
00943     sw.add_float64(phase);
00944     sw.add_int32(size);
00945     sw.add_int32(status);
00946     sw.add_int32(num_files);
00947     
00948     (*i)->_hash.write_stream(sw);
00949 
00950     // Only write out the file information if you are the server
00951     if (want_server_info) {
00952       // Now iterate over this multifile's files writing them to the stream
00953       // Iterate over the multifiles writing them to the stream
00954       pvector< PT(FileRecord) >::const_iterator j = (*i)->_file_records.begin();
00955       for(; j != (*i)->_file_records.end(); ++j) {
00956         name_length = (*j)->_name.length();
00957 
00958         // Compute the length of this datagram
00959         header_length =
00960           sizeof(header_length) +  // Size of this header length
00961           sizeof(name_length) +    // Size of the size of the name string
00962           (*j)->_name.length();    // Size of the name string
00963 
00964         // Add the length of this entire datagram
00965         sw.add_int32(header_length);
00966 
00967         // Add the length of the name
00968         sw.add_int32(name_length);
00969         // Add the name
00970         sw.append_data((*j)->_name);
00971       }
00972     }
00973   }
00974 
00975   return true;
00976 }
00977 
00978 ////////////////////////////////////////////////////////////////////
00979 //     Function: DownloadDb::Db::write_bogus_header
00980 //       Access: Private
00981 //  Description: Writes the bogus header uncompressed with platform-
00982 //               independent byte ordering. This header will get
00983 //               overwritten with the real magic number as the last
00984 //               step in the write
00985 ////////////////////////////////////////////////////////////////////
00986 bool DownloadDb::Db::
00987 write_bogus_header(StreamWriter &sw) {
00988   // Write the db magic number
00989   sw.add_uint32(_bogus_magic_number);
00990 
00991   // Write the number of multifiles
00992   sw.add_int32(get_num_multifiles());
00993 
00994   return true;
00995 }
00996 
00997 ////////////////////////////////////////////////////////////////////
00998 //     Function: DownloadDb::Db::write_header
00999 //       Access: Private
01000 //  Description: Writes the header uncompressed with platform-
01001 //               independent byte ordering
01002 ////////////////////////////////////////////////////////////////////
01003 bool DownloadDb::Db::
01004 write_header(ostream &write_stream) {
01005   Datagram dg;
01006 
01007   // Write the db magic number
01008   dg.add_uint32(_magic_number);
01009 
01010   // Write the number of multifiles
01011   dg.add_int32(get_num_multifiles());
01012 
01013   string msg = dg.get_message();
01014 
01015   // Seek back to the beginning of the write stream
01016   write_stream.seekp(0);
01017   // Overwrite the old bogus header with the real header
01018   write_stream.write(msg.data(), msg.length());
01019   return true;
01020 }
01021 
01022 
01023 
01024 ////////////////////////////////////////////////////////////////////
01025 // FileRecord methods
01026 ////////////////////////////////////////////////////////////////////
01027 
01028 
01029 ////////////////////////////////////////////////////////////////////
01030 //     Function: DownloadDb::FileRecord::Constructor
01031 //       Access: Public
01032 //  Description:
01033 ////////////////////////////////////////////////////////////////////
01034 DownloadDb::FileRecord::
01035 FileRecord() {
01036   _name = "";
01037 }
01038 
01039 
01040 ////////////////////////////////////////////////////////////////////
01041 //     Function: DownloadDb::FileRecord::Constructor
01042 //       Access: Public
01043 //  Description:
01044 ////////////////////////////////////////////////////////////////////
01045 DownloadDb::FileRecord::
01046 FileRecord(string name) {
01047   _name = name;
01048 }
01049 
01050 ////////////////////////////////////////////////////////////////////
01051 //     Function: DownloadDb::FileRecord::output
01052 //       Access: Public
01053 //  Description:
01054 ////////////////////////////////////////////////////////////////////
01055 void DownloadDb::FileRecord::
01056 write(ostream &out) const {
01057   out << " FileRecord: " << _name << endl;
01058 }
01059 
01060 ////////////////////////////////////////////////////////////////////
01061 //     Function: DownloadDb::add_version
01062 //       Access: Published
01063 //  Description: Appends a new version of the file onto the end of the
01064 //               list, or changes the hash associated with a version
01065 //               previously added.
01066 //
01067 //               Note: version numbers start at 1
01068 ////////////////////////////////////////////////////////////////////
01069 void DownloadDb::
01070 add_version(const Filename &name, const HashVal &hash, int version) {
01071   nassertv(version >= 1);
01072 
01073   VectorHash &vhash = _versions[name];
01074   int size = vhash.size();
01075 
01076   // We should not skip over versions as we add them.
01077   nassertv(version <= size+1);
01078 
01079   if (version-1 < size) {
01080     // If you are overwriting an old hash value, just rewrite the value
01081     vhash[version-1] = hash;
01082 
01083   } else {
01084     // Otherwise, extend the vector.
01085     vhash.push_back(hash);
01086   }
01087 }
01088 
01089 ////////////////////////////////////////////////////////////////////
01090 //     Function: DownloadDb::insert_new_version
01091 //       Access: Published
01092 //  Description: Inserts a new version 1 copy of the file, sliding all
01093 //               the other versions up by one.
01094 ////////////////////////////////////////////////////////////////////
01095 void DownloadDb::
01096 insert_new_version(const Filename &name, const HashVal &hash) {
01097   VectorHash &vhash = _versions[name];
01098   vhash.insert(vhash.begin(), hash);
01099 }
01100 
01101 ////////////////////////////////////////////////////////////////////
01102 //     Function: DownloadDb::has_version
01103 //       Access: Published
01104 //  Description: Returns true if the indicated file has version
01105 //               information, false otherwise.  Some files recorded in
01106 //               the database may not bother to track versions.
01107 ////////////////////////////////////////////////////////////////////
01108 bool DownloadDb::
01109 has_version(const Filename &name) const {
01110   return (_versions.find(name) != _versions.end());
01111 }
01112 
01113 ////////////////////////////////////////////////////////////////////
01114 //     Function: DownloadDb::get_num_versions
01115 //       Access: Published
01116 //  Description: Returns the number of versions stored for the
01117 //               indicated file.
01118 ////////////////////////////////////////////////////////////////////
01119 int DownloadDb::
01120 get_num_versions(const Filename &name) const {
01121   VersionMap::const_iterator vmi = _versions.find(name);
01122   if (vmi == _versions.end()) {
01123     return 0;
01124   }
01125 
01126   return (int)(*vmi).second.size();
01127 }
01128 
01129 ////////////////////////////////////////////////////////////////////
01130 //     Function: DownloadDb::set_num_versions
01131 //       Access: Published
01132 //  Description: Reduces the number of versions of a particular file
01133 //               stored in the ddb by throwing away all versions
01134 //               higher than the indicated index.
01135 ////////////////////////////////////////////////////////////////////
01136 void DownloadDb::
01137 set_num_versions(const Filename &name, int num_versions) {
01138   VersionMap::iterator vmi = _versions.find(name);
01139   if (vmi == _versions.end()) {
01140     nassertv(num_versions == 0);
01141     return;
01142   }
01143 
01144   VectorHash &vhash = (*vmi).second;
01145 
01146   nassertv(num_versions <= (int)vhash.size());
01147   vhash.erase(vhash.begin() + num_versions, vhash.end());
01148 }
01149 
01150 ////////////////////////////////////////////////////////////////////
01151 //     Function: DownloadDb::get_version
01152 //       Access: Published
01153 //  Description: Returns the version number of this particular file,
01154 //               determined by looking up the hash generated from the
01155 //               file.  Returns -1 if the version number cannot be
01156 //               determined.
01157 ////////////////////////////////////////////////////////////////////
01158 int DownloadDb::
01159 get_version(const Filename &name, const HashVal &hash) const {
01160   VersionMap::const_iterator vmi = _versions.find(name);
01161   if (vmi == _versions.end()) {
01162     downloader_cat.debug()
01163       << "DownloadDb::get_version() - can't find: " << name << endl;
01164     return -1;
01165   }
01166   const VectorHash &vhash = (*vmi).second;
01167   VectorHash::const_iterator i = find(vhash.begin(), vhash.end(), hash);
01168   if (i != vhash.end())
01169     return (i - vhash.begin() + 1);
01170   downloader_cat.debug()
01171     << "DownloadDb::get_version() - can't find hash: " << hash << endl;
01172   return -1;
01173 }
01174 
01175 ////////////////////////////////////////////////////////////////////
01176 //     Function: DownloadDb::get_hash
01177 //       Access: Published
01178 //  Description: Returns the MD5 hash associated with the indicated
01179 //               version of the indicated file.
01180 ////////////////////////////////////////////////////////////////////
01181 const HashVal &DownloadDb::
01182 get_hash(const Filename &name, int version) const {
01183   static HashVal bogus_hash;
01184 
01185   VersionMap::const_iterator vmi = _versions.find(name);
01186   if (vmi == _versions.end()) {
01187     downloader_cat.error()
01188       << "DownloadDb::get_hash() - can't find: " << name << endl;
01189     return bogus_hash;
01190   }
01191 
01192   const VectorHash &vhash = (*vmi).second;
01193   if (version < 1 || version > (int)vhash.size()) {
01194     downloader_cat.error()
01195       << "DownloadDb::get_hash() - no version " << version 
01196       << " for " << name << endl;
01197     return bogus_hash;
01198   }
01199   return vhash[version - 1];
01200 }
01201 
01202 ////////////////////////////////////////////////////////////////////
01203 //     Function: DownloadDb::write_version_map
01204 //       Access: Protected
01205 //  Description:
01206 ////////////////////////////////////////////////////////////////////
01207 void DownloadDb::
01208 write_version_map(StreamWriter &sw) {
01209   VersionMap::iterator vmi;
01210   VectorHash::iterator i;
01211   string name;
01212 
01213   sw.add_int32(_versions.size());
01214   for (vmi = _versions.begin(); vmi != _versions.end(); ++vmi) {
01215     name = (*vmi).first;
01216     downloader_cat.spam()
01217       << "DownloadDb::write_version_map() - writing file: "
01218       << name << " of length: " << name.length() << endl;
01219     sw.add_int32(name.length());
01220     sw.append_data(name);
01221     sw.add_int32((*vmi).second.size());
01222     for (i = (*vmi).second.begin(); i != (*vmi).second.end(); ++i) {
01223       // *i will point to a HashVal
01224       (*i).write_stream(sw);
01225     }
01226   }
01227 }
01228 
01229 ////////////////////////////////////////////////////////////////////
01230 //     Function: DownloadDb::read_version_map
01231 //       Access: Protected
01232 //  Description:
01233 ////////////////////////////////////////////////////////////////////
01234 bool DownloadDb::
01235 read_version_map(StreamReader &sr) {
01236   int num_entries = sr.get_int32();
01237   if (sr.get_istream()->fail()) {
01238     return false;
01239   }
01240 
01241   for (int i = 0; i < num_entries; i++) {
01242 
01243     // Get the length of the file name
01244     int name_length = sr.get_int32();
01245     if (sr.get_istream()->fail()) {
01246       return false;
01247     }
01248     downloader_cat.spam()
01249       << "DownloadDb::read_version_map() - name length: " << name_length
01250       << endl;
01251 
01252     // Get the file name
01253     string name = sr.extract_bytes(name_length);
01254     downloader_cat.spam()
01255       << "DownloadDb::read_version_map() - name: " << name << endl;
01256 
01257     // Get number of hash values for name
01258     int length = sr.get_int32();
01259     if (sr.get_istream()->fail()) {
01260       return false;
01261     }
01262     downloader_cat.spam()
01263       << "DownloadDb::read_version_map() - number of values: " << length
01264       << endl;
01265 
01266     for (int j = 0; j < length; j++) {
01267       HashVal hash;
01268       hash.read_stream(sr);
01269       if (sr.get_istream()->fail()) {
01270         return false;
01271       }
01272       add_version(name, hash, j + 1);
01273     }
01274   }
01275   return true;
01276 }
01277 
01278 ////////////////////////////////////////////////////////////////////
01279 //     Function: DownloadDb::write_version_map
01280 //       Access: Public
01281 //  Description:
01282 ////////////////////////////////////////////////////////////////////
01283 void DownloadDb::
01284 write_version_map(ostream &out) const {
01285   out << "Version Map: " << endl;
01286   VersionMap::const_iterator vmi;
01287   VectorHash::const_iterator i;
01288   for (vmi = _versions.begin(); vmi != _versions.end(); ++vmi) {
01289     out << "  " << (*vmi).first << endl;
01290     for (i = (*vmi).second.begin(); i != (*vmi).second.end(); ++i) {
01291       HashVal hash = *i;
01292       out << "    " << hash.as_dec() << endl;
01293     }
01294   }
01295   out << endl;
01296 }
 All Classes Functions Variables Enumerations