KiCad PCB EDA Suite
Loading...
Searching...
No Matches
orcad_cis.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
22
23#include <algorithm>
24#include <charconv>
25#include <boost/algorithm/string/split.hpp>
26
27#include <ki_exception.h>
28
29
30namespace
31{
32
33std::string payload( const std::vector<char>& aData )
34{
35 if( aData.size() < 4 )
36 THROW_IO_ERROR( wxS( "OrCAD CIS stream is missing its length" ) );
37
38 ORCAD_STREAM stream( aData );
39 uint32_t length = stream.ReadU32();
40
41 if( length != aData.size() - 4 )
42 THROW_IO_ERROR( wxS( "OrCAD CIS stream length is invalid" ) );
43
44 return std::string( aData.begin() + 4, aData.end() );
45}
46
47
48std::vector<std::string> split( const std::string& aText, unsigned char aSeparator )
49{
50 std::vector<std::string> result;
51 boost::split( result, aText,
52 [aSeparator]( unsigned char aByte ) { return aByte == aSeparator; } );
53
54 return result;
55}
56
57
58uint32_t decimal( const std::string& aText, const wxString& aWhat )
59{
60 if( aText.empty() )
61 THROW_IO_ERROR( aWhat );
62
63 uint32_t value = 0;
64 auto [end, error] = std::from_chars( aText.data(), aText.data() + aText.size(), value );
65
66 if( error != std::errc() || end != aText.data() + aText.size() )
67 THROW_IO_ERROR( aWhat );
68
69 return value;
70}
71
72
73std::vector<std::string> readStrings( ORCAD_STREAM& aStream, const wxString& aWhat )
74{
75 uint32_t count = aStream.ReadU32();
76 std::vector<std::string> result;
77
78 if( count > aStream.Remaining() / 3 )
79 THROW_IO_ERROR( aWhat );
80
81 result.reserve( count );
82
83 for( uint32_t i = 0; i < count; ++i )
84 result.push_back( aStream.ReadLzt() );
85
86 return result;
87}
88
89} // namespace
90
91
92std::vector<std::string> OrcadCisParseCountedList( const std::vector<char>& aData, uint8_t aSeparator )
93{
94 std::string contents = payload( aData );
95
96 if( contents.size() == 1 && static_cast<unsigned char>( contents.front() ) == 0xFB )
97 return {};
98
99 std::vector<std::string> fields = split( contents, aSeparator );
100
101 uint32_t count = decimal( fields.front(), wxS( "OrCAD CIS list count is invalid" ) );
102 fields.erase( fields.begin() );
103
104 if( fields.size() != count )
105 THROW_IO_ERROR( wxS( "OrCAD CIS list count does not match its contents" ) );
106
107 return fields;
108}
109
110
111std::map<uint32_t, std::map<std::string, std::string>> OrcadCisParsePropertyUpdates( const std::vector<char>& aData )
112{
113 std::map<uint32_t, std::map<std::string, std::string>> result;
114
115 for( const std::string& record : split( payload( aData ), '~' ) )
116 {
117 if( record.empty() )
118 continue;
119
120 size_t namesAt = record.find( static_cast<char>( 0xB0 ) );
121 size_t valuesAt = record.find( static_cast<char>( 0xC0 ), namesAt == std::string::npos ? 0 : namesAt + 1 );
122
123 if( namesAt == std::string::npos || valuesAt == std::string::npos )
124 THROW_IO_ERROR( wxS( "OrCAD CIS property update is malformed" ) );
125
126 uint32_t occurrence = decimal( record.substr( 0, namesAt ), wxS( "OrCAD CIS occurrence ID is invalid" ) );
127 std::vector<std::string> names = split( record.substr( namesAt + 1, valuesAt - namesAt - 1 ), '^' );
128 std::vector<std::string> values = split( record.substr( valuesAt + 1 ), '^' );
129
130 if( names.size() != values.size() )
131 THROW_IO_ERROR( wxS( "OrCAD CIS property names and values have different counts" ) );
132
133 auto& properties = result[occurrence];
134
135 for( size_t i = 0; i < names.size(); ++i )
136 {
137 if( names[i].empty() )
138 THROW_IO_ERROR( wxS( "OrCAD CIS property name is empty" ) );
139
140 properties[names[i]] = values[i];
141 }
142 }
143
144 return result;
145}
146
147
148std::map<uint32_t, bool> OrcadCisParseMemberships( const std::vector<char>& aData )
149{
150 std::map<uint32_t, bool> result;
151
152 for( const std::string& record : split( payload( aData ), '~' ) )
153 {
154 if( record.empty() )
155 continue;
156
157 size_t separator = record.find( static_cast<char>( 0xB0 ) );
158
159 if( separator != 1 || ( record[0] != '0' && record[0] != '1' ) )
160 THROW_IO_ERROR( wxS( "OrCAD CIS group membership is malformed" ) );
161
162 uint32_t occurrence =
163 decimal( record.substr( separator + 1 ), wxS( "OrCAD CIS membership occurrence ID is invalid" ) );
164 result[occurrence] = record[0] == '1';
165 }
166
167 return result;
168}
169
170
172{
173 ORCAD_STREAM stream( aData );
175 uint32_t recordCount = stream.ReadU32();
176
177 for( uint32_t recordIndex = 0; recordIndex < recordCount; ++recordIndex )
178 {
179 uint32_t databaseId = stream.ReadU32();
180 std::vector<std::string> groups = readStrings( stream, wxS( "OrCAD CIS group list is invalid" ) );
181 std::vector<std::string> states = readStrings( stream, wxS( "OrCAD CIS group state list is invalid" ) );
182 uint32_t propertySetCount = stream.ReadU32();
183
184 if( states.size() != groups.size() || propertySetCount != groups.size() )
185 THROW_IO_ERROR( wxS( "OrCAD CIS schematic group counts do not match" ) );
186
187 for( uint32_t groupIndex = 0; groupIndex < propertySetCount; ++groupIndex )
188 {
189 std::vector<std::string> names =
190 readStrings( stream, wxS( "OrCAD CIS schematic property names are invalid" ) );
191 std::vector<std::string> values =
192 readStrings( stream, wxS( "OrCAD CIS schematic property values are invalid" ) );
193
194 if( names.size() != values.size() )
195 THROW_IO_ERROR( wxS( "OrCAD CIS schematic property counts do not match" ) );
196
197 ORCAD_CIS_PROPERTIES& properties = result[groups[groupIndex]][databaseId];
198
199 for( size_t propertyIndex = 0; propertyIndex < names.size(); ++propertyIndex )
200 {
201 if( names[propertyIndex].empty() )
202 THROW_IO_ERROR( wxS( "OrCAD CIS schematic property name is empty" ) );
203
204 properties[names[propertyIndex]] = values[propertyIndex];
205 }
206 }
207
208 stream.ReadU32();
209 }
210
211 if( !stream.AtEnd() )
212 THROW_IO_ERROR( wxS( "OrCAD CIS schematic stream has trailing data" ) );
213
214 return result;
215}
216
217
218std::string OrcadCisSelectVariant( const std::vector<std::string>& aNames,
219 const std::optional<std::string>& aRequested )
220{
221 if( aRequested )
222 {
223 if( std::find( aNames.begin(), aNames.end(), *aRequested ) == aNames.end() )
224 THROW_IO_ERROR( wxS( "The requested OrCAD CIS variant does not exist" ) );
225
226 return *aRequested;
227 }
228
229 if( aNames.empty() )
230 return {};
231
232 return *std::min_element( aNames.begin(), aNames.end() );
233}
The caller owns the buffer.
size_t Remaining() const
Bytes left; 0 when the cursor is at or past the end.
bool AtEnd() const
std::string ReadLzt()
– strings ----------------------------------------------------------------—
uint32_t ReadU32()
static bool empty(const wxTextEntryBase *aCtrl)
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
std::map< uint32_t, bool > OrcadCisParseMemberships(const std::vector< char > &aData)
std::map< uint32_t, std::map< std::string, std::string > > OrcadCisParsePropertyUpdates(const std::vector< char > &aData)
std::string OrcadCisSelectVariant(const std::vector< std::string > &aNames, const std::optional< std::string > &aRequested)
ORCAD_CIS_SCHEMATIC_INFO OrcadCisParseSchematicInfo(const std::vector< char > &aData)
std::vector< std::string > OrcadCisParseCountedList(const std::vector< char > &aData, uint8_t aSeparator)
Definition orcad_cis.cpp:92
std::map< std::string, std::map< uint32_t, ORCAD_CIS_PROPERTIES > > ORCAD_CIS_SCHEMATIC_INFO
Definition orcad_cis.h:31
std::map< std::string, std::string > ORCAD_CIS_PROPERTIES
Definition orcad_cis.h:30
static std::vector< std::string > split(const std::string &aStr, const std::string &aDelim)
Split the input string into a vector of output strings.
VECTOR2I end
wxString result
Test unit parsing edge cases and error handling.