// ******************************************************************
// *
// *    .,-:::::    .,::      .::::::::.    .,::      .:
// *  ,;;;'````'    `;;;,  .,;;  ;;;'';;'   `;;;,  .,;; 
// *  [[[             '[[,,[['   [[[__[[\.    '[[,,[['  
// *  $$$              Y$$$P     $$""""Y$$     Y$$$P    
// *  `88bo,__,o,    oP"``"Yo,  _88o,,od8P   oP"``"Yo,  
// *    "YUMMMMMP",m"       "Mm,""YUMMMP" ,m"       "Mm,
// *
// *   Cxbx->Core->Xbe.cpp
// *
// *  This file is part of the Cxbx project.
// *
// *  Cxbx and Cxbe are free software; you can redistribute them
// *  and/or modify them under the terms of the GNU General Public
// *  License as published by the Free Software Foundation; either
// *  version 2 of the license, or (at your option) any later version.
// *
// *  This program is distributed in the hope that it will be useful,
// *  but WITHOUT ANY WARRANTY; without even the implied warranty of
// *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// *  GNU General Public License for more details.
// *
// *  You should have recieved a copy of the GNU General Public License
// *  along with this program; see the file COPYING.
// *  If not, write to the Free Software Foundation, Inc.,
// *  59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// *  (c) 2002-2003 Aaron Robinson <caustik@caustik.com>
// *
// *  All rights reserved
// *
// ******************************************************************
#include "Xbe.h"
#include "Exe.h"

#include <memory.h>
#include <locale.h>
#include <stdlib.h>
#include <time.h>

// ******************************************************************
// * constructor
// ******************************************************************
Xbe::Xbe(const char *x_szFilename)
{
    char szBuffer[255];

    ConstructorInit();

    printf("Xbe::Xbe: Opening Xbe file...");

    FILE *XbeFile = fopen(x_szFilename, "rb");

    // ******************************************************************
    // * verify xbe file was opened
    // ******************************************************************
    if(XbeFile == 0)
    {
        SetError("Could not open Xbe file.", true);
        return;
    }

    printf("OK\n");

    // ******************************************************************
    // * read xbe image header
    // ******************************************************************
    {
        printf("Xbe::Xbe: Reading Image Header...");

        if(fread(&m_Header, sizeof(m_Header), 1, XbeFile) != 1)
        {
            SetError("Unexpected end of file while reading Xbe Image Header", true);
            goto cleanup;
        }

        if(m_Header.dwMagic != *(uint32 *)"XBEH")
        {
            SetError("Invalid magic number in Xbe file", true);
            goto cleanup;
        }

        printf("OK\n");
    }

    // ******************************************************************
    // * read xbe image header extra bytes
    // ******************************************************************
    if(m_Header.dwSizeofHeaders > sizeof(m_Header))
    {
        printf("Xbe::Xbe: Reading Image Header Extra Bytes...");

        uint32 ExSize = RoundUp(m_Header.dwSizeofHeaders, 0x1000) - sizeof(m_Header);

		m_HeaderEx = new char[ExSize];

		if(fread(m_HeaderEx, ExSize, 1, XbeFile) != 1)
		{
			SetError("Unexpected end of file while reading Xbe Image Header (Ex)", true);
			goto cleanup;
		}

        printf("OK\n");
    }

    // ******************************************************************
    // * read xbe certificate
    // ******************************************************************
    {
        printf("Xbe::Xbe: Reading Certificate...");

        fseek(XbeFile, m_Header.dwCertificateAddr - m_Header.dwBaseAddr, SEEK_SET);

        if(fread(&m_Certificate, sizeof(m_Certificate), 1, XbeFile) != 1)
        {
            SetError("Unexpected end of file while reading Xbe Certificate", true);
            goto cleanup;
        }

        setlocale( LC_ALL, "English" );

        wcstombs(m_szAsciiTitle, m_Certificate.wszTitleName, 40);

        printf("OK\n");

        printf("Xbe::Xbe: Title identified as %s\n", m_szAsciiTitle);
    }

    // ******************************************************************
    // * read xbe section headers
    // ******************************************************************
    {
        printf("Xbe::Xbe: Reading Section Headers...\n");

        fseek(XbeFile, m_Header.dwSectionHeadersAddr - m_Header.dwBaseAddr, SEEK_SET);

        m_SectionHeader = new SectionHeader[m_Header.dwSections];

        for(uint32 v=0;v<m_Header.dwSections;v++)
        {
            printf("Xbe::Xbe: Reading Section Header 0x%.04X...", v);

            if(fread(&m_SectionHeader[v], sizeof(*m_SectionHeader), 1, XbeFile) != 1)
            {
                sprintf(szBuffer, "Unexpected end of file while reading Xbe Section Header %d (%Xh)", v, v);
                SetError(szBuffer, true);
                goto cleanup;
            }

            printf("OK\n");
        }
    }

    // ******************************************************************
    // * read xbe section names
    // ******************************************************************
    {
        printf("Xbe::Xbe: Reading Section Names...\n");

        m_szSectionName = new char[m_Header.dwSections][9];
        for(uint32 v=0;v<m_Header.dwSections;v++)
        {
            printf("Xbe::Xbe: Reading Section Name 0x%.04X...", v);

            uint08 *sn = GetAddr(m_SectionHeader[v].dwSectionNameAddr);

            memset(m_szSectionName[v], 0, 9);

            if(sn != 0)
            {
                for(int b=0;b<8;b++)
                {
                    m_szSectionName[v][b] = sn[b];

                    if(m_szSectionName[v][b] == '\0')
                        break;
                }
            }

            printf("OK (%s)\n", m_szSectionName[v]);
        }
    }

    // ******************************************************************
    // * read xbe library versions
    // ******************************************************************
    if(m_Header.dwLibraryVersionsAddr != 0)
    {
        printf("Xbe::Xbe: Reading Library Versions...\n");

        fseek(XbeFile, m_Header.dwLibraryVersionsAddr - m_Header.dwBaseAddr, SEEK_SET);

        m_LibraryVersion = new LibraryVersion[m_Header.dwLibraryVersions];

        for(uint32 v=0;v<m_Header.dwLibraryVersions;v++)
        {
            printf("Xbe::Xbe: Reading Library Version 0x%.04X...", v);

            if(fread(&m_LibraryVersion[v], sizeof(*m_LibraryVersion), 1, XbeFile) != 1)
            {
                sprintf(szBuffer, "Unexpected end of file while reading Xbe Library Version %d (%Xh)", v, v);
                SetError(szBuffer, true);
                goto cleanup;
            }

            printf("OK\n");
        }

        // ******************************************************************
        // * read xbe kernel library version
        // ******************************************************************
        {
            printf("Xbe::Xbe: Reading Kernel Library Version...");

            if(m_Header.dwKernelLibraryVersionAddr == 0)
            {
                SetError("Could not locate kernel library version", true);
                goto cleanup;
            }

            fseek(XbeFile, m_Header.dwKernelLibraryVersionAddr - m_Header.dwBaseAddr, SEEK_SET);

            m_KernelLibraryVersion = new LibraryVersion;

            if(fread(m_KernelLibraryVersion, sizeof(*m_LibraryVersion), 1, XbeFile) != 1)
            {
                SetError("Unexpected end of file while reading Xbe Kernel Version", true);
                goto cleanup;
            }

            printf("OK\n");
        }

        // ******************************************************************
        // * read xbe xapi library version
        // ******************************************************************
        {
            printf("Xbe::Xbe: Reading Xapi Library Version...");

            if(m_Header.dwXAPILibraryVersionAddr == 0)
            {
                SetError("Could not locate Xapi Library Version", true);
                goto cleanup;
            }

            fseek(XbeFile, m_Header.dwXAPILibraryVersionAddr - m_Header.dwBaseAddr, SEEK_SET);

            m_XAPILibraryVersion = new LibraryVersion;

            if(fread(m_XAPILibraryVersion, sizeof(*m_LibraryVersion), 1, XbeFile) != 1)
            {
                SetError("Unexpected end of file while reading Xbe Xapi Version", true);
                goto cleanup;
            }

            printf("OK\n");
        }
    }

    // ******************************************************************
    // * read xbe sections
    // ******************************************************************
    {
        printf("Xbe::Xbe: Reading Sections...\n");

        m_bzSection = new uint08*[m_Header.dwSections];

        memset(m_bzSection, 0, m_Header.dwSections);

        for(uint32 v=0;v<m_Header.dwSections;v++)
        {
            printf("Xbe::Xbe: Reading Section 0x%.04X...", v);

            uint32 RawSize = m_SectionHeader[v].dwSizeofRaw;
            uint32 RawAddr = m_SectionHeader[v].dwRawAddr;

            m_bzSection[v] = new uint08[RawSize];

            fseek(XbeFile, RawAddr, SEEK_SET);

            if(RawSize == 0)
            {
                printf("OK\n");
                continue;
            }

            if(fread(m_bzSection[v], RawSize, 1, XbeFile) != 1)
            {
                sprintf(szBuffer, "Unexpected end of file while reading Xbe Section %d (%Xh) (%s)", v, v, m_szSectionName[v]);
                SetError(szBuffer, true);
                goto cleanup;
            }

            printf("OK\n");
        }
    }

    // ******************************************************************
    // * read xbe thread local storage
    // ******************************************************************
    if(m_Header.dwTLSAddr != 0)
    {
        printf("Xbe::Xbe: Reading Thread Local Storage...");

        void *Addr = GetAddr(m_Header.dwTLSAddr);

        if(Addr == 0)
        {
            SetError("Could not locate Thread Local Storage", true);
            goto cleanup;
        }

        m_TLS = new TLS;

        memcpy(m_TLS, Addr, sizeof(*m_TLS));

        printf("OK\n");
    }

cleanup:

    if(GetError() != 0)
    {
        printf("FAILED!\n");
        printf("Xbe::Xbe: ERROR -> %s\n", GetError());
    }

    fclose(XbeFile);

    return;
}

// ******************************************************************
// * constructor
// ******************************************************************
Xbe::Xbe(class Exe *x_Exe, const char *x_szTitle, bool x_bRetail)
{
    ConstructorInit();

    time_t CurrentTime;

    time(&CurrentTime);

    printf("Xbe::Xbe: Pass 1 (Simple Pass)...");

    // ******************************************************************
    // * Pass 1
    // ******************************************************************
    {
        // ******************************************************************
        // * standard xbe magic number
        // ******************************************************************
        m_Header.dwMagic = *(uint32*)"XBEH";

        // ******************************************************************
        // * nobody has the private key yet, so zero this out
        // ******************************************************************
        memset(m_Header.pbDigitalSignature, 0, 256);

        // ******************************************************************
        // * we'll only allow 0x00010000 for now
        // ******************************************************************
        m_Header.dwBaseAddr = 0x00010000;

        // ******************************************************************
        // * this is a constant
        // ******************************************************************
        m_Header.dwSizeofImageHeader = sizeof(m_Header);

        // ******************************************************************
        // * we'll have the same number of sections as the .exe,
        // ******************************************************************
        m_Header.dwSections = x_Exe->m_Header.m_sections;

        // ******************************************************************
        // * TODO: allow configuration
        // ******************************************************************
        {
            memset(&m_Header.dwInitFlags, 0, sizeof(m_Header.dwInitFlags));

            m_Header.dwInitFlags.bLimit64MB = true;
            m_Header.dwInitFlags.bDontSetupHarddisk = false;
            m_Header.dwInitFlags.bMountUtilityDrive = true;
        }

        // ******************************************************************
        // * various pe copies
        // ******************************************************************
        {
            m_Header.dwPeStackCommit = 0x00010000; //x_Exe->m_OptionalHeader.m_sizeof_stack_commit;
            m_Header.dwPeHeapReserve = x_Exe->m_OptionalHeader.m_sizeof_heap_reserve;
            m_Header.dwPeHeapCommit  = x_Exe->m_OptionalHeader.m_sizeof_heap_commit;
            m_Header.dwPeSizeofImage = x_Exe->m_OptionalHeader.m_sizeof_image;
            m_Header.dwPeChecksum     = 0x00000000;
            m_Header.dwPeTimeDate     = x_Exe->m_Header.m_timedate;
        }

        // ******************************************************************
        // * build time/date
        // ******************************************************************
        m_Header.dwTimeDate = CurrentTime;

        // ******************************************************************
        // * TODO: generate valid addr if necessary
        // ******************************************************************
        m_Header.dwNonKernelImportDirAddr = 0;

        // ******************************************************************
        // * TODO: generate these values
        // ******************************************************************
        m_Header.dwLibraryVersions = 0;
        m_Header.dwLibraryVersionsAddr = 0;
        m_Header.dwKernelLibraryVersionAddr = 0;
        m_Header.dwXAPILibraryVersionAddr = 0;
    }

    printf("OK\n");

    printf("Xbe::Xbe: Pass 2 (Calculating Requirements)...");

    // ******************************************************************
    // * Pass 2
    // ******************************************************************
    {
        // ******************************************************************
        // * make room cursor
        // ******************************************************************
        uint32 mrc = m_Header.dwBaseAddr + sizeof(m_Header);

        // ******************************************************************
        // * make room for certificate
        // ******************************************************************
        {
            m_Header.dwCertificateAddr = mrc;

            mrc += sizeof(m_Certificate);
        }

        // ******************************************************************
        // * make room for section headers
        // ******************************************************************
        {
            m_Header.dwSectionHeadersAddr = mrc;

            mrc += m_Header.dwSections * (sizeof(*m_SectionHeader));

            // ******************************************************************
            // * make room for head / tail reference count words
            // ******************************************************************
            mrc += (m_Header.dwSections+1)*2;

            // ******************************************************************
            // * make room for section names
            // ******************************************************************
            for(uint32 v=0;v<m_Header.dwSections;v++)
            {
                uint32 s = 0;

                while(s < 8 && x_Exe->m_SectionHeader[v].m_name[s] != '\0')
                    s++;

                mrc += s + 1;
            }
        }

        // ******************************************************************
        // * TODO: make room for library versions
        // ******************************************************************
        {
        }

        // ******************************************************************
        // * make room for debug path / debug file names
        // ******************************************************************
        {
            // TODO: allow this to be configured, right now we will just null out these values
            m_Header.dwDebugUnicodeFilenameAddr = mrc;
            m_Header.dwDebugPathnameAddr = mrc;
            m_Header.dwDebugFilenameAddr = mrc;

            mrc += 2;
        }

        // ******************************************************************
        // * make room for largest possible logo bitmap
        // ******************************************************************
        {
            mrc = RoundUp(mrc, 0x10);

            m_Header.dwLogoBitmapAddr = mrc;
            m_Header.dwSizeofLogoBitmap = 100*17;    // Max Possible

            mrc += m_Header.dwSizeofLogoBitmap;
        }

        // ******************************************************************
        // * update size of headers
        // ******************************************************************
        m_Header.dwSizeofHeaders = mrc - m_Header.dwBaseAddr;
    }

    printf("OK\n");

    printf("Xbe::Xbe: Pass 3 (Generating Xbe)...\n");

    // ******************************************************************
    // * Pass 3
    // ******************************************************************
    {
        m_Header.dwPeBaseAddr = m_Header.dwBaseAddr + RoundUp(m_Header.dwSizeofHeaders, 0x1000) - x_Exe->m_SectionHeader[0].m_virtual_addr;

        // ******************************************************************
        // * encode entry point
        // ******************************************************************
        {
            printf("Xbe::Xbe: Encoding %s Entry Point...", x_bRetail?"Retail":"Debug");

            uint32 ep = x_Exe->m_OptionalHeader.m_entry + m_Header.dwPeBaseAddr;

            if(x_bRetail)
                ep ^= XOR_EP_RETAIL;
            else
                ep ^= XOR_EP_DEBUG;

            m_Header.dwEntryAddr = ep;

            printf("OK (0x%.08X)\n", ep);
        }

        // ******************************************************************
        // * header write cursor
        // ******************************************************************
        uint32 hwc = m_Header.dwBaseAddr + sizeof(m_Header);

        // ******************************************************************
        // * check if we need to store extra header bytes (we always will)
        // ******************************************************************
        if(m_Header.dwSizeofHeaders > sizeof(m_Header))
        {
            printf("Xbe::Xbe: Found Extra Header Bytes...");

            uint32 ExSize = RoundUp(m_Header.dwSizeofHeaders - sizeof(m_Header), 0x1000);

            m_HeaderEx = new char[ExSize];

            printf("OK\n");
        }

        // ******************************************************************
        // * start a write buffer inside header_ex
        // ******************************************************************
        char *szBuffer = m_HeaderEx;

        // ******************************************************************
        // * write certificate
        // ******************************************************************
        {
            // ******************************************************************
            // * certificate size is a constant
            // ******************************************************************
            m_Certificate.dwSize = sizeof(m_Certificate);

            m_Certificate.dwTimeDate = CurrentTime;

            // ******************************************************************
            // * TODO: generate in the form CX-9999
            // ******************************************************************
            m_Certificate.dwTitleId = 0xFFFF0002;

            // ******************************************************************
            // * title name
            // ******************************************************************
            {
                memset(m_Certificate.wszTitleName, 0, 40);

                mbstowcs(m_Certificate.wszTitleName, x_szTitle, 40);
            }

            // ******************************************************************
            // * zero out alternate ids
            // ******************************************************************
            {
                for(uint32 c=0;c<0x10;c++)
                    m_Certificate.dwAlternateTitleId[c] = 0;
            }

            // ******************************************************************
            // * for now we'll just allow any media you could want
            // ******************************************************************
            m_Certificate.dwAllowedMedia = XBEIMAGE_MEDIA_TYPE_HARD_DISK | XBEIMAGE_MEDIA_TYPE_DVD_CD | XBEIMAGE_MEDIA_TYPE_MEDIA_BOARD;

            // ******************************************************************
            // * TODO: allow configuration
            // ******************************************************************
            m_Certificate.dwGameRegion = XBEIMAGE_GAME_REGION_MANUFACTURING | XBEIMAGE_GAME_REGION_NA | XBEIMAGE_GAME_REGION_JAPAN | XBEIMAGE_GAME_REGION_RESTOFWORLD;

            // ******************************************************************
            // * TODO: allow configuration
            // ******************************************************************
            m_Certificate.dwGameRatings = 0xFFFFFFFF;

            // ******************************************************************
            // * always disk 0, AFAIK
            // ******************************************************************
            m_Certificate.dwDiskNumber = 0;

            // ******************************************************************
            // * TODO: allow configuration
            // ******************************************************************
            m_Certificate.dwVersion = 0;

            // ******************************************************************
            // * generate blank lan, signature, and alternate signature keys
            // ******************************************************************
            {
                for(uint32 v=0;v<0x10;v++)
                    m_Certificate.bzLanKey[v] = m_Certificate.bzSignatureKey[v] = 0;

                for(uint32 x=0;x<0x10;x++)
                    for(uint32 y=0;y<0x10;y++)
                        m_Certificate.bzTitleAlternateSignatureKey[x][y] = 0;

            }

            // ******************************************************************
            // * write certificate
            // ******************************************************************
            {
                memcpy(szBuffer, &m_Certificate, sizeof(m_Certificate));

                szBuffer += sizeof(m_Certificate);

                hwc += sizeof(m_Certificate);
            }
        }

        // ******************************************************************
        // * generate ascii title from certificate title name
        // ******************************************************************
        {
            setlocale( LC_ALL, "English" );

            wcstombs(m_szAsciiTitle, m_Certificate.wszTitleName, 40);
        }

        // ******************************************************************
        // * write section headers / section names
        // ******************************************************************
        {
            m_szSectionName = new char[m_Header.dwSections][9];

            m_SectionHeader = new SectionHeader[m_Header.dwSections];

            uint32 SectionCursor = RoundUp(m_Header.dwSizeofHeaders, 0x1000);

            // ******************************************************************
            // * head / tail reference count write buffer
            // ******************************************************************
            uint16 *htrc = (uint16*)(szBuffer + m_Header.dwSections*sizeof(*m_SectionHeader));

            // ******************************************************************
            // * section write buffer
            // ******************************************************************
            char *secn = (char*)((uint32)htrc + (m_Header.dwSections+1)*2);

            // ******************************************************************
            // * head / tail reference count write cursor
            // ******************************************************************
            uint32 hwc_htrc = hwc + m_Header.dwSections*sizeof(*m_SectionHeader);

            // ******************************************************************
            // * section write cursor
            // ******************************************************************
            uint32 hwc_secn = hwc_htrc + (m_Header.dwSections+1)*2;

            printf("Xbe::Xbe: Generating Section Headers...\n");

            for(uint32 v=0;v<m_Header.dwSections;v++)
            {
                printf("Xbe::Xbe: Generating Section Header %.04X...", v);

                uint32 characteristics = x_Exe->m_SectionHeader[v].m_characteristics;

                memset(&m_SectionHeader[v].dwFlags, 0, sizeof(m_SectionHeader->dwFlags));

                if(characteristics & IMAGE_SCN_MEM_WRITE)
                    m_SectionHeader[v].dwFlags.bWritable =  true;

                if( (characteristics & IMAGE_SCN_MEM_EXECUTE) || (characteristics & IMAGE_SCN_CNT_CODE) )
                    m_SectionHeader[v].dwFlags.bExecutable = true;

                m_SectionHeader[v].dwFlags.bPreload = true;
                m_SectionHeader[v].dwVirtualAddr = x_Exe->m_SectionHeader[v].m_virtual_addr + m_Header.dwPeBaseAddr;

                if(v < m_Header.dwSections-1)
                    m_SectionHeader[v].dwVirtualSize = x_Exe->m_SectionHeader[v+1].m_virtual_addr - x_Exe->m_SectionHeader[v].m_virtual_addr;
                else
                    m_SectionHeader[v].dwVirtualSize = RoundUp(x_Exe->m_SectionHeader[v].m_virtual_size, 4);

                m_SectionHeader[v].dwRawAddr = SectionCursor;

                // ******************************************************************
                // * calculate sizeof_raw...this is done by locating the last non-
                // * -zero value in the raw section data
                // ******************************************************************
                {
                    uint32 r = x_Exe->m_SectionHeader[v].m_sizeof_raw - 1;

                    while(r > 0)
                    {
                        if(x_Exe->m_bzSection[v][r--] != 0)
                            break;
                    }
                    
                    // word aligned
                    m_SectionHeader[v].dwSizeofRaw = RoundUp(r+2, 4);
                }

                SectionCursor += RoundUp(m_SectionHeader[v].dwSizeofRaw, 0x1000);

                // ******************************************************************
                // * head / tail reference count
                // ******************************************************************
                {
                    m_SectionHeader[v].dwHeadSharedRefCountAddr = hwc_htrc;
                    htrc[v] = 0;

                    hwc_htrc += 2;

                    m_SectionHeader[v].dwTailSharedRefCountAddr = hwc_htrc;
                    htrc[v+1] = 0;
                }

                // ******************************************************************
                // * section name
                // ******************************************************************
                {
                    uint32 s = 0;

                    memset(secn, 0, 8);

                    m_SectionHeader[v].dwSectionNameAddr = hwc_secn;
                    while(s < 8 && x_Exe->m_SectionHeader[v].m_name[s] != '\0')
                    {
                        m_szSectionName[v][s] = secn[s] = x_Exe->m_SectionHeader[v].m_name[s];
                        s++;
                    }

                    m_szSectionName[v][s] = '\0';

                    secn += s+1;
                    hwc_secn += s+1;
                }

                m_SectionHeader[v].dwSectionRefCount = 0;

                // ******************************************************************
                // * write section digest (just zeros)
                // ******************************************************************
                memset(m_SectionHeader[v].bzSectionDigest, 0, 20);

                // ******************************************************************
                // * write section header
                // ******************************************************************
                memcpy(szBuffer, &m_SectionHeader[v], sizeof(*m_SectionHeader));

                szBuffer += sizeof(*m_SectionHeader);

                printf("OK\n");
            }

            hwc = hwc_secn;
        }

        // ******************************************************************
        // * write debug path / debug file names
        // ******************************************************************
        {
            *(uint16*)szBuffer = 0x0000;

            szBuffer += 2;
            hwc      += 2;
        }

        // ******************************************************************
        // * write default "OpenXDK" logo bitmap
        // ******************************************************************
        {
            printf("Xbe::Xbe: Generating \"OpenXDK\" Logo Bitmap...");

            uint08 *RawAddr = GetAddr(m_Header.dwLogoBitmapAddr);

            memset(RawAddr, 0, 100*17);

            memcpy(RawAddr, OpenXDK, dwSizeOfOpenXDK);

            m_Header.dwSizeofLogoBitmap = dwSizeOfOpenXDK;

            printf("OK\n");
        }

        // ******************************************************************
        // * write sections
        // ******************************************************************
        {
            printf("Xbe::Xbe: Generating Sections...\n");

            m_bzSection = new uint08*[m_Header.dwSections];

            memset(m_bzSection, 0, m_Header.dwSections);

            for(uint32 v=0;v<m_Header.dwSections;v++)
            {
                printf("Xbe::Xbe: Generating Section %.04X...", v);

                uint32 RawSize = m_SectionHeader[v].dwSizeofRaw;

                m_bzSection[v] = new uint08[RawSize];

                memcpy(m_bzSection[v], x_Exe->m_bzSection[v], RawSize);

                printf("OK\n");
            }
        }
    }

    printf("Xbe::Xbe: Pass 4 (Finalizing)...\n");

    // ******************************************************************
    // * Pass 4
    // ******************************************************************
    {
        m_Header.dwSizeofImage = m_SectionHeader[m_Header.dwSections-1].dwVirtualAddr + m_SectionHeader[m_Header.dwSections-1].dwVirtualSize - m_Header.dwBaseAddr;

        m_Header.dwTLSAddr = 0;

        // ******************************************************************
        // * Relocate to base : 0x00010000
        // ******************************************************************
        {
            printf("Xbe::Xbe: Relocating to Base 0x00010000...");

            uint32 fixCount = 0;

            uint32 relo_addr = x_Exe->m_OptionalHeader.m_image_data_directory[5].m_virtual_addr;
            uint32 relo_size = x_Exe->m_OptionalHeader.m_image_data_directory[5].m_size;

            uint32 dwBaseDiff = m_Header.dwPeBaseAddr - x_Exe->m_OptionalHeader.m_image_base;

            uint08 *reloc = GetAddr(relo_addr + m_Header.dwPeBaseAddr);

            // ******************************************************************
            // * relocate, if necessary
            // ******************************************************************
            if(reloc != 0)
            {
                uint32 v = 0;

                // ******************************************************************
                // * relocate each relocation block
                // ******************************************************************
                while(v < relo_size)
                {
                    uint32 block_addr = *(uint32 *)&reloc[v+0];
                    uint32 block_stop = *(uint32 *)&reloc[v+4] + v;

                    v += 8;

                    // ******************************************************************
                    // * relocate each rva
                    // ******************************************************************
                    while(v < block_stop && v < relo_size)
                    {
                        uint16 data = *(uint16 *)&reloc[v];

                        uint32 type = (data & 0xF000) >> 12;

                        if(type == 0)
                        {
                            v+=2;
                            break;
                        }

                        // ******************************************************************
                        // * 32-bit field relocation
                        // ******************************************************************
                        if(type == IMAGE_REL_BASED_HIGHLOW)
                        {
                            fixCount++;

                            uint32 dwFixAddr = block_addr + (data & 0x0FFF) + m_Header.dwPeBaseAddr;

                            uint08 *bzModRVA = GetAddr(dwFixAddr);

                            if(bzModRVA != 0)
                                *(uint32*)bzModRVA += dwBaseDiff;
                        }
                        else
                        {
                            SetError("Unsupported relocation type", true);
                            goto cleanup;
                        }

                        v+=2;
                    }
                }
            }

            printf("OK (%d Fixups)\n", fixCount);
        }

        // ******************************************************************
        // * Locate kernel thunk table
        // ******************************************************************
        {
            uint32 kt = x_Exe->m_OptionalHeader.m_image_data_directory[12].m_virtual_addr + m_Header.dwPeBaseAddr;

            if(x_bRetail)
                kt ^= XOR_KT_RETAIL;
            else
                kt ^= XOR_KT_DEBUG;

            m_Header.dwKernelImageThunkAddr = kt;
        }
    }

cleanup:

    if(GetError() != 0)
    {
        printf("FAILED!\n");
        printf("Xbe::Xbe: ERROR -> %s\n", GetError());
    }

    return;
}

// ******************************************************************
// * deconstructor
// ******************************************************************
Xbe::~Xbe()
{
    if(m_bzSection != 0)
    {
        for(uint32 v=0;v<m_Header.dwSections;v++)
            delete[] m_bzSection[v];

        delete[] m_bzSection;
    }

    delete   m_XAPILibraryVersion;
    delete   m_KernelLibraryVersion;
    delete[] m_LibraryVersion;
    delete   m_TLS;
    delete[] m_szSectionName;
    delete[] m_SectionHeader;
	delete[] m_HeaderEx;
}


// ******************************************************************
// * Export
// ******************************************************************
void Xbe::Export(const char *x_szXbeFilename)
{
    if(GetError() != 0)
        return;

    char szBuffer[255];

    printf("Xbe::Export: Writing Xbe file...");

    FILE *XbeFile = fopen(x_szXbeFilename, "wb");

    // ******************************************************************
    // * verify xbe file was opened
    // ******************************************************************
    if(XbeFile == 0)
    {
        SetError("Could not open .xbe file.", false);
        return;
    }

    printf("OK\n");

    // ******************************************************************
    // * write xbe image header
    // ******************************************************************
    {
        printf("Xbe::Export: Writing Image Header...");

        if(fwrite(&m_Header, sizeof(m_Header), 1, XbeFile) != 1)
		{
            SetError("Unexpected write error while writing Xbe Image Header", false);
            goto cleanup;
        }

        printf("OK\n");

        printf("Xbe::Export: Writing Image Header Extra Bytes...");

		if(fwrite(m_HeaderEx, m_Header.dwSizeofHeaders, 1, XbeFile) != 1)
		{
			SetError("Unexpected write error while writing Xbe Image Header (Ex)", false);
			goto cleanup;
		}

        printf("OK\n");
    }

    // ******************************************************************
    // * write xbe certificate
    // ******************************************************************
	{
        printf("Xbe::Export: Writing Certificate...");

        fseek(XbeFile, m_Header.dwCertificateAddr - m_Header.dwBaseAddr, SEEK_SET);

		if(fwrite(&m_Certificate, sizeof(m_Certificate), 1, XbeFile) != 1)
		{
			SetError("Unexpected write error while writing Xbe Certificate", false);
			goto cleanup;
		}

        printf("OK\n");
	}

    // ******************************************************************
    // * write xbe section headers
    // ******************************************************************
	{
        printf("Xbe::Export: Writing Section Headers...\n");

        fseek(XbeFile, m_Header.dwSectionHeadersAddr - m_Header.dwBaseAddr, SEEK_SET);

        for(uint32 v=0;v<m_Header.dwSections;v++)
        {
            printf("Xbe::Export: Writing Section Header 0x%.04X...", v);

            if(fwrite(&m_SectionHeader[v], sizeof(*m_SectionHeader), 1, XbeFile) != 1)
            {
                sprintf(szBuffer, "Unexpected write error while writing Xbe Section %d (%Xh)", v, v);
                SetError(szBuffer, false);
                goto cleanup;
            }

            printf("OK\n");
        }
	}

    // ******************************************************************
    // * write xbe sections
    // ******************************************************************
	{
        printf("Xbe::Export: Writing Sections...\n");

        for(uint32 v=0;v<m_Header.dwSections;v++)
		{
            printf("Xbe::Export: Writing Section 0x%.04X (%s)...", v, m_szSectionName[v]);

            uint32 RawSize = m_SectionHeader[v].dwSizeofRaw;
            uint32 RawAddr = m_SectionHeader[v].dwRawAddr;

            fseek(XbeFile, RawAddr, SEEK_SET);

            if(RawSize == 0)
            {
                printf("OK\n");
                continue;
            }

			if(fwrite(m_bzSection[v], RawSize, 1, XbeFile) != 1)
			{
				sprintf(szBuffer, "Unexpected write error while writing Xbe Section %d (%Xh) (%s)", v, v, m_szSectionName[v]);
				SetError(szBuffer, false);
				goto cleanup;
			}

            printf("OK\n");
		}
	}

    // ******************************************************************
    // * zero pad
    // ******************************************************************
	{
        printf("Xbe::Export: Writing Zero Padding...");

        fpos_t pos;

		uint32 remaining = 0;

		fgetpos(XbeFile, &pos);

		remaining = (uint32)(0x1000 - ftell(XbeFile)%0x1000);

        // ******************************************************************
        // * write remaining bytes
        // ******************************************************************
		{
			char *szBuffer = new char[remaining];

			for(uint32 v=0;v<remaining;v++)
				szBuffer[v] = 0;

			fwrite(szBuffer, remaining, 1, XbeFile);

			delete[] szBuffer;
		}

        printf("OK\n");
	}

cleanup:

    // ******************************************************************
    // * if we came across an error, delete the file we were creating
    // ******************************************************************
    if(GetError() != 0)
    {
        remove(x_szXbeFilename);
        printf("FAILED!\n");
        printf("Xbe::Export: ERROR -> %s\n", GetError());
    }

    fclose(XbeFile);
}

// ******************************************************************
// * ConstructorInit
// ******************************************************************
void Xbe::ConstructorInit()
{
    m_HeaderEx             = 0;
    m_SectionHeader        = 0;
    m_szSectionName        = 0;
    m_LibraryVersion       = 0;
    m_KernelLibraryVersion = 0;
    m_XAPILibraryVersion   = 0;
    m_TLS                  = 0;
    m_bzSection            = 0;
}

// ******************************************************************
// * BetterTime
// ******************************************************************
static char *BetterTime(char *x_ctime)
{
    int v=0;

    for(v=0;x_ctime[v] != '\n';v++);

    x_ctime[v] = '\0';

    return x_ctime;
}

// ******************************************************************
// * DumpInformation
// ******************************************************************
void Xbe::DumpInformation(FILE *x_file)
{
    if(GetError() != 0)
        return;

    fprintf(x_file, "XBE information generated by CXBX (Version " _CXBX_VERSION ")\n");
    fprintf(x_file, "\n");
    fprintf(x_file, "Title identified as \"%s\"\n", m_szAsciiTitle);
    fprintf(x_file, "\n");
    fprintf(x_file, "Dumping XBE file header...\n");
    fprintf(x_file, "\n");
    fprintf(x_file, "Magic Number                     : XBEH\n");

    // ******************************************************************
    // * print digital signature
    // ******************************************************************
    {
        fprintf(x_file, "Digitial Signature               : <Hex Dump>");
        for(int y=0;y<16;y++)
        {
            fprintf(x_file, "\n                                   ");
            for(int x=0;x<16;x++)
                fprintf(x_file, "%.02X", m_Header.pbDigitalSignature[y*16+x]);
        }
        fprintf(x_file, "\n                                   </Hex Dump>\n");
    }

    fprintf(x_file, "Base Address                     : 0x%.08X\n", m_Header.dwBaseAddr);
    fprintf(x_file, "Size of Headers                  : 0x%.08X\n", m_Header.dwSizeofHeaders);
    fprintf(x_file, "Size of Image                    : 0x%.08X\n", m_Header.dwSizeofImage);
    fprintf(x_file, "Size of Image Header             : 0x%.08X\n", m_Header.dwSizeofImageHeader);
    fprintf(x_file, "TimeDate Stamp                   : 0x%.08X (%s)\n", m_Header.dwTimeDate, BetterTime(ctime((const long*)&m_Header.dwTimeDate)));
    fprintf(x_file, "Certificate Address              : 0x%.08X\n", m_Header.dwCertificateAddr);
    fprintf(x_file, "Number of Sections               : 0x%.08X\n", m_Header.dwSections);
    fprintf(x_file, "Section Headers Address          : 0x%.08X\n", m_Header.dwSectionHeadersAddr);

    // ******************************************************************
    // * print init flags
    // ******************************************************************
    {
        fprintf(x_file, "Init Flags                       : 0x%.08X ", m_Header.dwInitFlags);

        if(m_Header.dwInitFlags.bMountUtilityDrive)
            fprintf(x_file, "[Mount Utility Drive] ");

        if(m_Header.dwInitFlags.bFormatUtilityDrive)
            fprintf(x_file, "[Format Utility Drive] ");

        if(m_Header.dwInitFlags.bLimit64MB)
            fprintf(x_file, "[Limit Devkit Run Time Memory to 64MB] ");

        if(!m_Header.dwInitFlags.bDontSetupHarddisk)
            fprintf(x_file, "[Setup Harddisk] ");

        fprintf(x_file, "\n");
    }

    char AsciiFilename[40];

    setlocale( LC_ALL, "English" );

    const wchar_t *wszFilename = (const wchar_t *)GetAddr(m_Header.dwDebugUnicodeFilenameAddr);

    if(wszFilename != NULL)
        wcstombs(AsciiFilename, wszFilename, 40);
    else
        AsciiFilename[0] = '\0';

    fprintf(x_file, "Entry Point                      : 0x%.08X (Retail: 0x%.08X, Debug: 0x%.08X)\n", m_Header.dwEntryAddr, m_Header.dwEntryAddr ^ XOR_EP_RETAIL, m_Header.dwEntryAddr ^ XOR_EP_DEBUG);
    fprintf(x_file, "TLS Address                      : 0x%.08X\n", m_Header.dwTLSAddr);
    fprintf(x_file, "(PE) Stack Commit                : 0x%.08X\n", m_Header.dwPeStackCommit);
    fprintf(x_file, "(PE) Heap Reserve                : 0x%.08X\n", m_Header.dwPeHeapReserve);
    fprintf(x_file, "(PE) Heap Commit                 : 0x%.08X\n", m_Header.dwPeHeapCommit);
    fprintf(x_file, "(PE) Base Address                : 0x%.08X\n", m_Header.dwPeBaseAddr);
    fprintf(x_file, "(PE) Size of Image               : 0x%.08X\n", m_Header.dwPeSizeofImage);
    fprintf(x_file, "(PE) Checksum                    : 0x%.08X\n", m_Header.dwPeChecksum);
    fprintf(x_file, "(PE) TimeDate Stamp              : 0x%.08X (%s)\n", m_Header.dwPeTimeDate, BetterTime(ctime((time_t*)&m_Header.dwPeTimeDate)));
    fprintf(x_file, "Debug Pathname Address           : 0x%.08X (\"%s\")\n", m_Header.dwDebugPathnameAddr, GetAddr(m_Header.dwDebugPathnameAddr));
    fprintf(x_file, "Debug Filename Address           : 0x%.08X (\"%s\")\n", m_Header.dwDebugFilenameAddr, GetAddr(m_Header.dwDebugFilenameAddr));
    fprintf(x_file, "Debug Unicode filename Address   : 0x%.08X (L\"%s\")\n", m_Header.dwDebugUnicodeFilenameAddr, AsciiFilename);
    fprintf(x_file, "Kernel Image Thunk Address       : 0x%.08X (Retail: 0x%.08X, Debug: 0x%.08X)\n", m_Header.dwKernelImageThunkAddr, m_Header.dwKernelImageThunkAddr ^ XOR_KT_RETAIL, m_Header.dwKernelImageThunkAddr ^ XOR_KT_DEBUG);
    fprintf(x_file, "NonKernel Import Dir Address     : 0x%.08X\n", m_Header.dwNonKernelImportDirAddr);
    fprintf(x_file, "Library Versions                 : 0x%.08X\n", m_Header.dwLibraryVersions);
    fprintf(x_file, "Library Versions Address         : 0x%.08X\n", m_Header.dwLibraryVersionsAddr);
    fprintf(x_file, "Kernel Library Version Address   : 0x%.08X\n", m_Header.dwKernelLibraryVersionAddr);
    fprintf(x_file, "XAPI Library Version Address     : 0x%.08X\n", m_Header.dwXAPILibraryVersionAddr);
    fprintf(x_file, "Logo Bitmap Address              : 0x%.08X\n", m_Header.dwLogoBitmapAddr);
    fprintf(x_file, "Logo Bitmap Size                 : 0x%.08X\n", m_Header.dwSizeofLogoBitmap);
    fprintf(x_file, "\n");
    fprintf(x_file, "Dumping XBE Certificate...\n");
    fprintf(x_file, "\n");
    fprintf(x_file, "Size of Certificate              : 0x%.08X\n", m_Certificate.dwSize);
    fprintf(x_file, "TimeDate Stamp                   : 0x%.08X (%s)\n", m_Certificate.dwTimeDate, BetterTime(ctime((time_t*)&m_Certificate.dwTimeDate)));
    fprintf(x_file, "Title ID                         : 0x%.08X\n", m_Certificate.dwTitleId);
    fprintf(x_file, "Title                            : L\"%s\"\n", m_szAsciiTitle);

    // ******************************************************************
    // * print alternate titles
    // ******************************************************************
    {
        fprintf(x_file, "Alternate Titles IDs             : ");

        for(int v=0;v<0x10;v++)
        {
            if(v != 0)
                fprintf(x_file, "                                   ");
            fprintf(x_file, "0x%.08X", m_Certificate.dwAlternateTitleId[v]);
            if(v != 0x0F)
                fprintf(x_file, "\n");
        }

        fprintf(x_file, "\n");
    }

    fprintf(x_file, "Allowed Media                    : 0x%.08X\n", m_Certificate.dwAllowedMedia);
    fprintf(x_file, "Game Region                      : 0x%.08X\n", m_Certificate.dwGameRegion);
    fprintf(x_file, "Game Ratings                     : 0x%.08X\n", m_Certificate.dwGameRatings);
    fprintf(x_file, "Disk Number                      : 0x%.08X\n", m_Certificate.dwDiskNumber);
    fprintf(x_file, "Version                          : 0x%.08X\n", m_Certificate.dwVersion);

    // ******************************************************************
    // * print lan key
    // ******************************************************************
    {
        fprintf(x_file, "LAN Key                          : ");
        for(int x=0;x<16;x++)
            fprintf(x_file, "%.02X", m_Certificate.bzLanKey[x]);
        fprintf(x_file, "\n");
    }

    // ******************************************************************
    // * print signature key
    // ******************************************************************
    {
        fprintf(x_file, "Signature Key                    : ");
        for(int x=0;x<16;x++)
            fprintf(x_file, "%.02X", m_Certificate.bzSignatureKey[x]);
        fprintf(x_file, "\n");
    }

    // ******************************************************************
    // * print alternative signature keys
    // ******************************************************************
    {
        fprintf(x_file, "Title Alternative Signature Keys : <Hex Dump>");
        for(int y=0;y<16;y++)
        {
            fprintf(x_file, "\n                                   ");
            for(int x=0;x<16;x++)
                fprintf(x_file, "%.02X", m_Certificate.bzTitleAlternateSignatureKey[y][x]);
        }
        fprintf(x_file, "\n                                   </Hex Dump>\n");
    }

    fprintf(x_file, "\n");
    fprintf(x_file, "Dumping XBE Section Headers...\n");
    fprintf(x_file, "\n");

    // ******************************************************************
    // * print section headers
    // ******************************************************************
    {
        for(uint32 v=0;v<m_Header.dwSections;v++)
        {
            fprintf(x_file, "Section Name                     : 0x%.08X (\"%s\")\n", m_SectionHeader[v].dwSectionNameAddr, m_szSectionName[v]);


            // ******************************************************************
            // * print flags
            // ******************************************************************
            {
                fprintf(x_file, "Flags                            : 0x%.08X ", m_SectionHeader[v].dwFlags);

                if(m_SectionHeader[v].dwFlags.bWritable)
                    fprintf(x_file, "(Writable) ");

                if(m_SectionHeader[v].dwFlags.bPreload)
                    fprintf(x_file, "(Preload) ");

                if(m_SectionHeader[v].dwFlags.bExecutable)
                    fprintf(x_file, "(Executable) ");

                if(m_SectionHeader[v].dwFlags.bInsertedFile)
                    fprintf(x_file, "(Inserted File) ");

                if(m_SectionHeader[v].dwFlags.bHeadPageRO)
                    fprintf(x_file, "(Head Page RO) ");

                if(m_SectionHeader[v].dwFlags.bTailPageRO)
                    fprintf(x_file, "(Tail Page RO) ");

                fprintf(x_file, "\n");
            }

            fprintf(x_file, "Virtual Address                  : 0x%.08X\n", m_SectionHeader[v].dwVirtualAddr);
            fprintf(x_file, "Virtual Size                     : 0x%.08X\n", m_SectionHeader[v].dwVirtualSize);
            fprintf(x_file, "Raw Address                      : 0x%.08X\n", m_SectionHeader[v].dwRawAddr);
            fprintf(x_file, "Size of Raw                      : 0x%.08X\n", m_SectionHeader[v].dwSizeofRaw);
            fprintf(x_file, "Section Name Address             : 0x%.08X\n", m_SectionHeader[v].dwSectionNameAddr);
            fprintf(x_file, "Section Reference Count          : 0x%.08X\n", m_SectionHeader[v].dwSectionRefCount);
            fprintf(x_file, "Head Shared Reference Count Addr : 0x%.08X\n", m_SectionHeader[v].dwHeadSharedRefCountAddr);
            fprintf(x_file, "Tail Shared Reference Count Addr : 0x%.08X\n", m_SectionHeader[v].dwTailSharedRefCountAddr);

            // ******************************************************************
            // * print section digest
            // ******************************************************************
            {
                fprintf(x_file, "Section Digest                   : ");
                for(int s=0;s<20;s++)
                    fprintf(x_file, "%.02X", m_SectionHeader[v].bzSectionDigest[s]);
                fprintf(x_file, "\n");
            }

            fprintf(x_file, "\n");
        }
    }

    fprintf(x_file, "Dumping XBE Library Versions...\n");
    fprintf(x_file, "\n");

    // ******************************************************************
    // * print library versions
    // ******************************************************************
    {
        if(m_LibraryVersion == 0 || m_Header.dwLibraryVersions == 0)
        {
            fprintf(x_file, "(This XBE contains no Library Versions)\n");
            fprintf(x_file, "\n");
        }
        else
        {
            for(uint32 v=0;v<m_Header.dwLibraryVersions;v++)
            {
                char tmp[9];

                for(uint32 c=0;c<8;c++)
                    tmp[c] = m_LibraryVersion[v].szName[c];

                tmp[8] = '\0';

                fprintf(x_file, "Library Name                     : %s\n", tmp);
                fprintf(x_file, "Version                          : %d.%d.%d\n", m_LibraryVersion[v].wMajorVersion, m_LibraryVersion[v].wMinorVersion, m_LibraryVersion[v].wBuildVersion);
 
                // ******************************************************************
                // * print flags
                // ******************************************************************
                {
                    fprintf(x_file, "Flags                            : ");

                    fprintf(x_file, "QFEVersion : 0x%.04X, ", m_LibraryVersion[v].dwFlags.QFEVersion);

                    if(m_LibraryVersion[v].dwFlags.bDebugBuild)
                        fprintf(x_file, "Debug, ");
                    else
                        fprintf(x_file, "Retail, ");

                    switch(m_LibraryVersion[v].dwFlags.Approved)
                    {
                        case 0:
                            fprintf(x_file, "Unapproved");
                            break;
                        case 1:
                            fprintf(x_file, "Possibly Approved");
                            break;
                        case 2:
                            fprintf(x_file, "Approved");
                            break;
                    }

                    fprintf(x_file, "\n");
                }

                fprintf(x_file, "\n");
            }
        }
    }

    fprintf(x_file, "Dumping XBE TLS...\n");
    fprintf(x_file, "\n");

    // ******************************************************************
    // * print thread local storage
    // ******************************************************************
    if(m_TLS != 0)
    {
        fprintf(x_file, "Data Start Address               : 0x%.08X\n", m_TLS->dwDataStartAddr);
        fprintf(x_file, "Data End Address                 : 0x%.08X\n", m_TLS->dwDataEndAddr);
        fprintf(x_file, "TLS Index Address                : 0x%.08X\n", m_TLS->dwTLSIndexAddr);
        fprintf(x_file, "TLS Callback Address             : 0x%.08X\n", m_TLS->dwTLSCallbackAddr);
        fprintf(x_file, "Size of Zero Fill                : 0x%.08X\n", m_TLS->dwSizeofZeroFill);
        fprintf(x_file, "Characteristics                  : 0x%.08X\n", m_TLS->dwCharacteristics);
    }
    else
    {
        fprintf(x_file, "(This XBE contains no TLS)\n");
    }
}


// ******************************************************************
// * ImportLogoBitmap
// ******************************************************************
void Xbe::ImportLogoBitmap(const uint08 x_Gray[100*17])
{
    char  *LogoBuffer = new char[4*1024];
    uint32 LogoSize = 0;

    // ******************************************************************
    // * encode logo bitmap
    // ******************************************************************
    {
        for(uint32 v=1;v<100*17;LogoSize++)
        {
            char color = x_Gray[v] >> 4;

            uint32 len = 1;

            while(++v<100*17-1 && len < 1024 && color == x_Gray[v] >> 4)
                len++;

            LogoRLE *cur = (LogoRLE *)&LogoBuffer[LogoSize];

            if(len <= 7)
            {
                cur->m_Eight.bType1 = 1;
                cur->m_Eight.Len    = len;
                cur->m_Eight.Data   = color;
            }
            else
            {
                cur->m_Sixteen.bType1 = 0;
                cur->m_Sixteen.bType2 = 1;
                cur->m_Sixteen.Len    = len;
                cur->m_Sixteen.Data   = color;
                LogoSize++;
            }
        }
    }

    // ******************************************************************
    // * check if there is room to save this, it not then throw an error
    // ******************************************************************
    {
        uint08 *RLE = GetLogoBitmap(LogoSize);

        if(RLE == 0)
        {
            if(GetError() == 0)
                SetError("Logo bitmap could not be imported (not enough space in file?)", false);

            return;
        }

        memcpy(RLE, LogoBuffer, LogoSize);
    }

    return;
}

// ******************************************************************
// * ExportLogoBitmap
// ******************************************************************
// *
// * This algorithm was originally discovered by superfro. I couldnt
// * figure out what the hell the encoding format was before he gave
// * me the information:
// *
// * basically what is going on here is a single pass through the
// * bitmap data, with 2 possible encodings per rle chunk. data is
// * stored as 4 bit grayscale, so the logical approach is to expand
// * this to 8 bit using a simple 4 bit left shift (*16). However, it
// * has been suggested to me by superfro that you might calculate a
// * slightly darker image by multiplying by 15 and adding .5. It's
// * a toss up, but i've choosen a simple bit shift left.
// *
// ******************************************************************
void Xbe::ExportLogoBitmap(uint08 x_Gray[100*17])
{
	memset(x_Gray, 0, 100*17);

    uint32 dwLength = m_Header.dwSizeofLogoBitmap;

    uint08 *RLE = GetAddr(m_Header.dwLogoBitmapAddr);

    if(RLE == 0 || GetError())
        return;

    uint32 o = 0;

    for(uint32 v=0;v<dwLength;v++)
    {
        uint32 len = 0, data = 0;

        LogoRLE *cur = (LogoRLE *)&RLE[v];

        if(cur->m_Eight.bType1)
        {
            len  = cur->m_Eight.Len;
            data = cur->m_Eight.Data;
        }
        else
        {
            if(cur->m_Sixteen.bType2)
            {
                len  = cur->m_Sixteen.Len;
                data = cur->m_Sixteen.Data;
                v   += 1;
            }
        }

        for(uint32 j=0;j<len;j++)
        {
            o++;

            if(o < 100*17)
                x_Gray[o] = (char)(data << 4); // could use (int)(data * 15.0 + .5);
        }
    }
}

// ******************************************************************
// * GetAddr
// ******************************************************************
uint08 *Xbe::GetAddr(uint32 x_dwVirtualAddress)
{
    uint32 dwOffs = x_dwVirtualAddress - m_Header.dwBaseAddr;

    // ******************************************************************
    // * offset in image header
    // ******************************************************************
    if(dwOffs < sizeof(m_Header))
        return &((uint08*)&m_Header)[dwOffs];

    // ******************************************************************
    // * offset in image header extra bytes
    // ******************************************************************
    if(dwOffs < m_Header.dwSizeofHeaders)
 		return (uint08*)&m_HeaderEx[dwOffs - sizeof(m_Header)];

    // ******************************************************************
    // * offset in some random section
    // ******************************************************************
    {
        for(uint32 v=0;v<m_Header.dwSections;v++)
        {
            uint32 VirtAddr = m_SectionHeader[v].dwVirtualAddr;
            uint32 VirtSize = m_SectionHeader[v].dwVirtualSize;

            if( (x_dwVirtualAddress >= VirtAddr) && (x_dwVirtualAddress < (VirtAddr + VirtSize)) )
                return &m_bzSection[v][x_dwVirtualAddress - VirtAddr];
        }
    }

    return 0;
}

// ******************************************************************
// * GetLogoBitmap
// ******************************************************************
uint08 *Xbe::GetLogoBitmap(uint32 x_dwSize)
{
    uint32 dwOffs = m_Header.dwLogoBitmapAddr - m_Header.dwBaseAddr;
    uint32 dwLength = m_Header.dwSizeofLogoBitmap;

	if(dwOffs == 0 || dwLength == 0)
		return 0;

    // ******************************************************************
    // * if this bitmap will fit inside the already existing one, we
    // * don't need to resize anything in the xbe, just return a pointer
    // ******************************************************************
    if(dwLength >= x_dwSize)
    {
        // ******************************************************************
        // * update size of headers, if necessary
        // ******************************************************************
        if(dwOffs < m_Header.dwSizeofHeaders)
        {
            m_Header.dwSizeofHeaders -= dwLength;
            m_Header.dwSizeofHeaders += x_dwSize;
            m_Header.dwSizeofLogoBitmap = x_dwSize;

            return GetAddr(m_Header.dwLogoBitmapAddr);
        }
    }

    return 0;
}
