Пример #1
0
    def write_to(self, io):
        """Writes ERF to file.

        :param io: A file path.
        """
        out = io
        io, path = tempfile.mkstemp()
        fnlen = Erf.filename_length(self.fversion)
        lstr_iter = iter(sorted(self.localized_strings.items()))
        locstr = []
        for k, v in lstr_iter:
            locstr.append(struct.pack("<L L %ds x" % len(v), k, len(v) + 1, v.encode(get_encoding())))
        locstr = b''.join(locstr)

        keylist = []
        for i, co in enumerate(self.content):
            pad = 0
            if len(co.resref) < fnlen:
                pad = fnlen - len(co.resref)

            keylist.append(struct.pack("<%ds %dx L h h" % (len(co.resref), pad),
                                       co.resref.encode(get_encoding()),
                                       i, co.res_type, 0))
        keylist = b''.join(keylist)

        offset = 160 + len(locstr) + len(keylist) + 8 * len(self.content)

        reslist = []
        for co in self.content:
            reslist.append(struct.pack("< L L", offset, co.size))
            offset += co.size

        reslist = b''.join(reslist)

        offset_to_locstr = 160
        offset_to_keylist = offset_to_locstr + len(locstr)
        offset_to_resourcelist = offset_to_keylist + len(keylist)

        header = struct.pack("8s LL LL LL LL L 116x",
                             (self.ftype + ' ' + self.fversion).encode(get_encoding()),
                             len(self.localized_strings),
                             len(locstr), len(self.content), offset_to_locstr, offset_to_keylist,
                             offset_to_resourcelist, self.year, self.day_of_year, self.desc_strref)

        os.write(io, header)
        os.write(io, locstr)
        os.write(io, keylist)
        os.write(io, reslist)

        for co in self.content:
            os.write(io, co.get())

        os.close(io)
        shutil.copy(path, out)
        os.remove(path)
Пример #2
0
    def write(self, io):
        header = struct.pack("4s 4s I I I",
                             self.ftype.encode('ascii'),
                             self.fvers.encode('ascii'),
                             self.lang,
                             len(self),
                             self.HEADER_SIZE + len(self) * self.DATA_ELEMENT_SIZE)
        io.write(header)

        offset = 0
        strings = []
        for i in range(len(self)):
            n = self[i]
            entries = struct.pack("I 16s I I I I f",
                                  0x1 if len(n) else 0,
                                  b"",
                                  0,
                                  0,
                                  offset if len(n) else 0,
                                  len(n),
                                  0)
            io.write(entries)
            offset += len(n)
            strings.append(n)
        io.write(bytearray(''.join(strings), get_encoding()))
Пример #3
0
    def write_tlk(self, io, lang):
        header = struct.pack("4s 4s I I I",
                             b'TLK ',
                             b'V3.0',
                             lang,
                             len(self),
                             Tlk.HEADER_SIZE + len(self) * Tlk.DATA_ELEMENT_SIZE)
        io.write(header)

        offset = 0
        strings = []

        # Always inject Bad Strref
        if not self[0]:
            self[0] = 'Bad Strref'

        for i in range(len(self)):
            n = self[i]
            entries = struct.pack("I 16s I I I I f",
                                  0x1 if len(n) else 0,
                                  b"",
                                  0,
                                  0,
                                  offset if len(n) else 0,
                                  len(n),
                                  0)
            io.write(entries)
            if len(n):
                offset += len(n)
                strings.append(n)
        io.write(bytearray(''.join(strings), get_encoding()))
Пример #4
0
    def unpack(source, offset):
        """Parses a gff cexostring."""

        source.seek(offset)
        length = struct.unpack('I', source.read(4))[0]
        pattern = "%ds" % length
        data = struct.unpack(pattern, source.read(length))[0].decode(get_encoding())

        return NWString(data)
Пример #5
0
    def unpack(source, offset):
        """Parses a gff cexostring."""

        source.seek(offset)
        length = struct.unpack('I', source.read(4))[0]
        pattern = "%ds" % length
        data = struct.unpack(pattern,
                             source.read(length))[0].decode(get_encoding())

        return NWString(data)
Пример #6
0
    def unpack(source, offset):
        """Parses a gff resref."""

        # identify the current position, read and parse the resref, and return to the current position
        source.seek(offset)
        length = struct.unpack('B', source.read(1))[0]
        if length == 0:
            val = ''
        else:
            pattern = "%ds" % length
            val = struct.unpack(pattern, source.read(length))[0].decode(get_encoding())

        return NWResref(val)
Пример #7
0
    def unpack(source, offset):
        """Parses a gff resref."""

        # identify the current position, read and parse the resref, and return to the current position
        source.seek(offset)
        length = struct.unpack('B', source.read(1))[0]
        if length == 0:
            val = ''
        else:
            pattern = "%ds" % length
            val = struct.unpack(pattern,
                                source.read(length))[0].decode(get_encoding())

        return NWResref(val)
Пример #8
0
    def __init__(self, source):
        if isinstance(source, str):
            source = ContentObject.from_file(source)
        elif not isinstance(source, ContentObject):
            raise ValueError("Unsupported source type %s!" % type(source))

        self.columns = []
        self.rows = []
        self.max = None
        self.newline = "\n"
        self.default = None
        self.co = source
        data = source.get()
        if not isinstance(data, str):
            data = data.decode(get_encoding())
        self.parse(data)
Пример #9
0
    def write(self, io):
        header = struct.pack(
            "4s 4s I I I", self.ftype.encode('ascii'),
            self.fvers.encode('ascii'), self.lang, len(self),
            self.HEADER_SIZE + len(self) * self.DATA_ELEMENT_SIZE)
        io.write(header)

        offset = 0
        strings = []
        for i in range(len(self)):
            n = self[i]
            entries = struct.pack("I 16s I I I I f", 0x1 if len(n) else 0, b"",
                                  0, 0, offset if len(n) else 0, len(n), 0)
            io.write(entries)
            offset += len(n)
            strings.append(n)
        io.write(bytearray(''.join(strings), get_encoding()))
Пример #10
0
    def __getitem__(self, i):
        """Get a TLK element.  Tlk supports integer indices and
        Python slices.  Please note that taking a huge slice say a
        reverse (tlk[::-1] can be a very costly.
        """

        if isinstance(i, slice):
            indices = i.indices(len(self))

            n = Tlk(None)
            n.ftype = self.ftype
            n.fvers = self.fvers
            n.lang = self.lang
            n.cache = {}

            for i in range(*indices):
                n.add(**self[i])

            return n

        if i == 0xffffffff or i >= len(self) or i < 0:
            return ""
        elif i in self.entries:
            return self.entries[i]
        elif self.io is not None:
            seek_to = self.HEADER_SIZE + i * self.DATA_ELEMENT_SIZE
            self.io.seek(seek_to)

            data = self.io.read(self.DATA_ELEMENT_SIZE)

            temp = struct.unpack("I 16s I I I I f", data)
            flags, sound_resref, v_variance, p_variance, offset, size, sound_length = temp
            sound_resref = sound_resref

            self.io.seek(self.str_offset + offset)
            text = self.io.read(size)
            try:
                text = text.decode(get_encoding()) if flags & 0x1 > 0 else ""
            except UnicodeDecodeError:
                print("Encoding Error: Unable to read entry %d" % i)
                text = ''

            return text
        return ""
Пример #11
0
    def __getitem__(self, i):
        """Get a TLK element.  Tlk supports integer indices and
        Python slices.  Please note that taking a huge slice say a
        reverse (tlk[::-1] can be a very costly.
        """

        if isinstance(i, slice):
            indices = i.indices(len(self))

            n = Tlk(None)
            n.ftype = self.ftype
            n.fvers = self.fvers
            n.lang = self.lang
            n.cache = {}

            for i in range(*indices):
                n.add(**self[i])

            return n

        if i == 0xffffffff or i >= len(self) or i < 0:
            return ""
        elif i in self.entries:
            return self.entries[i]
        elif self.io is not None:
            seek_to = self.HEADER_SIZE + i * self.DATA_ELEMENT_SIZE
            self.io.seek(seek_to)

            data = self.io.read(self.DATA_ELEMENT_SIZE)

            temp = struct.unpack("I 16s I I I I f", data)
            flags, sound_resref, v_variance, p_variance, offset, size, sound_length = temp
            sound_resref = sound_resref

            self.io.seek(self.str_offset + offset)
            text = self.io.read(size)
            try:
                text = text.decode(get_encoding()) if flags & 0x1 > 0 else ""
            except UnicodeDecodeError:
                print("Encoding Error: Unable to read entry %d" % i)
                text = ''

            return text
        return ""
Пример #12
0
    def unpack(source, offset):
        """Parses a gff cexolocstring."""

        # identify the current position, read and parse the cexolocstring, and return to the current position
        position = source.tell()
        source.seek(offset)
        length, stringref, count = struct.unpack('IiI', source.read(12))
        result = []
        if count > 0:
            for substring in range(0, count):
                id, length = struct.unpack('2I', source.read(8))
                pattern = "%ds" % length
                data = struct.unpack(pattern,
                                     source.read(
                                         struct.calcsize(pattern)))[0].decode(
                                             get_encoding())
                result.append([id, data])

        source.seek(position)

        return NWLocalizedString(stringref, result)
Пример #13
0
    def write_tlk(self, io, lang):
        header = struct.pack(
            "4s 4s I I I", b'TLK ', b'V3.0', lang, len(self),
            Tlk.HEADER_SIZE + len(self) * Tlk.DATA_ELEMENT_SIZE)
        io.write(header)

        offset = 0
        strings = []

        # Always inject Bad Strref
        if not self[0]:
            self[0] = 'Bad Strref'

        for i in range(len(self)):
            n = self[i]
            entries = struct.pack("I 16s I I I I f", 0x1 if len(n) else 0, b"",
                                  0, 0, offset if len(n) else 0, len(n), 0)
            io.write(entries)
            if len(n):
                offset += len(n)
                strings.append(n)
        io.write(bytearray(''.join(strings), get_encoding()))
Пример #14
0
    def __init__(self, fname, data_path):
        super(Key, self).__init__()

        self.root = data_path
        self.bif = []

        with open(fname, 'rb') as io:
            header = io.read(8 + (4 * 6) + 32)
            hs = struct.unpack("<4s 4s LLLLLL 32s", header)

            self.ftype = hs[0]
            self.fvers = hs[1]

            bif_count = hs[2]
            key_count = hs[3]
            offset_to_file_table = hs[4]
            offset_to_key_table = hs[5]

            self.year = hs[6]
            self.day_of_year = hs[7]
            reserved = hs[8]

            io.seek(offset_to_file_table)
            data = io.read(12 * bif_count)

            self.file_table = []
            for c in chunks(data, 12):
                if len(c) != 12:
                    break

                size, name_offset, name_size, drives = struct.unpack("IIhh", c)
                io.seek(name_offset)
                name = io.read(name_size)
                name = struct.unpack("%ds" % name_size, name)[0]
                name = name.decode(get_encoding())
                name = name.rstrip(' \t\r\n\0')
                name = os.path.join(self.root, name.replace('\\', os.sep))
                name = os.path.abspath(name)
                self.bif.append(Bif(self, name))
                self.file_table.append((size, name, drives))

            self.key_table = {}
            io.seek(offset_to_key_table)
            data = io.read(22 * key_count)

            for c in chunks(data, 22):
                if len(c) != 22:
                    break
                resref, res_type, res_id = struct.unpack("<16s hL", c)
                resref = resref.decode(get_encoding())
                self.key_table[res_id] = (resref.rstrip(' \t\r\n\0'), res_type)

            self.fn_to_co = {}
            for res_id, (resref, res_type) in self.key_table.items():
                bif_idx = res_id >> 20
                bif = self.bif[bif_idx]
                res_id &= 0xfffff

                # print res_id, resref, res_type, bif_idx
                if res_id not in bif.contained:
                    msg = "%s does not have %d" % (bif.io.name, res_id)
                    raise ValueError(msg)

                ofs, sz, _rt = bif.contained[res_id]
                o = res.ContentObject(resref, res_type, bif.io, ofs, sz)

                fn = o.get_filename()
                if fn in self.fn_to_co and self.fn_to_co[fn][2] < bif_idx:
                    oo, biff, unused = self.fn_to_co[fn]
                    print("%s, in %s shadowed by file in %s" %
                          (fn, biff.io, biff.io))
                    self.content.remove(oo)

                self.fn_to_co[fn] = (o, bif, bif_idx)
                self.add(o)
Пример #15
0
    def unpack(source, offset):
        """Parses a gff cexolocstring."""

        # identify the current position, read and parse the cexolocstring, and return to the current position
        position = source.tell()
        source.seek(offset)
        length, stringref, count = struct.unpack('IiI', source.read(12))
        result = []
        if count > 0:
            for substring in range(0, count):
                id, length = struct.unpack('2I', source.read(8))
                pattern = "%ds" % length
                data = struct.unpack(pattern, source.read(struct.calcsize(pattern)))[0].decode(get_encoding())
                result.append([id, data])

        source.seek(position)

        return NWLocalizedString(stringref, result)
Пример #16
0
    def from_file(fname):
        """Create an Erf from a file handle.

        :param fname: File name.

        """
        new_erf = None
        with open(fname, 'rb') as io:
            header = io.read(160)
            hs = struct.unpack("< 4s 4s LL LL LL LL L 116s", header)

            ftype = hs[0].decode(get_encoding()).strip()
            if ftype not in Erf.TYPES:
                raise ValueError("Invalid file type!")

            fvers = hs[1].decode(get_encoding())
            fname_len = Erf.filename_length(fvers)

            new_erf = Erf(ftype, fvers)
            new_erf.io = fname

            lstr_count = hs[2]
            lstr_size = hs[3]
            entry_count = hs[4]
            offset_to_lstr = hs[5]
            offset_to_keys = hs[6]
            offset_to_res = hs[7]
            new_erf.year = hs[8]
            new_erf.day_of_year = hs[9]
            new_erf.desc_strref = hs[10]

            io.seek(offset_to_lstr)
            lstr = io.read(lstr_size)

            for ls in range(lstr_count):
                if len(lstr) == 0:
                    print("locstr table: not enough entries (expected: %d, got: %d)" % (lstr_count, ls),
                          file=sys.stderr)
                    break

                if len(lstr) < 8:
                    print("locstr table: not enough entries (expected: %d, got: %d) partial data: %s" % (
                        lstr_count, ls, lstr), file=sys.stderr)
                    break

                lid, strsz = struct.unpack("<L L", lstr[:8])
                if strsz > len(lstr) - 8:
                    strsz = len(lstr) - 8

                # Necessary for hacking around the fact that erf.exe adds an extra null
                # to the end of the description string.
                try:
                    str = struct.unpack("8x %ds" % strsz, lstr)[0].decode(get_encoding())  #
                except struct.error:
                    str = struct.unpack("8x %ds" % (strsz + 1,), lstr)[0].decode(get_encoding())  #

                new_erf.localized_strings[lid] = str.rstrip(' \t\r\n\0')
                lstr = lstr[8 + len(str):]

            keylist_entry_size = fname_len + 4 + 2 + 2
            io.seek(offset_to_keys)
            keylist = io.read(keylist_entry_size * entry_count)

            fmt = "%ds I h h" % fname_len
            fmt = fmt * entry_count
            fmt = '<' + fmt

            keylist = struct.unpack(fmt, keylist)

            for resref, res_id, res_type, unused in chunks(keylist, 4):
                co = res.ContentObject(resref.decode(get_encoding()).rstrip(' \t\r\n\0'),
                                       res_type, fname)
                new_erf.add(co)

            resourcelist_entry_size = 4 + 4
            io.seek(offset_to_res)
            resourcelist = io.read(resourcelist_entry_size * entry_count)
            resourcelist = struct.unpack("I I" * entry_count, resourcelist)
            _index = -1
            for offset, size in chunks(resourcelist, 2):
                _index += 1
                try:
                    co = new_erf.content[_index]
                    co.offset = offset
                    co.size = size
                except IndexError:
                    print("WARNING: Attempt to index invalid content object in '%s' at offset %X" % (fname, offset),
                          file=sys.stderr)

        return new_erf
Пример #17
0
    def __init__(self, fname, data_path):
        super(Key, self).__init__()

        self.root = data_path
        self.bif = []

        with open(fname, 'rb') as io:
            header = io.read(8 + (4 * 6) + 32)
            hs = struct.unpack("<4s 4s LLLLLL 32s", header)

            self.ftype = hs[0]
            self.fvers = hs[1]

            bif_count = hs[2]
            key_count = hs[3]
            offset_to_file_table = hs[4]
            offset_to_key_table = hs[5]

            self.year = hs[6]
            self.day_of_year = hs[7]
            reserved = hs[8]

            io.seek(offset_to_file_table)
            data = io.read(12 * bif_count)

            self.file_table = []
            for c in chunks(data, 12):
                if len(c) != 12:
                    break

                size, name_offset, name_size, drives = struct.unpack("IIhh", c)
                io.seek(name_offset)
                name = io.read(name_size)
                name = struct.unpack("%ds" % name_size, name)[0]
                name = name.decode(get_encoding())
                name = name.rstrip(' \t\r\n\0')
                name = os.path.join(self.root, name.replace('\\', os.sep))
                name = os.path.abspath(name)
                self.bif.append(Bif(self, name))
                self.file_table.append((size, name, drives))

            self.key_table = {}
            io.seek(offset_to_key_table)
            data = io.read(22 * key_count)

            for c in chunks(data, 22):
                if len(c) != 22:
                    break
                resref, res_type, res_id = struct.unpack("<16s hL", c)
                resref = resref.decode(get_encoding())
                self.key_table[res_id] = (resref.rstrip(' \t\r\n\0'), res_type)

            self.fn_to_co = {}
            for res_id, (resref, res_type) in self.key_table.items():
                bif_idx = res_id >> 20
                bif = self.bif[bif_idx]
                res_id &= 0xfffff

                # print res_id, resref, res_type, bif_idx
                if res_id not in bif.contained:
                    msg = "%s does not have %d" % (bif.io.name, res_id)
                    raise ValueError(msg)

                ofs, sz, _rt = bif.contained[res_id]
                o = res.ContentObject(resref, res_type, bif.io, ofs, sz)

                fn = o.get_filename()
                if fn in self.fn_to_co and self.fn_to_co[fn][2] < bif_idx:
                    oo, biff, unused = self.fn_to_co[fn]
                    print("%s, in %s shadowed by file in %s" % (fn, biff.io, biff.io))
                    self.content.remove(oo)

                self.fn_to_co[fn] = (o, bif, bif_idx)
                self.add(o)