def buildDmg(startDir): fstartDir = Filename.fromOsSpecific(startDir) rootFilename = Filename(fstartDir, 'bundle') output = Filename(fstartDir, 'nppanda3d.dmg') output.unlink() cmd = 'hdiutil create -fs HFS+ -srcfolder "%(dir)s" -volname "%(volname)s" "%(output)s"' % { 'dir': rootFilename.toOsSpecific(), 'volname': 'nppanda3d', 'output': output.toOsSpecific(), } os.system(cmd)
def buildDmg(startDir): fstartDir = Filename.fromOsSpecific(startDir) rootFilename = Filename(fstartDir, 'bundle') output = Filename(fstartDir, 'nppanda3d.dmg') output.unlink() cmd = 'hdiutil create -fs HFS+ -srcfolder "%(dir)s" -volname "%(volname)s" "%(output)s"' % { 'dir' : rootFilename.toOsSpecific(), 'volname' : 'nppanda3d', 'output' : output.toOsSpecific(), } os.system(cmd)
def onSave(self, event=None): # dlg = wx.FileDialog(self.frame, "Choose a location to save the source file", \ # defaultDir= base.le.currentProj.dir.toOsSpecific(),\ # defaultFile = 'conversation' + '.xml', wildcard="*.xml", style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) dlg = wx.TextEntryDialog(self.frame, "Choose a name for this conversation", defaultValue=self.convoName) if dlg.ShowModal() == wx.ID_OK: name = dlg.GetValue() if name in base.le.lib.conversations: messageDlg = wx.MessageDialog(self.frame, "\"%s\" already exists, do you want to overwrite?" %(name)) if messageDlg.ShowModal() == wx.ID_OK: #print 'overwrite it' base.le.lib.removeConversation(name) else: #print 'do not overwrite it' messageDlg.Destroy() return else: self.convoName = name base.le.ui.SetCursor(wx.StockCursor(wx.CURSOR_WAIT)) #== Create temporary XML file for the asset doc = xml.dom.minidom.Document() root = doc.createElement("the_root") doc.appendChild(root) root.appendChild(self.conversation.encode(doc)) tempFilePath = base.le.lib.projDir.toOsSpecific() + '/' + name + '.xml' assetFilename = Filename(tempFilePath) #print 'temporary file path for asset: ' + assetFilename.toOsSpecific() f = open(assetFilename.toOsSpecific(), 'w') f.write(doc.toxml()) f.close() #== Make an Asset based on the temporary source file and add it to the library asset = ConversationAsset(name, assetFilename) base.le.lib.addConversation(asset, True) base.le.ui.storyObjUI.update() #== Remove the temporary source file tempFilename = Filename(tempFilePath) #print 'temporary file path to now delete: ' + tempFilename.toOsSpecific() os.remove(tempFilename.toOsSpecific()) self.changesSinceLastSave = False base.le.ui.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
def saveNewsCache(self): cacheIndexFilename = Filename(self.newsDir, self.CacheIndexFilename) try: file = open(cacheIndexFilename.toOsSpecific(), "w") except IOError, e: self.notify.warning("error opening news cache file %s: %s" % (cacheIndexFilename, str(e))) return
def addScene(self, name=None, sceneFile=None): if (name == None): suffix = len(self.scenes) defaultname = "default_" name = defaultname + str(suffix) sceneFile = Filename(name + ".scene") f = Filename(sceneFile) f.setDirname(self.dir.getFullpath()) while (os.path.exists(f.toOsSpecific()) or self.filenameIndexes.has_key( sceneFile.getBasenameWoExtension())): suffix += 1 name = defaultname + str(suffix) sceneFile = Filename(name + ".scene") f = Filename(sceneFile) f.setDirname(self.dir.getFullpath()) #if there is a name but no sceneFile given for that name #create a file with that name #TO DO: make it more error prone, for example this is not checking #if the file with the name already exist in the folder. This part may not be #be used at all but should be thought about. if (sceneFile == None): sceneFile = Filename(name + ".scene") self.scenes[name] = sceneFile self.filenameIndexes[sceneFile.getBasenameWoExtension()] = sceneFile if (len(self.scenes) == 1): #self.sceneFilename = sceneFile self.sceneName = name #f = Filename(sceneFile) #f.setDirname(self.dir.getFullpath()) self.scenesOrder = sorted(self.scenes) return (name, sceneFile)
def addScene(self,name=None, sceneFile=None): if(name == None): suffix = len(self.scenes) defaultname = "default_" name = defaultname+str(suffix) sceneFile = Filename(name+".scene") f = Filename(sceneFile) f.setDirname(self.dir.getFullpath()) while(os.path.exists(f.toOsSpecific())or self.filenameIndexes.has_key(sceneFile.getBasenameWoExtension())): suffix += 1 name = defaultname+str(suffix) sceneFile = Filename(name+".scene") f = Filename(sceneFile) f.setDirname(self.dir.getFullpath()) #if there is a name but no sceneFile given for that name #create a file with that name #TO DO: make it more error prone, for example this is not checking #if the file with the name already exist in the folder. This part may not be #be used at all but should be thought about. if(sceneFile == None): sceneFile = Filename(name+".scene") self.scenes[name] = sceneFile self.filenameIndexes[sceneFile.getBasenameWoExtension()] = sceneFile if(len(self.scenes) == 1 ): #self.sceneFilename = sceneFile self.sceneName = name #f = Filename(sceneFile) #f.setDirname(self.dir.getFullpath()) self.scenesOrder = sorted(self.scenes) return (name,sceneFile)
def saveNewsCache(self): """ Saves self.newsCache to cache_index.txt """ cacheIndexFilename = Filename(self.newsDir, self.CacheIndexFilename) file = open(cacheIndexFilename.toOsSpecific(), 'w') for filename, (size, date) in self.newsCache.items(): print >> file, '%s\t%s\t%s' % (filename, size, date)
def downloadDescFileGenerator(self, http): """ A generator function that implements downloadDescFile() one piece at a time. It yields one of stepComplete, stepFailed, or stepContinue. """ assert self.descFile if self.hasDescFile: # We've already got one. yield self.stepComplete return if self.host.appRunner and self.host.appRunner.verifyContents != self.host.appRunner.P3DVCNever: # We're allowed to download it. self.http = http func = lambda step, self=self: self.__downloadFile( None, self.descFile, urlbase=self.descFile.filename, filename=self.descFileBasename ) step = self.InstallStep(func, self.descFile.size, self.downloadFactor, "downloadDesc") for token in step.func(): if token == self.stepContinue: yield token else: break while token == self.restartDownload: # Try again. func = lambda step, self=self: self.__downloadFile( None, self.descFile, urlbase=self.descFile.filename, filename=self.descFileBasename ) step = self.InstallStep(func, self.descFile.size, self.downloadFactor, "downloadDesc") for token in step.func(): if token == self.stepContinue: yield token else: break if token == self.stepFailed: # Couldn't download the desc file. yield self.stepFailed return assert token == self.stepComplete filename = Filename(self.getPackageDir(), self.descFileBasename) # Now that we've written the desc file, make it read-only. os.chmod(filename.toOsSpecific(), 0444) if not self.__readDescFile(): # Weird, it passed the hash check, but we still can't read # it. filename = Filename(self.getPackageDir(), self.descFileBasename) self.notify.warning("Failure reading %s" % (filename)) yield self.stepFailed return yield self.stepComplete return
def saveNewsCache(self): cacheIndexFilename = Filename(self.newsDir, self.CacheIndexFilename) try: file = open(cacheIndexFilename.toOsSpecific(), 'w') except IOError, e: self.notify.warning('error opening news cache file %s: %s' % (cacheIndexFilename, str(e))) return
def __uncompressArchive(self, step): """ Turns the compressed archive into the uncompressed archive. Yields one of stepComplete, stepFailed, restartDownload, or stepContinue. """ 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 sourcePathname = Filename(self.getPackageDir(), self.compressedArchive.filename) targetPathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename) targetPathname.unlink() self.notify.info("Uncompressing %s to %s" % (sourcePathname, targetPathname)) decompressor = Decompressor() decompressor.initiate(sourcePathname, targetPathname) totalBytes = self.uncompressedArchive.size result = decompressor.run() while result == EUOk: step.bytesDone = int(totalBytes * decompressor.getProgress()) self.__updateStepProgress(step) result = decompressor.run() 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 decompresss %s" % (sourcePathname)) yield self.stepFailed return yield self.stepContinue if result != EUSuccess: yield self.stepFailed return step.bytesDone = totalBytes self.__updateStepProgress(step) if not self.uncompressedArchive.quickVerify(self.getPackageDir(), notify=self.notify): self.notify.warning("after uncompressing, %s still incorrect" % (self.uncompressedArchive.filename)) yield self.stepFailed return # Now that we've verified the archive, make it read-only. os.chmod(targetPathname.toOsSpecific(), 0444) # Now we can safely remove the compressed archive. sourcePathname.unlink() yield self.stepComplete return
def loadConversations(sceneFile, libraryFile): # Anton added 2/24 sceneFilename = Filename(sceneFile) libraryFilename = Filename.fromOsSpecific(os.getcwd()) + '/' + libraryFile lib = Library(Filename(libraryFilename.getDirname())) assetIndex = AssetIndex(lib) conversationAssets = assetIndex.conversations conversationObjects = {} for key, filename in conversationAssets.items(): xmlFilename = Filename(filename) doc = xml.dom.minidom.parse(xmlFilename.toOsSpecific()) Debug.debug(__name__,'loading '+str(xmlFilename.toOsSpecific())+'...') convo = Conversation.decode(doc) conversationObjects[key] = convo return conversationObjects
def __init__(self, pathname, ignoreUsageXml=False): self.pathname = pathname self.filenames = [] self.fileSize = 0 self.nested = [] self.nestedSize = 0 xusage = None if not ignoreUsageXml: # Look for a usage.xml file in this directory. If we find # one, we read it for the file size and then stop here, as # an optimization. usageFilename = Filename(pathname, 'usage.xml') doc = TiXmlDocument(usageFilename.toOsSpecific()) if doc.LoadFile(): xusage = doc.FirstChildElement('usage') if xusage: diskSpace = xusage.Attribute('disk_space') try: diskSpace = int(diskSpace or '') except ValueError: diskSpace = None if diskSpace is not None: self.fileSize = diskSpace return files = vfs.scanDirectory(self.pathname) if files is None: files = [] for vfile in files: if hasattr(vfile, 'getMount'): if not isinstance(vfile.getMount(), VirtualFileMountSystem): # Not a real file; ignore it. continue if vfile.isDirectory(): # A nested directory. subdir = ScanDirectoryNode(vfile.getFilename(), ignoreUsageXml=ignoreUsageXml) self.nested.append(subdir) self.nestedSize += subdir.getTotalSize() elif vfile.isRegularFile(): # A nested file. self.filenames.append(vfile.getFilename()) self.fileSize += vfile.getFileSize() else: # Some other wacky file thing. self.filenames.append(vfile.getFilename()) if xusage: # Now update the usage.xml file with the newly-determined # disk space. xusage.SetAttribute('disk_space', str(self.getTotalSize())) tfile = Filename.temporary(pathname.cStr(), '.xml') if doc.SaveFile(tfile.toOsSpecific()): tfile.renameTo(usageFilename)
def exportScripts(self, file): outputLines = [] tab = " " outputLines.append("SCRIPTS_LIST = []\n\n") #whole file for script, asset in self.editor.lib.scripts.iteritems(): #writing a script file to the whole file filename = self.editor.lib.scripts[script].getFullFilename() scriptFile = open(filename.toOsSpecific()) lines = scriptFile.readlines() mainArguments = self.getArgumentsFromScriptFile(lines) scriptFunctionName = script if (len(mainArguments) == 0): prefix = "world" else: prefix = "world, " functionHeader = "\ndef " + scriptFunctionName + "(" + prefix + ",".join( mainArguments) + "):\n" mainFunc = "main(" + prefix + ",".join(mainArguments) + ")" mainFuncHeader = "def " + mainFunc + ":\n" outputLines.append(functionHeader) isReturn = False for line in lines: #print line if line.strip().startswith("#"): continue if line.strip().startswith("def main"): newline = mainFuncHeader elif line.find("Interface") != -1: newline = line.replace('Interface', "world.scriptInterface") else: newline = line if line.strip().startswith("return"): isReturn = True outputLines.append(tab + newline) if (isReturn): outputLines.append("\n" + tab + "return " + mainFunc) else: outputLines.append("\n" + tab + mainFunc) outputLines.append("\nSCRIPTS_LIST.append(" + scriptFunctionName + ")\n") scriptFile.close() scriptsFilename = Filename( Filename.fromOsSpecific(file).getDirname() + '/Scripts.py') try: scriptsFile = open(scriptsFilename.toOsSpecific(), 'w') except IOError: print "ERROR: Couldn't open the script file for writing" scriptsFile.writelines(outputLines) scriptsFile.close() return scriptsFilename
def exportScripts(self, file): outputLines = [] tab = " " outputLines.append("SCRIPTS_LIST = []\n\n") #whole file for script, asset in self.editor.lib.scripts.iteritems(): #writing a script file to the whole file filename = self.editor.lib.scripts[script].getFullFilename() scriptFile = open(filename.toOsSpecific()) lines = scriptFile.readlines() mainArguments = self.getArgumentsFromScriptFile(lines) scriptFunctionName = script if(len(mainArguments) == 0): prefix = "world" else: prefix = "world, " functionHeader = "\ndef "+scriptFunctionName+"("+prefix+",".join(mainArguments)+"):\n" mainFunc = "main("+prefix+",".join(mainArguments)+")" mainFuncHeader = "def "+mainFunc+":\n" outputLines.append(functionHeader) isReturn = False for line in lines: #print line if line.strip().startswith("#"): continue if line.strip().startswith("def main"): newline = mainFuncHeader elif line.find("Interface")!=-1: newline = line.replace('Interface', "world.scriptInterface") else: newline = line if line.strip().startswith("return"): isReturn = True outputLines.append(tab+newline) if(isReturn): outputLines.append("\n"+tab+"return "+mainFunc) else: outputLines.append("\n"+tab+mainFunc) outputLines.append("\nSCRIPTS_LIST.append("+scriptFunctionName+")\n") scriptFile.close() scriptsFilename = Filename(Filename.fromOsSpecific(file).getDirname()+'/Scripts.py') try: scriptsFile = open(scriptsFilename.toOsSpecific(), 'w') except IOError: print "ERROR: Couldn't open the script file for writing" scriptsFile.writelines(outputLines) scriptsFile.close() return scriptsFilename
def loadConversations(sceneFile, libraryFile): # Anton added 2/24 sceneFilename = Filename(sceneFile) libraryFilename = Filename.fromOsSpecific(os.getcwd()) + '/' + libraryFile lib = Library(Filename(libraryFilename.getDirname())) assetIndex = AssetIndex(lib) conversationAssets = assetIndex.conversations conversationObjects = {} for key, filename in conversationAssets.items(): xmlFilename = Filename(filename) doc = xml.dom.minidom.parse(xmlFilename.toOsSpecific()) Debug.debug(__name__, 'loading ' + str(xmlFilename.toOsSpecific()) + '...') convo = Conversation.decode(doc) conversationObjects[key] = convo return conversationObjects
def __init__(self, pathname, ignoreUsageXml = False): self.pathname = pathname self.filenames = [] self.fileSize = 0 self.nested = [] self.nestedSize = 0 xusage = None if not ignoreUsageXml: # Look for a usage.xml file in this directory. If we find # one, we read it for the file size and then stop here, as # an optimization. usageFilename = Filename(pathname, 'usage.xml') doc = TiXmlDocument(usageFilename.toOsSpecific()) if doc.LoadFile(): xusage = doc.FirstChildElement('usage') if xusage: diskSpace = xusage.Attribute('disk_space') try: diskSpace = int(diskSpace or '') except ValueError: diskSpace = None if diskSpace is not None: self.fileSize = diskSpace return files = vfs.scanDirectory(self.pathname) if files is None: files = [] for vfile in files: if hasattr(vfile, 'getMount'): if not isinstance(vfile.getMount(), VirtualFileMountSystem): # Not a real file; ignore it. continue if vfile.isDirectory(): # A nested directory. subdir = ScanDirectoryNode(vfile.getFilename(), ignoreUsageXml = ignoreUsageXml) self.nested.append(subdir) self.nestedSize += subdir.getTotalSize() elif vfile.isRegularFile(): # A nested file. self.filenames.append(vfile.getFilename()) self.fileSize += vfile.getFileSize() else: # Some other wacky file thing. self.filenames.append(vfile.getFilename()) if xusage: # Now update the usage.xml file with the newly-determined # disk space. xusage.SetAttribute('disk_space', str(self.getTotalSize())) tfile = Filename.temporary(pathname.cStr(), '.xml') if doc.SaveFile(tfile.toOsSpecific()): tfile.renameTo(usageFilename)
def saveAs(self, newDir, newName, keepOld=False): if keepOld: shutil.copytree(self.dir.toOsSpecific(), newDir.toOsSpecific()) else: if not os.path.exists(newDir.toOsSpecific()): os.makedirs(newDir.toOsSpecific()) for x in os.listdir(self.dir.toOsSpecific()): shutil.move((self.dir + '/' + x).toOsSpecific(), (newDir + '/' + x).toOsSpecific()) #make sure everything in the new directory is not marked read only for root, dirs, files in os.walk(newDir.toOsSpecific()): for name in files: os.chmod(os.path.join(root, name), stat.S_IWRITE) newFilename = newDir + '/' + newName + '.proj' newSceneFilename = Filename(newName + '.scene') newJournalFilename = Filename(newName + '.journal') newInventoryMapFilename = Filename(newName + '.inventory') f = Filename(self.filename) f.setDirname(newDir.getFullpath()) f2 = Filename(self.scenes[self.sceneName]) #self.sceneFilename) f2.setDirname(newDir.getFullpath()) f3 = Filename(self.journalFilename) f3.setDirname(newDir.getFullpath()) f4 = Filename(self.inventoryMapFilename) f4.setDirname(newDir.getFullpath()) #make sure we don't end up with a project or scene file with the old name #in the new directory if os.path.exists(f.toOsSpecific()): os.remove(f.toOsSpecific()) if os.path.exists(f2.toOsSpecific()): os.remove(f2.toOsSpecific()) if os.path.exists(f3.toOsSpecific()): os.remove(f3.toOsSpecific()) if os.path.exists(f4.toOsSpecific()): os.remove(f4.toOsSpecific()) self.name = newName self.dir = newDir self.filename = newFilename #self.scenes["default"] = newSceneFilename #self.sceneFilename = newSceneFilename self.journalFilename = newJournalFilename self.inventoryMapFilename = newInventoryMapFilename self.lib.moveTo(newDir) self.saveToFile()
def saveAs(self, newDir, newName, keepOld=False): if keepOld: shutil.copytree(self.dir.toOsSpecific(), newDir.toOsSpecific()) else: if not os.path.exists(newDir.toOsSpecific()): os.makedirs(newDir.toOsSpecific()) for x in os.listdir(self.dir.toOsSpecific()): shutil.move((self.dir + '/' + x).toOsSpecific(), (newDir + '/' + x).toOsSpecific()) #make sure everything in the new directory is not marked read only for root, dirs, files in os.walk(newDir.toOsSpecific()): for name in files: os.chmod(os.path.join(root, name), stat.S_IWRITE) newFilename = newDir + '/' +newName + '.proj' newSceneFilename = Filename(newName + '.scene') newJournalFilename = Filename(newName + '.journal') newInventoryMapFilename = Filename(newName + '.inventory') f = Filename(self.filename) f.setDirname(newDir.getFullpath()) f2 = Filename(self.scenes[self.sceneName])#self.sceneFilename) f2.setDirname(newDir.getFullpath()) f3 = Filename(self.journalFilename) f3.setDirname(newDir.getFullpath()) f4 = Filename(self.inventoryMapFilename) f4.setDirname(newDir.getFullpath()) #make sure we don't end up with a project or scene file with the old name #in the new directory if os.path.exists(f.toOsSpecific()): os.remove(f.toOsSpecific()) if os.path.exists(f2.toOsSpecific()): os.remove(f2.toOsSpecific()) if os.path.exists(f3.toOsSpecific()): os.remove(f3.toOsSpecific()) if os.path.exists(f4.toOsSpecific()): os.remove(f4.toOsSpecific()) self.name = newName self.dir = newDir self.filename = newFilename #self.scenes["default"] = newSceneFilename #self.sceneFilename = newSceneFilename self.journalFilename = newJournalFilename self.inventoryMapFilename = newInventoryMapFilename self.lib.moveTo(newDir) self.saveToFile()
def makeBundle(startDir): fstartDir = Filename.fromOsSpecific(startDir) # Search for panda3d_mac along $PATH. path = DSearchPath() if 'PATH' in os.environ: path.appendPath(os.environ['PATH']) path.appendPath(os.defpath) panda3d_mac = path.findFile('panda3d_mac') if not panda3d_mac: raise StandardError, "Couldn't find panda3d_mac on path." # Construct a search path to look for the images. search = DSearchPath() # First on the path: an explicit $PLUGIN_IMAGES env var. if ExecutionEnvironment.hasEnvironmentVariable('PLUGIN_IMAGES'): search.appendDirectory(Filename.expandFrom('$PLUGIN_IMAGES')) # Next on the path: the models/plugin_images directory within the # current directory. search.appendDirectory('models/plugin_images') # Finally on the path: models/plugin_images within the model # search path. for dir in getModelPath().getDirectories(): search.appendDirectory(Filename(dir, 'plugin_images')) # Now find the icon file on the above search path. icons = search.findFile('panda3d.icns') if not icons: raise StandardError, "Couldn't find panda3d.icns on model-path." # Generate the bundle directory structure rootFilename = Filename(fstartDir) bundleFilename = Filename(rootFilename, 'Panda3D.app') if os.path.exists(bundleFilename.toOsSpecific()): shutil.rmtree(bundleFilename.toOsSpecific()) plistFilename = Filename(bundleFilename, 'Contents/Info.plist') plistFilename.makeDir() exeFilename = Filename(bundleFilename, 'Contents/MacOS/panda3d_mac') exeFilename.makeDir() iconFilename = Filename(bundleFilename, 'Contents/Resources/panda3d.icns') iconFilename.makeDir() # Copy in Info.plist, the icon file, and the compiled executable. shutil.copyfile( Filename(fstartDir, "panda3d_mac.plist").toOsSpecific(), plistFilename.toOsSpecific()) shutil.copyfile(icons.toOsSpecific(), iconFilename.toOsSpecific()) print panda3d_mac, exeFilename shutil.copyfile(panda3d_mac.toOsSpecific(), exeFilename.toOsSpecific()) os.chmod(exeFilename.toOsSpecific(), 0755) # All done! bundleFilename.touch() print bundleFilename.toOsSpecific()
def saveNewsCache(self): cacheIndexFilename = Filename(self.newsDir, self.CacheIndexFilename) try: file = open(cacheIndexFilename.toOsSpecific(), 'w') except IOError as e: self.notify.warning('error opening news cache file %s: %s' % (cacheIndexFilename, str(e))) return for filename, (size, date) in self.newsCache.items(): print >> file, '%s\t%s\t%s' % (filename, size, date)
def saveNewsCache(self): cacheIndexFilename = Filename(self.newsDir, self.CacheIndexFilename) try: file = open(cacheIndexFilename.toOsSpecific(), 'w') except IOError as e: self.notify.warning('error opening news cache file %s: %s' % (cacheIndexFilename, str(e))) return for filename, (size, date) in self.newsCache.items(): print >> file, '%s\t%s\t%s' % (filename, size, date)
def buildArch(self, output, platform): """ Builds an ArchLinux package and stores it in the path indicated by the 'output' argument. It will be built for the architecture specified by the 'arch' argument. If 'output' is a directory, the deb file will be stored in it. """ arch = platform.rsplit("_", 1)[-1] assert arch in ("i386", "amd64") arch = {"i386": "i686", "amd64": "x86_64"}[arch] pkgver = self.version + "-1" output = Filename(output) if output.isDirectory(): output = Filename( output, "%s-%s-%s.pkg.tar.gz" % (self.shortname.lower(), pkgver, arch)) Installer.notify.info("Creating %s..." % output) modtime = int(time.time()) # Create a temporary directory and write the launcher and dependencies to it. tempdir, totsize = self.__buildTempLinux(platform) # Create a pkginfo file in memory. pkginfo = StringIO() print >> pkginfo, "# Generated using pdeploy" print >> pkginfo, "# %s" % time.ctime(modtime) print >> pkginfo, "pkgname = %s" % self.shortname.lower() print >> pkginfo, "pkgver = %s" % pkgver print >> pkginfo, "pkgdesc = %s" % self.fullname print >> pkginfo, "builddate = %s" % modtime print >> pkginfo, "packager = %s <%s>" % (self.authorname, self.authoremail) print >> pkginfo, "size = %d" % totsize print >> pkginfo, "arch = %s" % arch if self.licensename != "": print >> pkginfo, "license = %s" % self.licensename pkginfoinfo = TarInfoRoot(".PKGINFO") pkginfoinfo.mtime = modtime pkginfoinfo.size = pkginfo.tell() pkginfo.seek(0) # Create the actual package now. pkgfile = tarfile.open(output.toOsSpecific(), "w:gz", tarinfo=TarInfoRoot) pkgfile.addfile(pkginfoinfo, pkginfo) pkgfile.add(tempdir.toOsSpecific(), "/") if not self.licensefile.empty(): pkgfile.add( self.licensefile.toOsSpecific(), "/usr/share/licenses/%s/LICENSE" % self.shortname.lower()) pkgfile.close() return output
def makeBundle(startDir): fstartDir = Filename.fromOsSpecific(startDir) # Search for panda3d_mac along $PATH. path = DSearchPath() if 'PATH' in os.environ: path.appendPath(os.environ['PATH']) path.appendPath(os.defpath) panda3d_mac = path.findFile('panda3d_mac') if not panda3d_mac: raise StandardError, "Couldn't find panda3d_mac on path." # Construct a search path to look for the images. search = DSearchPath() # First on the path: an explicit $PLUGIN_IMAGES env var. if ExecutionEnvironment.hasEnvironmentVariable('PLUGIN_IMAGES'): search.appendDirectory(Filename.expandFrom('$PLUGIN_IMAGES')) # Next on the path: the models/plugin_images directory within the # current directory. search.appendDirectory('models/plugin_images') # Finally on the path: models/plugin_images within the model # search path. for dir in getModelPath().getDirectories(): search.appendDirectory(Filename(dir, 'plugin_images')) # Now find the icon file on the above search path. icons = search.findFile('panda3d.icns') if not icons: raise StandardError, "Couldn't find panda3d.icns on model-path." # Generate the bundle directory structure rootFilename = Filename(fstartDir) bundleFilename = Filename(rootFilename, 'Panda3D.app') if os.path.exists(bundleFilename.toOsSpecific()): shutil.rmtree(bundleFilename.toOsSpecific()) plistFilename = Filename(bundleFilename, 'Contents/Info.plist') plistFilename.makeDir() exeFilename = Filename(bundleFilename, 'Contents/MacOS/panda3d_mac') exeFilename.makeDir() iconFilename = Filename(bundleFilename, 'Contents/Resources/panda3d.icns') iconFilename.makeDir() # Copy in Info.plist, the icon file, and the compiled executable. shutil.copyfile(Filename(fstartDir, "panda3d_mac.plist").toOsSpecific(), plistFilename.toOsSpecific()) shutil.copyfile(icons.toOsSpecific(), iconFilename.toOsSpecific()) print panda3d_mac, exeFilename shutil.copyfile(panda3d_mac.toOsSpecific(), exeFilename.toOsSpecific()) os.chmod(exeFilename.toOsSpecific(), 0755) # All done! bundleFilename.touch() print bundleFilename.toOsSpecific()
def quickVerify(self, packageDir = None, pathname = None, notify = None): """ Performs a quick test to ensure the file has not been modified. This test is vulnerable to people maliciously attempting to fool the program (by setting datestamps etc.). Returns true if it is intact, false if it needs to be redownloaded. """ if not pathname: pathname = Filename(packageDir, self.filename) try: st = os.stat(pathname.toOsSpecific()) except OSError: # If the file is missing, the file fails. if notify: notify.debug("file not found: %s" % (pathname)) return False if st.st_size != self.size: # If the size is wrong, the file fails. if notify: notify.debug("size wrong: %s" % (pathname)) return False if st.st_mtime == self.timestamp: # If the size is right and the timestamp is right, the # file passes. if notify: notify.debug("file ok: %s" % (pathname)) return True if notify: notify.debug("modification time wrong: %s" % (pathname)) # If the size is right but the timestamp is wrong, the file # soft-fails. We follow this up with a hash check. if not self.checkHash(packageDir, pathname, st): # Hard fail, the hash is wrong. if notify: notify.debug("hash check wrong: %s" % (pathname)) notify.debug(" found %s, expected %s" % (self.actualFile.hash, self.hash)) return False if notify: notify.info("hash check ok: %s" % (pathname)) # The hash is OK after all. Change the file's timestamp back # to what we expect it to be, so we can quick-verify it # successfully next time. self.__updateTimestamp(pathname, st) return True
def quickVerify(self, packageDir=None, pathname=None, notify=None): """ Performs a quick test to ensure the file has not been modified. This test is vulnerable to people maliciously attempting to fool the program (by setting datestamps etc.). Returns true if it is intact, false if it needs to be redownloaded. """ if not pathname: pathname = Filename(packageDir, self.filename) try: st = os.stat(pathname.toOsSpecific()) except OSError: # If the file is missing, the file fails. if notify: notify.debug("file not found: %s" % (pathname)) return False if st.st_size != self.size: # If the size is wrong, the file fails. if notify: notify.debug("size wrong: %s" % (pathname)) return False if st.st_mtime == self.timestamp: # If the size is right and the timestamp is right, the # file passes. if notify: notify.debug("file ok: %s" % (pathname)) return True if notify: notify.debug("modification time wrong: %s" % (pathname)) # If the size is right but the timestamp is wrong, the file # soft-fails. We follow this up with a hash check. if not self.checkHash(packageDir, pathname, st): # Hard fail, the hash is wrong. if notify: notify.debug("hash check wrong: %s" % (pathname)) notify.debug(" found %s, expected %s" % (self.actualFile.hash, self.hash)) return False if notify: notify.info("hash check ok: %s" % (pathname)) # The hash is OK after all. Change the file's timestamp back # to what we expect it to be, so we can quick-verify it # successfully next time. self.__updateTimestamp(pathname, st) return True
def __uncompressArchive(self, step): """ Turns the compressed archive into the uncompressed archive. Yields one of stepComplete, stepFailed, restartDownload, or stepContinue. """ 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 sourcePathname = Filename(self.getPackageDir(), self.compressedArchive.filename) targetPathname = Filename(self.getPackageDir(), self.uncompressedArchive.filename) targetPathname.unlink() self.notify.info("Uncompressing %s to %s" % (sourcePathname, targetPathname)) decompressor = Decompressor() decompressor.initiate(sourcePathname, targetPathname) totalBytes = self.uncompressedArchive.size result = decompressor.run() while result == EUOk: step.bytesDone = int(totalBytes * decompressor.getProgress()) self.__updateStepProgress(step) result = decompressor.run() 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 decompresss %s" % (sourcePathname)) yield self.stepFailed return yield self.stepContinue if result != EUSuccess: yield self.stepFailed return step.bytesDone = totalBytes self.__updateStepProgress(step) if not self.uncompressedArchive.quickVerify(self.getPackageDir(), notify=self.notify): self.notify.warning("after uncompressing, %s still incorrect" % (self.uncompressedArchive.filename)) yield self.stepFailed return # Now that we've verified the archive, make it read-only. os.chmod(targetPathname.toOsSpecific(), 0444) # Now we can safely remove the compressed archive. sourcePathname.unlink() yield self.stepComplete return
def onOpenConvoEditor(self, x, item): convoKey = self.treeStoryObj.GetItemText(item) #print "treeStoryObj type: %s" %(type(self.treeStoryObj)) #print 'convo key: %s' %(convoKey) if convoKey in self.lib.conversations: convoFilename = Filename(self.editor.lib.conversations[convoKey].getFullFilename()) f = open(convoFilename.toOsSpecific()) doc = xml.dom.minidom.parse(f) root = doc.documentElement convo = Conversation.decode(doc) f.close() else: convo = None editor = ConversationEditor(self, self.editor, convo, convoKey)
def onOpenConvoEditor(self, x, item): convoKey = self.treeStoryObj.GetItemText(item) #print "treeStoryObj type: %s" %(type(self.treeStoryObj)) #print 'convo key: %s' %(convoKey) if convoKey in self.lib.conversations: convoFilename = Filename( self.editor.lib.conversations[convoKey].getFullFilename()) f = open(convoFilename.toOsSpecific()) doc = xml.dom.minidom.parse(f) root = doc.documentElement convo = Conversation.decode(doc) f.close() else: convo = None editor = ConversationEditor(self, self.editor, convo, convoKey)
def buildArch(self, output, platform): """ Builds an ArchLinux package and stores it in the path indicated by the 'output' argument. It will be built for the architecture specified by the 'arch' argument. If 'output' is a directory, the deb file will be stored in it. """ arch = platform.rsplit("_", 1)[-1] assert arch in ("i386", "amd64") arch = {"i386": "i686", "amd64": "x86_64"}[arch] pkgver = self.version + "-1" output = Filename(output) if output.isDirectory(): output = Filename(output, "%s-%s-%s.pkg.tar.gz" % (self.shortname.lower(), pkgver, arch)) Installer.notify.info("Creating %s..." % output) modtime = int(time.time()) # Create a temporary directory and write the launcher and dependencies to it. tempdir, totsize = self.__buildTempLinux(platform) # Create a pkginfo file in memory. pkginfo = StringIO() print >> pkginfo, "# Generated using pdeploy" print >> pkginfo, "# %s" % time.ctime(modtime) print >> pkginfo, "pkgname = %s" % self.shortname.lower() print >> pkginfo, "pkgver = %s" % pkgver print >> pkginfo, "pkgdesc = %s" % self.fullname print >> pkginfo, "builddate = %s" % modtime print >> pkginfo, "packager = %s <%s>" % (self.authorname, self.authoremail) print >> pkginfo, "size = %d" % totsize print >> pkginfo, "arch = %s" % arch if self.licensename != "": print >> pkginfo, "license = %s" % self.licensename pkginfoinfo = TarInfoRoot(".PKGINFO") pkginfoinfo.mtime = modtime pkginfoinfo.size = pkginfo.tell() pkginfo.seek(0) # Create the actual package now. pkgfile = tarfile.open(output.toOsSpecific(), "w:gz", tarinfo=TarInfoRoot) pkgfile.addfile(pkginfoinfo, pkginfo) pkgfile.add(tempdir.toOsSpecific(), "/") if not self.licensefile.empty(): pkgfile.add(self.licensefile.toOsSpecific(), "/usr/share/licenses/%s/LICENSE" % self.shortname.lower()) pkgfile.close() return output
def getUsage(self): """ Returns the xusage element that is read from the usage.xml file, or None if there is no usage.xml file. """ if not hasattr(PandaModules, 'TiXmlDocument'): return None filename = Filename(self.getPackageDir(), self.UsageBasename) doc = TiXmlDocument(filename.toOsSpecific()) if not doc.LoadFile(): return None xusage = doc.FirstChildElement('usage') if not xusage: return None return copy.copy(xusage)
def getUsage(self): """ Returns the xusage element that is read from the usage.xml file, or None if there is no usage.xml file. """ if not hasattr(PandaModules, 'TiXmlDocument'): return None filename = Filename(self.getPackageDir(), self.UsageBasename) doc = TiXmlDocument(filename.toOsSpecific()) if not doc.LoadFile(): return None xusage = doc.FirstChildElement('usage') if not xusage: return None return copy.copy(xusage)
def removeScene(self, sceneName, delFile = False): if(len(self.scenes)==1): raise SceneDeleteError() else: #delete the file in the folder too. if delFile: toDel = Filename(self.dir.getFullpath() + '/' + self.scenes[sceneName].getFullpath()) try: os.remove(toDel.toOsSpecific()) except OSError as e: pass if(self.sceneName == sceneName): self.sceneName = self.scenesOrder[0] #self.sceneFilename = self.scenes[self.sceneName] del self.filenameIndexes[self.scenes[sceneName].getBasenameWoExtension()] del self.scenes[sceneName] self.scenesOrder = sorted(self.scenes)
def readNewsCache(self): cacheIndexFilename = Filename(self.newsDir, self.CacheIndexFilename) self.newsCache = {} if cacheIndexFilename.isRegularFile(): file = open(cacheIndexFilename.toOsSpecific(), 'r') for line in file.readlines(): line = line.strip() keywords = line.split('\t') if len(keywords) == 3: filename, size, date = keywords if filename in self.newsFiles: try: size = int(size) except ValueError: size = 0 self.newsCache[filename] = (size, date)
def readNewsCache(self): cacheIndexFilename = Filename(self.newsDir, self.CacheIndexFilename) self.newsCache = {} if cacheIndexFilename.isRegularFile(): file = open(cacheIndexFilename.toOsSpecific(), "r") for line in file.readlines(): line = line.strip() keywords = line.split("\t") if len(keywords) == 3: filename, size, date = keywords if filename in self.newsFiles: try: size = int(size) except ValueError: size = 0 self.newsCache[filename] = (size, date)
def removeScene(self, sceneName, delFile=False): if (len(self.scenes) == 1): raise SceneDeleteError() else: #delete the file in the folder too. if delFile: toDel = Filename(self.dir.getFullpath() + '/' + self.scenes[sceneName].getFullpath()) try: os.remove(toDel.toOsSpecific()) except OSError as e: pass if (self.sceneName == sceneName): self.sceneName = self.scenesOrder[0] #self.sceneFilename = self.scenes[self.sceneName] del self.filenameIndexes[ self.scenes[sceneName].getBasenameWoExtension()] del self.scenes[sceneName] self.scenesOrder = sorted(self.scenes)
def fromFile(self, packageDir, filename, pathname=None, st=None): """ Reads the file information from the indicated file. If st is supplied, it is the result of os.stat on the filename. """ vfs = VirtualFileSystem.getGlobalPtr() filename = Filename(filename) if pathname is None: pathname = Filename(packageDir, filename) self.filename = filename.cStr() self.basename = filename.getBasename() if st is None: st = os.stat(pathname.toOsSpecific()) self.size = st.st_size self.timestamp = st.st_mtime self.readHash(pathname)
def fromFile(self, packageDir, filename, pathname = None, st = None): """ Reads the file information from the indicated file. If st is supplied, it is the result of os.stat on the filename. """ vfs = VirtualFileSystem.getGlobalPtr() filename = Filename(filename) if pathname is None: pathname = Filename(packageDir, filename) self.filename = filename.cStr() self.basename = filename.getBasename() if st is None: st = os.stat(pathname.toOsSpecific()) self.size = st.st_size self.timestamp = st.st_mtime self.readHash(pathname)
def fullVerify(self, packageDir=None, pathname=None, notify=None): """ Performs a more thorough test to ensure the file has not been modified. This test is less vulnerable to malicious attacks, since it reads and verifies the entire file. Returns true if it is intact, false if it needs to be redownloaded. """ if not pathname: pathname = Filename(packageDir, self.filename) try: st = os.stat(pathname.toOsSpecific()) except OSError: # If the file is missing, the file fails. if notify: notify.debug("file not found: %s" % (pathname)) return False if st.st_size != self.size: # If the size is wrong, the file fails; if notify: notify.debug("size wrong: %s" % (pathname)) return False if not self.checkHash(packageDir, pathname, st): # Hard fail, the hash is wrong. if notify: notify.debug("hash check wrong: %s" % (pathname)) notify.debug(" found %s, expected %s" % (self.actualFile.hash, self.hash)) return False if notify: notify.debug("hash check ok: %s" % (pathname)) # The hash is OK. If the timestamp is wrong, change it back # to what we expect it to be, so we can quick-verify it # successfully next time. if st.st_mtime != self.timestamp: self.__updateTimestamp(pathname, st) return True
def __buildTempLinux(self, platform): """ Builds a filesystem for Linux. Used so that buildDEB, buildRPM and buildArch can share the same temp directory. """ if self.__linuxRoot is not None: return self.__linuxRoot tempdir = Filename(self.tempDir, platform) tempdir.makeDir() Filename(tempdir, "usr/bin/").makeDir() if self.includeRequires: extraTokens = {"host_dir": "/usr/lib/" + self.shortname.lower()} else: extraTokens = {} self.standalone.build( Filename(tempdir, "usr/bin/" + self.shortname.lower()), platform, extraTokens) if not self.licensefile.empty(): Filename(tempdir, "usr/share/doc/%s/" % self.shortname.lower()).makeDir() shutil.copyfile( self.licensefile.toOsSpecific(), Filename(tempdir, "usr/share/doc/%s/copyright" % self.shortname.lower()).toOsSpecific()) shutil.copyfile( self.licensefile.toOsSpecific(), Filename(tempdir, "usr/share/doc/%s/LICENSE" % self.shortname.lower()).toOsSpecific()) if self.includeRequires: hostDir = Filename(tempdir, "usr/lib/" + self.shortname.lower()) hostDir.makeDir() self.installPackagesInto(hostDir, platform) totsize = 0 for root, dirs, files in self.os_walk(tempdir.toOsSpecific()): for name in files: totsize += os.path.getsize(os.path.join(root, name)) self.__linuxRoot = (tempdir, totsize) return self.__linuxRoot
def readConfigXml(self): """ Reads the config.xml file that may be present in the root directory. """ if not hasattr(PandaModules, 'TiXmlDocument'): return from pandac.PandaModules import TiXmlDocument filename = Filename(self.rootDir, self.ConfigBasename) doc = TiXmlDocument(filename.toOsSpecific()) if not doc.LoadFile(): return xconfig = doc.FirstChildElement('config') if xconfig: maxDiskUsage = xconfig.Attribute('max_disk_usage') try: self.maxDiskUsage = int(maxDiskUsage or '') except ValueError: pass
def readConfigXml(self): """ Reads the config.xml file that may be present in the root directory. """ if not hasattr(PandaModules, 'TiXmlDocument'): return from pandac.PandaModules import TiXmlDocument filename = Filename(self.rootDir, self.ConfigBasename) doc = TiXmlDocument(filename.toOsSpecific()) if not doc.LoadFile(): return xconfig = doc.FirstChildElement('config') if xconfig: maxDiskUsage = xconfig.Attribute('max_disk_usage') try: self.maxDiskUsage = int(maxDiskUsage or '') except ValueError: pass
def fullVerify(self, packageDir = None, pathname = None, notify = None): """ Performs a more thorough test to ensure the file has not been modified. This test is less vulnerable to malicious attacks, since it reads and verifies the entire file. Returns true if it is intact, false if it needs to be redownloaded. """ if not pathname: pathname = Filename(packageDir, self.filename) try: st = os.stat(pathname.toOsSpecific()) except OSError: # If the file is missing, the file fails. if notify: notify.debug("file not found: %s" % (pathname)) return False if st.st_size != self.size: # If the size is wrong, the file fails; if notify: notify.debug("size wrong: %s" % (pathname)) return False if not self.checkHash(packageDir, pathname, st): # Hard fail, the hash is wrong. if notify: notify.debug("hash check wrong: %s" % (pathname)) notify.debug(" found %s, expected %s" % (self.actualFile.hash, self.hash)) return False if notify: notify.info("hash check ok: %s" % (pathname)) # The hash is OK. If the timestamp is wrong, change it back # to what we expect it to be, so we can quick-verify it # successfully next time. if st.st_mtime != self.timestamp: self.__updateTimestamp(pathname, st) return True
def __buildTempLinux(self, platform): """ Builds a filesystem for Linux. Used so that buildDEB, buildRPM and buildArch can share the same temp directory. """ if self.__linuxRoot is not None: return self.__linuxRoot tempdir = Filename(self.tempDir, platform) tempdir.makeDir() Filename(tempdir, "usr/bin/").makeDir() if self.includeRequires: extraTokens = {"host_dir": "/usr/lib/" + self.shortname.lower()} else: extraTokens = {} self.standalone.build(Filename(tempdir, "usr/bin/" + self.shortname.lower()), platform, extraTokens) if not self.licensefile.empty(): Filename(tempdir, "usr/share/doc/%s/" % self.shortname.lower()).makeDir() shutil.copyfile( self.licensefile.toOsSpecific(), Filename(tempdir, "usr/share/doc/%s/copyright" % self.shortname.lower()).toOsSpecific(), ) shutil.copyfile( self.licensefile.toOsSpecific(), Filename(tempdir, "usr/share/doc/%s/LICENSE" % self.shortname.lower()).toOsSpecific(), ) if self.includeRequires: hostDir = Filename(tempdir, "usr/lib/" + self.shortname.lower()) hostDir.makeDir() self.installPackagesInto(hostDir, platform) totsize = 0 for root, dirs, files in self.os_walk(tempdir.toOsSpecific()): for name in files: totsize += os.path.getsize(os.path.join(root, name)) self.__linuxRoot = (tempdir, totsize) return self.__linuxRoot
def writeConfigXml(self): """ Rewrites the config.xml to the root directory. This isn't called automatically; an application may call this after adjusting some parameters (such as self.maxDiskUsage). """ from pandac.PandaModules import TiXmlDocument, TiXmlDeclaration, TiXmlElement filename = Filename(self.rootDir, self.ConfigBasename) doc = TiXmlDocument(filename.toOsSpecific()) decl = TiXmlDeclaration("1.0", "utf-8", "") doc.InsertEndChild(decl) xconfig = TiXmlElement('config') xconfig.SetAttribute('max_disk_usage', str(self.maxDiskUsage)) doc.InsertEndChild(xconfig) # Write the file to a temporary filename, then atomically move # it to its actual filename, to avoid race conditions when # updating this file. tfile = Filename.temporary(self.rootDir.cStr(), '.xml') if doc.SaveFile(tfile.toOsSpecific()): tfile.renameTo(filename)
def writeConfigXml(self): """ Rewrites the config.xml to the root directory. This isn't called automatically; an application may call this after adjusting some parameters (such as self.maxDiskUsage). """ from pandac.PandaModules import TiXmlDocument, TiXmlDeclaration, TiXmlElement filename = Filename(self.rootDir, self.ConfigBasename) doc = TiXmlDocument(filename.toOsSpecific()) decl = TiXmlDeclaration("1.0", "utf-8", "") doc.InsertEndChild(decl) xconfig = TiXmlElement('config') xconfig.SetAttribute('max_disk_usage', str(self.maxDiskUsage)) doc.InsertEndChild(xconfig) # Write the file to a temporary filename, then atomically move # it to its actual filename, to avoid race conditions when # updating this file. tfile = Filename.temporary(self.rootDir.cStr(), '.xml') if doc.SaveFile(tfile.toOsSpecific()): tfile.renameTo(filename)
def makeBundle(startDir): fstartDir = Filename.fromOsSpecific(startDir) # Search for nppandad along $DYLD_LIBRARY_PATH. path = DSearchPath() if 'LD_LIBRARY_PATH' in os.environ: path.appendPath(os.environ['LD_LIBRARY_PATH']) if 'DYLD_LIBRARY_PATH' in os.environ: path.appendPath(os.environ['DYLD_LIBRARY_PATH']) nppanda3d = path.findFile('nppanda3d') if not nppanda3d: raise StandardError, "Couldn't find nppanda3d on path." # Generate the bundle directory structure rootFilename = Filename(fstartDir, 'bundle') if os.path.exists(rootFilename.toOsSpecific()): shutil.rmtree(rootFilename.toOsSpecific()) bundleFilename = Filename(rootFilename, 'nppanda3d.plugin') plistFilename = Filename(bundleFilename, 'Contents/Info.plist') plistFilename.makeDir() exeFilename = Filename(bundleFilename, 'Contents/MacOS/nppanda3d') exeFilename.makeDir() resourceFilename = Filename(bundleFilename, 'Contents/Resources/nppanda3d.rsrc') resourceFilename.makeDir() # Compile the .r file to an .rsrc file. os.system('/Developer/Tools/Rez -useDF -o %s %s' % (resourceFilename.toOsSpecific(), Filename(fstartDir, "nppanda3d.r").toOsSpecific())) if not resourceFilename.exists(): raise IOError, 'Unable to run Rez' # Copy in Info.plist and the compiled executable. shutil.copyfile( Filename(fstartDir, "nppanda3d.plist").toOsSpecific(), plistFilename.toOsSpecific()) shutil.copyfile(nppanda3d.toOsSpecific(), exeFilename.toOsSpecific()) # All done! bundleFilename.touch() print bundleFilename.toOsSpecific()
def makeBundle(startDir): fstartDir = Filename.fromOsSpecific(startDir) # Search for nppandad along $DYLD_LIBRARY_PATH. path = DSearchPath() if 'LD_LIBRARY_PATH' in os.environ: path.appendPath(os.environ['LD_LIBRARY_PATH']) if 'DYLD_LIBRARY_PATH' in os.environ: path.appendPath(os.environ['DYLD_LIBRARY_PATH']) nppanda3d = path.findFile('nppanda3d') if not nppanda3d: raise StandardError, "Couldn't find nppanda3d on path." # Generate the bundle directory structure rootFilename = Filename(fstartDir, 'bundle') if os.path.exists(rootFilename.toOsSpecific()): shutil.rmtree(rootFilename.toOsSpecific()) bundleFilename = Filename(rootFilename, 'nppanda3d.plugin') plistFilename = Filename(bundleFilename, 'Contents/Info.plist') plistFilename.makeDir() exeFilename = Filename(bundleFilename, 'Contents/MacOS/nppanda3d') exeFilename.makeDir() resourceFilename = Filename(bundleFilename, 'Contents/Resources/nppanda3d.rsrc') resourceFilename.makeDir() # Compile the .r file to an .rsrc file. os.system('/Developer/Tools/Rez -useDF -o %s %s' % ( resourceFilename.toOsSpecific(), Filename(fstartDir, "nppanda3d.r").toOsSpecific())) if not resourceFilename.exists(): raise IOError, 'Unable to run Rez' # Copy in Info.plist and the compiled executable. shutil.copyfile(Filename(fstartDir, "nppanda3d.plist").toOsSpecific(), plistFilename.toOsSpecific()) shutil.copyfile(nppanda3d.toOsSpecific(), exeFilename.toOsSpecific()) # All done! bundleFilename.touch() print bundleFilename.toOsSpecific()
def quickVerify(self, packageDir = None, pathname = None, notify = None, correctSelf = False): """ Performs a quick test to ensure the file has not been modified. This test is vulnerable to people maliciously attempting to fool the program (by setting datestamps etc.). if correctSelf is True, then any discrepency is corrected by updating the appropriate fields internally, making the assumption that the file on disk is the authoritative version. Returns true if it is intact, false if it is incorrect. If correctSelf is true, raises OSError if the self-update is impossible (for instance, because the file does not exist).""" if not pathname: pathname = Filename(packageDir, self.filename) try: st = os.stat(pathname.toOsSpecific()) except OSError: # If the file is missing, the file fails. if notify: notify.debug("file not found: %s" % (pathname)) if correctSelf: raise return False if st.st_size != self.size: # If the size is wrong, the file fails. if notify: notify.debug("size wrong: %s" % (pathname)) if correctSelf: self.__correctHash(packageDir, pathname, st, notify) return False if st.st_mtime == self.timestamp: # If the size is right and the timestamp is right, the # file passes. if notify: notify.debug("file ok: %s" % (pathname)) return True if notify: notify.debug("modification time wrong: %s" % (pathname)) # If the size is right but the timestamp is wrong, the file # soft-fails. We follow this up with a hash check. if not self.checkHash(packageDir, pathname, st): # Hard fail, the hash is wrong. if notify: notify.debug("hash check wrong: %s" % (pathname)) notify.debug(" found %s, expected %s" % (self.actualFile.hash, self.hash)) if correctSelf: self.__correctHash(packageDir, pathname, st, notify) return False if notify: notify.info("hash check ok: %s" % (pathname)) # The hash is OK after all. Change the file's timestamp back # to what we expect it to be, so we can quick-verify it # successfully next time. if correctSelf: # Or update our own timestamp. self.__correctTimestamp(pathname, st, notify) return False else: self.__updateTimestamp(pathname, st) return True
def downloadDescFileGenerator(self, http): """ A generator function that implements downloadDescFile() one piece at a time. It yields one of stepComplete, stepFailed, or stepContinue. """ assert self.descFile if self.hasDescFile: # We've already got one. yield self.stepComplete return if self.host.appRunner and self.host.appRunner.verifyContents != self.host.appRunner.P3DVCNever: # We're allowed to download it. self.http = http func = lambda step, self=self: self.__downloadFile( None, self.descFile, urlbase=self.descFile.filename, filename=self.descFileBasename) step = self.InstallStep(func, self.descFile.size, self.downloadFactor, 'downloadDesc') for token in step.func(): if token == self.stepContinue: yield token else: break while token == self.restartDownload: # Try again. func = lambda step, self=self: self.__downloadFile( None, self.descFile, urlbase=self.descFile.filename, filename=self.descFileBasename) step = self.InstallStep(func, self.descFile.size, self.downloadFactor, 'downloadDesc') for token in step.func(): if token == self.stepContinue: yield token else: break if token == self.stepFailed: # Couldn't download the desc file. yield self.stepFailed return assert token == self.stepComplete filename = Filename(self.getPackageDir(), self.descFileBasename) # Now that we've written the desc file, make it read-only. os.chmod(filename.toOsSpecific(), 0444) if not self.__readDescFile(): # Weird, it passed the hash check, but we still can't read # it. filename = Filename(self.getPackageDir(), self.descFileBasename) self.notify.warning("Failure reading %s" % (filename)) yield self.stepFailed return yield self.stepComplete return
class DirectNewsFrame(DirectObject.DirectObject): TaskName = 'HtmlViewUpdateTask' TaskChainName = "RedownladTaskChain" RedownloadTaskName = "RedownloadNewsTask" NewsBaseDir = config.GetString("news-base-dir", "/httpNews") NewsStageDir = config.GetString("news-stage-dir", "news") # taken from In Game NewsFrame FrameDimensions = (-1.30666637421, 1.30666637421, -0.751666665077, 0.751666665077) notify = DirectNotifyGlobal.directNotify.newCategory("DirectNewsFrame") NewsIndexFilename = config.GetString("news-index-filename", "http_news_index.txt") NewsOverHttp = config.GetBool("news-over-http", True) CacheIndexFilename = 'cache_index.txt' # home page is considered one section, must always be first # home, news, events, talk of the town, ask toontown, toon resistance SectionIdents = ['hom', 'new', 'evt', 'tot', 'att', 'tnr'] def __init__(self, parent=aspect2d): DirectObject.DirectObject.__init__(self) self.accept("newsSnapshot", self.doSnapshot) self.active = False self.parent = parent self.issues = [] self.accept("newsChangeWeek", self.changeWeek) self.curIssueIndex = 0 self.strFilenames = None self.redownloadingNews = False self.startRedownload = datetime.datetime.now() # just used for timing self.endRedownload = datetime.datetime.now() # just used for timing self.load() self.percentDownloaded = 0.0 self.numIssuesExpected = 0 self.needsParseNews = True if self.NewsOverHttp: self.redownloadNews() self.accept("newIssueOut", self.handleNewIssueOut) self.accept("clientCleanup", self.handleClientCleanup) def parseNewsContent(self): """Open up the directory, read all the files, and figure out the structure.""" if not self.needsParseNews: return assert not self.redownloadingNews self.needsParseNews = False result = False newsDir = self.findNewsDir() if newsDir: allHomeFiles = self.getAllHomeFilenames(newsDir) self.notify.debug("len allHomeFiles = %s" % len(allHomeFiles)) self.numIssuesExpected = len(allHomeFiles) if allHomeFiles: for myIssueIndex, oneHomeFile in enumerate(allHomeFiles): if type(oneHomeFile) == type(""): justFilename = oneHomeFile else: justFilename = oneHomeFile.getFilename().getBasename() self.notify.debug("parseNewContent %s" % justFilename) parts = justFilename.split('_') dateStr = parts[3] oneIssue = IssueFrame.IssueFrame(self.backFrame, newsDir, dateStr, myIssueIndex, len(allHomeFiles), self.strFilenames) oneIssue.hide() self.issues.append(oneIssue) if self.issues: self.issues[-1].show() self.curIssueIndex = len(self.issues) - 1 result = True if hasattr(base.cr, 'inGameNewsMgr') and base.cr.inGameNewsMgr: # we should get here only when a new issue comes out mid game self.createdTime = base.cr.inGameNewsMgr.getLatestIssue() self.notify.debug("setting created time to latest issue %s" % self.createdTime) else: # this is sucky that at this point (initial load) we don't have in game news mgr self.createdTime = base.cr.toontownTimeManager.getCurServerDateTime( ) self.notify.debug("setting created time cur server time %s" % self.createdTime) return result def getAllHomeFilenames(self, newsDir): """Find all the issues that are available.""" self.notify.debug("getAllHomeFilenames") newsDirAsFile = vfs.getFile(Filename(newsDir)) fileList = newsDirAsFile.scanDirectory() fileNames = fileList.getFiles() self.notify.debug("filenames=%s" % fileNames) # scan through and find hom1. thats got to be a home page homeFileNames = set([]) for name in fileNames: self.notify.debug("processing %s" % name) baseName = name.getFilename().getBasename() self.notify.debug("baseName=%s" % baseName) if "hom1." in baseName: homeFileNames.add(name) else: self.notify.debug("hom1. not in baseName") if not homeFileNames: #self.notify.error("couldnt find hom1. in %s" % fileNames) self.notify.warning("couldnt find hom1. in %s" % fileNames) self.setErrorMessage(TTLocalizer.NewsPageNoIssues) return [] def fileCmp(fileA, fileB): return fileA.getFilename().compareTo(fileB.getFilename()) homeFileNames = list(homeFileNames) homeFileNames.sort(cmp=fileCmp) self.notify.debug("returned homeFileNames=%s" % homeFileNames) return homeFileNames def findNewsDir(self): """Returns the directory string for news content. Returns None if it cant find the directory """ if self.NewsOverHttp: # If we're running news-over-http, we dump the news into a # staging directory. return self.NewsStageDir searchPath = DSearchPath() if AppRunnerGlobal.appRunner: # In the web-publish runtime, it will always be here: searchPath.appendDirectory( Filename.expandFrom('$TT_3_5_ROOT/phase_3.5/models/news')) else: # In the launcher or dev environment, look here: basePath = os.path.expandvars('$TTMODELS') or './ttmodels' searchPath.appendDirectory( Filename.fromOsSpecific(basePath + '/built/' + self.NewsBaseDir)) searchPath.appendDirectory(Filename(self.NewsBaseDir)) pfile = Filename(self.NewsIndexFilename) found = vfs.resolveFilename(pfile, searchPath) if not found: self.notify.warning('findNewsDir - no path: %s' % self.NewsIndexFilename) self.setErrorMessage(TTLocalizer.NewsPageErrorDownloadingFile % self.NewsIndexFilename) return None self.notify.debug("found index file %s" % pfile) realDir = pfile.getDirname() return realDir def load(self): """Create the gui objects we need.""" self.loadBackground() #self.loadMainPage() def loadBackground(self): """Create a plain white background image, that covers over the shtickerbook""" # HtmlView: webFrame = -1.30666637421 1.30666637421 -0.751666665077 0.751666665077 upsellBackground = loader.loadModel( "phase_3.5/models/gui/tt_m_gui_ign_newsStatusBackground") imageScaleX = self.FrameDimensions[1] - self.FrameDimensions[0] imageScaleY = self.FrameDimensions[3] - self.FrameDimensions[2] self.backFrame = DirectFrame( parent=self.parent, image=upsellBackground, image_scale=(imageScaleX, 1, imageScaleY), frameColor=(1, 1, 1, 0), frameSize=self.FrameDimensions, pos=(0, 0, 0), relief=DGG.FLAT, text=TTLocalizer.NewsPageDownloadingNews1, text_scale=0.06, text_pos=(0, -0.4), ) def addDownloadingTextTask(self): """Add a simple little task to show in game news is downloading stuff.""" self.removeDownloadingTextTask() task = taskMgr.doMethodLater(1, self.loadingTextTask, "DirectNewsFrameDownloadingTextTask") task.startTime = globalClock.getFrameTime() self.loadingTextTask(task) def removeDownloadingTextTask(self): """Add a simple little task to show in game news is downloading stuff.""" taskMgr.remove("DirectNewsFrameDownloadingTextTask") def loadMainPage(self): """Create the other gui for this.""" self.mainFrame = DirectFrame( parent=self.backFrame, frameSize=self.FrameDimensions, frameColor=(1, 0, 0, 1), ) def activate(self): """ Check if we have a new issue, and prompt the user if we have one. """ if hasattr(self,"createdTime") and \ self.createdTime < base.cr.inGameNewsMgr.getLatestIssue() and \ self.NewsOverHttp and \ not self.redownloadingNews: # we have a new issue, ask the user if he wants to download it # let's assume he clicked yes self.redownloadNews() pass else: self.addDownloadingTextTask() # Load up the news content the first time the user asks to see # it. if self.needsParseNews and not self.redownloadingNews: self.parseNewsContent() self.active = True def deactivate(self): """ self.quad.hide() taskMgr.remove(self.TaskName) """ self.removeDownloadingTextTask() self.active = False def unload(self): """ self.deactivate() HtmlView.HtmlView.unload(self) """ self.removeDownloadingTextTask() result = taskMgr.remove(self.RedownloadTaskName) self.ignore("newsSnapshot") self.ignore("newsChangeWeek") self.ignore("newIssueOut") self.ignore("clientCleanup") def handleClientCleanup(self): """User killing toontown, detach the backframe.""" pass def doSnapshot(self): "Save the current browser contents to a png file." "" pass def changeWeek(self, issueIndex): """Change the issue we are displaying.""" if 0 <= issueIndex and issueIndex < len(self.issues): self.issues[self.curIssueIndex].hide() self.issues[issueIndex].show() self.curIssueIndex = issueIndex def loadingTextTask(self, task): """Change a visual element to indicate we're still downloading.""" timeIndex = int(globalClock.getFrameTime() - task.startTime) % 3 timeStrs = (TTLocalizer.NewsPageDownloadingNews0, TTLocalizer.NewsPageDownloadingNews1, TTLocalizer.NewsPageDownloadingNews2) textToDisplay = timeStrs[timeIndex] % (int( self.percentDownloaded * 100)) if self.backFrame["text"] != textToDisplay: if TTLocalizer.NewsPageDownloadingNewsSubstr in self.backFrame[ "text"]: # don't change the text if we're displaying an error message self.backFrame["text"] = textToDisplay return task.again def setErrorMessage(self, errText): """Tell the user something has gone wrong.""" self.backFrame["text"] = errText def redownloadNews(self): """Get the new issue that came out while he was playing.""" if self.redownloadingNews: self.notify.warning( "averting potential crash redownloadNews called twice, just returning" ) return # I know it's info, it's important enough I feel to appear in the logs self.percentDownloaded = 0.0 self.notify.info("starting redownloadNews") self.startRedownload = datetime.datetime.now() self.redownloadingNews = True self.addDownloadingTextTask() # Clean up the old issues and start new stuff downloading. for issue in self.issues: issue.destroy() self.issues = [] self.curIssueIndex = 0 self.strFilenames = None self.needsParseNews = True # Start by downloading the index file. self.newsUrl = self.getInGameNewsUrl() self.newsDir = Filename(self.findNewsDir()) # Ensure self.newsDir exists and is a directory. Filename(self.newsDir + '/.').makeDir() http = HTTPClient.getGlobalPtr() self.url = self.newsUrl + self.NewsIndexFilename self.ch = http.makeChannel(True) self.ch.beginGetDocument(self.url) self.rf = Ramfile() self.ch.downloadToRam(self.rf) taskMgr.remove(self.RedownloadTaskName) taskMgr.add(self.downloadIndexTask, self.RedownloadTaskName) def downloadIndexTask(self, task): """ Get the initial index file from the HTTP server. """ if self.ch.run(): return task.cont if not self.ch.isValid(): self.notify.warning("Unable to download %s" % (self.url)) self.redownloadingNews = False return task.done # OK, now we've got the list of files hosted by the server. # Parse the list. self.newsFiles = [] filename = self.rf.readline() while filename: filename = filename.strip() if filename: self.newsFiles.append(filename) filename = self.rf.readline() del self.rf self.newsFiles.sort() self.notify.info("Server lists %s news files" % (len(self.newsFiles))) # Now see if we already have copies of these files we # downloaded previously. self.readNewsCache() # Clean up any unexpected files in this directory--they might # be old news files, or partial failed downloads from before. for basename in os.listdir(self.newsDir.toOsSpecific()): if basename != self.CacheIndexFilename and basename not in self.newsCache: junk = Filename(self.newsDir, basename) self.notify.info("Removing %s" % (junk)) junk.unlink() # And start downloading the files. self.nextNewsFile = 0 return self.downloadNextFile(task) def downloadNextFile(self, task): """ Starts the next news file downloading from the HTTP server. """ if self.nextNewsFile >= len(self.newsFiles): # Hey, we're done! self.notify.info("Done downloading news.") self.percentDownloaded = 1 del self.newsFiles del self.nextNewsFile del self.newsUrl del self.newsDir del self.ch del self.url if hasattr(self, 'filename'): del self.filename self.redownloadingNews = False if self.active: # If we're looking at the page now, go ahead and load it. self.parseNewsContent() return task.done self.percentDownloaded = float(self.nextNewsFile) / float( len(self.newsFiles)) # Get the next file on the list. self.filename = self.newsFiles[self.nextNewsFile] self.nextNewsFile += 1 self.url = self.newsUrl + self.filename localFilename = Filename(self.newsDir, self.filename) doc = DocumentSpec(self.url) if self.filename in self.newsCache: # We have already downloaded this file. Ask the # server to give us another copy only if the server's # copy is newer. size, date = self.newsCache[self.filename] if date and localFilename.exists() and ( size == 0 or localFilename.getFileSize() == size): doc.setDate(date) doc.setRequestMode(doc.RMNewer) self.ch.beginGetDocument(doc) self.ch.downloadToFile(localFilename) taskMgr.remove(self.RedownloadTaskName) taskMgr.add(self.downloadCurrentFileTask, self.RedownloadTaskName) def downloadCurrentFileTask(self, task): """ Continues downloading the URL in self.url and self.filename. """ if self.ch.run(): return task.cont if self.ch.getStatusCode() == 304: # This file is still cached from before. We don't need to # download it again. Move on to the next file. self.notify.info("already cached: %s" % (self.filename)) return self.downloadNextFile(task) localFilename = Filename(self.newsDir, self.filename) if not self.ch.isValid(): self.notify.warning("Unable to download %s" % (self.url)) localFilename.unlink() if self.filename in self.newsCache: del self.newsCache[self.filename] self.saveNewsCache() # Might as well see if we can get the next file. return self.downloadNextFile(task) # Successfully downloaded. self.notify.info("downloaded %s" % (self.filename)) # The HTTP "Entity Tag" appears to be useless with our CDN: # different CDN servers will serve up different etag values # for the same file. We rely on file size and date instead. size = self.ch.getFileSize() doc = self.ch.getDocumentSpec() date = '' if doc.hasDate(): date = doc.getDate().getString() self.newsCache[self.filename] = (size, date) self.saveNewsCache() # Continue downloading files. return self.downloadNextFile(task) def readNewsCache(self): """ Reads cache_index.txt into self.newsCache. """ cacheIndexFilename = Filename(self.newsDir, self.CacheIndexFilename) self.newsCache = {} if cacheIndexFilename.isRegularFile(): file = open(cacheIndexFilename.toOsSpecific(), 'r') for line in file.readlines(): line = line.strip() keywords = line.split('\t') if len(keywords) == 3: filename, size, date = keywords if filename in self.newsFiles: try: size = int(size) except ValueError: size = 0 self.newsCache[filename] = (size, date) def saveNewsCache(self): """ Saves self.newsCache to cache_index.txt """ cacheIndexFilename = Filename(self.newsDir, self.CacheIndexFilename) file = open(cacheIndexFilename.toOsSpecific(), 'w') for filename, (size, date) in self.newsCache.items(): print >> file, '%s\t%s\t%s' % (filename, size, date) def handleNewIssueOut(self): """Handle getting this newIssueOut message.""" # we will get this immediately after DistributedInGameNewsManager gets created # we will get this again when a new issue comes out while we are playing if hasattr(self,"createdTime") and \ base.cr.inGameNewsMgr.getLatestIssue() < self.createdTime: self.createdTime = base.cr.inGameNewsMgr.getLatestIssue() else: # we got a new issue while playing the game if self.NewsOverHttp and not self.redownloadingNews: # let's not abruptly yank the page if he's reading the news if not self.active: self.redownloadNews() pass def getInGameNewsUrl(self): """Get the appropriate URL to use if we are in test, qa, or live.""" # First if all else fails, we hard code the live news url result = base.config.GetString( "fallback-news-url", "http://cdn.toontown.disney.go.com/toontown/en/gamenews/") # next check if we have an override, say they want to url to point to a file in their harddisk override = base.config.GetString("in-game-news-url", "") if override: self.notify.info( "got an override url, using %s for in game news" % override) result = override else: try: launcherUrl = base.launcher.getValue("GAME_IN_GAME_NEWS_URL", "") if launcherUrl: result = launcherUrl self.notify.info( "got GAME_IN_GAME_NEWS_URL from launcher using %s" % result) else: self.notify.info( "blank GAME_IN_GAME_NEWS_URL from launcher, using %s" % result) except: self.notify.warning( "got exception getting GAME_IN_GAME_NEWS_URL from launcher, using %s" % result) return result def calcIssueVersion(self, dateStr): majorVer = 1 minorVer = 0 for entry in self.newsIndexEntries: if 'aaver' in entry and dateStr in entry: parts = entry.split('_') if len(parts) > 5: try: majorVer = int(parts[5]) except: self.notify.warning('could not int %s' % parts[5]) else: self.notify.warning('expected more than 5 parts in %s' % entry) if len(parts) > 6: try: minorVer = int(parts[6]) except: self.notify.warning('could not int %s' % parts[6]) else: self.notify.warning('expected more than 6 parts in %s' % entry) break return (majorVer, minorVer)
def __downloadFile(self, step, fileSpec, urlbase = None, filename = None, allowPartial = False): """ Downloads the indicated file from the host into packageDir. Yields one of stepComplete, stepFailed, restartDownload, or stepContinue. """ if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever: # We're not allowed to download anything. yield self.stepFailed; return self.updated = True if not urlbase: urlbase = self.descFileDirname + '/' + fileSpec.filename # Build up a list of URL's to try downloading from. Unlike # the C++ implementation in P3DPackage.cxx, here we build the # URL's in forward order. tryUrls = [] if self.host.appRunner and self.host.appRunner.superMirrorUrl: # We start with the "super mirror", if it's defined. url = self.host.appRunner.superMirrorUrl + urlbase tryUrls.append((url, False)) if self.host.mirrors: # Choose two mirrors at random. mirrors = self.host.mirrors[:] for i in range(2): mirror = random.choice(mirrors) mirrors.remove(mirror) url = mirror + urlbase tryUrls.append((url, False)) if not mirrors: break # After trying two mirrors and failing (or if there are no # mirrors), go get it from the original host. url = self.host.downloadUrlPrefix + urlbase tryUrls.append((url, False)) # And finally, if the original host also fails, try again with # a cache-buster. tryUrls.append((url, True)) for url, cacheBust in tryUrls: request = DocumentSpec(url) if cacheBust: # On the last attempt to download a particular file, # we bust through the cache: append a query string to # do this. url += '?' + str(int(time.time())) request = DocumentSpec(url) request.setCacheControl(DocumentSpec.CCNoCache) self.notify.info("%s downloading %s" % (self.packageName, url)) if not filename: filename = fileSpec.filename targetPathname = Filename(self.getPackageDir(), filename) targetPathname.setBinary() channel = self.http.makeChannel(False) # If there's a previous partial download, attempt to resume it. bytesStarted = 0 if allowPartial and not cacheBust and targetPathname.exists(): bytesStarted = targetPathname.getFileSize() if bytesStarted < 1024*1024: # Not enough bytes downloaded to be worth the risk of # a partial download. bytesStarted = 0 elif bytesStarted >= fileSpec.size: # Couldn't possibly be our file. bytesStarted = 0 if bytesStarted: self.notify.info("Resuming %s after %s bytes already downloaded" % (url, bytesStarted)) # Make sure the file is writable. os.chmod(targetPathname.toOsSpecific(), 0644) channel.beginGetSubdocument(request, bytesStarted, 0) else: # No partial download possible; get the whole file. targetPathname.makeDir() targetPathname.unlink() channel.beginGetDocument(request) channel.downloadToFile(targetPathname) while channel.run(): if step: step.bytesDone = channel.getBytesDownloaded() + channel.getFirstByteDelivered() if step.bytesDone > step.bytesNeeded: # Oops, too much data. Might as well abort; # it's the wrong file. self.notify.warning("Got more data than expected for download %s" % (url)) break 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 %s" % (url)) yield self.stepFailed; return yield self.stepContinue if step: step.bytesDone = channel.getBytesDownloaded() + channel.getFirstByteDelivered() self.__updateStepProgress(step) if not channel.isValid(): self.notify.warning("Failed to download %s" % (url)) elif not fileSpec.fullVerify(self.getPackageDir(), pathname = targetPathname, notify = self.notify): self.notify.warning("After downloading, %s incorrect" % (Filename(fileSpec.filename).getBasename())) # This attempt failed. Maybe the original contents.xml # file is stale. Try re-downloading it now, just to be # sure. if self.host.redownloadContentsFile(self.http): # Yes! Go back and start over from the beginning. yield self.restartDownload; return else: # Success! yield self.stepComplete; return # Maybe the mirror is bad. Go back and try the next # mirror. # All attempts failed. Maybe the original contents.xml file # is stale. Try re-downloading it now, just to be sure. if self.host.redownloadContentsFile(self.http): # Yes! Go back and start over from the beginning. yield self.restartDownload; return # All mirrors failed; the server (or the internet connection) # must be just fubar. yield self.stepFailed; return
class DirectNewsFrame(DirectObject.DirectObject): TaskName = 'HtmlViewUpdateTask' TaskChainName = 'RedownladTaskChain' RedownloadTaskName = 'RedownloadNewsTask' NewsBaseDir = config.GetString('news-base-dir', '/httpNews') NewsStageDir = config.GetString('news-stage-dir', 'news') FrameDimensions = (-1.30666637421, 1.30666637421, -0.75166666507699997, 0.75166666507699997) notify = DirectNotifyGlobal.directNotify.newCategory('DirectNewsFrame') NewsIndexFilename = config.GetString('news-index-filename', 'http_news_index.txt') NewsOverHttp = config.GetBool('news-over-http', True) CacheIndexFilename = 'cache_index.txt' SectionIdents = [ 'hom', 'new', 'evt', 'tot', 'att', 'tnr'] def __init__(self, parent = aspect2d): DirectObject.DirectObject.__init__(self) self.accept('newsSnapshot', self.doSnapshot) self.active = False self.parent = parent self.issues = [] self.accept('newsChangeWeek', self.changeWeek) self.curIssueIndex = 0 self.strFilenames = None self.redownloadingNews = False self.startRedownload = datetime.datetime.now() self.endRedownload = datetime.datetime.now() self.load() self.percentDownloaded = 0.0 self.numIssuesExpected = 0 self.needsParseNews = True self.newsIndexEntries = [] if self.NewsOverHttp: self.redownloadNews() self.accept('newIssueOut', self.handleNewIssueOut) self.accept('clientCleanup', self.handleClientCleanup) def parseNewsContent(self): if not self.needsParseNews: return None self.needsParseNews = False result = False newsDir = self.findNewsDir() if newsDir: allHomeFiles = self.getAllHomeFilenames(newsDir) self.notify.debug('len allHomeFiles = %s' % len(allHomeFiles)) self.numIssuesExpected = len(allHomeFiles) if allHomeFiles: for (myIssueIndex, oneHomeFile) in enumerate(allHomeFiles): if type(oneHomeFile) == type(''): justFilename = oneHomeFile else: justFilename = oneHomeFile.getFilename().getBasename() self.notify.debug('parseNewContent %s' % justFilename) parts = justFilename.split('_') dateStr = parts[3] (majorVer, minorVer) = self.calcIssueVersion(dateStr) if majorVer == 1: oneIssue = IssueFrame.IssueFrame(self.backFrame, newsDir, dateStr, myIssueIndex, len(allHomeFiles), self.strFilenames) elif majorVer == 2: oneIssue = IssueFrameV2.IssueFrameV2(self.backFrame, newsDir, dateStr, myIssueIndex, len(allHomeFiles), self.strFilenames, self.newsIndexEntries) else: self.notify.warning('Dont know how to handle version %s, asuming v2' % majorVer) oneIssue = IssueFrameV2.IssueFrameV2(self.backFrame, newsDir, dateStr, myIssueIndex, len(allHomeFiles), self.strFilenames, self.newsIndexEntries) oneIssue.hide() self.issues.append(oneIssue) if self.issues: self.issues[-1].show() self.curIssueIndex = len(self.issues) - 1 result = True if hasattr(base.cr, 'inGameNewsMgr') and base.cr.inGameNewsMgr: self.createdTime = base.cr.inGameNewsMgr.getLatestIssue() self.notify.debug('setting created time to latest issue %s' % self.createdTime) else: self.createdTime = base.cr.toontownTimeManager.getCurServerDateTime() self.notify.debug('setting created time cur server time %s' % self.createdTime) return result def getAllHomeFilenames(self, newsDir): self.notify.debug('getAllHomeFilenames') newsDirAsFile = vfs.getFile(Filename(newsDir)) fileList = newsDirAsFile.scanDirectory() fileNames = fileList.getFiles() self.notify.debug('filenames=%s' % fileNames) homeFileNames = set([]) for name in fileNames: self.notify.debug('processing %s' % name) baseName = name.getFilename().getBasename() self.notify.debug('baseName=%s' % baseName) if 'hom1.' in baseName: homeFileNames.add(name) continue self.notify.debug('hom1. not in baseName') if not homeFileNames: self.notify.warning('couldnt find hom1. in %s' % fileNames) self.setErrorMessage(TTLocalizer.NewsPageNoIssues) return [] def fileCmp(fileA, fileB): return fileA.getFilename().compareTo(fileB.getFilename()) homeFileNames = list(homeFileNames) homeFileNames.sort(cmp = fileCmp) self.notify.debug('returned homeFileNames=%s' % homeFileNames) return homeFileNames def findNewsDir(self): if self.NewsOverHttp: return self.NewsStageDir searchPath = DSearchPath() if AppRunnerGlobal.appRunner: searchPath.appendDirectory(Filename.expandFrom('$TT_3_5_ROOT/phase_3.5/models/news')) elif not os.path.expandvars('$TTMODELS'): pass basePath = './ttmodels' searchPath.appendDirectory(Filename.fromOsSpecific(basePath + '/built/' + self.NewsBaseDir)) searchPath.appendDirectory(Filename(self.NewsBaseDir)) pfile = Filename(self.NewsIndexFilename) found = vfs.resolveFilename(pfile, searchPath) if not found: self.notify.warning('findNewsDir - no path: %s' % self.NewsIndexFilename) self.setErrorMessage(TTLocalizer.NewsPageErrorDownloadingFile % self.NewsIndexFilename) return None self.notify.debug('found index file %s' % pfile) realDir = pfile.getDirname() return realDir def load(self): self.loadBackground() def loadBackground(self): upsellBackground = loader.loadModel('phase_3.5/models/gui/tt_m_gui_ign_newsStatusBackground') imageScaleX = self.FrameDimensions[1] - self.FrameDimensions[0] imageScaleY = self.FrameDimensions[3] - self.FrameDimensions[2] self.backFrame = DirectFrame(parent = self.parent, image = upsellBackground, image_scale = (imageScaleX, 1, imageScaleY), frameColor = (1, 1, 1, 0), frameSize = self.FrameDimensions, pos = (0, 0, 0), relief = DGG.FLAT, text = TTLocalizer.NewsPageDownloadingNews1, text_scale = 0.059999999999999998, text_pos = (0, -0.40000000000000002)) def addDownloadingTextTask(self): self.removeDownloadingTextTask() task = taskMgr.doMethodLater(1, self.loadingTextTask, 'DirectNewsFrameDownloadingTextTask') task.startTime = globalClock.getFrameTime() self.loadingTextTask(task) def removeDownloadingTextTask(self): taskMgr.remove('DirectNewsFrameDownloadingTextTask') def loadMainPage(self): self.mainFrame = DirectFrame(parent = self.backFrame, frameSize = self.FrameDimensions, frameColor = (1, 0, 0, 1)) def activate(self): if hasattr(self, 'createdTime') and self.createdTime < base.cr.inGameNewsMgr.getLatestIssue() and self.NewsOverHttp and not (self.redownloadingNews): self.redownloadNews() else: self.addDownloadingTextTask() if self.needsParseNews and not (self.redownloadingNews): self.parseNewsContent() self.active = True def deactivate(self): self.removeDownloadingTextTask() self.active = False def unload(self): self.removeDownloadingTextTask() result = taskMgr.remove(self.RedownloadTaskName) self.ignore('newsSnapshot') self.ignore('newsChangeWeek') self.ignore('newIssueOut') self.ignore('clientCleanup') def handleClientCleanup(self): pass def doSnapshot(self): pass def changeWeek(self, issueIndex): if 0 <= issueIndex and issueIndex < len(self.issues): self.issues[self.curIssueIndex].hide() self.issues[issueIndex].show() self.curIssueIndex = issueIndex def loadingTextTask(self, task): timeIndex = int(globalClock.getFrameTime() - task.startTime) % 3 timeStrs = (TTLocalizer.NewsPageDownloadingNews0, TTLocalizer.NewsPageDownloadingNews1, TTLocalizer.NewsPageDownloadingNews2) textToDisplay = timeStrs[timeIndex] % int(self.percentDownloaded * 100) if self.backFrame['text'] != textToDisplay: if TTLocalizer.NewsPageDownloadingNewsSubstr in self.backFrame['text']: self.backFrame['text'] = textToDisplay return task.again def setErrorMessage(self, errText): self.backFrame['text'] = errText def redownloadNews(self): if self.redownloadingNews: self.notify.warning('averting potential crash redownloadNews called twice, just returning') return None self.percentDownloaded = 0.0 self.notify.info('starting redownloadNews') self.startRedownload = datetime.datetime.now() self.redownloadingNews = True self.addDownloadingTextTask() for issue in self.issues: issue.destroy() self.issues = [] self.curIssueIndex = 0 self.strFilenames = None self.needsParseNews = True self.newsUrl = self.getInGameNewsUrl() self.newsDir = Filename(self.findNewsDir()) Filename(self.newsDir + '/.').makeDir() http = HTTPClient.getGlobalPtr() self.url = self.newsUrl + self.NewsIndexFilename self.ch = http.makeChannel(True) self.ch.beginGetDocument(self.url) self.rf = Ramfile() self.ch.downloadToRam(self.rf) taskMgr.remove(self.RedownloadTaskName) taskMgr.add(self.downloadIndexTask, self.RedownloadTaskName) def downloadIndexTask(self, task): if self.ch.run(): return task.cont if not self.ch.isValid(): self.notify.warning('Unable to download %s' % self.url) self.redownloadingNews = False return task.done self.newsFiles = [] filename = self.rf.readline() while filename: filename = filename.strip() if filename: self.newsFiles.append(filename) filename = self.rf.readline() del self.rf self.newsFiles.sort() self.newsIndexEntries = list(self.newsFiles) self.notify.info('Server lists %s news files' % len(self.newsFiles)) self.notify.debug('self.newsIndexEntries=%s' % self.newsIndexEntries) self.readNewsCache() for basename in os.listdir(self.newsDir.toOsSpecific()): if basename != self.CacheIndexFilename and basename not in self.newsCache: junk = Filename(self.newsDir, basename) self.notify.info('Removing %s' % junk) junk.unlink() continue self.nextNewsFile = 0 return self.downloadNextFile(task) def downloadNextFile(self, task): while self.nextNewsFile < len(self.newsFiles) and 'aaver' in self.newsFiles[self.nextNewsFile]: self.nextNewsFile += 1 if self.nextNewsFile >= len(self.newsFiles): self.notify.info('Done downloading news.') self.percentDownloaded = 1 del self.newsFiles del self.nextNewsFile del self.newsUrl del self.newsDir del self.ch del self.url if hasattr(self, 'filename'): del self.filename self.redownloadingNews = False if self.active: self.parseNewsContent() return task.done self.percentDownloaded = float(self.nextNewsFile) / float(len(self.newsFiles)) self.filename = self.newsFiles[self.nextNewsFile] self.nextNewsFile += 1 self.url = self.newsUrl + self.filename localFilename = Filename(self.newsDir, self.filename) self.notify.info('testing for %s' % localFilename.getFullpath()) doc = DocumentSpec(self.url) if self.filename in self.newsCache: (size, date) = self.newsCache[self.filename] if date and localFilename.exists(): if size == 0 or localFilename.getFileSize() == size: doc.setDate(date) doc.setRequestMode(doc.RMNewer) localFilename.getFileSize() == size self.ch.beginGetDocument(doc) self.ch.downloadToFile(localFilename) taskMgr.remove(self.RedownloadTaskName) taskMgr.add(self.downloadCurrentFileTask, self.RedownloadTaskName) def downloadCurrentFileTask(self, task): if self.ch.run(): return task.cont if self.ch.getStatusCode() == 304: self.notify.info('already cached: %s' % self.filename) return self.downloadNextFile(task) localFilename = Filename(self.newsDir, self.filename) if not self.ch.isValid(): self.notify.warning('Unable to download %s' % self.url) localFilename.unlink() if self.filename in self.newsCache: del self.newsCache[self.filename] self.saveNewsCache() return self.downloadNextFile(task) self.notify.info('downloaded %s' % self.filename) size = self.ch.getFileSize() doc = self.ch.getDocumentSpec() date = '' if doc.hasDate(): date = doc.getDate().getString() self.newsCache[self.filename] = (size, date) self.saveNewsCache() return self.downloadNextFile(task) def readNewsCache(self): cacheIndexFilename = Filename(self.newsDir, self.CacheIndexFilename) self.newsCache = { } if cacheIndexFilename.isRegularFile(): file = open(cacheIndexFilename.toOsSpecific(), 'r') for line in file.readlines(): line = line.strip() keywords = line.split('\t') if len(keywords) == 3: (filename, size, date) = keywords if filename in self.newsFiles: try: size = int(size) except ValueError: size = 0 self.newsCache[filename] = (size, date) filename in self.newsFiles def saveNewsCache(self): cacheIndexFilename = Filename(self.newsDir, self.CacheIndexFilename) try: file = open(cacheIndexFilename.toOsSpecific(), 'w') except IOError: e = None self.notify.warning('error opening news cache file %s: %s' % (cacheIndexFilename, str(e))) return None for (size, date) in self.newsCache.items(): print >>file, '%s\t%s\t%s' % (filename, size, date) def handleNewIssueOut(self): if hasattr(self, 'createdTime') and base.cr.inGameNewsMgr.getLatestIssue() < self.createdTime: self.createdTime = base.cr.inGameNewsMgr.getLatestIssue() elif self.NewsOverHttp and not (self.redownloadingNews): if not self.active: self.redownloadNews() def getInGameNewsUrl(self): result = base.config.GetString('fallback-news-url', 'http://cdn.toontown.disney.go.com/toontown/en/gamenews/') override = base.config.GetString('in-game-news-url', '') if override: self.notify.info('got an override url, using %s for in game news' % override) result = override else: try: launcherUrl = base.launcher.getValue('GAME_IN_GAME_NEWS_URL', '') if launcherUrl: result = launcherUrl self.notify.info('got GAME_IN_GAME_NEWS_URL from launcher using %s' % result) else: self.notify.info('blank GAME_IN_GAME_NEWS_URL from launcher, using %s' % result) except: self.notify.warning('got exception getting GAME_IN_GAME_NEWS_URL from launcher, using %s' % result) return result def calcIssueVersion(self, dateStr): majorVer = 1 minorVer = 0 for entry in self.newsIndexEntries: if 'aaver' in entry and dateStr in entry: parts = entry.split('_') if len(parts) > 5: try: majorVer = int(parts[5]) self.notify.warning('could not int %s' % parts[5]) else: self.notify.warning('expected more than 5 parts in %s' % entry) if len(parts) > 6: try: minorVer = int(parts[6]) self.notify.warning('could not int %s' % parts[6]) else: self.notify.warning('expected more than 6 parts in %s' % entry) break continue return (majorVer, minorVer)
class DirectNewsFrame(DirectObject.DirectObject): TaskName = "HtmlViewUpdateTask" TaskChainName = "RedownladTaskChain" RedownloadTaskName = "RedownloadNewsTask" NewsBaseDir = config.GetString("news-base-dir", "/httpNews") NewsStageDir = config.GetString("news-stage-dir", "news") FrameDimensions = (-1.30666637421, 1.30666637421, -0.751666665077, 0.751666665077) notify = DirectNotifyGlobal.directNotify.newCategory("DirectNewsFrame") NewsIndexFilename = config.GetString("news-index-filename", "http_news_index.txt") NewsOverHttp = config.GetBool("news-over-http", True) CacheIndexFilename = "cache_index.txt" SectionIdents = ["hom", "new", "evt", "tot", "att", "tnr"] def __init__(self, parent=aspect2d): DirectObject.DirectObject.__init__(self) self.accept("newsSnapshot", self.doSnapshot) self.active = False self.parent = parent self.issues = [] self.accept("newsChangeWeek", self.changeWeek) self.curIssueIndex = 0 self.strFilenames = None self.redownloadingNews = False self.startRedownload = datetime.datetime.now() self.endRedownload = datetime.datetime.now() self.load() self.percentDownloaded = 0.0 self.numIssuesExpected = 0 self.needsParseNews = True self.newsIndexEntries = [] if self.NewsOverHttp: self.redownloadNews() self.accept("newIssueOut", self.handleNewIssueOut) self.accept("clientCleanup", self.handleClientCleanup) return def parseNewsContent(self): if not self.needsParseNews: return self.needsParseNews = False result = False newsDir = self.findNewsDir() if newsDir: allHomeFiles = self.getAllHomeFilenames(newsDir) self.notify.debug("len allHomeFiles = %s" % len(allHomeFiles)) self.numIssuesExpected = len(allHomeFiles) if allHomeFiles: for myIssueIndex, oneHomeFile in enumerate(allHomeFiles): if type(oneHomeFile) == type(""): justFilename = oneHomeFile else: justFilename = oneHomeFile.getFilename().getBasename() self.notify.debug("parseNewContent %s" % justFilename) parts = justFilename.split("_") dateStr = parts[3] majorVer, minorVer = self.calcIssueVersion(dateStr) if majorVer == 1: oneIssue = IssueFrame.IssueFrame( self.backFrame, newsDir, dateStr, myIssueIndex, len(allHomeFiles), self.strFilenames ) elif majorVer == 2: oneIssue = IssueFrameV2.IssueFrameV2( self.backFrame, newsDir, dateStr, myIssueIndex, len(allHomeFiles), self.strFilenames, self.newsIndexEntries, ) else: self.notify.warning("Dont know how to handle version %s, asuming v2" % majorVer) oneIssue = IssueFrameV2.IssueFrameV2( self.backFrame, newsDir, dateStr, myIssueIndex, len(allHomeFiles), self.strFilenames, self.newsIndexEntries, ) oneIssue.hide() self.issues.append(oneIssue) if self.issues: self.issues[-1].show() self.curIssueIndex = len(self.issues) - 1 result = True if hasattr(base.cr, "inGameNewsMgr") and base.cr.inGameNewsMgr: self.createdTime = base.cr.inGameNewsMgr.getLatestIssue() self.notify.debug("setting created time to latest issue %s" % self.createdTime) else: self.createdTime = base.cr.toontownTimeManager.getCurServerDateTime() self.notify.debug("setting created time cur server time %s" % self.createdTime) return result def getAllHomeFilenames(self, newsDir): self.notify.debug("getAllHomeFilenames") newsDirAsFile = vfs.getFile(Filename(newsDir)) fileList = newsDirAsFile.scanDirectory() fileNames = fileList.getFiles() self.notify.debug("filenames=%s" % fileNames) homeFileNames = set([]) for name in fileNames: self.notify.debug("processing %s" % name) baseName = name.getFilename().getBasename() self.notify.debug("baseName=%s" % baseName) if "hom1." in baseName: homeFileNames.add(name) else: self.notify.debug("hom1. not in baseName") if not homeFileNames: self.notify.warning("couldnt find hom1. in %s" % fileNames) self.setErrorMessage(TTLocalizer.NewsPageNoIssues) return [] def fileCmp(fileA, fileB): return fileA.getFilename().compareTo(fileB.getFilename()) homeFileNames = list(homeFileNames) homeFileNames.sort(cmp=fileCmp) self.notify.debug("returned homeFileNames=%s" % homeFileNames) return homeFileNames def findNewsDir(self): if self.NewsOverHttp: return self.NewsStageDir searchPath = DSearchPath() if AppRunnerGlobal.appRunner: searchPath.appendDirectory(Filename.expandFrom("$TT_3_5_ROOT/phase_3.5/models/news")) else: basePath = os.path.expandvars("$TTMODELS") or "./ttmodels" searchPath.appendDirectory(Filename.fromOsSpecific(basePath + "/built/" + self.NewsBaseDir)) searchPath.appendDirectory(Filename(self.NewsBaseDir)) pfile = Filename(self.NewsIndexFilename) found = vfs.resolveFilename(pfile, searchPath) if not found: self.notify.warning("findNewsDir - no path: %s" % self.NewsIndexFilename) self.setErrorMessage(TTLocalizer.NewsPageErrorDownloadingFile % self.NewsIndexFilename) return None self.notify.debug("found index file %s" % pfile) realDir = pfile.getDirname() return realDir def load(self): self.loadBackground() def loadBackground(self): upsellBackground = loader.loadModel("phase_3.5/models/gui/tt_m_gui_ign_newsStatusBackground") imageScaleX = self.FrameDimensions[1] - self.FrameDimensions[0] imageScaleY = self.FrameDimensions[3] - self.FrameDimensions[2] self.backFrame = DirectFrame( parent=self.parent, image=upsellBackground, image_scale=(imageScaleX, 1, imageScaleY), frameColor=(1, 1, 1, 0), frameSize=self.FrameDimensions, pos=(0, 0, 0), relief=DGG.FLAT, text=TTLocalizer.NewsPageDownloadingNews1, text_scale=0.06, text_pos=(0, -0.4), ) def addDownloadingTextTask(self): self.removeDownloadingTextTask() task = taskMgr.doMethodLater(1, self.loadingTextTask, "DirectNewsFrameDownloadingTextTask") task.startTime = globalClock.getFrameTime() self.loadingTextTask(task) def removeDownloadingTextTask(self): taskMgr.remove("DirectNewsFrameDownloadingTextTask") def loadMainPage(self): self.mainFrame = DirectFrame(parent=self.backFrame, frameSize=self.FrameDimensions, frameColor=(1, 0, 0, 1)) def activate(self): if ( hasattr(self, "createdTime") and self.createdTime < base.cr.inGameNewsMgr.getLatestIssue() and self.NewsOverHttp and not self.redownloadingNews ): self.redownloadNews() else: self.addDownloadingTextTask() if self.needsParseNews and not self.redownloadingNews: self.parseNewsContent() self.active = True def deactivate(self): self.removeDownloadingTextTask() self.active = False def unload(self): self.removeDownloadingTextTask() result = taskMgr.remove(self.RedownloadTaskName) self.ignore("newsSnapshot") self.ignore("newsChangeWeek") self.ignore("newIssueOut") self.ignore("clientCleanup") def handleClientCleanup(self): pass def doSnapshot(self): pass def changeWeek(self, issueIndex): if 0 <= issueIndex and issueIndex < len(self.issues): self.issues[self.curIssueIndex].hide() self.issues[issueIndex].show() self.curIssueIndex = issueIndex def loadingTextTask(self, task): timeIndex = int(globalClock.getFrameTime() - task.startTime) % 3 timeStrs = ( TTLocalizer.NewsPageDownloadingNews0, TTLocalizer.NewsPageDownloadingNews1, TTLocalizer.NewsPageDownloadingNews2, ) textToDisplay = timeStrs[timeIndex] % int(self.percentDownloaded * 100) if self.backFrame["text"] != textToDisplay: if TTLocalizer.NewsPageDownloadingNewsSubstr in self.backFrame["text"]: self.backFrame["text"] = textToDisplay return task.again def setErrorMessage(self, errText): self.backFrame["text"] = errText def redownloadNews(self): if self.redownloadingNews: self.notify.warning("averting potential crash redownloadNews called twice, just returning") return self.percentDownloaded = 0.0 self.notify.info("starting redownloadNews") self.startRedownload = datetime.datetime.now() self.redownloadingNews = True self.addDownloadingTextTask() for issue in self.issues: issue.destroy() self.issues = [] self.curIssueIndex = 0 self.strFilenames = None self.needsParseNews = True self.newsUrl = self.getInGameNewsUrl() self.newsDir = Filename(self.findNewsDir()) Filename(self.newsDir + "/.").makeDir() http = HTTPClient.getGlobalPtr() self.url = self.newsUrl + self.NewsIndexFilename self.ch = http.makeChannel(True) self.ch.beginGetDocument(self.url) self.rf = Ramfile() self.ch.downloadToRam(self.rf) taskMgr.remove(self.RedownloadTaskName) taskMgr.add(self.downloadIndexTask, self.RedownloadTaskName) return def downloadIndexTask(self, task): if self.ch.run(): return task.cont if not self.ch.isValid(): self.notify.warning("Unable to download %s" % self.url) self.redownloadingNews = False return task.done self.newsFiles = [] filename = self.rf.readline() while filename: filename = filename.strip() if filename: self.newsFiles.append(filename) filename = self.rf.readline() del self.rf self.newsFiles.sort() self.newsIndexEntries = list(self.newsFiles) self.notify.info("Server lists %s news files" % len(self.newsFiles)) self.notify.debug("self.newsIndexEntries=%s" % self.newsIndexEntries) self.readNewsCache() for basename in os.listdir(self.newsDir.toOsSpecific()): if basename != self.CacheIndexFilename and basename not in self.newsCache: junk = Filename(self.newsDir, basename) self.notify.info("Removing %s" % junk) junk.unlink() self.nextNewsFile = 0 return self.downloadNextFile(task) def downloadNextFile(self, task): while self.nextNewsFile < len(self.newsFiles) and "aaver" in self.newsFiles[self.nextNewsFile]: self.nextNewsFile += 1 if self.nextNewsFile >= len(self.newsFiles): self.notify.info("Done downloading news.") self.percentDownloaded = 1 del self.newsFiles del self.nextNewsFile del self.newsUrl del self.newsDir del self.ch del self.url if hasattr(self, "filename"): del self.filename self.redownloadingNews = False if self.active: self.parseNewsContent() return task.done self.percentDownloaded = float(self.nextNewsFile) / float(len(self.newsFiles)) self.filename = self.newsFiles[self.nextNewsFile] self.nextNewsFile += 1 self.url = self.newsUrl + self.filename localFilename = Filename(self.newsDir, self.filename) self.notify.info("testing for %s" % localFilename.getFullpath()) doc = DocumentSpec(self.url) if self.filename in self.newsCache: size, date = self.newsCache[self.filename] if date and localFilename.exists() and (size == 0 or localFilename.getFileSize() == size): doc.setDate(date) doc.setRequestMode(doc.RMNewer) self.ch.beginGetDocument(doc) self.ch.downloadToFile(localFilename) taskMgr.remove(self.RedownloadTaskName) taskMgr.add(self.downloadCurrentFileTask, self.RedownloadTaskName) def downloadCurrentFileTask(self, task): if self.ch.run(): return task.cont if self.ch.getStatusCode() == 304: self.notify.info("already cached: %s" % self.filename) return self.downloadNextFile(task) localFilename = Filename(self.newsDir, self.filename) if not self.ch.isValid(): self.notify.warning("Unable to download %s" % self.url) localFilename.unlink() if self.filename in self.newsCache: del self.newsCache[self.filename] self.saveNewsCache() return self.downloadNextFile(task) self.notify.info("downloaded %s" % self.filename) size = self.ch.getFileSize() doc = self.ch.getDocumentSpec() date = "" if doc.hasDate(): date = doc.getDate().getString() self.newsCache[self.filename] = (size, date) self.saveNewsCache() return self.downloadNextFile(task) def readNewsCache(self): cacheIndexFilename = Filename(self.newsDir, self.CacheIndexFilename) self.newsCache = {} if cacheIndexFilename.isRegularFile(): file = open(cacheIndexFilename.toOsSpecific(), "r") for line in file.readlines(): line = line.strip() keywords = line.split("\t") if len(keywords) == 3: filename, size, date = keywords if filename in self.newsFiles: try: size = int(size) except ValueError: size = 0 self.newsCache[filename] = (size, date) def saveNewsCache(self): cacheIndexFilename = Filename(self.newsDir, self.CacheIndexFilename) try: file = open(cacheIndexFilename.toOsSpecific(), "w") except IOError, e: self.notify.warning("error opening news cache file %s: %s" % (cacheIndexFilename, str(e))) return for filename, (size, date) in self.newsCache.items(): print >> file, "%s\t%s\t%s" % (filename, size, date)
#from ShowBaseGlobal import * #import ToontownClientRepository #cr = ToontownClientRepository.ToontownClientRepository("D:\\Cygwin\\home\\jnschell\\player\\toontown\\src\\configfiles\\toon.dc") #import FSMInspector #ins = FSMInspector.FSMInspector(ClassicFSM=cr.fsm) #cr.fsm.request("connect", ["206.18.93.17", 6667]) from ToonBaseGlobal import * from toontown.distributed import ToontownClientRepository import os from pandac.PandaModules import Filename # Start up the client repository fname = Filename(os.getenv("TOONTOWN") + "/src/configfiles/toon.dc") cr = ToontownClientRepository.ToontownClientRepository(fname.toOsSpecific()) # Start the show base.startShow(cr) run()
def __downloadFile(self, step, fileSpec, urlbase=None, filename=None, allowPartial=False): """ Downloads the indicated file from the host into packageDir. Yields one of stepComplete, stepFailed, restartDownload, or stepContinue. """ if self.host.appRunner and self.host.appRunner.verifyContents == self.host.appRunner.P3DVCNever: # We're not allowed to download anything. yield self.stepFailed return self.updated = True if not urlbase: urlbase = self.descFileDirname + '/' + fileSpec.filename # Build up a list of URL's to try downloading from. Unlike # the C++ implementation in P3DPackage.cxx, here we build the # URL's in forward order. tryUrls = [] if self.host.appRunner and self.host.appRunner.superMirrorUrl: # We start with the "super mirror", if it's defined. url = self.host.appRunner.superMirrorUrl + urlbase tryUrls.append((url, False)) if self.host.mirrors: # Choose two mirrors at random. mirrors = self.host.mirrors[:] for i in range(2): mirror = random.choice(mirrors) mirrors.remove(mirror) url = mirror + urlbase tryUrls.append((url, False)) if not mirrors: break # After trying two mirrors and failing (or if there are no # mirrors), go get it from the original host. url = self.host.downloadUrlPrefix + urlbase tryUrls.append((url, False)) # And finally, if the original host also fails, try again with # a cache-buster. tryUrls.append((url, True)) for url, cacheBust in tryUrls: request = DocumentSpec(url) if cacheBust: # On the last attempt to download a particular file, # we bust through the cache: append a query string to # do this. url += '?' + str(int(time.time())) request = DocumentSpec(url) request.setCacheControl(DocumentSpec.CCNoCache) self.notify.info("%s downloading %s" % (self.packageName, url)) if not filename: filename = fileSpec.filename targetPathname = Filename(self.getPackageDir(), filename) targetPathname.setBinary() channel = self.http.makeChannel(False) # If there's a previous partial download, attempt to resume it. bytesStarted = 0 if allowPartial and not cacheBust and targetPathname.exists(): bytesStarted = targetPathname.getFileSize() if bytesStarted < 1024 * 1024: # Not enough bytes downloaded to be worth the risk of # a partial download. bytesStarted = 0 elif bytesStarted >= fileSpec.size: # Couldn't possibly be our file. bytesStarted = 0 if bytesStarted: self.notify.info( "Resuming %s after %s bytes already downloaded" % (url, bytesStarted)) # Make sure the file is writable. os.chmod(targetPathname.toOsSpecific(), 0644) channel.beginGetSubdocument(request, bytesStarted, 0) else: # No partial download possible; get the whole file. targetPathname.makeDir() targetPathname.unlink() channel.beginGetDocument(request) channel.downloadToFile(targetPathname) while channel.run(): if step: step.bytesDone = channel.getBytesDownloaded( ) + channel.getFirstByteDelivered() if step.bytesDone > step.bytesNeeded: # Oops, too much data. Might as well abort; # it's the wrong file. self.notify.warning( "Got more data than expected for download %s" % (url)) break 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 %s" % (url)) yield self.stepFailed return yield self.stepContinue if step: step.bytesDone = channel.getBytesDownloaded( ) + channel.getFirstByteDelivered() self.__updateStepProgress(step) if not channel.isValid(): self.notify.warning("Failed to download %s" % (url)) elif not fileSpec.fullVerify(self.getPackageDir(), pathname=targetPathname, notify=self.notify): self.notify.warning( "After downloading, %s incorrect" % (Filename(fileSpec.filename).getBasename())) # This attempt failed. Maybe the original contents.xml # file is stale. Try re-downloading it now, just to be # sure. if self.host.redownloadContentsFile(self.http): # Yes! Go back and start over from the beginning. yield self.restartDownload return else: # Success! yield self.stepComplete return # Maybe the mirror is bad. Go back and try the next # mirror. # All attempts failed. Maybe the original contents.xml file # is stale. Try re-downloading it now, just to be sure. if self.host.redownloadContentsFile(self.http): # Yes! Go back and start over from the beginning. yield self.restartDownload return # All mirrors failed; the server (or the internet connection) # must be just fubar. yield self.stepFailed return
class DirectNewsFrame(DirectObject.DirectObject): TaskName = 'HtmlViewUpdateTask' TaskChainName = 'RedownladTaskChain' RedownloadTaskName = 'RedownloadNewsTask' NewsBaseDir = config.GetString('news-base-dir', '/httpNews') NewsStageDir = config.GetString('news-stage-dir', 'news') FrameDimensions = (-1.30666637421, 1.30666637421, -0.751666665077, 0.751666665077) notify = DirectNotifyGlobal.directNotify.newCategory('DirectNewsFrame') NewsIndexFilename = config.GetString('news-index-filename', 'http_news_index.txt') NewsOverHttp = config.GetBool('news-over-http', True) CacheIndexFilename = 'cache_index.txt' SectionIdents = ['hom', 'new', 'evt', 'tot', 'att', 'tnr'] def __init__(self, parent=aspect2d): DirectObject.DirectObject.__init__(self) self.accept('newsSnapshot', self.doSnapshot) self.active = False self.parent = parent self.issues = [] self.accept('newsChangeWeek', self.changeWeek) self.curIssueIndex = 0 self.strFilenames = None self.redownloadingNews = False self.startRedownload = datetime.datetime.now() self.endRedownload = datetime.datetime.now() self.load() self.percentDownloaded = 0.0 self.numIssuesExpected = 0 self.needsParseNews = True self.newsIndexEntries = [] if self.NewsOverHttp: self.redownloadNews() self.accept('newIssueOut', self.handleNewIssueOut) self.accept('clientCleanup', self.handleClientCleanup) return def parseNewsContent(self): if not self.needsParseNews: return self.needsParseNews = False result = False newsDir = self.findNewsDir() if newsDir: allHomeFiles = self.getAllHomeFilenames(newsDir) self.notify.debug('len allHomeFiles = %s' % len(allHomeFiles)) self.numIssuesExpected = len(allHomeFiles) if allHomeFiles: for myIssueIndex, oneHomeFile in enumerate(allHomeFiles): if type(oneHomeFile) == type(''): justFilename = oneHomeFile else: justFilename = oneHomeFile.getFilename().getBasename() self.notify.debug('parseNewContent %s' % justFilename) parts = justFilename.split('_') dateStr = parts[3] majorVer, minorVer = self.calcIssueVersion(dateStr) if majorVer == 1: oneIssue = IssueFrame.IssueFrame( self.backFrame, newsDir, dateStr, myIssueIndex, len(allHomeFiles), self.strFilenames) elif majorVer == 2: oneIssue = IssueFrameV2.IssueFrameV2( self.backFrame, newsDir, dateStr, myIssueIndex, len(allHomeFiles), self.strFilenames, self.newsIndexEntries) else: self.notify.warning( 'Dont know how to handle version %s, asuming v2' % majorVer) oneIssue = IssueFrameV2.IssueFrameV2( self.backFrame, newsDir, dateStr, myIssueIndex, len(allHomeFiles), self.strFilenames, self.newsIndexEntries) oneIssue.hide() self.issues.append(oneIssue) if self.issues: self.issues[-1].show() self.curIssueIndex = len(self.issues) - 1 result = True if hasattr(base.cr, 'inGameNewsMgr') and base.cr.inGameNewsMgr: self.createdTime = base.cr.inGameNewsMgr.getLatestIssue() self.notify.debug('setting created time to latest issue %s' % self.createdTime) else: self.createdTime = base.cr.toontownTimeManager.getCurServerDateTime( ) self.notify.debug('setting created time cur server time %s' % self.createdTime) return result def getAllHomeFilenames(self, newsDir): self.notify.debug('getAllHomeFilenames') newsDirAsFile = vfs.getFile(Filename(newsDir)) fileList = newsDirAsFile.scanDirectory() fileNames = fileList.getFiles() self.notify.debug('filenames=%s' % fileNames) homeFileNames = set([]) for name in fileNames: self.notify.debug('processing %s' % name) baseName = name.getFilename().getBasename() self.notify.debug('baseName=%s' % baseName) if 'hom1.' in baseName: homeFileNames.add(name) else: self.notify.debug('hom1. not in baseName') if not homeFileNames: self.notify.warning('couldnt find hom1. in %s' % fileNames) self.setErrorMessage(TTLocalizer.NewsPageNoIssues) return [] def fileCmp(fileA, fileB): return fileA.getFilename().compareTo(fileB.getFilename()) homeFileNames = list(homeFileNames) homeFileNames.sort(cmp=fileCmp) self.notify.debug('returned homeFileNames=%s' % homeFileNames) return homeFileNames def findNewsDir(self): if self.NewsOverHttp: return self.NewsStageDir searchPath = DSearchPath() if AppRunnerGlobal.appRunner: searchPath.appendDirectory( Filename.expandFrom('$TT_3_5_ROOT/phase_3.5/models/news')) else: basePath = os.path.expandvars('$TTMODELS') or './ttmodels' searchPath.appendDirectory( Filename.fromOsSpecific(basePath + '/built/' + self.NewsBaseDir)) searchPath.appendDirectory(Filename(self.NewsBaseDir)) pfile = Filename(self.NewsIndexFilename) found = vfs.resolveFilename(pfile, searchPath) if not found: self.notify.warning('findNewsDir - no path: %s' % self.NewsIndexFilename) self.setErrorMessage(TTLocalizer.NewsPageErrorDownloadingFile % self.NewsIndexFilename) return None self.notify.debug('found index file %s' % pfile) realDir = pfile.getDirname() return realDir def load(self): self.loadBackground() def loadBackground(self): upsellBackground = loader.loadModel( 'phase_3.5/models/gui/tt_m_gui_ign_newsStatusBackground') imageScaleX = self.FrameDimensions[1] - self.FrameDimensions[0] imageScaleY = self.FrameDimensions[3] - self.FrameDimensions[2] self.backFrame = DirectFrame(parent=self.parent, image=upsellBackground, image_scale=(imageScaleX, 1, imageScaleY), frameColor=(1, 1, 1, 0), frameSize=self.FrameDimensions, pos=(0, 0, 0), relief=DGG.FLAT, text=TTLocalizer.NewsPageDownloadingNews1, text_scale=0.06, text_pos=(0, -0.4)) def addDownloadingTextTask(self): self.removeDownloadingTextTask() task = taskMgr.doMethodLater(1, self.loadingTextTask, 'DirectNewsFrameDownloadingTextTask') task.startTime = globalClock.getFrameTime() self.loadingTextTask(task) def removeDownloadingTextTask(self): taskMgr.remove('DirectNewsFrameDownloadingTextTask') def loadMainPage(self): self.mainFrame = DirectFrame(parent=self.backFrame, frameSize=self.FrameDimensions, frameColor=(1, 0, 0, 1)) def activate(self): if hasattr( self, 'createdTime' ) and self.createdTime < base.cr.inGameNewsMgr.getLatestIssue( ) and self.NewsOverHttp and not self.redownloadingNews: self.redownloadNews() else: self.addDownloadingTextTask() if self.needsParseNews and not self.redownloadingNews: self.parseNewsContent() self.active = True def deactivate(self): self.removeDownloadingTextTask() self.active = False def unload(self): self.removeDownloadingTextTask() result = taskMgr.remove(self.RedownloadTaskName) self.ignore('newsSnapshot') self.ignore('newsChangeWeek') self.ignore('newIssueOut') self.ignore('clientCleanup') def handleClientCleanup(self): pass def doSnapshot(self): pass def changeWeek(self, issueIndex): if 0 <= issueIndex and issueIndex < len(self.issues): self.issues[self.curIssueIndex].hide() self.issues[issueIndex].show() self.curIssueIndex = issueIndex def loadingTextTask(self, task): timeIndex = int(globalClock.getFrameTime() - task.startTime) % 3 timeStrs = (TTLocalizer.NewsPageDownloadingNews0, TTLocalizer.NewsPageDownloadingNews1, TTLocalizer.NewsPageDownloadingNews2) textToDisplay = timeStrs[timeIndex] % int(self.percentDownloaded * 100) if self.backFrame['text'] != textToDisplay: if TTLocalizer.NewsPageDownloadingNewsSubstr in self.backFrame[ 'text']: self.backFrame['text'] = textToDisplay return task.again def setErrorMessage(self, errText): self.backFrame['text'] = errText def redownloadNews(self): if self.redownloadingNews: self.notify.warning( 'averting potential crash redownloadNews called twice, just returning' ) return self.percentDownloaded = 0.0 self.notify.info('starting redownloadNews') self.startRedownload = datetime.datetime.now() self.redownloadingNews = True self.addDownloadingTextTask() for issue in self.issues: issue.destroy() self.issues = [] self.curIssueIndex = 0 self.strFilenames = None self.needsParseNews = True self.newsUrl = self.getInGameNewsUrl() self.newsDir = Filename(self.findNewsDir()) Filename(self.newsDir + '/.').makeDir() http = HTTPClient.getGlobalPtr() self.url = self.newsUrl + self.NewsIndexFilename self.ch = http.makeChannel(True) self.ch.beginGetDocument(self.url) self.rf = Ramfile() self.ch.downloadToRam(self.rf) taskMgr.remove(self.RedownloadTaskName) taskMgr.add(self.downloadIndexTask, self.RedownloadTaskName) return def downloadIndexTask(self, task): if self.ch.run(): return task.cont if not self.ch.isValid(): self.notify.warning('Unable to download %s' % self.url) self.redownloadingNews = False return task.done self.newsFiles = [] filename = self.rf.readline() while filename: filename = filename.strip() if filename: self.newsFiles.append(filename) filename = self.rf.readline() del self.rf self.newsFiles.sort() self.newsIndexEntries = list(self.newsFiles) self.notify.info('Server lists %s news files' % len(self.newsFiles)) self.notify.debug('self.newsIndexEntries=%s' % self.newsIndexEntries) self.readNewsCache() for basename in os.listdir(self.newsDir.toOsSpecific()): if basename != self.CacheIndexFilename and basename not in self.newsCache: junk = Filename(self.newsDir, basename) self.notify.info('Removing %s' % junk) junk.unlink() self.nextNewsFile = 0 return self.downloadNextFile(task) def downloadNextFile(self, task): while self.nextNewsFile < len( self.newsFiles) and 'aaver' in self.newsFiles[ self.nextNewsFile]: self.nextNewsFile += 1 if self.nextNewsFile >= len(self.newsFiles): self.notify.info('Done downloading news.') self.percentDownloaded = 1 del self.newsFiles del self.nextNewsFile del self.newsUrl del self.newsDir del self.ch del self.url if hasattr(self, 'filename'): del self.filename self.redownloadingNews = False if self.active: self.parseNewsContent() return task.done self.percentDownloaded = float(self.nextNewsFile) / float( len(self.newsFiles)) self.filename = self.newsFiles[self.nextNewsFile] self.nextNewsFile += 1 self.url = self.newsUrl + self.filename localFilename = Filename(self.newsDir, self.filename) self.notify.info('testing for %s' % localFilename.getFullpath()) doc = DocumentSpec(self.url) if self.filename in self.newsCache: size, date = self.newsCache[self.filename] if date and localFilename.exists() and ( size == 0 or localFilename.getFileSize() == size): doc.setDate(date) doc.setRequestMode(doc.RMNewer) self.ch.beginGetDocument(doc) self.ch.downloadToFile(localFilename) taskMgr.remove(self.RedownloadTaskName) taskMgr.add(self.downloadCurrentFileTask, self.RedownloadTaskName) def downloadCurrentFileTask(self, task): if self.ch.run(): return task.cont if self.ch.getStatusCode() == 304: self.notify.info('already cached: %s' % self.filename) return self.downloadNextFile(task) localFilename = Filename(self.newsDir, self.filename) if not self.ch.isValid(): self.notify.warning('Unable to download %s' % self.url) localFilename.unlink() if self.filename in self.newsCache: del self.newsCache[self.filename] self.saveNewsCache() return self.downloadNextFile(task) self.notify.info('downloaded %s' % self.filename) size = self.ch.getFileSize() doc = self.ch.getDocumentSpec() date = '' if doc.hasDate(): date = doc.getDate().getString() self.newsCache[self.filename] = (size, date) self.saveNewsCache() return self.downloadNextFile(task) def readNewsCache(self): cacheIndexFilename = Filename(self.newsDir, self.CacheIndexFilename) self.newsCache = {} if cacheIndexFilename.isRegularFile(): file = open(cacheIndexFilename.toOsSpecific(), 'r') for line in file.readlines(): line = line.strip() keywords = line.split('\t') if len(keywords) == 3: filename, size, date = keywords if filename in self.newsFiles: try: size = int(size) except ValueError: size = 0 self.newsCache[filename] = (size, date) def saveNewsCache(self): cacheIndexFilename = Filename(self.newsDir, self.CacheIndexFilename) try: file = open(cacheIndexFilename.toOsSpecific(), 'w') except IOError, e: self.notify.warning('error opening news cache file %s: %s' % (cacheIndexFilename, str(e))) return for filename, (size, date) in self.newsCache.items(): print >> file, '%s\t%s\t%s' % (filename, size, date)
def readContentsFile(self, tempFilename = None, freshDownload = False): """ Reads the contents.xml file for this particular host, once it has been downloaded into the indicated temporary file. Returns true on success, false if the contents file is not already on disk or is unreadable. If tempFilename is specified, it is the filename read, and it is copied the file into the standard location if it's not there already. If tempFilename is not specified, the standard filename is read if it is known. """ if not hasattr(PandaModules, 'TiXmlDocument'): return False if not tempFilename: if self.hostDir: # If the filename is not specified, we can infer it # if we already know our hostDir hostDir = self.hostDir else: # Otherwise, we have to guess the hostDir. hostDir = self.__determineHostDir(None, self.hostUrl) tempFilename = Filename(hostDir, 'contents.xml') doc = PandaModules.TiXmlDocument(tempFilename.toOsSpecific()) if not doc.LoadFile(): return False xcontents = doc.FirstChildElement('contents') if not xcontents: return False maxAge = xcontents.Attribute('max_age') if maxAge: try: maxAge = int(maxAge) except: maxAge = None if maxAge is None: # Default max_age if unspecified (see p3d_plugin.h). from direct.p3d.AppRunner import AppRunner maxAge = AppRunner.P3D_CONTENTS_DEFAULT_MAX_AGE # Get the latest possible expiration time, based on the max_age # indication. Any expiration time later than this is in error. now = int(time.time()) self.contentsExpiration = now + maxAge if freshDownload: self.contentsSpec.readHash(tempFilename) # Update the XML with the new download information. xorig = xcontents.FirstChildElement('orig') while xorig: xcontents.RemoveChild(xorig) xorig = xcontents.FirstChildElement('orig') xorig = PandaModules.TiXmlElement('orig') self.contentsSpec.storeXml(xorig) xorig.SetAttribute('expiration', str(self.contentsExpiration)) xcontents.InsertEndChild(xorig) else: # Read the download hash and expiration time from the XML. expiration = None xorig = xcontents.FirstChildElement('orig') if xorig: self.contentsSpec.loadXml(xorig) expiration = xorig.Attribute('expiration') if expiration: try: expiration = int(expiration) except: expiration = None if not self.contentsSpec.hash: self.contentsSpec.readHash(tempFilename) if expiration is not None: self.contentsExpiration = min(self.contentsExpiration, expiration) # Look for our own entry in the hosts table. if self.hostUrl: self.__findHostXml(xcontents) else: assert self.hostDir self.__findHostXmlForHostDir(xcontents) if not self.hostDir: self.hostDir = self.__determineHostDir(None, self.hostUrl) # Get the list of packages available for download and/or import. xpackage = xcontents.FirstChildElement('package') while xpackage: name = xpackage.Attribute('name') platform = xpackage.Attribute('platform') version = xpackage.Attribute('version') try: solo = int(xpackage.Attribute('solo') or '') except ValueError: solo = False package = self.__makePackage(name, platform, version, solo) package.descFile = FileSpec() package.descFile.loadXml(xpackage) package.setupFilenames() package.importDescFile = None ximport = xpackage.FirstChildElement('import') if ximport: package.importDescFile = FileSpec() package.importDescFile.loadXml(ximport) xpackage = xpackage.NextSiblingElement('package') self.hasContentsFile = True # Now save the contents.xml file into the standard location. if not self.appRunner or self.appRunner.verifyContents != self.appRunner.P3DVCNever: assert self.hostDir filename = Filename(self.hostDir, 'contents.xml') filename.makeDir() if freshDownload: doc.SaveFile(filename.toOsSpecific()) else: if filename != tempFilename: tempFilename.copyTo(filename) 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.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(), 0555) 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 __readDescFile(self): """ Reads the desc xml file for this particular package, assuming it's been already downloaded and verified. Returns true on success, false on failure. """ if self.hasDescFile: # No need to read it again. return True if self.solo: # If this is a "solo" package, we don't actually "read" # the desc file; that's the entire contents of the # package. self.hasDescFile = True self.hasPackage = True return True filename = Filename(self.getPackageDir(), self.descFileBasename) if not hasattr(PandaModules, 'TiXmlDocument'): return False doc = PandaModules.TiXmlDocument(filename.toOsSpecific()) if not doc.LoadFile(): return False xpackage = doc.FirstChildElement('package') if not xpackage: return False try: self.patchVersion = int(xpackage.Attribute('patch_version') or '') except ValueError: self.patchVersion = None self.displayName = None xconfig = xpackage.FirstChildElement('config') if xconfig: # The name for display to an English-speaking user. self.displayName = xconfig.Attribute('display_name') # True if any apps that use this package must be GUI apps. guiApp = xconfig.Attribute('gui_app') if guiApp: self.guiApp = int(guiApp) # The uncompressed archive, which will be mounted directly, # and also used for patching. xuncompressedArchive = xpackage.FirstChildElement( 'uncompressed_archive') if xuncompressedArchive: self.uncompressedArchive = FileSpec() self.uncompressedArchive.loadXml(xuncompressedArchive) # The compressed archive, which is what is downloaded. xcompressedArchive = xpackage.FirstChildElement('compressed_archive') if xcompressedArchive: self.compressedArchive = FileSpec() self.compressedArchive.loadXml(xcompressedArchive) # The list of files that should be extracted to disk. self.extracts = [] xextract = xpackage.FirstChildElement('extract') while xextract: file = FileSpec() file.loadXml(xextract) self.extracts.append(file) xextract = xextract.NextSiblingElement('extract') # The list of additional packages that must be installed for # this package to function properly. self.requires = [] xrequires = xpackage.FirstChildElement('requires') while xrequires: packageName = xrequires.Attribute('name') version = xrequires.Attribute('version') hostUrl = xrequires.Attribute('host') if packageName and hostUrl: host = self.host.appRunner.getHostWithAlt(hostUrl) self.requires.append((packageName, version, host)) xrequires = xrequires.NextSiblingElement('requires') self.hasDescFile = True # Now that we've read the desc file, go ahead and use it to # verify the download status. if self.__checkArchiveStatus(): # It's all fully downloaded, unpacked, and ready. self.hasPackage = True return True # Still have to download it. self.__buildInstallPlans() return True