Ejemplo n.º 1
0
Archivo: erf.py Proyecto: rmilne/pynwn
    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
            max = len(co.resref)
            if len(co.resref) > fnlen:
                print("truncating filename %s, longer than %d" % (co.resref, fnlen))
                max = fnlen
            else:
                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)
Ejemplo n.º 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()))
Ejemplo n.º 3
0
Archivo: tls.py Proyecto: rmilne/pynwn
    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()))
Ejemplo n.º 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)
Ejemplo n.º 5
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)
Ejemplo n.º 6
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)
Ejemplo n.º 7
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)
Ejemplo n.º 8
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 not self.io is 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 as e:
                print("Encoding Error: Unable to read entry %d" % i)
                text = ""

            return text
        return ""
Ejemplo n.º 9
0
Archivo: tls.py Proyecto: rmilne/pynwn
    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()))
Ejemplo n.º 10
0
Archivo: gff.py Proyecto: rmilne/pynwn
    def load(self):
        """Loads the source of the associated gff file."""

        # attempt to open the gff file and load its header
        self.source = io.BytesIO(self.co.get())

        header = struct.unpack(self.HeaderPattern,
                               self.source.read(struct.calcsize(self.HeaderPattern)))

        if (header[0].decode(get_encoding()).rstrip() == self.filetype
            and header[1].decode(get_encoding()) == self.Version):
            self.structoffset, self.structcount = header[2:4]
            self.fieldoffset, self.fieldcount = header[4:6]
            self.labeloffset, self.labelcount = header[6:8]
            self.dataoffset, self.datasize = header[8:10]
            self.indiceoffset, self.indicesize = header[10:12]
            self.listoffset, self.listsize = header[12:14]
        else:
            if header[1].decode(get_encoding()) != self.Version:
                raise ValueError("File: %s: gff file version '%s' does not match current valid version '%s'" % (self.co.get_filename(), header[1], self.Version))
            else:
                raise ValueError("File: %s: gff file type '%s' does not match specified file type '%s'" % (self.co.get_filename(), header[0].rstrip(), self.filetype))

        # position the source file at the struct array and prepare structs list
        self.source.seek(self.structoffset)
        self.structs = []

        # parse the gff struct array
        size = struct.calcsize(self.StructPattern)
        rd = self.source.read(self.structcount * size)

        for chunk in chunks(rd, size):
            type, offset, count = struct.unpack(self.StructPattern, chunk)
            if offset == 0xffffffff:
                self.structs.append([type, -1])
            elif count == 1:
                self.structs.append([type, offset])
            else:
                pattern = "%dI" % count
                position = self.source.tell()
                self.source.seek(self.indiceoffset + offset)
                data = self.source.read(struct.calcsize(pattern))
                self.source.seek(position)

                indexes = struct.unpack(pattern, data)
                self.structs.append([type, list(indexes)])


        # position the source file at the label array and prepare labels list
        self.source.seek(self.labeloffset)
        self.labels = []

        # parse the gff label array
        size = struct.calcsize(self.LabelPattern)
        rd = self.source.read(size * self.labelcount)
        for chunk in chunks(rd, size):
            label = struct.unpack(self.LabelPattern, chunk)[0].decode(get_encoding())
            self.labels.append(label.rstrip('\x00'))

        # position the source file at the field array and prepare fields list
        self.source.seek(self.fieldoffset)
        self.fields = []

        # parse the gff field array
        size = struct.calcsize(self.FieldPattern)
        dwordsize = struct.calcsize(self.DwordPattern)
        for index in range(0, self.fieldcount):
            type, label = struct.unpack(self.FieldPattern,
                                        self.source.read(size))
            Type = self.Classes[type]

            position = None
            # False indicates there is no offset
            if not Type.at_offset is False:
                offset = struct.unpack('I', self.source.read(4))[0]
                position = self.source.tell()

            if Type.at_offset == 'data':
                offset += self.dataoffset
            elif Type.at_offset == 'list':
                offset += self.listoffset

            if position:
                data = Type.unpack(self.source, offset)
                self.source.seek(position)
            else:
                data = Type.unpack(self.source)

            type_name  = Type.type
            label_name = self.labels[label]
            self.fields.append([type_name, label_name, data])

        # close the source file and build the gff structure, then indicate
        # status
        self.source.close()
        self._structure = self.build_struct(0)
        return True
Ejemplo n.º 11
0
Archivo: key.py Proyecto: rmilne/pynwn
    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("LLhh", 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 = res_id & 0xfffff

                #print res_id, resref, res_type, bif_idx
                if not res_id 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)
Ejemplo n.º 12
0
Archivo: erf.py Proyecto: rmilne/pynwn
    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
            max = len(co.resref)
            if len(co.resref) > fnlen:
                print("truncating filename %s, longer than %d" %
                      (co.resref, fnlen))
                max = fnlen
            else:
                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)
Ejemplo n.º 13
0
Archivo: erf.py Proyecto: rmilne/pynwn
    def from_file(fname):
        """Create an Erf from a file handle.

        :param io: A file handle.

        """
        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 not ftype 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))
                    break

                if len(lstr) < 8:
                    print(
                        "locstr table: not enough entries (expected: %d, got: %d)"
                        % (lstr_count, ls) + " partial data: " + lstr)
                    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 as e:
                    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 as e:
                    print(
                        "WARNING: Attempt to index invalid content object in '%s' at offset %X"
                        % (fname, offset))

        return new_erf
Ejemplo n.º 14
0
Archivo: gff.py Proyecto: rmilne/pynwn
    def load(self):
        """Loads the source of the associated gff file."""

        # attempt to open the gff file and load its header
        self.source = io.BytesIO(self.co.get())

        header = struct.unpack(
            self.HeaderPattern,
            self.source.read(struct.calcsize(self.HeaderPattern)))

        if (header[0].decode(get_encoding()).rstrip() == self.filetype
                and header[1].decode(get_encoding()) == self.Version):
            self.structoffset, self.structcount = header[2:4]
            self.fieldoffset, self.fieldcount = header[4:6]
            self.labeloffset, self.labelcount = header[6:8]
            self.dataoffset, self.datasize = header[8:10]
            self.indiceoffset, self.indicesize = header[10:12]
            self.listoffset, self.listsize = header[12:14]
        else:
            if header[1].decode(get_encoding()) != self.Version:
                raise ValueError(
                    "File: %s: gff file version '%s' does not match current valid version '%s'"
                    % (self.co.get_filename(), header[1], self.Version))
            else:
                raise ValueError(
                    "File: %s: gff file type '%s' does not match specified file type '%s'"
                    % (self.co.get_filename(), header[0].rstrip(),
                       self.filetype))

        # position the source file at the struct array and prepare structs list
        self.source.seek(self.structoffset)
        self.structs = []

        # parse the gff struct array
        size = struct.calcsize(self.StructPattern)
        rd = self.source.read(self.structcount * size)

        for chunk in chunks(rd, size):
            type, offset, count = struct.unpack(self.StructPattern, chunk)
            if offset == 0xffffffff:
                self.structs.append([type, -1])
            elif count == 1:
                self.structs.append([type, offset])
            else:
                pattern = "%dI" % count
                position = self.source.tell()
                self.source.seek(self.indiceoffset + offset)
                data = self.source.read(struct.calcsize(pattern))
                self.source.seek(position)

                indexes = struct.unpack(pattern, data)
                self.structs.append([type, list(indexes)])

        # position the source file at the label array and prepare labels list
        self.source.seek(self.labeloffset)
        self.labels = []

        # parse the gff label array
        size = struct.calcsize(self.LabelPattern)
        rd = self.source.read(size * self.labelcount)
        for chunk in chunks(rd, size):
            label = struct.unpack(self.LabelPattern,
                                  chunk)[0].decode(get_encoding())
            self.labels.append(label.rstrip('\x00'))

        # position the source file at the field array and prepare fields list
        self.source.seek(self.fieldoffset)
        self.fields = []

        # parse the gff field array
        size = struct.calcsize(self.FieldPattern)
        dwordsize = struct.calcsize(self.DwordPattern)
        for index in range(0, self.fieldcount):
            type, label = struct.unpack(self.FieldPattern,
                                        self.source.read(size))
            Type = self.Classes[type]

            position = None
            # False indicates there is no offset
            if not Type.at_offset is False:
                offset = struct.unpack('I', self.source.read(4))[0]
                position = self.source.tell()

            if Type.at_offset == 'data':
                offset += self.dataoffset
            elif Type.at_offset == 'list':
                offset += self.listoffset

            if position:
                data = Type.unpack(self.source, offset)
                self.source.seek(position)
            else:
                data = Type.unpack(self.source)

            type_name = Type.type
            label_name = self.labels[label]
            self.fields.append([type_name, label_name, data])

        # close the source file and build the gff structure, then indicate
        # status
        self.source.close()
        self._structure = self.build_struct(0)
        return True
Ejemplo n.º 15
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("LLhh", 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 = res_id & 0xfffff

                #print res_id, resref, res_type, bif_idx
                if not res_id 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)
Ejemplo n.º 16
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)
Ejemplo n.º 17
0
Archivo: erf.py Proyecto: rmilne/pynwn
    def from_file(fname):
        """Create an Erf from a file handle.

        :param io: A file handle.

        """
        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 not ftype 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))
                    break

                if len(lstr) < 8:
                    print("locstr table: not enough entries (expected: %d, got: %d)" % (lstr_count, ls) + " partial data: " + lstr)
                    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 as e:
                    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 as e:
                    print("WARNING: Attempt to index invalid content object in '%s' at offset %X" % (fname, offset))

        return new_erf