def getHost(self, hostUrl):
        if hostUrl in self.hosts:
            return self.hosts[hostUrl]

        host = HostInfo(hostUrl, appRunner=appRunner, hostDir=self.hostDir, asMirror=False, perPlatform=False)
        if not host.hasContentsFile:
            if not host.readContentsFile():
                if not host.downloadContentsFile(self.http):
                    Installer.notify.error("couldn't read host %s" % host.hostUrl)
                    return None
        self.hosts[hostUrl] = host
        return host
    def getHost(self, hostUrl):
        if hostUrl in self.hosts:
            return self.hosts[hostUrl]

        host = HostInfo(hostUrl,
                        appRunner=appRunner,
                        hostDir=self.hostDir,
                        asMirror=False,
                        perPlatform=False)
        if not host.hasContentsFile:
            if not host.readContentsFile():
                if not host.downloadContentsFile(self.http):
                    Installer.notify.error("couldn't read host %s" %
                                           host.hostUrl)
                    return None
        self.hosts[hostUrl] = host
        return host
class Standalone:
    """ This class creates a standalone executable from a given .p3d file. """

    notify = directNotify.newCategory("Standalone")

    def __init__(self, p3dfile, tokens={}):
        self.p3dfile = Filename(p3dfile)
        self.basename = self.p3dfile.getBasenameWoExtension()
        self.tokens = tokens

        self.tempDir = Filename.temporary("", self.basename, "") + "/"
        self.tempDir.makeDir()
        self.host = HostInfo(
            PandaSystem.getPackageHostUrl(), appRunner=appRunner, hostDir=self.tempDir, asMirror=False, perPlatform=True
        )

        self.http = HTTPClient.getGlobalPtr()
        if not self.host.hasContentsFile:
            if not self.host.readContentsFile():
                if not self.host.downloadContentsFile(self.http):
                    Standalone.notify.error("couldn't read host")
                    return

    def __del__(self):
        try:
            appRunner.rmtree(self.tempDir)
        except:
            try:
                shutil.rmtree(self.tempDir.toOsSpecific())
            except:
                pass

    def buildAll(self, outputDir="."):
        """ Builds standalone executables for every known platform,
        into the specified output directory. """

        platforms = set()
        for package in self.host.getPackages(name="p3dembed"):
            platforms.add(package.platform)
        if len(platforms) == 0:
            Standalone.notify.warning("No platforms found to build for!")

        outputDir = Filename(outputDir + "/")
        outputDir.makeDir()
        for platform in platforms:
            if platform.startswith("win"):
                self.build(Filename(outputDir, platform + "/" + self.basename + ".exe"), platform)
            else:
                self.build(Filename(outputDir, platform + "/" + self.basename), platform)

    def build(self, output, platform=None, extraTokens={}):
        """ Builds a standalone executable and stores it into the path
        indicated by the 'output' argument. You can specify to build for
        a different platform by altering the 'platform' argument. """

        if platform == None:
            platform = PandaSystem.getPlatform()

        vfs = VirtualFileSystem.getGlobalPtr()

        for package in self.host.getPackages(name="p3dembed", platform=platform):
            if not package.downloadDescFile(self.http):
                Standalone.notify.warning("  -> %s failed for platform %s" % (package.packageName, package.platform))
                continue
            if not package.downloadPackage(self.http):
                Standalone.notify.warning("  -> %s failed for platform %s" % (package.packageName, package.platform))
                continue

            # Figure out where p3dembed might be now.
            if package.platform.startswith("win"):
                # Use p3dembedw unless console_environment was set.
                if extraTokens.get("console_environment", self.tokens.get("console_environment", 0)) != 0:
                    p3dembed = Filename(self.host.hostDir, "p3dembed/%s/p3dembed.exe" % package.platform)
                else:
                    p3dembed = Filename(self.host.hostDir, "p3dembed/%s/p3dembedw.exe" % package.platform)
                    # Fallback for older p3dembed versions
                    if not vfs.exists(p3dembed):
                        Filename(self.host.hostDir, "p3dembed/%s/p3dembed.exe" % package.platform)
            else:
                p3dembed = Filename(self.host.hostDir, "p3dembed/%s/p3dembed" % package.platform)

            if not vfs.exists(p3dembed):
                Standalone.notify.warning("  -> %s failed for platform %s" % (package.packageName, package.platform))
                continue

            return self.embed(output, p3dembed, extraTokens)

        Standalone.notify.error("Failed to build standalone for platform %s" % platform)

    def embed(self, output, p3dembed, extraTokens={}):
        """ Embeds the p3d file into the provided p3dembed executable.
        This function is not really useful - use build() or buildAll() instead. """

        # Load the p3dembed data into memory
        size = p3dembed.getFileSize()
        p3dembed_data = VirtualFileSystem.getGlobalPtr().readFile(p3dembed, True)
        assert len(p3dembed_data) == size

        # Find the magic size string and replace it with the real size,
        # regardless of the endianness of the p3dembed executable.
        hex_size = hex(size)[2:].rjust(8, "0")
        enc_size = "".join([chr(int(hex_size[i] + hex_size[i + 1], 16)) for i in range(0, len(hex_size), 2)])
        p3dembed_data = p3dembed_data.replace(P3DEMBED_MAGIC, enc_size)
        p3dembed_data = p3dembed_data.replace(P3DEMBED_MAGIC[::-1], enc_size[::-1])

        # Write the output file
        Standalone.notify.info("Creating %s..." % output)
        output.makeDir()
        ohandle = open(output.toOsSpecific(), "wb")
        ohandle.write(p3dembed_data)

        # Write out the tokens. Set log_basename to the basename by default
        tokens = {"log_basename": self.basename}
        tokens.update(self.tokens)
        tokens.update(extraTokens)
        for token in tokens.items():
            ohandle.write("\0%s=%s" % token)
        ohandle.write("\0\0")

        # Buffer the p3d file to the output file. 1 MB buffer size.
        phandle = open(self.p3dfile.toOsSpecific(), "rb")
        buf = phandle.read(1024 * 1024)
        while len(buf) != 0:
            ohandle.write(buf)
            buf = phandle.read(1024 * 1024)
        ohandle.close()
        phandle.close()

        os.chmod(output.toOsSpecific(), 0755)

    def getExtraFiles(self, platform):
        """ Returns a list of extra files that will need to be included
        with the standalone executable in order for it to run, such as
        dependent libraries. The returned paths are full absolute paths. """

        package = self.host.getPackages(name="p3dembed", platform=platform)[0]

        if not package.downloadDescFile(self.http):
            Standalone.notify.warning("  -> %s failed for platform %s" % (package.packageName, package.platform))
            return []
        if not package.downloadPackage(self.http):
            Standalone.notify.warning("  -> %s failed for platform %s" % (package.packageName, package.platform))
            return []

        filenames = []
        vfs = VirtualFileSystem.getGlobalPtr()
        for e in package.extracts:
            if e.basename not in [
                "p3dembed",
                "p3dembed.exe",
                "p3dembed.exe.manifest",
                "p3dembedw.exe",
                "p3dembedw.exe.manifest",
            ]:
                filename = Filename(package.getPackageDir(), e.filename)
                filename.makeAbsolute()
                if vfs.exists(filename):
                    filenames.append(filename)
                else:
                    Standalone.notify.error("%s mentioned in xml, but does not exist" % e.filename)

        return filenames
class Standalone:
    """ This class creates a standalone executable from a given .p3d file. """
    notify = directNotify.newCategory("Standalone")

    def __init__(self, p3dfile, tokens={}):
        self.p3dfile = Filename(p3dfile)
        self.basename = self.p3dfile.getBasenameWoExtension()
        self.tokens = tokens

        self.tempDir = Filename.temporary("", self.basename, "") + "/"
        self.tempDir.makeDir()
        self.host = HostInfo(PandaSystem.getPackageHostUrl(),
                             appRunner=appRunner,
                             hostDir=self.tempDir,
                             asMirror=False,
                             perPlatform=True)

        self.http = HTTPClient.getGlobalPtr()
        if not self.host.hasContentsFile:
            if not self.host.readContentsFile():
                if not self.host.downloadContentsFile(self.http):
                    Standalone.notify.error("couldn't read host")
                    return

    def __del__(self):
        try:
            appRunner.rmtree(self.tempDir)
        except:
            try:
                shutil.rmtree(self.tempDir.toOsSpecific())
            except:
                pass

    def buildAll(self, outputDir="."):
        """ Builds standalone executables for every known platform,
        into the specified output directory. """

        platforms = set()
        for package in self.host.getPackages(name="p3dembed"):
            platforms.add(package.platform)
        if len(platforms) == 0:
            Standalone.notify.warning("No platforms found to build for!")

        outputDir = Filename(outputDir + "/")
        outputDir.makeDir()
        for platform in platforms:
            if platform.startswith("win"):
                self.build(
                    Filename(outputDir,
                             platform + "/" + self.basename + ".exe"),
                    platform)
            else:
                self.build(Filename(outputDir, platform + "/" + self.basename),
                           platform)

    def build(self, output, platform=None, extraTokens={}):
        """ Builds a standalone executable and stores it into the path
        indicated by the 'output' argument. You can specify to build for
        a different platform by altering the 'platform' argument. """

        if platform == None:
            platform = PandaSystem.getPlatform()

        vfs = VirtualFileSystem.getGlobalPtr()

        for package in self.host.getPackages(name="p3dembed",
                                             platform=platform):
            if not package.downloadDescFile(self.http):
                Standalone.notify.warning(
                    "  -> %s failed for platform %s" %
                    (package.packageName, package.platform))
                continue
            if not package.downloadPackage(self.http):
                Standalone.notify.warning(
                    "  -> %s failed for platform %s" %
                    (package.packageName, package.platform))
                continue

            # Figure out where p3dembed might be now.
            if package.platform.startswith("win"):
                # Use p3dembedw unless console_environment was set.
                if extraTokens.get("console_environment",
                                   self.tokens.get("console_environment",
                                                   0)) != 0:
                    p3dembed = Filename(
                        self.host.hostDir,
                        "p3dembed/%s/p3dembed.exe" % package.platform)
                else:
                    p3dembed = Filename(
                        self.host.hostDir,
                        "p3dembed/%s/p3dembedw.exe" % package.platform)
                    # Fallback for older p3dembed versions
                    if not vfs.exists(p3dembed):
                        Filename(self.host.hostDir,
                                 "p3dembed/%s/p3dembed.exe" % package.platform)
            else:
                p3dembed = Filename(self.host.hostDir,
                                    "p3dembed/%s/p3dembed" % package.platform)

            if not vfs.exists(p3dembed):
                Standalone.notify.warning(
                    "  -> %s failed for platform %s" %
                    (package.packageName, package.platform))
                continue

            return self.embed(output, p3dembed, extraTokens)

        Standalone.notify.error("Failed to build standalone for platform %s" %
                                platform)

    def embed(self, output, p3dembed, extraTokens={}):
        """ Embeds the p3d file into the provided p3dembed executable.
        This function is not really useful - use build() or buildAll() instead. """

        # Load the p3dembed data into memory
        size = p3dembed.getFileSize()
        p3dembed_data = VirtualFileSystem.getGlobalPtr().readFile(
            p3dembed, True)
        assert len(p3dembed_data) == size

        # Find the magic size string and replace it with the real size,
        # regardless of the endianness of the p3dembed executable.
        hex_size = hex(size)[2:].rjust(8, "0")
        enc_size = "".join([
            chr(int(hex_size[i] + hex_size[i + 1], 16))
            for i in range(0, len(hex_size), 2)
        ])
        p3dembed_data = p3dembed_data.replace(P3DEMBED_MAGIC, enc_size)
        p3dembed_data = p3dembed_data.replace(P3DEMBED_MAGIC[::-1],
                                              enc_size[::-1])

        # Write the output file
        Standalone.notify.info("Creating %s..." % output)
        output.makeDir()
        ohandle = open(output.toOsSpecific(), "wb")
        ohandle.write(p3dembed_data)

        # Write out the tokens. Set log_basename to the basename by default
        tokens = {"log_basename": self.basename}
        tokens.update(self.tokens)
        tokens.update(extraTokens)
        for token in tokens.items():
            ohandle.write("\0%s=%s" % token)
        ohandle.write("\0\0")

        # Buffer the p3d file to the output file. 1 MB buffer size.
        phandle = open(self.p3dfile.toOsSpecific(), "rb")
        buf = phandle.read(1024 * 1024)
        while len(buf) != 0:
            ohandle.write(buf)
            buf = phandle.read(1024 * 1024)
        ohandle.close()
        phandle.close()

        os.chmod(output.toOsSpecific(), 0755)

    def getExtraFiles(self, platform):
        """ Returns a list of extra files that will need to be included
        with the standalone executable in order for it to run, such as
        dependent libraries. The returned paths are full absolute paths. """

        package = self.host.getPackages(name="p3dembed", platform=platform)[0]

        if not package.downloadDescFile(self.http):
            Standalone.notify.warning("  -> %s failed for platform %s" %
                                      (package.packageName, package.platform))
            return []
        if not package.downloadPackage(self.http):
            Standalone.notify.warning("  -> %s failed for platform %s" %
                                      (package.packageName, package.platform))
            return []

        filenames = []
        vfs = VirtualFileSystem.getGlobalPtr()
        for e in package.extracts:
            if e.basename not in [
                    "p3dembed", "p3dembed.exe", "p3dembed.exe.manifest",
                    "p3dembedw.exe", "p3dembedw.exe.manifest"
            ]:
                filename = Filename(package.getPackageDir(), e.filename)
                filename.makeAbsolute()
                if vfs.exists(filename):
                    filenames.append(filename)
                else:
                    Standalone.notify.error(
                        "%s mentioned in xml, but does not exist" % e.filename)

        return filenames