Skip to content

Instantly share code, notes, and snippets.

@pudquick
Last active August 29, 2015 14:16
Show Gist options
  • Select an option

  • Save pudquick/8bb4b041c73b369d4d68 to your computer and use it in GitHub Desktop.

Select an option

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
# 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')
This file has been truncated, but you can view the full file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment