Skip to content

Instantly share code, notes, and snippets.

@yut23
Last active August 12, 2022 04:51
Show Gist options
  • Save yut23/5b6ded1b894b4b6f13ea26285b624f78 to your computer and use it in GitHub Desktop.
Save yut23/5b6ded1b894b4b6f13ea26285b624f78 to your computer and use it in GitHub Desktop.
ChipWizard save format notes
ChipWizard save format notes:
* stored in save.dat base64-encoded and zlib compressed
* multi-byte integers are little-endian
4-byte header at start of file: 0xEA 0x03 0x00 0x00
120 bytes of data for layout
* 4 bytes for each cell, 5 rows × 6 columns
* starts at lower-left corner, proceeds to the right, wrapping to the next row above
byte 1: bit field for components
0x01: metal
0x02: n-type
0x04: p-type
0x08: capacitor
0x10: via
0x20: transistor type (set by n-type, cleared by p-type, persists after deletion)
0x40 & 0x80: unused
byte 2: metal connections, only lowest 4 bits used
0x01: right
0x02: top
0x04: left
0x08: bottom
byte 3: n-type connections (same values as metal)
byte 4: p-type connections (same values as metal)
Examples in binary:
metal, none: 00000001 00000000 00000000 00000000
metal, right: 00000001 00000001 00000000 00000000
metal, top: 00000001 00000010 00000000 00000000
metal, left: 00000001 00000100 00000000 00000000
metal, bottom: 00000001 00001000 00000000 00000000
n-type, none: 00100010 00000000 00000000 00000000
n-type, right: 00100010 00000000 00000001 00000000
n-type, top: 00100010 00000000 00000010 00000000
n-type, left: 00100010 00000000 00000100 00000000
n-type, bottom: 00100010 00000000 00001000 00000000
p-type, none: 00000100 00000000 00000000 00000000
p-type, right: 00000100 00000000 00000000 00000001
p-type, top: 00000100 00000000 00000000 00000010
p-type, left: 00000100 00000000 00000000 00000100
p-type, bottom: 00000100 00000000 00000000 00001000
capacitor: 00001000 00000000 00000000 00000000 (exclusive with silicon)
via: 00010000 00000000 00000000 00000000 (can only be placed on silicon)
p-type, L&R: 00000100 00000000 00000000 00000101 (p-type, right | p-type, left)
PNP, base on top: 00100110 00000000 00000010 00000101 (p-type, right | p-type, left | n-type, up)
n-type, L&R: 00100010 00000000 00000101 00000000 (n-type, right | n-type, left)
NPN, base on bottom: 00000110 00000000 00000101 00000010 (n-type, right | n-type, left | p-type, up)
The transistor type flag is used to distinguish between transistors that have the same connection layout:
PNP, base on top and bottom: 00000110 00000000 00000101 00001010
NPN, base on left and right: 00100110 00000000 00000101 00001010
It gets set when dragging over a cell with n-type silicon selected.
This is followed by 0x01 and 16 more bytes if there's a selection, or just 0x00 if not
selection format:
u4: lower-left corner x
u4: lower-left corner y
u4: width
u4: height
#!/usr/bin/env python3
"""Reads ChipWizard solutions from `save.dat` and pretty-prints them."""
import base64
import io
import struct
import sys
import zlib
for line in sys.stdin:
key, _, string = line.partition(" = ")
if not key.startswith("Volgograd.Solution"):
continue
data = zlib.decompress(base64.b64decode(string.strip()))
stream = io.BytesIO(data)
print(f"{key}:")
# check header
assert struct.unpack("<I", stream.read(4))[0] == 1002
rows = []
for r in range(5):
row = []
for c in range(6):
cell = stream.read(4)
row.append(cell)
rows.append(row)
if stream.read(1)[0]:
selection = True
x, y, width, height = struct.unpack("<4I", stream.read(16))
else:
selection = False
# print in reverse order, to match the layout in-game
for row in rows[::-1]:
for cell in row:
print(" ".join([f"{byte:08b}" for byte in cell]), end=" ")
print()
if selection:
print(f"Selection: {x=}, {y=}, {width=}, {height=}")
print()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment