#include "bitstreamverifier.h"

#include <QFile>
#include <QByteArray>

#include "mydebug.h"

struct xilinx_db_t {
    const char* sign;
    const char* name;
    unsigned int expected_size;
};

static const xilinx_db_t xdb[]=
{
    {"xq7z045", "ZynQ", 0},
    {"xcku040", "Xilinx/CARRIER", 0},
    {0,0, 0}
};

BitstreamVerifier::BitstreamVerifier()
{

}

class BitstreamDecoder
{
public:
    BitstreamDecoder(const void* data, unsigned int size):
        d((unsigned char*)data),
        s(size),
        pos(0)

    {
    }

    unsigned int getFieldSize(unsigned int max_size=512)
    {
        unsigned short l=(d[pos]<<8)+d[pos+1];
        if ((l>max_size) || ((pos+l)>s))
        {
            throw "overrun";
        }
        skip(2);
        return l;
    }

    void skip(unsigned int len)
    {
        if ((pos+len)>=s)
            throw "overrun";
        pos+=len;
    }

    unsigned char getCode()
    {
        unsigned char c=d[pos];
        skip(1);
        return c;
    }

    const char* str()
    {
        return (const char*)&d[pos];
    }

    const unsigned char* d;
    unsigned int s;

    unsigned int pos;
};

bool BitstreamVerifier::verify(const QString& filename)
{
    QFile f(filename);

    bool ok=f.open(QIODevice::ReadOnly);
    if (!ok)
        return false;

    QByteArray d=f.read(8*1024);

    return verify(d);
}

bool BitstreamVerifier::verify(const QByteArray& d)
{
    if (d.size()<8*1024)
        return false;

    BitstreamDecoder decoder(d.constData(), d.size());

    try
    {
        //Field1: 9 bytes of some sort of header
        unsigned int l=decoder.getFieldSize();
        if (l!=9)
            return false;
        decoder.skip(l);
        //Field 2: letter 'a'
        l=decoder.getFieldSize();
        if (l!=1)
            return false;
        unsigned char c=decoder.getCode();
        if (c!='a')
            return false;
        //Field 3: a string with some info as "name;UserID=...;Version=...\0
        l=decoder.getFieldSize();
        const char* p=decoder.str();
        if (p[l-1]!=0)
        {
            MyDebug<<"BS:"<<"Unserminated string";
            return false;
        }
        QString name(p);
        auto nl=name.split(QChar(';'));
        if (nl.size()<1)
            return false;
        m_info.name=nl[0];
        for(int i=1; i<nl.size(); ++i)
        {
            QString tmp=nl[i];
            auto vl=tmp.split(QChar('='));
            if (vl.size()==2)
            {
                if (vl[0]=="UserID")
                {
                    m_info.user=vl[1];
                    MyDebug<<"BS:"<<"User="<<m_info.user;
                }
                if (vl[0]=="Version")
                {
                    m_info.generator=vl[1];
                    MyDebug<<"BS:"<<"Version="<<m_info.generator;
                }
            }
        }
        decoder.skip(l);
        //Field 4: part num ('b')
        c=decoder.getCode();
        if (c!='b')
        {
            return false;
        }
        l=decoder.getFieldSize();
        m_info.partNumber=decoder.str();
        MyDebug<<"BS:"<<"P/N"<<m_info.partNumber;
        decoder.skip(l);
        {//Rep type
            for(int i=0; xdb[i].sign; ++i)
            {
                if (m_info.partNumber.startsWith(xdb[i].sign))
                {
                    m_info.repType=xdb[i].name;
                }
            }
        }
        //Field 5: date ('c')
        c=decoder.getCode();
        if (c!='c')
            return true;
        l=decoder.getFieldSize();
        m_info.date=decoder.str();
        decoder.skip(l);
        //Filed 6: time ('d')
        c=decoder.getCode();
        if (c!='d')
            return true;
        l=decoder.getFieldSize();
        m_info.date=m_info.date+" "+decoder.str();
        decoder.skip(l);
    } catch(...)
    {
        return false;
    }

    return true;
}



/*
 *
 * http://www.fpga-faq.com/FAQ_Pages/0026_Tell_me_about_bit_files.htm

The Xilinx .bit format is pretty simple.  It uses keys and lengths to divide the file.
Here is an example.  Below is a hex dump from the beginning of a .bit file:

00000000:  00 09 0f f0 0f f0 0f f0 0f f0 00 00 01 61 00 0a  .............a..
00000010:  78 66 6f 72 6d 2e 6e 63 64 00 62 00 0c 76 31 30  xform.ncd.b..v10
00000020:  30 30 65 66 67 38 36 30 00 63 00 0b 32 30 30 31  00efg860.c..2001
00000030:  2f 30 38 2f 31 30 00 64 00 09 30 36 3a 35 35 3a  /08/10.d..06:55:
00000040:  30 34 00 65 00 0c 28 18 ff ff ff ff aa 99 55 66  04.e..(.......Uf

Field 1
2 bytes          length 0x0009           (big endian)
9 bytes          some sort of header

Field 2
2 bytes          length 0x0001
1 byte           key 0x61                (The letter "a")

Field 3
2 bytes          length 0x000a           (value depends on file name length)
10 bytes         string design name "xform.ncd" (including a trailing 0x00)

Field 4
1 byte           key 0x62                (The letter "b")
2 bytes          length 0x000c           (value depends on part name length)
12 bytes         string part name "v1000efg860" (including a trailing 0x00)

Field 4
1 byte           key 0x63                (The letter "c")
2 bytes          length 0x000b
11 bytes         string date "2001/08/10"  (including a trailing 0x00)

Field 5
1 byte           key 0x64                (The letter "d")
2 bytes          length 0x0009
9 bytes          string time "06:55:04"    (including a trailing 0x00)

Field 6
1 byte           key 0x65                 (The letter "e")
4 bytes          length 0x000c9090        (value depends on device type,
                                           and maybe design details)
8233440 bytes    raw bit stream starting with 0xffffffff aa995566 sync
                 word documented below.

Once you get the raw bits, XAPP138 "Virtex FPGA Series Configuration and
Readback" and XAPP139 "Configuration and Readback of Virtex FPGAs using
(JTAG) Boundary-Scan" will tell you what to do with them.

Note the "Enable .bit File Compression" option doesn't change the file
format at all.  It only changes how the bitstream is interpreted by the
configuration state machine inside the Xilinx part. (and of course the length of the file)

The following documents at Xilinx are also great evening reading

    http://www.xilinx.com/bvdocs/appnotes/xapp138.pdf

    http://www.xilinx.com/bvdocs/appnotes/xapp139.pdf

    http://www.xilinx.com/bvdocs/appnotes/xapp151.pdf
*/
