Search code examples
pythoncode-generation

Generating code for Valve QC file using Python


After about a year of writing these files by hand, I'm looking for a way to generate QC files. I have tried just hard coding the commands in a list and just writing out the the command and its value as text, but this feels very brute force-ish, and I'm figuring there must be a more elegant way to populating the file. If it matters, my end goal turning this into an add-on for blender. EDIT: Adding example output/QC file

$modelname "AnrachyReigns\Jack the ripper.mdl"
$maxverts "65000"

$model "Jack" "genericmale.dmx"{
 eyeball "eye_right" "bip_head" -1.66 0.53 66.89 "eyeball_r" 1 4 "iris_unused" 2.9
 eyeball "eye_left" "bip_head" -1.66 0.53 66.89 "eyeball_l" 1 4 "iris_unused" 2.9
 mouth 0 "mouth" "bip_head" 0 1 0
flexcontroller eyes range -65 65 eyes_updown
flexconttroller eye range -65 65 eyes_rightleft
}

$bodygroup "Right Arm"{
 studio "Gattling.dmx"
 blank
}

$bodygroup "Left arm"{
 studio "HandCannon.dmx"
 blank
}

$mostlyopaque

$attacement "eyes" "bip_head" 1 1 1 rotate 0 -80 -90
$attachment "mouth" "bip_head" 0 0 0 rotate 0 0 0

$cdmaterials "models\GameModels\AnarachyReigns\Jack\"

$sequence "Jack" "genericmale.dmx


Solution

  • As I alluded to in the comments, you could come up with a tree-like API for these objects.

    For example,

    import json
    import sys
    
    
    class QCObject:
        def __init__(self, tag, parameters=(), children=()):
            self.tag = tag
            self.parameters = list(parameters)
            self.children = list(children)
    
        def add(self, child):
            self.children.append(child)
    
        def serialize(self, stream, depth=-1):
            indent = "  " * depth
            if self.tag:
                header_line = [self.tag]
                # Here's hoping JSON serialization is close enough to QC
                header_line.extend([json.dumps(val) for val in self.parameters])
                if self.children:
                    header_line.append("{")
                print(indent + " ".join(header_line), file=stream)
            for child in self.children:
                child.serialize(stream, depth=depth + 1)
            if self.tag and self.children:
                print(indent + "}", file=stream)
    
    
    root = QCObject(None)
    root.add(QCObject("$modelname", ["Jack.mdl"]))
    root.add(QCObject("$maxverts", ["65000"]))
    
    jack_model = QCObject(
        "$model",
        ["Jack" "genericmale.dmx"],
        children=[
            QCObject(
                "eyeball", ["eye_right", "bip_head", -1.66, 0.53, 66.89, "eyeball_r", 1, 4, "iris_unused", 2.9]
            ),
        ],
    )
    
    root.add(jack_model)
    
    root.add(
        QCObject("$bodygroup", ["Right Arm"], children=[QCObject("studio", ["Gattling.dmx"]), QCObject("blank"),])
    )
    
    root.serialize(sys.stdout)
    

    outputs

    $modelname "Jack.mdl"
    $maxverts "65000"
    $model "Jackgenericmale.dmx" {
      eyeball "eye_right" "bip_head" -1.66 0.53 66.89 "eyeball_r" 1 4 "iris_unused" 2.9
    }
    $bodygroup "Right Arm" {
      studio "Gattling.dmx"
      blank
    }
    

    with automagical indentation and no managel string mangling.