/******************************************************************************
 *      "Copyright (C) 2006, ApS s.r.o Brno, All Rights Reserved"             *
 ******************************************************************************
 *============================================================================*
 *   Version „number“    Header/source file for the project   "file name"     *
 *----------------------------------------------------------------------------*
 * VERSION   DATE    TIME  BY   CHANGE/COMMENTS                               *
 *----------------------------------------------------------------------------*
 *============================================================================*/


//
//  main class of object file.
//
//  author:  Libor Vasicek, xvasic15@stud.fit.vutbr.cz
//  created: 21.02.2006
//
//////////////////////////////////////////////////////////////////////

#ifndef __OBJECTFILE_H__
#define __OBJECTFILE_H__

#include <ctime>
#include <map>
#include <queue>
#include <string>
#include <vector>
#include <cassert>

#include "exceptions.h"
#include "utils.h"

namespace ObjectFile
{
    class CObjectFile;
    class CSectionContainer;
    class CSection;
    class CSectionData;
    class CRelocationTable;
    class CRelocation;
    class CLineNumberTable;
    class CReference;
    class CSymbolTableContainer;
    class CSymbolTable;
    class CSymbol;
    class CExpansionTable;

    class CPersistentObject;

///////////////////////////////////////////////////////////////////////////////
// v unixu jsou pauzivany nasledujici makra, abychom mohli pouzit tyto nazvy
// ve vyctevem typu, musime je oddefinovat.
// mas: 180507
#undef ABSOLUTE
#undef RELATIVE

#undef UNDEFINED
#undef LITTLE_ENDIAN
#undef BIG_ENDIAN

    //! Zpusob ulozeni slova v pameti.
    enum eByteOrder {
        UNDEFINED,      /*!< Nedefinovana hodnota. */
        LITTLE_ENDIAN,  /*!< Little Endian. */
        BIG_ENDIAN      /*!< Big Endian. */
    };

    ///////////////////////////////////////////////////////////////////////////

    //! Podpora perzistentnich objektu.
    /*!
      Bazova trida, ktera slouzi jako rozhrani k podpore ukladani a nahravani
      objektu ze souboru. Tridy, ktere jsou odvozeny z teto abstraktni tridy,
      musi implementovat vsechny virtualni metody.
    */
    class CPersistentObject
    {
    public:
        typedef CPersistentObject * Item;

		virtual ~CPersistentObject();

        //! Porovnani perzistentnich objektu.
        /*!
          Tato struktura je nutna ke spravne cinnosti prioritni fronty.
          Objekty ve fronte jsou serazeny podle pozice v souboru.
        */
        struct Less : public std::binary_function<Item, Item, bool>
        {
            //! Pretizeny operator ocekavany knihovnou std k porovnani.
            /*!
              \param e1 prvni polozka k porovnani.
              \param e2 druha polozka k porovnani.
              \return vysledek porovnani.
            */
            bool operator()(const Item e1, const Item e2) const
            {
                assert(e1 != 0);
                assert(e2 != 0);

                return e1->GetAddress() > e2->GetAddress();
            }
        };

        //! Prioritni fronta slouzici k planovani nacitanych/ukladanych dat.
        typedef std::priority_queue<Item, std::vector<Item>, Less> Scheduler;

    public:
        //! Pocet polozek v datove strukture.
        /*! Tato informace je nutna predevsim u nacitani objektu ze souboru.
            Pred vlastnim nactenim dat je tato hodnota nastavena a objekt je
            schopen nacist vice dat najednou. Timto zpusobem jsme schopni
            nacitat pole dat a vytvorit tak perzistentni kolekce.
            \return pocet polozek.
        */
        virtual int  Count()      = 0;

        //! Poloha ve vstupnim/vystupnim souboru.
        /*! Pomoci teto hodnoty je algoritmus schopen objekty nacitat/ukladat
            ve spravnem poradi a na spravne misto v souboru.
            \return pozice v souboru.
        */
        virtual int  GetAddress() = 0;

        //! Nastaveni polohy ve vystupnim souboru.
        /*! Metoda je urcene predevsim k nastaveni spravne polohy objektu
            v souboru pred jeho ulozenim.
        */
        virtual void SetAddress(int nAddress) = 0;

        //! Velikost datove struktury.
        /*! Vetsinou je tato hodnota vypocitana jako Count() * velikost polozky,
            ale neni to podminkou. Struktura muze obsahovat i dalsi informace.
            \return velikost struktury.
        */
        virtual int  GetSize()    = 0;

        //! Nacteni dat ze vstupniho proudu.
        /*!
            \param rStream vstupni proud, ze ktereho se budou cist data.
            \param vScheduler prioritni fronta k pripadnemu naplanovani dalsich
                perzistetnich objektu potrebnych k nacteni. Takto mame zajisteno
                nacitani zanorenych struktur.
        */
        virtual void LoadFromStream(CInputStream & rStream,
                        Scheduler & vScheduler)  = 0;

        //! Ulozeni dat do vystupniho proudu.
        /*!
            \param rStream vystupni proud, do ktereho se budou zapisovat data.
            \param vScheduler prioritni fronta k pripadnemu naplanovani dalsich
                perzistetnich objektu potrebnych k ulozeni. Takto mame zajisteno
                ukladani zanorenych struktur.
        */
        virtual void SaveToStream(COutputStream & rStream,
                        Scheduler & vScheduler)  = 0;
    };

    ///////////////////////////////////////////////////////////////////////////

    //! Sekce vystupniho formatu.
    /*!
      Trida zapouzdruje veskerou funkcionalitu spojenou s manipulaci se sekci
      vystupniho formatu.
    */
    class CSection
    {
    public:
        //! Typ sekce.
        enum eType {
            UNDEFINED,  /*!< Nedefinovany typ. */
            TEXT,       /*!< Kod. */
            DATA,       /*!< Data. */
            BSS         /*!< Neinicializovana data. */
        };

        enum eDataType {
            S_DATA  = 1 << 0,
            S_LINE  = 1 << 1,
            S_RELOC = 1 << 2
        };

    public:
        /*!
            \param parent vystupni format, ke kteremu sekce nalezi.
            \param nType typ sekce.
            \param sName jmeno sekce.
        */
        CSection(CObjectFile * parent = 0,
            eType nType = UNDEFINED, const string & sName = "");
        CSection(CSection & rSection);
        ~CSection();

        //! Uvolni veskera alokovana data.
        void Free();

        CSection * Copy(int nInclude, CSymbolTable *pSymTable = 0);

        inline void SetObjectFile(CObjectFile * parent)
        {
            m_pObjectFile = parent;
        }

    public:
        //! Pocet radku hlavicky.
        const static int    nHeaderSize = 11;

    public:
        //! Vymaze veskera data: data sekce, relokovane udaje, radkove informace.
        void ClearAll();
        //! Vymaze pouze data sekce.
        void ClearData();

        //! Ziska aktualni polohu ukazatele v datech sekce.
        /*! \sa CSectionData::GetCurAddress()
        */
        int  GetCurAddress(int * nPos = 0);

        //! Vlozi data na aktualni pozici v datech sekce.
        /*! \param pData vkladana data. Ukazatel nesmi byt nulovy.
            \param nPos presna poloha ve slove kam byla data ulozena.
            \return adresa kam byla data ulozena.
            \sa CSectionData::Add()
            \sa CSectionData::GetCurAddress()
        */
        int  AddData(const char * pData, int * nPos = 0);

        //! Vlozi data na aktualni pozici v datech sekce a zaznamy o relokaci.
        /*! Pomoci teto funkce lze vlozit spolu s daty i relokovane udaje.
            Tabulka relokovanych udaju obsahuje pozici symbolu v ramci
            vkladanych dat. Tato pozice nemusi odpovidat adresovacimu modelu.
            Veskere informace jsou prepocitany a spravne ulozeny.
            \param pData vkladana data. Ukazatel nesmi byt nulovy.
            \param rTable tabulka zaznamu o relokaci ve vkladanych datech.
            \param nPos presna poloha ve slove kam byla data ulozena.
            \return adresa kam byla data ulozena.
            \sa CSectionData::Add()
            \sa CSectionData::GetCurAddress()
        */
        int  AddData(const char * pData, CRelocationTable & rTable, int * nPos = 0);

        //! Vlozi udaj o cislech radku.
        /*! \sa CLineNumberTable::Add() */
        CReference  * AddLineInfo(const CReference & rReference);
        
        //! Vlozi udaj o relokaci.
        /*! \sa CRelocationTable::Add() */
        CRelocation * AddRelocation(const CRelocation & rRelocation);

        int  GetRelocData(CRelocation * pRelocation);
        void SetRelocData(CRelocation * pRelocation, int nVal);

        void CorrectReloc(CRelocation * pRelocation, int nInstrEnd);

    public:
        //! Nahraje sekci ze souboru.
        /*! \sa CPersistentObject::LoadFromStream() */
        void LoadFromStream(CInputStream & rStream,
                CPersistentObject::Scheduler & vScheduler);
        
        //! Ulozi sekci do souboru.
        /*! \sa CPersistentObject::SaveToStream() */
        void SaveToStream(COutputStream & rStream,
                CPersistentObject::Scheduler & vScheduler);

        //! Jmeno sekce.
        inline std::string GetName()
        {
            return m_sName;
        }
        //! Nastavi jmeno sekce.
        void SetName(const std::string & sName);

        //! Adresa dat sekce v pameti.
        int GetMemoryAddress();
        //! Nastavi adresu dat sekce v pameti.
        void SetMemoryAddress(int nAddress);

        //! Typ sekce.
        inline eType GetType()
        {
            return m_nType;
        }
        //! Nastavi typ sekce.
        /*! \return predchozi typ sekce. */
        eType SetType(eType type);

        //! Unikatni cislo sekce.
        /*! Pomoci id je sekce identifikovana ve vystupnim souboru. */
        int ID();

        //! Pocet dat sekce (binarnich udaju).
        /*! \sa CSectionData::Count() */
        int SectionDataCount();

        //! Pocet relokovanych udaju.
        /*! \sa CRelocationTable::Count() */
        int RelocationCount();

        //! Pocet udaju o cislech radku.
        /*! \sa CLineNumberTable::Count() */
        int LineNumberCount();

        //! Vraci relokovany udaj.
        /*! \param i index relokovaneho udaje.
            \sa CRelocationTable::Get()
        */
        CRelocation * GetRelocation(int i);

        //! Vraci udaj o cislu radku.
        /*! \param i index udaje o cislu radku.
            \sa CLineNumberTable::Get()
        */
        CReference * GetReference(int i);

        CSectionData * GetSectionData();
        CSymbolTable * GetSymbolTable();
        CRelocationTable * GetRelocationTable();
        CLineNumberTable * GetLineNumberTable();

        //! Vraci objekt vystupniho formatu, ke kteremu sekce nalezi.
        inline CObjectFile * GetObjectFile()
        {
            return m_pObjectFile;
        }

    private:
        CSectionData       *m_pSectionData;
        CRelocationTable   *m_pRelocationTable;
        CLineNumberTable   *m_pLineNumberTable;

    private:
        std::string         m_sName;
        int                 m_nMemoryAddress;
        eType               m_nType;

        CObjectFile        *m_pObjectFile;
    };

    ///////////////////////////////////////////////////////////////////////////

    //! Hlavni trida vystupniho formatu.
    /*!
      Jedna se o zakladni tridu vystupniho formatu, ktera umoznuje nahravat a
      ukladat tento format ze/do souboru. Pomoci teto tridy muzeme dynamicky
      pracovat se sekcemi a symbolickymi tabulkami.
    */
    class CObjectFile
    {
    public:
        /*! \param nWordLength pocet bitu ve slove.
            \param nByteOrder zpusob ulozeni slova.
            \param bRelative relativni/absolutni informace.
            \sa GetWordLength()
            \sa GetByteOrder()
            \sa IsRelative()
        */
        CObjectFile(int nWordLength = 0, eByteOrder nByteOrder = UNDEFINED,
            bool bRelative = true);
        ~CObjectFile();

    private:
        //! Uvolni veskera alokovana data.
        void Free();

    public:
        //! Nahraje vystupni format ze souboru.
        /*! \param filename jmeno vstupniho souboru. */
        void LoadFile(const std::string filename);

        //! Ulozi vystupni format do souboru.
        /*! \param filename jmeno vystupniho souboru. */
        void SaveFile(const std::string filename);

        //! Mapuje vsechny perzistentni objekty do souboru.
        /*! Tato metoda je volana automaticky pred vlastnim ulozenim
            do souboru.
        */
        int  RemapToFile();

        //! Kontrola a dynamicke mapovani objektu.
        /*! Po nacteni objektu ze souboru jsou veskere reference na objekty
            zkontrolovany a dynamicky mapovany.
        */
        void CheckLinks();

    public:
        //! Typy priznaku vystupniho formatu.
        enum eObjectFlags {
            R = 1 << 0, /*!< Obsahuje relativni informaci. */
            X = 1 << 1, /*!< Neobsahuji nevyresene odkazy (proveditelny). */
            L = 1 << 2, /*!< Neobsahuje informaci o radcich zdroj. souboru. */
            S = 1 << 3  /*!< Neobsahuje symbolickou informaci. */
        };

        enum eAllocation {
            ABSOLUTE,
            RELATIVE
        };

        const static int    nFlagsCount = 4;        //!< Pocet typu priznaku.

        const static char  *sMagicString;           //!< Magicky retezec.
        const static char   cEndLine    = '\n';     //!< Konec radku.
        const static int    nHeaderSize = 9;        //!< Pocet radku hlavicky.

    public:
        //! Vytvori novou tabulku symbolu.
        CSymbolTable * CreateSymbolTable();
        
        //! Vytvori novou sekci.
        /*! \sa CSection::CSection() */
        CSection * CreateSection(CSection::eType nType = CSection::UNDEFINED,
            const string & sName = "SECTION");

        void AddSection(CSection *pSection);

    public:
        //! Pocet bitu ve slove.
        inline int GetWordLength()
        {
            return m_nWordLength;
        }

        inline void SetWordLength(int nWordLength)
        {
            m_nWordLength = nWordLength;
        }


        //! Zpusob ulozeni slova (Endianness).
        inline eByteOrder GetByteOrder()
        {
            return m_nByteOrder;
        }
	
		inline void SetByteOrder(eByteOrder nByteOrder)
        {
            m_nByteOrder = nByteOrder;
        }

        //! Casove razitko.
        inline CTimeStamp & GetTimeStamp()
        {
            return m_cTimeStamp;
        }

        //! Soubor obsahuje Relativni/Absolutni informaci.
        /*! Vystupni format muze obsahovat pouze sekce a relativni nebo
            absolutni pozici v pameti. Kombinace obou typu v jednom souboru
            neni mozna.
        */
        inline bool IsRelative()
        {
            return m_bRelative;
        }

        inline void SetSectionsAlloc(eAllocation nType)
        {
            m_bRelative = (nType == RELATIVE);
        }

        //! Priznaky vystupniho formatu.
        int GetFlags();

        //! Pocet sekci.
        int SectionsCount();
        //! Pocet symbolickych tabulek.
        int SymbolTablesCount();

        //! Vraci symbolickou tabulku.
        /*! \param i index symbolicke tabulky.
            \sa CSymbolTableContainer::Get()
        */
        CSymbolTable * GetSymbolTable(int i = 0);

        //! Vraci sekci.
        /*! \param i index sekce.
            \sa CSectionContainer::Get()
        */
        CSection * GetSection(int i);
		int GetSection(const string & sName, CSectionContainer & rSections);

        //! Vraci index odkazovane sekce.
        /*! \param pSection odkaz na sekci ruzny od 0.
            \return pokud neni sekce nalezena je vyvolana vyjimka.
            \sa CSectionContainer::Find()
        */
        int FindSection(const CSection * pSection);

    private:
        CSectionContainer      *m_pSections;
        CSymbolTableContainer  *m_pSymbolTables;

    private:
        int             m_nWordLength;  //!< Pocet bitu ve slove.
        eByteOrder      m_nByteOrder;   //!< Zpusob ulozeni.
        CTimeStamp      m_cTimeStamp;   //!< Casove razitko.

        bool            m_bRelative;    //!< Obsahuje relativni informaci.
    };

    ///////////////////////////////////////////////////////////////////////////

    //! Kolekce sekci.
    /*!
      Trida organizuje sekce v pameti a zaroven je schopna vsechny sekce nahrat
      a ulozit ze/do souboru.
    */
    class CSectionContainer : public CPersistentObject
    {
    public:
        //! Standardni konstruktor.
        /*! \param parent objekt vystupniho formatu ke kteremu nalezi vsechny sekce.
            \param nCount pocet sekci ktere budou pri vytvoreni alokovany.
            \sa Count()
        */
        CSectionContainer(CObjectFile * parent = 0, int nCount = 0);

        //! Konstruktor slouzi k podpore nacteni dat ze souboru.
        /*! \param nAddress pocatecni adresa hlavicky prvni sekce v souboru.
            \param nCount pocet sekci.
            \param parent objekt vystupniho formatu ke kteremu nalezi vsechny sekce.
            \sa GetAddress()
            \sa Count()
        */
        CSectionContainer(int nAddress, int nCount, CObjectFile * parent = 0) :
            m_nAddress(nAddress) ,
            m_nCount(nCount)     ,
            m_pObjectFile(parent)
        {
        }

        virtual ~CSectionContainer();

    private:
        //! Uvolni veskera alokovana data.
        void Free();

    public:
        const static int nRecordSize = 0;        //!< Pocet radku zaznamu.

    public:
        virtual int Count();
        virtual int GetAddress()
        {
            return m_nAddress;
        }

        virtual void SetAddress(int nAddress)
        {
            m_nAddress = nAddress;
        }

        virtual int  GetSize();

        //! Nahraje sekce ze souboru.
        /*! \sa CPersistentObject::LoadFromStream() */
        virtual void LoadFromStream(CInputStream & rStream,
                        Scheduler & vScheduler);

        //! Ulozi sekce do souboru.
        /*! \sa CPersistentObject::SaveToStream() */
        virtual void SaveToStream(COutputStream & rStream,
                        Scheduler & vScheduler);

        void Add(CSection *pSection);

        //! Vraci sekci.
        /*! \param i index sekce. */
        CSection * Get(int i);

        //! Vytvori novou sekci.
        /*! \sa CSection::CSection() */
        CSection * Create(CSection::eType nType, const string & sName);

        //! Vraci index odkazovane sekce.
        /*! \param section odkaz na sekci.
            \return pokud neni sekce nalezena vraci hodnotu < 0,
                jinak vraci index sekce.
        */
        int Find(const CSection * section);

        //! Pretizeny operator vraci sekci.
        /*! \param i index sekce.
            \sa Get()
        */
        inline CSection * operator[](int i)
        {
            return Get(i);
        }

        //! Pocet dat ve vsech sekcich dohromady.
        int SectionDataCount();
        //! Pocet relokovanych udaju ve vsech sekcich dohromady.
        int RelocationCount();
        //! Pocet udaju o radcich ve vsech sekcich dohromady.
        int LineNumberCount();

    private:
        typedef std::vector<CSection *>     sectionVector;
        typedef sectionVector::iterator     sectionVectorIterator;

        sectionVector   m_vSections;
        int             m_nAddress;
        int             m_nCount;

    private:
        CObjectFile    *m_pObjectFile;
    };

    ///////////////////////////////////////////////////////////////////////////

    //! Kolekce symbolickych tabulek.
    /*!
      Trida organizuje tabulky symbolu v pameti a zaroven je schopna vsechny
      tabulky nahrat a ulozit ze/do souboru.
    */
    class CSymbolTableContainer : public CPersistentObject
    {
    public:
        //! Standardni konstruktor.
        /*! \param parent objekt vystupniho formatu ke kteremu nalezi vsechny
                symbolicke tabulky.
            \param nCount pocet symbolickych tabulek ktere budou pri vytvoreni
                alokovany.
            \sa Count()
        */
        CSymbolTableContainer(CObjectFile * parent = 0, int nCount = 0);

        //! Konstruktor slouzi k podpore nacteni dat ze souboru.
        /*! \param nAddress pocatecni adresa hlavicky prvni symbolicke tabulky
                v souboru.
            \param nCount pocet symbolickych tabulek.
            \param parent objekt vystupniho formatu ke kteremu nalezi vsechny
                symbolicke tabulky.
            \sa GetAddress()
            \sa Count()
        */
        CSymbolTableContainer(int nAddress, int nCount, CObjectFile * parent = 0) :
            m_nAddress(nAddress) ,
            m_nCount(nCount)     ,
            m_pObjectFile(parent)
        {
        }

        virtual ~CSymbolTableContainer();

    private:
        //! Uvolni veskera alokovana data.
        void Free();

    public:
        const static int nRecordSize = 0;        //!< Pocet radku zaznamu.

    public:
        virtual int Count()
        {
            return (int)m_vSymbolTables.size();
        }

        virtual int GetAddress()
        {
            return m_nAddress;
        }

        virtual void SetAddress(int nAddress)
        {
            m_nAddress = nAddress;
        }

        virtual int  GetSize();

        //! Nahraje symbolicke tabulky ze souboru.
        /*! \sa CPersistentObject::LoadFromStream() */
        virtual void LoadFromStream(CInputStream & rStream,
                        Scheduler & vScheduler);

        //! Ulozi symbolicke tabulky do souboru.
        /*! \sa CPersistentObject::SaveToStream() */
        virtual void SaveToStream(COutputStream & rStream,
                        Scheduler & vScheduler);

        //! Vraci tabulku symbolu.
        /*! \param i index sekce.
        */
        CSymbolTable * Get(int i);

        //! Vytvori novou tabulku symbolu.
        /*! \sa CSymbolTable::CSymbolTable() */
        CSymbolTable * Create();

        //! Vraci index odkazovane tabulky symbolu.
        /*! \param symtable odkaz na tabulku symbolu.
            \return pokud neni tabulka symbolu nalezena vraci hodnotu < 0,
                jinak vraci index sekce.
        */
        int Find(const CSymbolTable * symtable);

        //! Pretizeny operator tabulku symbolu.
        /*! \param i index sekce.
            \sa Get()
        */
        inline CSymbolTable * operator[](int i)
        {
            return Get(i);
        }

        //! Pocet symbolu ve vsech tabulkach dohromady.
        int SymbolCount();
        //! Pocet externich symbolu ve vsech tabulkach dohromady.
        int ExternSymbolCount();

    private:
        typedef std::vector<CSymbolTable *>     symbolTableVector;
        typedef symbolTableVector::iterator     symbolTableVectorIterator;

        symbolTableVector   m_vSymbolTables;
        int                 m_nAddress;
        int                 m_nCount;

    private:
        CObjectFile    *m_pObjectFile;
    };

    ///////////////////////////////////////////////////////////////////////////
    typedef std::string CDataItem; //!< Jeden radek (slovo) v datech sekce.

    //! Data sekce.
    /*!
      Trida zapouzdruje data sekce a zaroven je schopna vsechny data nahrat
      a ulozit ze/do souboru.
    */
    class CSectionData : public CPersistentObject
    {
    public:
        //! Standardni konstruktor.
        /*! \param nWordLength pocet bitu ve slove.
            \sa CObjectFile::GetWordLength()
        */
        CSectionData(int nWordLength);

        //! Konstruktor slouzi k podpore nacteni dat ze souboru.
        /*! \param nAddress pocatecni adresa dat sekce.
            \param nCount pocet udaju v datech sekce.
            \param nWordLength pocet bitu ve slove.
            \sa GetAddress()
            \sa Count()
            \sa CObjectFile::GetWordLength()
        */
        CSectionData(int nAddress, int nCount, int nWordLength) :
            m_nWordLength(nWordLength) ,
            m_nCurAddress(0)           ,
            m_nPos(0)                  ,
            m_nAddress(nAddress)       ,
            m_nCount(nCount)
        {
        }

        CSectionData(CSectionData & rData);

        virtual ~CSectionData();

    public:
        const static int nRecordSize = 1;        //!< Pocet radku zaznamu.

    public:
        //! Vymaze vsechny data sekce.
        void Clear();

        //! Prida data.
        /*! Data se pridavaji na aktualni pozici ukazatele v datech sekce.
            Po pridani je tento ukazatel posunut na aktualni pozici.
            \sa GetCurAddress()
        */
        void Add(const char * pData);

        //! Vraci data.
        /*! Velikost dat je dana velikosti slova.
            \param i adresa dat.
            \sa CObjectFile::GetWordLength()
        */
        CDataItem Get(int i);

        void Set(int i, const CDataItem & rVal);

        //! Ziska aktualni polohu ukazatele v datech sekce.
        /*! Pri zapisu/cteni do dat sekce se tento ukazatel posouva.
            Touto funkci zjistime jeho aktualni polohu.
            \param nPos muze byt 0. Pokud nebylo prectene/zapsano cele slovo,
                je v tomto parametru vracena nenulova hodnota v bitech. Tato
                hodnota predstavuje presnou polohu v aktualnim slove.
            \return vraci aktualni polohu. Hodnota 1 odpovida velikosti jednoho
                slova. Adresy jsou indexovany od 0.
            \sa Add()
        */
        int GetCurAddress(int * nPos);

    public:
        //! Pocet binarnich udaju (pocet radku).
        virtual int Count()
        {
            return (int)m_vSectionData.size();
        }

        //! Adresa dat pro sekci.
        virtual int GetAddress()
        {
            return m_nAddress;
        }

        virtual void SetAddress(int nAddress)
        {
            m_nAddress = nAddress;
        }

        inline int GetWordLength()
        {
            return m_nWordLength;
        }

        virtual int  GetSize();

        //! Nahraje data sekce ze souboru.
        /*! \sa CPersistentObject::LoadFromStream() */
        virtual void LoadFromStream(CInputStream & rStream,
                        Scheduler & vScheduler);

        //! Ulozi data sekce do souboru.
        /*! \sa CPersistentObject::SaveToStream() */
        virtual void SaveToStream(COutputStream & rStream,
                        Scheduler & vScheduler);

        //! Pretizeny operator dat sekce.
        /*! \param i adresa dat sekce.
            \sa Get()
        */
        inline CDataItem operator[](int i)
        {
            return Get(i);
        }

    private:
        typedef std::vector<CDataItem>  dataVector;
        typedef dataVector::iterator    dataVectorIterator;

        int             m_nWordLength;  //!< Pocet bitu ve slove.
        int             m_nCurAddress;
        int             m_nPos;

        dataVector      m_vSectionData;
        int             m_nAddress;
        int             m_nCount;
    };

    ///////////////////////////////////////////////////////////////////////////

    //! Udaje o relokaci.
    /*!
      Trida obsahuje relokovane udaje sekce a zaroven je schopna vsechny udaje
      nahrat a ulozit ze/do souboru.
    */
    class CRelocationTable : public CPersistentObject
    {
    public:
        //! Standardni konstruktor.
        CRelocationTable();

        //! Konstruktor slouzi k podpore nacteni dat ze souboru.
        /*! \param nAddress pocatecni adresa relokovanych udaju sekce.
            \param nCount pocet relokovanych udaju.
            \sa GetAddress()
            \sa Count()
        */
        CRelocationTable(int nAddress, int nCount) :
            m_nAddress(nAddress) ,
            m_nCount(nCount)
        {
        }

        CRelocationTable(CRelocationTable & rRelocTable,
            CSymbolTable *pSymTable = 0);

        virtual ~CRelocationTable();

    private:
        //! Uvolni veskera alokovana data.
        void Free();

    public:
        const static int nRecordSize = 0;        //!< Pocet radku zaznamu.

    public:
        //! Prida novy udaj o relokaci.
        CRelocation * Add(const CRelocation & rRelocation);

    public:
        //! Pocet relokovanych udaju.
        virtual int Count()
        {
            return (int)m_vRelocations.size();
        }

        //! Adresa dat pro udaje o relokaci.
        virtual int GetAddress()
        {
            return m_nAddress;
        }

        virtual void SetAddress(int nAddress)
        {
            m_nAddress = nAddress;
        }

        virtual int  GetSize();

        //! Nahraje udaje o relokaci ze souboru.
        /*! \sa CPersistentObject::LoadFromStream() */
        virtual void LoadFromStream(CInputStream & rStream,
                        Scheduler & vScheduler);

        //! Ulozi udaje o relokaci do souboru.
        /*! \sa CPersistentObject::SaveToStream() */
        virtual void SaveToStream(COutputStream & rStream,
                        Scheduler & vScheduler);

        //! Vraci udaj o relokaci.
        /*! \param i index relokovaneho udaje. */
        CRelocation * Get(int i);

        //! Vytvori novy relokovany udaj.
        /*! \sa CRelocation::CRelocation() */
        CRelocation * Create();

        //! Vraci index relokovaneho udaje.
        /*! \param relocation odkaz na relokovany udaj.
            \return pokud neni relokovany udaj nalezen vraci hodnotu < 0,
                jinak vraci index relokovaneho udaje.
        */
        int Find(const CRelocation * relocation);

        //! Pretizeny operator vraci udaj o relokaci.
        /*! \param i index relokovaneho udaje.
            \sa Get()
        */
        inline CRelocation * operator[](int i)
        {
            return Get(i);
        }

    private:
        typedef std::vector<CRelocation *>  relocationVector;
        typedef relocationVector::iterator  relocationVectorIterator;

        relocationVector    m_vRelocations;
        int                 m_nAddress;
        int                 m_nCount;
    };

    ///////////////////////////////////////////////////////////////////////////

    //! Tabulka udaju o relokaci.
    /*!
      Trida zapouzdruje veskera data potrebna k jednomu zaznamu o relokaci.
    */
    class CRelocation
    {
    public:
        CRelocation();

        CRelocation(const CRelocation & rRelocation,
            CSymbolTable *pSymTable = 0);

        ~CRelocation();

    public:
        //! Model pameti.
        enum eMemoryFlags {
            RELATIVE  = 1 << 0, //!< Je nastaveno ? relativni : absolutni.
            FLAT      = 1 << 1  //!< Plochy adresovaci model.
        };

        const static int nRecordSize = 7;        //!< Pocet radku zaznamu.

    public:
        //! Adresa relokovaneho udaje v datech sekce.
        inline int GetMemoryAddress()
        {
            return m_nMemoryAddress;
        }

        //! Nastavi adresu relokovaneho udaje v datech sekce.
        inline void SetMemoryAddress(int nMemoryAddress)
        {
            m_nMemoryAddress = nMemoryAddress;
        }

        //! Typ relokace.
        inline int GetType()
        {
            return m_nType;
        }

        //! Nastavi typ relokace.
        inline void SetType(int nType)
        {
            m_nType = nType;
        }

        //! Pocet slov tvorici/obsahujici adresu.
        inline int GetAddressSize()
        {
            return m_nAddressSize;
        }

        //! Nastavi pocet slov tvorici/obsahujici adresu.
        inline void SetAddressSize(int nAddressSize)
        {
            m_nAddressSize = nAddressSize;
        }

        //! Nejnizsi bit adresy.
        /*! Bere se v uvahu jen pro jedno slovo v adrese. */
        inline int GetAddressLowBit()
        {
            return m_nAddressLowBit;
        }

        //! Nastavi nejnizsi bit adresy.
        /*! \sa GetAddressLowBit() */
        inline void SetAddressLowBit(int nAddressLowBit)
        {
            m_nAddressLowBit = nAddressLowBit;
        }

        //! Nejvyssi bit adresy.
        /*! Bere se v uvahu jen pro jedno slovo v adrese. */
        inline int GetAddressHighBit()
        {
            return m_nAddressHighBit;
        }

        //! Nastavi nejvyssi bit adresy.
        /*! \sa GetAddressHighBit() */
        inline void SetAddressHighBit(int nAddressHighBit)
        {
            m_nAddressHighBit = nAddressHighBit;
        }

        inline int GetDisplacement()
        {
            return m_nDisplacement;
        }

        inline void SetDisplacement(int nDisplacement)
        {
            m_nDisplacement = nDisplacement;
        }

        //! Symbol ktery se relokuje.
        inline CSymbol * GetSymbol()
        {
            return m_pSymbol;
        }

        //! Nastavi symbol ktery se relokuje.
        inline void SetSymbol(CSymbol * pSymbol)
        {
            m_pSymbol = pSymbol;
        }

        //! Odkaz do tabulky symbolu (index, od 0).
        /*! Ziska index na symbol.
            \sa GetSymbol()
        */
        int GetSymbolID();

        //! Nahraje relokovany udaj ze vstupniho proudu.
        void LoadFromStream(CInputStream & rStream);

        //! Ulozi relokovany udaj do vystupniho proudu.
        void SaveToStream(COutputStream & rStream);

    private:
        int m_nMemoryAddress;   //!< Adresa relokovaneho udaje v datech sekce.
        int m_nType;            //!< Typ relokace.

        int m_nAddressSize;     //!< Pocet slov tvorici/obsahujici adresu.

        //! Nejnizsi bit adresy.
        /*! Bere se v uvahu jen pro jedno slovo v adrese. */
        int m_nAddressLowBit;

        //! Nejvyssi bit adresy.
        /*! Bere se v uvahu jen pro jedno slovo v adrese. */
        int m_nAddressHighBit;

		int m_nDisplacement;

        int      m_nSymbolID;   //!< Odkaz do tabulky symbolu (index, od 0).
        CSymbol *m_pSymbol;
    };

    ///////////////////////////////////////////////////////////////////////////

    //! Tabulka udaju o cislech radku.
    /*!
      Trida obsahuje udaje o cislech radku sekce a zaroven je schopna vsechny
      udaje nahrat a ulozit ze/do souboru.
    */
    class CLineNumberTable : public CPersistentObject
    {
    public:
        //! Standardni konstruktor.
        CLineNumberTable();

        //! Konstruktor slouzi k podpore nacteni dat ze souboru.
        /*! \param nAddress pocatecni adresa udaju o cislech radku.
            \param nCount pocet udaju o cislech radku.
            \sa GetAddress()
            \sa Count()
        */
        CLineNumberTable(int nAddress, int nCount) :
            m_nAddress(nAddress) ,
            m_nCount(nCount)
        {
        }

        CLineNumberTable(CLineNumberTable & rLineInfo,
            CSymbolTable *pSymTable = 0);

        virtual ~CLineNumberTable();

    private:
        //! Uvolni veskera alokovana data.
        void Free();

    public:
        const static int nRecordSize = 1;        //!< Pocet radku zaznamu.

    public:
        //! Prida novy udaj o cisle radku.
        CReference * Add(const CReference & rReference);

    public:
        //! Pocet udaju v tabulce o radcich.
        virtual int Count()
        {
            return (int)m_vReferences.size();
        }

        //! Adresa dat pro radkovou informaci.
        virtual int GetAddress()
        {
            return m_nAddress;
        }

        virtual void SetAddress(int nAddress)
        {
            m_nAddress = nAddress;
        }

        virtual int  GetSize();

        //! Nahraje udaju o cislech radku ze souboru.
        /*! \sa CPersistentObject::LoadFromStream() */
        virtual void LoadFromStream(CInputStream & rStream,
                        Scheduler & vScheduler);

        //! Ulozi udaju o cislech radku do souboru.
        /*! \sa CPersistentObject::SaveToStream() */
        virtual void SaveToStream(COutputStream & rStream,
                        Scheduler & vScheduler);

        //! Vraci udaj o cisle radku.
        /*! \param i index udaje o cisle radku. */
        CReference * Get(int i);

        //! Vytvori novy udaj cisle radku.
        /*! \sa CReference::CReference() */
        CReference * Create();

        //! Vraci index udaje o cisle radku.
        /*! \param reference odkaz na udaj o cisle radku.
            \return pokud neni udaj o cisle radku nalezen vraci hodnotu < 0,
                jinak vraci index udaje o cisle radku.
        */
        int Find(const CReference * reference);

        //! Pretizeny operator vraci udaj o cisle radku.
        /*! \param i index udaje o cisle radku.
            \sa Get()
        */
        inline CReference * operator[](int i)
        {
            return Get(i);
        }

    private:
        typedef std::vector<CReference *>   referenceVector;
        typedef referenceVector::iterator   referenceVectorIterator;

        referenceVector     m_vReferences;
        int                 m_nAddress;
        int                 m_nCount;
    };


    ///////////////////////////////////////////////////////////////////////////

    //! Odkaz na cislo radku v sekci.
    /*!
      Trida zapouzdruje veskera data potrebna k jednomu zaznamu o odkazu na
      cislo radku v sekci. Odkaz radku je spojen s odkazem na data sekce. Radky
      se cisluji od 1. Radek 0 slouzi k odkazu na sybol. Veskere udaje slouzi k
      podpore pri ladeni programu.
    */
    class CReference
    {
    public:
        //! Standardni konstruktor.
        CReference();

        //! Vytvori referenci na symbol.
        /*! \sa SetSymbol() */
        CReference(CSymbol * pSymbol);

        //! Vytvori spojeni mezi cislem radku a adresou.
        CReference(int nLineNumber, int nMemoryAddress);

        CReference(const CReference & rReference,
            CSymbolTable *pSymTable = 0);

        ~CReference();

    public:
        const static int nRecordSize = 2;        //!< Pocet radku zaznamu.

    public:
        //! Relativni cislo radku.
        /*! Cislovat zaciname od 1, 0 ma specialni vyznam.
            Nulovy udaj je vzdy prvni!
        */
        inline int GetLineNumber()
        {
            return m_nLineNumber;
        }

        //! Jedna se o odkaz na symbol?
        inline bool IsSymbolName() const
        {
            return m_nLineNumber == 0;
        }

        //! Vraci adresu do dat sekce.
        /*! Na teto adrese lezi kod v binarni podobe, ktery odpovida zdrojovemu
            kodu na danem radku. Pokud tento odkaz neobsahuje adresu do dat
            sekce, ale odkaz na symbol, metoda vraci zapornou hodnotu.
            \sa GetLineNumber()
        */
        inline int GetMemoryAddress()
        {
            return m_nLineNumber != 0 ? m_nMemoryAddress : -1;
        }

        //! Symbol na ktery je odkazovano.
        /*! Pokud neni nastaven odkaz na symbol sekce, ale je nastavena adresa
            do dat, metoda vraci nulovy ukazatel.
            \sa GetLineNumber()
        */
        inline CSymbol * GetSymbol()
        {
            return m_nLineNumber == 0 ? m_pSymbol : 0;
        }

        //! Nastavi odkaz na symbol.
        inline void SetSymbol(CSymbol * pSymbol)
        {
            m_pSymbol     = pSymbol;
            m_nLineNumber = 0;
        }

        //! Odkaz do tabulky symbolu (index, od 0).
        /*! Ziska index na symbol. Pokud neni nastaven odkaz na symbol sekce,
            ale je nastavena adresa do dat, metoda vraci zapornou hodnotu.
            \sa GetSymbol()
        */
        int GetSymbolID();

        //! Nahraje odkaz z vstupniho proudu.
        void LoadFromStream(CInputStream & rStream);

        //! Ulozi odkaz do vystupniho proudu.
        void SaveToStream(COutputStream & rStream);

    private:
        //! Vraci adresu na data nebo index na symbol.
        /*! Hodnota je vracena podle aktualniho stavu odkazu. Tato metoda se
            pouziva predevsim k ulozeni hodnoty do vystupniho souboru.
        */
        int GetAddressOrIndex();

    private:
        //! Relativni cislo radku.
        /*! Cislovat zaciname od 1, 0 ma specialni vyznam.
            Nulovy udaj musi byt prvni!
        */
        int         m_nLineNumber;      
        int         m_nMemoryAddress;   //!< m_nLineNumber != 0: adresa v kodu sekce.

        int         m_nSymbol;          //!< m_nLineNumber == 0: index do tabulky jmen.
        CSymbol    *m_pSymbol;
    };

    ///////////////////////////////////////////////////////////////////////////

    //! Tabulka symbolu.
    /*!
      Trida slouzi k podpore symbolickych tabulek a praci se symbolickou
      informaci.
    */
    class CSymbolTable
    {
    public:
        CSymbolTable();
        virtual ~CSymbolTable();

    private:
        //! Uvolni veskera alokovana data.
        void Free();

    public:
        const static int nRecordSize = 2;        //!< Pocet radku zaznamu.

    public:
        //! Pocet udaju v tabulce.
        virtual int Count()
        {
            return (int)m_vSymbols.size();
        }

        //! Absolutni adresa symbolicke informace.
        virtual int GetAddress()
        {
            return m_nAddress;
        }

        //! Celkova velikost symbolicke tabulky.
        virtual int  GetSize();

        //! Nahraje tabulku symbolu ze vstupniho proudu.
        virtual void LoadFromStream(CInputStream & rStream);

        //! Ulozi tabulku symbolu do vystupniho proudu.
        virtual void SaveToStream(COutputStream & rStream);

        //! Pretizeny operator vraci symbol.
        /*! \param i index symbolu.
        */
        inline CSymbol * operator[](int i)
        {
            return m_vSymbols[i];
        }

        //! Vraci odkaz na symbol.
        /*! Pomoci teto metody obdrzime vzdy odkaz na symbol. Tento symbol byl
            jiz v tabulce nebo se vytvori novy.
            \param sName jmeno pozadovaneho symbolu.
            \return pokud neni symbol nalezen vytvori novy zaznam a ten vrati.
        */
        CSymbol * LookUpSymbol(const std::string & sName);

        CSymbol * LookUpSymbol(const char *sName);

        //! Vraci odkaz na symbol.
        /*! \param sName jmeno hledaneho symbolu.
            \return pokud neni symbol nalezen vraci nulovy ukazatel.
        */
        CSymbol * FindSymbol(const std::string & sName);

        //! Vraci odkaz na symbol.
        /*! \param i index symbolu.
            \return pokud symbol neexistuje vyvola vyjimku.
        */
        CSymbol * GetSymbol(int i);

        //! Vraci index odkazovaneho symbolu.
        /*! \param pSymbol odkaz na symbol.
            \return pokud neni symbol nalezen vraci nulovy ukazatel,
                jinak vraci index symbolu.
        */
        int FindSymbol(CSymbol * pSymbol);

        //! Vraci pocet vsech externich symbolu.
        int ExternSymbolCount();
		
	//ahs 07/08/10  Pridana nova metoda pro pridani noveho symbolu. Metoda LookUpSymbol 
	//              sice slouzi k necemu podobnemu, ale je nesmyslne pojmenovana.
		//! Ulozi novy symbol do tabulky symbolu.
		/*! Vyvola assert pokud je jiz symbol se stejnym nazvem v tabulce pritomen.
		 *  Tabulka symbolu se stane vlastnikem predaneho objektu.
		 */
		void AddSymbol(CSymbol* pSymbol);
		
    private:
        typedef std::map<string, CSymbol *> symbolMap;
        typedef symbolMap::iterator         symbolMapIterator;

        typedef std::vector<CSymbol *>      symbolVector;
        typedef symbolVector::iterator      symbolVectorIterator;

        symbolMap           m_mSymbols;
        symbolVector        m_vSymbols;
        int                 m_nAddress;
    };

    ///////////////////////////////////////////////////////////////////////////

    //! Rozsirujici informace symbolu.
    /*!
      Kazdy symbol muze obsahovat rozsirujici informace. Tato trida zapouzdruje
      veskerou funkcionalitu k udrzbe rozsirujicich informaci. V aktualni verzi
      vystupniho formatu se do techto informaci zahrnul nazev zdrojoveho
      souboru assembleru.
    */
    class CExpansionTable
    {
    public:
        CExpansionTable();
        CExpansionTable(const CExpansionTable & rExpTable);
        virtual ~CExpansionTable();

    public:
        const static int nRecordSize = 1;        //!< pocet radku zaznamu
        typedef std::string expansionDataItem;

    public:
        //! Jmeno souboru zdrojoveho kodu.
        inline std::string & GetFileName()
        {
            return m_sFileName;
        }

        //! Nastavi jmeno souboru zdrojoveho kodu.
        inline void SetFileName(const std::string & sFileName)
        {
            m_sFileName = sFileName;
        }

	   //! Nastavi jmeno souboru zdrojoveho kodu.
        inline void SetFileName(const char *sFileName)
        {
            m_sFileName = sFileName;
        }
        
    public:
        //! Pocet radku rozsirujici informace.
        int Count();

        //! Nahraje rozsirujici informace ze vstupniho proudu.
        void LoadFromStream(CInputStream & rStream);

        //! Ulozi rozsirujici informace do vystupniho proudu.
        void SaveToStream(COutputStream & rStream);

        //! Pretizeny operator vraci jeden radek rozsirujici informace.
        /*! \param i index rozsirujici informace.
            \sa Get()
        */
        inline expansionDataItem & operator[](int i)
        {
            return m_vExpansionData[i];
        }

    private:
        typedef std::vector<expansionDataItem>  expansionVector;
        typedef expansionVector::iterator       expansionVectorIterator;

        std::string     m_sFileName;

        expansionVector m_vExpansionData;
    };

    ///////////////////////////////////////////////////////////////////////////

    //! Symbolicka informace.
    /*!
      Trida zapouzdruje veskera data potrebna k jednomu zaznamu o symbolu.
    */
    class CSymbol
    {
    public:
        //! Vytvori symbol daneho jmena
        /*! Symboly jsou svazany se symbolickou tabulkou.
        */
        CSymbol(CSymbolTable * pSymTable, const std::string & sName);
        CSymbol(const CSymbol & rSymbol, CSymbolTable * pSymTable = 0);
        ~CSymbol();

    public:
        //! Typ symbolu.
        enum eType {
            UNDEFINED , //!< Nedefinovany typ.
            PRIVATE   , //!< Soukromi symbol.
            PUBLIC    , //!< Verejny.
            EXTERN      //!< Externi (ocekavan jinde).
        };

        const static int nRecordSize = 7;        //!< Pocet radku zaznamu.

    public:
        //! Jmeno symbolu.
        inline std::string GetName()
        {
            return m_sName;
        }

        //! Typ symbolu.
        inline eType GetType()
        {
            return m_nType;
        }

        //! Nastavi typ symbolu.
        inline void SetType(eType nType)
        {
            m_nType = nType;
        }

        inline bool IsExtern()
        {
            return m_nType == EXTERN;
        }

        //! Sekce ke ktere symbol nalezi.
        inline CSection * GetSection()
        {
            return m_pSection;
        }

        //! Vraci rozsirujici informace o symbolu.
        inline CExpansionTable & GetExpansion()
        {
            return m_cExpansionTable;
        }

        //! Nastavi sekci ke ktere symbol nalezi.
        inline void SetSection(CSection * pSection)
        {
            m_pSection = pSection;
        }

        //! Index sekce ke ktere symbol nalezi.
        /*! \sa GetSection() */
        int GetSectionID();

        //! Relativni adresa v datech sekce.
        inline int GetMemoryAddress()
        {
            return m_nMemoryAddress;
        }

        //! Nastavi relativni adresu v datech sekce.
        inline void SetMemoryAddress(int nAddress)
        {
            m_nMemoryAddress = nAddress;
        }

        //! Pocet radku rozsirujici informace.
        inline int ExpansionCount()
        {
            return m_cExpansionTable.Count();
        }

        //! Nahraje symbol ze vstupniho proudu.
        void LoadFromStream(CInputStream & rStream);

        //! Nahraje symbol do vystupniho proudu.
        void SaveToStream(COutputStream & rStream);

        //! Unikatni cislo symbolu.
        /*! Pomoci id je symbol identifikovan ve vystupnim souboru. */
        int ID();

    private:
        std::string         m_sName;            //!< Jmeno symbolu.
        eType               m_nType;            //!< Typ symbolu.

        int                 m_nSection;         //!< Index do tabulky hlavicek sekci.
        CSection           *m_pSection;
        int                 m_nMemoryAddress;   //!< Relativni adresa v datech sekce.
        CExpansionTable     m_cExpansionTable;  //!< Rozsirujici informace.

    private:
        CSymbolTable       *m_pSymbolTable;
    };
}

#endif //__OBJECTFILE_H__
