You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
186 lines
5.9 KiB
Python
186 lines
5.9 KiB
Python
'''
|
|
Command line module for making Team Fortress 2 macro scripts from
|
|
YAML source code.
|
|
'''
|
|
|
|
__all__ = ['parseFile']
|
|
__author__ = 'Nicholas Hope <tfscript@nickhope.world'
|
|
__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
|
|
|
|
try:
|
|
from winreg import HKEY_LOCAL_MACHINE, ConnectRegistry, OpenKey, EnumValue
|
|
except ModuleNotFoundError:
|
|
# Not running on windows
|
|
pass
|
|
|
|
# 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 verify.py
|
|
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
|
|
else:
|
|
return config, defaults
|
|
|
|
def parseConfig(config, defaults):
|
|
'''With validated data structure, write out all the files.'''
|
|
global args
|
|
global targetDir
|
|
|
|
if isdir(targetDir) == False:
|
|
mkdir(targetDir)
|
|
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(
|
|
config[class_],
|
|
default=(class_ == 'default')
|
|
)
|
|
replaceDict = writing.writeOutput(stringToWrite, class_, args)
|
|
tempsAndReals.update(replaceDict)
|
|
|
|
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',
|
|
'implicit'
|
|
]
|
|
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:
|
|
warn(
|
|
'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:
|
|
try:
|
|
accessSubkeyName, data, _ = EnumValue(accessKey, keyNum)
|
|
except EnvironmentError:
|
|
break
|
|
else:
|
|
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 args.directory is not None:
|
|
targetDir = normpath(args.directory) + dirsep
|
|
else:
|
|
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 = '.'
|
|
else:
|
|
# 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__':
|
|
exit(main())
|