Endless ATC custom airport development tools

This commit is contained in:
Calvin Ng
2020-12-28 21:09:40 -08:00
parent 8e7dfb771d
commit 712fdae601
5 changed files with 185 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
__pycache__
eatcdev.ini
+9
View File
@@ -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.
+53
View File
@@ -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())
+88
View File
@@ -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())
+33
View File
@@ -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())