summaryrefslogblamecommitdiff
path: root/misc/twine.py
blob: 4949ec830c8a6217c46ab3dfb7e19e2cc6851882 (plain) (tree)






































































































































































                                                                                  
#!/usr/bin/python2

import sys 
from HTMLParser import HTMLParser

''' twine2rpy is for storyboarding/testing Ren'py scripts in Twine and converting
the Twine source file to .rpy. 

This example was made for use with Twine 2, which exports directly to HTML.
It only supports simple narrator speech and flow control: Variables, conditionals,
images, and even markup are not supported.

It should not be hard to extend this script (unless you're using HTML inside the
dialog blocks). THIS FILE IS PROVIDED AS-IS, WITHOUT WARRANTIES OF ANY KIND,
EXPRESS OR IMPLIED, EXCEPT WHERE OTHERWISE REQUIRED BY LAW.

PS. This script version is not meant for production use.
From the command line: twine2rpy_2.py source.txt '''
        

#######################################################################
# Open Twine source file  

# This function will (in an expensive manner) find the tuples we want
# on HTML argument list.
def dl_getkey(array, key):
    for k in array:
        if k[0] == key:
            return k[1]

# Receive arguments from commandline
try:
    file_name = sys.argv[1]
except IndexError:
    print("Usage: ./converter.py <twineproject.html>")
    print("")
    print("Made with Harlowe2 syntax. Use other syntaxes at your own risk!")
    exit(0)

# Receives a HTML file
open_file = open(file_name)
txt_file = open_file.read() 
txt_file = txt_file.replace("&quot;", "\"").replace("&#39;", "\'")

# Prepare the Ren'Py file
renpy_file = "t2_" + file_name[:file_name.find(".")].rstrip() + ".txt"
rpy_file = open(renpy_file, "w")

# Global dictionaries
# TLT : Top Level Tag (what we're examining right now).
TLT="html"
LABELS={}
INMENU=False
PARSE=False
BREAK=False

# Parse data
# The "data" is received as-is; As long that it doesn't uses HTML,
# It should not break.
def parse(basedata):
    global TLT, LABELS, INMENU, BREAK
    data=basedata.split("\n")

    # Convert stuff
    for d in data:
        # regexes (or anything which would break this)
        d=d.replace("\"", "\\\"")
        d=d.strip()

        # Exceptions: Do not add empty dialog boxes
        if d == "":
            if BREAK:
                rpy_file.write("    next;\n")
                #rpy_file.write("     mesn;\n")
                BREAK=False
            rpy_file.write("\n")
            continue

        # Handle comments
        if (d.startswith("//")):
            rpy_file.write("    %s\n" % d)
            continue

        # Here is an example of how simple menus could be handed:
        if (d.startswith("[[") and not INMENU):
            d=d.replace("[[", "").replace("]]", "")
            INMENU=True
            rpy_file.write("    menu\n")
            target=LABELS[d]
            rpy_file.write("        l(\"%s\"), L_%s" % (d, target))
        elif (d.startswith("[[") and INMENU):
            d=d.replace("[[", "").replace("]]", "")
            target=LABELS[d]
            rpy_file.write(",\n        l(\"%s\"), L_%s" % (d, target))
        elif INMENU:
            INMENU=False
            rpy_file.write(";\n")

        else:
            # Save it as a narrator speech block;
            # It will ignore other markdown and comments, too, but thankfully,
            # it is not very difficult to add regexes and replaces for them.
            rpy_file.write("    mesq l(\"%s\");\n" % d)
            BREAK=True


# HTML Magic
# create a subclass and override the default handler methods
class MyHTMLParser(HTMLParser):
    def handle_starttag(self, tag, attrs):
        global TLT, LABELS, PARSE
        # Set on TLT variable what tag we're in
        # FIXME: You probably don't want to redefine TLT after
        # it has been set to tw-passagedata; Instead, you'll want
        # handle_endtag() to unset this. This is necessary if you plan
        # in converting scripts with HTML code inside the passages.
        TLT=tag

        # A new passage! Lets get its name and pid.
        if tag == "tw-passagedata":
            labelname=dl_getkey(attrs, "name")
            labelid=dl_getkey(attrs, "pid")
            # Not writing file, lets register the label on the dict
            if not PARSE:
                LABELS[labelname]=labelid
                print("Register label: %s" % labelname)
            # If we're writing the file, then give it a safe label name
            else:
                rpy_file.write("L_%s:\n" % labelid)

    def handle_endtag(self, tag):
        global PARSE, INMENU
        # Ops, seems like we finished the passage!
        # To prevent implicit fallthroughs, lets call return
        if tag == "tw-passagedata" and PARSE:
            if not INMENU:
                rpy_file.write("    close;\n\n")
            else:
                INMENU=False
                rpy_file.write(";\n")
                rpy_file.write("    close;\n\n")

    def handle_data(self, data):
        global TLT, PARSE
        if (TLT in ["script", "style"] or not PARSE):
            # Silently ignore Twine stuff
            pass
        else:
            # We're writing and we're inside a passage, so call parse() on it
            if (TLT == "tw-passagedata"):
                parse(data)

# Do a dry run.
# This is needed to prepare LABELS dictionary
parser = MyHTMLParser()
parser.feed(txt_file)

# Prepare the file
PARSE=True
parser.feed(txt_file)

# Close files we opened
open_file.close()
rpy_file.close()

# Report our success
print("Success!")