Pārlūkot izejas kodu

add file timestamps

David Rose 19 gadi atpakaļ
vecāks
revīzija
87a912818b

+ 35 - 0
dtool/src/dtoolutil/filename.cxx

@@ -1166,6 +1166,41 @@ compare_timestamps(const Filename &other,
   return other_missing_is_old ? 1 : -1;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Filename::get_timestamp
+//       Access: Published
+//  Description: Returns a time_t value that represents the time the
+//               file was last modified, to within whatever precision
+//               the operating system records this information (on a
+//               Windows95 system, for instance, this may only be
+//               accurate to within 2 seconds).
+//
+//               If the timestamp cannot be determined, either because
+//               it is not supported by the operating system or
+//               because there is some error (such as file not found),
+//               returns 0.
+////////////////////////////////////////////////////////////////////
+time_t Filename::
+get_timestamp() const {
+  string os_specific = get_filename_index(0).to_os_specific();
+
+#ifdef WIN32_VC
+  struct _stat this_buf;
+
+  if (_stat(os_specific.c_str(), &this_buf) == 0) {
+    return this_buf.st_mtime;
+  }
+#else  // WIN32_VC
+  struct stat this_buf;
+
+  if (stat(os_specific.c_str(), &this_buf) == 0) {
+    return this_buf.st_mtime;
+  }
+#endif
+
+  return 0;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Filename::resolve_filename
 //       Access: Published

+ 2 - 0
dtool/src/dtoolutil/filename.h

@@ -159,6 +159,8 @@ PUBLISHED:
   int compare_timestamps(const Filename &other,
                          bool this_missing_is_old = true,
                          bool other_missing_is_old = true) const;
+  time_t get_timestamp() const;
+
   bool resolve_filename(const DSearchPath &searchpath,
                         const string &default_extension = string());
   bool make_relative_to(Filename directory, bool allow_backups = true);

+ 34 - 4
panda/src/downloadertools/multify.cxx

@@ -388,6 +388,31 @@ extract_files(int argc, char *argv[]) {
   return true;
 }
 
+const char *
+format_timestamp(time_t timestamp) {
+  static const size_t buffer_size = 512;
+  static char buffer[buffer_size];
+
+  if (timestamp == 0) {
+    // A zero timestamp is a special case.
+    return "  (no date) ";
+  }
+
+  time_t now = time(NULL);
+  struct tm *tm_p = localtime(&timestamp);
+
+  if (timestamp > now || (now - timestamp > 86400 * 365)) {
+    // A timestamp in the future, or more than a year in the past,
+    // gets a year appended.
+    strftime(buffer, buffer_size, "%b %d  %Y", tm_p);
+  } else {
+    // Otherwise, within the past year, show the date and time.
+    strftime(buffer, buffer_size, "%b %d %H:%M", tm_p);
+  }
+
+  return buffer;
+}
+
 bool
 list_files(int argc, char *argv[]) {
   if (!multifile_name.exists()) {
@@ -419,23 +444,28 @@ list_files(int argc, char *argv[]) {
             ratio = (double)internal_length / (double)orig_length;
           }
           if (ratio > 1.0) {
-            printf("%12d worse %c %s\n",
+            printf("%12d worse %c %s %s\n",
                    multifile->get_subfile_length(i),
                    encrypted_symbol,
+                   format_timestamp(multifile->get_subfile_timestamp(i)),
                    subfile_name.c_str());
           } else {
-            printf("%12d  %3.0f%% %c %s\n",
+            printf("%12d  %3.0f%% %c %s %s\n",
                    multifile->get_subfile_length(i),
                    100.0 - ratio * 100.0, encrypted_symbol,
+                   format_timestamp(multifile->get_subfile_timestamp(i)),
                    subfile_name.c_str());
           }
         } else {
-          printf("%12d       %c %s\n", 
+          printf("%12d       %c %s %s\n", 
                  multifile->get_subfile_length(i),
-                 encrypted_symbol, subfile_name.c_str());
+                 encrypted_symbol,
+                 format_timestamp(multifile->get_subfile_timestamp(i)),
+                 subfile_name.c_str());
         }
       }
     }
+    cout << "Last modification " << format_timestamp(multifile->get_timestamp()) << "\n";
     fflush(stdout);
     if (multifile->get_scale_factor() != 1) {
       cout << "Scale factor is " << multifile->get_scale_factor() << "\n";

+ 16 - 0
panda/src/express/multifile.I

@@ -63,6 +63,21 @@ needs_repack() const {
   return _needs_repack || (_scale_factor != _new_scale_factor);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Multifile::get_timestamp
+//       Access: Published
+//  Description: Returns the modification timestamp of the overall
+//               Multifile.  This indicates the most recent date at
+//               which subfiles were added or removed from the
+//               Multifile.  Note that it is logically possible for an
+//               individual subfile to have a more recent timestamp
+//               than the overall timestamp.
+////////////////////////////////////////////////////////////////////
+INLINE time_t Multifile::
+get_timestamp() const {
+  return _timestamp;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Multifile::get_scale_factor
 //       Access: Published
@@ -197,6 +212,7 @@ Subfile() {
   _index_start = 0;
   _data_start = 0;
   _data_length = 0;
+  _timestamp = 0;
   _source = (istream *)NULL;
   _flags = 0;
   _compression_level = 0;

+ 115 - 7
panda/src/express/multifile.cxx

@@ -36,7 +36,9 @@ const size_t Multifile::_header_size = 6;
 // change in the major version is intolerable; while a Multifile with
 // an older minor version may still be read.
 const int Multifile::_current_major_ver = 1;
-const int Multifile::_current_minor_ver = 0;
+
+const int Multifile::_current_minor_ver = 1;
+// Bumped to version 1.1 on 6/8/06 to add timestamps.
 
 // To confirm that the supplied password matches, we write the
 // Mutifile magic header at the beginning of the encrypted stream.
@@ -63,6 +65,7 @@ const size_t Multifile::_encrypt_header_size = 6;
 //   uint32     Scale factor.  This scales all address references within
 //              the file.  Normally 1, this may be set larger to
 //              support Multifiles larger than 4GB.
+//   uint32     An overall modification timestamp for the entire multifile.
 
 //
 // (2) Zero or more index entries, one for each subfile within the
@@ -82,6 +85,7 @@ const size_t Multifile::_encrypt_header_size = 6;
 //               subfile, if it is compressed or encrypted.  This field
 //               is only present if one or both of the SF_compressed
 //               or SF_encrypted bits are set in _flags.
+//   uint32     A modification timestamp for the subfile.
 //   uint16     The length in bytes of the subfile's name.
 //   char[n]    The subfile's name.
 //
@@ -105,6 +109,8 @@ Multifile() {
   _next_index = 0;
   _last_index = 0;
   _needs_repack = false;
+  _timestamp = 0;
+  _timestamp_dirty = false;
   _scale_factor = 1;
   _new_scale_factor = 1;
   _encryption_flag = false;
@@ -162,6 +168,8 @@ open_read(const Filename &multifile_name) {
   if (!fname.open_read(_read_file)) {
     return false;
   }
+  _timestamp = fname.get_timestamp();
+  _timestamp_dirty = true;
   _read = &_read_file;
   _multifile_name = multifile_name;
   return read_index();
@@ -188,6 +196,8 @@ open_write(const Filename &multifile_name) {
   if (!fname.open_write(_write_file, true)) {
     return false;
   }
+  _timestamp = time(NULL);
+  _timestamp_dirty = true;
   _write = &_write_file;
   _multifile_name = multifile_name;
   return true;
@@ -215,6 +225,12 @@ open_read_write(const Filename &multifile_name) {
   if (!fname.open_read_write(_read_write_file)) {
     return false;
   }
+  if (exists) {
+    _timestamp = fname.get_timestamp();
+  } else {
+    _timestamp = time(NULL);
+  }
+  _timestamp_dirty = true;
   _read = &_read_write_file;
   _write = &_read_write_file;
   _multifile_name = multifile_name;
@@ -248,8 +264,11 @@ close() {
   _next_index = 0;
   _last_index = 0;
   _needs_repack = false;
+  _timestamp = 0;
+  _timestamp_dirty = false;
   _scale_factor = 1;
   _new_scale_factor = 1;
+  _encryption_flag = false;
   _file_major_ver = 0;
   _file_minor_ver = 0;
 
@@ -335,6 +354,9 @@ add_subfile(const string &subfile_name, const Filename &filename,
     add_new_subfile(subfile, compression_level);
   }
 
+  _timestamp = time(NULL);
+  _timestamp_dirty = true;
+
   return name;
 }
 
@@ -343,9 +365,9 @@ add_subfile(const string &subfile_name, const Filename &filename,
 //       Access: Published
 //  Description: Adds a file on disk to the subfile.  If a subfile
 //               already exists with the same name, its contents are
-//               compared to the disk file, and it is replaced only if
-//               it is different; otherwise, the multifile is left
-//               unchanged.
+//               compared byte-for-byte to the disk file, and it is
+//               replaced only if it is different; otherwise, the
+//               multifile is left unchanged.
 ////////////////////////////////////////////////////////////////////
 string Multifile::
 update_subfile(const string &subfile_name, const Filename &filename,
@@ -376,6 +398,9 @@ update_subfile(const string &subfile_name, const Filename &filename,
     add_new_subfile(subfile, compression_level);
   }
 
+  _timestamp = time(NULL);
+  _timestamp_dirty = true;
+
   return name;
 }
 
@@ -412,6 +437,13 @@ flush() {
     if (!write_header()) {
       return false;
     }
+
+  } else {
+    if (_file_minor_ver != _current_minor_ver) {
+      // If we *do* have an index already, but this is an old version
+      // multifile, we have to completely rewrite it anyway.
+      return repack();
+    }
   }
 
   nassertr(_write != (ostream *)NULL, false);
@@ -484,8 +516,8 @@ flush() {
     
     // Now go back and fill in the proper addresses for the data start.
     // We didn't do it in the first pass, because we don't really want
-    // to keep all those pile handles open, and so we didn't have to
-    // determine each pile's length ahead of time.
+    // to keep all those file handles open, and so we didn't have to
+    // determine each file's length ahead of time.
     for (pi = _new_subfiles.begin(); pi != _new_subfiles.end(); ++pi) {
       Subfile *subfile = (*pi);
       subfile->rewrite_index_data_start(*_write, this);
@@ -494,6 +526,18 @@ flush() {
     _new_subfiles.clear();
   }
 
+  // Also update the overall timestamp.
+  if (_timestamp_dirty) {
+    nassertr(!_write->fail(), false);
+    static const size_t timestamp_pos = _header_size + 2 + 2 + 4;
+    _write->seekp(timestamp_pos);
+    nassertr(!_write->fail(), false);
+    
+    StreamWriter writer(*_write);
+    writer.add_uint32(_timestamp);
+    _timestamp_dirty = false;
+  }
+
   _write->flush();
   if (!wrote_ok || _write->fail()) {
     express_cat.info()
@@ -725,6 +769,9 @@ remove_subfile(int index) {
   _removed_subfiles.push_back(subfile);
   _subfiles.erase(_subfiles.begin() + index);
 
+  _timestamp = time(NULL);
+  _timestamp_dirty = true;
+
   _needs_repack = true;
 }
 
@@ -756,6 +803,21 @@ get_subfile_length(int index) const {
   return _subfiles[index]->_uncompressed_length;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Multifile::get_subfile_timestamp
+//       Access: Published
+//  Description: Returns the modification time of the nth
+//               subfile.  If this is called on an older .mf file,
+//               which did not store individual timestamps in the
+//               file, this will return the modification time of the
+//               overall multifile.
+////////////////////////////////////////////////////////////////////
+time_t Multifile::
+get_subfile_timestamp(int index) const {
+  nassertr(index >= 0 && index < (int)_subfiles.size(), 0);
+  return _subfiles[index]->_timestamp;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Multifile::is_subfile_compressed
 //       Access: Published
@@ -951,6 +1013,18 @@ compare_subfile(int index, const Filename &filename) {
     return false;
   }
 
+  // Check the file size.
+  in2.seekg(0, ios::end);
+  streampos file_size = in2.tellg();
+
+  if (file_size != (streampos)get_subfile_length(index)) {
+    // The files have different sizes.
+    delete in1;
+    return false;
+  }
+
+  // Check the file data, byte-for-byte.
+  in2.seekg(0);
   int byte1 = in1->get();
   int byte2 = in2.get();
   while (!in1->fail() && !in1->eof() &&
@@ -967,6 +1041,7 @@ compare_subfile(int index, const Filename &filename) {
   delete in1;
 
   nassertr(!failed, false);
+
   return true;
 }
 
@@ -1036,6 +1111,8 @@ read_subfile(int index, string &result) {
 bool Multifile::
 open_read(istream *multifile_stream) {
   close();
+  _timestamp = time(NULL);
+  _timestamp_dirty = true;
   _read = multifile_stream;
   return read_index();
 }
@@ -1053,6 +1130,8 @@ open_read(istream *multifile_stream) {
 bool Multifile::
 open_write(ostream *multifile_stream) {
   close();
+  _timestamp = time(NULL);
+  _timestamp_dirty = true;
   _write = multifile_stream;
   return true;
 }
@@ -1071,6 +1150,8 @@ open_write(ostream *multifile_stream) {
 bool Multifile::
 open_read_write(iostream *multifile_stream) {
   close();
+  _timestamp = time(NULL);
+  _timestamp_dirty = true;
   _read = multifile_stream;
   _write = multifile_stream;
 
@@ -1303,6 +1384,10 @@ read_index() {
     return false;
   }
 
+  if (_file_minor_ver >= 1) {
+    _timestamp = reader.get_uint32();
+    _timestamp_dirty = false;
+  }
 
   // Now read the index out.
   _next_index = _read->tellg();
@@ -1360,6 +1445,9 @@ read_index() {
 ////////////////////////////////////////////////////////////////////
 bool Multifile::
 write_header() {
+  _file_major_ver = _current_major_ver;
+  _file_minor_ver = _current_minor_ver;
+
   nassertr(_write != (ostream *)NULL, false);
   nassertr(_write->tellp() == (streampos)0, false);
   _write->write(_header, _header_size);
@@ -1368,6 +1456,8 @@ write_header() {
   writer.add_int16(_current_minor_ver);
   writer.add_uint32(_scale_factor);
 
+  writer.add_uint32(_timestamp);
+
   _next_index = _write->tellp();
   _next_index = pad_to_streampos(_next_index);
   _last_index = 0;
@@ -1421,6 +1511,12 @@ read_index(istream &read, streampos fpos, Multifile *multifile) {
   } else {
     _uncompressed_length = _data_length;
   }
+  if (multifile->_file_minor_ver < 1) {
+    _timestamp = multifile->get_timestamp();
+  } else {
+    _timestamp = reader.get_uint32();
+  }
+
   size_t name_length = reader.get_uint16();
   if (read.eof() || read.fail()) {
     _flags |= SF_index_invalid;
@@ -1471,6 +1567,7 @@ write_index(ostream &write, streampos fpos, Multifile *multifile) {
   if ((_flags & (SF_compressed | SF_encrypted)) != 0) {
     dg.add_uint32(_uncompressed_length);
   }
+  dg.add_uint32(_timestamp);
   dg.add_uint16(_name.length());
 
   // For no real good reason, we'll invert all the bits in the name.
@@ -1497,7 +1594,7 @@ write_index(ostream &write, streampos fpos, Multifile *multifile) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: Multifile::Subfile::write_index
+//     Function: Multifile::Subfile::write_data
 //       Access: Public
 //  Description: Writes the data record for the Subfile to the
 //               indicated ostream: the actual contents of the
@@ -1621,6 +1718,16 @@ write_data(ostream &write, istream *read, streampos fpos,
   // Subfile.  (In case we are running during repack()).
   _data_start = fpos;
 
+  // Get the modification timestamp for this subfile.  This is read
+  // from the source file, if we have a filename; otherwise, it's the
+  // current time.
+  if (!_source_filename.empty()) {
+    _timestamp = _source_filename.get_timestamp();
+  }
+  if (_timestamp == 0) {
+    _timestamp = time(NULL);
+  }
+
   _source = (istream *)NULL;
   _source_filename = Filename();
   source_file.close();
@@ -1651,6 +1758,7 @@ rewrite_index_data_start(ostream &write, Multifile *multifile) {
   if ((_flags & (SF_compressed | SF_encrypted)) != 0) {
     writer.add_uint32(_uncompressed_length);
   }
+  writer.add_uint32(_timestamp);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 6 - 0
panda/src/express/multifile.h

@@ -54,6 +54,8 @@ PUBLISHED:
   INLINE bool is_write_valid() const;
   INLINE bool needs_repack() const;
 
+  INLINE time_t get_timestamp() const;
+
   void set_scale_factor(size_t scale_factor);
   INLINE size_t get_scale_factor() const;
 
@@ -77,6 +79,7 @@ PUBLISHED:
   void remove_subfile(int index);
   const string &get_subfile_name(int index) const;
   size_t get_subfile_length(int index) const;
+  time_t get_subfile_timestamp(int index) const;
   bool is_subfile_compressed(int index) const;
   bool is_subfile_encrypted(int index) const;
   size_t get_subfile_internal_length(int index) const;
@@ -131,6 +134,7 @@ private:
     streampos _data_start;
     size_t _data_length;
     size_t _uncompressed_length;
+    time_t _timestamp;
     istream *_source;
     Filename _source_filename;
     int _flags;
@@ -162,6 +166,8 @@ private:
   streampos _last_index;
 
   bool _needs_repack;
+  time_t _timestamp;
+  bool _timestamp_dirty;
   size_t _scale_factor;
   size_t _new_scale_factor;
 

+ 19 - 0
panda/src/express/virtualFile.cxx

@@ -177,6 +177,25 @@ get_file_size(istream *stream) const {
   return 0;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: VirtualFile::get_timestamp
+//       Access: Published, Virtual
+//  Description: Returns a time_t value that represents the time the
+//               file was last modified, to within whatever precision
+//               the operating system records this information (on a
+//               Windows95 system, for instance, this may only be
+//               accurate to within 2 seconds).
+//
+//               If the timestamp cannot be determined, either because
+//               it is not supported by the operating system or
+//               because there is some error (such as file not found),
+//               returns 0.
+////////////////////////////////////////////////////////////////////
+time_t VirtualFile::
+get_timestamp() const {
+  return 0;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: VirtualFile::close_read_file
 //       Access: Public

+ 1 - 0
panda/src/express/virtualFile.h

@@ -58,6 +58,7 @@ PUBLISHED:
   virtual istream *open_read_file(bool auto_unwrap) const;
   void close_read_file(istream *stream) const;
   virtual streampos get_file_size(istream *stream) const;
+  virtual time_t get_timestamp() const;
 
 public:
   INLINE void set_original_filename(const Filename &filename);

+ 1 - 0
panda/src/express/virtualFileMount.h

@@ -53,6 +53,7 @@ public:
   virtual istream *open_read_file(const Filename &file) const=0;
   void close_read_file(istream *stream) const;
   virtual streampos get_file_size(const Filename &file, istream *stream) const=0;
+  virtual time_t get_timestamp(const Filename &file) const=0;
 
   virtual bool scan_directory(vector_string &contents, 
                               const Filename &dir) const=0;

+ 23 - 0
panda/src/express/virtualFileMountMultifile.cxx

@@ -102,6 +102,29 @@ get_file_size(const Filename &file, istream *) const {
   return _multifile->get_subfile_length(subfile_index);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: VirtualFileMountMultifile::get_timestamp
+//       Access: Published, Virtual
+//  Description: Returns a time_t value that represents the time the
+//               file was last modified, to within whatever precision
+//               the operating system records this information (on a
+//               Windows95 system, for instance, this may only be
+//               accurate to within 2 seconds).
+//
+//               If the timestamp cannot be determined, either because
+//               it is not supported by the operating system or
+//               because there is some error (such as file not found),
+//               returns 0.
+////////////////////////////////////////////////////////////////////
+time_t VirtualFileMountMultifile::
+get_timestamp(const Filename &file) const {
+  int subfile_index = _multifile->find_subfile(file);
+  if (subfile_index < 0) {
+    return 0;
+  }
+  return _multifile->get_subfile_timestamp(subfile_index);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: VirtualFileMountMultifile::scan_directory
 //       Access: Public, Virtual

+ 1 - 0
panda/src/express/virtualFileMountMultifile.h

@@ -46,6 +46,7 @@ public:
 
   virtual istream *open_read_file(const Filename &file) const;
   virtual streampos get_file_size(const Filename &file, istream *stream) const;
+  virtual time_t get_timestamp(const Filename &file) const;
 
   virtual bool scan_directory(vector_string &contents, 
                               const Filename &dir) const;

+ 20 - 0
panda/src/express/virtualFileMountSystem.cxx

@@ -141,6 +141,26 @@ get_file_size(const Filename &, istream *stream) const {
   return size;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: VirtualFileMountSystem::get_timestamp
+//       Access: Published, Virtual
+//  Description: Returns a time_t value that represents the time the
+//               file was last modified, to within whatever precision
+//               the operating system records this information (on a
+//               Windows95 system, for instance, this may only be
+//               accurate to within 2 seconds).
+//
+//               If the timestamp cannot be determined, either because
+//               it is not supported by the operating system or
+//               because there is some error (such as file not found),
+//               returns 0.
+////////////////////////////////////////////////////////////////////
+time_t VirtualFileMountSystem::
+get_timestamp(const Filename &file) const {
+  Filename pathname(_physical_filename, file);
+  return pathname.get_timestamp();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: VirtualFileMountSystem::scan_directory
 //       Access: Public, Virtual

+ 1 - 0
panda/src/express/virtualFileMountSystem.h

@@ -42,6 +42,7 @@ public:
 
   virtual istream *open_read_file(const Filename &file) const;
   virtual streampos get_file_size(const Filename &file, istream *stream) const;
+  virtual time_t get_timestamp(const Filename &file) const;
 
   virtual bool scan_directory(vector_string &contents, 
                               const Filename &dir) const;

+ 19 - 0
panda/src/express/virtualFileSimple.cxx

@@ -124,6 +124,25 @@ get_file_size(istream *stream) const {
   return _mount->get_file_size(_local_filename, stream);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: VirtualFileSimple::get_timestamp
+//       Access: Published, Virtual
+//  Description: Returns a time_t value that represents the time the
+//               file was last modified, to within whatever precision
+//               the operating system records this information (on a
+//               Windows95 system, for instance, this may only be
+//               accurate to within 2 seconds).
+//
+//               If the timestamp cannot be determined, either because
+//               it is not supported by the operating system or
+//               because there is some error (such as file not found),
+//               returns 0.
+////////////////////////////////////////////////////////////////////
+time_t VirtualFileSimple::
+get_timestamp() const {
+  return _mount->get_timestamp(_local_filename);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: VirtualFileSimple::scan_local_directory
 //       Access: Protected, Virtual

+ 1 - 0
panda/src/express/virtualFileSimple.h

@@ -45,6 +45,7 @@ public:
 
   virtual istream *open_read_file(bool auto_unwrap) const;
   virtual streampos get_file_size(istream *stream) const;
+  virtual time_t get_timestamp() const;
 
 protected:
   virtual bool scan_local_directory(VirtualFileList *file_list,