EEVblog Electronics Community Forum
Electronics => PCB/EDA/CAD => Altium Designer => Topic started by: daedalus on January 18, 2012, 12:38:25 pm
-
Hi,
Has anyone come across a file format description for altium's file types?
I spent this morning fiddling with a few file types in a hex editor, and can extract schdoc, most of schlib data, but pcbdoc and pcblib are problematic as they have lots of binary data.
-
It's a propriety format, so no details publicly available.
What are you trying to do?
Dave.
-
You can save documents in a text format, which from memory looked like it would be very simple to write a conversion tool for. The binary format just seems to wrap the text version's textual data in a binary header! It's probably simplest to just skip to the start of the plain text bit unless you really want to reverse engineer the binary bits.
-
What I originally wanted to do was programmatically generate schematic and footprint libraries. After poking round a bit I started to wonder whether it would be possible to convert back and forth between altium and eagle project formats (which would be useful for me). One of the altium VARs offers this, so I assume they have done similar dev work, although maybe they have info under NDA.
Each altium file type can be opened in 7zip to see the internal file structure. It seems three approaches are used, either ascii files, or binary block files (with payloads filled with ascii or binary data) or complete binary blobs, depending on the file. PCB documents are the problem, as they have lots of binary data and no ascii save option.
On an aside, eagle file support took 15 minutes, as they use a nice open XML format, and even provide the schema file to help people interface to it. You would have thought companies would be more open about file formats, considering any competitor worth their salt will just throw an intern at the problem. Hiding the details just screws over everyone else.
-
Ah, I don't remember if I looked at the PCB files, so I didn't know they didn't have an ASCII version.
If you very keen, there are some reverse engineering tools for Delphi that might help with decoding the binary formats. I might have a fiddle after I finish the gardening!
-
Ah, I don't remember if I looked at the PCB files, so I didn't know they didn't have an ASCII version.
If you very keen, there are some reverse engineering tools for Delphi that might help with decoding the binary formats. I might have a fiddle after I finish the gardening!
Altium will import PCAD and PCAD has a documented (optional) ASCII format - I should have a copy of the info if you need it.
Not sure if Altium imports ASCII PCAD though - probably worth checking first - I can supply a sample file if you want to try
-
All Protel ASCII formats are published although finding them can get a little bit difficult at times.
The binary formats are derived from the ASCII formats.
I have reversed out the ASCII format for my program PCB Synergy. It was actually easier than reading the specification
which got to be very large.
https://pcbsynergy.com (https://pcbsynergy.com)
The Altium Designer format has changed at some point and is harder to make sense of now, so start with the earlier version and then modify it.
Also the XY points IIRC have changed from being referenced from the zero reference, to being absolute. You then do the subtraction.
The 010 editor is an excellent tool to help read a binary file with BT Templates.
http://www.sweetscape.com/ (http://www.sweetscape.com/)
I wrote a template to read Essemtec BRD file and wish I had known of it, when I reverse engineered Juki binary files.
regards sarason
-
Pcb libraries are largely dependant on a Windows Composite File format I believe, it's the same they used to store help files. Within there are folders with each footprint. The footprint itself is saved in binary format, which costs quite some effort to figure out.
The schematic symbols seem to be a mix of both, ascii parameters and binary properties..
Eventually got something working to generate preview files:
-
Hi,
I managed to get the separate footprints out of the library, but I'm stuck at deciphering the real data. For the schematic symbols this was quite easy, but it seems there is no link between the file formats.
Would you mind sharing some of the details?
Kind regards,
Remco Poelstra
-
I get a popup message saying the ascii pcb is version 4. I wonder if Altium has dropped support for the ascii version. I believe the latest PCB file version is around ver 6.
Edit: The info in the file says version 5.
-
Hi All,
Altium recently announced its DXP Developer extension for those who want to programmatically interact directly with Altium Designer (and it's design files): http://www.altium.com/en/developer (http://www.altium.com/en/developer)
At least in the short term, this will be the supported mechanism for accessing and manipulating design files.
Regards
Marty
-
Developer Extension is only available as part of the subscription. ::)
But, I wonder if that is going to be the tipping point for my decision. I could see developing some plugins. Ah, who am I kidding. I haven't had any free time in a year.
-
Just in case I put this here, maybe this can help someone with Altium v5 PCBDOC or v6 PCBDOC :
---------------------------------
The .PCBDOC from Altium can be opened with 7zip (the archive format is "Compound")
After the different folders can be accessed and contains different information, readable in ASCII or not.
The most importants are :
* /Board/Data => READABLE IN ASCII, contains the board shape vertexs, the origin point X & Y, all in mils from the bottom left of the screen in Altium.
* /Components/Data => READABLE IN ASCII, contains the list of all the components on the board, with their footprint, and absolute position in mils.
* /Texts/Data => NOT READABLE IN ASCII (with an hex editor only), contains all the string of the board, with the layer number and position. Each data blocks are separated by 0x05E6000000,
are around 250 bytes. At start+230 there is the length of the string and at start+235 with have the string in ASCII. At start+7 0xFFFF means that it is a
free text, otherwise the component index. Don't know where is the layer number.
The file finishs with "XX" in ascii.
* /Tracks/Data => NOT READABLE IN ASCII, contains all the visible lines. The format is :
04 2D xx xx xx 39 xx xx FF FF FF FF FF FF FF FF FF FF 4B 4D 21 04 4B 4D 21 04 4B 4D 21 04 4B 4D 21 04 + 14 bytes
The 0x042D is the start, the 0x39 is the layer number, foundable in /Board/Data, the four 0x21044B4D are the 4 coordinates (x_start, y_start, x_stop, y_stop) in uint32 format, giving a number like 69291339 wich means 6929,1339 mils
----------------------------------------
-
Here is something I have found:
https://github.com/vadmium/python-altium/blob/master/format.md (https://github.com/vadmium/python-altium/blob/master/format.md)
-
Hi, thank you very much Gribo, nice to open SCH document.
Here is my work in C++/Qt to read the .PCBDOC files, sorry I didn't write the documentation, but that could help someone who want to do the same thing (display the board on the screen with the components, their names and values, and all the lines / arcs). I let here only the code to read the binary files, the others are no so hard to understand in ASCII).
To extract the components value :
if (file.open(QIODevice::ReadOnly)) {
// Read the file extracted
QDataStream streamIn(&file);
streamIn.setByteOrder(QDataStream::LittleEndian);
QByteArray separator;
separator.resize(4);
streamIn.readRawData(separator.data(), 4);
QByteArray sizeOfFieldsHexa;
sizeOfFieldsHexa.append(separator.at(1));
int sizeOfFields = sizeOfFieldsHexa.toHex().toInt(NULL, 16) + 5;
qDebug() << "Sep :" << separator.toHex();
qDebug() << "Raw file size" << file.size();
qDebug() << "Size of fields w sep" << sizeOfFields;
do
{
streamIn.skipRawData(1);
quint8 numLayer;
streamIn >> numLayer;
streamIn.skipRawData(6);
quint16 numComponent;
streamIn >> numComponent;
streamIn.skipRawData(4);
quint32 posXs, posYs;
streamIn >> posXs;
streamIn >> posYs;
streamIn.skipRawData(6);
double angleRotTmp;
streamIn >> angleRotTmp;
streamIn.skipRawData(6);
bool isADesignator;
streamIn >> isADesignator;
streamIn.skipRawData(sizeOfFields-28-19);
quint8 sizeOfString;
streamIn >> sizeOfString;
QByteArray tmpString;
tmpString.resize(sizeOfString-1);
streamIn.skipRawData(4);
streamIn.readRawData(tmpString.data(), sizeOfString-1);
QString str = QString::fromLocal8Bit(tmpString);
streamIn.skipRawData(4); // skip the new separator
if (((numLayer == 33) || (numLayer == 34)) && numComponent != 65535) // mech 33 => top overlay & numComponent is a real component number
{
if (numComponent >= compCommentList.size()) // sometimes the num comp doesn't follow : 100, 101, 102, 106,107
{
compCommentList.resize(numComponent+1);
compDesignList.resize(numComponent+1);
//qDebug() << "Resize";
}
if (!isADesignator) { // it's a comment (first)
compCommentList[numComponent] = str;
//qDebug() << "Description[" << numComponent << "] found :" << str;
}
else // it's a designator (second)
{
compDesignList[numComponent] = str;
//qDebug() << "Designator[" << numComponent << "] found :" << str;
}
}
} while(!file.atEnd());
qDebug() << "Start checking designator / description pairs";
for (int i=0 ; i<compCommentList.size() ; i++) {
if (!compCommentList[i].isEmpty() && !compDesignList[i].isEmpty())
{
for (int j=0 ; j<(*p_tmpComponentsList).size() ; j++) {
if ((*p_tmpComponentsList)[j].designator == compDesignList[i]) {
(*p_tmpComponentsList)[j].description = compCommentList[i];
//qDebug() << "Pair [" << j << "] found :" << (*p_tmpComponentsList)[j].designator << "description :" << (*p_tmpComponentsList)[j].description;
counterComp++;
}
}
}
}
qDebug() << counterComp << "components in /Texts/Data";
if (nbComponents == counterComp)
return true; // success
else {
QMessageBox msgBox;
msgBox.setText(QString("Error : descriptor / designator missing in Texts/Data, ") + QString::number(nbComponents-counterComp) + " cannot be loaded.");
msgBox.setIcon(QMessageBox::Critical);
msgBox.exec();
}
}
To extract the strings :
if (file.open(QIODevice::ReadOnly)) {
// Read the file extracted
QDataStream streamIn(&file);
streamIn.setByteOrder(QDataStream::LittleEndian);
QByteArray separator;
separator.resize(4);
streamIn.readRawData(separator.data(), 4);
QByteArray sizeOfFieldsHexa;
sizeOfFieldsHexa.append(separator.at(1));
int sizeOfFields = sizeOfFieldsHexa.toHex().toInt(NULL, 16) + 5;
qDebug() << "Sep :" << separator.toHex();
qDebug() << "Raw file size" << file.size();
qDebug() << "Size of fields w sep" << sizeOfFields;
m_listPosTexts.clear();
m_listStringTexts.clear();
m_listRotationTexts.clear();
m_listTypeTexts.clear();
m_listHeigthTexts.clear();
do
{
streamIn.skipRawData(1);
quint8 numLayer;
streamIn >> numLayer;
streamIn.skipRawData(12);
quint32 posXs, posYs;
streamIn >> posXs;
streamIn >> posYs;
quint32 textHeight;
streamIn >> textHeight;
streamIn.skipRawData(2);
double angleRotTmp;
streamIn >> angleRotTmp;
streamIn.skipRawData(1);
quint32 textWidth;
streamIn >> textWidth;
bool isADescriptionText;
streamIn >> isADescriptionText;
bool isADesignatorText;
streamIn >> isADesignatorText;
streamIn.skipRawData(sizeOfFields-28-19);
quint8 sizeOfString;
streamIn >> sizeOfString;
QByteArray tmpString;
tmpString.resize(sizeOfString-1);
streamIn.skipRawData(4);
streamIn.readRawData(tmpString.data(), sizeOfString-1);
QString str = QString::fromLocal8Bit(tmpString);
streamIn.skipRawData(4); // skip the new separator
double posXs_mm = (double)(posXs) / 10000.0 * 0.0254;
double posYs_mm = (double)(posYs) / 10000.0 * 0.0254;
double textHeight_mm = (double)(textHeight) / 10000.0 * 0.0254;
if ((numLayer == 33) && !str.contains("MMBX receptacle")) // mech 33 => top overlay
{
//qDebug() << "Text on layer :" << numLayer << " Xs=" << posXs_mm << " Ys=" << posYs_mm << "Size :" << sizeOfString << " String :" << str << "Rotat. :" << angleRotTmp;
m_listPosTexts.append(QVector2D(posXs_mm, posYs_mm));
m_listStringTexts.append(str);
m_listRotationTexts.append(angleRotTmp);
if (isADesignatorText)
m_listTypeTexts.append(_DESIGNATOR_TEXT);
else if (isADescriptionText)
m_listTypeTexts.append(_DESCRIPTION_TEXT);
else
m_listTypeTexts.append(_FREE_TEXT);
m_listHeigthTexts.append(textHeight_mm);
counterTexts++;
}
} while(!file.atEnd());
qDebug() << counterTexts << "strings in /Texts/Data";
}
To extract the tracks :
if (file.open(QIODevice::ReadOnly)) {
// Read the file extracted
QDataStream streamIn(&file);
streamIn.setByteOrder(QDataStream::LittleEndian);
QByteArray separator;
separator.resize(4);
streamIn.readRawData(separator.data(), 4);
QByteArray sizeOfFieldsHexa;
sizeOfFieldsHexa.append(separator.at(1));
int sizeOfFields = sizeOfFieldsHexa.toHex().toInt(NULL, 16) + 5;
const int sizeDatasAtEndToSkip = sizeOfFields - 4 - 2 - 12 - 4 - 4 - 4 - 4 + 4; // +5 at end to skip the next separator
qDebug() << "Sep :" << separator.toHex();
qDebug() << "Raw file size" << file.size();
qDebug() << "Size of fields w sep" << sizeOfFields;
qDebug() << "Size to skip at end" << sizeDatasAtEndToSkip;
m_listLayersTracks.clear();
m_listPointStartTracks.clear();
m_listPointEndTracks.clear();
do
{
streamIn.skipRawData(1);
quint8 numLayer;
streamIn >> numLayer;
streamIn.skipRawData(12);
quint32 posXs, posYs, posXe, posYe;
streamIn >> posXs;
streamIn >> posYs;
streamIn >> posXe;
streamIn >> posYe;
streamIn.skipRawData(sizeDatasAtEndToSkip);
double posXs_mm = (double)(posXs) / 10000.0 * 0.0254;
double posYs_mm = (double)(posYs) / 10000.0 * 0.0254;
double posXe_mm = (double)(posXe) / 10000.0 * 0.0254;
double posYe_mm = (double)(posYe) / 10000.0 * 0.0254;
if ((numLayer == m_numBoardOutlineFromPCBDOC) || (numLayer == 66) || (numLayer == 67)) // mech 60 for P581 => 60 or mech10 or mech11
{
if (posXs_mm > maxX)
maxX = posXs_mm;
if (posYs_mm > maxY)
maxY = posYs_mm;
if (posXe_mm > maxX)
maxX = posXe_mm;
if (posYe_mm > maxY)
maxY = posYe_mm;
if (posXs_mm < minX)
minX = posXs_mm;
if (posYs_mm < minY)
minY = posYs_mm;
if (posXe_mm < minX)
minX = posXe_mm;
if (posYe_mm < minY)
minY = posYe_mm;
m_minimumTracksVertex = QPointF(minX, minY);
m_maximumTracksVertex = QPointF(maxX, maxY);
//qDebug() << "Track on layer : " << numLayer << " Xs=" << posXs_mm << " Ys=" << posYs_mm << " Xe=" << posXe_mm << " Ye=" << posYe_mm ;
m_listLayersTracks.append(numLayer);
m_listPointStartTracks.append(QVector2D(posXs_mm, posYs_mm));
m_listPointEndTracks.append(QVector2D(posXe_mm, posYe_mm));
counterTracks++;
}
} while(!file.atEnd());
qDebug() << "Track vertex min=" << m_minimumTracksVertex << " Max=" << m_maximumTracksVertex;
qDebug() << counterTracks << "tracks in /Tracks/Data";
}
To extract the arcs :
if (file.open(QIODevice::ReadOnly)) {
// Read the file extracted
QDataStream streamIn(&file);
streamIn.setByteOrder(QDataStream::LittleEndian);
QByteArray separator;
separator.resize(4);
streamIn.readRawData(separator.data(), 4);
QByteArray sizeOfFieldsHexa;
sizeOfFieldsHexa.append(separator.at(1));
int sizeOfFields = sizeOfFieldsHexa.toHex().toInt(NULL, 16) + 5;
const int sizeDatasAtEndToSkip = sizeOfFields - 4 - 2 - 12 - 4 - 4 - 4 - 8 - 8 + 4; // +5 at end to skip the next separator
qDebug() << "Sep :" << separator.toHex();
qDebug() << "Raw file size" << file.size();
qDebug() << "Size of fields w sep" << sizeOfFields;
qDebug() << "Size to skip at end" << sizeDatasAtEndToSkip;
m_listLayersArcs.clear();
m_listCenterArcs.clear();
m_listRadiusArcs.clear();
m_listStartAngleArcs.clear();
m_listEndAngleArcs.clear();
do
{
streamIn.skipRawData(1);
quint8 numLayer;
streamIn >> numLayer;
streamIn.skipRawData(12);
quint32 posX, posY, radius;
streamIn >> posX;
streamIn >> posY;
streamIn >> radius;
float startAngle, endAngle;
streamIn >> startAngle;
streamIn >> endAngle;
streamIn.skipRawData(sizeDatasAtEndToSkip);
double posXcenter_mm = (double)(posX) / 10000.0 * 0.0254;
double posYcenter_mm = (double)(posY) / 10000.0 * 0.0254;
double radius_mm = (double)(radius) / 10000.0 * 0.0254;
if ((numLayer == m_numBoardOutlineFromPCBDOC) || (numLayer == 66) || (numLayer == 67)) { // mech 60 for P581 => 60 or mech10 or mech11
//qDebug() << "Arc on layer : " << numLayer << " X=" << posXcenter_mm << " Y=" << posYcenter_mm << " R=" << radius_mm << " StartA=" << startAngle << " EndA=" << endAngle ;
m_listLayersArcs.append(numLayer);
m_listCenterArcs.append(QVector2D(posXcenter_mm, posYcenter_mm));
m_listRadiusArcs.append(radius_mm);
m_listStartAngleArcs.append(startAngle);
m_listEndAngleArcs.append(endAngle);
counterArcs++;
}
} while(!file.atEnd());
qDebug() << counterArcs << "arcs in /Arcs/Data";
file.close();
}
-
Here is a list of supported altium file format.
http://techdocs.altium.com/display/ADOH/Importing+and+Exporting+Design+Files (http://techdocs.altium.com/display/ADOH/Importing+and+Exporting+Design+Files)