Panda3D
Loading...
Searching...
No Matches
downloadDb.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file downloadDb.cxx
10 * @author shochet
11 * @date 2000-09-08
12 */
13
14#include "config_downloader.h"
15#include "downloadDb.h"
16#include "streamReader.h"
17#include "streamWriter.h"
18#include "ramfile.h"
19#include "virtualFileSystem.h"
20
21#include <algorithm>
22
23using std::endl;
24using std::istream;
25using std::istringstream;
26using std::ostream;
27using std::string;
28
29// Defines
30
31// Written at the top of the file so we know this is a downloadDb
32uint32_t DownloadDb::_magic_number = 0xfeedfeed;
33
34// Written at the top of the file to signify we are not done writing to the
35// file yet. If you load a db with this magic number that means the previous
36// time it got written out was probably interrupted in the middle of the
37// write.
38uint32_t DownloadDb::_bogus_magic_number = 0x11111111;
39
40
41static string
42back_to_front_slash(const string &str) {
43 string result = str;
44 string::iterator si;
45 for (si = result.begin(); si != result.end(); ++si) {
46 if ((*si) == '\\') {
47 (*si) = '/';
48 }
49 }
50
51 return result;
52}
53
54
55/**
56 * Create a download db with these client and server dbs
57 */
59DownloadDb(Ramfile &server_file, Filename &client_file) {
60 if (downloader_cat.is_debug())
61 downloader_cat.debug()
62 << "DownloadDb constructor called" << endl;
63 _client_db = read_db(client_file, 0);
64 _client_db._filename = client_file;
65 _server_db = read_db(server_file, 1);
66}
67
68/**
69 * Create a download db with these client and server dbs
70 */
72DownloadDb(Filename &server_file, Filename &client_file) {
73 if (downloader_cat.is_debug())
74 downloader_cat.debug()
75 << "DownloadDb constructor called" << endl;
76 _client_db = read_db(client_file, 0);
77 _client_db._filename = client_file;
78 _server_db = read_db(server_file, 1);
79 _server_db._filename = server_file;
80}
81
82/**
83 * Primarily used for testing.
84 */
86DownloadDb() {
87 _client_db = Db();
88 _server_db = Db();
89}
90
91/**
92 *
93 */
94DownloadDb::
95~DownloadDb() {
96 if (downloader_cat.is_debug())
97 downloader_cat.debug()
98 << "DownloadDb destructor called" << endl;
99}
100
101
102/**
103 *
104 */
105void DownloadDb::
106output(ostream &out) const {
107 out << "[" << _server_db._filename << " " << _client_db._filename << "]";
108}
109
110/**
111 *
112 */
113void DownloadDb::
114write(ostream &out) const {
115 out << "DownloadDb" << endl;
116 out << "============================================================" << endl;
117 out << " Client DB file: " << _client_db._filename << endl;
118 out << "============================================================" << endl;
119 _client_db.write(out);
120 out << endl;
121 out << "============================================================" << endl;
122 out << " Server DB file: " << _server_db._filename << endl;
123 out << "============================================================" << endl;
124 _server_db.write(out);
125 write_version_map(out);
126 out << endl;
127}
128
129
130/**
131 *
132 */
133bool DownloadDb::
134write_client_db(Filename &file) {
135 return write_db(file, _client_db, 0);
136}
137
138
139/**
140 *
141 */
142bool DownloadDb::
143write_server_db(Filename &file) {
144 return write_db(file, _server_db, 1);
145}
146
147/**
148 *
149 */
150bool DownloadDb::
151client_multifile_exists(string mfname) const {
152 return (_client_db.multifile_exists(mfname));
153}
154
155/**
156 * A multifile is complete when it is completely downloaded. Note: it may
157 * already be decompressed or extracted and it is still complete
158 */
160client_multifile_complete(string mfname) const {
161 int client_status = _client_db.get_multifile_record_named(mfname)->_status;
162 return (client_status >= Status_complete);
163}
164
165/**
166 *
167 */
168bool DownloadDb::
169client_multifile_decompressed(string mfname) const {
170 int client_status = _client_db.get_multifile_record_named(mfname)->_status;
171 return (client_status >= Status_decompressed);
172}
173
174/**
175 *
176 */
177bool DownloadDb::
178client_multifile_extracted(string mfname) const {
179 int client_status = _client_db.get_multifile_record_named(mfname)->_status;
180 return (client_status >= Status_extracted);
181}
182
183
184/**
185 * Return the hash value of the file we are working on
186 */
188get_client_multifile_hash(string mfname) const {
189 return _client_db.get_multifile_record_named(mfname)->_hash;
190}
191
192
193/**
194 * Return the hash value of the server file
195 */
197get_server_multifile_hash(string mfname) const {
198 return _server_db.get_multifile_record_named(mfname)->_hash;
199}
200
201
202/**
203 * Set the hash value of file we are working on
204 */
206set_client_multifile_hash(string mfname, HashVal val) {
207 _client_db.get_multifile_record_named(mfname)->_hash = val;
208 write_client_db(_client_db._filename);
209}
210
211
212/**
213 * Set the hash value of file we are working on
214 */
216set_server_multifile_hash(string mfname, HashVal val) {
217 _server_db.get_multifile_record_named(mfname)->_hash = val;
218}
219
220// Operations on multifiles
221
222/**
223 *
224 */
225void DownloadDb::
226delete_client_multifile(string mfname) {
227}
228
229/**
230 *
231 */
232void DownloadDb::
233add_client_multifile(string server_mfname) {
234 PT(MultifileRecord) server_mfr = _server_db.get_multifile_record_named(server_mfname);
235 PT(MultifileRecord) client_mfr = new MultifileRecord;
236 client_mfr->_name = server_mfr->_name;
237 client_mfr->_phase = server_mfr->_phase;
238 _client_db.add_multifile_record(client_mfr);
239}
240
241
242/**
243 *
244 */
245void DownloadDb::
246expand_client_multifile(string mfname) {
247}
248
249
250/**
251 *
252 */
253DownloadDb::Db DownloadDb::
254read_db(Filename &file, bool want_server_info) {
256 Db db;
257
258 // Open the multifile for reading
259 file.set_binary();
260 istream *read_stream = vfs->open_read_file(file, true);
261
262 if (read_stream == nullptr) {
263 downloader_cat.error()
264 << "failed to open input file: "
265 << file << endl;
266 return db;
267 }
268
269 StreamReader sr(*read_stream);
270 if (!db.read(sr, want_server_info)) {
271 downloader_cat.error()
272 << "read failed: "
273 << file << endl;
274 vfs->close_read_file(read_stream);
275 return db;
276 }
277
278 if (want_server_info) {
279 if (!read_version_map(sr)) {
280 downloader_cat.error()
281 << "read_version_map() failed: "
282 << file << endl;
283 }
284 }
285
286 vfs->close_read_file(read_stream);
287
288 return db;
289}
290
291/**
292 *
293 */
294DownloadDb::Db DownloadDb::
295read_db(Ramfile &file, bool want_server_info) {
296 // Open the multifile for reading
297 istringstream read_stream(file._data);
298 Db db;
299
300 StreamReader sr(read_stream);
301
302 if (!db.read(sr, want_server_info)) {
303 downloader_cat.error()
304 << "read failed" << endl;
305 return db;
306 }
307 if (want_server_info) {
308 if (!read_version_map(sr)) {
309 downloader_cat.error()
310 << "read_version_map() failed" << endl;
311 }
312 }
313
314 return db;
315}
316
317
318/**
319 *
320 */
321bool DownloadDb::
322write_db(Filename &file, Db db, bool want_server_info) {
323 pofstream write_stream;
324 file.set_binary();
325 if (!file.open_write(write_stream)) {
326 downloader_cat.error()
327 << "DownloadDb::write_db() - Failed to open output file: "
328 << file << endl;
329 return false;
330 }
331
332 downloader_cat.spam()
333 << "Writing to file: " << file << endl;
334
335 StreamWriter sw(write_stream);
336
337 // Initialize the write stream with a bogus header
338 db.write_bogus_header(sw);
339 db.write(sw, want_server_info);
340 if (want_server_info) {
341 write_version_map(sw);
342 }
343 // Now write the real header
344 db.write_header(write_stream);
345 write_stream.close();
346 return true;
347}
348
349
350/**
351 * Used on the server side makefiles to create a new clean server db
352 */
355 _server_db = Db();
356}
357
358
359/**
360 *
361 */
362void DownloadDb::
363server_add_multifile(string mfname, Phase phase, int size, int status) {
364 PT(MultifileRecord) mfr = new MultifileRecord(mfname, phase, size, status);
365 _server_db.add_multifile_record(mfr);
366}
367
368
369/**
370 *
371 */
372void DownloadDb::
373server_add_file(string mfname, string fname) {
374 // Make the new file record
375 PT(FileRecord) fr = new FileRecord(fname);
376
377 // Find the multifile with mfname
378 pvector< PT(MultifileRecord) >::iterator i = _server_db._mfile_records.begin();
379 for (; i != _server_db._mfile_records.end(); ++i) {
380 if (mfname == (*i)->_name) {
381 (*i)->add_file_record(fr);
382 return;
383 }
384 }
385
386 // Uh-oh, did not find it
387 downloader_cat.error() << "Could not find record named "
388 << mfname << " in database " << endl;
389 nassertv(false);
390 return;
391}
392
393
394// Multifile methods
395
396
397/**
398 *
399 */
400DownloadDb::MultifileRecord::
401MultifileRecord() {
402 _name = "";
403 _phase = 0;
404 _size = 0;
405 _status = Status_incomplete;
406}
407
408
409/**
410 *
411 */
412DownloadDb::MultifileRecord::
413MultifileRecord(string name, Phase phase, int size, int status) {
414 _name = name;
415 _phase = phase;
416 _size = size;
417 _status = status;
418}
419
420
421/**
422 *
423 */
424void DownloadDb::MultifileRecord::
425write(ostream &out) const {
426 out << "==================================================" << endl;
427 out << "MultifileRecord: " << _name << endl
428 << " phase: " << _phase << endl
429 << " size: " << _size << endl
430 << " status: " << _status << endl
431 << " hash: " << _hash.as_dec() << endl;
432 out << "--------------------------------------------------" << endl;
433 pvector< PT(FileRecord) >::const_iterator i = _file_records.begin();
434 for(; i != _file_records.end(); ++i) {
435 (*i)->write(out);
436 }
437}
438
439
440
441/**
442 *
443 */
444int DownloadDb::MultifileRecord::
445get_num_files() const {
446 return _file_records.size();
447}
448
449/**
450 *
451 */
452string DownloadDb::MultifileRecord::
453get_file_name(int index) const {
454 return _file_records[index]->_name;
455}
456
457
458/**
459 *
460 */
461bool DownloadDb::MultifileRecord::
462file_exists(string fname) const {
463 pvector< PT(FileRecord) >::const_iterator i = _file_records.begin();
464 for(; i != _file_records.end(); ++i) {
465 if (fname == (*i)->_name) {
466 return true;
467 }
468 }
469 return false;
470}
471
472
473/**
474 *
475 */
476PT(DownloadDb::FileRecord) DownloadDb::MultifileRecord::
477get_file_record_named(string fname) const {
478 pvector< PT(FileRecord) >::const_iterator i = _file_records.begin();
479 for(; i != _file_records.end(); ++i) {
480 if (fname == (*i)->_name) {
481 return (*i);
482 }
483 }
484 // Did not find it, just return an empty version
485 downloader_cat.error() << "Could not find record named "
486 << fname << " in multifile " << _name << endl;
487 PT(FileRecord) foo = new FileRecord;
488 nassertr(false, foo);
489 return foo;
490}
491
492
493/**
494 *
495 */
496void DownloadDb::MultifileRecord::
497add_file_record(PT(FileRecord) fr) {
498 _file_records.push_back(fr);
499}
500
501
502
503// Db methods
504
505
506
507
508/**
509 *
510 */
511DownloadDb::Db::
512Db() {
513 // The head is a magic number and the number of multifiles in the db
514 _header_length = sizeof(_magic_number) + sizeof(int32_t);
515}
516
517
518/**
519 *
520 */
521void DownloadDb::Db::
522write(ostream &out) const {
523 pvector< PT(MultifileRecord) >::const_iterator i = _mfile_records.begin();
524 for(; i != _mfile_records.end(); ++i) {
525 (*i)->write(out);
526 }
527}
528
529
530/**
531 *
532 */
533int DownloadDb::Db::
534get_num_multifiles() const {
535 return _mfile_records.size();
536}
537
538/**
539 *
540 */
541string DownloadDb::Db::
542get_multifile_name(int index) const {
543 return _mfile_records[index]->_name;
544}
545
546/**
547 *
548 */
549bool DownloadDb::Db::
550multifile_exists(string mfname) const {
551 pvector< PT(MultifileRecord) >::const_iterator i = _mfile_records.begin();
552 for(; i != _mfile_records.end(); ++i) {
553 if (mfname == (*i)->_name) {
554 return true;
555 }
556 }
557 return false;
558}
559
560/**
561 *
562 */
563PT(DownloadDb::MultifileRecord) DownloadDb::Db::
564get_multifile_record_named(string mfname) const {
565 pvector< PT(MultifileRecord) >::const_iterator i = _mfile_records.begin();
566 for(; i != _mfile_records.end(); ++i) {
567 if (mfname == (*i)->_name) {
568 return (*i);
569 }
570 }
571 // Did not find it, just return an empty version
572 downloader_cat.error() << "Could not find record named "
573 << mfname << " in database " << endl;
574 PT(MultifileRecord) foo = new MultifileRecord;
575 nassertr(false, foo);
576 return foo;
577}
578
579/**
580 *
581 */
582void DownloadDb::Db::
583add_multifile_record(PT(MultifileRecord) mfr) {
584 _mfile_records.push_back(mfr);
585}
586
587
588/**
589 * Verifies magic number, returns the number of multifiles or -1 if invalid
590 */
593 // Make sure we have a good header
594 DatagramIterator di(dg);
595 uint32_t magic_number = di.get_uint32();
596 downloader_cat.debug()
597 << "Parsed magic number: " << magic_number << endl;
598 // If the magic number is equal to the bogus magic number it signifies that
599 // the previous write was interrupted
600 if (magic_number == _bogus_magic_number) {
601 downloader_cat.error()
602 << "DownloadDb::parse_header() - "
603 << "Bogus magic number, previous write incomplete: "
604 << magic_number << " expected: " << _magic_number << endl;
605 return -1;
606 }
607 // If the magic number does not match at all, something is really wrong
608 else if (magic_number != _magic_number) {
609 downloader_cat.error()
610 << "DownloadDb::parse_header() - Invalid magic number: "
611 << magic_number << " expected: " << _magic_number << endl;
612 return -1;
613 }
614
615 int32_t num_multifiles = di.get_int32();
616 downloader_cat.debug()
617 << "Parsed number of multifiles: " << num_multifiles << endl;
618
619 // If we got all the way here, must be done
620 return num_multifiles;
621}
622
623
624
625/**
626 * Parses a file record (fr) header and returns the length of the next file
627 * record
628 */
631 DatagramIterator di(dg);
632 int32_t record_length = di.get_int32();
633 downloader_cat.spam()
634 << "Parsed record header length: " << record_length << endl;
635
636 // If we got all the way here, must be done
637 return record_length;
638}
639
640
641/**
642 * Parses a multifile record (mfr) and returns one
643 */
644PT(DownloadDb::MultifileRecord) DownloadDb::Db::
645parse_mfr(Datagram dg) {
646
648
649 DatagramIterator di(dg);
650 mfr->_name = di.get_string32();
651 mfr->_phase = di.get_float64();
652 mfr->_size = di.get_int32();
653 mfr->_status = di.get_int32();
654 mfr->_num_files = di.get_int32();
655
656 // At one time, we stored files in the database with a backslash separator.
657 // Nowadays we use a forward slash, but we should make sure we properly
658 // convert any old records we might read.
659 mfr->_name = back_to_front_slash(mfr->_name);
660
661 // Read the hash value
662 mfr->_hash.read_datagram(di);
663
664 downloader_cat.debug()
665 << "Parsed multifile record: " << mfr->_name << " phase: " << mfr->_phase
666 << " size: " << mfr->_size
667 << " status: " << mfr->_status << " num_files: " << mfr->_num_files << endl;
668
669 // Return the new MultifileRecord
670 return mfr;
671}
672
673
674
675
676/**
677 * Parses a file record (fr) and returns one
678 */
679PT(DownloadDb::FileRecord) DownloadDb::Db::
680parse_fr(Datagram dg) {
681
683
684 DatagramIterator di(dg);
685 fr->_name = di.get_string32();
686
687 // At one time, we stored files in the database with a backslash separator.
688 // Nowadays we use a forward slash, but we should make sure we properly
689 // convert any old records we might read.
690 fr->_name = back_to_front_slash(fr->_name);
691
692 downloader_cat.spam()
693 << "Parsed file record: " << fr->_name << endl;
694
695 // Return the new MultifileRecord
696 return fr;
697}
698
699
700
701
702/**
703 *
704 */
705bool DownloadDb::Db::
706read(StreamReader &sr, bool want_server_info) {
707 // Read the header
708 vector_uchar header = sr.extract_bytes(_header_length);
709 if (header.size() != (size_t)_header_length) {
710 downloader_cat.error() << "truncated db file" << endl;
711 return false;
712 }
713
714 // Parse the header
715 int num_multifiles = parse_header(Datagram(std::move(header)));
716 if (num_multifiles < 0) {
717 downloader_cat.error() << "invalid db header" << endl;
718 return false;
719 }
720
721 // Now that we know how many multifiles this db has, we can iterate reading
722 // them off one by one
723 for (int i = 0; i < num_multifiles; i++) {
724 // The multifile record header is just one int which represents the size
725 // of the record
726 int mfr_header_length = sizeof(int32_t);
727
728 vector_uchar mfr_header = sr.extract_bytes(mfr_header_length);
729 if (mfr_header.size() != (size_t)mfr_header_length) {
730 downloader_cat.error() << "invalid mfr header" << endl;
731 return false;
732 }
733
734 // Parse the header
735 int mfr_length = parse_record_header(Datagram(std::move(mfr_header)));
736
737 // Ok, now that we know the size of the mfr, read it in Make a buffer to
738 // read the multifile record into do not count the header length twice
739 int read_length = (mfr_length - mfr_header_length);
740 vector_uchar mfr_record = sr.extract_bytes(read_length);
741 if (mfr_record.size() != (size_t)read_length) {
742 downloader_cat.error() << "invalid mfr record" << endl;
743 return false;
744 }
745
746 // Parse the mfr
747 PT(DownloadDb::MultifileRecord) mfr = parse_mfr(Datagram(std::move(mfr_record)));
748
749 // Only read in the individual file info if you are the server
750 if (want_server_info) {
751
752 // Read off all the file records this multifile has
753 for (int j = 0; j < mfr->_num_files; j++) {
754 // The file record header is just one int which represents the size of
755 // the record
756 int fr_header_length = sizeof(int32_t);
757
758 // Read the header
759 vector_uchar fr_header = sr.extract_bytes(fr_header_length);
760 if (fr_header.size() != (size_t)fr_header_length) {
761 downloader_cat.error() << "invalid fr header" << endl;
762 return false;
763 }
764
765 // Parse the header
766 int fr_length = parse_record_header(Datagram(std::move(fr_header)));
767
768 // Ok, now that we know the size of the mfr, read it in do not count
769 // the header length twice
770 int read_length = (fr_length - fr_header_length);
771
772 vector_uchar fr_record = sr.extract_bytes(read_length);
773 if (fr_record.size() != (size_t)read_length) {
774 downloader_cat.error() << "invalid fr record" << endl;
775 return false;
776 }
777
778 // Parse the file record
779 PT(DownloadDb::FileRecord) fr = parse_fr(Datagram(std::move(fr_record)));
780
781 // Add this file record to the current multifilerecord
782 mfr->add_file_record(fr);
783 }
784 }
785
786 // Add the current multifilerecord to our database
787 add_multifile_record(mfr);
788 }
789
790 return true;
791}
792
793
794
795/**
796 *
797 */
798bool DownloadDb::Db::
799write(StreamWriter &sw, bool want_server_info) {
800 // Declare these outside the loop so we do not keep creating and deleting
801 // them
802 PN_float64 phase;
803 int32_t size;
804 int32_t status;
805 int32_t num_files;
806 int32_t name_length;
807 int32_t header_length;
808
809 // Iterate over the multifiles writing them to the stream
810 pvector< PT(MultifileRecord) >::const_iterator i = _mfile_records.begin();
811 for(; i != _mfile_records.end(); ++i) {
812 // Cache some properties so we do not have to keep asking for them
813 phase = (*i)->_phase;
814 size = (*i)->_size;
815 status = (*i)->_status;
816 num_files = (*i)->get_num_files();
817 name_length = (*i)->_name.length();
818
819 // Compute the length of this datagram
820 header_length =
821 sizeof(header_length) + // Size of this header length
822 sizeof(name_length) + // Size of the size of the name string
823 (*i)->_name.length() + // Size of the name string
824 sizeof(phase) + sizeof(size) +
825 sizeof(status) + sizeof(num_files) +
826 sizeof(uint32_t)*4; // Size of hash value
827
828 // Add the length of this entire datagram
829 sw.add_int32(header_length);
830
831 // Add the length of the name
832 sw.add_int32(name_length);
833 // Add the name
834 sw.append_data((*i)->_name);
835
836 // Add all the properties
837 sw.add_float64(phase);
838 sw.add_int32(size);
839 sw.add_int32(status);
840 sw.add_int32(num_files);
841
842 (*i)->_hash.write_stream(sw);
843
844 // Only write out the file information if you are the server
845 if (want_server_info) {
846 // Now iterate over this multifile's files writing them to the stream
847 // Iterate over the multifiles writing them to the stream
848 pvector< PT(FileRecord) >::const_iterator j = (*i)->_file_records.begin();
849 for(; j != (*i)->_file_records.end(); ++j) {
850 name_length = (*j)->_name.length();
851
852 // Compute the length of this datagram
853 header_length =
854 sizeof(header_length) + // Size of this header length
855 sizeof(name_length) + // Size of the size of the name string
856 (*j)->_name.length(); // Size of the name string
857
858 // Add the length of this entire datagram
859 sw.add_int32(header_length);
860
861 // Add the length of the name
862 sw.add_int32(name_length);
863 // Add the name
864 sw.append_data((*j)->_name);
865 }
866 }
867 }
868
869 return true;
870}
871
872/**
873 * Writes the bogus header uncompressed with platform- independent byte
874 * ordering. This header will get overwritten with the real magic number as
875 * the last step in the write
876 */
879 // Write the db magic number
880 sw.add_uint32(_bogus_magic_number);
881
882 // Write the number of multifiles
883 sw.add_int32(get_num_multifiles());
884
885 return true;
886}
887
888/**
889 * Writes the header uncompressed with platform- independent byte ordering
890 */
892write_header(ostream &write_stream) {
893 Datagram dg;
894
895 // Write the db magic number
896 dg.add_uint32(_magic_number);
897
898 // Write the number of multifiles
899 dg.add_int32(get_num_multifiles());
900
901 // Seek back to the beginning of the write stream
902 write_stream.seekp(0);
903 // Overwrite the old bogus header with the real header
904 write_stream.write((const char *)dg.get_data(), dg.get_length());
905 return true;
906}
907
908
909
910// FileRecord methods
911
912
913/**
914 *
915 */
916DownloadDb::FileRecord::
917FileRecord() {
918 _name = "";
919}
920
921
922/**
923 *
924 */
925DownloadDb::FileRecord::
926FileRecord(string name) {
927 _name = name;
928}
929
930/**
931 *
932 */
933void DownloadDb::FileRecord::
934write(ostream &out) const {
935 out << " FileRecord: " << _name << endl;
936}
937
938/**
939 * Appends a new version of the file onto the end of the list, or changes the
940 * hash associated with a version previously added.
941 *
942 * Note: version numbers start at 1
943 */
945add_version(const Filename &name, const HashVal &hash, int version) {
946 nassertv(version >= 1);
947
948 VectorHash &vhash = _versions[name];
949 int size = vhash.size();
950
951 // We should not skip over versions as we add them.
952 nassertv(version <= size+1);
953
954 if (version-1 < size) {
955 // If you are overwriting an old hash value, just rewrite the value
956 vhash[version-1] = hash;
957
958 } else {
959 // Otherwise, extend the vector.
960 vhash.push_back(hash);
961 }
962}
963
964/**
965 * Inserts a new version 1 copy of the file, sliding all the other versions up
966 * by one.
967 */
969insert_new_version(const Filename &name, const HashVal &hash) {
970 VectorHash &vhash = _versions[name];
971 vhash.insert(vhash.begin(), hash);
972}
973
974/**
975 * Returns true if the indicated file has version information, false
976 * otherwise. Some files recorded in the database may not bother to track
977 * versions.
978 */
980has_version(const Filename &name) const {
981 return (_versions.find(name) != _versions.end());
982}
983
984/**
985 * Returns the number of versions stored for the indicated file.
986 */
988get_num_versions(const Filename &name) const {
989 VersionMap::const_iterator vmi = _versions.find(name);
990 if (vmi == _versions.end()) {
991 return 0;
992 }
993
994 return (int)(*vmi).second.size();
995}
996
997/**
998 * Reduces the number of versions of a particular file stored in the ddb by
999 * throwing away all versions higher than the indicated index.
1000 */
1002set_num_versions(const Filename &name, int num_versions) {
1003 VersionMap::iterator vmi = _versions.find(name);
1004 if (vmi == _versions.end()) {
1005 nassertv(num_versions == 0);
1006 return;
1007 }
1008
1009 VectorHash &vhash = (*vmi).second;
1010
1011 nassertv(num_versions <= (int)vhash.size());
1012 vhash.erase(vhash.begin() + num_versions, vhash.end());
1013}
1014
1015/**
1016 * Returns the version number of this particular file, determined by looking
1017 * up the hash generated from the file. Returns -1 if the version number
1018 * cannot be determined.
1019 */
1021get_version(const Filename &name, const HashVal &hash) const {
1022 VersionMap::const_iterator vmi = _versions.find(name);
1023 if (vmi == _versions.end()) {
1024 downloader_cat.debug()
1025 << "DownloadDb::get_version() - can't find: " << name << endl;
1026 return -1;
1027 }
1028 const VectorHash &vhash = (*vmi).second;
1029 VectorHash::const_iterator i = find(vhash.begin(), vhash.end(), hash);
1030 if (i != vhash.end())
1031 return (i - vhash.begin() + 1);
1032 downloader_cat.debug()
1033 << "DownloadDb::get_version() - can't find hash: " << hash << endl;
1034 return -1;
1035}
1036
1037/**
1038 * Returns the MD5 hash associated with the indicated version of the indicated
1039 * file.
1040 */
1042get_hash(const Filename &name, int version) const {
1043 static HashVal bogus_hash;
1044
1045 VersionMap::const_iterator vmi = _versions.find(name);
1046 if (vmi == _versions.end()) {
1047 downloader_cat.error()
1048 << "DownloadDb::get_hash() - can't find: " << name << endl;
1049 return bogus_hash;
1050 }
1051
1052 const VectorHash &vhash = (*vmi).second;
1053 if (version < 1 || version > (int)vhash.size()) {
1054 downloader_cat.error()
1055 << "DownloadDb::get_hash() - no version " << version
1056 << " for " << name << endl;
1057 return bogus_hash;
1058 }
1059 return vhash[version - 1];
1060}
1061
1062/**
1063 *
1064 */
1065void DownloadDb::
1066write_version_map(StreamWriter &sw) {
1067 VersionMap::iterator vmi;
1068 VectorHash::iterator i;
1069 string name;
1070
1071 sw.add_int32(_versions.size());
1072 for (vmi = _versions.begin(); vmi != _versions.end(); ++vmi) {
1073 name = (*vmi).first;
1074 downloader_cat.spam()
1075 << "DownloadDb::write_version_map() - writing file: "
1076 << name << " of length: " << name.length() << endl;
1077 sw.add_int32(name.length());
1078 sw.append_data(name);
1079 sw.add_int32((*vmi).second.size());
1080 for (i = (*vmi).second.begin(); i != (*vmi).second.end(); ++i) {
1081 // *i will point to a HashVal
1082 (*i).write_stream(sw);
1083 }
1084 }
1085}
1086
1087/**
1088 *
1089 */
1090bool DownloadDb::
1091read_version_map(StreamReader &sr) {
1092 int num_entries = sr.get_int32();
1093 if (sr.get_istream()->fail()) {
1094 return false;
1095 }
1096
1097 for (int i = 0; i < num_entries; i++) {
1098
1099 // Get the file name
1100 string name = sr.get_string32();
1101 downloader_cat.spam()
1102 << "DownloadDb::read_version_map() - name: " << name << endl;
1103
1104 // Get number of hash values for name
1105 int length = sr.get_int32();
1106 if (sr.get_istream()->fail()) {
1107 return false;
1108 }
1109 downloader_cat.spam()
1110 << "DownloadDb::read_version_map() - number of values: " << length
1111 << endl;
1112
1113 for (int j = 0; j < length; j++) {
1114 HashVal hash;
1115 hash.read_stream(sr);
1116 if (sr.get_istream()->fail()) {
1117 return false;
1118 }
1119 add_version(name, hash, j + 1);
1120 }
1121 }
1122 return true;
1123}
1124
1125/**
1126 *
1127 */
1128void DownloadDb::
1129write_version_map(ostream &out) const {
1130 out << "Version Map: " << endl;
1131 VersionMap::const_iterator vmi;
1132 VectorHash::const_iterator i;
1133 for (vmi = _versions.begin(); vmi != _versions.end(); ++vmi) {
1134 out << " " << (*vmi).first << endl;
1135 for (i = (*vmi).second.begin(); i != (*vmi).second.end(); ++i) {
1136 HashVal hash = *i;
1137 out << " " << hash.as_dec() << endl;
1138 }
1139 }
1140 out << endl;
1141}
A class to retrieve the individual data elements previously stored in a Datagram.
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
int32_t get_int32()
Extracts a signed 32-bit integer.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition datagram.h:38
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition datagram.I:94
size_t get_length() const
Returns the number of bytes in the datagram.
Definition datagram.I:335
void add_int32(int32_t value)
Adds a signed 32-bit integer to the datagram.
Definition datagram.I:67
const void * get_data() const
Returns a pointer to the beginning of the datagram's data.
Definition datagram.I:327
int parse_header(Datagram dg)
Verifies magic number, returns the number of multifiles or -1 if invalid.
bool write_bogus_header(StreamWriter &sw)
Writes the bogus header uncompressed with platform- independent byte ordering.
bool write_header(std::ostream &write_stream)
Writes the header uncompressed with platform- independent byte ordering.
int parse_record_header(Datagram dg)
Parses a file record (fr) header and returns the length of the next file record.
const HashVal & get_hash(const Filename &name, int version) const
Returns the MD5 hash associated with the indicated version of the indicated file.
void set_server_multifile_hash(std::string mfname, HashVal val)
Set the hash value of file we are working on.
void create_new_server_db()
Used on the server side makefiles to create a new clean server db.
HashVal get_client_multifile_hash(std::string mfname) const
Return the hash value of the file we are working on.
bool client_multifile_complete(std::string mfname) const
A multifile is complete when it is completely downloaded.
int get_version(const Filename &name, const HashVal &hash) const
Returns the version number of this particular file, determined by looking up the hash generated from ...
bool has_version(const Filename &name) const
Returns true if the indicated file has version information, false otherwise.
void insert_new_version(const Filename &name, const HashVal &hash)
Inserts a new version 1 copy of the file, sliding all the other versions up by one.
HashVal get_server_multifile_hash(std::string mfname) const
Return the hash value of the server file.
DownloadDb()
Primarily used for testing.
void set_client_multifile_hash(std::string mfname, HashVal val)
Set the hash value of file we are working on.
void set_num_versions(const Filename &name, int num_versions)
Reduces the number of versions of a particular file stored in the ddb by throwing away all versions h...
void add_version(const Filename &name, const HashVal &hash, int version)
Appends a new version of the file onto the end of the list, or changes the hash associated with a ver...
int get_num_versions(const Filename &name) const
Returns the number of versions stored for the indicated file.
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
void set_binary()
Indicates that the filename represents a binary file.
Definition filename.I:414
bool open_write(std::ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
Stores a 128-bit value that represents the hashed contents (typically MD5) of a file or buffer.
Definition hashVal.h:31
std::string as_dec() const
Returns the HashVal as a string with four decimal numbers.
Definition hashVal.cxx:108
An in-memory buffer specifically designed for downloading files to memory.
Definition ramfile.h:25
A class to read sequential binary data directly from an istream.
std::string get_string32()
Extracts a variable-length string with a 32-bit length field.
int32_t get_int32()
Extracts a signed 32-bit integer.
size_t extract_bytes(unsigned char *into, size_t size)
Extracts the indicated number of bytes in the stream into the given character buffer.
get_istream
Returns the stream in use.
A StreamWriter object is used to write sequential binary data directly to an ostream.
void append_data(const void *data, size_t size)
Appends some more raw data to the end of the streamWriter.
void add_float64(PN_float64 value)
Adds a 64-bit floating-point number to the stream.
void add_int32(int32_t value)
Adds a signed 32-bit integer to the stream.
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the stream.
A hierarchy of directories and files that appears to be one continuous file system,...
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
std::istream * open_read_file(const Filename &filename, bool auto_unwrap) const
Convenience function; returns a newly allocated istream if the file exists and can be read,...
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
This is our own Panda specialization on the default STL vector.
Definition pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.