Skip to content

Instantly share code, notes, and snippets.

@pwaller
Created April 8, 2014 08:06
Show Gist options
  • Select an option

  • Save pwaller/10100874 to your computer and use it in GitHub Desktop.

Select an option

Save pwaller/10100874 to your computer and use it in GitHub Desktop.
#! /usr/bin/env python3
"""
usage: craftviz <file.craft>
"""
import re
from textwrap import dedent
import pyparsing as P
from docopt import docopt
L = lambda e: P.Literal(e).suppress()
noNL = lambda expr: expr.setWhitespaceChars(" \t\r")
ASSIGNMENT = P.Group(
P.Word(P.alphas)("name") +
L("=") +
P.Optional(noNL(P.Word(P.alphas + P.nums + " (),.-_:; ")))("value")
) + P.LineEnd().suppress()
BLOCK = P.Forward()
BLOCK << P.Group(
P.Word(P.alphas + "_")("block") +
L("{") +
P.Group(P.ZeroOrMore(ASSIGNMENT | BLOCK))("children") +
L("}"))
CRAFT_FILE = P.OneOrMore(ASSIGNMENT | BLOCK) + P.StringEnd()
class Block(object):
def __init__(self, name, parent, elements):
self.name, self.parent = name, parent
self.blocks = [e for e in elements if "block" in e]
self.assigns = [e for e in elements if "block" not in e]
self.d = {a["name"]: a.get("value")
for a in self.assigns}
self.children = [Block(b["block"], self, b.children)
for b in self.blocks]
@property
def what(self):
if self.name == "PART":
return self.d["part"]
@property
def attachments(self):
for a in self.assigns:
if a["name"] in "link attN srfN sym".split():
left, _, right = a["value"].partition(",")
if _:
yield right
else:
yield left
if a["name"] == "cData":
x = re.search("tgt: ([^;]+);", a["value"])
if x:
(tgt,) = x.groups()
# raise RuntimeError(tgt)
yield tgt
def __repr__(self):
return "{} {} {}".format(self.name, self.what, list(self.attachments))
def main():
args = docopt(__doc__)
with open(args["<file.craft>"]) as fd:
elements = CRAFT_FILE.parseString(fd.read())
top = Block(None, None, elements)
print("graph {")
for block in top.children:
# print(block)
for a in block.attachments:
print('"{}" -- "{}"'.format(block.what, a))
print("}")
if __name__ == "__main__":
main()
"""
Parses files of this form:
ship = Absurd I (Crash 13)
version = 0.23.5
description =
type = VAB
PART
{
part = Mark1-2Pod_4294777266
partName = Part
pos = 0.04093654,20.80854,0.07038268
rot = 0,0,0,1
attRot = 0,0,0,1
mir = 1,1,1
istg = 0
dstg = 0
sidx = -1
sqor = -1
attm = 0
link = RCSTank1-2_4294776478
link = GrapplingDevice_4294777156
attN = bottom,RCSTank1-2_4294776478
attN = top,GrapplingDevice_4294777156
EVENTS
{
}
ACTIONS
{
}
MODULE
{
name = ModuleCommand
isEnabled = True
controlSrcStatusText =
EVENTS
{
MakeReference
{
active = True
guiActive = True
guiIcon = Control From Here
guiName = Control From Here
category = Control From Here
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
RenameVessel
{
active = True
guiActive = True
guiIcon = Rename Vessel
guiName = Rename Vessel
category = Rename Vessel
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
}
ACTIONS
{
}
}
MODULE
{
name = ModuleSAS
isEnabled = True
EVENTS
{
}
ACTIONS
{
}
}
MODULE
{
name = ModuleReactionWheel
isEnabled = True
stateString = Active
WheelState = Active
EVENTS
{
OnToggle
{
active = True
guiActive = True
guiIcon = Toggle Torque
guiName = Toggle Torque
category = Toggle Torque
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
}
ACTIONS
{
Activate
{
actionGroup = None
}
Deactivate
{
actionGroup = None
}
Toggle
{
actionGroup = None
}
}
}
MODULE
{
name = ModuleScienceExperiment
isEnabled = True
Deployed = False
Inoperable = False
EVENTS
{
DeployExperiment
{
active = True
guiActive = True
guiIcon = Deploy
guiName = Deploy
category = Deploy
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
CollectDataExternalEvent
{
active = True
guiActive = False
guiIcon =
guiName =
category =
guiActiveUnfocused = True
unfocusedRange = 1.5
externalToEVAOnly = True
}
ReviewDataEvent
{
active = True
guiActive = True
guiIcon = Review Data
guiName = Review Data
category = Review Data
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
ResetExperiment
{
active = True
guiActive = True
guiIcon = Reset
guiName = Reset
category = Reset
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
ResetExperimentExternal
{
active = True
guiActive = False
guiIcon = Reset
guiName = Reset
category = Reset
guiActiveUnfocused = True
unfocusedRange = 2
externalToEVAOnly = True
}
}
ACTIONS
{
DeployAction
{
actionGroup = None
}
ResetAction
{
actionGroup = None
}
}
}
MODULE
{
name = ModuleScienceContainer
isEnabled = True
EVENTS
{
StoreDataExternalEvent
{
active = False
guiActive = False
guiIcon =
guiName = Store Experiments (0)
category =
guiActiveUnfocused = True
unfocusedRange = 2
externalToEVAOnly = True
}
CollectDataExternalEvent
{
active = False
guiActive = False
guiIcon =
guiName = Take Data (0)
category =
guiActiveUnfocused = True
unfocusedRange = 2
externalToEVAOnly = True
}
ReviewDataEvent
{
active = False
guiActive = True
guiIcon = Review Data
guiName = Review Stored Data (0)
category = Review Data
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
}
ACTIONS
{
}
}
MODULE
{
name = FlagDecal
isEnabled = True
flagDisplayed = True
EVENTS
{
ToggleFlag
{
active = True
guiActive = False
guiActiveEditor = True
guiIcon = ToggleFlag
guiName = ToggleFlag
category = ToggleFlag
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
}
ACTIONS
{
}
}
MODULE
{
name = ModuleTripLogger
isEnabled = True
EVENTS
{
}
ACTIONS
{
}
Surfaced
{
}
Flew
{
}
FlewBy
{
}
Orbited
{
}
SubOrbited
{
}
}
RESOURCE
{
name = ElectricCharge
amount = 150
maxAmount = 150
flowState = True
isTweakable = True
hideFlow = False
flowMode = Both
}
RESOURCE
{
name = MonoPropellant
amount = 30
maxAmount = 30
flowState = True
isTweakable = True
hideFlow = False
flowMode = Both
}
}
PART
{
part = RCSTank1-2_4294776478
partName = Part
pos = 0.04093654,19.8293,0.07038268
rot = 0,0,0,1
attRot = 0,0,0,1
mir = 1,1,1
istg = 0
dstg = 0
sidx = -1
sqor = -1
attm = 0
link = size3Decoupler_4294776412
attN = top,Mark1-2Pod_4294777266
attN = bottom,size3Decoupler_4294776412
EVENTS
{
}
ACTIONS
{
}
RESOURCE
{
name = MonoPropellant
amount = 750
maxAmount = 750
flowState = True
isTweakable = True
hideFlow = False
flowMode = Both
}
}
PART
{
part = GrapplingDevice_4294777156
partName = Part
pos = 0.04093654,22.14255,0.07038268
rot = 0,0,0,1
attRot = 0,0,0,1
mir = 1,1,1
istg = 0
dstg = 0
sidx = -1
sqor = -1
attm = 0
srfN = srfAttach,RCSTank1-2_4294776478
attN = top,Mark1-2Pod_4294777266
EVENTS
{
}
ACTIONS
{
}
MODULE
{
name = ModuleGrappleNode
isEnabled = True
state = Ready
dockUId = 0
EVENTS
{
Release
{
active = False
guiActive = True
guiIcon = Release
guiName = Release
category = Release
guiActiveUnfocused = True
unfocusedRange = 2
externalToEVAOnly = True
}
ReleaseSameVessel
{
active = False
guiActive = True
guiIcon = Release
guiName = Release
category = Release
guiActiveUnfocused = True
unfocusedRange = 2
externalToEVAOnly = True
}
Decouple
{
active = False
guiActive = True
guiIcon = Decouple Node
guiName = Decouple Node
category = Decouple Node
guiActiveUnfocused = True
unfocusedRange = 2
externalToEVAOnly = True
}
MakeReferenceTransform
{
active = True
guiActive = True
guiIcon = Control from Here
guiName = Control from Here
category = Control from Here
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
SetLoose
{
active = False
guiActive = True
guiIcon = Free Pivot
guiName = Free Pivot
category = Free Pivot
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
LockPivot
{
active = False
guiActive = True
guiIcon = Lock Pivot
guiName = Lock Pivot
category = Lock Pivot
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
}
ACTIONS
{
ReleaseAction
{
actionGroup = None
}
DecoupleAction
{
actionGroup = None
}
}
}
MODULE
{
name = ModuleAnimateGeneric
isEnabled = True
status = Locked
animSwitch = True
animTime = 0
animSpeed = 1
EVENTS
{
Toggle
{
active = True
guiActive = True
guiIcon = Toggle
guiName = Arm
category = Toggle
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
}
ACTIONS
{
ToggleAction
{
actionGroup = None
}
}
}
MODULE
{
name = FXModuleLookAtConstraint
isEnabled = True
EVENTS
{
}
ACTIONS
{
}
}
MODULE
{
name = FlagDecal
isEnabled = True
flagDisplayed = True
EVENTS
{
ToggleFlag
{
active = True
guiActive = False
guiActiveEditor = True
guiIcon = ToggleFlag
guiName = ToggleFlag
category = ToggleFlag
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
}
ACTIONS
{
}
}
}
PART
{
part = size3Decoupler_4294776412
partName = Part
pos = 0.04093654,18.89515,0.07038268
rot = 0,0,0,1
attRot = 0,0,0,1
mir = 1,1,1
istg = 0
dstg = 1
sidx = 0
sqor = 0
attm = 0
link = Size3LargeTank_4294776372
attN = top,RCSTank1-2_4294776478
attN = bottom,Size3LargeTank_4294776372
EVENTS
{
}
ACTIONS
{
}
MODULE
{
name = ModuleDecouple
isEnabled = True
isDecoupled = False
EVENTS
{
Decouple
{
active = True
guiActive = True
guiIcon = Decouple
guiName = Decouple
category = Decouple
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
}
ACTIONS
{
DecoupleAction
{
actionGroup = None
}
}
}
}
PART
{
part = Size3LargeTank_4294776372
partName = Part
pos = 0.04093654,14.71859,0.07038268
rot = 0,0,0,1
attRot = 0,0,0,1
mir = 1,1,1
istg = 2
dstg = 2
sidx = -1
sqor = -1
attm = 0
link = Size3EngineCluster_4294776344
attN = top,size3Decoupler_4294776412
attN = bottom,Size3EngineCluster_4294776344
EVENTS
{
}
ACTIONS
{
}
RESOURCE
{
name = LiquidFuel
amount = 6480
maxAmount = 6480
flowState = True
isTweakable = True
hideFlow = False
flowMode = Both
}
RESOURCE
{
name = Oxidizer
amount = 7920
maxAmount = 7920
flowState = True
isTweakable = True
hideFlow = False
flowMode = Both
}
}
PART
{
part = Size3EngineCluster_4294776344
partName = Part
pos = 0.04093654,9.451339,0.07038268
rot = 0,0,0,1
attRot = 0,0,0,1
mir = 1,1,1
istg = 1
dstg = 2
sidx = 0
sqor = 1
attm = 0
attN = top,Size3LargeTank_4294776372
EVENTS
{
}
ACTIONS
{
}
MODULE
{
name = ModuleEnginesFX
isEnabled = True
staged = False
flameout = False
EngineIgnited = False
engineShutdown = False
currentThrottle = 0
thrustPercentage = 100
manuallyOverridden = False
thrustPercentage_UIFlight
{
controlEnabled = True
minValue = 0
maxValue = 100
stepIncrement = 0.5
}
EVENTS
{
Activate
{
active = True
guiActive = True
guiIcon = Activate Engine
guiName = Activate Engine
category = Activate Engine
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
Shutdown
{
active = False
guiActive = True
guiIcon = Shutdown Engine
guiName = Shutdown Engine
category = Shutdown Engine
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
}
ACTIONS
{
OnAction
{
actionGroup = None
}
ShutdownAction
{
actionGroup = None
}
ActivateAction
{
actionGroup = None
}
}
}
MODULE
{
name = ModuleAlternator
isEnabled = True
EVENTS
{
}
ACTIONS
{
}
}
MODULE
{
name = ModuleGimbal
isEnabled = True
gimbalLock = False
EVENTS
{
LockGimbal
{
active = True
guiActive = True
guiActiveEditor = True
guiIcon = Lock Gimbal
guiName = Lock Gimbal
category = Lock Gimbal
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
FreeGimbal
{
active = False
guiActive = True
guiActiveEditor = True
guiIcon = Free Gimbal
guiName = Free Gimbal
category = Free Gimbal
guiActiveUnfocused = False
unfocusedRange = 2
externalToEVAOnly = True
}
}
ACTIONS
{
ToggleAction
{
actionGroup = None
}
}
}
MODULE
{
name = ModuleAnimateHeat
isEnabled = True
EVENTS
{
}
ACTIONS
{
}
}
RESOURCE
{
name = ElectricCharge
amount = 0
maxAmount = 0
flowState = True
isTweakable = False
hideFlow = True
flowMode = Both
}
}
"""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment