713 lines
23 KiB
Python
713 lines
23 KiB
Python
# Wizard Kit PE: Functions - Common
|
|
|
|
import os
|
|
import psutil
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
import traceback
|
|
import winreg
|
|
|
|
from subprocess import CalledProcessError
|
|
|
|
from settings.main import *
|
|
from settings.tools import *
|
|
|
|
# Global variables
|
|
global_vars = {}
|
|
|
|
# STATIC VARIABLES
|
|
COLORS = {
|
|
'CLEAR': '\033[0m',
|
|
'RED': '\033[31m',
|
|
'GREEN': '\033[32m',
|
|
'YELLOW': '\033[33m',
|
|
'BLUE': '\033[34m'
|
|
}
|
|
HKU = winreg.HKEY_USERS
|
|
HKCU = winreg.HKEY_CURRENT_USER
|
|
HKLM = winreg.HKEY_LOCAL_MACHINE
|
|
|
|
# Error Classes
|
|
class BIOSKeyNotFoundError(Exception):
|
|
pass
|
|
|
|
class BinNotFoundError(Exception):
|
|
pass
|
|
|
|
class GenericAbort(Exception):
|
|
pass
|
|
|
|
class GenericError(Exception):
|
|
pass
|
|
|
|
class GenericRepair(Exception):
|
|
pass
|
|
|
|
class MultipleInstallationsError(Exception):
|
|
pass
|
|
|
|
class NotInstalledError(Exception):
|
|
pass
|
|
|
|
class NoProfilesError(Exception):
|
|
pass
|
|
|
|
class PathNotFoundError(Exception):
|
|
pass
|
|
|
|
class UnsupportedOSError(Exception):
|
|
pass
|
|
|
|
# General functions
|
|
def abort():
|
|
"""Abort script."""
|
|
print_warning('Aborted.')
|
|
sleep(5)
|
|
exit_script()
|
|
|
|
def ask(prompt='Kotaero!'):
|
|
"""Prompt the user with a Y/N question, log answer, and return a bool."""
|
|
answer = None
|
|
prompt = '{} [Y/N]: '.format(prompt)
|
|
while answer is None:
|
|
tmp = input(prompt)
|
|
if re.search(r'^y(es|)$', tmp, re.IGNORECASE):
|
|
answer = True
|
|
elif re.search(r'^n(o|ope|)$', tmp, re.IGNORECASE):
|
|
answer = False
|
|
message = '{prompt}{answer_text}'.format(
|
|
prompt = prompt,
|
|
answer_text = 'Yes' if answer else 'No')
|
|
print_log(message=message)
|
|
return answer
|
|
|
|
def clear_screen():
|
|
"""Simple wrapper for cls."""
|
|
os.system('cls')
|
|
|
|
def convert_to_bytes(size):
|
|
"""Convert human-readable size str to bytes and return an int."""
|
|
size = str(size)
|
|
tmp = re.search(r'(\d+)\s+([KMGT]B)', size.upper())
|
|
if tmp:
|
|
size = int(tmp.group(1))
|
|
units = tmp.group(2)
|
|
if units == 'TB':
|
|
size *= 1099511627776
|
|
elif units == 'GB':
|
|
size *= 1073741824
|
|
elif units == 'MB':
|
|
size *= 1048576
|
|
elif units == 'KB':
|
|
size *= 1024
|
|
else:
|
|
return -1
|
|
|
|
return size
|
|
|
|
def exit_script(return_value=0):
|
|
"""Exits the script after some cleanup and opens the log (if set)."""
|
|
# Remove dirs (if empty)
|
|
for dir in ['BackupDir', 'LogDir', 'TmpDir']:
|
|
try:
|
|
dir = global_vars[dir]
|
|
os.rmdir(dir)
|
|
except Exception:
|
|
pass
|
|
|
|
# Open Log (if it exists)
|
|
log = global_vars.get('LogFile', '')
|
|
if log and os.path.exists(log):
|
|
try:
|
|
extract_item('NotepadPlusPlus', silent=True)
|
|
popen_program(
|
|
[global_vars['Tools']['NotepadPlusPlus'],
|
|
global_vars['LogFile']])
|
|
except Exception:
|
|
print_error('ERROR: Failed to extract Notepad++ and open log.')
|
|
pause('Press Enter to exit...')
|
|
|
|
# Kill Caffeine if still running
|
|
kill_process('caffeine.exe')
|
|
|
|
# Exit
|
|
sys.exit(return_value)
|
|
|
|
def extract_item(item, filter='', silent=False):
|
|
"""Extract item from .cbin into .bin."""
|
|
cmd = [
|
|
global_vars['Tools']['SevenZip'], 'x', '-aos', '-bso0', '-bse0',
|
|
'-p{ArchivePassword}'.format(**global_vars),
|
|
r'-o{BinDir}\{item}'.format(item=item, **global_vars),
|
|
r'{CBinDir}\{item}.7z'.format(item=item, **global_vars),
|
|
filter]
|
|
if not silent:
|
|
print_standard('Extracting "{item}"...'.format(item=item))
|
|
try:
|
|
run_program(cmd)
|
|
except subprocess.CalledProcessError:
|
|
if not silent:
|
|
print_warning('WARNING: Errors encountered while exctracting data')
|
|
|
|
def get_ticket_number():
|
|
"""Get TicketNumber from user, save in LogDir, and return as str."""
|
|
ticket_number = None
|
|
while ticket_number is None:
|
|
_input = input('Enter ticket number: ')
|
|
if re.match(r'^([0-9]+([-_]?\w+|))$', _input):
|
|
ticket_number = _input
|
|
with open(r'{LogDir}\TicketNumber'.format(**global_vars), 'w') as f:
|
|
f.write(ticket_number)
|
|
return ticket_number
|
|
|
|
def human_readable_size(size, decimals=0):
|
|
"""Convert size in bytes to a human-readable format and return a str."""
|
|
# Prep string formatting
|
|
width = 3+decimals
|
|
if decimals > 0:
|
|
width += 1
|
|
|
|
# Convert size to int
|
|
try:
|
|
size = int(size)
|
|
except ValueError:
|
|
size = convert_to_bytes(size)
|
|
|
|
# Verify we have a valid size
|
|
if size < 0:
|
|
return '{size:>{width}} b'.format(size='???', width=width)
|
|
|
|
# Convert to sensible units
|
|
if size >= 1099511627776:
|
|
size /= 1099511627776
|
|
units = 'Tb'
|
|
elif size >= 1073741824:
|
|
size /= 1073741824
|
|
units = 'Gb'
|
|
elif size >= 1048576:
|
|
size /= 1048576
|
|
units = 'Mb'
|
|
elif size >= 1024:
|
|
size /= 1024
|
|
units = 'Kb'
|
|
else:
|
|
units = ' b'
|
|
|
|
# Return
|
|
return '{size:>{width}.{decimals}f} {units}'.format(
|
|
size=size, width=width, decimals=decimals, units=units)
|
|
|
|
def kill_process(name):
|
|
"""Kill any running caffeine.exe processes."""
|
|
for proc in psutil.process_iter():
|
|
if proc.name() == name:
|
|
proc.kill()
|
|
|
|
def major_exception():
|
|
"""Display traceback and exit"""
|
|
print_error('Major exception')
|
|
print_warning(SUPPORT_MESSAGE)
|
|
print(traceback.format_exc())
|
|
print_log(traceback.format_exc())
|
|
sleep(30)
|
|
pause('Press Enter to exit...')
|
|
exit_script(1)
|
|
|
|
def menu_select(title='~ Untitled Menu ~',
|
|
prompt='Please make a selection', secret_exit=False,
|
|
main_entries=[], action_entries=[], disabled_label='DISABLED'):
|
|
"""Display options in a menu and return selected option as a str."""
|
|
# Bail early
|
|
if not main_entries and not action_entries:
|
|
raise Exception("MenuError: No items given")
|
|
|
|
# Set title
|
|
if 'Title' in global_vars:
|
|
title = '{}\n\n{}'.format(global_vars['Title'], title)
|
|
|
|
# Build menu
|
|
menu_splash = '{}\n\n'.format(title)
|
|
width = len(str(len(main_entries)))
|
|
valid_answers = []
|
|
if (secret_exit):
|
|
valid_answers.append('Q')
|
|
|
|
# Add main entries
|
|
for i in range(len(main_entries)):
|
|
entry = main_entries[i]
|
|
# Add Spacer
|
|
if ('CRLF' in entry):
|
|
menu_splash += '\n'
|
|
entry_str = '{number:>{width}}: {name}'.format(
|
|
number = i+1,
|
|
width = width,
|
|
name = entry.get('Display Name', entry['Name']))
|
|
if entry.get('Disabled', False):
|
|
entry_str = '{YELLOW}{entry_str} ({disabled}){CLEAR}'.format(
|
|
entry_str = entry_str,
|
|
disabled = disabled_label,
|
|
**COLORS)
|
|
else:
|
|
valid_answers.append(str(i+1))
|
|
menu_splash += '{}\n'.format(entry_str)
|
|
menu_splash += '\n'
|
|
|
|
# Add action entries
|
|
for entry in action_entries:
|
|
# Add Spacer
|
|
if ('CRLF' in entry):
|
|
menu_splash += '\n'
|
|
valid_answers.append(entry['Letter'])
|
|
menu_splash += '{letter:>{width}}: {name}\n'.format(
|
|
letter = entry['Letter'].upper(),
|
|
width = len(str(len(action_entries))),
|
|
name = entry['Name'])
|
|
menu_splash += '\n'
|
|
|
|
answer = ''
|
|
|
|
while (answer.upper() not in valid_answers):
|
|
os.system('cls')
|
|
print(menu_splash)
|
|
answer = input('{}: '.format(prompt))
|
|
|
|
return answer.upper()
|
|
|
|
def non_clobber_rename(full_path):
|
|
"""Append suffix to path, if necessary, to avoid clobbering path"""
|
|
new_path = full_path
|
|
_i = 1;
|
|
while os.path.exists(new_path):
|
|
new_path = '{path}_{i}'.format(i=_i, path=full_path)
|
|
_i += 1
|
|
|
|
return new_path
|
|
|
|
def pause(prompt='Press Enter to continue... '):
|
|
"""Simple pause implementation."""
|
|
input(prompt)
|
|
|
|
def ping(addr='google.com'):
|
|
"""Attempt to ping addr."""
|
|
cmd = ['ping', '-n', '2', addr]
|
|
run_program(cmd)
|
|
|
|
def popen_program(cmd, pipe=False, minimized=False, shell=False, **kwargs):
|
|
"""Run program and return a subprocess.Popen object."""
|
|
startupinfo=None
|
|
if minimized:
|
|
startupinfo = subprocess.STARTUPINFO()
|
|
startupinfo.dwFlags = subprocess.STARTF_USESHOWWINDOW
|
|
startupinfo.wShowWindow = 6
|
|
|
|
if pipe:
|
|
popen_obj = subprocess.Popen(cmd, shell=shell, startupinfo=startupinfo,
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
else:
|
|
popen_obj = subprocess.Popen(cmd, shell=shell, startupinfo=startupinfo)
|
|
|
|
return popen_obj
|
|
|
|
def print_error(*args, **kwargs):
|
|
"""Prints message to screen in RED."""
|
|
print_standard(*args, color=COLORS['RED'], **kwargs)
|
|
|
|
def print_info(*args, **kwargs):
|
|
"""Prints message to screen in BLUE."""
|
|
print_standard(*args, color=COLORS['BLUE'], **kwargs)
|
|
|
|
def print_standard(message='Generic info',
|
|
color=None, end='\n', timestamp=True, **kwargs):
|
|
"""Prints message to screen and log (if set)."""
|
|
display_message = message
|
|
if color:
|
|
display_message = color + message + COLORS['CLEAR']
|
|
# **COLORS is used below to support non-"standard" color printing
|
|
print(display_message.format(**COLORS), end=end, **kwargs)
|
|
print_log(message, end, timestamp)
|
|
|
|
def print_success(*args, **kwargs):
|
|
"""Prints message to screen in GREEN."""
|
|
print_standard(*args, color=COLORS['GREEN'], **kwargs)
|
|
|
|
def print_warning(*args, **kwargs):
|
|
"""Prints message to screen in YELLOW."""
|
|
print_standard(*args, color=COLORS['YELLOW'], **kwargs)
|
|
|
|
def print_log(message='', end='\n', timestamp=True):
|
|
time_str = time.strftime("%Y-%m-%d %H%M%z: ") if timestamp else ''
|
|
if 'LogFile' in global_vars and global_vars['LogFile'] is not None:
|
|
with open(global_vars['LogFile'], 'a') as f:
|
|
for line in message.splitlines():
|
|
f.write('{timestamp}{line}{end}'.format(
|
|
timestamp = time_str,
|
|
line = line,
|
|
end = end))
|
|
|
|
def run_program(cmd, args=[], check=True, pipe=True, shell=False):
|
|
"""Run program and return a subprocess.CompletedProcess object."""
|
|
if args:
|
|
# Deprecated so let's raise an exception to find & fix all occurances
|
|
print_error('ERROR: Using args is no longer supported.')
|
|
raise Exception
|
|
cmd = [c for c in cmd if c]
|
|
if shell:
|
|
cmd = ' '.join(cmd)
|
|
|
|
if pipe:
|
|
process_return = subprocess.run(cmd, check=check, shell=shell,
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
else:
|
|
process_return = subprocess.run(cmd, check=check, shell=shell)
|
|
|
|
return process_return
|
|
|
|
def set_title(title='~Some Title~'):
|
|
"""Set title.
|
|
|
|
Used for window title and menu titles."""
|
|
global_vars['Title'] = title
|
|
os.system('title {}'.format(title))
|
|
|
|
def show_data(message='~Some message~', data='~Some data~', indent=8, width=32,
|
|
info=False, warning=False, error=False):
|
|
"""Display info with formatting."""
|
|
message = '{indent}{message:<{width}}{data}'.format(
|
|
indent=' '*indent, width=width, message=message, data=data)
|
|
if error:
|
|
print_error(message)
|
|
elif warning:
|
|
print_warning(message)
|
|
elif info:
|
|
print_info(message)
|
|
else:
|
|
print_standard(message)
|
|
|
|
def show_info(message='~Some message~', info='~Some info~', indent=8, width=32):
|
|
show_data(message=message, data=info, indent=indent, width=width)
|
|
|
|
def sleep(seconds=2):
|
|
"""Wait for a while."""
|
|
time.sleep(seconds)
|
|
|
|
def stay_awake():
|
|
"""Prevent the system from sleeping or hibernating."""
|
|
# Bail if caffeine is already running
|
|
for proc in psutil.process_iter():
|
|
if proc.name() == 'caffeine.exe':
|
|
return
|
|
# Extract and run
|
|
extract_item('Caffeine', silent=True)
|
|
try:
|
|
popen_program(global_vars['Tools']['Caffeine'])
|
|
except Exception:
|
|
print_error('ERROR: No caffeine available.')
|
|
print_warning('Please set the power setting to High Performance.')
|
|
|
|
def get_exception(s):
|
|
"""Get exception by name, returns Exception object."""
|
|
return getattr(sys.modules[__name__], s)
|
|
|
|
def try_and_print(message='Trying...',
|
|
function=None, cs='CS', ns='NS', other_results={},
|
|
catch_all=True, print_return=False, silent_function=True,
|
|
indent=8, width=32, *args, **kwargs):
|
|
"""Run function, print if successful or not, and return dict.
|
|
|
|
other_results is in the form of
|
|
{
|
|
'Warning': {'ExceptionClassName': 'Result Message'},
|
|
'Error': {'ExceptionClassName': 'Result Message'}
|
|
}
|
|
The the ExceptionClassNames will be excepted conditions
|
|
and the result string will be printed in the correct color.
|
|
catch_all=False will result in unspecified exceptions being re-raised."""
|
|
err = None
|
|
out = None
|
|
w_exceptions = other_results.get('Warning', {}).keys()
|
|
w_exceptions = tuple(get_exception(e) for e in w_exceptions)
|
|
e_exceptions = other_results.get('Error', {}).keys()
|
|
e_exceptions = tuple(get_exception(e) for e in e_exceptions)
|
|
w_results = other_results.get('Warning', {})
|
|
e_results = other_results.get('Error', {})
|
|
|
|
# Run function and catch errors
|
|
print_standard('{indent}{message:<{width}}'.format(
|
|
indent=' '*indent, message=message, width=width), end='', flush=True)
|
|
try:
|
|
out = function(*args, **kwargs)
|
|
if print_return:
|
|
print_standard(out[0], timestamp=False)
|
|
for item in out[1:]:
|
|
print_standard('{indent}{item}'.format(
|
|
indent=' '*(indent+width), item=item))
|
|
elif silent_function:
|
|
print_success(cs, timestamp=False)
|
|
except w_exceptions as e:
|
|
_result = w_results.get(e.__class__.__name__, 'Warning')
|
|
print_warning(_result, timestamp=False)
|
|
err = e
|
|
except e_exceptions as e:
|
|
_result = e_results.get(e.__class__.__name__, 'Error')
|
|
print_error(_result, timestamp=False)
|
|
err = e
|
|
except Exception:
|
|
print_error(ns, timestamp=False)
|
|
err = traceback.format_exc()
|
|
|
|
# Return or raise?
|
|
if err and not catch_all:
|
|
raise
|
|
else:
|
|
return {'CS': not bool(err), 'Error': err, 'Out': out}
|
|
|
|
def upload_data(path, file):
|
|
"""Add CLIENT_INFO_SERVER to authorized connections and upload file."""
|
|
if not ENABLED_UPLOAD_DATA:
|
|
raise GenericError('Feature disabled.')
|
|
|
|
extract_item('PuTTY', filter='wizkit.ppk psftp.exe', silent=True)
|
|
|
|
# Authorize connection to the server
|
|
winreg.CreateKey(HKCU, r'Software\SimonTatham\PuTTY\SshHostKeys')
|
|
with winreg.OpenKey(HKCU, r'Software\SimonTatham\PuTTY\SshHostKeys',
|
|
access=winreg.KEY_WRITE) as key:
|
|
winreg.SetValueEx(key,
|
|
'rsa2@22:{IP}'.format(**CLIENT_INFO_SERVER), 0,
|
|
winreg.REG_SZ, CLIENT_INFO_SERVER['RegEntry'])
|
|
|
|
# Write batch file
|
|
with open(r'{TmpDir}\psftp.batch'.format(**global_vars),
|
|
'w', encoding='ascii') as f:
|
|
f.write('lcd "{path}"\n'.format(path=path))
|
|
f.write('cd "{Share}"\n'.format(**CLIENT_INFO_SERVER))
|
|
f.write('mkdir {TicketNumber}\n'.format(**global_vars))
|
|
f.write('cd {TicketNumber}\n'.format(**global_vars))
|
|
f.write('put "{file}"\n'.format(file=file))
|
|
|
|
# Upload Info
|
|
cmd = [
|
|
global_vars['Tools']['PuTTY-PSFTP'],
|
|
'-noagent',
|
|
'-i', r'{BinDir}\PuTTY\wizkit.ppk'.format(**global_vars),
|
|
'{User}@{IP}'.format(**CLIENT_INFO_SERVER),
|
|
'-b', r'{TmpDir}\psftp.batch'.format(**global_vars)]
|
|
run_program(cmd)
|
|
|
|
def upload_info():
|
|
"""Upload compressed Info file to the NAS as set in settings.main.py."""
|
|
if not ENABLED_UPLOAD_DATA:
|
|
raise GenericError('Feature disabled.')
|
|
|
|
path = '{ClientDir}'.format(**global_vars)
|
|
file = 'Info_{Date-Time}.7z'.format(**global_vars)
|
|
upload_data(path, file)
|
|
|
|
def compress_info():
|
|
"""Compress ClientDir info folders with 7-Zip for upload_info()."""
|
|
path = '{ClientDir}'.format(**global_vars)
|
|
file = 'Info_{Date-Time}.7z'.format(**global_vars)
|
|
_cmd = [
|
|
global_vars['Tools']['SevenZip'],
|
|
'a', '-t7z', '-mx=9', '-bso0', '-bse0',
|
|
r'{}\{}'.format(path, file),
|
|
r'{ClientDir}\Info'.format(**global_vars)]
|
|
run_program(_cmd)
|
|
|
|
def wait_for_process(name, poll_rate=3):
|
|
"""Wait for process by name."""
|
|
running = True
|
|
while running:
|
|
sleep(poll_rate)
|
|
running = False
|
|
for proc in psutil.process_iter():
|
|
if re.search(r'^{}'.format(name), proc.name(), re.IGNORECASE):
|
|
running = True
|
|
sleep(1)
|
|
|
|
# global_vars functions
|
|
def init_global_vars():
|
|
"""Sets global variables based on system info."""
|
|
print_info('Initializing')
|
|
os.system('title Wizard Kit')
|
|
init_functions = [
|
|
['Checking .bin...', find_bin],
|
|
['Checking environment...', set_common_vars],
|
|
['Checking OS...', check_os],
|
|
['Checking tools...', check_tools],
|
|
['Creating folders...', make_tmp_dirs],
|
|
['Clearing collisions...', clean_env_vars],
|
|
]
|
|
try:
|
|
for f in init_functions:
|
|
try_and_print(
|
|
message=f[0], function=f[1],
|
|
cs='Done', ns='Error', catch_all=False)
|
|
except:
|
|
major_exception()
|
|
|
|
def check_os():
|
|
"""Set OS specific variables."""
|
|
tmp = {}
|
|
|
|
# Query registry
|
|
_reg_path = winreg.OpenKey(
|
|
HKLM, r'SOFTWARE\Microsoft\Windows NT\CurrentVersion')
|
|
for key in ['CSDVersion', 'CurrentBuild', 'CurrentBuildNumber',
|
|
'CurrentVersion', 'ProductName']:
|
|
try:
|
|
tmp[key] = winreg.QueryValueEx(_reg_path, key)[0]
|
|
if key in ['CurrentBuild', 'CurrentBuildNumber']:
|
|
tmp[key] = int(tmp[key])
|
|
except ValueError:
|
|
# Couldn't convert Build to int so this should be interesting...
|
|
tmp[key] = 0
|
|
except Exception:
|
|
tmp[key] = 'Unknown'
|
|
|
|
# Determine OS bit depth
|
|
tmp['Arch'] = 32
|
|
if 'PROGRAMFILES(X86)' in global_vars['Env']:
|
|
tmp['Arch'] = 64
|
|
|
|
# Determine OS Name
|
|
tmp['Name'] = '{ProductName} {CSDVersion}'.format(**tmp)
|
|
if tmp['CurrentBuild'] == 9600:
|
|
tmp['Name'] += ' Update' # Win 8.1u
|
|
if tmp['CurrentBuild'] == 10240:
|
|
tmp['Name'] += ' Release 1507 "Threshold 1"'
|
|
if tmp['CurrentBuild'] == 10586:
|
|
tmp['Name'] += ' Release 1511 "Threshold 2"'
|
|
if tmp['CurrentBuild'] == 14393:
|
|
tmp['Name'] += ' Release 1607 "Redstone 1" / "Anniversary Update"'
|
|
if tmp['CurrentBuild'] == 15063:
|
|
tmp['Name'] += ' Release 1703 "Redstone 2" / "Creators Update"'
|
|
if tmp['CurrentBuild'] == 16299:
|
|
tmp['Name'] += ' Release 1709 "Redstone 3" / "Fall Creators Update"'
|
|
tmp['Name'] = tmp['Name'].replace('Service Pack ', 'SP')
|
|
tmp['Name'] = tmp['Name'].replace('Unknown Release', 'Release')
|
|
tmp['Name'] = re.sub(r'\s+', ' ', tmp['Name'])
|
|
|
|
# Determine OS version
|
|
name = '{Name} x{Arch}'.format(**tmp)
|
|
if tmp['CurrentVersion'] == '6.0':
|
|
tmp['Version'] = 'Vista'
|
|
name += ' (very outdated)'
|
|
elif tmp['CurrentVersion'] == '6.1':
|
|
tmp['Version'] = '7'
|
|
if tmp['CSDVersion'] == 'Service Pack 1':
|
|
name += ' (outdated)'
|
|
else:
|
|
name += ' (very outdated)'
|
|
elif tmp['CurrentVersion'] in ['6.2', '6.3']:
|
|
if int(tmp['CurrentBuildNumber']) <= 9600:
|
|
tmp['Version'] = '8'
|
|
elif int(tmp['CurrentBuildNumber']) >= 10240:
|
|
tmp['Version'] = '10'
|
|
if tmp['CurrentBuild'] in [9200, 10240, 10586]:
|
|
name += ' (very outdated)'
|
|
elif tmp['CurrentBuild'] in [9600, 14393, 15063]:
|
|
name += ' (outdated)'
|
|
elif tmp['CurrentBuild'] == 16299:
|
|
pass # Current Win10
|
|
else:
|
|
name += ' (unrecognized)'
|
|
tmp['DisplayName'] = name
|
|
|
|
# == vista ==
|
|
# 6.0.6000
|
|
# 6.0.6001
|
|
# 6.0.6002
|
|
# ==== 7 ====
|
|
# 6.1.7600
|
|
# 6.1.7601
|
|
# 6.1.7602
|
|
# ==== 8 ====
|
|
# 6.2.9200
|
|
# === 8.1 ===
|
|
# 6.3.9200
|
|
# === 8.1u ==
|
|
# 6.3.9600
|
|
# === 10 v1507 "Threshold 1" ==
|
|
# 6.3.10240
|
|
# === 10 v1511 "Threshold 2" ==
|
|
# 6.3.10586
|
|
# === 10 v1607 "Redstone 1" "Anniversary Update" ==
|
|
# 6.3.14393
|
|
# === 10 v1703 "Redstone 2" "Creators Update" ==
|
|
# 6.3.15063
|
|
# === 10 v1709 "Redstone 3" "Fall Creators Update" ==
|
|
# 6.3.16299
|
|
global_vars['OS'] = tmp
|
|
|
|
def check_tools():
|
|
"""Set tool variables based on OS bit-depth and tool availability."""
|
|
if global_vars['OS'].get('Arch', 32) == 64:
|
|
global_vars['Tools'] = {
|
|
k: v.get('64', v.get('32')) for (k, v) in TOOLS.items()}
|
|
else:
|
|
global_vars['Tools'] = {k: v.get('32') for (k, v) in TOOLS.items()}
|
|
|
|
# Fix paths
|
|
global_vars['Tools'] = {k: os.path.join(global_vars['BinDir'], v)
|
|
for (k, v) in global_vars['Tools'].items()}
|
|
|
|
def clean_env_vars():
|
|
"""Remove conflicting global_vars and env variables.
|
|
|
|
This fixes an issue where both global_vars and
|
|
global_vars['Env'] are expanded at the same time."""
|
|
for key in global_vars.keys():
|
|
global_vars['Env'].pop(key, None)
|
|
|
|
def find_bin():
|
|
"""Find .bin folder in the cwd or it's parents."""
|
|
wd = os.getcwd()
|
|
base = None
|
|
while base is None:
|
|
if os.path.exists('.bin'):
|
|
base = os.getcwd()
|
|
break
|
|
if re.fullmatch(r'\w:\\', os.getcwd()):
|
|
break
|
|
os.chdir('..')
|
|
os.chdir(wd)
|
|
if base is None:
|
|
raise BinNotFoundError
|
|
global_vars['BaseDir'] = base
|
|
|
|
def make_tmp_dirs():
|
|
"""Make temp directories."""
|
|
os.makedirs(global_vars['BackupDir'], exist_ok=True)
|
|
os.makedirs(global_vars['LogDir'], exist_ok=True)
|
|
os.makedirs(global_vars['TmpDir'], exist_ok=True)
|
|
|
|
def set_common_vars():
|
|
"""Set common variables."""
|
|
global_vars['Date'] = time.strftime("%Y-%m-%d")
|
|
global_vars['Date-Time'] = time.strftime("%Y-%m-%d_%H%M_%z")
|
|
global_vars['Env'] = os.environ.copy()
|
|
|
|
global_vars['ArchivePassword'] = ARCHIVE_PASSWORD
|
|
global_vars['BinDir'] = r'{BaseDir}\.bin'.format(
|
|
**global_vars)
|
|
global_vars['CBinDir'] = r'{BaseDir}\.cbin'.format(
|
|
**global_vars)
|
|
global_vars['ClientDir'] = r'{SYSTEMDRIVE}\{prefix}'.format(
|
|
prefix=KIT_NAME_SHORT, **global_vars['Env'])
|
|
global_vars['BackupDir'] = r'{ClientDir}\Backups\{Date}'.format(
|
|
**global_vars)
|
|
global_vars['LogDir'] = r'{ClientDir}\Info\{Date}'.format(
|
|
**global_vars)
|
|
global_vars['ProgBackupDir'] = r'{ClientDir}\Backups'.format(
|
|
**global_vars)
|
|
global_vars['QuarantineDir'] = r'{ClientDir}\Quarantine'.format(
|
|
**global_vars)
|
|
global_vars['TmpDir'] = r'{BinDir}\tmp'.format(
|
|
**global_vars)
|
|
|
|
if __name__ == '__main__':
|
|
print("This file is not meant to be called directly.")
|