/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2023 Alex Shvartzkop * Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include "easyedapro_import_utils.h" #include "easyedapro_parser.h" #include #include #include #include #include #include #include #include #include #include #include wxString EASYEDAPRO::ShortenLibName( wxString aProjectName ) { wxString shortenedName = aProjectName; shortenedName.Replace( wxS( "ProProject_" ), wxS( "" ) ); shortenedName.Replace( wxS( "ProDocument_" ), wxS( "" ) ); shortenedName = shortenedName.substr( 0, 10 ); return LIB_ID::FixIllegalChars( shortenedName + wxS( "-easyedapro" ), true ); } LIB_ID EASYEDAPRO::ToKiCadLibID( const wxString& aLibName, const wxString& aLibReference ) { wxString libName = LIB_ID::FixIllegalChars( aLibName, true ); wxString libReference = EscapeString( aLibReference, CTX_LIBID ); wxString key = !aLibName.empty() ? ( aLibName + ':' + libReference ) : libReference; LIB_ID libId; libId.Parse( key, true ); return libId; } std::vector EASYEDAPRO::ProjectToSelectorDialog( const nlohmann::json& aProject, bool aPcbOnly, bool aSchOnly ) { std::vector result; std::map prjSchematics = aProject.at( "schematics" ); std::map prjBoards = aProject.at( "boards" ); std::map prjPcbNames = aProject.at( "pcbs" ); for( const auto& [prjName, board] : prjBoards ) { IMPORT_PROJECT_DESC desc; desc.ComboName = desc.ComboId = prjName; desc.PCBId = board.pcb; desc.SchematicId = board.schematic; auto pcbNameIt = prjPcbNames.find( desc.PCBId ); if( pcbNameIt != prjPcbNames.end() ) { desc.PCBName = pcbNameIt->second; if( desc.PCBName.empty() ) desc.PCBName = pcbNameIt->first; prjPcbNames.erase( pcbNameIt ); } auto schIt = prjSchematics.find( desc.SchematicId ); if( schIt != prjSchematics.end() ) { desc.SchematicName = schIt->second.name; if( desc.SchematicName.empty() ) desc.SchematicName = schIt->first; prjSchematics.erase( schIt ); } result.emplace_back( desc ); } if( !aSchOnly ) { for( const auto& [pcbId, pcbName] : prjPcbNames ) { IMPORT_PROJECT_DESC desc; desc.PCBId = pcbId; desc.PCBName = pcbName; if( desc.PCBName.empty() ) desc.PCBName = pcbId; result.emplace_back( desc ); } } if( !aPcbOnly ) { for( const auto& [schId, schData] : prjSchematics ) { IMPORT_PROJECT_DESC desc; desc.SchematicId = schId; desc.SchematicName = schData.name; if( desc.SchematicName.empty() ) desc.SchematicName = schId; result.emplace_back( desc ); } } return result; } nlohmann::json EASYEDAPRO::FindJsonFile( const wxString& aZipFileName, const std::set& aFileNames ) { std::shared_ptr entry; wxFFileInputStream in( aZipFileName ); wxZipInputStream zip( in ); while( entry.reset( zip.GetNextEntry() ), entry.get() != NULL ) { wxString name = entry->GetName(); try { if( aFileNames.find( name ) != aFileNames.end() ) { wxMemoryOutputStream memos; memos << zip; wxStreamBuffer* buf = memos.GetOutputStreamBuffer(); wxString str = wxString::FromUTF8( (char*) buf->GetBufferStart(), buf->GetBufferSize() ); return nlohmann::json::parse( str ); } } catch( nlohmann::json::exception& e ) { THROW_IO_ERROR( wxString::Format( _( "JSON error reading '%s': %s" ), name, e.what() ) ); } catch( std::exception& e ) { THROW_IO_ERROR( wxString::Format( _( "Error reading '%s': %s" ), name, e.what() ) ); } } return nlohmann::json{}; } nlohmann::json EASYEDAPRO::ReadProjectOrDeviceFile( const wxString& aZipFileName ) { static const std::set c_files = { wxS( "project.json" ), wxS( "device.json" ), wxS( "footprint.json" ), wxS( "symbol.json" ) }; nlohmann::json j = FindJsonFile( aZipFileName, c_files ); if( !j.is_null() ) return j; THROW_IO_ERROR( wxString::Format( _( "'%s' does not appear to be a valid EasyEDA (JLCEDA) Pro " "project or library file. Cannot find project.json or device.json." ), aZipFileName ) ); } void EASYEDAPRO::IterateZipFiles( const wxString& aFileName, std::function aCallback ) { std::shared_ptr entry; wxFFileInputStream in( aFileName ); wxZipInputStream zip( in ); if( !zip.IsOk() ) { THROW_IO_ERROR( wxString::Format( _( "Cannot read ZIP archive '%s'" ), aFileName ) ); } while( entry.reset( zip.GetNextEntry() ), entry.get() != NULL ) { wxString name = entry->GetName(); wxString baseName = name.AfterLast( '\\' ).AfterLast( '/' ).BeforeFirst( '.' ); try { if( aCallback( name, baseName, zip ) ) break; } catch( nlohmann::json::exception& e ) { THROW_IO_ERROR( wxString::Format( _( "JSON error reading '%s': %s" ), name, e.what() ) ); } catch( std::exception& e ) { THROW_IO_ERROR( wxString::Format( _( "Error reading '%s': %s" ), name, e.what() ) ); } } } std::vector EASYEDAPRO::ParseJsonLines( wxInputStream& aInput, const wxString& aSource ) { wxTextInputStream txt( aInput, wxS( " " ), wxConvUTF8 ); int currentLine = 1; std::vector lines; while( aInput.CanRead() ) { try { nlohmann::json js = nlohmann::json::parse( txt.ReadLine() ); lines.emplace_back( js ); } catch( nlohmann::json::exception& e ) { wxLogWarning( wxString::Format( _( "Cannot parse JSON line %d in '%s': %s" ), currentLine, aSource, e.what() ) ); } currentLine++; } return lines; }