// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// ******************************************************************
// *
// *  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 "common\xbe\XbePrinter.h"
#include "CxbxVersion.h" // For CxbxVersionStr

#include <locale> // For ctime
#include <sstream> // For std::stringstream
#include <iomanip> // For std::setfill, std::uppercase, std::hex
#include "common/util/strConverter.hpp" // for utf16le_to_ascii

// better time
static char *BetterTime(uint32_t x_timeDate)
{
    time_t x_time = x_timeDate;
    char *x_ctime = ctime(&x_time);

    int v=0;

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

    x_ctime[v] = '\0';

    return x_ctime;
}

std::string DumpInformation(Xbe* Xbe_object)
{
    if(Xbe_object->HasError()) {
        return "ERROR";
    }
    XbePrinter printer(Xbe_object);
    return printer.GenXbeInfo();
}

#define SSTREAM_SET_HEX(stream_name) stream_name << std::setfill('0') << std::uppercase << std::hex;
#define SSTREAM_SET_DEC(stream_name) stream_name << std::setfill('0') << std::uppercase << std::dec;

std::string FormatTitleId(uint32_t title_id)
{
    std::stringstream ss;

    // If the Title ID prefix is a printable character, parse it
    // This shows the correct game serial number for retail titles!
    // EG: MS-001 for 1st tile published by MS, EA-002 for 2nd title by EA, etc
    // Some special Xbes (Dashboard, XDK Samples) use non-alphanumeric serials
    // We fall back to Hex for those
    // ergo720: we cannot use isalnum() here because it will treat chars in the range -1 - 255 as valid ascii chars which can
    // lead to unicode characters being printed in the title (e.g.: dashboard uses 0xFE and 0xFF)
    uint8_t pTitleId1 = (title_id >> 24) & 0xFF;
    uint8_t pTitleId2 = (title_id >> 16) & 0xFF;

    if ((pTitleId1 < 65 || pTitleId1 > 90) || (pTitleId2 < 65 || pTitleId2 > 90)) {
        // Prefix was non-printable, so we need to print a hex reprentation of the entire title_id
        ss << std::setfill('0') << std::setw(8) << std::hex << std::uppercase << title_id;
        return ss.str();
    }

    ss << pTitleId1 << pTitleId2;
    ss << "-";
    ss << std::setfill('0') << std::setw(3) << std::dec << (title_id & 0x0000FFFF);

    return ss.str();
}

XbePrinter::XbePrinter(Xbe* Xbe_object)
{
    Xbe_to_print = Xbe_object;
    Xbe_header = &(Xbe_object->m_Header);
    Xbe_certificate = &(Xbe_object->m_Certificate);
}

std::string XbePrinter::GenXbeInfo()
{
    std::string info;
    info.append(GenDumpHeader());
    info.append(GenXbeHeaderInfo());
    info.append(GenXbeCertificateInfo());
	info.append(ValidateXbeSignature());
    info.append(GenSectionInfo());
    info.append(GenLibraryVersions());
    info.append(GenTLS());
    return info;
}

std::string XbePrinter::GenHexRow(
    uint8_t *signature, const uint8_t row, const uint8_t row_size
)
{
    const uint16_t offset = row * row_size;
    std::stringstream text;
    SSTREAM_SET_HEX(text);
    for(uint8_t x = 0; x < row_size; x++) {
        text << std::setw(2) << static_cast<unsigned>(signature[offset + x]);
    }
    return text.str();
}

std::string XbePrinter::AllowedMediaToString()
{
    const uint32_t dwAllowedMedia = Xbe_certificate->dwAllowedMedia;
    std::string text = "Media Types:";

    if(dwAllowedMedia & XBEIMAGE_MEDIA_TYPE_MEDIA_MASK) {
        if(dwAllowedMedia & XBEIMAGE_MEDIA_TYPE_HARD_DISK)
            text.append(" HARD_DISK");
        if(dwAllowedMedia & XBEIMAGE_MEDIA_TYPE_DVD_X2)
            text.append(" DVD_X2");
        if(dwAllowedMedia & XBEIMAGE_MEDIA_TYPE_DVD_CD)
            text.append(" DVD_CD");
        if(dwAllowedMedia & XBEIMAGE_MEDIA_TYPE_CD)
            text.append(" CD");
        if(dwAllowedMedia & XBEIMAGE_MEDIA_TYPE_DVD_5_RO)
            text.append(" DVD_5_RO");
        if(dwAllowedMedia & XBEIMAGE_MEDIA_TYPE_DVD_9_RO)
            text.append(" DVD_9_RO");
        if(dwAllowedMedia & XBEIMAGE_MEDIA_TYPE_DVD_5_RW)
            text.append(" DVD_5_RW");
        if(dwAllowedMedia & XBEIMAGE_MEDIA_TYPE_DVD_9_RW)
            text.append(" DVD_9_RW");
        if(dwAllowedMedia & XBEIMAGE_MEDIA_TYPE_DONGLE)
            text.append(" DONGLE");
        if(dwAllowedMedia & XBEIMAGE_MEDIA_TYPE_MEDIA_BOARD)
            text.append(" BOARD");
        if((dwAllowedMedia & XBEIMAGE_MEDIA_TYPE_MEDIA_MASK) >= (XBEIMAGE_MEDIA_TYPE_MEDIA_BOARD * 2))
            text.append(" UNKNOWN");
    }

    if(dwAllowedMedia & ~XBEIMAGE_MEDIA_TYPE_MEDIA_MASK) {
        text.append(" NONSECURE");
        if(dwAllowedMedia & XBEIMAGE_MEDIA_TYPE_NONSECURE_HARD_DISK)
            text.append(" HARD_DISK");
        if(dwAllowedMedia & XBEIMAGE_MEDIA_TYPE_NONSECURE_MODE)
            text.append(" MODE");
    }
    return text;
}

std::string XbePrinter::GameRatingToString()
{
    std::string text;
    // Info from: https://xboxdevwiki.net/EEPROM
    switch(Xbe_certificate->dwGameRatings) {
        case 0x0:
            text.append("(RP) Rating Pending");
            break;
        case 0x1:
            text.append("(AO) Adults Only");
            break;
        case 0x2:
            text.append("(M) Mature");
            break;
        case 0x3:
            text.append("(T) Teen");
            break;
        case 0x4:
            text.append("(E) Everyone");
            break;
        case 0x5:
            text.append("(K-A) Kids to Adults");
            break;
        case 0x6:
            text.append("(EC) Early Childhood");
            break;
        default:
            text.append("ERROR: no rating");
            break;
    }
    return text;
}

std::string XbePrinter::GenDumpHeader()
{
    std::string text;
    text.append("XBE information generated by Cxbx-Reloaded (Version ");
    text.append(CxbxVersionStr);
    text.append(")\n\n");
    text.append("Title identified as \"");
    text.append(Xbe_to_print->m_szAsciiTitle);
    text.append("\"\n\n");
    text.append("Dumping XBE file header...\n\n");
    text.append("Magic Number                     : XBEH\n");
    return text;
}

std::string XbePrinter::GenXbeHeaderInfo()
{
    std::string text;
    text.append(GenDigitalSignature());
    text.append(GenGeneralHeaderInfo1());
    text.append(GenInitFlags());
    text.append(GenGeneralHeaderInfo2());
    return text;
}

std::string XbePrinter::GenDigitalSignature()
{
    const uint8_t row_size = 16;
    std::string text;
    text.append("Digital Signature               : <Hex Dump>");
    for(int row = 0; row < 16; row++) {
        text.append("\n                                   ");
        text.append(GenHexRow(&(Xbe_header->pbDigitalSignature[0]), row, row_size));
    }
    text.append("\n                                   </Hex Dump>\n");
    return text;
}

std::string XbePrinter::GenGeneralHeaderInfo1()
{
    std::stringstream text;
    SSTREAM_SET_HEX(text);
    text << "Base Address                     : 0x" << std::setw(8) << Xbe_header->dwBaseAddr << "\n";
    text << "Size of Headers                  : 0x" << std::setw(8) << Xbe_header->dwSizeofHeaders << "\n";
    text << "Size of Image                    : 0x" << std::setw(8) << Xbe_header->dwSizeofImage << "\n";
    text << "Size of Image Header             : 0x" << std::setw(8) << Xbe_header->dwSizeofImageHeader << "\n";
    text << "TimeDate Stamp                   : 0x" << std::setw(8) << Xbe_header->dwTimeDate << " (" << BetterTime(Xbe_header->dwTimeDate) << ")\n";
    text << "Certificate Address              : 0x" << std::setw(8) << Xbe_header->dwCertificateAddr << "\n";
    text << "Number of Sections               : 0x" << std::setw(8) << Xbe_header->dwSections << "\n";
    text << "Section Headers Address          : 0x" << std::setw(8) << Xbe_header->dwSectionHeadersAddr << "\n";
    return text.str();
}

std::string XbePrinter::GenInitFlags()
{
    std::stringstream text;
    SSTREAM_SET_HEX(text);
    text << "Init Flags                       : 0x" << std::setw(8) << Xbe_header->dwInitFlags_value << " ";

    if(Xbe_header->dwInitFlags.bMountUtilityDrive) {
        text << "[Mount Utility Drive] ";
    }
    if(Xbe_header->dwInitFlags.bFormatUtilityDrive) {
        text << "[Format Utility Drive] ";
    }
    if(Xbe_header->dwInitFlags.bLimit64MB) {
        text << "[Limit Devkit Run Time Memory to 64MB] ";
    }
    if(!Xbe_header->dwInitFlags.bDontSetupHarddisk) {
        text << "[Setup Harddisk] ";
    }
    text << "\n";
    return text.str();
}

std::string XbePrinter::GenGeneralHeaderInfo2()
{
    const uint32_t retail_entry_point = Xbe_header->dwEntryAddr ^ XOR_EP_RETAIL;
    const uint32_t debug_entry_point = Xbe_header->dwEntryAddr ^ XOR_EP_DEBUG;
    const uint32_t retail_thunk_addr = Xbe_header->dwKernelImageThunkAddr ^ XOR_KT_RETAIL;
    const uint32_t debug_thunk_addr = Xbe_header->dwKernelImageThunkAddr ^ XOR_KT_DEBUG;
    const std::string AsciiFilename = utf16le_to_ascii(Xbe_to_print->GetUnicodeFilenameAddr());
    std::stringstream text;
    SSTREAM_SET_HEX(text);
    text << "Entry Point                      : 0x" << std::setw(8) << Xbe_header->dwEntryAddr << " (Retail: 0x" << std::setw(8) << retail_entry_point << ", Debug: 0x" <<  std::setw(8) << debug_entry_point << ")\n";
    text << "TLS Address                      : 0x" << std::setw(8) << Xbe_header->dwTLSAddr << "\n";
    text << "(PE) Stack Commit                : 0x" << std::setw(8) << Xbe_header->dwPeStackCommit << "\n";
    text << "(PE) Heap Reserve                : 0x" << std::setw(8) << Xbe_header->dwPeHeapReserve << "\n";
    text << "(PE) Heap Commit                 : 0x" << std::setw(8) << Xbe_header->dwPeHeapCommit << "\n";
    text << "(PE) Base Address                : 0x" << std::setw(8) << Xbe_header->dwPeBaseAddr << "\n";
    text << "(PE) Size of Image               : 0x" << std::setw(8) << Xbe_header->dwPeSizeofImage << "\n";
    text << "(PE) Checksum                    : 0x" << std::setw(8) << Xbe_header->dwPeChecksum << "\n";
    text << "(PE) TimeDate Stamp              : 0x" << std::setw(8) << Xbe_header->dwPeTimeDate << " (" << BetterTime(Xbe_header->dwPeTimeDate) << ")\n";
    text << "Debug Pathname Address           : 0x" << std::setw(8) << Xbe_header->dwDebugPathnameAddr << " (\"" << Xbe_to_print->GetAddr(Xbe_header->dwDebugPathnameAddr) << "\")\n";
    text << "Debug Filename Address           : 0x" << std::setw(8) << Xbe_header->dwDebugFilenameAddr << " (\"" << Xbe_to_print->GetAddr(Xbe_header->dwDebugFilenameAddr) << "\")\n";
    text << "Debug Unicode filename Address   : 0x" << std::setw(8) << Xbe_header->dwDebugUnicodeFilenameAddr << " (L\"" << AsciiFilename << "\")\n";
    text << "Kernel Image Thunk Address       : 0x" << std::setw(8) << Xbe_header->dwKernelImageThunkAddr << " (Retail: 0x" << std::setw(8) << retail_thunk_addr << ", Debug: 0x" <<  std::setw(8) << debug_thunk_addr << ")\n";
    text << "NonKernel Import Dir Address     : 0x" << std::setw(8) << Xbe_header->dwNonKernelImportDirAddr << "\n";
    text << "Library Versions                 : 0x" << std::setw(8) << Xbe_header->dwLibraryVersions << "\n";
    text << "Library Versions Address         : 0x" << std::setw(8) << Xbe_header->dwLibraryVersionsAddr << "\n";
    text << "Kernel Library Version Address   : 0x" << std::setw(8) << Xbe_header->dwKernelLibraryVersionAddr << "\n";
    text << "XAPI Library Version Address     : 0x" << std::setw(8) << Xbe_header->dwXAPILibraryVersionAddr << "\n";
    text << "Logo Bitmap Address              : 0x" << std::setw(8) << Xbe_header->dwLogoBitmapAddr << "\n";
    text << "Logo Bitmap Size                 : 0x" << std::setw(8) << Xbe_header->dwSizeofLogoBitmap << "\n\n";
    return text.str();
}

std::string XbePrinter::ValidateXbeSignature()
{
	std::string text("\nInvalid xbe signature. Homebrew, tampered or pirated xbe?\n");
	if (Xbe_to_print->CheckSignature()) {
		text = "\nValid xbe signature. Xbe is legit\n";
	}
	return text;
}

std::string XbePrinter::GenXbeCertificateInfo()
{
    std::string text;
    text.append(GenCertificateHeader());
    text.append(GenAlternateTitleIDs());
    text.append(GenMediaInfo());
    text.append(GenLANKey());
    text.append(GenSignatureKey());
    text.append(GenAlternateSignatureKeys());
    text.append(GenExtraInfo());
    return text;
}

std::string XbePrinter::GenCertificateHeader()
{
    std::stringstream text;
    SSTREAM_SET_HEX(text);
    text << "Dumping XBE Certificate...\n\n";
    text << "Size of Certificate              : 0x" << std::setw(8) << Xbe_certificate->dwSize << "\n";
    text << "TimeDate Stamp                   : 0x" << std::setw(8) << Xbe_certificate->dwTimeDate << " (" << BetterTime(Xbe_certificate->dwTimeDate) << ")\n";
    text << "Title ID                         : " << FormatTitleId(Xbe_certificate->dwTitleId) << "\n";
    text << "Title ID (Hex)                   : 0x" << std::setw(8) << Xbe_certificate->dwTitleId << "\n";
    text << "Title                            : L\"" << Xbe_to_print->m_szAsciiTitle << "\"\n";
    return text.str();
}

std::string XbePrinter::GenAlternateTitleIDs()
{
    std::stringstream text;
    SSTREAM_SET_HEX(text);
    text << "Alternate Titles IDs             : ";

    for(int v = 0; v < 0x10; v++) {
        if(v != 0) {
            text << "                                   ";
        }
        text << "0x" << std::setw(8) << Xbe_certificate->dwAlternateTitleId[v];
        if(v != 0x0F) {
            text << "\n";
        }
    }
    text << "\n";
    return text.str();
}

std::string XbePrinter::GenMediaInfo()
{
    std::stringstream text;
    SSTREAM_SET_HEX(text);
    text << "Allowed Media                    : 0x" << std::setw(8) << Xbe_certificate->dwAllowedMedia << " (" << AllowedMediaToString() << ")\n";
    text << "Game Region                      : 0x" << std::setw(8) << Xbe_certificate->dwGameRegion << " (" << Xbe_to_print->GameRegionToString() << ")\n";
    text << "Game Ratings                     : 0x" << std::setw(8) << Xbe_certificate->dwGameRatings << " (" << GameRatingToString() << ")\n";
    text << "Disc Number                      : 0x" << std::setw(8) << Xbe_certificate->dwDiscNumber << "\n";
    text << "Version                          : 0x" << std::setw(8) << Xbe_certificate->dwVersion << "\n";
    SSTREAM_SET_DEC(text);
    text << "Disc Version                     : " << std::setw(0) << Xbe_to_print->GetDiscVersion() << "\n";
    text << "Patch Version                    : " << std::setw(0) << Xbe_to_print->GetPatchVersion() << "\n";

    return text.str();
}

std::string XbePrinter::GenLANKey()
{
    const uint8_t row = 0;
    const uint8_t row_size = 16;
    std::string text;
    text.append("LAN Key                          : ");
    text.append(GenHexRow(&(Xbe_certificate->bzLanKey[0]), row, row_size));
    text.append("\n");
    return text;
}

std::string XbePrinter::GenSignatureKey()
{
    const uint8_t row = 0;
    const uint8_t row_size = 16;
    std::string text;
    text.append("Signature Key                    : ");
    text.append(GenHexRow(&(Xbe_certificate->bzSignatureKey[0]), row, row_size));
    text.append("\n");
    return text;
}

std::string XbePrinter::GenAlternateSignatureKeys()
{
    const uint8_t row = 0;
    const uint8_t row_size = 16;
    std::string text;

    text.append("Title Alternate Signature Keys : <Hex Dump>");
    for(int row = 0; row < 16; row++)
    {
        text.append("\n                                   ");
        text.append(GenHexRow(&(Xbe_certificate->bzTitleAlternateSignatureKey[0][0]), row, row_size));
    }
    text.append("\n                                   </Hex Dump>\n");
    return text;
}

std::string XbePrinter::GenExtraInfo()
{
    std::stringstream text;
    
    if (Xbe_certificate->dwSize >= 0x1EC)
    {
        SSTREAM_SET_HEX(text);
        text << "Original Certificate Size        : 0x" << std::setw(8) << Xbe_certificate->dwOriginalCertificateSize << "\n";
        text << "Online Service ID                : 0x" << std::setw(8) << Xbe_certificate->dwOnlineService << "\n";
        text << "Extra Security Flags             : 0x" << std::setw(8) << Xbe_certificate->dwSecurityFlags << "\n";
        text << "Code Encryption Key              : ";
        text << GenHexRow(&(Xbe_certificate->bzCodeEncKey[0]), 0, 16) << "\n";
    }
    return text.str();
}

std::string XbePrinter::GenSectionInfo()
{
    std::string text;
    text.append("\nDumping XBE Section Headers...\n\n");
    text.append(GenSectionHeaders());
    return text;
}

std::string XbePrinter::GenSectionHeaders()
{
    std::stringstream text;
    SSTREAM_SET_HEX(text);
    for(uint32_t v=0; v < Xbe_header->dwSections; v++) {
        text << "Section Name                     : 0x" << std::setw(8) << Xbe_to_print->m_SectionHeader[v].dwSectionNameAddr << " (\"" << Xbe_to_print->m_szSectionName[v] << "\")\n";
        text << GenSectionFlags(Xbe_to_print->m_SectionHeader[v]);
        text << "Virtual Address                  : 0x" << std::setw(8) << Xbe_to_print->m_SectionHeader[v].dwVirtualAddr << "\n";
        text << "Virtual Size                     : 0x" << std::setw(8) << Xbe_to_print->m_SectionHeader[v].dwVirtualSize << "\n";
        text << "Raw Address                      : 0x" << std::setw(8) << Xbe_to_print->m_SectionHeader[v].dwRawAddr << "\n";
        text << "Size of Raw                      : 0x" << std::setw(8) << Xbe_to_print->m_SectionHeader[v].dwSizeofRaw << "\n";
        text << "Section Name Address             : 0x" << std::setw(8) << Xbe_to_print->m_SectionHeader[v].dwSectionNameAddr << "\n";
        text << "Section Reference Count          : 0x" << std::setw(8) << Xbe_to_print->m_SectionHeader[v].dwSectionRefCount << "\n";
        text << "Head Shared Reference Count Addr : 0x" << std::setw(8) << Xbe_to_print->m_SectionHeader[v].dwHeadSharedRefCountAddr << "\n";
        text << "Tail Shared Reference Count Addr : 0x" << std::setw(8) << Xbe_to_print->m_SectionHeader[v].dwTailSharedRefCountAddr << "\n";
        text << GenSectionDigest(Xbe_to_print->m_SectionHeader[v]) << "\n";
        if (Xbe_to_print->CheckSectionIntegrity(v)) {
            text << "SHA hash check of section " << Xbe_to_print->m_szSectionName[v] << " successful" << "\n\n";
        }
        else {
            text << "SHA hash of section " << Xbe_to_print->m_szSectionName[v] << " doesn't match, section is corrupted" << "\n\n";
        }
    }
    return text.str();
}

std::string XbePrinter::GenSectionFlags(Xbe::SectionHeader section_header)
{
    std::stringstream text;
    SSTREAM_SET_HEX(text);
    text << "Flags                            : 0x" << std::setw(8) << section_header.dwFlags_value << " ";

    if(section_header.dwFlags.bWritable) {
       text << "(Writable) ";
    }
    if(section_header.dwFlags.bPreload) {
       text << "(Preload) ";
    }
    if(section_header.dwFlags.bExecutable) {
       text << "(Executable) ";
    }
    if(section_header.dwFlags.bInsertedFile) {
       text << "(Inserted File) ";
    }
    if(section_header.dwFlags.bHeadPageRO) {
       text << "(Head Page RO) ";
    }
    if(section_header.dwFlags.bTailPageRO) {
        text << "(Tail Page RO) ";
    }
    text << "\n";
    return text.str();
}

std::string XbePrinter::GenSectionDigest(Xbe::SectionHeader section_header)
{
    std::string text;
    text.append("Section Digest                   : ");
    text.append(GenHexRow(&section_header.bzSectionDigest[0], 0, 20));
    return text;
}

std::string XbePrinter::GenLibraryVersions()
{
    std::stringstream text;
    text << "Dumping XBE Library Versions...\n\n";

    if(Xbe_to_print->m_LibraryVersion == 0 || Xbe_header->dwLibraryVersions == 0) {
        text << "(This XBE contains no Library Versions)\n\n";
    }
    else {
        for(uint32_t v = 0; v < Xbe_header->dwLibraryVersions; v++) {
            char libname[9];
            for(uint32_t c=0;c<8;c++) {
                libname[c] = Xbe_to_print->m_LibraryVersion[v].szName[c];
            }
            libname[8] = '\0';

            text << "Library Name                     : " << libname << "\n";
            text << "Version                          : " 
                 << Xbe_to_print->m_LibraryVersion[v].wMajorVersion << "." 
                 << Xbe_to_print->m_LibraryVersion[v].wMinorVersion << "." 
                 << Xbe_to_print->m_LibraryVersion[v].wBuildVersion << "\n";
            text << GenLibraryFlags(Xbe_to_print->m_LibraryVersion[v]);
            text << "\n";
        }
    }
    return text.str();
}

std::string XbePrinter::GenLibraryFlags(Xbe::LibraryVersion libver)
{
    std::stringstream text;
    SSTREAM_SET_HEX(text);
    text << "Flags                            : 0x" << std::setw(4) << libver.wFlags_value << " ";
    text << "QFEVersion : 0x" << std::setw(4) << libver.wFlags.QFEVersion << ", ";

    if(libver.wFlags.bDebugBuild) {
        text << "Debug, ";
    }
    else {
        text << "Retail, ";
    }

    switch(libver.wFlags.Approved) {
        case 0:
            text << "Unapproved";
            break;
        case 1:
            text << "Possibly Approved";
            break;
        case 2:
            text << "Approved";
            break;
    }
    text << "\n";
    return text.str();
}

std::string XbePrinter::GenTLS()
{
    std::stringstream text;
    SSTREAM_SET_HEX(text);
    Xbe::TLS *local_TLS = Xbe_to_print->m_TLS;

    text << "Dumping XBE TLS...\n\n";
    // print thread local storage
    if(local_TLS != 0) {
        text << "Data Start Address               : 0x" << std::setw(8) << local_TLS->dwDataStartAddr << "\n";
        text << "Data End Address                 : 0x" << std::setw(8) << local_TLS->dwDataEndAddr << "\n";
        text << "TLS Index Address                : 0x" << std::setw(8) << local_TLS->dwTLSIndexAddr << "\n";
        text << "TLS Callback Address             : 0x" << std::setw(8) << local_TLS->dwTLSCallbackAddr << "\n";
        text << "Size of Zero Fill                : 0x" << std::setw(8) << local_TLS->dwSizeofZeroFill << "\n";
        text << "Characteristics                  : 0x" << std::setw(8) << local_TLS->dwCharacteristics << "\n";
    }
    else {
        text << "(This XBE contains no TLS)\n";
    }
    return text.str();
}
