Completed
Push — master ( 152b55...0bdad9 )
by John
01:22
created

make_offset()   F

Complexity

Conditions 15

Size

Total Lines 81

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 15
dl 0
loc 81
rs 2.0551
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like make_offset() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
#!/usr/bin/env python3
2
"""This module is the Python-ized implementation of cap.exe"""
3
4
import os  # path work
5
import binascii  # to hex and back again
6
import glob  # filename matching
7
import base64  # storage
8
from bbarchivist import bbconstants  # versions/constants
9
from bbarchivist import utilities  # finding cap
10
11
__author__ = "Thurask"
12
__license__ = "WTFPL v2"
13
__copyright__ = "Copyright 2015-2016 Thurask"
14
15
16
def ghetto_convert(intsize):
17
    """
18
    Convert from decimal integer to little endian
19
    hexadecimal string, padded to 16 characters with zeros.
20
21
    :param intsize: Integer you wish to convert.
22
    :type intsize: int
23
    """
24
    if not isinstance(intsize, int):
25
        intsize = int(intsize)
26
    hexsize = format(intsize, '08x')  # '00AABBCC'
27
    newlist = [hexsize[i:i + 2] for i in range(0, len(hexsize), 2)]  # ['00', 'AA','BB','CC']
28
    newlist.reverse()
29
    ghetto_hex = "".join(newlist)  # 'CCBBAA'
30
    ghetto_hex = ghetto_hex.rjust(16, '0')
31
    if len(ghetto_hex) == 16:
32
        return binascii.unhexlify(bytes(ghetto_hex.upper(), 'ascii'))
33
34
35
def make_offset(files, folder=None):
36
    """
37
    Create magic offset file for use in autoloader creation.
38
    Cap.exe MUST match separator version.
39
    Version defined in :data:`bbarchivist.bbconstants.CAP.version`.
40
41
    :param files: List of 1-6 signed files.
42
    :type files: list(str)
43
44
    :param folder: Working folder. Optional. Default is local.
45
    :type folder: str
46
    """
47
    if folder is None:
48
        folder = os.getcwd()
49
    capfile = utilities.grab_cap()
50
    filelist = [file for file in files if file]
51
    filecount = len(filelist)
52
    fcount = b'0' + bytes(str(filecount), 'ascii')
53
    # immutable things
54
    scaff = b'at9dFE5LTEdOT0hHR0lTCxcKDR4MFFMtPiU6LT0zPjs6Ui88U05GTVFOSUdRTlFOT3BwcJzVxZec1cWXnNXFlw=='
55
    separator = base64.b64decode(scaff)
56
    password = binascii.unhexlify(b'0' * 160)
57
    pad = b'\x00'  # 1x, 2x or 8x
58
    filepad = binascii.unhexlify(fcount)  # 01-06
59
    trailers = binascii.unhexlify(b'00' * (7 - filecount))  # 00, 1-6x
60
    capsize = os.path.getsize(capfile)
61
    if not filecount:  # we need at least one file
62
        raise SystemExit
63
    first = str(glob.glob(filelist[0])[0])
64
    firstsize = os.path.getsize(first)  # required
65
    if filecount >= 2:
66
        second = str(glob.glob(filelist[1])[0])
67
        secondsize = os.path.getsize(second)
68
    if filecount >= 3:
69
        third = str(glob.glob(filelist[2])[0])
70
        thirdsize = os.path.getsize(third)
71
    if filecount >= 4:
72
        fourth = str(glob.glob(filelist[3])[0])
73
        fourthsize = os.path.getsize(fourth)
74
    if filecount >= 5:
75
        fifth = str(glob.glob(filelist[4])[0])
76
        fifthsize = os.path.getsize(fifth)
77
    # start of first file; length of cap + length of offset
78
    beginlength = len(separator) + len(password) + 64
79
    firstoffset = beginlength + capsize
80
    firststart = ghetto_convert(firstoffset)
81
    secondstart = thirdstart = fourthstart = fifthstart = sixthstart = pad * 8
82
    if filecount >= 2:
83
        secondoffset = firstoffset + firstsize  # start of second file
84
        secondstart = ghetto_convert(secondoffset)
85
    if filecount >= 3:
86
        thirdoffset = secondoffset + secondsize  # start of third file
87
        thirdstart = ghetto_convert(thirdoffset)
88
    if filecount >= 4:
89
        fourthoffset = thirdoffset + thirdsize  # start of fourth file
90
        fourthstart = ghetto_convert(fourthoffset)
91
    if filecount >= 5:
92
        fifthoffset = fourthoffset + fourthsize  # start of fifth file
93
        fifthstart = ghetto_convert(fifthoffset)
94
    if filecount == 6:
95
        sixthoffset = fifthoffset + fifthsize  # start of sixth file
96
        sixthstart = ghetto_convert(sixthoffset)
97
    makeuplen = 64 - 6 * len(pad * 8) - 2 * len(pad * 2) - 2 * \
98
        len(pad) - len(trailers) - len(filepad)
99
    makeup = b'\x00' * makeuplen  # pad to match offset begin
100
    with open(os.path.join(folder, "offset.hex"), "wb") as file:
101
        file.write(separator)
102
        file.write(password)
103
        file.write(filepad)
104
        file.write(pad * 2)
105
        file.write(pad)
106
        file.write(firststart)
107
        file.write(secondstart)
108
        file.write(thirdstart)
109
        file.write(fourthstart)
110
        file.write(fifthstart)
111
        file.write(sixthstart)
112
        file.write(pad)
113
        file.write(pad * 2)
114
        file.write(trailers)
115
        file.write(makeup)
116
117
118
def write_4k(infile, outfile):
119
    """
120
    Write a file from another file, 4k bytes at a time.
121
122
    :param infile: Filename. Input file.
123
    :type infile: str
124
125
    :param outfile: Open (!!!) file handle. Output file.
126
    :type outfile: str
127
    """
128
    with open(os.path.abspath(infile), "rb") as afile:
129
        print("WRITING FILE...\n{0}".format(os.path.basename(infile)))
130
        while True:
131
            chunk = afile.read(4096)  # 4k chunks
132
            if not chunk:
133
                break
134
            outfile.write(chunk)
135
136
137
def make_autoloader(filename, files, folder=None):
138
    """
139
    Write cap.exe, magic offset, signed files to a .exe file.
140
    :func:`make_offset` is used to create the offset.
141
142
    :param filename: Name of autoloader.
143
    :type filename: str
144
145
    :param files: List of 1-6 signed files.
146
    :type files: list(str)
147
148
    :param folder: Working folder. Optional. Default is local.
149
    :type folder: str
150
    """
151
    if folder is None:
152
        folder = os.getcwd()
153
    make_offset(files, folder)
154
    filelist = [os.path.abspath(file) for file in files if file]
155
    print("CREATING: {0}".format(filename))
156
    try:
157
        with open(os.path.join(os.path.abspath(folder), filename), "wb") as autoloader:
158
            with open(os.path.normpath(utilities.grab_cap()), "rb") as capfile:
159
                print("WRITING CAP VERSION {0}...".format(bbconstants.CAP.version))
160
                while True:
161
                    chunk = capfile.read(4096)  # 4k chunks
162
                    if not chunk:
163
                        break
164
                    autoloader.write(chunk)
165
            with open(os.path.join(folder, "offset.hex"), "rb") as offset:
166
                print("WRITING MAGIC OFFSET...")
167
                autoloader.write(offset.read())
168
            for file in filelist:
169
                write_4k(file, autoloader)
170
    except IOError as exc:
171
        print("Operation failed: {0}".format(exc.strerror))
172
    else:
173
        print("{0} FINISHED!".format(filename))
174
    os.remove(os.path.join(folder, "offset.hex"))
175