/*******************************************************************************
*
*  (C) COPYRIGHT MICROSOFT CORP., 1993-1994
*
*  TITLE:       REGPRINT.C
*
*  VERSION:     4.0
*
*  AUTHOR:      Tracy Sharpe
*
*  DATE:        21 Nov 1993
*
*  Print routines for the Registry Editor.
*
*******************************************************************************/

#include "pch.h"
#include "regprint.h"
#include "regcdhk.h"
#include "regresid.h"
#include "regedit.h"
#include "richedit.h"
#include "regporte.h"
#include "reg1632.h"
#include <malloc.h>

extern void PrintResourceData(PBYTE pbData, UINT uSize, DWORD dwType);

const TCHAR s_PrintLineBreak[] = TEXT(",\n  ");

PRINTDLGEX g_PrintDlg;

typedef struct _PRINT_IO {
    BOOL fContinueJob;
    UINT ErrorStringID;
    HWND hRegPrintAbortWnd;
    RECT rcPage;
    RECT rcOutput;
    PTSTR pLineBuffer;
    UINT cch;
    UINT cBufferPos;
    LPTSTR lpNewLineChars;
}   PRINT_IO;

#define CANCEL_NONE                     0x0000
#define CANCEL_MEMORY_ERROR             0x0001
#define CANCEL_PRINTER_ERROR            0x0002
#define CANCEL_ABORT                    0x0004

#define INITIAL_PRINTBUFFER_SIZE        8192

PRINT_IO s_PrintIo;

BOOL
CALLBACK
RegPrintAbortProc(
    HDC hDC,
    int Error
    );

INT_PTR
CALLBACK
RegPrintAbortDlgProc(
    HWND hWnd,
    UINT Message,
    WPARAM wParam,
    LPARAM lParam
    );

void RegPrintSubtree();
void PrintBranch(HKEY hKey, LPTSTR lpFullKeyName);
void PrintKeyValues(HKEY hKey);
void PrintValueData(PBYTE pbValueData, DWORD cbValueData, DWORD dwType);
void PrintKeyHeader(HKEY hKey, LPTSTR lpFullKeyName);
void PrintClassName(HKEY hKey);
void PrintLastWriteTime(HKEY hKey);
void PrintDynamicString(UINT uStringID);
void PrintType(DWORD dwType);
void PrintBinaryData(PBYTE ValueData, UINT cbcbValueData);
void PrintDWORDData(PBYTE ValueData, UINT cbcbValueData);
void PrintLiteral(PTSTR lpLiteral);
BOOL PrintChar(TCHAR Char);
void PrintMultiString(LPTSTR pszData, int cbData);
UINT PrintToSubTreeError(UINT uPrintErrorStringID);
void PrintNewLine();

/*******************************************************************************
*
*  Implement IPrintDialogCallback
*
*  DESCRIPTION:
*     This interface is necessary to handle messages through PrintDlgEx
*     This interface doesn't need to have all the correct semantics of a COM
*     Object
*
*******************************************************************************/

typedef struct
{
    IPrintDialogCallback ipcb;
} CPrintCallback;

#define IMPL(type, pos, ptr) (type*)

static
HRESULT
CPrintCallback_QueryInterface(IPrintDialogCallback *ppcb, REFIID riid, void **ppv)
{
    CPrintCallback *this = (CPrintCallback*)ppcb;
    if (IsEqualIID (riid, &IID_IUnknown) || IsEqualIID (riid, &IID_IPrintDialogCallback))
        *ppv = &this->ipcb;
    else
    {
        *ppv = 0;
        return E_NOINTERFACE;
    }

    this->ipcb.lpVtbl->AddRef(&this->ipcb);
    return NOERROR;
}

static
ULONG
CPrintCallback_AddRef(IPrintDialogCallback *ppcb)
{
    CPrintCallback *this = (CPrintCallback*)ppcb;
    return 1;
}

static
ULONG
CPrintCallback_Release(IPrintDialogCallback *ppcb)
{
    CPrintCallback *this = (CPrintCallback*)ppcb;
    return 1;
}

static
HRESULT
CPrintCallback_InitDone(IPrintDialogCallback *ppcb)
{
    return S_OK;
}

static
HRESULT
CPrintCallback_SelectionChange(IPrintDialogCallback *ppcb)
{
    return S_OK;
}

static
HRESULT
CPrintCallback_HandleMessage(
    IPrintDialogCallback *ppcb,
    HWND hDlg,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam,
    LRESULT *pResult)
{
    *pResult = RegCommDlgHookProc(hDlg, uMsg, wParam, lParam);
    return S_OK;
}


static IPrintDialogCallbackVtbl vtblPCB =
{
    CPrintCallback_QueryInterface,
    CPrintCallback_AddRef,
    CPrintCallback_Release,
    CPrintCallback_InitDone,
    CPrintCallback_SelectionChange,
    CPrintCallback_HandleMessage
};

CPrintCallback g_callback;

/*******************************************************************************
*
*  RegEdit_OnCommandPrint
*
*  DESCRIPTION:
*     Handles the selection of the "Print" option by the user for the RegEdit
*     dialog box.
*
*  PARAMETERS:
*     hWnd, handle of RegPrint window.
*
*******************************************************************************/

VOID
PASCAL
RegEdit_OnCommandPrint(
    HWND hWnd
    )
{

    LPDEVNAMES lpDevNames;
    TEXTMETRIC TextMetric;
    DOCINFO DocInfo;
    LOGFONT lf;
    HGLOBAL hDevMode;
    HGLOBAL hDevNames;
    RECT rc;
    HWND hRichEdit;
    FORMATRANGE fr;
    HINSTANCE hInstRichEdit;
    int nOffsetX;
    int nOffsetY;
    PTSTR pszFontName;

    g_callback.ipcb.lpVtbl = &vtblPCB;

    // We have to completely fill out the PRINTDLGEX structure
    // correctly or the PrintDlgEx function will return an error.
    // The easiest way is to memset it to 0

    hDevMode = g_PrintDlg.hDevMode;
    hDevNames = g_PrintDlg.hDevNames;
    memset(&g_PrintDlg, 0, sizeof(g_PrintDlg));

    g_PrintDlg.lStructSize = sizeof(PRINTDLGEX);
    g_PrintDlg.hwndOwner = hWnd;
    g_PrintDlg.hDevMode = hDevMode;
    g_PrintDlg.hDevNames = hDevNames;
    g_PrintDlg.hDC = NULL;
    g_PrintDlg.Flags = PD_NOPAGENUMS | PD_RETURNDC | PD_ENABLEPRINTTEMPLATE;
    g_PrintDlg.Flags2 = 0;
    g_PrintDlg.ExclusionFlags = 0;
    g_PrintDlg.hInstance = g_hInstance;
    g_PrintDlg.nCopies = 1;
    g_PrintDlg.nStartPage = START_PAGE_GENERAL;
    g_PrintDlg.lpCallback = (IUnknown*) &g_callback.ipcb;
    g_PrintDlg.lpPrintTemplateName = MAKEINTRESOURCE(IDD_REGPRINT);
    g_RegCommDlgDialogTemplate = IDD_REGPRINT;

    if (FAILED(PrintDlgEx(&g_PrintDlg)))
        return;
    if (g_PrintDlg.dwResultAction != PD_RESULT_PRINT)
        return;

    s_PrintIo.ErrorStringID = IDS_PRINTERRNOMEMORY;

    if ((lpDevNames = GlobalLock(g_PrintDlg.hDevNames)) == NULL)
        goto error_ShowDialog;

    //
    //  For now, assume a page with top and bottom margins of 1/2 inch and
    //  left and right margins of 3/4 inch (the defaults of Notepad).
    //  rcPage and rcOutput are in TWIPS (1/20th of a point)
    //

    rc.left = rc.top = 0;
    rc.bottom = GetDeviceCaps(g_PrintDlg.hDC, PHYSICALHEIGHT);
    rc.right = GetDeviceCaps(g_PrintDlg.hDC, PHYSICALWIDTH);
    nOffsetX = GetDeviceCaps(g_PrintDlg.hDC, PHYSICALOFFSETX);
    nOffsetY = GetDeviceCaps(g_PrintDlg.hDC, PHYSICALOFFSETY);

    s_PrintIo.rcPage.left = s_PrintIo.rcPage.top = 0;
    s_PrintIo.rcPage.right = MulDiv(rc.right, 1440, GetDeviceCaps(g_PrintDlg.hDC, LOGPIXELSX));
    s_PrintIo.rcPage.bottom = MulDiv(rc.bottom, 1440, GetDeviceCaps(g_PrintDlg.hDC, LOGPIXELSY));

    s_PrintIo.rcOutput.left = 1080;
    s_PrintIo.rcOutput.top = 720;
    s_PrintIo.rcOutput.right = s_PrintIo.rcPage.right - 1080;
    s_PrintIo.rcOutput.bottom = s_PrintIo.rcPage.bottom - 720;

    //
    //
    //

    if ((s_PrintIo.pLineBuffer = (PTSTR) LocalAlloc(LPTR, INITIAL_PRINTBUFFER_SIZE*sizeof(TCHAR))) == NULL)
        goto error_DeleteDC;
    s_PrintIo.cch = INITIAL_PRINTBUFFER_SIZE;
    s_PrintIo.cBufferPos = 0;

    if ((s_PrintIo.hRegPrintAbortWnd = CreateDialog(g_hInstance,
        MAKEINTRESOURCE(IDD_REGPRINTABORT), hWnd, RegPrintAbortDlgProc)) ==
        NULL)
        goto error_FreeLineBuffer;

    EnableWindow(hWnd, FALSE);

    //
    //  Prepare the document for printing.
    //
    s_PrintIo.ErrorStringID = 0;
    s_PrintIo.fContinueJob = TRUE;
    s_PrintIo.lpNewLineChars = TEXT("\n");
    SetAbortProc(g_PrintDlg.hDC, RegPrintAbortProc);

    DocInfo.cbSize = sizeof(DOCINFO);
    DocInfo.lpszDocName = LoadDynamicString(IDS_REGEDIT);
    DocInfo.lpszOutput = (LPTSTR) lpDevNames + lpDevNames-> wOutputOffset;
    DocInfo.lpszDatatype = NULL;
    DocInfo.fwType = 0;

    s_PrintIo.ErrorStringID = 0;

    if (StartDoc(g_PrintDlg.hDC, &DocInfo) <= 0) {

        if (GetLastError() != ERROR_PRINT_CANCELLED)
            s_PrintIo.ErrorStringID = IDS_PRINTERRPRINTER;
        goto error_DeleteDocName;

    }

    // Print registry subtree.
    RegPrintSubtree();

    if (s_PrintIo.ErrorStringID != 0)
    {
        InternalMessageBox(g_hInstance, hWnd,
            MAKEINTRESOURCE(s_PrintIo.ErrorStringID),
            MAKEINTRESOURCE(IDS_REGEDIT), MB_ICONERROR | MB_OK);
    }

    hInstRichEdit = LoadLibrary(TEXT("riched20.dll"));

    hRichEdit = CreateWindowEx(0, RICHEDIT_CLASS, NULL, ES_MULTILINE, 0, 0, 100, 100, NULL, NULL, NULL, NULL);
    SendMessage(hRichEdit, WM_SETTEXT, 0, (LPARAM)s_PrintIo.pLineBuffer);

    pszFontName = LoadDynamicString(IDS_PRINT_FONT);
    if (pszFontName)
    {
        CHARFORMAT cf;

        cf.cbSize = sizeof(CHARFORMAT);
        cf.dwMask = CFM_FACE | CFM_BOLD;
        cf.dwEffects = 0x00;
        cf.bPitchAndFamily = FIXED_PITCH | FF_MODERN;
        wsprintf(cf.szFaceName, TEXT("%s"), pszFontName);

        SendMessage(hRichEdit, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);

        DeleteDynamicString(pszFontName);
    }

    fr.hdc = g_PrintDlg.hDC;
    fr.hdcTarget = g_PrintDlg.hDC;
    fr.rc = s_PrintIo.rcOutput;
    fr.rcPage = s_PrintIo.rcPage;
    fr.chrg.cpMin = 0;
    fr.chrg.cpMax = -1;

    while (fr.chrg.cpMin < (int) s_PrintIo.cBufferPos) {
        StartPage(g_PrintDlg.hDC);

        // We have to adjust the origin because 0,0 is not at the corner of the paper
        // but is at the corner of the printable region

        SetViewportOrgEx(g_PrintDlg.hDC, -nOffsetX, -nOffsetY, NULL);
        fr.chrg.cpMin = (LONG)SendMessage(hRichEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
        SendMessage(hRichEdit, EM_DISPLAYBAND, 0, (LPARAM)&s_PrintIo.rcOutput);
        EndPage(g_PrintDlg.hDC);
        if (!s_PrintIo.fContinueJob)
            break;
    }
    SendMessage(hRichEdit, EM_FORMATRANGE, FALSE, 0);

    //
    //  End the print job.
    //

    if (s_PrintIo.ErrorStringID == 0 && s_PrintIo.fContinueJob) {

        if (EndDoc(g_PrintDlg.hDC) <= 0) {
            s_PrintIo.ErrorStringID = IDS_PRINTERRPRINTER;
            goto error_AbortDoc;
        }
    }

    //
    //  Either a printer error occurred or the user cancelled the printing, so
    //  abort the print job.
    //

    else {

error_AbortDoc:
        AbortDoc(g_PrintDlg.hDC);

    }

    DestroyWindow(hRichEdit);
    FreeLibrary(hInstRichEdit);

error_DeleteDocName:
    DeleteDynamicString(DocInfo.lpszDocName);

//  error_DestroyRegPrintAbortWnd:
    EnableWindow(hWnd, TRUE);
    DestroyWindow(s_PrintIo.hRegPrintAbortWnd);

error_FreeLineBuffer:
    LocalFree((HLOCAL)s_PrintIo.pLineBuffer);

error_DeleteDC:
    DeleteDC(g_PrintDlg.hDC);
    g_PrintDlg.hDC = NULL;
    GlobalUnlock(g_PrintDlg.hDevNames);

error_ShowDialog:
    if (s_PrintIo.ErrorStringID != 0)
        InternalMessageBox(g_hInstance, hWnd,
            MAKEINTRESOURCE(s_PrintIo.ErrorStringID),
            MAKEINTRESOURCE(IDS_REGEDIT), MB_ICONERROR | MB_OK);

}


//------------------------------------------------------------------------------
// RegEdit_SaveAsSubtree
//
// DESCRIPTION: Saves a subtree to a file
//
// PARAMETERS: LPTSTR lpFileName - file name
//             LPTSTR lpSelectedPath - path to key
//------------------------------------------------------------------------------
UINT RegEdit_SaveAsSubtree(LPTSTR lpFileName, LPTSTR lpSelectedPath)
{
    s_PrintIo.pLineBuffer = (PTSTR) LocalAlloc(LPTR, INITIAL_PRINTBUFFER_SIZE*sizeof(TCHAR));
    if (s_PrintIo.pLineBuffer)
    {
        FILE_HANDLE hFile;

        // Init the printing info
        s_PrintIo.pLineBuffer[0] = 0xFEFF; //unicode byte order mark
        s_PrintIo.cch = INITIAL_PRINTBUFFER_SIZE;
        s_PrintIo.cBufferPos = 1;
        s_PrintIo.fContinueJob = TRUE;
        s_PrintIo.ErrorStringID = 0;
        s_PrintIo.lpNewLineChars = TEXT("\r\n");

        RegPrintSubtree();

        // write the buffer to the file
        if (OPENWRITEFILE(lpFileName, hFile))
        {
            DWORD cbWritten = 0;

            if (!WRITEFILE(hFile, s_PrintIo.pLineBuffer, s_PrintIo.cBufferPos*sizeof(TCHAR), &cbWritten))
            {
                s_PrintIo.ErrorStringID = IDS_EXPFILEERRFILEWRITE;
            }

            CLOSEFILE(hFile);
        }
        else
        {
            s_PrintIo.ErrorStringID = IDS_EXPFILEERRFILEOPEN;
        }

        LocalFree(s_PrintIo.pLineBuffer);
    }

    return PrintToSubTreeError(s_PrintIo.ErrorStringID);
}


//------------------------------------------------------------------------------
// PrintToSubTreeError
//
// DESCRIPTION: Prints a subtree
//
// PARAMETER: UINT uPrintErrorStringID - print error string id
//------------------------------------------------------------------------------
UINT PrintToSubTreeError(UINT uPrintErrorStringID)
{
    UINT uError = uPrintErrorStringID;

    switch (uPrintErrorStringID)
    {
    case IDS_PRINTERRNOMEMORY:
        uError = IDS_SAVETREEERRNOMEMORY;

    case IDS_PRINTERRCANNOTREAD:
        uError = IDS_SAVETREEERRCANNOTREAD;
    }

    return uError;
}


//------------------------------------------------------------------------------
// RegPrintSubtree
//
// DESCRIPTION: Prints a subtree
//------------------------------------------------------------------------------
void RegPrintSubtree()
{
    HTREEITEM hSelectedTreeItem = TreeView_GetSelection(g_RegEditData.hKeyTreeWnd);

    if (g_fRangeAll)
    {
        HTREEITEM hComputerItem = RegEdit_GetComputerItem(hSelectedTreeItem);

        lstrcpy(g_SelectedPath, g_RegistryRoots[INDEX_HKEY_LOCAL_MACHINE].lpKeyName);
        PrintBranch(Regedit_GetRootKeyFromComputer(hComputerItem, g_SelectedPath),
            g_SelectedPath);

        lstrcpy(g_SelectedPath, g_RegistryRoots[INDEX_HKEY_USERS].lpKeyName);
        PrintBranch(Regedit_GetRootKeyFromComputer(hComputerItem, g_SelectedPath),
            g_SelectedPath);
    }
    else
    {
        HKEY hKey;

        if (EditRegistryKey(RegEdit_GetComputerItem(hSelectedTreeItem),
            &hKey, g_SelectedPath, ERK_OPEN) == ERROR_SUCCESS)
        {
            PrintBranch(hKey, g_SelectedPath);
            RegCloseKey(hKey);
        }
    }
}

/*******************************************************************************
*
*  RegPrintAbortProc
*
*  DESCRIPTION:
*     Callback procedure to check if the print job should be canceled.
*
*  PARAMETERS:
*     hDC, handle of printer device context.
*     Error, specifies whether an error has occurred.
*     (returns), TRUE to continue the job, else FALSE to cancel the job.
*
*******************************************************************************/

BOOL
CALLBACK
RegPrintAbortProc(
    HDC hDC,
    int Error
    )
{

    while (s_PrintIo.fContinueJob && MessagePump(s_PrintIo.hRegPrintAbortWnd))
        ;

    return s_PrintIo.fContinueJob;

    UNREFERENCED_PARAMETER(hDC);
    UNREFERENCED_PARAMETER(Error);

}

/*******************************************************************************
*
*  RegPrintAbortDlgProc
*
*  DESCRIPTION:
*     Callback procedure for the RegPrintAbort dialog box.
*
*  PARAMETERS:
*     hWnd, handle of RegPrintAbort window.
*     Message,
*     wParam,
*     lParam,
*     (returns),
*
*******************************************************************************/

INT_PTR
CALLBACK
RegPrintAbortDlgProc(
    HWND hWnd,
    UINT Message,
    WPARAM wParam,
    LPARAM lParam
    )
{

    switch (Message) {

        case WM_INITDIALOG:
            break;

        case WM_CLOSE:
        case WM_COMMAND:
            s_PrintIo.fContinueJob = FALSE;
            break;

        default:
            return FALSE;

    }

    return TRUE;

}

/*******************************************************************************
*
*  PrintBranch
*
*  DESCRIPTION:
*
*  PARAMETERS:
*
*******************************************************************************/

void PrintBranch(HKEY hKey, LPTSTR lpFullKeyName)
{
    // Write out the section header.
    PrintKeyHeader(hKey, lpFullKeyName);

    // Print the vales for the key.
    PrintKeyValues(hKey);

    if (s_PrintIo.ErrorStringID == 0)
    {
        HKEY hSubKey;
        int nLenFullKey;
        DWORD EnumIndex = 0;
        LPTSTR lpSubKeyName;
        LPTSTR lpTempFullKeyName;

        //  Write out all of the subkeys and recurse into them.

        //copy the existing key into a new buffer with enough room for the next key
        nLenFullKey = lstrlen(lpFullKeyName);
        lpTempFullKeyName = (LPTSTR) alloca( (nLenFullKey+MAXKEYNAME)*sizeof(TCHAR));
        lstrcpy(lpTempFullKeyName, lpFullKeyName);
        lpSubKeyName = lpTempFullKeyName + nLenFullKey;
        *lpSubKeyName++ = TEXT('\\');
        *lpSubKeyName = 0;

        PrintNewLine();

        while (s_PrintIo.fContinueJob)
        {
            if (RegEnumKey(hKey, EnumIndex++, lpSubKeyName, MAXKEYNAME-1) !=
                ERROR_SUCCESS)
                break;

            if(RegOpenKeyEx(hKey,lpSubKeyName,0,KEY_ENUMERATE_SUB_KEYS|KEY_QUERY_VALUE,&hSubKey) ==
                NO_ERROR)
            {

                PrintBranch(hSubKey, lpTempFullKeyName);
                RegCloseKey(hSubKey);
            }
            else
            {
                DebugPrintf(("RegOpenKey failed."));
            }

        }
    }
}


//------------------------------------------------------------------------------
// PrintKeyHeader
//
// DESCRIPTION: Prints the header information of a key
//
// PARAMETERS: HKEY hKey - key
//             LPTSTR lpFullKeyName - path to key
//------------------------------------------------------------------------------
void PrintKeyValues(HKEY hKey)
{
    DWORD EnumIndex = 0;

    //  Write out all of the value names and their data.
    while (s_PrintIo.fContinueJob)
    {
        DWORD Type;
        DWORD cbValueData;
        PBYTE pbValueData;
        TCHAR acAuxNumber[MAXVALUENAME_LENGTH];
        DWORD cchValueName = ARRAYSIZE(g_ValueNameBuffer);

        // Query for data size
        if (RegEnumValue(hKey, EnumIndex++, g_ValueNameBuffer,
            &cchValueName, NULL, &Type, NULL, &cbValueData) != ERROR_SUCCESS)
        {
            break;
        }

        // Print value number
        PrintDynamicString(IDS_PRINT_NUMBER);
        wsprintf(acAuxNumber, TEXT("%d"), EnumIndex - 1);
        PrintLiteral(acAuxNumber);
        PrintNewLine();

        // Print key name
        PrintDynamicString(IDS_PRINT_NAME);
        if (cchValueName)
        {
            PrintLiteral(g_ValueNameBuffer);
        }
        else
        {
            PrintDynamicString(IDS_PRINT_NO_NAME);
        }
        PrintNewLine();

        // Print Type
        PrintType(Type);

        // allocate memory for data
        pbValueData =  LocalAlloc(LPTR, cbValueData+ExtraAllocLen(Type));
        if (pbValueData)
        {
            if (RegEdit_QueryValueEx(hKey, g_ValueNameBuffer,
                NULL, &Type, pbValueData, &cbValueData) == ERROR_SUCCESS)
            {
                PrintValueData(pbValueData, cbValueData, Type);
            }
            else
            {
                s_PrintIo.ErrorStringID = IDS_PRINTERRCANNOTREAD;
            }

            if (pbValueData)
            {
                LocalFree(pbValueData);
                pbValueData = NULL;
            }
        }
        else
        {
            s_PrintIo.ErrorStringID = IDS_PRINTERRNOMEMORY;
            break;
        }
    }
}


//------------------------------------------------------------------------------
// PrintValueData
//
// DESCRIPTION: Prints the header information of a key
//
// PARAMETERS: pbValueData - byte data
//             cbValueData - count of bytes
//             dwType - data type
//------------------------------------------------------------------------------
void PrintValueData(PBYTE pbValueData, DWORD cbValueData, DWORD dwType)
{
    PrintDynamicString(IDS_PRINT_DATA);

    switch (dwType)
    {
    case REG_MULTI_SZ:
        PrintMultiString((LPTSTR)pbValueData, cbValueData);
        break;

    case REG_SZ:
    case REG_EXPAND_SZ:
        PrintLiteral((LPTSTR)pbValueData);
        PrintNewLine();
        break;

    case REG_DWORD:
        PrintDWORDData((PBYTE)pbValueData, cbValueData);
        break;

    case REG_RESOURCE_LIST:
    case REG_FULL_RESOURCE_DESCRIPTOR:
    case REG_RESOURCE_REQUIREMENTS_LIST:
        PrintResourceData((PBYTE)pbValueData, cbValueData, dwType);
        break;

    default:
        PrintBinaryData((PBYTE)pbValueData, cbValueData);
        break;
    }

    PrintNewLine();
}


//------------------------------------------------------------------------------
// PrintKeyHeader
//
// DESCRIPTION: Prints the header information of a key
//
// PARAMETERS: HKEY hKey - key
//             LPTSTR lpFullKeyName - path to key
//------------------------------------------------------------------------------
void PrintKeyHeader(HKEY hKey, LPTSTR lpFullKeyName)
{
    PrintDynamicString(IDS_PRINT_KEY_NAME);
    PrintLiteral(lpFullKeyName);
    PrintNewLine();

    PrintClassName(hKey);
    PrintLastWriteTime(hKey);
}


//------------------------------------------------------------------------------
// PrintClassName
//
// DESCRIPTION: Prints the class name
//
// PARAMETERS: HKEY hKey - key
//------------------------------------------------------------------------------
void PrintClassName(HKEY hKey)
{
    PTSTR pszClass;

    PrintDynamicString(IDS_PRINT_CLASS_NAME);

    pszClass = LocalAlloc(LPTR, ALLOCATION_INCR);
    if (pszClass)
    {
        HRESULT hr;
        DWORD cbClass = sizeof(pszClass);

        hr = RegQueryInfoKey(hKey, pszClass, &cbClass, NULL, NULL, NULL, NULL, NULL,
            NULL, NULL, NULL, NULL);

        if (hr == ERROR_MORE_DATA)
        {
            // need a bigger buffer
            PBYTE pbValueData = LocalReAlloc(pszClass, cbClass + 1, LMEM_MOVEABLE);
            if (pbValueData)
            {
                pszClass = (PTSTR)pbValueData;
                hr = RegQueryInfoKey(hKey, pszClass, &cbClass, NULL, NULL, NULL, NULL, NULL,
                    NULL, NULL, NULL, NULL);
            }
        }

        if (cbClass && (hr == ERROR_SUCCESS))
        {
            PrintLiteral(pszClass);
        }
        else
        {
            PrintDynamicString(IDS_PRINT_NO_CLASS);
        }

        LocalFree(pszClass);
    }

    PrintNewLine();
}


//------------------------------------------------------------------------------
// PrintLastWriteTime
//
// DESCRIPTION: Prints the last write time
//
// PARAMETERS: HKEY hKey - key
//------------------------------------------------------------------------------
void PrintLastWriteTime(HKEY hKey)
{
    FILETIME ftLastWriteTime;

    PrintDynamicString(IDS_PRINT_LAST_WRITE_TIME);

    if (RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
            NULL, NULL, NULL, &ftLastWriteTime) == ERROR_SUCCESS)
    {
        FILETIME ftLocalLastWriteTime;
        if (FileTimeToLocalFileTime(&ftLastWriteTime, &ftLocalLastWriteTime))
        {
            SYSTEMTIME stLastWriteTime;
            if (FileTimeToSystemTime(&ftLocalLastWriteTime, &stLastWriteTime))
            {
                TCHAR achData[50];
                TCHAR achTime[50];

                GetDateFormat(GetSystemDefaultLCID(), DATE_SHORTDATE, &stLastWriteTime,
                    NULL, achData, ARRAYSIZE(achData));

                GetTimeFormat(GetSystemDefaultLCID(), TIME_NOSECONDS, &stLastWriteTime,
                    NULL, achTime, ARRAYSIZE(achTime));

                PrintLiteral(achData);
                PrintLiteral(TEXT(" - "));
                PrintLiteral(achTime);
            }
        }
    }

    PrintNewLine();
}


//------------------------------------------------------------------------------
// PrintDynamicString
//
// DESCRIPTION: Prints the dynamic string
//
// PARAMETERS: UINT uStringID - resource string id
//------------------------------------------------------------------------------
void PrintDynamicString(UINT uStringID)
{
    PTSTR psz = LoadDynamicString(uStringID);
    if (psz)
    {
        PrintLiteral(psz);
        DeleteDynamicString(psz);
    }
}


//------------------------------------------------------------------------------
// PrintType
//
// DESCRIPTION: Prints the value type
//
// PARAMETERS: HKEY hKey - key
//------------------------------------------------------------------------------
void PrintType(DWORD dwType)
{
    UINT uTypeStringId;

    switch (dwType)
    {
    case REG_NONE:
        uTypeStringId = IDS_PRINT_TYPE_REG_NONE;
        break;
    case REG_SZ:
        uTypeStringId = IDS_PRINT_TYPE_REG_SZ;
        break;
    case REG_EXPAND_SZ:
        uTypeStringId = IDS_PRINT_TYPE_REG_EXPAND_SZ;
        break;
    case REG_BINARY:
        uTypeStringId = IDS_PRINT_TYPE_REG_BINARY;
        break;
    case REG_DWORD:
        uTypeStringId = IDS_PRINT_TYPE_REG_DWORD;
        break;
    case REG_LINK:
        uTypeStringId = IDS_PRINT_TYPE_REG_LINK;
        break;
    case REG_MULTI_SZ:
        uTypeStringId = IDS_PRINT_TYPE_REG_MULTI_SZ;
        break;
    case REG_RESOURCE_LIST:
        uTypeStringId = IDS_PRINT_TYPE_REG_RESOURCE_LIST;
        break;
    case REG_FULL_RESOURCE_DESCRIPTOR:
        uTypeStringId = IDS_PRINT_TYPE_REG_FULL_RESOURCE_DESCRIPTOR;
        break;
    case REG_RESOURCE_REQUIREMENTS_LIST:
        uTypeStringId = IDS_PRINT_TYPE_REG_RESOURCE_REQUIREMENTS_LIST;
        break;
    case REG_QWORD:
        uTypeStringId = IDS_PRINT_TYPE_REG_REG_QWORD;
        break;
    default:
        uTypeStringId = IDS_PRINT_TYPE_REG_UNKNOWN;
    }

    PrintDynamicString(IDS_PRINT_TYPE);
    PrintDynamicString(uTypeStringId);
    PrintNewLine();
}


/*******************************************************************************
*
*  PrintLiteral
*
*  DESCRIPTION:
*
*  PARAMETERS:
*
*******************************************************************************/
VOID PrintLiteral(PTSTR lpLiteral)
{
    if (s_PrintIo.fContinueJob)
        while (*lpLiteral != 0 && PrintChar(*lpLiteral++));
}


//------------------------------------------------------------------------------
// PrintBinaryData
//
// DESCRIPTION:  Print a string that contains the binary data
//
// PARAMETERS:   ValueData - Buffer that contains the binary data
//               cbValueData - Number of bytes in the buffer
//------------------------------------------------------------------------------
void PrintBinaryData(PBYTE ValueData, UINT cbValueData)
{
    DWORD   dwDataIndex;
    DWORD   dwDataIndex2 = 0; //tracks multiples of 16.

    if (cbValueData && ValueData)
    {
        // Display rows of 16 bytes of data.
        TCHAR achAuxData[80];

        PrintNewLine();

        for(dwDataIndex = 0;
            dwDataIndex < ( cbValueData >> 4 );
            dwDataIndex++,
            dwDataIndex2 = dwDataIndex << 4 )
        {
            //  The string that contains the format in the sprintf below
            //  cannot be broken because cfront  on mips doesn't like it.
            wsprintf(achAuxData,
                     TEXT("%08x   %02x %02x %02x %02x %02x %02x %02x %02x - %02x %02x %02x %02x %02x %02x %02x %02x  %c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c"),
                     dwDataIndex2,
                     ValueData[ dwDataIndex2 + 0  ],
                     ValueData[ dwDataIndex2 + 1  ],
                     ValueData[ dwDataIndex2 + 2  ],
                     ValueData[ dwDataIndex2 + 3  ],
                     ValueData[ dwDataIndex2 + 4  ],
                     ValueData[ dwDataIndex2 + 5  ],
                     ValueData[ dwDataIndex2 + 6  ],
                     ValueData[ dwDataIndex2 + 7  ],
                     ValueData[ dwDataIndex2 + 8  ],
                     ValueData[ dwDataIndex2 + 9  ],
                     ValueData[ dwDataIndex2 + 10 ],
                     ValueData[ dwDataIndex2 + 11 ],
                     ValueData[ dwDataIndex2 + 12 ],
                     ValueData[ dwDataIndex2 + 13 ],
                     ValueData[ dwDataIndex2 + 14 ],
                     ValueData[ dwDataIndex2 + 15 ],
                     iswprint( ValueData[ dwDataIndex2 + 0  ] )
                        ? ValueData[ dwDataIndex2 + 0  ]  : TEXT('.'),
                     iswprint( ValueData[ dwDataIndex2 + 1  ] )
                        ? ValueData[ dwDataIndex2 + 1  ]  : TEXT('.'),
                     iswprint( ValueData[ dwDataIndex2 + 2  ] )
                        ? ValueData[ dwDataIndex2 + 2  ]  : TEXT('.'),
                     iswprint( ValueData[ dwDataIndex2 + 3  ] )
                        ? ValueData[ dwDataIndex2 + 3  ]  : TEXT('.'),
                     iswprint( ValueData[ dwDataIndex2 + 4  ] )
                        ? ValueData[ dwDataIndex2 + 4  ]  : TEXT('.'),
                     iswprint( ValueData[ dwDataIndex2 + 5  ] )
                        ? ValueData[ dwDataIndex2 + 5  ]  : TEXT('.'),
                     iswprint( ValueData[ dwDataIndex2 + 6  ] )
                        ? ValueData[ dwDataIndex2 + 6  ]  : TEXT('.'),
                     iswprint( ValueData[ dwDataIndex2 + 7  ] )
                        ? ValueData[ dwDataIndex2 + 7  ]  : TEXT('.'),
                     iswprint( ValueData[ dwDataIndex2 + 8  ] )
                        ? ValueData[ dwDataIndex2 + 8  ]  : TEXT('.'),
                     iswprint( ValueData[ dwDataIndex2 + 9  ] )
                        ? ValueData[ dwDataIndex2 + 9  ]  : TEXT('.'),
                     iswprint( ValueData[ dwDataIndex2 + 10 ] )
                        ? ValueData[ dwDataIndex2 + 10 ]  : TEXT('.'),
                     iswprint( ValueData[ dwDataIndex2 + 11 ] )
                        ? ValueData[ dwDataIndex2 + 11 ]  : TEXT('.'),
                     iswprint( ValueData[ dwDataIndex2 + 12 ] )
                        ? ValueData[ dwDataIndex2 + 12 ]  : TEXT('.'),
                     iswprint( ValueData[ dwDataIndex2 + 13 ] )
                        ? ValueData[ dwDataIndex2 + 13 ]  : TEXT('.'),
                     iswprint( ValueData[ dwDataIndex2 + 14 ] )
                        ? ValueData[ dwDataIndex2 + 14 ]  : TEXT('.'),
                     iswprint( ValueData[ dwDataIndex2 + 15 ] )
                        ? ValueData[ dwDataIndex2 + 15 ]  : TEXT('.'));

            PrintLiteral(achAuxData);
            PrintNewLine();
        }

        // If the cbValueData is not an even multiple of 16
        // then there is one additonal line of data to display.
        if( cbValueData % 16 != 0 )
        {
            UINT cchBlanks = 0;
            UINT uLinePos = 0;
            DWORD dwSeperatorChars = 0;
            UINT  uIndex = wsprintf(achAuxData, TEXT("%08x   "), dwDataIndex << 4 );

            // Display the remaining data, one byte at a time in hex.
            for(dwDataIndex = dwDataIndex2;
                dwDataIndex < cbValueData;
                dwDataIndex++ )
            {
                uIndex += wsprintf((achAuxData + uIndex ), TEXT("%02x "), ValueData[dwDataIndex]);

                // If eight data values have been displayed, print the seperator.
                if( dwDataIndex % 8 == 7 )
                {
                    uIndex += wsprintf( &achAuxData[uIndex], TEXT("%s"), TEXT("- "));
                    // Remember that two seperator characters were displayed.
                    dwSeperatorChars = 2;
                }
            }

            // Fill with blanks to the printable characters position.
            // That is position 64 less 8 spaces for the 'address',
            // 3 blanks, 3 spaces for each value displayed, possibly
            // two for the seperator plus two blanks at the end.
            uLinePos = (8 + 3 + (( dwDataIndex % 16 ) * 3 ) + dwSeperatorChars + 2 );
            uLinePos = min(uLinePos, 64);

            for(cchBlanks = 64 - uLinePos;
                cchBlanks > 0;
                cchBlanks--)
            {
                achAuxData[uIndex++] = TEXT(' ');
            }

            // Display the remaining data, one byte at a time as
            // printable characters.
            for(
                dwDataIndex = dwDataIndex2;
                dwDataIndex < cbValueData;
                dwDataIndex++ )
            {

                uIndex += wsprintf(&achAuxData[ uIndex ],
                                  TEXT("%c"),
                                  iswprint( ValueData[ dwDataIndex ] )
                                   ? ValueData[ dwDataIndex ] : TEXT('.'));

            }
            PrintLiteral(achAuxData);
        }
    }
    PrintNewLine();
}


//------------------------------------------------------------------------------
// PrintDWORDData
//
// DESCRIPTION:  Prints a DWORD
//
// PARAMETERS:   ValueData - Buffer that contains the binary data
//               cbValueData - Number of bytes in the buffer
//------------------------------------------------------------------------------
void PrintDWORDData(PBYTE ValueData, UINT cbValueData)
{
    DWORD dwData = *((PDWORD)ValueData);
    if (cbValueData && ValueData)
    {
        TCHAR achAuxData[20]; // the largest dword string is only 8 hex digits
        wsprintf(achAuxData, TEXT("%#x"), dwData);

        PrintLiteral(achAuxData);
    }
    PrintNewLine();
}

/*******************************************************************************
*
*  PrintChar
*
*  DESCRIPTION:
*
*  PARAMETERS:
*
*******************************************************************************/

BOOL PrintChar(TCHAR Char)
{

    //
    //  Keep track of what column we're currently at.  This is useful in cases
    //  such as writing a large binary registry record.  Instead of writing one
    //  very long line, the other Print* routines can break up their output.
    //

    if (s_PrintIo.cBufferPos == s_PrintIo.cch) {
        PTSTR pNewBuffer = LocalAlloc(LPTR, 2*s_PrintIo.cch*sizeof(TCHAR));
        if (pNewBuffer == NULL)
            return FALSE;
        memcpy(pNewBuffer, s_PrintIo.pLineBuffer, s_PrintIo.cch*sizeof(TCHAR));
        LocalFree(s_PrintIo.pLineBuffer);
        s_PrintIo.pLineBuffer = pNewBuffer;
        s_PrintIo.cch *= 2;
    }

    s_PrintIo.pLineBuffer[s_PrintIo.cBufferPos++] = Char;

    return TRUE;
}


//------------------------------------------------------------------------------
//  PrintMultiString
//
//  DESCRIPTION: Prints a multi-string
//
//  PARAMETERS:  pszData - string
//               cbData  - number of bytes in string, including nulls
//------------------------------------------------------------------------------

VOID PrintMultiString(LPTSTR pszData, int cbData)
{
    if (s_PrintIo.fContinueJob)
    {
        int i = 0;
        int ccData = (cbData / sizeof(TCHAR)) - 2; // don't want last null of last string or multi-string

        for(i = 0; i < ccData; i++)
        {
            if (pszData[i] == TEXT('\0'))
            {
                PrintNewLine();
                PrintDynamicString(IDS_PRINT_KEY_NAME_INDENT);
            }
            else
            {
                PrintChar(pszData[i]);
            }
        }
    }
    PrintNewLine();
}


//------------------------------------------------------------------------------
//  PrintNewLine()
//
//  DESCRIPTION: Prints the newline chars.
//
//  PARAMETERS:  pszData - string
//               cbData  - number of bytes in string, including nulls
//------------------------------------------------------------------------------
void PrintNewLine()
{
    PrintLiteral(s_PrintIo.lpNewLineChars);
}