Full-node GUI wallet for DragonX cryptocurrency. Built with Dear ImGui, SDL3, and OpenGL3/DX11. Features: - Send/receive shielded and transparent transactions - Autoshield with merged transaction display - Built-in CPU mining (xmrig) - Peer management and network monitoring - Wallet encryption with PIN lock - QR code generation for receive addresses - Transaction history with pagination - Console for direct RPC commands - Cross-platform (Linux, Windows)
179 lines
6.0 KiB
Python
179 lines
6.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Convert DragonX ui.json / ui-dark.json / ui-light.json to TOML format.
|
|
|
|
Usage:
|
|
python3 scripts/json2toml.py res/themes/ui.json res/themes/ui.toml
|
|
python3 scripts/json2toml.py res/themes/ui-dark.json res/themes/ui-dark.toml
|
|
python3 scripts/json2toml.py res/themes/ui-light.json res/themes/ui-light.toml
|
|
python3 scripts/json2toml.py --all # converts all three
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
import os
|
|
import re
|
|
from collections import OrderedDict
|
|
|
|
# Keys that need quoting in TOML because they contain special chars
|
|
def needs_quoting(key):
|
|
# TOML bare keys: [A-Za-z0-9_-]+
|
|
return not re.match(r'^[A-Za-z0-9_-]+$', key)
|
|
|
|
def quote_key(key):
|
|
if needs_quoting(key):
|
|
return f'"{key}"'
|
|
return key
|
|
|
|
def format_value(val):
|
|
"""Format a Python value as a TOML value string."""
|
|
if isinstance(val, bool):
|
|
return "true" if val else "false"
|
|
elif isinstance(val, int):
|
|
return str(val)
|
|
elif isinstance(val, float):
|
|
# Ensure floats always have a decimal point
|
|
s = repr(val)
|
|
if '.' not in s and 'e' not in s and 'E' not in s:
|
|
s += '.0'
|
|
return s
|
|
elif isinstance(val, str):
|
|
# Escape backslashes and quotes
|
|
escaped = val.replace('\\', '\\\\').replace('"', '\\"')
|
|
return f'"{escaped}"'
|
|
elif isinstance(val, list):
|
|
parts = [format_value(v) for v in val]
|
|
return f'[{", ".join(parts)}]'
|
|
else:
|
|
return repr(val)
|
|
|
|
def is_simple_leaf(obj):
|
|
"""Check if an object is a simple leaf that can be an inline table.
|
|
Simple leafs: {"size": X}, {"color": "..."}, {"height": X}, or small
|
|
objects with only primitive values and no nested objects."""
|
|
if not isinstance(obj, dict):
|
|
return False
|
|
for v in obj.values():
|
|
if isinstance(v, (dict, list)):
|
|
return False
|
|
# Keep objects with many keys as sections (threshold: 6 keys)
|
|
return len(obj) <= 6
|
|
|
|
def is_array_of_objects(val):
|
|
"""Check if val is an array of objects (needs [[array.of.tables]])."""
|
|
return isinstance(val, list) and all(isinstance(v, dict) for v in val) and len(val) > 0
|
|
|
|
def write_inline_table(obj):
|
|
"""Write a dict as a TOML inline table."""
|
|
parts = []
|
|
for k, v in obj.items():
|
|
parts.append(f'{quote_key(k)} = {format_value(v)}')
|
|
return '{ ' + ', '.join(parts) + ' }'
|
|
|
|
def emit_toml(data, lines, prefix='', depth=0):
|
|
"""Recursively emit TOML from a parsed JSON dict."""
|
|
|
|
# Separate keys into: scalars/arrays, simple-leaf objects, complex objects, array-of-tables
|
|
scalars = []
|
|
inline_leaves = []
|
|
complex_tables = []
|
|
array_tables = []
|
|
|
|
for key, val in data.items():
|
|
# Skip _comment keys (we'll handle them differently)
|
|
if key.startswith('_comment'):
|
|
continue
|
|
|
|
if isinstance(val, dict):
|
|
if is_simple_leaf(val):
|
|
inline_leaves.append((key, val))
|
|
else:
|
|
complex_tables.append((key, val))
|
|
elif is_array_of_objects(val):
|
|
array_tables.append((key, val))
|
|
else:
|
|
scalars.append((key, val))
|
|
|
|
# Emit scalars first
|
|
for key, val in scalars:
|
|
lines.append(f'{quote_key(key)} = {format_value(val)}')
|
|
|
|
# Emit inline leaf objects (like { size = 42.0 })
|
|
for key, val in inline_leaves:
|
|
lines.append(f'{quote_key(key)} = {write_inline_table(val)}')
|
|
|
|
# Emit complex sub-tables with [section] headers
|
|
for key, val in complex_tables:
|
|
subprefix = f'{prefix}.{key}' if prefix else key
|
|
lines.append('')
|
|
lines.append(f'[{subprefix}]')
|
|
emit_toml(val, lines, subprefix, depth + 1)
|
|
|
|
# Emit array-of-tables with [[section]] headers
|
|
for key, val in array_tables:
|
|
subprefix = f'{prefix}.{key}' if prefix else key
|
|
for item in val:
|
|
lines.append('')
|
|
lines.append(f'[[{subprefix}]]')
|
|
if isinstance(item, dict):
|
|
for ik, iv in item.items():
|
|
if isinstance(iv, dict):
|
|
# Nested object inside array item — inline table
|
|
lines.append(f'{quote_key(ik)} = {write_inline_table(iv)}')
|
|
else:
|
|
lines.append(f'{quote_key(ik)} = {format_value(iv)}')
|
|
|
|
def convert_file(input_path, output_path):
|
|
"""Convert a JSON theme file to TOML."""
|
|
with open(input_path) as f:
|
|
data = json.load(f, object_pairs_hook=OrderedDict)
|
|
|
|
lines = []
|
|
|
|
# Add header comments (converted from _comment_* keys)
|
|
for key, val in data.items():
|
|
if key.startswith('_comment') and isinstance(val, str):
|
|
if val:
|
|
lines.append(f'# {val}')
|
|
else:
|
|
lines.append('')
|
|
|
|
if lines:
|
|
lines.append('')
|
|
|
|
# Emit all non-comment content
|
|
emit_toml(data, lines)
|
|
|
|
# Clean up extra blank lines
|
|
output = '\n'.join(lines).strip() + '\n'
|
|
# Collapse 3+ consecutive newlines to 2
|
|
while '\n\n\n' in output:
|
|
output = output.replace('\n\n\n', '\n\n')
|
|
|
|
with open(output_path, 'w') as f:
|
|
f.write(output)
|
|
|
|
print(f'Converted: {input_path} -> {output_path}')
|
|
print(f' JSON: {os.path.getsize(input_path):,} bytes')
|
|
print(f' TOML: {os.path.getsize(output_path):,} bytes')
|
|
print(f' Reduction: {100 - 100*os.path.getsize(output_path)/os.path.getsize(input_path):.0f}%')
|
|
|
|
def main():
|
|
if len(sys.argv) == 2 and sys.argv[1] == '--all':
|
|
base = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
themes = os.path.join(base, 'res', 'themes')
|
|
for name in ['ui', 'ui-dark', 'ui-light']:
|
|
inp = os.path.join(themes, f'{name}.json')
|
|
out = os.path.join(themes, f'{name}.toml')
|
|
if os.path.exists(inp):
|
|
convert_file(inp, out)
|
|
else:
|
|
print(f'Skipping (not found): {inp}')
|
|
elif len(sys.argv) == 3:
|
|
convert_file(sys.argv[1], sys.argv[2])
|
|
else:
|
|
print(__doc__)
|
|
sys.exit(1)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|