Endless ATC custom airport development tools
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
__pycache__
|
||||
eatcdev.ini
|
||||
@@ -0,0 +1,9 @@
|
||||
In this directory are a few tools useful for writing Endless ATC airport files. You can see examples of its usage in RJTT and RJCC.
|
||||
|
||||
The tools are Python 3 scripts. They have not been tested in Python 2. There should be no dependencies.
|
||||
|
||||
Depending on your platform, you will need to run 'python3 deploy.py', 'python deploy.py', or possibly even 'deploy.py' will work.
|
||||
|
||||
For further help, view the help available by running 'python deploy.py -h'
|
||||
|
||||
If you find any bugs, you can report on the community Discord.
|
||||
@@ -0,0 +1,53 @@
|
||||
import argparse
|
||||
import configparser
|
||||
import glob
|
||||
import os
|
||||
import shutil
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read('eatcdev.ini')
|
||||
default_destination = config['deploy'].get("path", "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Endless ATC\\locations\\") if 'deploy' in config else "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Endless ATC\\locations\\"
|
||||
|
||||
def main(args):
|
||||
if args.build:
|
||||
import expand
|
||||
import renumber
|
||||
|
||||
destination = args.destination_path or default_destination
|
||||
if args.deploy:
|
||||
print(f"Deploying to {args.destination_path or destination}.")
|
||||
|
||||
if not args.codes:
|
||||
import distutils.util
|
||||
if not distutils.util.strtobool(input("Confirm you wish to process all airport files? (y/n) ")):
|
||||
print("Aborting.")
|
||||
return
|
||||
args.codes.append("")
|
||||
|
||||
for code in args.codes:
|
||||
path = os.path.join(args.input_dir, "**", args.source_dir, args.pattern.format(code = code))
|
||||
for file in glob.glob(path, recursive=True):
|
||||
print(f'Found {file}')
|
||||
if args.build:
|
||||
file = expand.main(args, file)
|
||||
renumber.main(args, file)
|
||||
if args.deploy:
|
||||
result = shutil.copy(file, destination)
|
||||
print(f"Copied {file} to {result}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Build specified Endless ATC airports and copy to the Endless ATC directory.", epilog="https://github.com/AdamJCavanaugh/EndlessATCAirports")
|
||||
parser.add_argument('codes', nargs='*', help="Airport codes to build and deploy. Prefixes can be used. If no code specified, you will be prompted if all airport files are to be processed.")
|
||||
parser.add_argument('-w', '--input-dir', nargs='?', default=os.path.join(os.pardir, 'final'), help='''The directory containing the airport files.
|
||||
Subdirectories that are not source directories will also be searched. Defaults to '../final'.''')
|
||||
parser.add_argument('-s', '--source-dir', nargs='?', default='source', help="The name of the folders that will contain source files. Defaults to 'source'.")
|
||||
parser.add_argument('-p', '--pattern', nargs='?', default='{code}*.txt', help="The glob pattern for the file names to build based on the input codes. Defaults to '{code}*.txt'.")
|
||||
parser.add_argument('-o', '--output-path', nargs='?', default=os.pardir, help='''The path to the directory to store the output of the build process relative to the source file.
|
||||
Defaults to the parent directory relative to the source file.''')
|
||||
parser.add_argument('-d', '--destination-path', nargs='?', help='''The directory to copy the output of the build process to (e.g. Endless ATC locations folder).
|
||||
Defaults to "C:\\Program Files (x86)\\Steam\\steamapps\\common\\Endless ATC\\locations\\".
|
||||
This default can be overridden by a 'path = ' entry under a [deploy] section in an eatcdev.ini.''')
|
||||
parser.add_argument('-n', '--no-build', action='store_false', dest='build', help='Specify to skip build, and just copy sources to output folder.')
|
||||
parser.add_argument('-b', '--build-only', action='store_false', dest='deploy', help="Specify this option to skip copying output of build processes to destination folder.")
|
||||
|
||||
main(parser.parse_args())
|
||||
@@ -0,0 +1,88 @@
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
|
||||
def main(args, input_file=None):
|
||||
pattern = re.compile(r"^(?P<airport_section>\[airport(?P<airport_id>\d*)\])|(?P<airline_entry>#!\t(?P<airline_code>[-\w]*), (?P<airline_frequency>\d*), (?P<airline_parameters>[\w/d]*, [-\w ]*, [nswe]*))|(?P<result_marker>#!expansionoutput(?P<result_id>\d+))|(?P<result_end_marker>#!expansionoutputend)|(?P<sid_marker>#!sid(?P<sid_frequency>[\d]+)x)")
|
||||
|
||||
result = {'output': []}
|
||||
airport = 0
|
||||
ignore_lines = False
|
||||
ignore_one_line = False
|
||||
sid_frequency = 0
|
||||
sid_lines = []
|
||||
if input_file is None:
|
||||
input_file = args.input_file
|
||||
output_file = input_file if args.output_file is None else args.output_file
|
||||
else:
|
||||
output_file = os.path.join(os.path.dirname(input_file), args.output_path, os.path.basename(input_file))
|
||||
print("Building {0} to {1}".format(input_file, output_file))
|
||||
|
||||
with open(input_file, 'r', newline='') as airport_file:
|
||||
for line in airport_file:
|
||||
match = pattern.match(line)
|
||||
if match:
|
||||
if match['airport_section']:
|
||||
airport = match['airport_id']
|
||||
if not airport in result:
|
||||
result[airport] = []
|
||||
|
||||
elif match['airline_entry']:
|
||||
total_frequency = int(match['airline_frequency'])
|
||||
frequencies = []
|
||||
while total_frequency > 10:
|
||||
frequencies.append(10)
|
||||
total_frequency -= 10
|
||||
frequencies.append(total_frequency)
|
||||
for frequency in frequencies:
|
||||
result[airport].append("\t{match[airline_code]}, {frequency}, {match[airline_parameters]}\n".format(match=match, frequency=frequency))
|
||||
|
||||
elif match['result_marker']:
|
||||
result['output'].append(line)
|
||||
for result_line in result[match['result_id']]:
|
||||
result['output'].append(result_line)
|
||||
ignore_lines = True
|
||||
|
||||
elif match['result_end_marker']:
|
||||
ignore_lines = False
|
||||
|
||||
elif match['sid_marker']:
|
||||
sid_frequency = int(match['sid_frequency']) - 1
|
||||
ignore_one_line = True
|
||||
|
||||
if sid_frequency:
|
||||
if line.isspace():
|
||||
for _ in range(sid_frequency):
|
||||
result['output'].extend(sid_lines)
|
||||
sid_frequency = 0
|
||||
sid_lines = []
|
||||
elif not len(sid_lines):
|
||||
sid_lines.append("\n")
|
||||
else:
|
||||
sid_lines.append(line)
|
||||
|
||||
if not ignore_lines and not ignore_one_line:
|
||||
result['output'].append(line)
|
||||
|
||||
if ignore_one_line:
|
||||
ignore_one_line = False
|
||||
|
||||
with open(output_file, 'w', newline='') as airport_file:
|
||||
airport_file.writelines(result['output'])
|
||||
return output_file
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='''Expands certain commands to allow for concise Endless ATC airport source files.
|
||||
|
||||
#!expansionoutput<airport_id> can be inserted on its own line in a source file terminated by
|
||||
#!expansionoutputend on a following line. This block, which should remain empty, will be used
|
||||
to write the result of expanding any airline definitions in a #! comment. Any #! definitions
|
||||
with frequency greater are split into entries with max 10 frequency each.
|
||||
|
||||
#!sid<n>x can be inserted before any "routex =" declaration in a [departure] section to repeat the
|
||||
route <n> times. This can be used to adjust the distribution of traffic on each SID. Note the
|
||||
numbering of each "route" will not be adjusted. See renumber.py for such operation.''')
|
||||
parser.add_argument('input_file')
|
||||
parser.add_argument('output_file', nargs='?')
|
||||
|
||||
main(parser.parse_args())
|
||||
@@ -0,0 +1,33 @@
|
||||
import argparse
|
||||
import re
|
||||
|
||||
header_re = re.compile(r"^(?:\[(?P<header>(?:approach)|(?:transition)|(?:departure)|(?:area))\d*\])|^(?:route\d+ *= *)")
|
||||
|
||||
def main(args, file=None):
|
||||
|
||||
def number_approach(match, indexes={'approach': 0, 'transition': 0, 'departure': 0, 'area': 0, 'route': 0}):
|
||||
header = match.group("header")
|
||||
if header:
|
||||
indexes['route'] = 0
|
||||
else:
|
||||
header = 'route'
|
||||
indexes[header] += 1
|
||||
return (header == 'route' and "{header}{index} = " or "[{header}{index}]").format(header = header, index = indexes[header])
|
||||
|
||||
if file is None:
|
||||
file = args.airport_file
|
||||
|
||||
result = []
|
||||
|
||||
with open(file, 'r', newline='') as airport_file:
|
||||
for line in airport_file:
|
||||
result.append(header_re.sub(number_approach, line))
|
||||
|
||||
with open(file, 'w', newline='') as airport_file:
|
||||
airport_file.writelines(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='Re-number [approach] sections for Endless ATC airport files.')
|
||||
parser.add_argument('airport_file')
|
||||
|
||||
main(parser.parse_args())
|
||||
Reference in New Issue
Block a user