Last active
August 29, 2015 14:16
-
-
Save pudquick/8bb4b041c73b369d4d68 to your computer and use it in GitHub Desktop.
Python script for parsing the current (as of this gist) Prison Architect file format
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # You need python 2.7 for this | |
| # FYI: There's a filepath hardcoded in this example at the end | |
| # If you've got python installed, you can run this with: python prison_json.py | |
| import re, mmap, collections, json, os | |
| # These are special keys which should be treated as an array/list of values | |
| multi_keys = ["Traits", "Reputation", "ReputationHigh"] | |
| def dequote(keyname): | |
| if (keyname[0] == '"'): | |
| return keyname[1:-1] | |
| return keyname | |
| def funky_words_read(filepath): | |
| with open(filepath, 'rb') as fin: | |
| # Map the entire file to RAM, for fun | |
| mf = mmap.mmap(fin.fileno(), 0, access=mmap.ACCESS_READ) | |
| words = re.finditer('("[^\n\r"]+[ ]+[^\n\r"]*"|.*?)[ \n\r]+', mf, re.MULTILINE) | |
| # This iteratively parses the file, in order, looking for the next regular expression match | |
| # | |
| # This regular expression matches values of the form: | |
| # "[non-whitespace] [non-whitespace]"[whitespace] | |
| # "[non-whitespace] "[whitespace] (usually a Forname) | |
| # [non-whitespace][whitespace] | |
| # | |
| # Special case: Can match initial whitespace at the beginning of the file with no characters before it | |
| while True: | |
| # Check for that initial blank | |
| maybe_blank = words.next().group(1) | |
| if maybe_blank: | |
| # Not blank, yield and continue on without checking any more | |
| yield dequote(maybe_blank) | |
| break | |
| # Return everything else | |
| for word in words: | |
| yield dequote(word.group(1)) | |
| def recurse_parse(i): | |
| # This recursively parses the words extracted from the .prison file | |
| # The basic structure is one of the following: | |
| # <KEY> <VALUE> | |
| # <KEY> <VALUE> | |
| # BEGIN <KEY> <... sub-dict ...> END | |
| # KEYs and VALUEs can contain spaces by being wrapped in "quotes" | |
| this_dict = collections.OrderedDict() | |
| mode = 0 # Wait to define next attribute/etc. | |
| for val in i: | |
| if mode == 0: | |
| # Hunting for attribute name or command | |
| if (val == 'BEGIN'): | |
| # Switch to mode 1, grab the next value as an attribute name and start a sub-dict | |
| mode = 1 | |
| continue | |
| elif (val == 'END'): | |
| # Return what we have, aborting remaining iteration | |
| return this_dict | |
| else: | |
| # We have an attribute name, the next bit should be a value | |
| keyname = val | |
| mode = 2 | |
| continue | |
| elif mode == 1: | |
| # This value is a name of our new sub-dict | |
| this_dict[val] = recurse_parse(i) | |
| # Return to normal | |
| mode = 0 | |
| continue | |
| elif mode == 2: | |
| # We have a simple value, time to store it | |
| if (keyname in multi_keys): | |
| # Handle the value differently if the key is a multi_key | |
| current_val = this_dict.get(keyname, []) | |
| val = current_val + [val] | |
| this_dict[keyname] = val | |
| keyname = None | |
| # Return to normal | |
| mode = 0 | |
| continue | |
| # Ran out of values, we should be in mode 0 | |
| if (mode != 0): | |
| raise("Unable to parse this file, appears malformed") | |
| return this_dict | |
| def process_file(filename): | |
| parsed = recurse_parse(funky_words_read(filename)) | |
| old_file, _ = os.path.splitext(filename) | |
| with open('%s.json' % old_file, 'w') as f: | |
| f.write(json.dumps(parsed, indent=4)) | |
| # Example usage | |
| process_file('/Users/mike/Desktop/24.prison') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment