186 lines
5.9 KiB

Command line module for making Team Fortress 2 macro scripts from
YAML source code.
__all__ = ['parseFile']
__author__ = 'Nicholas Hope <'
__date__ = '26 August 2022'
__version__ = '1.0'
__copyright__ = 'Copyright © 2022 Nicholas Hope. See LICENSE for details.'
# Standard libraries
from sys import stderr
from os import mkdir, sep as dirsep
from os.path import isdir, expanduser, normpath
import argparse
from warnings import warn
from tempfile import NamedTemporaryFile
import yaml
from platform import system as GetOSName, release as GetOSRelease
from winreg import HKEY_LOCAL_MACHINE, ConnectRegistry, OpenKey, EnumValue
except ModuleNotFoundError:
# Not running on windows
# Local libraries
import tfscript
from tfscript import verify, writing, makeCFG
args = {}
targetDir = ''
def parseFile(inputFile) -> (dict, dict):
'''Parse, verify, and do the conversion.'''
config = yaml.safe_load(inputFile)
# See
config, defaults = verify.verifyConfig(config)
if 'warnings' in config:
for cclass, messages in config.pop('warnings').items():
print(f'Warning in {cclass}:', file=stderr)
for msg in messages:
print(f' {msg}', file=stderr)
if 'errors' in config:
for cclass, messages in config['errors'].items():
print(f'Error in {cclass}:', file=stderr)
for msg in messages:
print(f' {msg}', file=stderr)
return None, None
return config, defaults
def parseConfig(config, defaults):
'''With validated data structure, write out all the files.'''
global args
global targetDir
if isdir(targetDir) == False:
if args.debug:
print( f'DEBUG: Created directory {targetDir}', file=stderr)
tempsAndReals = {}
if defaults is not None:
config.update({'default': defaults})
for class_ in config:
stringToWrite = makeCFG(
default=(class_ == 'default')
replaceDict = writing.writeOutput(stringToWrite, class_, args)
return tempsAndReals
def parseCLI():
# Handle command line
parser = argparse.ArgumentParser(
description='Parse YAML file and produce TF2 config script.'
parser.add_argument( '-d', '--debug', action='store_true',
help='Enable debugging messages.')
parser.add_argument( '-n', '--dry-run', action='store_true',
help='Parse input file, but don\'t write anything.')
parser.add_argument( '-f', '--force', action='store_true',
help='Force tfscript to continue until catastrophic failure')
parser.add_argument( '-D', '--directory', action='store', type=str,
help='Change output directory')
# warnings
parseWarnNames = [
'implicit-release', 'implicit-off',
'implicit-primary', 'implicit-secondary',
for warnName in parseWarnNames:
splitWarnName = ' '.join(warnName.split('-'))
parser.add_argument( '-W' + warnName, action='store_true',
help=f'Generate warning on {splitWarnName} creation')
# positional argument: first non-hyphenated argument is input file
parser.add_argument( 'infile', type=argparse.FileType('r'),
help='File containing YAML to convert.')
return parser
def getTargetDir(systemName):
if systemName == 'Darwin':
if float( '.'.join( GetOSRelease().split('.')[0:2] ) ) >= 10.15:
'As of macOS Catalina (v10.15), 32-bit applications'
+ ' like TF2 do not run. tfscript will run, but you can\'t run TF2'
+ ' on this system',
category=RuntimeWarning )
return expanduser('~/Library/Application Support/Steam')
if systemName == 'Windows':
# oh god why do we have to use the registry
accessReg = ConnectRegistry(None, HKEY_LOCAL_MACHINE)
accessKey = OpenKey(accessReg, 'SOFTWARE\\WOW6432Node\\Valve\\Steam')
keyNum = 0
while True:
accessSubkeyName, data, _ = EnumValue(accessKey, keyNum)
except EnvironmentError:
if accessSubkeyName == 'InstallPath':
return data
keyNum += 1
return None
if systemName == 'Linux':
homedir = expanduser('~')
for potentialdir in ['.steam/steam', '.local/share/Steam']:
fullTargetPath = normpath(f'{homedir}/{potentialdir}')
if isdir(fullTargetPath):
return fullTargetPath
return None
if systemName == 'Java':
warn('Java-based OSes are not supported yet by tfscript.', category=RuntimeWarning)
return None
def main() -> int:
''' Command line interface. '''
global args
global targetDir
parser = parseCLI()
args = parser.parse_args()
systemName = GetOSName()
if is not None:
targetDir = normpath( + dirsep
targetDir = getTargetDir(systemName)
if targetDir is not None:
# Supported OS: add steamapps path
targetDir += normpath('/steamapps/common/Team Fortress 2/tf/cfg') + dirsep
elif args.force:
# Unsupported OS but -f specified
if args.debug:
print('DEBUG: forced to continue, output set to current directory', file=stderr)
targetDir = '.'
# Unsupported OS and not forced to continue
return 2
config, defaults = parseFile(args.infile)
if config is None:
return 2
fileNames = parseConfig(config, defaults)
fileList = writing.replaceFiles(targetDir, fileNames, args)
defaultsGiven = (defaults is not None)
writing.appendToActuals(targetDir, fileList, defaultsGiven, args)
return 0
if __name__ == '__main__':