Panda3D
|
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 }