├── .gitignore ├── README.md ├── doc └── MSDN_CFB_Format_Spec.pdf ├── inc ├── altium_lib.hpp └── cfb_types.hpp ├── src ├── altium_lib.cpp └── cfb_types.cpp └── util ├── Makefile ├── README.md ├── inc ├── direntries.hpp └── print_header_info.hpp └── src ├── direntries.cpp ├── libload.cpp └── print_header_info.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | #Altium Libraries 2 | *.SchLib 3 | *.PcbLib 4 | 5 | #Compilation Output 6 | bin/* 7 | util/bin/* 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Altium Library Parsing 2 | 3 | Altium Designer stores schematic symbol and footprint libraries in binary file formats (.SchLib and .PcbLib, respectively.) Parsing these files should allow for an external program to determine the contents of the files (i.e., names of symbols and footprints) and render them graphically. 4 | 5 | The current implementation is in C++ with the end goal of being part of a larger Qt-based front-end for a parts database. 6 | 7 | -------------------------------------------------------------------------------- /doc/MSDN_CFB_Format_Spec.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fierst/AltiumLibParser/1a253ac1a5e09b9df9c1aa88da124879fe050124/doc/MSDN_CFB_Format_Spec.pdf -------------------------------------------------------------------------------- /inc/altium_lib.hpp: -------------------------------------------------------------------------------- 1 | #include "cfb_types.hpp" 2 | 3 | #include "boost/filesystem.hpp" 4 | #include "boost/filesystem/fstream.hpp" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace fs = boost::filesystem; 12 | 13 | class altium_lib 14 | { 15 | 16 | public: 17 | 18 | altium_lib(void); 19 | altium_lib(const std::string & path_to_file); 20 | 21 | // load the lib file from the specified path (or the default path) 22 | // returns whether or not it was successful. 23 | bool load_from_file(const std::string & path_to_file = std::string()); 24 | 25 | // get all of the library entries (symbol names or footprint names) from the library file 26 | // returns whether or not there were any. 27 | bool get_library_entries(std::vector & list_of_entries); 28 | 29 | private: 30 | 31 | void build_header(std::ifstream &cfb_file); 32 | void build_sat(std::ifstream &cfb_file); 33 | void build_dir_entry_chain(std::ifstream &cfb_file); 34 | void read_next_directory_entry(std::ifstream &cfb_file); 35 | 36 | std::string filepath_; 37 | 38 | // The CFB header. Shouldn't be directly accessible from outside this class 39 | header_t header_; 40 | 41 | // Sector allocation table/chain, for building the rest of the file. 42 | std::vector sector_alloc_table; 43 | 44 | // Keywords used by Altium for the library, that are *not* symbol/footprint names 45 | // TODO: There must be a better way to figure out which are keywords and which are symbols/footprints... 46 | std::vector altium_lib_keywords {"Library", "Models", "ModelsNoEmbed", 47 | "Textures", "ComponentParamsTOC", "UniqueIDPrimitiveInformation", 48 | "FileVersionInfo", "PadViaLibrary", "ExtendedPrimitiveInformation"}; 49 | 50 | // TODO: Convert to a red-black tree? That's how the CFB spec handles it... 51 | std::vector dir_entry_tree; 52 | 53 | }; 54 | -------------------------------------------------------------------------------- /inc/cfb_types.hpp: -------------------------------------------------------------------------------- 1 | // Types inherent to the CFB file format 2 | // All of these should be outlined in doc/MSDN_CFB_Format_Spec.pdf 3 | // Or at https://msdn.microsoft.com/en-us/library/dd942138.aspx 4 | #ifndef CFB_TYPES_H 5 | #define CFB_TYPES_H 6 | 7 | #include 8 | #include 9 | 10 | #include "boost/locale.hpp" 11 | 12 | // Sector ID Constants 13 | const int32_t MAXREGSECT = 0xFFFFFFFA; 14 | const int32_t DIFSECT = 0xFFFFFFFC; 15 | const int32_t FATSECT = 0xFFFFFFFD; 16 | const int32_t ENDOFCHAIN = 0xFFFFFFFE; 17 | const int32_t FREESECT = 0xFFFFFFFF; 18 | 19 | // Directory Entry Color Flags 20 | const uint8_t CF_RED = 0x00; 21 | const uint8_t CF_BLACK = 0x01; 22 | 23 | // Directory Entry IDs 24 | const int32_t MAXREGSID = 0xFFFFFFFA; 25 | const int32_t NOSTREAM = 0xFFFFFFFF; 26 | 27 | // Directory Entry Object Types 28 | const uint8_t OT_UNKNOWN = 0x00; 29 | const uint8_t OT_STORAGE = 0x01; 30 | const uint8_t OT_STREAM = 0x02; 31 | const uint8_t OT_ROOT = 0x05; 32 | 33 | // Contains map of structure for the rest of the file 34 | // Since the Altium libraries are generally small, there's not a lot to worry about 35 | #pragma pack(push, 1) 36 | struct header_t 37 | { 38 | uint64_t magic; 39 | uint8_t uid[16]; 40 | uint16_t version_number[2]; 41 | uint16_t byte_order; 42 | uint16_t sec_size; 43 | uint16_t short_sec_size; 44 | uint8_t spare1[10]; 45 | uint32_t sec_count; 46 | uint32_t first_sec_id; 47 | uint32_t spare2; 48 | uint32_t min_std_stream_size; 49 | uint32_t first_short_sec_id; 50 | uint32_t short_sec_count; 51 | uint32_t first_master_sec_id; 52 | uint32_t master_sec_count; 53 | uint32_t master_sec_ids[109]; 54 | }; 55 | #pragma pack(pop) 56 | 57 | // Each Directory Entry is 128 bytes in length 58 | const size_t CFB_DIR_ENTRY_SIZE = 128; 59 | 60 | enum dir_entry_object_type 61 | { 62 | UNKNOWN = 0, 63 | STORAGE, 64 | STREAM, 65 | ROOT = 5 66 | }; 67 | 68 | // Directory Entry class 69 | // Each directory entry is part of a red-black tree 70 | // Currently only implemented methods required for the application 71 | class directory_entry 72 | { 73 | public: 74 | directory_entry(); 75 | directory_entry(const directory_entry &); 76 | directory_entry(uint8_t * dir_entry_bytes, size_t bytes_to_read); 77 | 78 | // Assign the directory entry bytes post-instantiation 79 | void assign_bytes(uint8_t * dir_entry_bytes, size_t bytes_to_read); 80 | 81 | // Return name string 82 | std::string get_name(); 83 | 84 | // Return creation time in a human-readable string (YYYY-MM-DD hh:mm:ss) 85 | // TODO: Allow format specifiers? 86 | std::string get_creation_time(); 87 | 88 | // Return modification time in a human-readable string (YYYY-MM-DD hh:mm:ss) 89 | // TODO: Allow format specifiers? 90 | std::string get_modified_time(); 91 | 92 | // Get the object type of the directory entry 93 | dir_entry_object_type get_object_type(); 94 | 95 | // Return the sector id of the stream 96 | uint32_t get_stream_sector_id(); 97 | 98 | // Return the size of the stream 99 | uint32_t get_stream_size(); 100 | 101 | private: 102 | 103 | std::valarray get_dir_entry_param_bytes(std::string param_key); 104 | 105 | // Container for the raw bytes of the directory entry 106 | std::valarray dir_entry_bytes; 107 | 108 | // Map of parameters for the directory entry 109 | // Key - Name of the requested parameter 110 | // Value - std::pair with the offset and number of bytes for the requested parameter 111 | std::map> dir_entry_params 112 | { 113 | std::make_pair("name", std::make_pair(0, 64)), 114 | std::make_pair("name_length", std::make_pair(64, 2)), 115 | std::make_pair("type", std::make_pair(66, 1)), 116 | std::make_pair("color", std::make_pair(67, 1)), 117 | std::make_pair("l_sibling_id", std::make_pair(68, 4)), 118 | std::make_pair("r_sibling_id", std::make_pair(72, 4)), 119 | std::make_pair("child_id", std::make_pair(76, 4)), 120 | std::make_pair("clsid", std::make_pair(80, 16)), 121 | std::make_pair("user_flags", std::make_pair(96, 4)), 122 | std::make_pair("time_created", std::make_pair(100, 8)), 123 | std::make_pair("time_modified", std::make_pair(108, 8)), 124 | std::make_pair("stream_sec_id", std::make_pair(116, 4)), 125 | std::make_pair("stream_size", std::make_pair(120, 4)), 126 | std::make_pair("spare", std::make_pair(124, 4)) 127 | }; 128 | }; 129 | 130 | #endif // CFB_TYPES_H -------------------------------------------------------------------------------- /src/altium_lib.cpp: -------------------------------------------------------------------------------- 1 | #include "altium_lib.hpp" 2 | 3 | altium_lib::altium_lib(void) 4 | { 5 | 6 | } 7 | 8 | altium_lib::altium_lib(const std::string & path_to_file) 9 | { 10 | this->filepath_ = path_to_file; 11 | } 12 | 13 | bool altium_lib::load_from_file(const std::string & path_to_file) 14 | { 15 | std::string file_to_load; 16 | 17 | if(path_to_file.empty()) 18 | { 19 | if(filepath_.empty()) 20 | { 21 | return false; 22 | } 23 | else 24 | { 25 | file_to_load = filepath_; 26 | } 27 | } 28 | else 29 | { 30 | file_to_load = path_to_file; 31 | } 32 | 33 | // Create the file stream object 34 | fs::ifstream ifs; 35 | 36 | // Path to the library, specified by user at the command line 37 | fs::path libfile(file_to_load); 38 | 39 | // Check to make sure it's actually a valid file 40 | if(exists(libfile)) 41 | { 42 | if(is_directory(libfile)) 43 | { 44 | return false; 45 | } 46 | } 47 | else 48 | { 49 | return false; 50 | } 51 | 52 | ifs.open(libfile, std::ios::binary | std::ios::ate); 53 | 54 | // Seek to the beginning of the file 55 | ifs.seekg(0, std::ios::beg); 56 | 57 | // Build the header 58 | build_header(ifs); 59 | 60 | // build the sector allocation table 61 | build_sat(ifs); 62 | 63 | build_dir_entry_chain(ifs); 64 | 65 | ifs.close(); 66 | 67 | return true; 68 | } 69 | 70 | bool altium_lib::get_library_entries(std::vector & list_of_entries) 71 | { 72 | // Clear the entries vector and set its size to zero 73 | list_of_entries.erase(list_of_entries.begin(), list_of_entries.end()); 74 | 75 | std::string current_name; 76 | bool is_keyword; 77 | 78 | for(size_t i = 0; i < dir_entry_tree.size(); i++) 79 | { 80 | current_name = dir_entry_tree.at(i).get_name(); 81 | is_keyword = (std::find(altium_lib_keywords.begin(), altium_lib_keywords.end(), current_name) != altium_lib_keywords.end()); 82 | 83 | if( dir_entry_tree.at(i).get_object_type() == dir_entry_object_type::STORAGE && !is_keyword) 84 | { 85 | list_of_entries.push_back(current_name); 86 | } 87 | } 88 | 89 | // Return true if there are any entries to read 90 | return (!list_of_entries.empty()); 91 | } 92 | 93 | void altium_lib::build_header(std::ifstream &cfb_file) 94 | { 95 | cfb_file.read(reinterpret_cast(&header_), sizeof(header_t)); 96 | } 97 | 98 | void altium_lib::build_sat(std::ifstream &cfb_file) 99 | { 100 | uint32_t offset; 101 | 102 | uint32_t sector_size = (1 << header_.sec_size); 103 | 104 | int32_t sector_id; 105 | 106 | for(uint32_t i = 0; i < header_.sec_count; i++) 107 | { 108 | offset = (1 + header_.master_sec_ids[i]) * (sector_size); 109 | cfb_file.seekg(offset, std::ios::beg); 110 | for(uint32_t j = 0; j < sector_size / sizeof(uint32_t); j++) 111 | { 112 | cfb_file.read(reinterpret_cast(§or_id), sizeof(int32_t)); 113 | sector_alloc_table.push_back(sector_id); 114 | } 115 | } 116 | } 117 | 118 | void altium_lib::build_dir_entry_chain(std::ifstream &cfb_file) 119 | { 120 | uint32_t offset = 0; 121 | 122 | int32_t current_sector = header_.first_sec_id; 123 | 124 | uint32_t sector_size = (1 << header_.sec_size); 125 | 126 | uint32_t dir_entries_per_sector = sector_size / (CFB_DIR_ENTRY_SIZE); 127 | 128 | while(current_sector != ENDOFCHAIN) 129 | { 130 | offset = (1 + current_sector) * (sector_size); 131 | 132 | cfb_file.seekg(offset, std::ios::beg); 133 | 134 | for(uint8_t i = 0; i < dir_entries_per_sector; i++) 135 | { 136 | read_next_directory_entry(cfb_file); 137 | } 138 | // Move to the next sector containing directory entries 139 | current_sector = sector_alloc_table.at(current_sector); 140 | } 141 | } 142 | 143 | void altium_lib::read_next_directory_entry(std::ifstream &cfb_file) 144 | { 145 | uint8_t dir_entry_bytes[CFB_DIR_ENTRY_SIZE]; 146 | 147 | cfb_file.read(reinterpret_cast(&dir_entry_bytes), CFB_DIR_ENTRY_SIZE); 148 | 149 | directory_entry new_dir_entry(dir_entry_bytes, sizeof(dir_entry_bytes)); 150 | 151 | dir_entry_tree.push_back(new_dir_entry); 152 | } -------------------------------------------------------------------------------- /src/cfb_types.cpp: -------------------------------------------------------------------------------- 1 | #include "cfb_types.hpp" 2 | 3 | directory_entry::directory_entry() 4 | { 5 | this->dir_entry_bytes.resize(CFB_DIR_ENTRY_SIZE); 6 | } 7 | 8 | directory_entry::directory_entry(const directory_entry & to_copy) 9 | { 10 | this->dir_entry_bytes.resize(CFB_DIR_ENTRY_SIZE); 11 | this->dir_entry_bytes = to_copy.dir_entry_bytes; 12 | } 13 | 14 | directory_entry::directory_entry(uint8_t * dir_entry_bytes, size_t bytes_to_read) 15 | { 16 | this->dir_entry_bytes.resize(CFB_DIR_ENTRY_SIZE); 17 | this->assign_bytes(dir_entry_bytes, bytes_to_read); 18 | } 19 | 20 | void directory_entry::assign_bytes(uint8_t * dir_entry_bytes, size_t bytes_to_read) 21 | { 22 | this->dir_entry_bytes = std::valarray(dir_entry_bytes, bytes_to_read); 23 | } 24 | 25 | std::string directory_entry::get_name() 26 | { 27 | std::string name_string = ""; 28 | 29 | // Find "name" entry in map 30 | std::valarray name_bytes = get_dir_entry_param_bytes("name"); 31 | 32 | // Get the length of the name string and trim the last char16_t because it's NULL 33 | uint8_t name_length = get_dir_entry_param_bytes("name_length")[0] - sizeof(char16_t); 34 | 35 | // Slice the name string into an appropriate length 36 | std::valarray sliced_name_bytes = name_bytes[std::slice(0, name_length, 1)]; 37 | 38 | // If we have at least one char16_t to read 39 | if(name_bytes.size() > sizeof(char16_t)) 40 | { 41 | // Convert it to a string because UTF-16 strings are very bad + unprintable 42 | std::u16string u16name(reinterpret_cast(&sliced_name_bytes[0]), sliced_name_bytes.size() / sizeof(char16_t)); 43 | name_string = boost::locale::conv::utf_to_utf(&u16name[0], &u16name[0] + u16name.length()); 44 | } 45 | 46 | // This string will be empty if there was no name found 47 | return name_string; 48 | } 49 | 50 | std::string directory_entry::get_creation_time() 51 | { 52 | return ""; 53 | } 54 | 55 | std::string directory_entry::get_modified_time() 56 | { 57 | return ""; 58 | } 59 | 60 | dir_entry_object_type directory_entry::get_object_type() 61 | { 62 | dir_entry_object_type ot = (dir_entry_object_type)(get_dir_entry_param_bytes("type")[0]); 63 | 64 | return ot; 65 | } 66 | 67 | // TODO: Template function that returns the value/type we're actually interested in? 68 | std::valarray directory_entry::get_dir_entry_param_bytes(std::string param_key) 69 | { 70 | // Find entry in map 71 | std::map>::iterator it = dir_entry_params.find(param_key); 72 | 73 | // If the item actually exists 74 | if(it != dir_entry_params.end()) 75 | { 76 | // Get the offset and the size (in bytes) 77 | uint8_t offset = it->second.first; 78 | uint8_t size = it->second.second; 79 | // Grab a slice based on 80 | std::valarray bytes(dir_entry_bytes[std::slice(offset,size, 1)]); 81 | return bytes; 82 | } 83 | 84 | // If no param was found, or there was an error, return an empty valarray 85 | return std::valarray(); 86 | } 87 | 88 | // TODO: Not implemented yet 89 | uint32_t directory_entry::get_stream_sector_id() 90 | { 91 | return FREESECT; 92 | } 93 | 94 | uint32_t directory_entry::get_stream_size() 95 | { 96 | return FREESECT; 97 | } -------------------------------------------------------------------------------- /util/Makefile: -------------------------------------------------------------------------------- 1 | CXX=g++ 2 | CXXFLAGS=-c -Wall -g -std=c++11 3 | INCDIRS=-Iinc -I../inc 4 | BASESRCDIR=../src 5 | UTILSRCDIR=src 6 | LDFLAGS=-Wall 7 | LDLIBS= -lboost_filesystem -lboost_system 8 | BINDIR=bin 9 | MKDIR=mkdir -p 10 | OBJS=print_header_info direntries libload 11 | 12 | all: output_dirs $(OBJS) clean 13 | 14 | print_header_info: print_header_info.o 15 | $(CXX) $(LDFLAGS) print_header_info.o -o $(BINDIR)/print_header_info $(LDLIBS) 16 | 17 | print_header_info.o: $(UTILSRCDIR)/print_header_info.cpp 18 | $(CXX) $(CXXFLAGS) $(UTILSRCDIR)/print_header_info.cpp $(INCDIRS) 19 | 20 | direntries: direntries.o cfb_types.o 21 | $(CXX) $(LDFLAGS) direntries.o cfb_types.o -o $(BINDIR)/direntries $(LDLIBS) 22 | 23 | direntries.o: $(UTILSRCDIR)/direntries.cpp 24 | $(CXX) $(CXXFLAGS) $(UTILSRCDIR)/direntries.cpp $(INCDIRS) 25 | 26 | libload: libload.o cfb_types.o altium_lib.o 27 | $(CXX) $(LDFLAGS) libload.o cfb_types.o altium_lib.o -o $(BINDIR)/libload $(LDLIBS) 28 | 29 | libload.o: $(UTILSRCDIR)/libload.cpp 30 | $(CXX) $(CXXFLAGS) $(UTILSRCDIR)/libload.cpp $(INCDIRS) 31 | 32 | cfb_types.o: $(BASESRCDIR)/cfb_types.cpp 33 | $(CXX) $(CXXFLAGS) $(BASESRCDIR)/cfb_types.cpp $(INCDIRS) 34 | 35 | altium_lib.o: $(BASESRCDIR)/altium_lib.cpp $(BASESRCDIR)/cfb_types.cpp 36 | $(CXX) $(CXXFLAGS) $(BASESRCDIR)/altium_lib.cpp $(INCDIRS) 37 | 38 | output_dirs: $(BINDIR) 39 | 40 | $(BINDIR): 41 | $(MKDIR) $(BINDIR) 42 | 43 | clean: 44 | \rm -f *.o 45 | 46 | -------------------------------------------------------------------------------- /util/README.md: -------------------------------------------------------------------------------- 1 | # Utility Funtions 2 | 3 | A small group of utility functions to glean information from the library files or test the types without modifying the main source. 4 | 5 | While I'm trying to keep this as portable as possible, there may be some platform-specific additions depending on which computer (home or work) I'm working on. 6 | 7 | ### Programs 8 | * **print_header_info**: print relevant information from the header 9 | 10 | * **direntries**: populate the directory entries table and print each one's information 11 | 12 | * **libload**: read an altium library file and output some debug information, including the names of symbols/footprints in the library -------------------------------------------------------------------------------- /util/inc/direntries.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | 5 | 6 | #include 7 | #include 8 | 9 | #include "cfb_types.hpp" 10 | #include "altium_lib.hpp" 11 | 12 | #include "boost/filesystem.hpp" 13 | #include "boost/filesystem/fstream.hpp" 14 | 15 | // Sector Allocation Table 16 | // Starts at sector 0 (post-header) 17 | // May comprise more than one sector based on the MSAT in the header 18 | std::vector sector_alloc_table; 19 | 20 | header_t header; 21 | 22 | 23 | // Build the header 24 | void build_header(std::ifstream &cfb_file); 25 | 26 | // Build the sector allocation table 27 | void build_sat(std::ifstream &cfb_file); 28 | 29 | void build_dir_entry_chain(std::ifstream &cfb_file); 30 | 31 | std::vector dir_entries; 32 | 33 | // Read the next directory entry and push it back to the dir_entries vector 34 | // Returns true if it read a directory entry 35 | // Returns false if there wasn't one or it wasn't valid 36 | void read_next_directory_entry(std::ifstream &cfb_file); 37 | 38 | // Prints the information for a directory entry specified by 'at_index' 39 | void print_directory_entry(size_t at_index); 40 | 41 | -------------------------------------------------------------------------------- /util/inc/print_header_info.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "cfb_types.hpp" 5 | 6 | #include "boost/filesystem.hpp" 7 | #include "boost/filesystem/fstream.hpp" 8 | 9 | // Master function to print the information 10 | void print_info(header_t * header); 11 | 12 | // Get the version information from the revision_number and version_number 13 | std::string get_version_info(uint16_t * version); 14 | 15 | // Verify the magic number of the file 16 | bool check_magic_number(uint64_t * magic_number); 17 | 18 | -------------------------------------------------------------------------------- /util/src/direntries.cpp: -------------------------------------------------------------------------------- 1 | #include "direntries.hpp" 2 | 3 | namespace fs = boost::filesystem; 4 | 5 | int main(int argc, char const *argv[]) 6 | { 7 | if(argc < 2) 8 | { 9 | std::cout << "Usage: ./direntries [path to library file]\n"; 10 | return 1; 11 | } 12 | 13 | // Create the file stream object 14 | fs::ifstream ifs; 15 | 16 | // Path to the library, specified by user at the command line 17 | fs::path libfile(argv[1]); 18 | 19 | // Check to make sure it's actually a valid file 20 | if(exists(libfile)) 21 | { 22 | if(is_directory(libfile)) 23 | { 24 | std::cout << "Error: " << libfile << " is a directory\n"; 25 | return 1; 26 | } 27 | } 28 | else 29 | { 30 | std::cout << "Error: " << libfile << " does not exist\n"; 31 | return 1; 32 | } 33 | 34 | ifs.open(libfile, std::ios::binary | std::ios::ate); 35 | 36 | // Seek to the beginning of the file 37 | ifs.seekg(0, std::ios::beg); 38 | 39 | // Build the header 40 | build_header(ifs); 41 | 42 | // build the sector allocation table 43 | build_sat(ifs); 44 | 45 | build_dir_entry_chain(ifs); 46 | 47 | // Print the name of each directory entry 48 | // See: https://stackoverflow.com/a/5616234/2614831 49 | // UTF16 is bad, so hopefully we don't need to print these often 50 | for(size_t i = 0; i < dir_entries.size(); i++) 51 | { 52 | print_directory_entry(i); 53 | } 54 | 55 | ifs.close(); 56 | 57 | } 58 | 59 | void build_header(std::ifstream &cfb_file) 60 | { 61 | cfb_file.read(reinterpret_cast(&header), sizeof(header_t)); 62 | } 63 | 64 | void build_sat(std::ifstream &cfb_file) 65 | { 66 | uint32_t offset; 67 | 68 | uint32_t sector_size = (1 << header.sec_size); 69 | 70 | int32_t sector_id; 71 | 72 | for(uint32_t i = 0; i < header.sec_count; i++) 73 | { 74 | offset = (1 + header.master_sec_ids[i]) * (sector_size); 75 | cfb_file.seekg(offset, std::ios::beg); 76 | for(uint32_t j = 0; j < sector_size / sizeof(uint32_t); j++) 77 | { 78 | cfb_file.read(reinterpret_cast(§or_id), sizeof(int32_t)); 79 | sector_alloc_table.push_back(sector_id); 80 | } 81 | } 82 | } 83 | 84 | void build_dir_entry_chain(std::ifstream &cfb_file) 85 | { 86 | uint32_t offset = 0; 87 | 88 | int32_t current_sector = header.first_sec_id; 89 | 90 | uint32_t sector_size = (1 << header.sec_size); 91 | 92 | uint32_t dir_entries_per_sector = sector_size / (CFB_DIR_ENTRY_SIZE); 93 | 94 | while(current_sector != ENDOFCHAIN) 95 | { 96 | offset = (1 + current_sector) * (sector_size); 97 | 98 | cfb_file.seekg(offset, std::ios::beg); 99 | 100 | for(uint8_t i = 0; i < dir_entries_per_sector; i++) 101 | { 102 | read_next_directory_entry(cfb_file); 103 | } 104 | // Move to the next sector containing directory entries 105 | current_sector = sector_alloc_table.at(current_sector); 106 | } 107 | } 108 | 109 | void read_next_directory_entry(std::ifstream &cfb_file) 110 | { 111 | uint8_t dir_entry_bytes[CFB_DIR_ENTRY_SIZE]; 112 | 113 | cfb_file.read(reinterpret_cast(&dir_entry_bytes), CFB_DIR_ENTRY_SIZE); 114 | 115 | directory_entry new_dir_entry(dir_entry_bytes, sizeof(dir_entry_bytes)); 116 | 117 | dir_entries.push_back(new_dir_entry); 118 | } 119 | 120 | void print_directory_entry(size_t at_index) 121 | { 122 | std::cout << dir_entries[at_index].get_name() << "\n"; 123 | } 124 | -------------------------------------------------------------------------------- /util/src/libload.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "altium_lib.hpp" 5 | 6 | int main(int argc, char * argv[]) 7 | { 8 | if(argc < 2) 9 | { 10 | std::cout << "Usage: ./libload [path to library file]\n"; 11 | return 1; 12 | } 13 | 14 | std::string lib_path_string(argv[1]); 15 | 16 | std::cout << "Creating new library...\n"; 17 | 18 | altium_lib libfile(lib_path_string); 19 | 20 | std::cout << "> Loading library at " << lib_path_string << "...\n"; 21 | 22 | if(libfile.load_from_file()) 23 | { 24 | std::cout << ">> succeeded\n"; 25 | } 26 | else 27 | { 28 | std::cout << ">> failed!\n"; 29 | return 1; 30 | } 31 | 32 | std::cout << "Getting symbol/footprint names...\n"; 33 | 34 | std::vector lib_entities; 35 | 36 | if(libfile.get_library_entries(lib_entities)) 37 | { 38 | std::cout << ">> succeeded\n"; 39 | std::cout << "Entities:\n"; 40 | 41 | for(size_t i = 0; i < lib_entities.size(); i++) 42 | { 43 | std::cout << ">> " << lib_entities.at(i) << '\n'; 44 | } 45 | } 46 | else 47 | { 48 | std::cout << ">> failed! No entities found\n"; 49 | return 1; 50 | } 51 | 52 | return 0; 53 | } -------------------------------------------------------------------------------- /util/src/print_header_info.cpp: -------------------------------------------------------------------------------- 1 | #include "print_header_info.hpp" 2 | 3 | namespace fs = boost::filesystem; 4 | 5 | int main(int argc, char const *argv[]) 6 | { 7 | if(argc < 2) 8 | { 9 | std::cout << "Usage: ./print_header_info [path to library file]\n"; 10 | return 1; 11 | } 12 | 13 | // Create the file stream object 14 | fs::ifstream ifs; 15 | 16 | // Path to the library, specified by user at the command line 17 | fs::path libfile(argv[1]); 18 | 19 | // Check to make sure it's actually a valid file 20 | if(exists(libfile)) 21 | { 22 | if(is_directory(libfile)) 23 | { 24 | std::cout << "Error: " << libfile << " is a directory\n"; 25 | return 1; 26 | } 27 | } 28 | else 29 | { 30 | std::cout << "Error: " << libfile << " does not exist\n"; 31 | return 1; 32 | } 33 | 34 | ifs.open(libfile, std::ios::binary | std::ios::ate); 35 | 36 | // Create a new header_t 37 | header_t lib_header; 38 | 39 | ifs.seekg(0, std::ios::beg); 40 | 41 | // Since the header_t is packed, we can just raw_copy the bytes from the filesystem 42 | // There's probably a much better/safer way to do this 43 | // But this is mostly just for testing... 44 | ifs.read(reinterpret_cast(&lib_header), 512); 45 | 46 | ifs.close(); 47 | 48 | print_info(&lib_header); 49 | 50 | return 0; 51 | } 52 | 53 | void print_info(header_t * header) 54 | { 55 | std::cout << "HEADER PARAMETER\t\tVALUE\n"; 56 | std::cout << "magic number:\t\t\t" << std::hex << __builtin_bswap64((*header).magic) << "\n"; 57 | std::cout << "uid:\t\t\t\t" << std::hex << *(int*)(*header).uid << "\n"; 58 | std::cout << "version:\t\t\t" << get_version_info(header->version_number) << "\n"; 59 | std::cout << "endianness:\t\t\t" << std::hex << (*header).byte_order << "\n"; 60 | std::cout << "sector size:\t\t\t" << (*header).sec_size << "\n"; 61 | std::cout << "short sector size:\t\t" << (*header).short_sec_size << "\n"; 62 | std::cout << "sector count:\t\t\t" << (*header).sec_count << "\n"; 63 | std::cout << "first sector id:\t\t" << (*header).first_sec_id << "\n"; 64 | std::cout << "min std stream size:\t\t" << std::hex << (*header).min_std_stream_size << "\n"; 65 | std::cout << "first short sector id:\t\t" << (*header).first_short_sec_id << "\n"; 66 | std::cout << "short sector count:\t\t" << (*header).short_sec_count << "\n"; 67 | std::cout << "first master sector id:\t\t" << (*header).first_master_sec_id << "\n"; 68 | std::cout << "master sector count:\t\t" << (*header).master_sec_count << "\n"; 69 | } 70 | 71 | std::string get_version_info(uint16_t * version) 72 | { 73 | std::stringstream version_string; 74 | 75 | version_string << version[1] << "." << version[0]; 76 | 77 | return version_string.str(); 78 | } 79 | --------------------------------------------------------------------------------