#!/usr/bin/env python3
import os
import binascii
import glob
try:
from . import bbconstants # @UnusedImport
except SystemError:
import bbconstants # @UnresolvedImport @Reimport
[docs]def ghetto_convert(intsize):
"""
Convert from decimal integer to little endian
hexadecimal string, padded to 16 characters with zeros.
:param intsize: Integer you wish to convert.
:type intsize: integer
"""
hexsize = format(intsize, '08x') # '00AABBCC'
newlist = [hexsize[i:i + 2]
for i in range(0, len(hexsize), 2)] # ['00', 'AA','BB','CC']
while "00" in newlist:
newlist.remove("00") # extra padding
newlist.reverse()
ghetto_hex = "".join(newlist) # 'CCBBAA'
ghetto_hex = ghetto_hex.rjust(16, '0')
return binascii.unhexlify(bytes(ghetto_hex.upper(), 'ascii'))
[docs]def make_offset(cap, firstfile, secondfile="", thirdfile="",
fourthfile="", fifthfile="", sixthfile="", folder=os.getcwd()):
"""
Create magic offset file for use in autoloader creation.
Cap.exe MUST match separator version.
Version defined in :data:`bbarchivist.bbconstants._capversion`.
:param cap: Location of cap.exe file.
:type cap: str
:param firstfile: First signed file. Required.
:type firstfile: str
:param secondfile: Second signed file. Optional.
:type secondfile: str
:param thirdfile: Third signed file. Optional.
:type thirdfile: str
:param fourthfile: Fourth signed file. Optional.
:type fourthfile: str
:param fifthfile: Fifth signed file. Optional.
:type fifthfile: str
:param sixthfile: Sixth signed file. Optional.
:type sixthfile: str
:param folder: Working folder. Optional.
:type folder: str
"""
filecount = 0
filelist = [
firstfile,
secondfile,
thirdfile,
fourthfile,
fifthfile,
sixthfile]
for i in filelist:
if i:
filecount += 1
# immutable things
separator = binascii.unhexlify(
"""6ADF5D144E4B4D4E474F46464D4E532B170A0D1E0C14532B372A2D3E2C34522F3C534F514\
F514F514F534E464D514E4947514E51474F70709CD5C5979CD5C5979CD5C597""")
password = binascii.unhexlify("0" * 160)
singlepad = binascii.unhexlify("0" * 2)
doublepad = binascii.unhexlify("0" * 4)
signedpad = binascii.unhexlify("0" * 16)
filepad = binascii.unhexlify(
bytes(
str(filecount).rjust(
2,
'0'),
'ascii')) # between 01 and 06
trailermax = int(7 - int(filecount))
trailermax = trailermax * 2
trailer = "0" * trailermax # 00 repeated between 1 and 6 times
trailers = binascii.unhexlify(trailer)
capfile = str(glob.glob(cap)[0])
capsize = os.path.getsize(capfile) # size of cap.exe, in bytes
first = str(glob.glob(firstfile)[0])
firstsize = os.path.getsize(first) # required
if (filecount >= 2):
second = str(glob.glob(secondfile)[0])
secondsize = os.path.getsize(second)
if (filecount >= 3):
third = str(glob.glob(thirdfile)[0])
thirdsize = os.path.getsize(third)
if (filecount >= 4):
fourth = str(glob.glob(fourthfile)[0])
fourthsize = os.path.getsize(fourth)
if (filecount >= 5):
fifth = str(glob.glob(fifthfile)[0])
fifthsize = os.path.getsize(fifth)
# start of first file; length of cap + length of offset
firstoffset = len(separator) + len(password) + 64 + capsize
firststart = ghetto_convert(firstoffset)
if (filecount >= 2):
secondoffset = firstoffset + firstsize # start of second file
secondstart = ghetto_convert(secondoffset)
if (filecount >= 3):
thirdoffset = secondstart + secondsize # start of third file
thirdstart = ghetto_convert(thirdoffset)
if (filecount >= 4):
fourthoffset = thirdoffset + thirdsize # start of fourth file
fourthstart = ghetto_convert(fourthoffset)
if (filecount >= 5):
fifthoffset = fourthstart + fourthsize # start of fifth file
fifthstart = ghetto_convert(fifthoffset)
if (filecount == 6):
sixthoffset = fifthoffset + fifthsize # start of sixth file
sixthstart = ghetto_convert(sixthoffset)
with open(os.path.join(folder, "offset.hex"), "w+b") as file:
file.write(separator)
file.write(password)
file.write(filepad)
file.write(doublepad)
file.write(firststart)
file.write(singlepad)
if (filecount >= 2):
file.write(secondstart)
else:
file.write(signedpad)
file.write(singlepad)
if (filecount >= 3):
file.write(thirdstart)
else:
file.write(signedpad)
file.write(singlepad)
if (filecount >= 4):
file.write(fourthstart)
else:
file.write(signedpad)
file.write(singlepad)
if (filecount >= 5):
file.write(fifthstart)
else:
file.write(signedpad)
file.write(singlepad)
if (filecount == 6):
file.write(sixthstart)
else:
file.write(signedpad)
file.write(singlepad)
file.write(doublepad)
file.write(trailers)
[docs]def make_autoloader(filename, cap, firstfile, secondfile="", thirdfile="",
fourthfile="", fifthfile="", sixthfile="",
folder=os.getcwd()):
"""
Python implementation of cap.exe.
Writes cap.exe, magic offset, signed files to a .exe file.
:func:`make_offset` is used to create the offset.
:param filename: Name of autoloader.
:type filename: str
:param cap: Location of cap.exe file.
:type cap: str
:param firstfile: First signed file. Required.
:type firstfile: str
:param secondfile: Second signed file. Optional.
:type secondfile: str
:param thirdfile: Third signed file. Optional.
:type thirdfile: str
:param fourthfile: Fourth signed file. Optional.
:type fourthfile: str
:param fifthfile: Fifth signed file. Optional.
:type fifthfile: str
:param sixthfile: Sixth signed file. Optional.
:type sixthfile: str
:param folder: Working folder. Optional.
:type folder: str
"""
make_offset(
cap,
firstfile,
secondfile,
thirdfile,
fourthfile,
fifthfile,
sixthfile,
folder)
filecount = 0
filelist = [
firstfile,
secondfile,
thirdfile,
fourthfile,
fifthfile,
sixthfile]
for i in filelist:
if i:
filecount += 1
try:
with open(os.path.join(os.path.abspath(folder),
filename), "wb") as autoloader:
try:
with open(os.path.normpath(cap), "rb") as capfile:
print("WRITING CAP VERSION",
bbconstants._capversion + "...")
while True:
chunk = capfile.read(4096) # 4k chunks
if not chunk:
break
autoloader.write(chunk)
except IOError as e:
print("Operation failed:", e.strerror)
try:
with open(os.path.join(folder, "offset.hex"), "rb") as offset:
print("WRITING MAGIC OFFSET...")
autoloader.write(offset.read())
except IOError as e:
print("Operation failed:", e.strerror)
try:
with open(firstfile, "rb") as first:
print(
"WRITING SIGNED FILE #1...\n",
os.path.basename(firstfile))
while True:
chunk = first.read(4096) # 4k chunks
if not chunk:
break
autoloader.write(chunk)
except IOError as e:
print("Operation failed:", e.strerror)
if (filecount >= 2):
try:
print(
"WRITING SIGNED FILE #2...\n",
os.path.basename(secondfile))
with open(secondfile, "rb") as second:
while True:
chunk = second.read(4096) # 4k chunks
if not chunk:
break
autoloader.write(chunk)
except IOError as e:
print("Operation failed:", e.strerror)
if (filecount >= 3):
try:
print(
"WRITING SIGNED FILE #3...\n",
os.path.basename(thirdfile))
with open(thirdfile, "rb") as third:
while True:
chunk = third.read(4096) # 4k chunks
if not chunk:
break
autoloader.write(chunk)
except IOError as e:
print("Operation failed:", e.strerror)
if (filecount >= 4):
try:
print(
"WRITING SIGNED FILE #5...\n",
os.path.basename(fourthfile))
with open(fourthfile, "rb") as fourth:
while True:
chunk = fourth.read(4096) # 4k chunks
if not chunk:
break
autoloader.write(chunk)
except IOError as e:
print("Operation failed:", e.strerror)
if (filecount >= 5):
try:
print(
"WRITING SIGNED FILE #5...\n",
os.path.basename(fifthfile))
with open(fifthfile, "rb") as fifth:
while True:
chunk = fifth.read(4096) # 4k chunks
if not chunk:
break
autoloader.write(chunk)
except IOError as e:
print("Operation failed:", e.strerror)
if (filecount == 6):
try:
print(
"WRITING SIGNED FILE #6...\n",
os.path.basename(sixthfile))
with open(sixthfile, "rb") as sixth:
while True:
chunk = sixth.read(4096) # 4k chunks
if not chunk:
break
autoloader.write(chunk)
except IOError as e:
print("Operation failed:", e.strerror)
except IOError as e:
print("Operation failed:", e.strerror)
print(filename, "FINISHED!\n")
os.remove(os.path.join(folder, "offset.hex"))