def mount_multifile(self): mf = Multifile() mf.open_read(Filename.from_os_specific('phase_1.ef')) mf.set_encryption_flag(True) mf.set_encryption_password(self.PW) if not VirtualFileSystem.get_global_ptr().mount(mf, Filename('/'), 0): raise Exception('Multifile could not be mounted.')
def md5_contents(filename): m = Multifile() m.openRead(filename) payload = [] for i in range(m.getNumSubfiles()): payload.append(m.get_subfile_name(i)) m.close() return payload
def loadPhases(): for mf in [3, 3.5, 4, 5, 5.5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14.5]: filenameBase = 'resources/default/phase_%s' % mf mounted = mountWithSignature(filenameBase + '.mf', mf) if not mounted: print('Unable to mount phase_{0}.').format(mf) sys.exit() break filenameBase = 'resources' for pack in os.listdir(filenameBase): if pack == 'default': continue directory = os.path.join(filenameBase, pack) if not os.path.isdir(directory): continue print('Loading content pack {0}...').format(pack) for file in glob.glob('%s/%s/*.mf' % (filenameBase, pack)): mf = Multifile() mf.openReadWrite(Filename(file)) names = mf.getSubfileNames() for name in names: ext = os.path.splitext(name)[1] if ext not in ('.jpg', '.jpeg', '.ogg', '.rgb', '.png'): mf.removeSubfile(name) vfs.mount(mf, Filename('/'), 0)
def mountWithSignature(filename, name): if not os.path.isfile(filename): return False mf = Multifile() mf.openRead(Filename(filename)) if mf.getNumSignatures() != 1: return False if mf.getSignaturePublicKey(0) != OfflineGlobals.getmfcrt(): return False if name == 14 or name == 14.5: mf.setEncryptionPassword(OfflineGlobals.getmfkey(filename)) return vfs.mount(mf, Filename('/'), 0)
def applyFile(self, filename): mf = Multifile() mf.openReadWrite(Filename(os.path.join(self.packPath, filename))) for subfilename in mf.getSubfileNames(): if os.path.splitext(subfilename)[1] not in SupportedExtensions: mf.removeSubfile(subfilename) print 'Removing a file that is not allowed: %s' % str( subfilename) self.vfSys.mount(mf, self.mountPoint, 0) print 'Successfully Mounted: ' + str(filename)
def vfs_mount_multifile(mount_point: str, multifile: str) -> bool: """ Mounts the multifile to the requested mount point if it exists. Returning the result of the mounting operation """ if not os_path_exists(multifile): return False m = Multifile() m.openReadWrite(multifile) __file_notify.info('Mounting MF "%s" at "%s"' % (multifile, mount_point)) result = _vfs.mount(m, mount_point, VirtualFileSystem.MFReadOnly) if not result: __file_notify.warning('Failed to mount multifile (%s) to (%s)!' % (multifile, mount_point)) get_model_path().append_directory(mount_point) switch_file_functions_to_vfs() return result
def test_multifile_read_empty(): stream = StringStream( b'pmf\x00\n\r\x01\x00\x01\x00\x01\x00\x00\x00\xdb\x9d7\\\x00\x00\x00\x00' ) wrapper = IStreamWrapper(stream) m = Multifile() assert m.open_read(wrapper) assert m.is_read_valid() assert m.get_num_subfiles() == 0 m.close()
def test_multifile_read_empty(): stream = StringStream(b'pmf\x00\n\r\x01\x00\x01\x00\x01\x00\x00\x00\xdb\x9d7\\\x00\x00\x00\x00') wrapper = IStreamWrapper(stream) m = Multifile() assert m.open_read(wrapper) assert m.is_read_valid() assert m.get_num_subfiles() == 0 m.close()
def applyMultifile(self, filename): """ Apply the specified multifile. """ mf = Multifile() mf.openReadWrite(Filename(os.path.join(self.filepath, filename))) # Discard content with non-whitelisted extensions: for subfileName in mf.getSubfileNames(): ext = os.path.splitext(subfileName)[1] if ext not in CONTENT_EXT_WHITELIST: mf.removeSubfile(subfileName) self.vfs.mount(mf, self.mountPoint, 0)
def apply(self, filename): """ Apply the specified content pack on top of the existing content. """ self.notify.info("Applying %s..." % filename[len(self.filepath) :]) mf = Multifile() mf.openReadWrite(Filename(filename)) # Discard content with non-whitelisted extensions: for subfileName in mf.getSubfileNames(): ext = os.path.splitext(subfileName)[1] if ext not in CONTENT_EXT_WHITELIST: mf.removeSubfile(subfileName) self.vfs.mount(mf, self.mountPoint, 0)
def mountMultifiles(self, resourcePackName=None): rsPackPath = None if not resourcePackName else 'resourcepacks/' + resourcePackName # This boolean flag is set false if pack.yaml is not found. allowResourcePackLoad = True if rsPackPath and os.path.exists(rsPackPath): if not os.path.exists(rsPackPath + '/pack.yaml'): self.notify.warning( 'You must have a \'pack.yaml\' configuration in the directory of your resource pack to use it.' ) allowResourcePackLoad = False else: self.resourcePack = ResourcePack(rsPackPath) allowResourcePackLoad = self.resourcePack.digest() if allowResourcePackLoad: author = '' if len(self.resourcePack.authors ) == 0 else self.resourcePack.authors[0] self.notify.info('Loading Resource Pack %s [%s] by %s...' % (self.resourcePack.name, self.resourcePack.version, author)) self.envConfig = self.resourcePack self.notify.info( 'Using Resource Pack Environment Configuration.') # This is a boolean flag that stores if we let the user know that a resource # pack directory was not found. warnedOfMissingPack = False vfs = VirtualFileSystem.getGlobalPtr() for phase in self.Phases: mf = Multifile() mf.setEncryptionPassword(metadata.RESOURCE_ENCRYPTION_PASSWORD) mf.openReadWrite(Filename(metadata.PHASE_DIRECTORY + phase + '.mf')) # Let's handle the mounting of certain file types from resource packs. rsPackMf = None loadedRsPackMf = False if allowResourcePackLoad: if rsPackPath and os.path.exists(rsPackPath): rsPhasePath = '%s/%s.mf' % (rsPackPath, phase) if os.path.exists(rsPhasePath): # This is the phase that exists within the resource pack. rsPackMf = Multifile() rsPackMf.openReadWrite(Filename(rsPhasePath)) # Let's remove the unneeded files from the default multifile for this phase. for subFile in mf.getSubfileNames(): ext = os.path.splitext(subFile)[1][1:] # This code removes files that are overwritten by the resource pack. if ext in self.LegalResourcePackExtensions and subFile in rsPackMf.getSubfileNames( ): mf.removeSubfile(subFile) # This code removes illegal files inside of the resource pack multifile. elif not ext in self.LegalResourcePackExtensions and subFile in rsPackMf.getSubfileNames( ): rsPackMf.removeSubfile(subFile) # Let's flag that we've loaded a resource pack multifile. loadedRsPackMf = True elif rsPackPath and not os.path.exists( rsPackPath) and not warnedOfMissingPack: self.notify.warning( 'Desired resource pack could not be found in the \'resourcepacks\' directory.' ) warnedOfMissingPack = True vfs.mount(mf, '.', 0) if loadedRsPackMf: vfs.mount(rsPackMf, '.', 0) self.notify.info('Mounted %s from resource pack.' % phase) else: self.notify.info('Mounted %s from default.' % phase) if phase == 'phase_3': self.notify.info( 'Loading Default Environment Configuration...') # Make an IO stream to the environment.yaml file. self.envConfigStream = io.BytesIO( vfs.readFile('phase_3/etc/environment.yaml', False)) # Create a new EnvironmentConfiguration object and read the data. self.envConfig = EnvironmentConfiguration( self.envConfigStream) self.envConfig.digest() # Now, close out the stream. self.envConfigStream.close() self.envConfigStream = None self.notify.info( 'Environment Configuration load complete!') self.progressScreen = CIProgressScreen() self.notify.info('All phases loaded! Ready to play!')
def test_multifile_password(): m = Multifile() m.set_encryption_password('Panda3D rocks!') assert m.get_encryption_password() == 'Panda3D rocks!' m.set_encryption_password(b'Panda3D is awesome!') assert m.get_encryption_password() == 'Panda3D is awesome!' m.set_encryption_password(b'\xc4\x97\xa1\x01\x85\xb6') assert m.get_encryption_password() == b'\xc4\x97\xa1\x01\x85\xb6'
def installPackage(self, appRunner): """ Mounts the package and sets up system paths so it becomes available for use. Returns true on success, false on failure. """ assert self.hasPackage if self.installed: # Already installed. return True assert self not in appRunner.installedPackages mfPathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename) mf = Multifile() if not mf.openRead(mfPathname): self.notify.warning("Couldn't open %s" % (mfPathname)) return False # We mount it under its actual location on disk. root = self.getPackageDir() vfs = VirtualFileSystem.getGlobalPtr() vfs.mount(mf, root, vfs.MFReadOnly) # Add this to the Python search path, if it's not already # there. We have to take a bit of care to check if it's # already there, since there can be some ambiguity in # os-specific path strings. osRoot = self.getPackageDir().toOsSpecific() foundOnPath = False for p in sys.path: if osRoot == p: # Already here, exactly. foundOnPath = True break elif osRoot == Filename.fromOsSpecific(p).toOsSpecific(): # Already here, with some futzing. foundOnPath = True break if not foundOnPath: # Not already here; add it. sys.path.append(osRoot) # Put it on the model-path, too. We do this indiscriminantly, # because the Panda3D runtime won't be adding things to the # model-path, so it shouldn't be already there. getModelPath().appendDirectory(self.getPackageDir()) # Set the environment variable to reference the package root. envvar = '%s_ROOT' % (self.packageName.upper()) ExecutionEnvironment.setEnvironmentVariable(envvar, osRoot) # Add the package root to the system paths. if sys.platform.startswith('win'): path = os.environ.get('PATH', '') os.environ['PATH'] = "%s;%s" % (osRoot, path) else: path = os.environ.get('PATH', '') os.environ['PATH'] = "%s:%s" % (osRoot, path) path = os.environ.get('LD_LIBRARY_PATH', '') os.environ['LD_LIBRARY_PATH'] = "%s:%s" % (osRoot, path) if sys.platform == "darwin": path = os.environ.get('DYLD_LIBRARY_PATH', '') os.environ['DYLD_LIBRARY_PATH'] = "%s:%s" % (osRoot, path) # Now that the environment variable is set, read all of the # prc files in the package. appRunner.loadMultifilePrcFiles(mf, self.getPackageDir()) # Also, find any toplevel Python packages, and add these as # shared packages. This will allow different packages # installed in different directories to share Python files as # if they were all in the same directory. for filename in mf.getSubfileNames(): if filename.endswith('/__init__.pyc') or \ filename.endswith('/__init__.pyo') or \ filename.endswith('/__init__.py'): components = filename.split('/')[:-1] moduleName = '.'.join(components) VFSImporter.sharedPackages[moduleName] = True # Fix up any shared directories so we can load packages from # disparate locations. VFSImporter.reloadSharedPackages() self.installed = True appRunner.installedPackages.append(self) self.markUsed() return True
def __unpackArchive(self, step): """ Unpacks any files in the archive that want to be unpacked to disk. Yields one of stepComplete, stepFailed, restartDownload, or stepContinue. """ if not self.extracts: # Nothing to extract. self.hasPackage = True yield self.stepComplete; return if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever: # We're not allowed to! yield self.stepFailed; return self.updated = True mfPathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename) self.notify.info("Unpacking %s" % (mfPathname)) mf = Multifile() if not mf.openRead(mfPathname): self.notify.warning("Couldn't open %s" % (mfPathname)) yield self.stepFailed; return allExtractsOk = True step.bytesDone = 0 for file in self.extracts: i = mf.findSubfile(file.filename) if i == -1: self.notify.warning("Not in Multifile: %s" % (file.filename)) allExtractsOk = False continue targetPathname = Filename(self.getPackageDir(), file.filename) targetPathname.setBinary() targetPathname.unlink() if not mf.extractSubfile(i, targetPathname): self.notify.warning("Couldn't extract: %s" % (file.filename)) allExtractsOk = False continue if not file.quickVerify(self.getPackageDir(), notify = self.notify): self.notify.warning("After extracting, still incorrect: %s" % (file.filename)) allExtractsOk = False continue # Make sure it's executable, and not writable. os.chmod(targetPathname.toOsSpecific(), 0o555) step.bytesDone += file.size self.__updateStepProgress(step) if taskMgr.destroyed: # If the task manager has been destroyed, we must # be shutting down. Get out of here. self.notify.warning("Task Manager destroyed, aborting unpacking %s" % (mfPathname)) yield self.stepFailed; return yield self.stepContinue if not allExtractsOk: yield self.stepFailed; return self.hasPackage = True yield self.stepComplete; return
def crea_mf(name): name_r = name + "_r" + str(sys.argv[1]) if os.path.exists("arcns_mfs/" + name_r + ".mf"): if os.stat(name).st_mtime <= os.stat("arcns_mfs/" + name_r + ".mf").st_mtime: return else: os.unlink("arcns_mfs/" + name_r + ".mf") mf = Multifile() mf.openWrite("arcns_mfs/" + name_r + ".mf") for root, dirs, files in os.walk(name): for f in files: if f[-1] == "~" or f[-3:] == "pyc" or f[-3:] == "pyo" or f[ -3:] == "bam": pass elif f[-2:] == "py": if os.path.exists(f + "o") and os.stat(f).st_mtime <= os.stat( f + "o").st_mtime: pass else: py_compile.compile(root + "/" + f) fln = Filename(root + "/" + f + "o") fln.set_binary() mf.addSubfile(root + "/" + f + "o", fln, 6) elif f[-3:] == "egg": if os.path.exists(root + "/" + f[:-3] + "bam") and os.stat( root + "/" + f).st_mtime <= os.stat( root + "/" + f[:-3] + "bam").st_mtime: pass else: if os.path.exists(root + "/" + f[:-3] + "bam"): os.unlink(root + "/" + f[:-3] + "bam") os.system("egg2bam -o " + root + "/" + f[:-3] + "bam " + root + "/" + f) fln = Filename(root + "/" + f[:-3] + "bam") fln.set_binary() mf.addSubfile(root + "/" + f[:-3] + "bam", fln, 6) elif f[-3:] == "wav": fln = Filename(root + "/" + f) fln.set_binary() mf.addSubfile(root + "/" + f, fln, 6) mf.close()
from direct.extensions_native.VBase4_extensions import * import __builtin__, os, sys if hasattr(NodePath, 'DtoolClassDict'): for dtool in ('children', 'parent', 'name'): if dtool in NodePath.DtoolClassDict: del NodePath.DtoolClassDict[dtool] vfs = VirtualFileSystem.getGlobalPtr() phases = [ 'phase_3', 'phase_3.5', 'phase_4', 'phase_5', 'phase_5.5', 'phase_6', 'phase_7', 'phase_8', 'phase_9', 'phase_10', 'phase_11', 'phase_12', 'phase_13', 'phase_0', 'phase_14' ] packExtensions = ['.jpg', '.jpeg', '.png', '.ogg', '.rgb', '.mid'] for phase in phases: mf = Multifile() mf.setEncryptionPassword('cio-03-06-16_lsphases') mf.openReadWrite(Filename(phase + '.mf')) packMf = None if os.path.exists('resourcepack/%s.mf' % phase): for subFile in mf.getSubfileNames(): ext = os.path.splitext(subFile)[1] if ext in packExtensions: mf.removeSubfile(subFile) packMf = Multifile() packMf.openReadWrite(Filename('resourcepack/%s.mf' % phase)) for subFile in packMf.getSubfileNames(): ext = os.path.splitext(subFile)[1] if ext not in packExtensions: packMf.removeSubfile(subFile)
def setP3DFilename(self, p3dFilename, tokens, argv, instanceId, interactiveConsole, p3dOffset = 0, p3dUrl = None): """ Called by the browser to specify the p3d file that contains the application itself, along with the web tokens and/or command-line arguments. Once this method has been called, the application is effectively started. """ # One day we will have support for multiple instances within a # Python session. Against that day, we save the instance ID # for this instance. self.instanceId = instanceId self.tokens = tokens self.argv = argv # We build up a token dictionary with care, so that if a given # token appears twice in the token list, we record only the # first value, not the second or later. This is consistent # with the internal behavior of the core API. self.tokenDict = {} for token, keyword in tokens: self.tokenDict.setdefault(token, keyword) # Also store the arguments on sys, for applications that # aren't instance-ready. sys.argv = argv # That means we now know the altHost in effect. self.altHost = self.tokenDict.get('alt_host', None) # Tell the browser that Python is up and running, and ready to # respond to queries. self.notifyRequest('onpythonload') # Now go load the applet. fname = Filename.fromOsSpecific(p3dFilename) vfs = VirtualFileSystem.getGlobalPtr() if not vfs.exists(fname): raise ArgumentError, "No such file: %s" % (p3dFilename) fname.makeAbsolute() fname.setBinary() mf = Multifile() if p3dOffset == 0: if not mf.openRead(fname): raise ArgumentError, "Not a Panda3D application: %s" % (p3dFilename) else: if not mf.openRead(fname, p3dOffset): raise ArgumentError, "Not a Panda3D application: %s at offset: %s" % (p3dFilename, p3dOffset) # Now load the p3dInfo file. self.p3dInfo = None self.p3dPackage = None self.p3dConfig = None self.allowPythonDev = False i = mf.findSubfile('p3d_info.xml') if i >= 0 and hasattr(core, 'readXmlStream'): stream = mf.openReadSubfile(i) self.p3dInfo = core.readXmlStream(stream) mf.closeReadSubfile(stream) if self.p3dInfo: self.p3dPackage = self.p3dInfo.FirstChildElement('package') if self.p3dPackage: self.p3dConfig = self.p3dPackage.FirstChildElement('config') xhost = self.p3dPackage.FirstChildElement('host') while xhost: self.__readHostXml(xhost) xhost = xhost.NextSiblingElement('host') if self.p3dConfig: allowPythonDev = self.p3dConfig.Attribute('allow_python_dev') if allowPythonDev: self.allowPythonDev = int(allowPythonDev) guiApp = self.p3dConfig.Attribute('gui_app') if guiApp: self.guiApp = int(guiApp) trueFileIO = self.p3dConfig.Attribute('true_file_io') if trueFileIO: self.trueFileIO = int(trueFileIO) # The interactiveConsole flag can only be set true if the # application has allow_python_dev set. if not self.allowPythonDev and interactiveConsole: raise StandardError, "Impossible, interactive_console set without allow_python_dev." self.interactiveConsole = interactiveConsole if self.allowPythonDev: # Set the fps text to remind the user that # allow_python_dev is enabled. ConfigVariableString('frame-rate-meter-text-pattern').setValue('allow_python_dev %0.1f fps') if self.guiApp: init_app_for_gui() self.initPackedAppEnvironment() # Mount the Multifile under self.multifileRoot. vfs.mount(mf, self.multifileRoot, vfs.MFReadOnly) self.p3dMultifile = mf VFSImporter.reloadSharedPackages() self.loadMultifilePrcFiles(mf, self.multifileRoot) self.gotP3DFilename = True self.p3dFilename = fname if p3dUrl: # The url from which the p3d file was downloaded is # provided if available. It is only for documentation # purposes; the actual p3d file has already been # downloaded to p3dFilename. self.p3dUrl = core.URLSpec(p3dUrl) # Send this call to the main thread; don't call it directly. messenger.send('AppRunner_startIfReady', taskChain = 'default')
def __unpackArchive(self, step): """ Unpacks any files in the archive that want to be unpacked to disk. Yields one of stepComplete, stepFailed, restartDownload, or stepContinue. """ if not self.extracts: # Nothing to extract. self.hasPackage = True yield self.stepComplete return if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever: # We're not allowed to! yield self.stepFailed return self.updated = True mfPathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename) self.notify.info("Unpacking %s" % (mfPathname)) mf = Multifile() if not mf.openRead(mfPathname): self.notify.warning("Couldn't open %s" % (mfPathname)) yield self.stepFailed return allExtractsOk = True step.bytesDone = 0 for file in self.extracts: i = mf.findSubfile(file.filename) if i == -1: self.notify.warning("Not in Multifile: %s" % (file.filename)) allExtractsOk = False continue targetPathname = Filename(self.getPackageDir(), file.filename) targetPathname.setBinary() targetPathname.unlink() if not mf.extractSubfile(i, targetPathname): self.notify.warning("Couldn't extract: %s" % (file.filename)) allExtractsOk = False continue if not file.quickVerify(self.getPackageDir(), notify=self.notify): self.notify.warning("After extracting, still incorrect: %s" % (file.filename)) allExtractsOk = False continue # Make sure it's executable, and not writable. os.chmod(targetPathname.toOsSpecific(), 0o555) step.bytesDone += file.size self.__updateStepProgress(step) if taskMgr.destroyed: # If the task manager has been destroyed, we must # be shutting down. Get out of here. self.notify.warning( "Task Manager destroyed, aborting unpacking %s" % (mfPathname)) yield self.stepFailed return yield self.stepContinue if not allExtractsOk: yield self.stepFailed return self.hasPackage = True yield self.stepComplete return
def setP3DFilename(self, p3dFilename, tokens, argv, instanceId, interactiveConsole, p3dOffset=0, p3dUrl=None): """ Called by the browser to specify the p3d file that contains the application itself, along with the web tokens and/or command-line arguments. Once this method has been called, the application is effectively started. """ # One day we will have support for multiple instances within a # Python session. Against that day, we save the instance ID # for this instance. self.instanceId = instanceId self.tokens = tokens self.argv = argv # We build up a token dictionary with care, so that if a given # token appears twice in the token list, we record only the # first value, not the second or later. This is consistent # with the internal behavior of the core API. self.tokenDict = {} for token, keyword in tokens: self.tokenDict.setdefault(token, keyword) # Also store the arguments on sys, for applications that # aren't instance-ready. sys.argv = argv # That means we now know the altHost in effect. self.altHost = self.tokenDict.get('alt_host', None) # Tell the browser that Python is up and running, and ready to # respond to queries. self.notifyRequest('onpythonload') # Now go load the applet. fname = Filename.fromOsSpecific(p3dFilename) vfs = VirtualFileSystem.getGlobalPtr() if not vfs.exists(fname): raise ArgumentError, "No such file: %s" % (p3dFilename) fname.makeAbsolute() fname.setBinary() mf = Multifile() if p3dOffset == 0: if not mf.openRead(fname): raise ArgumentError, "Not a Panda3D application: %s" % ( p3dFilename) else: if not mf.openRead(fname, p3dOffset): raise ArgumentError, "Not a Panda3D application: %s at offset: %s" % ( p3dFilename, p3dOffset) # Now load the p3dInfo file. self.p3dInfo = None self.p3dPackage = None self.p3dConfig = None self.allowPythonDev = False i = mf.findSubfile('p3d_info.xml') if i >= 0 and hasattr(core, 'readXmlStream'): stream = mf.openReadSubfile(i) self.p3dInfo = core.readXmlStream(stream) mf.closeReadSubfile(stream) if self.p3dInfo: self.p3dPackage = self.p3dInfo.FirstChildElement('package') if self.p3dPackage: self.p3dConfig = self.p3dPackage.FirstChildElement('config') xhost = self.p3dPackage.FirstChildElement('host') while xhost: self.__readHostXml(xhost) xhost = xhost.NextSiblingElement('host') if self.p3dConfig: allowPythonDev = self.p3dConfig.Attribute('allow_python_dev') if allowPythonDev: self.allowPythonDev = int(allowPythonDev) guiApp = self.p3dConfig.Attribute('gui_app') if guiApp: self.guiApp = int(guiApp) trueFileIO = self.p3dConfig.Attribute('true_file_io') if trueFileIO: self.trueFileIO = int(trueFileIO) # The interactiveConsole flag can only be set true if the # application has allow_python_dev set. if not self.allowPythonDev and interactiveConsole: raise StandardError, "Impossible, interactive_console set without allow_python_dev." self.interactiveConsole = interactiveConsole if self.allowPythonDev: # Set the fps text to remind the user that # allow_python_dev is enabled. ConfigVariableString('frame-rate-meter-text-pattern').setValue( 'allow_python_dev %0.1f fps') if self.guiApp: init_app_for_gui() self.initPackedAppEnvironment() # Mount the Multifile under self.multifileRoot. vfs.mount(mf, self.multifileRoot, vfs.MFReadOnly) self.p3dMultifile = mf VFSImporter.reloadSharedPackages() self.loadMultifilePrcFiles(mf, self.multifileRoot) self.gotP3DFilename = True self.p3dFilename = fname if p3dUrl: # The url from which the p3d file was downloaded is # provided if available. It is only for documentation # purposes; the actual p3d file has already been # downloaded to p3dFilename. self.p3dUrl = core.URLSpec(p3dUrl) # Send this call to the main thread; don't call it directly. messenger.send('AppRunner_startIfReady', taskChain='default')