Esempio n. 1
0
    def __init__(self, root_path: str):
        """
        Parameters
        ----------
        root_path
            The root game directory path (where PathOfExile.exe is located)
        """
        self.directory: Union[FileSystemNode, None] = None

        self.root_path: str = root_path
        self.ggpk: Union[GGPKFile, None] = None

        ggpk_path = os.path.join(root_path, 'content.ggpk')
        if os.path.exists(os.path.join(root_path, 'content.ggpk')):
            self.ggpk = GGPKFile()
            self.ggpk.read(ggpk_path)
            self.ggpk.directory_build()

        self.index: Union[Index, None] = Index()
        try:
            if self.ggpk:
                self.index.read(self.ggpk[self.index.PATH].record.extract())
            else:
                self.index.read(os.path.join(root_path, self.index.PATH))
        except FileNotFoundError:
            self.index = None
Esempio n. 2
0
    def _setup(self, args):
        """
        :param args: argparse args passed on
        :return:
        """
        temp_dir = config.get_option('temp_dir', safe=False)

        content_ggpk = get_content_ggpk_path()

        console('Reading "%s"...' % content_ggpk)
        ggpk = GGPKFile()
        ggpk.read(content_ggpk)

        console('Building directory...')
        ggpk.directory_build()

        console('Extracting data files to "%s"...' % temp_dir)
        ggpk['Data'].extract_to(temp_dir)
        ggpk['Metadata'].extract_to(temp_dir)

        console('Hashing...')

        config.set_setup_variable('temp_dir', 'hash', get_content_ggpk_hash())

        console('Done.')
Esempio n. 3
0
    def run(self):
        fm = self.parent()
        p = fm.parent()

        p.sig_log_message.emit(
            self.tr('Open GGPK file "%(file)s".') % {'file': self._file_path})

        self.sig_update_progress.emit(self.tr('Reading GGPK records...'), 100)
        ggpk_file = GGPKFile()
        # Hook the function for progress bar
        ggpk_file._read_record = self._progress_ggpk(ggpk_file._read_record)
        ggpk_file.read(self._file_path)
        # Finished
        self.progress_bar.sig_progress.emit(100)

        p.sig_log_message.emit(self.tr('Building GGPK directory...'))
        ggpk_file.directory_build()

        self.ggpk_file = ggpk_file
Esempio n. 4
0
def run():
    out = []

    path = PoEPath(version=VERSION.STABLE,
                   distributor=DISTRIBUTOR.GGG).get_installation_paths()[0]
    dat.set_default_spec(VERSION.STABLE)
    existing_set = set(dat._default_spec.keys())

    ggpk = GGPKFile()
    ggpk.read(os.path.join(path, 'content.ggpk'))
    ggpk.directory_build()

    index = Index()
    index.read(ggpk[Index.PATH].record.extract())

    file_set = set()

    for name in index.get_dir_record('Data').files:
        if not name.endswith('.dat'):
            continue

        # Not a regular dat file, ignore
        if name in ['Languages.dat']:
            continue

        file_set.add(name)

    new = sorted(file_set.difference(set(existing_set)))

    for fn in new:
        fr = index.get_file_record('Data/' + fn)
        fr.bundle.read(ggpk[fr.bundle.ggpk_path].record.extract())
        binary = fr.get_file()
        data_offset = binary.find(dat.DAT_FILE_MAGIC_NUMBER)
        n_rows = struct.unpack('<I', binary[0:4])[0]
        length = data_offset - 4
        if n_rows > 0:
            record_length = length // n_rows

        out.append("""    '%s': File(
        fields=(
%s
        ),
    ),""" % (fn, spec_unknown(record_length)))

    print('\n'.join(out))
Esempio n. 5
0
def get_content_ggpk(path=None):
    """
    Gets the GGPKFile instance based on the stored config variables.

    :param path: path to use if not None, otherwise determine path automatically
    :type path: str or None

    :return: Parsed GGPKFile instance
    :rtype: GGPKFile()
    """
    if path is None:
        path = get_content_ggpk_path()

    ggpk = GGPKFile()
    ggpk.read(path)
    ggpk.directory_build()

    return ggpk
Esempio n. 6
0
def load_ggpk(ggpk_path):
    ggpk = GGPKFile()
    ggpk.read(ggpk_path)
    ggpk.directory_build()
    return ggpk
Esempio n. 7
0
def ggpkfile(poe_path):
    ggpk = GGPKFile()
    ggpk.read(os.path.join(poe_path, 'content.ggpk'))
    ggpk.directory_build()

    return ggpk
Esempio n. 8
0
class FileSystem:
    """
    The FileSystem class is used to simplify accessing files from the game via
    a single call rather then checking disk, ggpk or bundles separately.

    Upon initialization of the class, the corresponding Index bundle and GGPK
    will be automatically read if present.

    Further decompression of bundles or reading of data will be only be done
    when the get_file method is called.
    """
    def __init__(self, root_path: str):
        """
        Parameters
        ----------
        root_path
            The root game directory path (where PathOfExile.exe is located)
        """
        self.directory: Union[FileSystemNode, None] = None

        self.root_path: str = root_path
        self.ggpk: Union[GGPKFile, None] = None

        ggpk_path = os.path.join(root_path, 'content.ggpk')
        if os.path.exists(os.path.join(root_path, 'content.ggpk')):
            self.ggpk = GGPKFile()
            self.ggpk.read(ggpk_path)
            self.ggpk.directory_build()

        self.index: Union[Index, None] = Index()
        try:
            if self.ggpk:
                self.index.read(self.ggpk[self.index.PATH].record.extract())
            else:
                self.index.read(os.path.join(root_path, self.index.PATH))
        except FileNotFoundError:
            self.index = None

    def get_file(self, path: str) -> bytes:
        """
        Retrieves a file contents as binary data via the given path (relative
        to the root directory)

        Parameters
        ----------
        path
            The path relative to the root game directory (i.e. root_path)

        Returns
        -------
            The unbuffered binary file data in bytes
        """
        if self.index:
            try:
                fr = self.index.get_file_record(path)
            except FileNotFoundError:
                pass
            else:
                if self.ggpk:
                    fr.bundle.read(
                        self.ggpk[fr.bundle.ggpk_path].record.extract())
                else:
                    fr.bundle.read(
                        os.path.join(self.root_path, fr.bundle.ggpk_path))
                return fr.get_file()

        # If the file is in the index, this section can't be reached
        if self.ggpk:
            try:
                return self.ggpk[path].record.extract()
            except FileNotFoundError:
                pass

        # If no GGPK is loaded or the file isn't within the GGPK, lastly the
        # root directory is tried
        try:
            with open(os.path.join(self.root_path, path), 'rb') as f:
                return f.read()
        except FileNotFoundError:
            raise FileNotFoundError(
                'Specified file can not be found in the Index, content.ggpk '
                'or disk')

    def extract_dds(self, data: bytes) -> bytes:
        """
        Attempts to extract a .dds from the given data bytes.

        .dds files in the content.ggpk may be compressed with brotli or may be
        a reference to another .dds file.

        This function will take of those kind of files accordingly and try to return
        a file instead.
        If any problems arise an error will be raised instead.

        Parameters
        ----------
        data
            The raw data to extract the dds from.

        Returns
        -------
        bytes
            the uncompressed, dereferenced .dds file data

        Raises
        -------
        ValueError
            If the file data contains a reference, but path_or_ggpk is not specified
        TypeError
            If the file data contains a reference, but path_or_ggpk is of invalid
            type (i.e. not str or :class:`GGPKFile`
        ParserError
            If the uncompressed size does not match the size in the header
        brotli.error
            If whatever bytes were read were not brotli compressed
        """
        # Already a DDS file, so return it
        if data[:4] == b'DDS ':
            return data
        # Is this a reference?
        elif data[:1] == b'*':
            path = data[1:].decode()
            data = self.get_file(path)
            return self.extract_dds(data)
        else:
            size = int.from_bytes(data[:4], 'little')
            dec = brotli.decompress(data[4:])
            if len(dec) != size:
                raise ParserError(
                    'Decompressed size does not match size in the header')
            return dec

    def build_directory(self) -> FileSystemNode:
        """
        Builds a joint directory from the files available on disk, in ggpk and
        in bundles.

        The directory is not required to retrieve files from the file system
        and serves more educational purposes.

        Returns
        -------
            The directory.
        """
        self.directory = FileSystemNode(
            file_system=self,
            name='',
            parent=None,
            file_system_type=FILE_SYSTEM_TYPES.ROOT,
            is_file=False,
        )

        for path, directories, files in os.walk(self.root_path):
            p = os.path.commonprefix([self.root_path, path])
            node = self.directory[path.replace(p, '')]
            params = {
                'file_system': self,
                'parent': node,
                'file_system_type': FILE_SYSTEM_TYPES.DISK,
            }
            for name in directories:
                node.children[name] = FileSystemNode(name=name,
                                                     is_file=False,
                                                     **params)
            for name in files:
                node.children[name] = FileSystemNode(name=name,
                                                     is_file=True,
                                                     **params)

        if self.ggpk:

            def add_to_directory(node, depth):
                # Return at depth 0? Root object

                if node.parent:
                    root = self.directory[node.parent.get_path()]
                else:
                    root = self.directory

                root.children[node.name] = FileSystemNode(
                    file_system=self,
                    name=node.name,
                    file_system_type=FILE_SYSTEM_TYPES.GGPK,
                    is_file=node.is_file,
                    parent=root,
                )

            self.ggpk.directory.walk(function=add_to_directory)

        for dir_record in self.index.directories.values():
            parent = self.directory
            for directory in dir_record.path.split('/'):
                try:
                    parent = parent.children[directory]
                except KeyError:
                    node = FileSystemNode(
                        file_system=self,
                        name=directory,
                        file_system_type=FILE_SYSTEM_TYPES.BUNDLE,
                        is_file=False,
                        parent=parent)
                    parent.children[directory] = node
                    parent = node

            for file_name in dir_record.files:
                node = FileSystemNode(
                    file_system=self,
                    name=file_name,
                    file_system_type=FILE_SYSTEM_TYPES.BUNDLE,
                    is_file=True,
                    parent=parent)
                parent.children[file_name] = node

        return self.directory