blob: bff85fd3f3c9beddc56a4a48959880448d1b4329 [file] [log] [blame]
Brian Silverman8fa2aad2017-06-10 16:45:30 -07001VersionRegex = /v (\d{8}) (\d+)/
2ComponentRegex = /C (\d+) (\d+) ([01]) (\d{1,3}) ([01]) (.+)/
3TextRegex = /T (\d+) (\d+) (\d+) (\d+) ([01]) ([012]) (\d{1,3}) ([0-8]) (\d+)/
Brian Silvermande756452017-07-02 12:43:02 -07004PowerRegex = /^(([0-9.]+V-plus)|(title-.)|(gnd)|(output)|(input)|(io)|(vcc)|-1)|(vbat)|(vhalfbat)|(generic-power).sym$/
Brian Silverman8fa2aad2017-06-10 16:45:30 -07005
6# All coordinates are in "mils".
7
8class Component
9 attr_accessor :x, :y, :selectable, :angle, :mirrored, :filename
10 attr_accessor :attributes
11
12 def initialize(x, y, selectable, angle, mirrored, filename)
13 @x, @y, @selectable = x, y, selectable
14 @angle, @mirrored, @filename = angle, mirrored, filename
15
16 @attributes = {}
17 end
18
19 def refdes
20 if attributes.has_key?(:refdes)
21 attributes[:refdes].value
22 else
23 nil
24 end
25 end
26
27 def [](key)
28 r = attributes[key]
29 if r
30 r.value
31 else
32 nil
33 end
34 end
35
36 def is_power
37 filename =~ PowerRegex
38 end
39end
40
41def parse_bool(string)
42 if string == '0'
43 return false
44 elsif string == '1'
45 return true
46 else
47 raise "'#{string}' is not a bool"
48 end
49end
50
51class Attribute
52 attr_accessor :x, :y, :color, :size, :visible, :show, :angle, :alignment
53 attr_accessor :name, :value
54
55 def initialize(x, y, color, size, visible, show, angle, alignment, name, value)
56 @x, @y, @color, @size, @visible, @show = x, y, color, size, visible, show
57 @angle, @alignment, @name, @value = angle, alignment, name, value
58 end
59
60 def Attribute.parse(match, value_line)
61 raise "Don't know what multi-line attributes mean" if match[9] != '1'
62 attribute_value = value_line.chomp.split('=', 2)
63
64 Attribute.new(match[1].to_i,
65 match[2].to_i,
66 match[3].to_i,
67 match[4].to_i,
68 parse_bool(match[5]),
69 match[6],
70 match[7].to_i,
71 match[8].to_i,
72 attribute_value[0],
73 attribute_value[1])
74 end
75end
76
77class GschemFile
78 attr_accessor :version_date, :version
79
80 def initialize(filename)
81 lines = nil
82 File.open(filename) do |f|
83 lines = f.readlines
84 end
85 version_line = lines.shift
86 version_match = VersionRegex.match version_line
87 raise "Can't parse version line '#{version_line}'" unless version_match
88 @version_date = version_match[1]
89 @version = version_match[2].to_i
90
91 return lines
92 end
93end
94
95class GschemSymbol < GschemFile
96 attr_accessor :attributes
97
98 def initialize(filename)
99 lines = super(filename)
100
101 @attributes = {}
102
103 until lines.empty?
104 line = lines.shift
105
106 match = TextRegex.match(line)
107 if match
108 attribute = Attribute.parse(match, lines.shift)
109 @attributes[attribute.name.intern] = attribute
110 end
111 end
112 end
113
114 def [](key)
115 r = attributes[key]
116 if r
117 r.value
118 else
119 nil
120 end
121 end
122end
123
124class GschemSchematic < GschemFile
125 attr_accessor :components
126
127 def initialize(filename)
128 lines = super(filename)
129
130 @components = []
131
132 until lines.empty?
133 line = lines.shift
134
135 match = ComponentRegex.match(line)
136 if match
137 c = Component.new(match[1].to_i,
138 match[2].to_i,
139 parse_bool(match[3]),
140 match[4].to_i,
141 parse_bool(match[5]),
142 match[6])
143 line = lines.shift
144 unless line
145 # There's a component without {} at the end of the file.
146 raise "Huh?" unless lines.empty?
147 next
148 end
149 if line.chomp == '{'
150 line = lines.shift
151 until line.chomp == '}'
152 match = TextRegex.match(line)
153 raise "Error parsing attribute '#{line}'" unless match
154
155 attribute = Attribute.parse(match, lines.shift)
156 c.attributes[attribute.name.intern] = attribute
157
158 line = lines.shift
159 end
160 else
161 lines.unshift line
162 end
163 @components.push c
164 next
165 end
166 end
167 end
168end
169
170def get_schematic_filenames files
171 r = []
172 files.each do |file|
173 if file.end_with? '.gsch2pcb'
174 File.open(file, 'r') do |f|
Brian Silverman856af592017-12-18 11:17:09 -0500175 dirname = File.dirname(file) + '/'
176 r += f.readline.split(' ')[1..-1].map do |filename|
177 dirname + filename
178 end
Brian Silverman8fa2aad2017-06-10 16:45:30 -0700179 end
180 else
181 r.push file
182 end
183 end
184 r
185end