Сборник гениальных идей, рецептов, мыслей и изобретений
RSS icon Home icon
  • Ethornell (BGI) package extractor

    Posted on May 15th, 2010 evilgenius No comments

    A Python script to extract files from packages used by the Ethornell BGI (Buriko) engine. Has been successfully used to extract some music in Ogg Vorbis format.

    import struct, os
    from optparse import OptionParser
    
    class Unpacker:
            files = []
    
            def __init__(self, options, file):
                    self.opts = options
                    self.file = file
                    self.readtoc(file)
                    if self.opts.verbose:
                            print "There are %d files in the package" % (len(self.files))
    
            def readfmt(self, format):
                    return struct.unpack(format, self.input.read(struct.calcsize(format)))
    
            def sizefmt(self, num):
                    for x in ['bytes','KB','MB','GB','TB']:
                            if num < 1024.0:
                                    return "%3.1f%s" % (num, x)
                            num /= 1024.0
    
            def entryfmt(self, entry):
                    name, offset, size, zero = entry;
                    assert(zero == 0)
                    return (name.rstrip("\x00"), offset, size)
    
            def entryrepr(self, entry):
                    (name, offset, size) = entry;
                    return "%s (%s)" % (name, self.sizefmt(size))
    
            def readtoc(self, file):
                    self.input = open(file, "rb")
                    header, count = self.readfmt("12sl")
                    assert(header.rstrip() == "PackFile")
    
                    for n in range(0, count):
                            entry = self.entryfmt(self.readfmt("16sllQ"))
                            self.files.append(entry)
                            if self.opts.list:
                                    print self.entryrepr(entry)
    
                    optsize = self.readfmt("l")[0]
                    # heuristics to detect presence of the data block
                    if (optsize != 64):
                            optdata = False
                            self.input.seek(-4, os.SEEK_CUR)
                    else:
                            optdata = True
                            self.input.seek(optsize - 4, os.SEEK_CUR)
    
            def extract(self, ext, dir):
                    self.data_offset = self.input.tell()
    
                    if os.path.isdir(dir) == False:
                            os.mkdir(dir)
    
                    if self.opts.verbose:
                            print "data offset: %d" % (self.data_offset)
    
                    for file in self.files:
                            (name, offset, size) = file
                            dest = "%s/%s.%s" % (dir, name, ext)
                            print "extracting: %s to %s" % (self.entryrepr(file), dest)
                            self.extractf(dest, offset, size)
    
            def extractf(self, dest, offset, size):
                    output = open(dest, "wb")
                    self.input.seek(self.data_offset)
                    self.input.seek(offset, 1)
                    output.write(self.input.read(size))
                    output.close()
    
    def main():
            usage = "usage: %prog [options] args"
            parser = OptionParser(usage)
            parser.add_option("-v", "--verbose", action="store_true", dest="verbose", help="show verbose output")
            parser.add_option("-l", "--list", action="store_true", dest="list", help="list package contents")
            parser.add_option("-e", "--extension", dest="extension", help="append EXTENSION to output files")
            parser.add_option("-x", "--extract", action="store_true", dest="extract", help="extract package contents")
            parser.add_option("-d", "--directory", dest="directory", help="extract into the specified directory")
    
            parser.set_defaults(directory=".", extension="dat")
            (options, args) = parser.parse_args()
            if len(args) != 1:
                    parser.error("incorrect number of arguments")
    
            unpacker = Unpacker(options, args[0])
    
            if options.extract:
                    unpacker.extract(options.extension, options.directory)
    
    if __name__ == "__main__":
            main()