// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#include "precomp.h"

void PrintUsage()
{
    wprintf(L"lnkd usage:\n");
    wprintf(L"\tlnkd <path\\to\\foo.lnk>\n");
}

HRESULT GetPropertyBoolValue(_In_ IPropertyStore* pPropStore, _In_ REFPROPERTYKEY refPropKey, _Out_ BOOL* pfValue)
{
    PROPVARIANT propvar;
    HRESULT hr = pPropStore->GetValue(refPropKey, &propvar);
    if (SUCCEEDED(hr))
    {
        hr = PropVariantToBoolean(propvar, pfValue);
    }

    return hr;
}

HRESULT GetPropertyByteValue(_In_ IPropertyStore* pPropStore, _In_ REFPROPERTYKEY refPropKey, _Out_ BYTE* pbValue)
{
    PROPVARIANT propvar;
    HRESULT hr = pPropStore->GetValue(refPropKey, &propvar);
    if (SUCCEEDED(hr))
    {
        SHORT sValue;
        hr = PropVariantToInt16(propvar, &sValue);
        if (SUCCEEDED(hr))
        {
            hr = (sValue >= 0 && sValue <= BYTE_MAX) ? S_OK : E_INVALIDARG;
            if (SUCCEEDED(hr))
            {
                *pbValue = (BYTE)sValue;
            }
        }
    }

    return hr;
}

void DumpV2Properties(_In_ IShellLink* pslConsole)
{
    IPropertyStore* pPropStoreLnk;
    HRESULT hr = pslConsole->QueryInterface(IID_PPV_ARGS(&pPropStoreLnk));
    if (SUCCEEDED(hr))
    {
        wprintf(L"V2 Properties:\n");
        BOOL fForceV2;
        hr = GetPropertyBoolValue(pPropStoreLnk, PKEY_Console_ForceV2, &fForceV2);
        if (SUCCEEDED(hr))
        {
            wprintf(L"\tPKEY_Console_ForceV2: %s\n", (fForceV2) ? L"true" : L"false");
        }
        else
        {
            wprintf(L"ERROR: Unable to retrieve value of PKEY_Console_ForceV2. (HRESULT: 0x%08x)\n", hr);
        }

        BOOL fWrapText;
        hr = GetPropertyBoolValue(pPropStoreLnk, PKEY_Console_WrapText, &fWrapText);
        if (SUCCEEDED(hr))
        {
            wprintf(L"\tPKEY_Console_WrapText: %s\n", (fWrapText) ? L"true" : L"false");
        }
        else
        {
            wprintf(L"ERROR: Unable to retrieve value of PKEY_Console_WrapText. (HRESULT: 0x%08x)\n", hr);
        }

        BOOL fFilterOnPaste;
        hr = GetPropertyBoolValue(pPropStoreLnk, PKEY_Console_FilterOnPaste, &fFilterOnPaste);
        if (SUCCEEDED(hr))
        {
            wprintf(L"\tPKEY_Console_FilterOnPaste: %s\n", (fFilterOnPaste) ? L"true" : L"false");
        }
        else
        {
            wprintf(L"ERROR: Unable to retrieve value of PKEY_Console_FilterOnPaste. (HRESULT: 0x%08x)\n", hr);
        }

        BOOL fCtrlKeyShortcutsDisabled;
        hr = GetPropertyBoolValue(pPropStoreLnk, PKEY_Console_CtrlKeyShortcutsDisabled, &fCtrlKeyShortcutsDisabled);
        if (SUCCEEDED(hr))
        {
            wprintf(L"\tPKEY_Console_CtrlKeyShortcutsDisabled: %s\n", (fCtrlKeyShortcutsDisabled) ? L"true" : L"false");
        }
        else
        {
            wprintf(L"ERROR: Unable to retrieve value of PKEY_Console_CtrlKeyShortcutsDisabled. (HRESULT: 0x%08x)\n", hr);
        }

        BOOL fLineSelection;
        hr = GetPropertyBoolValue(pPropStoreLnk, PKEY_Console_LineSelection, &fLineSelection);
        if (SUCCEEDED(hr))
        {
            wprintf(L"\tPKEY_Console_LineSelection: %s\n", (fLineSelection) ? L"true" : L"false");
        }
        else
        {
            wprintf(L"ERROR: Unable to retrieve value of PKEY_Console_LineSelection. (HRESULT: 0x%08x)\n", hr);
        }

        BYTE bWindowTransparency;
        hr = GetPropertyByteValue(pPropStoreLnk, PKEY_Console_WindowTransparency, &bWindowTransparency);
        if (SUCCEEDED(hr))
        {
            wprintf(L"\tPKEY_Console_WindowTransparency: %d\n", bWindowTransparency);
        }
        else
        {
            wprintf(L"ERROR: Unable to retrieve value of PKEY_Console_WindowTransparency. (HRESULT: 0x%08x)\n", hr);
        }

        pPropStoreLnk->Release();
    }
}

void DumpCoord(_In_ PCWSTR pszAttrName, const COORD coord)
{
    wprintf(L"\t%s: (%d, %d) (0x%x)\n",
            pszAttrName,
            coord.X,
            coord.Y,
            coord);
}

void DumpBool(_In_ PCWSTR pszAttrName, const BOOL fEnabled)
{
    wprintf(L"\t%s: %s\n", pszAttrName, fEnabled ? L"true" : L"false");
}

HRESULT DumpV1Properties(_In_ IShellLink* pslConsole)
{
    IShellLinkDataList* pConsoleLnkDataList;
    HRESULT hr = pslConsole->QueryInterface(IID_PPV_ARGS(&pConsoleLnkDataList));
    if (SUCCEEDED(hr))
    {
        NT_CONSOLE_PROPS* pNtConsoleProps = nullptr;
        hr = pConsoleLnkDataList->CopyDataBlock(NT_CONSOLE_PROPS_SIG, (void**)&pNtConsoleProps);
        if (SUCCEEDED(hr))
        {
            wprintf(L"V1 Properties:\n");
            wprintf(L"\twFillAttribute: %x\n", pNtConsoleProps->wFillAttribute);
            wprintf(L"\twPopupFillAttribute: %x\n", pNtConsoleProps->wPopupFillAttribute);
            DumpCoord(L"dwScreenBufferSize", pNtConsoleProps->dwScreenBufferSize);
            DumpCoord(L"dwWindowSize", pNtConsoleProps->dwWindowSize);
            DumpCoord(L"dwWindowOrigin", pNtConsoleProps->dwWindowOrigin);
            wprintf(L"\tnFont: %x\n", pNtConsoleProps->nFont);
            wprintf(L"\tnInputBufferSize: %x\n", pNtConsoleProps->nInputBufferSize);
            DumpCoord(L"dwWindowOrigin", pNtConsoleProps->dwWindowOrigin);
            DumpCoord(L"dwFontSize", pNtConsoleProps->dwFontSize);
            wprintf(L"\tuFontFamily: %d\n", pNtConsoleProps->uFontFamily);
            wprintf(L"\tuFontWeight: %d\n", pNtConsoleProps->uFontWeight);
            wprintf(L"\tFaceName: \"%s\"\n", pNtConsoleProps->FaceName);
            wprintf(L"\tuCursorSize: %d\n", pNtConsoleProps->uCursorSize);
            DumpBool(L"bFullScreen", pNtConsoleProps->bFullScreen);
            DumpBool(L"bQuickEdit", pNtConsoleProps->bQuickEdit);
            DumpBool(L"bInsertMode", pNtConsoleProps->bInsertMode);
            DumpBool(L"bAutoPosition", pNtConsoleProps->bAutoPosition);
            wprintf(L"\tuHistoryBufferSize: %d\n", pNtConsoleProps->uHistoryBufferSize);
            wprintf(L"\tuNumberOfHistoryBuffers: %d\n", pNtConsoleProps->uNumberOfHistoryBuffers);
            DumpBool(L"bHistoryNoDup", pNtConsoleProps->bHistoryNoDup);
            wprintf(L"\tColorTable:\n");
            for (int iCurrColor = 0; iCurrColor < 16; iCurrColor++)
            {
                wprintf(L"\t\t%d:\t(R:%d\tG:\t%d\tB:\t%d)\n",
                        iCurrColor,
                        GetRValue(pNtConsoleProps->ColorTable[iCurrColor]),
                        GetGValue(pNtConsoleProps->ColorTable[iCurrColor]),
                        GetBValue(pNtConsoleProps->ColorTable[iCurrColor]));
            }
            LocalFree(pNtConsoleProps);
        }

        if (SUCCEEDED(hr))
        {
            // now dump East Asian properties if we can
            NT_FE_CONSOLE_PROPS* pNtFEConsoleProps;
            if (SUCCEEDED(pConsoleLnkDataList->CopyDataBlock(NT_FE_CONSOLE_PROPS_SIG,
                                                             (void**)&pNtFEConsoleProps)))
            {
                wprintf(L"\tuCodePage: %d", pNtFEConsoleProps->uCodePage);
                LocalFree(pNtFEConsoleProps);
            }
            else
            {
                wprintf(L"\t.lnk doesn't contain an explicit codepage setting.\n");
            }
        }

        pConsoleLnkDataList->Release();
    }

    return hr;
}

HRESULT DumpProperties(_In_ PCWSTR pszLnkFile)
{
    IShellLink* pslConsole;
    HRESULT hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&pslConsole));
    if (SUCCEEDED(hr))
    {
        IPersistFile* pPersistFileLnk;
        hr = pslConsole->QueryInterface(IID_PPV_ARGS(&pPersistFileLnk));
        if (SUCCEEDED(hr))
        {
            hr = pPersistFileLnk->Load(pszLnkFile, 0 /*grfMode*/);
            if (SUCCEEDED(hr))
            {
                hr = DumpV1Properties(pslConsole);
                if (SUCCEEDED(hr))
                {
                    wprintf(L"\n");
                    DumpV2Properties(pslConsole);
                }
                else if (hr == E_FAIL)
                {
                    wprintf(L"ERROR: .lnk file does not contain console properties.\n");
                }
            }
            else
            {
                wprintf(L"ERROR: Failed to load from lnk file (HRESULT: 0x%08x)\n", hr);
            }

            pPersistFileLnk->Release();
        }

        pslConsole->Release();
    }

    return hr;
}

int __cdecl wmain(int argc, WCHAR* argv[])
{
    if (argc == 2)
    {
        PCWSTR pszLnkFile = argv[1];
        wprintf(L"%s: Dumping lnk details for \"%s\"\n\n", PathFindFileName(argv[0]), pszLnkFile);
        if (PathFileExists(pszLnkFile))
        {
            HRESULT hr = CoInitialize(NULL);
            if (SUCCEEDED(hr))
            {
                hr = DumpProperties(pszLnkFile);
                CoUninitialize();
            }
        }
        else
        {
            wprintf(L"ERROR: Unable to open file: \"%s\". File does not exist.\n", pszLnkFile);
            return 1;
        }
    }
    else
    {
        PrintUsage();
    }
    return 0;
}
