degate  0.1.2
ProjectImporter.cc
Go to the documentation of this file.
00001 /*
00002 
00003   This file is part of the IC reverse engineering tool degate.
00004 
00005   Copyright 2008, 2009, 2010 by Martin Schobert
00006 
00007   Degate is free software: you can redistribute it and/or modify
00008   it under the terms of the GNU General Public License as published by
00009   the Free Software Foundation, either version 3 of the License, or
00010   any later version.
00011 
00012   Degate is distributed in the hope that it will be useful,
00013   but WITHOUT ANY WARRANTY; without even the implied warranty of
00014   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015   GNU General Public License for more details.
00016 
00017   You should have received a copy of the GNU General Public License
00018   along with degate. If not, see <http://www.gnu.org/licenses/>.
00019 
00020 */
00021 
00022 #include <ProjectImporter.h>
00023 #include <Layer.h>
00024 #include <FileSystem.h>
00025 #include <degate_exceptions.h>
00026 #include <GateLibraryImporter.h>
00027 #include <LogicModelImporter.h>
00028 #include <RCVBlacklistImporter.h>
00029 #include <PortColorManager.h>
00030 #include <Image.h>
00031 #include <LogicModelHelper.h>
00032 #include <ImageHelper.h>
00033 
00034 #include <sys/types.h>
00035 #include <sys/stat.h>
00036 #include <unistd.h>
00037 #include <errno.h>
00038 
00039 #include <string>
00040 #include <iostream>
00041 #include <sstream>
00042 #include <stdexcept>
00043 #include <list>
00044 
00045 #include <boost/format.hpp>
00046 
00047 using namespace std;
00048 using namespace degate;
00049 
00050 std::string ProjectImporter::get_project_filename(std::string const& dir) const {
00051 
00052   if(is_directory(dir))
00053     return join_pathes(dir, "project.xml");
00054   else
00055     return dir;
00056 }
00057 
00058 Project_shptr ProjectImporter::import_all(std::string const& directory) {
00059   Project_shptr prj = import(directory);
00060 
00061   if(prj != NULL) {
00062 
00063       GateLibraryImporter gl_importer;
00064 
00065       GateLibrary_shptr gate_lib;
00066 
00067       std::string gate_lib_file(get_basedir(directory) + "/gate_library.xml");
00068       std::string rcbl_file(get_basedir(directory) + "/rc_blacklist.xml");
00069 
00070       if(file_exists(gate_lib_file))
00071         gate_lib = gl_importer.import(gate_lib_file);
00072       else gate_lib = GateLibrary_shptr(new GateLibrary());
00073 
00074       LogicModelImporter lm_importer(prj->get_width(), prj->get_height(), gate_lib);
00075 
00076       lm_importer.import_into(prj->get_logic_model(),
00077                               get_basedir(directory) + "/lmodel.xml");
00078 
00079       LogicModel_shptr lmodel = prj->get_logic_model();
00080       lmodel->set_default_gate_port_diameter(prj->get_default_port_diameter());
00081 
00082       if(file_exists(rcbl_file)) {
00083         RCVBlacklistImporter rcvbl_importer(lmodel);
00084         rcvbl_importer.import_into(rcbl_file, prj->get_rcv_blacklist());
00085       }
00086       
00087       /*
00088         For degate projects that were exported with degate 0.0.6 the gate templates
00089         were expressed in terms of an image region. This is bad. Here is a part of the fix:
00090         We have loaded the project with the background images and we have the gate
00091         library. We iterate over the gate library, extract the template image from the
00092         background image and put it into the gate library. We do it for the first
00093         transistor, the first logic and the first metal layer.
00094       */
00095 
00096       debug(TM, "Check if we have template images.");
00097       for(GateLibrary::template_iterator iter = gate_lib->begin();
00098           iter != gate_lib->end(); ++iter) {
00099 
00100         debug(TM, "Will grab template image for gate template ID: %d", iter->first);
00101         GateTemplate_shptr tmpl = iter->second;
00102         assert(tmpl != NULL);
00103 
00104         BoundingBox const& bbox = tmpl->get_bounding_box();
00105         if(bbox.get_min_x() != 0 && bbox.get_min_y() != 0 &&
00106            bbox.get_max_x() != 0 && bbox.get_max_y() != 0) { // a heuristic
00107           debug(TM, "Grab template images from the background for template %s.", tmpl->get_name().c_str());
00108           grab_template_images(lmodel, tmpl, bbox);
00109         }
00110 
00111       }
00112       debug(TM, "Project loaded.");
00113       //prj->print_all(cout);
00114 
00115   }
00116 
00117   return prj;
00118 }
00119 
00120 Project_shptr ProjectImporter::import(std::string const& directory)  {
00121 
00122   string filename = get_project_filename(directory);
00123   if(RET_IS_NOT_OK(check_file(filename))) {
00124     debug(TM, "Problem: file %s not found.", filename.c_str());
00125     throw InvalidPathException("The ProjectImporter cannot load the project file. File does not exists.");
00126   }
00127 
00128   try {
00129 
00130     xmlpp::DomParser parser;
00131     parser.set_substitute_entities(); // We just want the text to be resolved/unescaped automatically.
00132 
00133     parser.parse_file(filename);
00134     assert(parser == true);
00135 
00136     const xmlpp::Document * doc = parser.get_document();
00137     assert(doc != NULL);
00138 
00139     const xmlpp::Element * root_elem = doc->get_root_node(); // deleted by DomParser
00140     assert(root_elem != NULL);
00141 
00142     // parse width and height
00143     int w = parse_number<length_t>(root_elem, "width");
00144     int h = parse_number<length_t>(root_elem, "height");
00145 
00146     Project_shptr prj(new Project(w, h, get_basedir(filename)));
00147     assert(prj->get_project_directory().length() != 0);
00148 
00149     parse_project_element(prj, root_elem);
00150 
00151     return prj;
00152   }
00153   catch(const std::exception& ex) {
00154     std::cout << "Exception caught: " << ex.what() << std::endl;
00155     throw InvalidXMLException(ex.what());
00156   }
00157 }
00158 
00159 
00160 void ProjectImporter::parse_layers_element(const xmlpp::Element * const layers_elem, Project_shptr prj) {
00161   debug(TM, "parsing layers");
00162 
00163   const xmlpp::Node::NodeList layer_list = layers_elem->get_children("layer");
00164   for(xmlpp::Node::NodeList::const_iterator iter = layer_list.begin();
00165       iter != layer_list.end();
00166       ++iter) {
00167 
00168     if(const xmlpp::Element* layer_elem = dynamic_cast<const xmlpp::Element*>(*iter)) {
00169 
00170       const std::string image_filename(layer_elem->get_attribute_value("image-filename"));
00171       const std::string layer_type_str(layer_elem->get_attribute_value("type"));
00172       const std::string layer_description(layer_elem->get_attribute_value("description"));
00173       unsigned int position = parse_number<unsigned int>(layer_elem, "position");
00174       const std::string layer_enabled_str = layer_elem->get_attribute_value("enabled");
00175 
00176       Layer::LAYER_TYPE layer_type = Layer::get_layer_type_from_string(layer_type_str);
00177       layer_id_t layer_id = parse_number<layer_id_t>(layer_elem, "id", 0);
00178 
00179       Layer_shptr new_layer(new Layer(prj->get_bounding_box(), layer_type));
00180       LogicModel_shptr lmodel = prj->get_logic_model();
00181 
00182       debug(TM, "Parsed a layer entry for type %s. This is a %s layer. Background image is %s",
00183             layer_type_str.c_str(),
00184             Layer::get_layer_type_as_string(layer_type).c_str(),
00185             image_filename.c_str());
00186 
00187       bool layer_enabled = true;
00188       if(layer_enabled_str.size() != 0)
00189         layer_enabled = parse_bool(layer_enabled_str);
00190       new_layer->set_enabled(layer_enabled);
00191 
00192       new_layer->set_description(layer_description);
00193       new_layer->set_layer_id(layer_id);
00194 
00195       lmodel->add_layer(position, new_layer);
00196 
00197       load_background_image(new_layer, image_filename, prj);
00198 
00199     }
00200   }
00201 }
00202 
00203 void ProjectImporter::load_background_image(Layer_shptr layer,
00204                                             std::string const& image_filename,
00205                                             Project_shptr prj) {
00206 
00207   debug(TM, "try to load image [%s]", image_filename.c_str());
00208   if(!image_filename.empty()) {
00209 
00210     assert(prj->get_project_directory().length() != 0);
00211 
00212     std::string image_path_to_load = join_pathes(prj->get_project_directory(), image_filename);
00213 
00214     debug(TM, "try to load image [%s]", image_path_to_load.c_str());
00215 
00216     if(is_directory(image_path_to_load)) { // new background image format
00217 
00218       debug(TM, "project importer loads an tile based image from [%s]", image_path_to_load.c_str());
00219 
00220       BackgroundImage_shptr bg_image =
00221         load_degate_image<BackgroundImage>(prj->get_width(),
00222                                            prj->get_height(),
00223                                            image_path_to_load);
00224 
00225       if(bg_image == NULL)
00226         throw DegateRuntimeException("Failed to load the background image");
00227 
00228       debug(TM, "Loading done.");
00229       layer->set_image(bg_image);
00230     }
00231     else if(is_file(image_path_to_load)) { // old image background format
00232 
00233       // determine where we can store the new backgound image
00234       boost::format fmter("layer_%1%.dimg");
00235       /* We convert old degate background images to the new tile based format.
00236          Therefore we need directory names. The directory name should not reflect
00237          a layer position number, because it it possible that the layers become
00238          reorderd later. We do not want to rename the directories in that case.
00239          To avoid, that a user thinks the directory name reflects a layer position,
00240          we just add a number. */
00241       fmter % (layer->get_layer_pos() + 0x2342);
00242       std::string new_dir(join_pathes(prj->get_project_directory(), fmter.str()));
00243 
00244       debug(TM, "project importer loads an old single file based image from [%s]",
00245             image_path_to_load.c_str());
00246 
00247       if(!file_exists(new_dir) && !is_directory(new_dir)) { // we have to check this, before we call the constructor
00248 
00249         // create new background image
00250         BackgroundImage_shptr new_bg_image(new BackgroundImage(prj->get_width(),
00251                                                                prj->get_height(),
00252                                                                new_dir));
00253 
00254         // load old single file image
00255         PersistentImage_RGBA_shptr old_bg_image =
00256           load_degate_image<PersistentImage_RGBA>(prj->get_width(),
00257                                                   prj->get_height(),
00258                                                   image_path_to_load);
00259 
00260         if(new_bg_image == NULL)
00261           throw DegateRuntimeException("Failed to load the background image");
00262 
00263         // convert image into new format
00264 
00265         debug(TM, "Copy the image into a new format. The data is stored in directory %s",
00266               new_dir.c_str());
00267 
00268         copy_image(new_bg_image, old_bg_image);
00269 
00270         layer->set_image(new_bg_image);
00271 
00272       }
00273       else {
00274 
00275         debug(TM,
00276               "There is already a directory named %s. It should be loaded as an image now.",
00277               new_dir.c_str());
00278 
00279         BackgroundImage_shptr new_bg_image(new BackgroundImage(prj->get_width(),
00280                                                                prj->get_height(),
00281                                                                new_dir));
00282 
00283         layer->set_image(new_bg_image);
00284       }
00285 
00286 
00287     }
00288 
00289   }
00290   else {
00291     debug(TM, "project in %s has no layer image defined for a layer.",
00292           prj->get_project_directory().c_str());
00293   }
00294 }
00295 
00296 
00297 void ProjectImporter::parse_port_colors_element(const xmlpp::Element * const port_colors_elem, Project_shptr prj) {
00298 
00299   const xmlpp::Node::NodeList color_list = port_colors_elem->get_children("port-color");
00300 
00301   PortColorManager_shptr port_color_manager = prj->get_port_color_manager();
00302 
00303   for(xmlpp::Node::NodeList::const_iterator iter = color_list.begin();
00304       iter != color_list.end();
00305       ++iter) {
00306 
00307     if(const xmlpp::Element* color_elem = dynamic_cast<const xmlpp::Element*>(*iter)) {
00308 
00309       const std::string port_name(color_elem->get_attribute_value("port-name"));
00310       const std::string fill_color_str(color_elem->get_attribute_value("fill-color"));
00311       const std::string frame_color_str(color_elem->get_attribute_value("frame-color"));
00312 
00313       port_color_manager->set_color(port_name,
00314                                     parse_color_string(frame_color_str),
00315                                     parse_color_string(fill_color_str) );
00316 
00317 
00318     }
00319   }
00320 
00321 }
00322 
00323 void ProjectImporter::parse_colors_element(const xmlpp::Element * const port_colors_elem,
00324                                            Project_shptr prj) {
00325 
00326   const xmlpp::Node::NodeList color_list = port_colors_elem->get_children("color");
00327 
00328   for(xmlpp::Node::NodeList::const_iterator iter = color_list.begin();
00329       iter != color_list.end();
00330       ++iter) {
00331 
00332     if(const xmlpp::Element* color_elem = dynamic_cast<const xmlpp::Element*>(*iter)) {
00333 
00334       const std::string object_name(color_elem->get_attribute_value("object"));
00335       const std::string color_str(color_elem->get_attribute_value("color"));
00336       ENTITY_COLOR o;
00337 
00338       if(!object_name.compare("wire")) o = DEFAULT_COLOR_WIRE;
00339       else if(!object_name.compare("via-up")) o = DEFAULT_COLOR_VIA_UP;
00340       else if(!object_name.compare("via-down")) o = DEFAULT_COLOR_VIA_DOWN;
00341       else if(!object_name.compare("grid")) o = DEFAULT_COLOR_GRID;
00342       else if(!object_name.compare("annotation")) o = DEFAULT_COLOR_ANNOTATION;
00343       else if(!object_name.compare("annotation-frame")) o = DEFAULT_COLOR_ANNOTATION_FRAME;
00344       else if(!object_name.compare("gate")) o = DEFAULT_COLOR_GATE;
00345       else if(!object_name.compare("gate-frame")) o = DEFAULT_COLOR_GATE_FRAME;
00346       else if(!object_name.compare("gate-port")) o = DEFAULT_COLOR_GATE_PORT;
00347       else if(!object_name.compare("text")) o = DEFAULT_COLOR_TEXT;
00348       else if(!object_name.compare("emarker")) o = DEFAULT_COLOR_EMARKER;
00349       else {
00350         boost::format f("Can't parse object type. '%1%'");
00351         f % object_name;
00352         throw XMLAttributeParseException(f.str());
00353       }
00354       prj->set_default_color(o, parse_color_string(color_str));
00355 
00356     }
00357   }
00358 
00359 }
00360 
00361 void ProjectImporter::parse_grids_element(const xmlpp::Element * const grids_elem, Project_shptr prj) {
00362 
00363   xmlpp::Node::NodeList::const_iterator iter;
00364 
00365   const xmlpp::Node::NodeList regular_grid_list = grids_elem->get_children("regular-grid");
00366   const xmlpp::Node::NodeList irregular_grid_list = grids_elem->get_children("irregular-grid");
00367 
00368 
00369   for(iter = regular_grid_list.begin(); iter != regular_grid_list.end(); ++iter) {
00370     if(const xmlpp::Element* regular_grid_elem = dynamic_cast<const xmlpp::Element*>(*iter)) {
00371 
00372       const Glib::ustring orientation(regular_grid_elem->get_attribute_value("orientation"));
00373 
00374       RegularGrid_shptr reg_grid = (orientation == "horizontal") ?
00375         prj->get_regular_horizontal_grid() : prj->get_regular_vertical_grid();
00376 
00377       reg_grid->set_distance(parse_number<unsigned int>(regular_grid_elem, "distance", 0));
00378       reg_grid->set_enabled(parse_bool(regular_grid_elem->get_attribute_value("enabled")));
00379     }
00380   }
00381 
00382   for(iter = irregular_grid_list.begin(); iter != irregular_grid_list.end(); ++iter) {
00383     if(const xmlpp::Element* irregular_grid_elem = dynamic_cast<const xmlpp::Element*>(*iter)) {
00384 
00385       const Glib::ustring orientation(irregular_grid_elem->get_attribute_value("orientation"));
00386 
00387       IrregularGrid_shptr irreg_grid = (orientation == "horizontal") ?
00388         prj->get_irregular_horizontal_grid() : prj->get_irregular_vertical_grid();
00389 
00390       irreg_grid->set_enabled(parse_bool(irregular_grid_elem->get_attribute_value("enabled")));
00391 
00392       const xmlpp::Node::NodeList offsets_entry_list = irregular_grid_elem->get_children("offsets");
00393       const xmlpp::Node::NodeList::const_iterator offsets_iter = offsets_entry_list.begin();
00394       if(offsets_iter != offsets_entry_list.end()) {
00395         const xmlpp::Element* offsets_elem = dynamic_cast<const xmlpp::Element*>(*offsets_iter);
00396         if(offsets_elem != NULL) {
00397 
00398           const xmlpp::Node::NodeList offset_entry_list = offsets_elem->get_children("offset-entry");
00399           for(xmlpp::Node::NodeList::const_iterator offs_iter = offset_entry_list.begin();
00400               offs_iter != offset_entry_list.end();
00401               ++offs_iter) {
00402             if(const xmlpp::Element* offset_entry_elem = dynamic_cast<const xmlpp::Element*>(*offs_iter)) {
00403               irreg_grid->add_offset(parse_number<int>(offset_entry_elem, "offset"));
00404             }
00405           }
00406         }
00407       }
00408     }
00409   }
00410 }
00411 
00412 
00413 
00414 void ProjectImporter::parse_project_element(Project_shptr parent_prj,
00415                                             const xmlpp::Element * const project_elem) {
00416 
00417   int w = parent_prj->get_width();
00418   int h = parent_prj->get_height();
00419 
00420   // Use geometry information to set up regular grid ranges.
00421   // The RegularGrid implementation might be changed in order to avoid this setup.
00422   RegularGrid_shptr reg_vert_grid = parent_prj->get_regular_vertical_grid();
00423   RegularGrid_shptr reg_hor_grid = parent_prj->get_regular_horizontal_grid();
00424   reg_vert_grid->set_range(0, w);
00425   reg_hor_grid->set_range(0, h);
00426 
00427   parent_prj->set_degate_version(project_elem->get_attribute_value("degate-version"));
00428   parent_prj->set_name(project_elem->get_attribute_value("name"));
00429   parent_prj->set_description(project_elem->get_attribute_value("description"));
00430 
00431   if(!project_elem->get_attribute_value("server-url").empty())
00432     parent_prj->set_server_url(project_elem->get_attribute_value("server-url"));
00433 
00434   if(!project_elem->get_attribute_value("last-pulled-transaction-id").empty())
00435     parent_prj->set_last_pulled_tid(parse_number<transaction_id_t>(project_elem,
00436                                                                    "last-pulled-transaction-id"));
00437 
00438   parent_prj->set_lambda(parse_number<length_t>(project_elem, "lambda"));
00439   parent_prj->set_default_pin_diameter(parse_number<diameter_t>(project_elem, "pin-diameter"));
00440   parent_prj->set_default_wire_diameter(parse_number<diameter_t>(project_elem, "wire-diameter"));
00441   parent_prj->set_default_port_diameter(parse_number<diameter_t>(project_elem, "port-diameter", 5));
00442 
00443   parent_prj->set_pixel_per_um(parse_number<double>(project_elem, "pixel-per-um", 0));
00444   parent_prj->set_template_dimension(parse_number<int>(project_elem, "template-dimension", 0));
00445   parent_prj->set_font_size(parse_number<unsigned int>(project_elem, "font-size", 10));
00446 
00447   const xmlpp::Element * e = get_dom_twig(project_elem, "grids");
00448   if(e != NULL) parse_grids_element(e, parent_prj);
00449 
00450   e = get_dom_twig(project_elem, "layers");
00451   if(e != NULL) parse_layers_element(e, parent_prj);
00452 
00453   e = get_dom_twig(project_elem, "port-colors");
00454   if(e != NULL) parse_port_colors_element(e, parent_prj);
00455 
00456   e = get_dom_twig(project_elem, "default-colors");
00457   if(e != NULL) parse_colors_element(e, parent_prj);
00458 
00459 }