//+---------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1993 - 1997.
//
//  File:       clspsht.cpp
//
//  Contents:   Implements class CClsidPropertySheet
//
//  Classes:
//
//  Methods:    CClsidPropertySheet::CClsidPropertySheet
//              CClsidPropertySheet::~CClsidPropertySheet
//              CClsidPropertySheet::InitData
//              CClsidPropertySheet::OnNcCreate
//              CClsidPropertySheet::ValidateAndUpdate
//              CClsidPropertySheet::OnCommand
//              CClsidPropertySheet::LookAtCLSIDs
//              CClsidPropertySheet::ChangeCLSIDInfo
//
//  History:    23-Apr-96   BruceMa    Created.
//
//----------------------------------------------------------------------


#include "stdafx.h"
#include "resource.h"
#include "clspsht.h"
#include "datapkt.h"

#if !defined(STANDALONE_BUILD)
extern "C"
{
#include <getuser.h>
}
#endif

#include "util.h"
#include "newsrvr.h"

#if !defined(STANDALONE_BUILD)
    extern "C"
    {
    #include <sedapi.h>
    #include <ntlsa.h>
    }
#endif

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif



/////////////////////////////////////////////////////////////////////////////
// CClsidPropertySheet

IMPLEMENT_DYNAMIC(CClsidPropertySheet, CPropertySheet)

CClsidPropertySheet::CClsidPropertySheet(CWnd* pParentWnd)
         : CPropertySheet(IDS_PROPSHT_CAPTION1, pParentWnd)
{
}

CClsidPropertySheet::~CClsidPropertySheet()
{
}

BEGIN_MESSAGE_MAP(CClsidPropertySheet, CPropertySheet)
        //{{AFX_MSG_MAP(CClsidPropertySheet)
        ON_WM_NCCREATE()
        //}}AFX_MSG_MAP
END_MESSAGE_MAP()

CClsidPropertySheet::InitData(
        CString szAppName,
        HKEY hkAppID,
        HKEY * rghkCLSID,
        unsigned cCLSIDs)
{
    m_szAppName = szAppName;
    m_hkAppID = hkAppID;
    m_rghkCLSID = rghkCLSID;
    m_cCLSIDs = cCLSIDs;

    // Save the appid key, the table of clsid keys and the application
    // title globally so the property pages can access them
    // it
    g_hAppid = hkAppID;
    g_rghkCLSID = rghkCLSID;
    g_cCLSIDs = cCLSIDs;
    g_szAppTitle = (TCHAR *) LPCTSTR(szAppName);

    m_Page2.m_fRemote = FALSE;
    m_Page4.m_fService = FALSE;
    m_Page2.m_fCanBeLocal = FALSE;
    m_Page2.m_fLocal = FALSE;
    m_Page1.m_fSurrogate = FALSE;

    if (!LookAtCLSIDs())
    {
        return FALSE;
    }

    TCHAR szBuffer[MAX_PATH];
    DWORD dwSize;
    long lErr;

    dwSize = sizeof(szBuffer);
    lErr = RegQueryValueEx(
            m_hkAppID,
            TEXT("LocalService"),
            NULL,
            NULL,
            (BYTE *)szBuffer,
            &dwSize);
    if (lErr == ERROR_SUCCESS)
    {
        m_Page1.m_szServerPath = szBuffer;
        m_Page4.m_fService = TRUE;
        m_Page2.m_fCanBeLocal = TRUE;
        m_Page2.m_fLocal = TRUE;
    }
    else
    {
        dwSize = sizeof(szBuffer);
        lErr = RegQueryValueEx(
                m_hkAppID,
                TEXT("_LocalService"),
                NULL,
                NULL,
                (BYTE *)szBuffer,
                &dwSize);
        if (lErr == ERROR_SUCCESS)
        {
            m_Page1.m_szServerPath = szBuffer;
            m_Page4.m_fService = TRUE;
            m_Page2.m_fCanBeLocal = TRUE;
        }
    }

    dwSize = sizeof(szBuffer);

    if (!m_Page2.m_fLocal)
    {
        lErr = RegQueryValueEx(
                m_hkAppID,
                TEXT("DllSurrogate"),
                NULL,
                NULL,
                (BYTE *)szBuffer,
                &dwSize);
        if (lErr == ERROR_SUCCESS)
        {
            if (szBuffer[0])
                m_Page1.m_szServerPath = szBuffer;
            else
                m_Page1.m_szServerPath.LoadString(IDS_DEFAULT);
            m_Page1.m_fSurrogate = TRUE;
        }

    }
    dwSize = sizeof(szBuffer);

    lErr = RegQueryValueEx(
            m_hkAppID,
            TEXT("RemoteServerName"),
            NULL,
            NULL,
            (BYTE *)szBuffer,
            &dwSize);
    if (lErr == ERROR_SUCCESS)
    {
        m_Page1.m_szComputerName = szBuffer;
        m_Page2.m_szComputerName = szBuffer;
        m_Page2.m_fRemote = TRUE;
    }

    m_Page2.m_fAtStorage = FALSE;
    dwSize = sizeof(szBuffer);
    lErr = RegQueryValueEx(
            m_hkAppID,
            TEXT("ActivateAtStorage"),
            NULL,
            NULL,
            (BYTE *)szBuffer,
            &dwSize);
    if (lErr == ERROR_SUCCESS)
    {
        if (szBuffer[0] ==  L'Y' || szBuffer[0] == L'y')
        {
//                      m_Page2.m_fRemote = TRUE;
            m_Page2.m_fAtStorage = TRUE;
        }
    }

    dwSize = sizeof(szBuffer);
    lErr = RegQueryValueEx(
            m_hkAppID,
            TEXT("RunAs"),
            NULL,
            NULL,
            (BYTE *)szBuffer,
            &dwSize);
    if (lErr == ERROR_SUCCESS)
    {
        // If the RunAs name is empty, jam in something
        if (szBuffer[0] == TEXT('\0'))
        {
            _tcscpy(szBuffer, TEXT("<domain>\\<user>"));
        }

        if (0 == _tcsicmp(szBuffer, TEXT("Interactive User")))
        {
            m_Page4.m_iIdentity = 0;
        }
        else
        {
            m_Page4.m_iIdentity = 2;
            m_Page4.m_szUserName = szBuffer;

            // Extract password from the Lsa private database
            g_util.RetrieveUserPassword(g_szAppid , m_Page4.m_szPassword);
            m_Page4.m_szConfirmPassword = m_Page4.m_szPassword;
        }
    }
    else
    {
        if (m_Page4.m_fService)
        {
            m_Page4.m_iIdentity = 3;
        }
        else
        {
            m_Page4.m_iIdentity = 1;
        }
    }

    m_Page1.m_szServerName = m_szAppName;

    if (!m_Page1.m_fSurrogate)
    {
        if (m_Page2.m_fCanBeLocal)
        {
            if (m_Page4.m_fService)
                m_Page1.m_iServerType = SERVICE;
            else
                m_Page1.m_iServerType = LOCALEXE;
            if (m_Page2.m_fRemote)
                m_Page1.m_iServerType += 3;
        }
        else
            m_Page1.m_iServerType = PURE_REMOTE;
    }
    else
    {
        m_Page1.m_iServerType = SURROGATE;
    }


    // Set the title
    SetTitle((const TCHAR *) m_szAppName, PSH_PROPTITLE);
    m_Page1.m_szServerName = m_szAppName;

    // TODO: If there are running instances, then make IDC_RUNNING,
    // IDC_LIST2, IDC_BUTTON1, IDC_BUTTON2, and IDC_BUTTON3 visible
    // and fill in IDC_LIST2 on page 1.

    m_Page2.m_pPage1 = &m_Page1;


    // Fetch RunAs key, LaunchPermission, AccessPermission and
    // ConfigurationPermission
    int   err;
    DWORD dwType;
    BYTE  bValue[16];
    BYTE *pbValue = NULL;
    ULONG ulSize = 1;

    m_Page3.m_iAccess = 0;
    m_Page3.m_iLaunch = 0;
    m_Page3.m_iConfig = 0;

    // "AccessPermission"
    // Note: We always expect to get ERROR_MORE_DATA
    err = RegQueryValueEx(g_hAppid, TEXT("AccessPermission"), 0,
                          &dwType, bValue, &ulSize);
    if (err == ERROR_MORE_DATA)
    {
        pbValue = (BYTE *)GlobalAlloc(GMEM_FIXED, ulSize);
        if (pbValue == NULL)
        {
            return FALSE;
        }
        err = RegQueryValueEx(g_hAppid, TEXT("AccessPermission"), 0,
                              &dwType, pbValue, &ulSize);
    }

    if (err == ERROR_SUCCESS && g_util.CheckForValidSD((SECURITY_DESCRIPTOR *)pbValue))
    {
        m_Page3.m_iAccess = 1;
        g_virtreg.NewRegSingleACL(g_hAppid,
                                  NULL,
                                  TEXT("AccessPermission"),
                                  (SECURITY_DESCRIPTOR *) pbValue,
                                  TRUE,    // Already in self-relative form
                                  &m_Page3.m_iAccessIndex);
        CDataPacket *pCdb = g_virtreg.GetAt(m_Page3.m_iAccessIndex);
        pCdb->SetModified(FALSE);
    }
    GlobalFree(pbValue);
    pbValue = NULL;

    // "LaunchPermission"
    // Note: We always expect to get ERROR_MORE_DATA
    ulSize = 1;
    pbValue = NULL;
    err = RegQueryValueEx(g_hAppid, TEXT("LaunchPermission"), 0,
                          &dwType, bValue, &ulSize);
    if (err == ERROR_MORE_DATA)
    {
        pbValue = (BYTE *)GlobalAlloc(GMEM_FIXED, ulSize);
        if (pbValue == NULL)
        {
            return FALSE;
        }
        err = RegQueryValueEx(g_hAppid, TEXT("LaunchPermission"), 0,
                              &dwType, pbValue, &ulSize);
    }

    if (err == ERROR_SUCCESS && g_util.CheckForValidSD((SECURITY_DESCRIPTOR *)pbValue))
    {
        m_Page3.m_iLaunch = 1;
        g_virtreg.NewRegSingleACL(g_hAppid,
                                  NULL,
                                  TEXT("LaunchPermission"),
                                  (SECURITY_DESCRIPTOR *) pbValue,
                                  TRUE,    // Already in self-relative form
                                  &m_Page3.m_iLaunchIndex);
        CDataPacket * pCdb = g_virtreg.GetAt(m_Page3.m_iLaunchIndex);
        pCdb->SetModified(FALSE);
    }
    GlobalFree(pbValue);
    pbValue = NULL;


    // "ConfigurationPermission"

    // Fetch the security descriptor on this AppID
    // Note: We always expect to get ERROR_INSUFFICIENT_BUFFER
    ulSize = 1;
    err = RegGetKeySecurity(g_hAppid,
                            OWNER_SECURITY_INFORMATION |
                            GROUP_SECURITY_INFORMATION |
                            DACL_SECURITY_INFORMATION,
                            pbValue,
                            &ulSize);
    if (err == ERROR_INSUFFICIENT_BUFFER)
    {
        pbValue = (BYTE *)GlobalAlloc(GMEM_FIXED, ulSize);
        if (pbValue == NULL)
        {
            return FALSE;
        }
        err = RegGetKeySecurity(g_hAppid,
                            OWNER_SECURITY_INFORMATION |
                            GROUP_SECURITY_INFORMATION |
                            DACL_SECURITY_INFORMATION,
                            pbValue,
                            &ulSize);
    }

    // Fetch the current security descriptor on HKEY_CLASSES_ROOT
    // Note: We always expect to get ERROR_INSUFFICIENT_BUFFER
    BYTE *pbValue2 = NULL;

    ulSize = 1;
    pbValue2 = NULL;
    err = RegGetKeySecurity(HKEY_CLASSES_ROOT,
                            OWNER_SECURITY_INFORMATION |
                            GROUP_SECURITY_INFORMATION |
                            DACL_SECURITY_INFORMATION,
                            pbValue2,
                            &ulSize);
    if (err == ERROR_INSUFFICIENT_BUFFER)
    {
        pbValue2 = (BYTE *)GlobalAlloc(GMEM_FIXED, ulSize);
        if (pbValue2 == NULL)
        {
            return FALSE;
        }
        err = RegGetKeySecurity(HKEY_CLASSES_ROOT,
                                OWNER_SECURITY_INFORMATION |
                                GROUP_SECURITY_INFORMATION |
                                DACL_SECURITY_INFORMATION,
                                pbValue2,
                                &ulSize);
    }

    // Now compare them.  If they differ then this AppId uses custom
    // configuration permissions

    if (err == ERROR_SUCCESS && g_util.CheckForValidSD((SECURITY_DESCRIPTOR *)pbValue))
    {
        if (!g_util.CompareSDs((PSrSecurityDescriptor) pbValue,
                               (PSrSecurityDescriptor) pbValue2))
        {
            err = g_virtreg.NewRegKeyACL(g_hAppid,
                                         rghkCLSID,
                                         cCLSIDs,
                                         g_szAppTitle,
                                         (SECURITY_DESCRIPTOR *) pbValue,
                                         (SECURITY_DESCRIPTOR *) pbValue,
                                         TRUE,
                                         &m_Page3.m_iConfigurationIndex);
            CDataPacket * pCdb = g_virtreg.GetAt(m_Page3.m_iConfigurationIndex);
            pCdb->SetModified(FALSE);
            m_Page3.m_iConfig = 1;
        }
    }
    GlobalFree(pbValue);
    GlobalFree(pbValue2);


    // Add all of the property pages here.  Note that
    // the order that they appear in here will be
    // the order they appear in on screen.  By default,
    // the first page of the set is the active one.
    // One way to make a different property page the
    // active one is to call SetActivePage().

    AddPage(&m_Page1);
    if (m_Page1.m_iServerType != SURROGATE)
    {
        AddPage(&m_Page2);
    }
    if (m_Page2.m_fCanBeLocal || m_Page1.m_fSurrogate)
    {
        AddPage(&m_Page3);
        AddPage(&m_Page4);
    }
    // add the property page for endpoint options
    AddPage(&m_Page5);
    m_Page5.InitData(szAppName, hkAppID);


    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CClsidPropertySheet message handlers


BOOL CClsidPropertySheet::OnNcCreate(LPCREATESTRUCT lpCreateStruct)
{
        if (!CPropertySheet::OnNcCreate(lpCreateStruct))
                return FALSE;

        ModifyStyleEx(0, WS_EX_CONTEXTHELP);

        return TRUE;
}

BOOL CClsidPropertySheet::ValidateAndUpdate(void)
{
    // Call update data on all initialized pages
    // to make sure that their private member variables are correct.
    long lErr;

    BOOL fReturn = UpdateData(TRUE);
    if (fReturn && m_Page1.m_hWnd)
        fReturn = m_Page1.ValidateChanges();
    if (fReturn && m_Page2.m_hWnd)
        fReturn = m_Page2.ValidateChanges();
    if (fReturn && m_Page3.m_hWnd)
        fReturn = m_Page3.ValidateChanges();
    if (fReturn && m_Page4.m_hWnd)
        fReturn = m_Page4.ValidateChanges();

    if (fReturn && m_Page5.m_hWnd)
        fReturn = m_Page5.ValidateChanges();

    if (!fReturn)
        return FALSE;

    m_Page1.UpdateChanges(m_hkAppID);
    m_Page2.UpdateChanges(m_hkAppID);
    m_Page3.UpdateChanges(m_hkAppID);
    m_Page4.UpdateChanges(m_hkAppID);
    m_Page5.UpdateChanges(m_hkAppID);

    ////////////////////////////////////////////////////////////////////
    // Persist cross page data

    if (m_Page4.m_fService)
    {
        if (m_Page2.m_fLocal)
        {
            BOOL fOk;

            // Write the LocalService value to the registry
            lErr = RegSetValueEx(
                    m_hkAppID,
                    TEXT("LocalService"),
                    0,
                    REG_SZ,
                    (BYTE *)(LPCTSTR)m_Page1.m_szServerPath,
                    (1 + m_Page1.m_szServerPath.GetLength()) * sizeof (TCHAR));
            lErr = RegDeleteValue(
                    m_hkAppID,
                    TEXT("_LocalService"));

            // Persist information to the service manager database
            if (m_Page4.m_iIdentity == 3)
            {
                fOk = g_util.ChangeService((LPCTSTR) m_Page1.m_szServerPath,
                                           TEXT("LocalSystem"),
                                           TEXT(""),
                                           (LPCTSTR) m_Page1.m_szServerName);
            }
            else
            {
                fOk = g_util.ChangeService((LPCTSTR) m_Page1.m_szServerPath,
                                           (LPCTSTR) m_Page4.m_szUserName,
                                           (LPCTSTR) m_Page4.m_szPassword,
                                           (LPCTSTR) m_Page1.m_szServerName);
            }
            if (!fOk)
            {
                return FALSE;
            }
        }
        else
        {
            lErr = RegSetValueEx(
                    m_hkAppID,
                    TEXT("_LocalService"),
                    0,
                    REG_SZ,
                    (BYTE *)(LPCTSTR)m_Page1.m_szServerPath,
                    (1 + m_Page1.m_szServerPath.GetLength()) * sizeof (TCHAR));
            lErr = RegDeleteValue(
                    m_hkAppID,
                    TEXT("LocalService"));
        }
    }

    return ChangeCLSIDInfo(m_Page2.m_fLocal);
}

BOOL CClsidPropertySheet::OnCommand(WPARAM wParam, LPARAM lParam)
{
        switch (LOWORD(wParam))
        {
        case IDOK:
        case ID_APPLY_NOW:
            if (!ValidateAndUpdate())
                return TRUE;
                break;
        }
        return CPropertySheet::OnCommand(wParam, lParam);
}

BOOL CClsidPropertySheet::LookAtCLSIDs(void)
{
    BOOL fFoundLocalServer = FALSE;
    TCHAR szBuffer[MAX_PATH];
    DWORD dwSize;
    HKEY hKey;
    long lErr;

    unsigned n = 0;
    while (n < m_cCLSIDs && !fFoundLocalServer)
    {
        lErr = RegOpenKeyEx(
                m_rghkCLSID[n],
                TEXT("LocalServer32"),
                0,
                KEY_ALL_ACCESS,
                &hKey);
        if (lErr == ERROR_SUCCESS)
        {
            dwSize = sizeof(szBuffer);
            lErr = RegQueryValueEx(
                    hKey,
                    TEXT(""),
                    NULL,
                    NULL,
                    (BYTE *)szBuffer,
                    &dwSize);
            if (lErr == ERROR_SUCCESS)
            {
                m_Page1.m_szServerPath = szBuffer;
                m_Page2.m_fLocal = TRUE;
                m_Page2.m_fCanBeLocal = TRUE;
                fFoundLocalServer = TRUE;
            }
            RegCloseKey(hKey);
        }


        if (!fFoundLocalServer)
        {
            lErr = RegOpenKeyEx(
                    m_rghkCLSID[n],
                    TEXT("LocalServer"),
                    0,
                    KEY_ALL_ACCESS,
                    &hKey);
            if (lErr == ERROR_SUCCESS)
            {
                dwSize = sizeof(szBuffer);
                lErr = RegQueryValueEx(
                        hKey,
                        TEXT(""),
                        NULL,
                        NULL,
                        (BYTE *)szBuffer,
                        &dwSize);
                if (lErr == ERROR_SUCCESS)
                {
                    m_Page1.m_szServerPath = szBuffer;
                    m_Page2.m_fLocal = TRUE;
                    m_Page2.m_fCanBeLocal = TRUE;
                    fFoundLocalServer = TRUE;
                }
                RegCloseKey(hKey);
            }
        }

        if (!fFoundLocalServer)
        {
            lErr = RegOpenKeyEx(
                    m_rghkCLSID[n],
                    TEXT("_LocalServer32"),
                    0,
                    KEY_ALL_ACCESS,
                    &hKey);
            if (lErr == ERROR_SUCCESS)
            {
                dwSize = sizeof(szBuffer);
                lErr = RegQueryValueEx(
                        hKey,
                        TEXT(""),
                        NULL,
                        NULL,
                        (BYTE *)szBuffer,
                        &dwSize);
                if (lErr == ERROR_SUCCESS)
                {
                    m_Page1.m_szServerPath = szBuffer;
                    m_Page2.m_fCanBeLocal = TRUE;
                    fFoundLocalServer = TRUE;
                }
                RegCloseKey(hKey);
            }
        }

        if (!fFoundLocalServer)
        {
            lErr = RegOpenKeyEx(
                    m_rghkCLSID[n],
                    TEXT("_LocalServer"),
                    0,
                    KEY_ALL_ACCESS,
                    &hKey);
            if (lErr == ERROR_SUCCESS)
            {
                dwSize = sizeof(szBuffer);
                lErr = RegQueryValueEx(
                        hKey,
                        TEXT(""),
                        NULL,
                        NULL,
                        (BYTE *)szBuffer,
                        &dwSize);
                if (lErr == ERROR_SUCCESS)
                {
                    m_Page1.m_szServerPath = szBuffer;
                    m_Page2.m_fCanBeLocal = TRUE;
                    fFoundLocalServer = TRUE;
                }
                RegCloseKey(hKey);
            }
        }

        n++;
    }
    return TRUE;
}




BOOL    CClsidPropertySheet::ChangeCLSIDInfo(BOOL fLocal)
{
    TCHAR szBuffer[MAX_PATH];
    CString szOld;
    CString szNew;
    CString szOld16;
    CString szNew16;
    DWORD dwSize;
    HKEY hKey;
    long lErr;

    if (fLocal)
    {
        szOld = TEXT("_LocalServer32");
        szNew = TEXT("LocalServer32");
        szOld16 = TEXT("_LocalServer");
        szNew16 = TEXT("LocalServer");
    }
    else
    {
        szOld = TEXT("LocalServer32");
        szNew = TEXT("_LocalServer32");
        szOld16 = TEXT("LocalServer");
        szNew16 = TEXT("_LocalServer");
    }

    unsigned n = 0;
    while (n < m_cCLSIDs)
    {
        // First do 32 servers
        lErr = RegOpenKeyEx(
                m_rghkCLSID[n],
                szOld,
                0,
                KEY_ALL_ACCESS,
                &hKey);
        if (lErr == ERROR_SUCCESS)
        {
            dwSize = sizeof(szBuffer);
            lErr = RegQueryValueEx(
                    hKey,
                    TEXT(""),
                    NULL,
                    NULL,
                    (BYTE *)szBuffer,
                    &dwSize);
            if (lErr == ERROR_SUCCESS)
            {
                HKEY hKeyNew;
                DWORD dwDisp;

                lErr = RegCreateKeyEx(
                        m_rghkCLSID[n],
                        szNew,
                        0,
                        NULL,
                        REG_OPTION_NON_VOLATILE,
                        KEY_ALL_ACCESS,
                        NULL,
                        &hKeyNew,
                        &dwDisp);
                if (lErr == ERROR_SUCCESS)
                {
                    lErr = RegSetValueEx(
                            hKeyNew,
                            TEXT(""),
                            NULL,
                            REG_SZ,
                            (BYTE *)szBuffer,
                            dwSize);
                    RegCloseKey(hKeyNew);
                }
            }
            RegCloseKey(hKey);
            lErr = RegDeleteKey(m_rghkCLSID[n], szOld);
        }


        // Then do 16 servers
        lErr = RegOpenKeyEx(
                m_rghkCLSID[n],
                szOld16,
                0,
                KEY_ALL_ACCESS,
                &hKey);
        if (lErr == ERROR_SUCCESS)
        {
            dwSize = sizeof(szBuffer);
            lErr = RegQueryValueEx(
                    hKey,
                    TEXT(""),
                    NULL,
                    NULL,
                    (BYTE *)szBuffer,
                    &dwSize);
            if (lErr == ERROR_SUCCESS)
            {
                HKEY hKeyNew;
                DWORD dwDisp;

                lErr = RegCreateKeyEx(
                        m_rghkCLSID[n],
                        szNew16,
                        0,
                        NULL,
                        REG_OPTION_NON_VOLATILE,
                        KEY_ALL_ACCESS,
                        NULL,
                        &hKeyNew,
                        &dwDisp);
                if (lErr == ERROR_SUCCESS)
                {
                    lErr = RegSetValueEx(
                            hKeyNew,
                            TEXT(""),
                            NULL,
                            REG_SZ,
                            (BYTE *)szBuffer,
                            dwSize);
                    RegCloseKey(hKeyNew);
                }
            }
            RegCloseKey(hKey);
            lErr = RegDeleteKey(m_rghkCLSID[n], szOld16);
        }

        n++;
    }
    return TRUE;
}