Browse Source

better download rules

David Rose 16 years ago
parent
commit
7293844e4a

+ 3 - 21
direct/src/plugin/load_plugin.cxx

@@ -123,7 +123,8 @@ static void unload_dso();
 //               path.
 ////////////////////////////////////////////////////////////////////
 bool
-load_plugin(const string &p3d_plugin_filename) {
+load_plugin(const string &p3d_plugin_filename, 
+            const string &contents_filename) {
   string filename = p3d_plugin_filename;
   if (filename.empty()) {
     // Look for the plugin along the path.
@@ -292,26 +293,7 @@ load_plugin(const string &p3d_plugin_filename) {
   // Successfully loaded.
   plugin_loaded = true;
 
-#ifdef P3D_PLUGIN_LOGFILE2
-  string logfilename = P3D_PLUGIN_LOGFILE2;
-#else
-  string logfilename;
-#endif  // P3D_PLUGIN_LOGFILE2
-
-  if (logfilename.empty()) {
-#ifdef _WIN32
-    static const size_t buffer_size = 4096;
-    char buffer[buffer_size];
-    if (GetTempPath(buffer_size, buffer) != 0) {
-      logfilename = buffer;
-      logfilename += "panda3d.2.log";
-    }
-#else
-    logfilename = "/tmp/panda3d.2.log";
-#endif  // _WIN32
-  }
-
-  if (!P3D_initialize(P3D_API_VERSION, logfilename.c_str())) {
+  if (!P3D_initialize(P3D_API_VERSION, contents_filename.c_str())) {
     // Oops, failure to initialize.
     cerr << "Failed to initialize plugin (wrong API version?)\n";
     unload_plugin();

+ 2 - 1
direct/src/plugin/load_plugin.h

@@ -58,7 +58,8 @@ extern P3D_instance_feed_url_stream_func *P3D_instance_feed_url_stream;
 extern P3D_instance_handle_event_func *P3D_instance_handle_event;
 
 string get_plugin_basename();
-bool load_plugin(const string &p3d_plugin_filename);
+bool load_plugin(const string &p3d_plugin_filename,
+                 const string &contents_filename);
 void unload_plugin();
 bool is_plugin_loaded();
 

+ 6 - 6
direct/src/plugin/make_contents.py

@@ -54,9 +54,9 @@ class ContentsMaker:
         print >> f, '<?xml version="1.0" ?>'
         print >> f, ''
         print >> f, '<contents>'
-        for packageName, packageVersion, packagePlatform, file in self.packages:
-            print >> f, '  <package name="%s" version="%s" platform="%s" %s />' % (
-                packageName, packageVersion, packagePlatform or '', file.getParams())
+        for packageName, packagePlatform, packageVersion, file in self.packages:
+            print >> f, '  <package name="%s" platform="%s" version="%s" %s />' % (
+                packageName, packagePlatform or '', packageVersion, file.getParams())
         print >> f, '</contents>'
         f.close()
 
@@ -93,14 +93,14 @@ class ContentsMaker:
                 file = FileSpec(localpath + xml,
                                 Filename(self.stageDir, localpath + xml))
                 print file.filename
-                self.packages.append((packageName, packageVersion, packagePlatform, file))
+                self.packages.append((packageName, packagePlatform, packageVersion, file))
 
             if localpath.count('/') == 3:
-                packageName, packageVersion, packagePlatform, junk = localpath.split('/')
+                packageName, packagePlatform, packageVersion, junk = localpath.split('/')
                 file = FileSpec(localpath + xml,
                                 Filename(self.stageDir, localpath + xml))
                 print file.filename
-                self.packages.append((packageName, packageVersion, packagePlatform, file))
+                self.packages.append((packageName, packagePlatform, packageVersion, file))
         
                 
 def makeContents(args):

+ 6 - 5
direct/src/plugin/make_package.py

@@ -77,8 +77,9 @@ class PackageMaker:
         self.packageStageDir = Filename(self.stageDir, '%s/%s' % (self.packageName, self.packageVersion))
 
         if self.packagePlatform:
-            self.packageFullname += '_%s' % (self.packagePlatform)
-            self.packageStageDir = Filename(self.packageStageDir, self.packagePlatform)
+            self.packageFullname = '%s_%s_%s' % (
+                self.packageName, self.packagePlatform, self.packageVersion)
+            self.packageStageDir = Filename(self.stageDir, '%s/%s/%s' % (self.packageName, self.packagePlatform, self.packageVersion))
 
         Filename(self.packageStageDir, '.').makeDir()
         self.cleanDir(self.packageStageDir)
@@ -121,7 +122,7 @@ class PackageMaker:
         f = open(descFilePathname.toOsSpecific(), 'w')
         print >> f, '<?xml version="1.0" ?>'
         print >> f, ''
-        print >> f, '<package name="%s" version="%s" platform="%s">' % (self.packageName, self.packageVersion, self.packagePlatform or '')
+        print >> f, '<package name="%s" platform="%s" version="%s">' % (self.packageName, self.packagePlatform or '', self.packageVersion)
         print >> f, '  <uncompressed_archive %s />' % (uncompressedArchive.getParams())
         print >> f, '  <compressed_archive %s />' % (compressedArchive.getParams())
         for file in self.components:
@@ -209,9 +210,9 @@ def makePackage(args):
             if len(tokens) >= 1:
                 pm.packageName = tokens[0]
             if len(tokens) >= 2:
-                pm.packageVersion = tokens[1]
-            if len(tokens) >= 3:
                 pm.packagePlatform = tokens[2]
+            if len(tokens) >= 3:
+                pm.packageVersion = tokens[1]
             if len(tokens) >= 4:
                 raise ArgumentError, 'Too many tokens in string: %s' % (value)
             

+ 4 - 4
direct/src/plugin/mkdir_complete.cxx

@@ -85,7 +85,7 @@ mkdir_complete(const string &dirname, ostream &logfile) {
   return false;
 
 #else  //_WIN32
-  if (mkdir(dirname.c_str(), 0777) == 0) {
+  if (mkdir(dirname.c_str(), 0755) == 0) {
     // Success!
     return true;
   }
@@ -101,7 +101,7 @@ mkdir_complete(const string &dirname, ostream &logfile) {
     string parent = get_dirname(dirname);
     if (!parent.empty() && mkdir_complete(parent, logfile)) {
       // Parent successfully created.  Try again to make the child.
-      if (mkdir(dirname.c_str(), 0777) == 0) {
+      if (mkdir(dirname.c_str(), 0755) == 0) {
         // Got it!
         return true;
       }
@@ -147,13 +147,13 @@ mkfile_complete(const string &filename, ostream &logfile) {
   return true;
 
 #else  // _WIN32
-  int fd = creat(filename.c_str(), 0777);
+  int fd = creat(filename.c_str(), 0755);
   if (fd == -1) {
     // Try to make the parent directory first.
     string parent = get_dirname(filename);
     if (!parent.empty() && mkdir_complete(parent, logfile)) {
       // Parent successfully created.  Try again to make the file.
-      fd = creat(filename.c_str(), 0777);
+      fd = creat(filename.c_str(), 0755);
     }
     if (fd == -1) {
       logfile

+ 3 - 1
direct/src/plugin/p3dDownload.cxx

@@ -131,6 +131,8 @@ download_progress() {
   if (now != _last_reported_time || true) {
     _last_reported_time = now;
   }
+  nout << "Downloading " << get_url() << ": " 
+       << int(get_download_progress() * 1000.0) / 10.0 << "\n";
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -143,6 +145,6 @@ download_progress() {
 ////////////////////////////////////////////////////////////////////
 void P3DDownload::
 download_finished(bool success) {
-  nout << "Downloading " << get_url() << ": " 
+  nout << "Downloaded " << get_url() << ": " 
        << int(get_download_progress() * 1000.0) / 10.0 << "\n";
 }

+ 36 - 15
direct/src/plugin/p3dFileParams.cxx

@@ -25,15 +25,47 @@ P3DFileParams() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: P3DFileParams::Constructor
+//     Function: P3DFileParams::Copy Constructor
 //       Access: Public
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 P3DFileParams::
-P3DFileParams(const string &p3d_filename, 
-              const P3D_token tokens[], size_t num_tokens) :
-  _p3d_filename(p3d_filename)
+P3DFileParams(const P3DFileParams &copy) :
+  _p3d_filename(copy._p3d_filename),
+  _tokens(copy._tokens)
 {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DFileParams::Copy Assignment
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void P3DFileParams::
+operator = (const P3DFileParams &other) {
+  _p3d_filename = other._p3d_filename;
+  _tokens = other._tokens;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DFileParams::set_p3d_filename
+//       Access: Public
+//  Description: Specifies the file that contains the instance data.
+////////////////////////////////////////////////////////////////////
+void P3DFileParams::
+set_p3d_filename(const string &p3d_filename) {
+  _p3d_filename = p3d_filename;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DFileParams::set_tokens
+//       Access: Public
+//  Description: Specifies the tokens associated with the instance.
+////////////////////////////////////////////////////////////////////
+void P3DFileParams::
+set_tokens(const P3D_token tokens[], size_t num_tokens) {
+  _tokens.clear();
+
   for (size_t i = 0; i < num_tokens; ++i) {
     Token token;
     if (tokens[i]._keyword != NULL) {
@@ -50,17 +82,6 @@ P3DFileParams(const string &p3d_filename,
   }
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: P3DFileParams::Copy Assignment
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-void P3DFileParams::
-operator = (const P3DFileParams &other) {
-  _p3d_filename = other._p3d_filename;
-  _tokens = other._tokens;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DFileParams::lookup_token
 //       Access: Public

+ 4 - 3
direct/src/plugin/p3dFileParams.h

@@ -27,11 +27,12 @@
 class P3DFileParams {
 public:
   P3DFileParams();
-  P3DFileParams(const string &p3d_filename, 
-                const P3D_token tokens[], size_t num_tokens);
-
+  P3DFileParams(const P3DFileParams &copy);
   void operator = (const P3DFileParams &other);
 
+  void set_p3d_filename(const string &p3d_filename);
+  void set_tokens(const P3D_token tokens[], size_t num_tokens);
+
   inline const string &get_p3d_filename() const;
   string lookup_token(const string &keyword) const;
   bool has_token(const string &keyword) const;

+ 10 - 13
direct/src/plugin/p3dInstance.cxx

@@ -49,7 +49,8 @@ typedef P3DSplashWindow SplashWindowType;
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 P3DInstance::
-P3DInstance(P3D_request_ready_func *func, void *user_data) :
+P3DInstance(P3D_request_ready_func *func, 
+            const P3D_token tokens[], size_t num_tokens, void *user_data) :
   _func(func)
 {
   _browser_script_object = NULL;
@@ -59,6 +60,8 @@ P3DInstance(P3D_request_ready_func *func, void *user_data) :
   _got_fparams = false;
   _got_wparams = false;
 
+  _fparams.set_tokens(tokens, num_tokens);
+
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   _instance_id = inst_mgr->get_unique_id();
 
@@ -137,16 +140,17 @@ P3DInstance::
   // download is still running?  Who will crash when this happens?
 }
 
+
 ////////////////////////////////////////////////////////////////////
-//     Function: P3DInstance::set_fparams
+//     Function: P3DInstance::set_p3d_filename
 //       Access: Public
-//  Description: Sets up the initial file parameters for the instance.
+//  Description: Specifies the file that contains the instance data.
 //               Normally this is only called once.
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
-set_fparams(const P3DFileParams &fparams) {
+set_p3d_filename(const string &p3d_filename) {
   _got_fparams = true;
-  _fparams = fparams;
+  _fparams.set_p3d_filename(p3d_filename);
 
   // This also sets up some internal data based on the contents of the
   // above file and the associated tokens.
@@ -176,13 +180,6 @@ set_fparams(const P3DFileParams &fparams) {
     P3D_object *result = P3D_OBJECT_EVAL(_browser_script_object, expression.c_str());
     P3D_OBJECT_XDECREF(result);
   }
-
-  // Maybe create the splash window.
-  if (!_instance_window_opened && _got_wparams) {
-    if (_splash_window == NULL) {
-      make_splash_window();
-    }
-  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -198,7 +195,7 @@ set_wparams(const P3DWindowParams &wparams) {
   _wparams = wparams;
 
   // Update or create the splash window.
-  if (!_instance_window_opened && _got_fparams) {
+  if (!_instance_window_opened) {
     if (_splash_window == NULL) {
       make_splash_window();
     }

+ 3 - 2
direct/src/plugin/p3dInstance.h

@@ -44,10 +44,11 @@ class P3DToplevelObject;
 ////////////////////////////////////////////////////////////////////
 class P3DInstance : public P3D_instance, public P3DReferenceCount {
 public:
-  P3DInstance(P3D_request_ready_func *func, void *user_data);
+  P3DInstance(P3D_request_ready_func *func, 
+              const P3D_token tokens[], size_t num_tokens, void *user_data);
   ~P3DInstance();
 
-  void set_fparams(const P3DFileParams &fparams);
+  void set_p3d_filename(const string &p3d_filename);
   inline const P3DFileParams &get_fparams() const;
 
   void set_wparams(const P3DWindowParams &wparams);

+ 11 - 0
direct/src/plugin/p3dInstanceManager.I

@@ -60,6 +60,17 @@ get_platform() const {
   return _platform;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::has_contents_file
+//       Access: Public
+//  Description: Returns true if a contents.xml file has been
+//               successfully read, false otherwise.
+////////////////////////////////////////////////////////////////////
+inline bool P3DInstanceManager::
+has_contents_file() const {
+  return (_xcontents != NULL);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstanceManager::get_num_instances
 //       Access: Public

+ 146 - 21
direct/src/plugin/p3dInstanceManager.cxx

@@ -22,6 +22,9 @@
 #include "p3dNoneObject.h"
 #include "p3dBoolObject.h"
 #include "find_root_dir.h"
+#include "mkdir_complete.h"
+#include "fileSpec.h"
+#include "get_tinyxml.h"
 
 #ifdef _WIN32
 #include <shlobj.h>
@@ -40,6 +43,7 @@ P3DInstanceManager::
 P3DInstanceManager() {
   _is_initialized = false;
   _unique_id = 0;
+  _xcontents = NULL;
 
   _notify_thread_continue = false;
   _started_notify_thread = false;
@@ -77,6 +81,10 @@ P3DInstanceManager::
     _started_notify_thread = false;
   }
 
+  if (_xcontents != NULL) {
+    delete _xcontents;
+  }
+
   assert(_instances.empty());
   assert(_sessions.empty());
 
@@ -112,13 +120,14 @@ P3DInstanceManager::
 //               redownloaded.
 ////////////////////////////////////////////////////////////////////
 bool P3DInstanceManager::
-initialize() {
+initialize(const string &contents_filename) {
   _root_dir = find_root_dir();
   _download_url = P3D_PLUGIN_DOWNLOAD;
   _platform = P3D_PLUGIN_PLATFORM;
 
-  nout << "_root_dir = " << _root_dir << ", download = " 
-       << _download_url << "\n";
+  nout << "_root_dir = " << _root_dir
+       << ", download = " << _download_url
+       << ", contents = " << contents_filename << "\n";
 
   if (_root_dir.empty()) {
     nout << "Could not find root directory.\n";
@@ -127,6 +136,47 @@ initialize() {
 
   _is_initialized = true;
 
+  // Attempt to read the supplied contents.xml file.
+  if (!contents_filename.empty()) {
+    if (!read_contents_file(contents_filename)) {
+      nout << "Couldn't read " << contents_filename << "\n";
+    }
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::read_contents_file
+//       Access: Public
+//  Description: Reads the contents.xml file in the indicated
+//               filename.  On success, copies the contents.xml file
+//               into the standard location.
+//
+//               Returns true on success, false on failure.
+////////////////////////////////////////////////////////////////////
+bool P3DInstanceManager::
+read_contents_file(const string &contents_filename) {
+  TiXmlDocument doc(contents_filename.c_str());
+  if (!doc.LoadFile()) {
+    return false;
+  }
+
+  TiXmlElement *xcontents = doc.FirstChildElement("contents");
+  if (xcontents == NULL) {
+    return false;
+  }
+
+  if (_xcontents != NULL) {
+    delete _xcontents;
+  }
+  _xcontents = (TiXmlElement *)xcontents->Clone();
+
+  string standard_filename = _root_dir + "/contents.xml";
+  if (standard_filename != contents_filename) {
+    copy_file(contents_filename, standard_filename);
+  }
+
   return true;
 }
 
@@ -137,8 +187,10 @@ initialize() {
 //               indicated startup information.
 ////////////////////////////////////////////////////////////////////
 P3DInstance *P3DInstanceManager::
-create_instance(P3D_request_ready_func *func, void *user_data) {
-  P3DInstance *inst = new P3DInstance(func, user_data);
+create_instance(P3D_request_ready_func *func, 
+                const P3D_token tokens[], size_t num_tokens, 
+                void *user_data) {
+  P3DInstance *inst = new P3DInstance(func, tokens, num_tokens, user_data);
   inst->ref();
   _instances.insert(inst);
 
@@ -154,13 +206,12 @@ create_instance(P3D_request_ready_func *func, void *user_data) {
 //               particular instance.
 ////////////////////////////////////////////////////////////////////
 bool P3DInstanceManager::
-start_instance(P3DInstance *inst, const string &p3d_filename,
-               const P3D_token tokens[], size_t num_tokens) {
+start_instance(P3DInstance *inst, const string &p3d_filename) {
   if (inst->is_started()) {
     nout << "Instance started twice: " << inst << "\n";
     return false;
   }
-  inst->set_fparams(P3DFileParams(p3d_filename, tokens, num_tokens));
+  inst->set_p3d_filename(p3d_filename);
 
   P3DSession *session;
   Sessions::iterator si = _sessions.find(inst->get_session_key());
@@ -192,16 +243,17 @@ finish_instance(P3DInstance *inst) {
   _instances.erase(ii);
 
   Sessions::iterator si = _sessions.find(inst->get_session_key());
-  assert(si != _sessions.end());
-  P3DSession *session = (*si).second;
-  session->terminate_instance(inst);
-
-  // If that was the last instance in this session, terminate the
-  // session.
-  if (session->get_num_instances() == 0) {
-    _sessions.erase(session->get_session_key());
-    session->shutdown();
-    unref_delete(session);
+  if (si != _sessions.end()) {
+    P3DSession *session = (*si).second;
+    session->terminate_instance(inst);
+
+    // If that was the last instance in this session, terminate the
+    // session.
+    if (session->get_num_instances() == 0) {
+      _sessions.erase(session->get_session_key());
+      session->shutdown();
+      unref_delete(session);
+    }
   }
 
   unref_delete(inst);
@@ -284,20 +336,61 @@ P3DPackage *P3DInstanceManager::
 get_package(const string &package_name, const string &package_version, 
             const string &package_display_name) {
   string package_platform = get_platform();
-  string key = package_name + "_" + package_version + "_" + package_platform;
+  string key = package_name + "_" + package_platform + "_" + package_version;
   Packages::iterator pi = _packages.find(key);
   if (pi != _packages.end()) {
     return (*pi).second;
   }
 
-  P3DPackage *package = new P3DPackage(package_name, package_version, 
-                                       package_platform, package_display_name);
+  P3DPackage *package = new P3DPackage(package_name, package_platform, 
+                                       package_version, package_display_name);
   bool inserted = _packages.insert(Packages::value_type(key, package)).second;
   assert(inserted);
 
   return package;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::get_package_desc_file
+//       Access: Public
+//  Description: Fills the indicated FileSpec with the hash
+//               information for the package's desc file.  Returns
+//               true if successful, false if the package is unknown.
+//               This requires has_contents_file() to return true in
+//               order to be successful.
+////////////////////////////////////////////////////////////////////
+bool P3DInstanceManager::
+get_package_desc_file(FileSpec &desc_file,
+                      const string &package_name,
+                      const string &package_version) {
+  if (_xcontents == NULL) {
+    return false;
+  }
+
+  string package_platform = get_platform();
+
+  // Scan the contents data for the indicated package.
+  TiXmlElement *xpackage = _xcontents->FirstChildElement("package");
+  while (xpackage != NULL) {
+    const char *name = xpackage->Attribute("name");
+    const char *platform = xpackage->Attribute("platform");
+    const char *version = xpackage->Attribute("version");
+    if (name != NULL && platform != NULL && version != NULL &&
+        package_name == name && 
+        package_platform == platform &&
+        package_version == version) {
+      // Here's the matching package definition.
+      desc_file.load_xml(xpackage);
+      return true;
+    }
+
+    xpackage = xpackage->NextSiblingElement("package");
+  }
+
+  // Couldn't find the named package.
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstanceManager::get_unique_id
 //       Access: Public
@@ -384,6 +477,38 @@ delete_global_ptr() {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: P3DInstanceManager::copy_file
+//       Access: Private
+//  Description: Copies the data in the file named by from_filename
+//               into the file named by to_filename.
+////////////////////////////////////////////////////////////////////
+bool P3DInstanceManager::
+copy_file(const string &from_filename, const string &to_filename) {
+  ifstream in(from_filename.c_str(), ios::in | ios::binary);
+  ofstream out(to_filename.c_str(), ios::out | ios::binary);
+        
+  static const size_t buffer_size = 4096;
+  char buffer[buffer_size];
+  
+  in.read(buffer, buffer_size);
+  size_t count = in.gcount();
+  while (count != 0) {
+    out.write(buffer, count);
+    if (out.fail()) {
+      return false;
+    }
+    in.read(buffer, buffer_size);
+    count = in.gcount();
+  }
+
+  if (!in.eof()) {
+    return false;
+  }
+
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: P3DInstanceManager::nt_thread_run
 //       Access: Private

+ 18 - 4
direct/src/plugin/p3dInstanceManager.h

@@ -25,6 +25,8 @@
 class P3DInstance;
 class P3DSession;
 class P3DPackage;
+class FileSpec;
+class TiXmlElement;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : P3DInstanceManager
@@ -37,7 +39,7 @@ private:
   ~P3DInstanceManager();
 
 public:
-  bool initialize();
+  bool initialize(const string &contents_xml_filename);
 
   inline bool is_initialized() const;
 
@@ -45,11 +47,15 @@ public:
   inline const string &get_download_url() const;
   inline const string &get_platform() const;
 
+  inline bool has_contents_file() const;
+  bool read_contents_file(const string &contents_filename);
+
   P3DInstance *
-  create_instance(P3D_request_ready_func *func, void *user_data);
+  create_instance(P3D_request_ready_func *func, 
+                  const P3D_token tokens[], size_t num_tokens, 
+                  void *user_data);
 
-  bool start_instance(P3DInstance *inst, const string &p3d_filename,
-                      const P3D_token tokens[], size_t num_tokens);
+  bool start_instance(P3DInstance *inst, const string &p3d_filename);
   void finish_instance(P3DInstance *inst);
 
   P3DInstance *validate_instance(P3D_instance *instance);
@@ -60,6 +66,9 @@ public:
   P3DPackage *get_package(const string &package_name, 
                           const string &package_version,
                           const string &package_display_name);
+  bool get_package_desc_file(FileSpec &desc_file, 
+                             const string &package_name,
+                             const string &package_version);
 
   inline int get_num_instances() const;
 
@@ -75,6 +84,9 @@ public:
   static P3DInstanceManager *get_global_ptr();
   static void delete_global_ptr();
 
+private:
+  bool copy_file(const string &from_filename, const string &to_filename);
+
 private:
   // The notify thread.  This thread runs only for the purpose of
   // generating asynchronous notifications of requests, to callers who
@@ -88,6 +100,8 @@ private:
   string _download_url;
   string _platform;
 
+  TiXmlElement *_xcontents;
+
   P3D_object *_undefined_object;
   P3D_object *_none_object;
   P3D_object *_true_object;

+ 103 - 22
direct/src/plugin/p3dPackage.cxx

@@ -35,23 +35,25 @@ static const double extract_portion = 0.05;
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 P3DPackage::
-P3DPackage(const string &package_name, const string &package_version,
+P3DPackage(const string &package_name,
            const string &package_platform,
+           const string &package_version,
            const string &package_display_name) :
   _package_name(package_name),
-  _package_version(package_version),
   _package_platform(package_platform),
+  _package_version(package_version),
   _package_display_name(package_display_name)
 {
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
 
-  _package_fullname = _package_name + "_" + _package_version;
+  _package_fullname = _package_name;
   _package_dir = inst_mgr->get_root_dir() + string("/") + _package_name;
-
   if (!_package_platform.empty()) {
-    _package_fullname += "_" + _package_platform;
-    _package_dir += "/" + _package_platform;
+    _package_fullname += string("_") + _package_platform;
+    _package_dir += string("/") + _package_platform;
   }
+  _package_fullname += string("_") + _package_version;
+  _package_dir += string("/") + _package_version;
 
   _ready = false;
   _failed = false;
@@ -59,7 +61,6 @@ P3DPackage(const string &package_name, const string &package_version,
   _partial_download = false;
 
   // Ensure the package directory exists; create it if it does not.
-  _package_dir += string("/") + _package_version;
   mkdir_complete(_package_dir, nout);
 
   _desc_file_basename = _package_fullname + ".xml";
@@ -186,20 +187,68 @@ begin_download() {
     return;
   }
 
-  // TODO: we should check the desc file for updates with the server.
-  // Perhaps this should be done in a parent class.
+  download_contents_file();
+}
 
-  // TODO: if the desc file exists, and is consistent with the server
-  // contents file, don't re-download it.
-  /*
-  // Load the desc file, if it exists.
-  TiXmlDocument doc(_desc_file_pathname.c_str());
-  if (!doc.LoadFile()) {
+////////////////////////////////////////////////////////////////////
+//     Function: P3DPackage::download_contents_file
+//       Access: Private
+//  Description: Starts downloading the root-level contents.xml file.
+//               This is only done for the first package, and only if
+//               the instance manager doesn't have the file already.
+////////////////////////////////////////////////////////////////////
+void P3DPackage::
+download_contents_file() {
+  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+
+  if (inst_mgr->has_contents_file()) {
+    // We've already got a contents.xml file; go straight to the
+    // package desc file.
     download_desc_file();
-  } else {
-    got_desc_file(&doc, false);
+    return;
+  }
+
+  string url = inst_mgr->get_download_url();
+  url += "contents.xml";
+
+  // Download contents.xml to a temporary filename first, in case
+  // multiple packages are downloading it simultaneously.
+  _contents_file_pathname = tempnam(NULL, "p3d_");
+
+  cerr << "starting contents download\n";
+  start_download(DT_contents_file, url, _contents_file_pathname, false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: P3DPackage::contents_file_download_finished
+//       Access: Private
+//  Description: Called when the desc file has been fully downloaded.
+////////////////////////////////////////////////////////////////////
+void P3DPackage::
+contents_file_download_finished(bool success) {
+  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+  cerr << "done contents download: " << success 
+       << ", has_contents = " << inst_mgr->has_contents_file()
+       << "\n";
+
+  if (!inst_mgr->has_contents_file()) {
+    if (!success || !inst_mgr->read_contents_file(_contents_file_pathname)) {
+      nout << "Couldn't read " << _contents_file_pathname << "\n";
+
+      // Maybe we can read an already-downloaded contents.xml file.
+      string standard_filename = inst_mgr->get_root_dir() + "/contents.xml";
+      if (!inst_mgr->read_contents_file(standard_filename)) {
+        // Couldn't even read that.  Fail.
+        report_done(false);
+        unlink(_contents_file_pathname.c_str());
+        return;
+      }
+    }
   }
-  */
+    
+  // The file is correctly installed by now; we can remove the
+  // temporary file.
+  unlink(_contents_file_pathname.c_str());
 
   download_desc_file();
 }
@@ -212,12 +261,40 @@ begin_download() {
 void P3DPackage::
 download_desc_file() {
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
+
+  // Attempt to check the desc file for freshness.  If it already
+  // exists, and is consistent with the server contents file, we don't
+  // need to re-download it.
+  string root_dir = inst_mgr->get_root_dir();
+  FileSpec desc_file;
+  if (!inst_mgr->get_package_desc_file(desc_file, _package_name, _package_version)) {
+    nout << "Couldn't find package " << _package_fullname
+         << " in contents file.\n";
+
+  } else if (desc_file.get_pathname(root_dir) != _desc_file_pathname) {
+    nout << "Wrong pathname for desc file: " 
+         << desc_file.get_pathname(root_dir) 
+         << " instead of " << _desc_file_pathname << "\n";
+
+  } else if (!desc_file.quick_verify(root_dir)) {
+    nout << _desc_file_pathname << " is stale.\n";
+
+  } else {
+    // The desc file is current.  Attempt to read it.
+    TiXmlDocument doc(_desc_file_pathname.c_str());
+    if (doc.LoadFile()) {
+      got_desc_file(&doc, false);
+      return;
+    }
+  }
+
+  // The desc file is not current.  Go download it.
   string url = inst_mgr->get_download_url();
-  url += _package_name + "/" + _package_version;
+  url += _package_name;
   if (!_package_platform.empty()) {
     url += "/" + _package_platform;
   }
-
+  url += "/" + _package_version;
   url += "/" + _desc_file_basename;
 
   start_download(DT_desc_file, url, _desc_file_pathname, false);
@@ -321,11 +398,11 @@ void P3DPackage::
 download_compressed_archive(bool allow_partial) {
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   string url = inst_mgr->get_download_url();
-  url += _package_name + "/" + _package_version;
+  url += _package_name;
   if (!_package_platform.empty()) {
     url += "/" + _package_platform;
   }
-
+  url += "/" + _package_version;
   url += "/" + _compressed_archive.get_filename();
 
   string target_pathname = _package_dir + "/" + _compressed_archive.get_filename();
@@ -667,6 +744,10 @@ download_finished(bool success) {
   _package->_active_download = NULL;
 
   switch (_dtype) {
+  case DT_contents_file:
+    _package->contents_file_download_finished(success);
+    break;
+
   case DT_desc_file:
     _package->desc_file_download_finished(success);
     break;

+ 8 - 1
direct/src/plugin/p3dPackage.h

@@ -36,8 +36,9 @@ class P3DInstance;
 ////////////////////////////////////////////////////////////////////
 class P3DPackage {
 public:
-  P3DPackage(const string &package_name, const string &package_version,
+  P3DPackage(const string &package_name, 
              const string &package_platform,
+             const string &package_version,
              const string &package_display_name);
   ~P3DPackage();
 
@@ -63,6 +64,7 @@ public:
 
 private:
   enum DownloadType {
+    DT_contents_file,
     DT_desc_file,
     DT_compressed_archive
   };
@@ -81,6 +83,9 @@ private:
   };
 
   void begin_download();
+  void download_contents_file();
+  void contents_file_download_finished(bool success);
+
   void download_desc_file();
   void desc_file_download_finished(bool success);
   void got_desc_file(TiXmlDocument *doc, bool freshly_downloaded);
@@ -105,6 +110,8 @@ private:
   string _package_fullname;
   string _package_dir;
 
+  string _contents_file_pathname;
+
   string _desc_file_basename;
   string _desc_file_pathname;
 

+ 27 - 27
direct/src/plugin/p3dWinSplashWindow.cxx

@@ -82,6 +82,7 @@ set_wparams(const P3DWindowParams &wparams) {
 void P3DWinSplashWindow::
 set_image_filename(const string &image_filename,
                    bool image_filename_temp) {
+  nout << "image_filename = " << image_filename << ", thread_id = " << _thread_id << "\n";
   ACQUIRE_LOCK(_install_lock);
   if (_image_filename != image_filename) {
     _image_filename = image_filename;
@@ -261,34 +262,33 @@ thread_run() {
     TranslateMessage(&msg);
     DispatchMessage(&msg);
 
-    if (_got_install) {
-      ACQUIRE_LOCK(_install_lock);
-      double install_progress = _install_progress;
-      if (_image_filename_changed) {
-        update_image_filename(_image_filename, _image_filename_temp);
-      }
-      _image_filename_changed = false;
-      if (_install_label_changed && _progress_bar != NULL) {
-        update_install_label(_install_label);
-      }
-      _install_label_changed = false;
-      RELEASE_LOCK(_install_lock);
-
-      if (install_progress != last_progress) {
-        if (_progress_bar == NULL) {
-          // Is it time to create the progress bar?
-          int now = GetTickCount();
-          if (now - _loop_started > 2000) {
-            make_progress_bar();
-          }
-        } else {
-          // Update the progress bar.  We do this only within the
-          // thread, to ensure we don't get a race condition when
-          // starting or closing the thread.
-          SendMessage(_progress_bar, PBM_SETPOS, (int)(install_progress * 100.0), 0);
-          
-          last_progress = install_progress;
+    ACQUIRE_LOCK(_install_lock);
+    bool got_install = _got_install;
+    double install_progress = _install_progress;
+    if (_image_filename_changed) {
+      update_image_filename(_image_filename, _image_filename_temp);
+    }
+    _image_filename_changed = false;
+    if (_install_label_changed && _progress_bar != NULL) {
+      update_install_label(_install_label);
+    }
+    _install_label_changed = false;
+    RELEASE_LOCK(_install_lock);
+
+    if (got_install && install_progress != last_progress) {
+      if (_progress_bar == NULL) {
+        // Is it time to create the progress bar?
+        int now = GetTickCount();
+        if (now - _loop_started > 2000) {
+          make_progress_bar();
         }
+      } else {
+        // Update the progress bar.  We do this only within the
+        // thread, to ensure we don't get a race condition when
+        // starting or closing the thread.
+        SendMessage(_progress_bar, PBM_SETPOS, (int)(install_progress * 100.0), 0);
+        
+        last_progress = install_progress;
       }
     }
 

+ 40 - 16
direct/src/plugin/p3d_plugin.cxx

@@ -13,6 +13,7 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "p3d_plugin_common.h"
+#include "p3d_plugin_config.h"
 #include "p3dInstanceManager.h"
 #include "p3dInstance.h"
 #include "p3dWindowParams.h"
@@ -37,7 +38,7 @@ ostream *nout_stream;
 
 
 bool 
-P3D_initialize(int api_version, const char *output_filename) {
+P3D_initialize(int api_version, const char *contents_filename) {
   if (api_version != P3D_API_VERSION) {
     // Can't accept an incompatible version.
     return false;
@@ -49,21 +50,40 @@ P3D_initialize(int api_version, const char *output_filename) {
   }
   ACQUIRE_LOCK(_api_lock);
 
-  plugin_output_filename = string();
-  if (output_filename != NULL) {
-    plugin_output_filename = output_filename;
+  if (contents_filename == NULL){ 
+    contents_filename = "";
   }
-  nout_stream = &cerr;
-  if (!plugin_output_filename.empty()) {
-    logfile.open(plugin_output_filename.c_str(), ios::out | ios::trunc);
-    if (logfile) {
-      logfile.setf(ios::unitbuf);
-      nout_stream = &logfile;
+
+#ifdef P3D_PLUGIN_LOGFILE2
+  string logfilename = P3D_PLUGIN_LOGFILE2;
+#else
+  string logfilename;
+#endif  // P3D_PLUGIN_LOGFILE2
+
+  if (logfilename.empty()) {
+#ifdef _WIN32
+    static const size_t buffer_size = 4096;
+    char buffer[buffer_size];
+    if (GetTempPath(buffer_size, buffer) != 0) {
+      logfilename = buffer;
+      logfilename += "panda3d.2.log";
     }
+#else
+    logfilename = "/tmp/panda3d.2.log";
+#endif  // _WIN32
+  }
+
+  cerr << "logfile: " << logfilename << "\n";
+
+  nout_stream = &cerr;
+  logfile.open(logfilename.c_str(), ios::out | ios::trunc);
+  if (logfile) {
+    logfile.setf(ios::unitbuf);
+    nout_stream = &logfile;
   }
 
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  bool result = inst_mgr->initialize();
+  bool result = inst_mgr->initialize(contents_filename);
   RELEASE_LOCK(_api_lock);
   return result;
 }
@@ -74,18 +94,21 @@ P3D_finalize() {
 }
 
 P3D_instance *
-P3D_new_instance(P3D_request_ready_func *func, void *user_data) {
+P3D_new_instance(P3D_request_ready_func *func, 
+                 const P3D_token tokens[], size_t num_tokens,
+                 void *user_data) {
+  nout << "new_instance\n";
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   ACQUIRE_LOCK(_api_lock);
   P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
-  P3DInstance *result = inst_mgr->create_instance(func, user_data);
+  P3DInstance *result = inst_mgr->create_instance(func, tokens, num_tokens, user_data);
   RELEASE_LOCK(_api_lock);
   return result;
 }
 
 bool
-P3D_instance_start(P3D_instance *instance, const char *p3d_filename, 
-                   const P3D_token tokens[], size_t num_tokens) {
+P3D_instance_start(P3D_instance *instance, const char *p3d_filename) {
+  nout << "instance_start\n";
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   if (p3d_filename == NULL) {
     p3d_filename = "";
@@ -95,7 +118,7 @@ P3D_instance_start(P3D_instance *instance, const char *p3d_filename,
   P3DInstance *inst = inst_mgr->validate_instance(instance);
   bool result = false;
   if (inst != NULL) {
-    result = inst_mgr->start_instance(inst, p3d_filename, tokens, num_tokens);
+    result = inst_mgr->start_instance(inst, p3d_filename);
   }
   RELEASE_LOCK(_api_lock);
   return result;
@@ -119,6 +142,7 @@ P3D_instance_setup_window(P3D_instance *instance,
                           int win_x, int win_y,
                           int win_width, int win_height,
                           P3D_window_handle parent_window) {
+  nout << "setup_window\n";
   assert(P3DInstanceManager::get_global_ptr()->is_initialized());
   P3DWindowParams wparams(window_type, win_x, win_y,
                           win_width, win_height, parent_window);

+ 17 - 29
direct/src/plugin/p3d_plugin.h

@@ -79,7 +79,7 @@ extern "C" {
    (below). This number will be incremented whenever there are changes
    to any of the interface specifications defined in this header
    file. */
-#define P3D_API_VERSION 4
+#define P3D_API_VERSION 5
 
 /************************ GLOBAL FUNCTIONS **************************/
 
@@ -91,17 +91,16 @@ extern "C" {
    the DLL can verify that it has been built with the same version of
    the API as the host.
 
-   The output_filename is usually NULL, but if you put a filename
-   here, it will be used as the log file for the output from the core
-   API.  This is useful for debugging, particularly when running
-   within a browser that squelches stderr.
+   The contents_filename, if not NULL or empty, names a contents.xml
+   file that has already been downloaded and verified from the server.
+   If this is NULL, a new file will be downloaded as needed.
 
    This function returns true if the core API is valid and uses a
    compatible API, false otherwise.  If it returns false, the host
    should not call any more functions in this API, and should
    immediately unload the DLL and (if possible) download a new one. */
 typedef bool 
-P3D_initialize_func(int api_version, const char *output_filename);
+P3D_initialize_func(int api_version, const char *contents_filename);
 
 /* This function should be called to unload the core API.  It will
    release all internally-allocated memory and return the core API to
@@ -210,16 +209,25 @@ typedef struct {
 
 /* This function creates a new Panda3D instance.  
 
+   For tokens, pass an array of P3D_token elements (above), which
+   correspond to the user-supplied keyword/value pairs that may appear
+   in the embed token within the HTML syntax; the host is responsible
+   for allocating this array, and for deallocating it after this call
+   (the core API will make its own copy of the array).  The tokens are
+   passed to the application, who is free to decide how to interpret
+   them; they have no meaning at the system level.
+
    The user_data pointer is any arbitrary pointer value; it will be
    copied into the _user_data member of the new P3D_instance object.
    This pointer is intended for the host to use to store private data
    associated with each instance; the core API will not do anything with
    this data.
-
  */
 
 typedef P3D_instance *
-P3D_new_instance_func(P3D_request_ready_func *func, void *user_data);
+P3D_new_instance_func(P3D_request_ready_func *func, 
+                      const P3D_token tokens[], size_t num_tokens,
+                      void *user_data);
 
 /* This function should be called at some point after
    P3D_new_instance(); it actually starts the instance running.
@@ -230,29 +238,9 @@ P3D_new_instance_func(P3D_request_ready_func *func, void *user_data);
    instance.  If this is empty or NULL, the "src" token (below) will
    be downloaded instead.
 
-   For tokens, pass an array of P3D_token elements (above), which
-   correspond to the user-supplied keyword/value pairs that may appear
-   in the embed token within the HTML syntax; the host is responsible
-   for allocating this array, and for deallocating it after this call
-   (the core API will make its own copy of the array).
-
-   Most tokens are implemented by the application and are undefined at
-   the system level.  However, two tokens in particular are
-   system-defined:
-
-     "src" : names a URL that will be loaded for the contents of the
-       p3d file, if p3d_filename is empty or NULL.
-
-     "output_filename" : names a file to create on disk which contains
-       the console output from the application.  This may be useful in
-       debugging.  If this is omitted, or an empty string, the console
-       output is written to the standard error output, which may be
-       NULL on a gui application.
-
    The return value is true on success, false on failure. */
 typedef bool
-P3D_instance_start_func(P3D_instance *instance, const char *p3d_filename, 
-                        const P3D_token tokens[], size_t num_tokens);
+P3D_instance_start_func(P3D_instance *instance, const char *p3d_filename);
 
 
 /* Call this function to interrupt a particular instance and stop it

+ 80 - 40
direct/src/plugin_npapi/ppInstance.cxx

@@ -51,6 +51,8 @@ PPInstance(NPMIMEType pluginType, NPP instance, uint16 mode,
     _tokens.push_back(token);
   }
 
+  _root_dir = find_root_dir();
+
   _started_instance_data = false;
   _got_instance_data = false;
   _got_window = false;
@@ -313,7 +315,15 @@ url_notify(const char *url, NPReason reason, void *notifyData) {
   case PPDownloadRequest::RT_contents_file:
     if (reason != NPRES_DONE) {
       nout << "Failure downloading " << url << "\n";
-      // TODO: fail
+
+      // Couldn't download a fresh contents.xml for some reason.  If
+      // there's an outstanding contents.xml file on disk, try to load
+      // that one as a fallback.
+      string contents_filename = _root_dir + "/contents.xml";
+      if (!read_contents_file(contents_filename)) {
+        nout << "Unable to read contents file " << contents_filename << "\n";
+        // TODO: fail
+      }
     }
     break;
     
@@ -361,11 +371,11 @@ stream_as_file(NPStream *stream, const char *fname) {
       // Looks like we've converted it successfully.
       filename = fname2;
 
-      // Here's another temporary hack.  In addition to the weird
-      // filename format, the file that Safari tells us about appears
-      // to be a temporary file that Safari's about to delete.  In
-      // order to protect ourselves from this, we need to temporarily
-      // copy the file somewhere else.
+      // Here's another crazy hack.  In addition to the weird filename
+      // format, the file that Safari tells us about appears to be a
+      // temporary file that Safari's about to delete.  In order to
+      // protect ourselves from this, we need to temporarily copy the
+      // file somewhere else.
       char *name = tempnam(NULL, "p3d_");
 
       // We prefer just making a hard link; it's quick and easy.
@@ -373,19 +383,7 @@ stream_as_file(NPStream *stream, const char *fname) {
         // But sometimes the hard link might fail, particularly if these
         // are two different file systems.  In this case we have to open
         // the files and copy the data by hand.
-        ifstream in(filename.c_str(), ios::in | ios::binary);
-        ofstream out(name, ios::out | ios::binary);
-        
-        static const size_t buffer_size = 4096;
-        char buffer[buffer_size];
-        
-        in.read(buffer, buffer_size);
-        size_t count = in.gcount();
-        while (count != 0) {
-          out.write(buffer, count);
-          in.read(buffer, buffer_size);
-          count = in.gcount();
-        }
+        copy_file(filename, name);
       }
       
       filename = name;
@@ -439,7 +437,7 @@ handle_request(P3D_request *request) {
     if (strcmp(request->_request._notify._message, "onwindowopen") == 0) {
       _python_window_open = true;
       if (_got_window) {
-        NPRect rect = { 0, 0, _window.height, _window.width };
+        NPRect rect = { 0, 0, (unsigned short)_window.height, (unsigned short)_window.width };
         browser->invalidaterect(_npp_instance, &rect);
       }
     }
@@ -447,7 +445,7 @@ handle_request(P3D_request *request) {
 
   case P3D_RT_refresh:
     if (_got_window) {
-      NPRect rect = { 0, 0, _window.height, _window.width };
+      NPRect rect = { 0, 0, (unsigned short)_window.height, (unsigned short)_window.width };
       browser->invalidaterect(_npp_instance, &rect);
     }
     break;
@@ -700,8 +698,8 @@ start_download(const string &url, PPDownloadRequest *req) {
 //               DLL downloading, if necessary.
 ////////////////////////////////////////////////////////////////////
 bool PPInstance::
-read_contents_file(const string &filename) {
-  TiXmlDocument doc(filename.c_str());
+read_contents_file(const string &contents_filename) {
+  TiXmlDocument doc(contents_filename.c_str());
   if (!doc.LoadFile()) {
     return false;
   }
@@ -765,9 +763,23 @@ downloaded_file(PPDownloadRequest *req, const string &filename) {
   case PPDownloadRequest::RT_contents_file:
     // Now we have the contents.xml file.  Read this to get the
     // filename and md5 hash of our core API DLL.
-    if (!read_contents_file(filename)) {
-      nout << "Unable to read contents file\n";
-      // TODO: fail
+    if (read_contents_file(filename)) {
+      // Successfully read.  Copy it into its normal place.
+      string contents_filename = _root_dir + "/contents.xml";
+      copy_file(filename, contents_filename);
+      
+    } else {
+      // Error reading the contents.xml file, or in loading the core
+      // API that it references.
+      nout << "Unable to read contents file " << filename << "\n";
+
+      // If there's an outstanding contents.xml file on disk, try to
+      // load that one as a fallback.
+      string contents_filename = _root_dir + "/contents.xml";
+      if (!read_contents_file(contents_filename)) {
+        nout << "Unable to read contents file " << contents_filename << "\n";
+        // TODO: fail
+      }
     }
     break;
 
@@ -782,7 +794,10 @@ downloaded_file(PPDownloadRequest *req, const string &filename) {
     // launch the instance.
     _got_instance_data = true;
     _p3d_filename = filename;
-    create_instance();
+
+    if (_p3d_inst != NULL) {
+      P3D_instance_start(_p3d_inst, _p3d_filename.c_str());
+    }
     break;
 
   case PPDownloadRequest::RT_user:
@@ -821,8 +836,6 @@ void PPInstance::
 get_core_api(TiXmlElement *xpackage) {
   _core_api_dll.load_xml(xpackage);
 
-  _root_dir = find_root_dir();
-
   if (_core_api_dll.quick_verify(_root_dir)) {
     // The DLL file is good.  Just load it.
     do_load_plugin();
@@ -919,7 +932,7 @@ do_load_plugin() {
   }
 #endif  // P3D_PLUGIN_P3D_PLUGIN
 
-  if (!load_plugin(pathname)) {
+  if (!load_plugin(pathname, "")) {
     nout << "Unable to launch core API in " << pathname << "\n";
     return;
   }
@@ -944,12 +957,11 @@ create_instance() {
     return;
   }
 
-  if (!_got_instance_data) {
-    // No instance data yet.
-    return;
+  P3D_token *tokens = NULL;
+  if (!_tokens.empty()) {
+    tokens = &_tokens[0];
   }
-
-  _p3d_inst = P3D_new_instance(request_ready, this);
+  _p3d_inst = P3D_new_instance(request_ready, tokens, _tokens.size(), this);
 
   if (_p3d_inst != NULL) {
     // Now get the browser's window object, to pass to the plugin.
@@ -971,11 +983,7 @@ create_instance() {
     }
 
     if (_got_instance_data) {
-      P3D_token *tokens = NULL;
-      if (!_tokens.empty()) {
-        tokens = &_tokens[0];
-      }
-      P3D_instance_start(_p3d_inst, _p3d_filename.c_str(), tokens, _tokens.size());
+      P3D_instance_start(_p3d_inst, _p3d_filename.c_str());
     }
 
     if (_got_window) {
@@ -1062,6 +1070,38 @@ send_window() {
      parent_window);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PPInstance::copy_file
+//       Access: Public
+//  Description: Copies the data in the file named by from_filename
+//               into the file named by to_filename.
+////////////////////////////////////////////////////////////////////
+bool PPInstance::
+copy_file(const string &from_filename, const string &to_filename) {
+  ifstream in(from_filename.c_str(), ios::in | ios::binary);
+  ofstream out(to_filename.c_str(), ios::out | ios::binary);
+        
+  static const size_t buffer_size = 4096;
+  char buffer[buffer_size];
+  
+  in.read(buffer, buffer_size);
+  size_t count = in.gcount();
+  while (count != 0) {
+    out.write(buffer, count);
+    if (out.fail()) {
+      return false;
+    }
+    in.read(buffer, buffer_size);
+    count = in.gcount();
+  }
+
+  if (!in.eof()) {
+    return false;
+  }
+
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: PPInstance::output_np_variant
 //       Access: Public

+ 2 - 1
direct/src/plugin_npapi/ppInstance.h

@@ -71,13 +71,14 @@ private:
   static string get_filename_from_url(const string &url);
   void feed_file(PPDownloadRequest *req, const string &filename);
 
-  bool read_contents_file(const string &filename);
+  bool read_contents_file(const string &contents_filename);
   void get_core_api(TiXmlElement *xpackage);
   void downloaded_plugin(const string &filename);
   void do_load_plugin();
 
   void create_instance();
   void send_window();
+  bool copy_file(const string &from_filename, const string &to_filename);
 
   static void handle_request_loop();
   static void browser_sync_callback(void *);

+ 15 - 14
direct/src/plugin_standalone/panda3d.cxx

@@ -286,8 +286,8 @@ run(int argc, char *argv[]) {
 bool Panda3D::
 get_plugin(const string &root_url, const string &this_platform, bool force_download) {
   // First, look for the existing contents.xml file.
-  Filename contents = Filename(Filename::from_os_specific(_root_dir), "contents.xml");
-  if (!force_download && read_contents_file(contents, root_url, this_platform)) {
+  Filename contents_filename = Filename(Filename::from_os_specific(_root_dir), "contents.xml");
+  if (!force_download && read_contents_file(contents_filename, root_url, this_platform)) {
     // Got the file, and it's good.
     return true;
   }
@@ -299,13 +299,13 @@ get_plugin(const string &root_url, const string &this_platform, bool force_downl
   
   HTTPClient *http = HTTPClient::get_global_ptr();
   PT(HTTPChannel) channel = http->get_document(url);
-  contents.make_dir();
-  if (!channel->download_to_file(contents)) {
+  contents_filename.make_dir();
+  if (!channel->download_to_file(contents_filename)) {
     cerr << "Unable to download " << url << "\n";
     return false;
   }
   
-  return read_contents_file(contents, root_url, this_platform);
+  return read_contents_file(contents_filename, root_url, this_platform);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -316,12 +316,12 @@ get_plugin(const string &root_url, const string &this_platform, bool force_downl
 //               possible.  Returns true on success, false on failure.
 ////////////////////////////////////////////////////////////////////
 bool Panda3D::
-read_contents_file(Filename contents, const string &root_url, 
+read_contents_file(Filename contents_filename, const string &root_url, 
                    const string &this_platform) {
   ifstream in;
-  contents.set_text();
-  if (!contents.open_read(in)) {
-    cerr << "Couldn't read " << contents.to_os_specific() << "\n";
+  contents_filename.set_text();
+  if (!contents_filename.open_read(in)) {
+    cerr << "Couldn't read " << contents_filename.to_os_specific() << "\n";
     return false;
   }
 
@@ -336,7 +336,7 @@ read_contents_file(Filename contents, const string &root_url,
       if (name != NULL && strcmp(name, "coreapi") == 0) {
         const char *xplatform = xpackage->Attribute("platform");
         if (xplatform != NULL && strcmp(xplatform, this_platform.c_str()) == 0) {
-          return get_core_api(root_url, xpackage);
+          return get_core_api(contents_filename, root_url, xpackage);
         }
       }
       
@@ -359,7 +359,8 @@ read_contents_file(Filename contents, const string &root_url,
 //               if necessary.
 ////////////////////////////////////////////////////////////////////
 bool Panda3D::
-get_core_api(const string &root_url, TiXmlElement *xpackage) {
+get_core_api(const Filename &contents_filename, const string &root_url, 
+             TiXmlElement *xpackage) {
   _core_api_dll.load_xml(xpackage);
 
   if (!_core_api_dll.quick_verify(_root_dir)) {
@@ -397,7 +398,7 @@ get_core_api(const string &root_url, TiXmlElement *xpackage) {
   }
 #endif  // P3D_PLUGIN_P3D_PLUGIN
 
-  if (!load_plugin(pathname)) {
+  if (!load_plugin(pathname, contents_filename.to_os_specific())) {
     cerr << "Unable to launch core API in " << pathname << "\n" << flush;
     return false;
   }
@@ -592,12 +593,12 @@ create_instance(const string &arg, P3D_window_type window_type,
     os_p3d_filename = p3d_filename.to_os_specific();
   } 
 
-  P3D_instance *inst = P3D_new_instance(NULL, NULL);
+  P3D_instance *inst = P3D_new_instance(NULL, tokens, num_tokens, NULL);
 
   if (inst != NULL) {
     P3D_instance_setup_window
       (inst, window_type, win_x, win_y, win_width, win_height, parent_window);
-    P3D_instance_start(inst, os_p3d_filename.c_str(), tokens, num_tokens);
+    P3D_instance_start(inst, os_p3d_filename.c_str());
   }
 
   return inst;

+ 3 - 2
direct/src/plugin_standalone/panda3d.h

@@ -44,9 +44,10 @@ public:
 private:
   bool get_plugin(const string &root_url, const string &this_platform,
                   bool force_download);
-  bool read_contents_file(Filename contents, const string &root_url, 
+  bool read_contents_file(Filename contents_filename, const string &root_url, 
                           const string &this_platform);
-  bool get_core_api(const string &root_url, TiXmlElement *xpackage);
+  bool get_core_api(const Filename &contents_filename, const string &root_url,
+                    TiXmlElement *xpackage);
   void run_getters();
   void handle_request(P3D_request *request);
   void make_parent_window(P3D_window_handle &parent_window, 

+ 2 - 2
direct/src/showutil/FreezeTool.py

@@ -55,7 +55,7 @@ if sys.platform == 'win32':
         MSVC = Filename('/c/Program Files/Microsoft Visual Studio .NET 2003/Vc7').toOsSpecific()
     else:
         print 'Could not locate Microsoft Visual C++ Compiler! Try running from the Visual Studio Command Prompt.'
-        exit(1)
+        sys.exit(1)
     
     if ('WindowsSdkDir' in os.environ):
         PSDK = os.environ['WindowsSdkDir']
@@ -65,7 +65,7 @@ if sys.platform == 'win32':
         PSDK = os.path.join(MSVC, 'PlatformSDK')
     else:
         print 'Could not locate the Microsoft Windows Platform SDK! Try running from the Visual Studio Command Prompt.'
-        exit(1)
+        sys.exit(1)
     
     # If it is run by makepanda, it handles the MSVC and PlatformSDK paths itself.
     if ('MAKEPANDA' in os.environ):