Initial Commit: working json -> hierarchical yaml unpacking

This commit is contained in:
Adam Goldsmith 2020-09-29 11:58:56 -04:00
commit 6a486c4f36
4 changed files with 170 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.egg-info/
__pycache__/

42
poetry.lock generated Normal file
View File

@ -0,0 +1,42 @@
[[package]]
name = "pathvalidate"
version = "2.3.0"
description = "pathvalidate is a Python library to sanitize/validate a string such as filenames/file-paths/etc."
category = "main"
optional = false
python-versions = ">=3.5"
[package.extras]
test = ["allpairspy", "click", "faker", "pytest"]
[[package]]
name = "pyyaml"
version = "5.3.1"
description = "YAML parser and emitter for Python"
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[metadata]
lock-version = "1.0"
python-versions = "^3.8"
content-hash = "9174acecc4f28cb786e63beb6c608fe788fb186e6485c64749543309a2b53121"
[metadata.files]
pathvalidate = [
{file = "pathvalidate-2.3.0-py3-none-any.whl", hash = "sha256:1697c8ea71ff4c48e7aa0eda72fe4581404be8f41e51a17363ef682dd6824d35"},
{file = "pathvalidate-2.3.0.tar.gz", hash = "sha256:32d30dbacb711c16bb188b12ce7e9a46b41785f50a12f64500f747480a4b6ee3"},
]
pyyaml = [
{file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"},
{file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"},
{file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"},
{file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"},
{file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"},
{file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"},
{file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"},
{file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"},
{file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"},
{file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"},
{file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"},
]

19
pyproject.toml Normal file
View File

@ -0,0 +1,19 @@
[tool.poetry]
name = "tts_yaml_unpacker"
version = "0.1.0"
description = ""
authors = ["Adam Goldsmith <adam@adamgoldsmith.name>"]
[tool.poetry.dependencies]
python = "^3.8"
pathvalidate = "^2.3.0"
pyyaml = "^5.3.1"
[tool.poetry.dev-dependencies]
[tool.poetry.scripts]
tts_yaml_unpacker = 'tts_yaml_unpacker:main'
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"

107
tts_yaml_unpacker.py Executable file
View File

@ -0,0 +1,107 @@
#!/usr/bin/env python3
import json
import yaml
import os
import sys
from pathvalidate import sanitize_filename
class IncludeTag:
yaml_tag = '!include'
def __init__(self, target):
self.target = target
@classmethod
def to_yaml(cls, dumper, node):
return dumper.represent_scalar(cls.yaml_tag, node.target)
@classmethod
def from_yaml(cls, loader, node):
base_dir = os.path.dirname(loader.stream.name)
target = os.path.join(base_dir, node.value)
with open(target) as f:
if target.endswith('.yaml'):
return yaml.load(f, Loader=yaml.Loader)
else:
return f.read()
yaml.add_constructor('!include', IncludeTag.from_yaml)
yaml.add_representer(IncludeTag, IncludeTag.to_yaml)
def uniqueName(obj):
if 'SaveName' in obj:
return obj['SaveName']
return f"{obj['Name']} {obj['Nickname']} {obj['GUID']}"
def shouldRecurse(obj, subobject_prop):
return subobject_prop in obj \
and obj.get('Name') not in ('Deck', 'DeckCustom') \
and len(obj[subobject_prop]) > 1
def recursivelyUnpackObject(parent_dir, obj,
subobject_prop='ContainedObjects', base_name=None):
def convertObjectsToIncludes(objects):
for subObj in objects:
filename = recursivelyUnpackObject(file_base_path, subObj)
yield IncludeTag(os.path.join(obj_base_name, filename))
obj_base_name = base_name or sanitize_filename(uniqueName(obj))
file_base_path = os.path.join(parent_dir, obj_base_name)
if shouldRecurse(obj, subobject_prop):
os.makedirs(file_base_path, exist_ok=True)
obj[subobject_prop] = list(
convertObjectsToIncludes(obj[subobject_prop]))
if 'LuaScript' in obj and len(obj['LuaScript']) > 0:
with open(file_base_path + '.ttslua', 'w') as f:
f.write(obj['LuaScript'])
obj['LuaScript'] = IncludeTag(obj_base_name + '.ttslua')
with open(file_base_path + '.yaml', 'w') as f:
yaml.dump(obj, f)
return obj_base_name + '.yaml'
def unpackJson(json_file, output_name):
with open(json_file) as f:
data = json.load(f)
recursivelyUnpackObject(
'', data, subobject_prop='ObjectStates', base_name=output_name)
def packYaml(yaml_file, output_json_file):
with open(yaml_file) as f:
data = yaml.load(f, Loader=yaml.Loader)
with open(output_json_file, 'w') as f:
json.dump(data, f, indent=2)
def usage():
print(f"Usage: {sys.argv[0]} <unpack|pack> <input> <output>")
def main():
if len(sys.argv) != 4:
usage()
elif sys.argv[1] == 'unpack':
unpackJson(sys.argv[2], sys.argv[3])
elif sys.argv[1] == 'pack':
packYaml(sys.argv[2], sys.argv[3])
else:
usage()
if __name__ == '__main__':
main()