Author Topic: Altium file format  (Read 24458 times)

0 Members and 1 Guest are viewing this topic.

Offline daedalusTopic starter

  • Regular Contributor
  • *
  • Posts: 140
  • Country: gb
Altium file format
« 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.


 

Offline EEVblog

  • Administrator
  • *****
  • Posts: 37740
  • Country: au
    • EEVblog
Re: Altium file format
« Reply #1 on: February 01, 2012, 07:06:26 am »
It's a propriety format, so no details publicly available.
What are you trying to do?

Dave.
 

Offline joelby

  • Frequent Contributor
  • **
  • Posts: 634
Re: Altium file format
« Reply #2 on: February 01, 2012, 09:02:38 am »
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. 
 

Offline daedalusTopic starter

  • Regular Contributor
  • *
  • Posts: 140
  • Country: gb
Re: Altium file format
« Reply #3 on: February 01, 2012, 08:29:39 pm »
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.
 

Offline joelby

  • Frequent Contributor
  • **
  • Posts: 634
Re: Altium file format
« Reply #4 on: February 01, 2012, 10:52:08 pm »
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!
 

Online mikeselectricstuff

  • Super Contributor
  • ***
  • Posts: 13748
  • Country: gb
    • Mike's Electric Stuff
Re: Altium file format
« Reply #5 on: February 04, 2012, 11:30:50 am »
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
Youtube channel:Taking wierd stuff apart. Very apart.
Mike's Electric Stuff: High voltage, vintage electronics etc.
Day Job: Mostly LEDs
 

Offline sarason

  • Contributor
  • Posts: 13
  • Country: 00
    • PCBSynergy
Re: Altium file format
« Reply #6 on: March 17, 2012, 01:16:51 am »
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

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/

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
« Last Edit: November 09, 2023, 12:32:19 pm by sarason »
 

Online hans

  • Super Contributor
  • ***
  • Posts: 1640
  • Country: nl
Re: Altium file format
« Reply #7 on: March 26, 2012, 07:24:00 pm »
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:
 

Offline remcopoelstra

  • Newbie
  • Posts: 1
Re: Altium file format
« Reply #8 on: November 06, 2013, 11:16:33 am »
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
 

Offline jamiechi

  • Contributor
  • Posts: 16
  • Country: us
Re: Altium file format
« Reply #9 on: November 14, 2013, 12:13:30 am »
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.
« Last Edit: November 14, 2013, 12:15:47 am by jamiechi »
 

Offline DrMarty

  • Newbie
  • Posts: 1
Re: Altium file format
« Reply #10 on: November 25, 2013, 02:30:38 am »
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

At least in the short term, this will be the supported mechanism for accessing and manipulating design files.

Regards
Marty
 

Offline sacherjj

  • Frequent Contributor
  • **
  • Posts: 993
  • Country: us
Re: Altium file format
« Reply #11 on: December 07, 2013, 03:40:28 pm »
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.
 

Offline xcxl

  • Newbie
  • Posts: 2
  • Country: fr
Re: Altium file format
« Reply #12 on: July 12, 2016, 10:45:29 am »
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
----------------------------------------
« Last Edit: July 12, 2016, 11:54:35 am by xcxl »
 

Offline Gribo

  • Frequent Contributor
  • **
  • Posts: 629
  • Country: ca
Re: Altium file format
« Reply #13 on: July 15, 2016, 08:15:45 pm »
I am available for freelance work.
 

Offline xcxl

  • Newbie
  • Posts: 2
  • Country: fr
Re: Altium file format
« Reply #14 on: July 25, 2016, 07:31:32 am »
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 :
Code: [Select]
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 :
Code: [Select]
    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 :
Code: [Select]
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 :
Code: [Select]
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();
    }
 

Offline technotronix

  • Regular Contributor
  • *
  • Posts: 210
  • Country: us
    • PCB Assembly
Re: Altium file format
« Reply #15 on: August 08, 2016, 07:20:45 am »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf