25#include <boost/algorithm/string/split.hpp>
33std::string payload(
const std::vector<char>& aData )
35 if( aData.size() < 4 )
39 uint32_t length = stream.ReadU32();
41 if( length != aData.size() - 4 )
44 return std::string( aData.begin() + 4, aData.end() );
48std::vector<std::string>
split(
const std::string& aText,
unsigned char aSeparator )
50 std::vector<std::string>
result;
51 boost::split(
result, aText,
52 [aSeparator](
unsigned char aByte ) {
return aByte == aSeparator; } );
58uint32_t decimal(
const std::string& aText,
const wxString& aWhat )
64 auto [
end, error] = std::from_chars( aText.data(), aText.data() + aText.size(), value );
66 if( error != std::errc() ||
end != aText.data() + aText.size() )
73std::vector<std::string> readStrings(
ORCAD_STREAM& aStream,
const wxString& aWhat )
75 uint32_t count = aStream.
ReadU32();
76 std::vector<std::string>
result;
83 for( uint32_t i = 0; i < count; ++i )
94 std::string contents = payload( aData );
96 if( contents.size() == 1 &&
static_cast<unsigned char>( contents.front() ) == 0xFB )
99 std::vector<std::string> fields =
split( contents, aSeparator );
101 uint32_t count = decimal( fields.front(), wxS(
"OrCAD CIS list count is invalid" ) );
102 fields.erase( fields.begin() );
104 if( fields.size() != count )
105 THROW_IO_ERROR( wxS(
"OrCAD CIS list count does not match its contents" ) );
113 std::map<uint32_t, std::map<std::string, std::string>>
result;
115 for(
const std::string& record :
split( payload( aData ),
'~' ) )
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 );
123 if( namesAt == std::string::npos || valuesAt == std::string::npos )
124 THROW_IO_ERROR( wxS(
"OrCAD CIS property update is malformed" ) );
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 ),
'^' );
130 if( names.size() != values.size() )
131 THROW_IO_ERROR( wxS(
"OrCAD CIS property names and values have different counts" ) );
133 auto& properties =
result[occurrence];
135 for(
size_t i = 0; i < names.size(); ++i )
137 if( names[i].
empty() )
140 properties[names[i]] = values[i];
150 std::map<uint32_t, bool>
result;
152 for(
const std::string& record :
split( payload( aData ),
'~' ) )
157 size_t separator = record.find(
static_cast<char>( 0xB0 ) );
159 if( separator != 1 || ( record[0] !=
'0' && record[0] !=
'1' ) )
160 THROW_IO_ERROR( wxS(
"OrCAD CIS group membership is malformed" ) );
162 uint32_t occurrence =
163 decimal( record.substr( separator + 1 ), wxS(
"OrCAD CIS membership occurrence ID is invalid" ) );
164 result[occurrence] = record[0] ==
'1';
175 uint32_t recordCount = stream.
ReadU32();
177 for( uint32_t recordIndex = 0; recordIndex < recordCount; ++recordIndex )
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();
184 if( states.size() != groups.size() || propertySetCount != groups.size() )
185 THROW_IO_ERROR( wxS(
"OrCAD CIS schematic group counts do not match" ) );
187 for( uint32_t groupIndex = 0; groupIndex < propertySetCount; ++groupIndex )
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" ) );
194 if( names.size() != values.size() )
195 THROW_IO_ERROR( wxS(
"OrCAD CIS schematic property counts do not match" ) );
199 for(
size_t propertyIndex = 0; propertyIndex < names.size(); ++propertyIndex )
201 if( names[propertyIndex].
empty() )
202 THROW_IO_ERROR( wxS(
"OrCAD CIS schematic property name is empty" ) );
204 properties[names[propertyIndex]] = values[propertyIndex];
211 if( !stream.
AtEnd() )
212 THROW_IO_ERROR( wxS(
"OrCAD CIS schematic stream has trailing data" ) );
219 const std::optional<std::string>& aRequested )
223 if( std::find( aNames.begin(), aNames.end(), *aRequested ) == aNames.end() )
224 THROW_IO_ERROR( wxS(
"The requested OrCAD CIS variant does not exist" ) );
232 return *std::min_element( aNames.begin(), aNames.end() );
The caller owns the buffer.
size_t Remaining() const
Bytes left; 0 when the cursor is at or past the end.
std::string ReadLzt()
– strings ----------------------------------------------------------------—
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)
std::map< std::string, std::map< uint32_t, ORCAD_CIS_PROPERTIES > > ORCAD_CIS_SCHEMATIC_INFO
std::map< std::string, std::string > ORCAD_CIS_PROPERTIES
static std::vector< std::string > split(const std::string &aStr, const std::string &aDelim)
Split the input string into a vector of output strings.
wxString result
Test unit parsing edge cases and error handling.