2013-05-28 00:33:36 +03:00
|
|
|
#!/usr/bin/env python2
|
|
|
|
|
2013-05-29 00:31:09 +03:00
|
|
|
"""
|
|
|
|
Copyright (c) 2013, Pelle Nilsson
|
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
modification, are permitted provided that the following conditions are
|
|
|
|
met:
|
|
|
|
|
|
|
|
Redistributions of source code must retain the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
|
|
|
|
Redistributions in binary form must reproduce the above copyright
|
|
|
|
notice, this list of conditions and the following disclaimer in
|
|
|
|
the documentation and/or other materials provided with the distribution.
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
"""
|
|
|
|
|
2013-05-29 01:01:17 +03:00
|
|
|
import os
|
|
|
|
import os.path
|
2013-05-28 00:33:36 +03:00
|
|
|
import sys
|
|
|
|
import json
|
|
|
|
|
2013-06-03 22:57:55 +03:00
|
|
|
import sections
|
2013-06-03 23:19:06 +03:00
|
|
|
import templates
|
2013-05-28 00:33:36 +03:00
|
|
|
from output import OutputFormat
|
|
|
|
|
|
|
|
USAGE = "usage: %prog [options] inputfile(s)... outputfile"
|
|
|
|
|
2013-06-03 23:19:06 +03:00
|
|
|
def of(extension, name):
|
2013-06-04 01:22:54 +03:00
|
|
|
return {'extension' : extension,
|
|
|
|
'name' : name}
|
2013-05-28 00:33:36 +03:00
|
|
|
|
2013-06-03 23:19:06 +03:00
|
|
|
OUTPUT_FORMATS = [
|
|
|
|
of('tex', 'LaTeX'),
|
|
|
|
of('rtf', 'Rich Text Format'),
|
|
|
|
of('dot', 'Graphviz section flowchart'),
|
|
|
|
of('html', 'HTML+JS playable in browser'),
|
|
|
|
of('debug', 'Gamebook Debug Output'),
|
|
|
|
]
|
2013-05-30 01:18:05 +03:00
|
|
|
|
2013-05-28 00:33:36 +03:00
|
|
|
def make_supported_formats_list_string():
|
|
|
|
return "Supported Output Formats:\n" + "\n".join(
|
|
|
|
[str(f) for f in OUTPUT_FORMATS])
|
|
|
|
|
2013-06-04 00:46:11 +03:00
|
|
|
def format_gamebook(inputfilenames,
|
|
|
|
outputfilename,
|
2013-06-04 01:22:54 +03:00
|
|
|
import_default_map_file,
|
|
|
|
templatedirs):
|
|
|
|
output_format = make_output(outputfilename, templatedirs)
|
2013-06-03 22:57:55 +03:00
|
|
|
book = sections.Book()
|
2013-05-28 00:33:36 +03:00
|
|
|
for inputfilename in inputfilenames:
|
|
|
|
parse_file_to_book(open(inputfilename, 'r'), book)
|
2013-06-04 00:46:11 +03:00
|
|
|
if import_default_map_file:
|
|
|
|
import_default_nr_map(outputfilename, book)
|
|
|
|
write_book(book, output_format, outputfilename)
|
2013-05-28 00:33:36 +03:00
|
|
|
|
|
|
|
def parse_file_to_book(inputfile, book):
|
2013-05-30 01:18:05 +03:00
|
|
|
name = None
|
|
|
|
number = None
|
|
|
|
text = ""
|
|
|
|
for line in inputfile.readlines():
|
|
|
|
if line.startswith('*'):
|
|
|
|
if name:
|
2013-06-04 00:46:11 +03:00
|
|
|
add_section_to_book(book, name, text, number)
|
2013-05-30 01:18:05 +03:00
|
|
|
number = None
|
|
|
|
text = ""
|
|
|
|
heading = line[1:].strip().split(' ')
|
|
|
|
if len(heading) == 1:
|
|
|
|
name = heading[0]
|
|
|
|
elif len(heading) == 2:
|
|
|
|
number = int(heading[0])
|
|
|
|
name = heading[1]
|
|
|
|
else:
|
2013-06-03 22:57:55 +03:00
|
|
|
raise Exception("bad section heading %s" % str(heading))
|
2013-05-28 00:33:36 +03:00
|
|
|
else:
|
2013-05-30 01:18:05 +03:00
|
|
|
text = text + " " + line.strip()
|
|
|
|
if name:
|
2013-06-04 00:46:11 +03:00
|
|
|
add_section_to_book(book, name, text, number)
|
|
|
|
|
|
|
|
def add_section_to_book(book, name, text, number=None):
|
|
|
|
book.add(sections.Section(name, text))
|
|
|
|
if number:
|
|
|
|
book.force_section_nr(name, number)
|
2013-05-28 00:33:36 +03:00
|
|
|
|
2013-06-04 01:22:54 +03:00
|
|
|
def make_output(outputfilename, templatedirs):
|
2013-05-28 00:33:36 +03:00
|
|
|
for of in OUTPUT_FORMATS:
|
2013-06-04 01:22:54 +03:00
|
|
|
extension = of['extension']
|
|
|
|
if outputfilename.endswith('.' + extension):
|
|
|
|
return OutputFormat(templates.Templates(templatedirs, extension))
|
2013-05-28 00:33:36 +03:00
|
|
|
raise Exception("Unsupported or unknown output format for %s."
|
2013-05-30 01:18:05 +03:00
|
|
|
% outputfilename)
|
2013-05-28 00:33:36 +03:00
|
|
|
|
2013-06-04 00:46:11 +03:00
|
|
|
def write_book(book, output_format, outputfilename):
|
|
|
|
shuffled_sections = book.shuffle()
|
|
|
|
output = open(outputfilename, 'w')
|
|
|
|
output_format.write_begin(book, output)
|
|
|
|
output_format.write_shuffled_sections(shuffled_sections, output)
|
|
|
|
output_format.write_end(book, output)
|
|
|
|
save_section_mapping(shuffled_sections, outputfilename)
|
|
|
|
|
|
|
|
def import_default_nr_map(outputfilename, book):
|
|
|
|
mapfilename = make_default_map_filename(outputfilename)
|
|
|
|
if os.path.exists(mapfilename): #FIXME better check
|
|
|
|
for name,nr in json.load(open(mapfilename)).iteritems():
|
|
|
|
book.force_section_nr(name, nr)
|
|
|
|
|
|
|
|
def save_section_mapping(shuffled_sections, outputfilename):
|
|
|
|
mapfilename = make_default_map_filename(outputfilename)
|
|
|
|
json.dump(shuffled_sections.name_to_nr, open(mapfilename, 'w'))
|
|
|
|
|
|
|
|
def make_default_map_filename(outputfilename):
|
|
|
|
basename = outputfilename
|
|
|
|
dotpos = outputfilename.rfind('.')
|
|
|
|
if dotpos >= 1:
|
|
|
|
basename = outputfilename[:dotpos]
|
|
|
|
return basename + '.map'
|
|
|
|
|
2013-05-28 00:33:36 +03:00
|
|
|
if __name__ == '__main__':
|
|
|
|
import argparse
|
|
|
|
ap = argparse.ArgumentParser(epilog=make_supported_formats_list_string(),
|
|
|
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
|
|
ap.add_argument('inputfiles', metavar='inputfile', nargs='+',
|
|
|
|
help='input gamebook file (eg test.json)')
|
|
|
|
ap.add_argument('outputfile', metavar='outputfile',
|
|
|
|
help='output file (eg test.tex or test.rtf)')
|
2013-06-04 00:46:11 +03:00
|
|
|
ap.add_argument('-M', '--no-default-map', action='store_false',
|
|
|
|
dest='import_default_map_file',
|
|
|
|
help='ignore default map file')
|
2013-06-04 01:22:54 +03:00
|
|
|
ap.add_argument('-t', '--template', metavar='D', dest='templatedirs',
|
2013-06-04 01:27:32 +03:00
|
|
|
action='append', help='add custom template dir')
|
2013-05-28 00:33:36 +03:00
|
|
|
args = ap.parse_args()
|
2013-06-04 01:22:54 +03:00
|
|
|
templatedirs = ['templates',
|
|
|
|
os.path.join(os.path.dirname(sys.argv[0]), 'templates')]
|
|
|
|
if args.templatedirs:
|
|
|
|
for t in args.templatedirs:
|
|
|
|
templatedirs.insert(-2, t)
|
2013-06-04 00:46:11 +03:00
|
|
|
format_gamebook(args.inputfiles,
|
|
|
|
args.outputfile,
|
2013-06-04 01:22:54 +03:00
|
|
|
args.import_default_map_file,
|
|
|
|
templatedirs)
|
2013-05-28 00:33:36 +03:00
|
|
|
|