#include <Windows.h>
#include <strsafe.h>
#include <stdio.h>

#include "Json.h"


// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// Static Functions Forwarded Declarations

static VOID JsonSkipWhitespace(PJSON_PARSER pParser);
static PJSON_VALUE JsonParseValue(PJSON_PARSER pParser);
static PJSON_VALUE JsonParseString(PJSON_PARSER pParser);
static PJSON_VALUE JsonParseNumber(PJSON_PARSER pParser);
static PJSON_VALUE JsonParseObject(PJSON_PARSER pParser);
static PJSON_VALUE JsonParseArray(PJSON_PARSER pParser);
static SIZE_T JsonEscapeString(LPCSTR pszSrc, LPSTR pszDest, SIZE_T nDestSize);
static SIZE_T JsonStringifyValue(PJSON_VALUE pValue, LPSTR pszBuffer, SIZE_T nBufferSize);
static SIZE_T JsonStringifyObject(PJSON_VALUE pObject, LPSTR pszBuffer, SIZE_T nBufferSize);

// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==

static VOID JsonSkipWhitespace(PJSON_PARSER pParser)
{
    while (pParser->nPos < pParser->nLength) 
    {
        CHAR ch = pParser->pszJson[pParser->nPos];
        if (ch != ' ' && ch != '\t' && ch != '\n' && ch != '\r') break;
        pParser->nPos++;
    }
}

static PJSON_VALUE JsonParseValue(PJSON_PARSER pParser)
{
    JsonSkipWhitespace(pParser);
    
    if (pParser->nPos >= pParser->nLength)
        return NULL;
 
    PJSON_VALUE pValue = NULL;

    if (pParser->pszJson[pParser->nPos] == '"')
        return JsonParseString(pParser);
    else if (pParser->pszJson[pParser->nPos] == '{')
        return JsonParseObject(pParser);
    else if (pParser->pszJson[pParser->nPos] == '[')
        return JsonParseArray(pParser);
    else if ((pParser->pszJson[pParser->nPos] >= '0' && pParser->pszJson[pParser->nPos] <= '9') || pParser->pszJson[pParser->nPos] == '-')
        return JsonParseNumber(pParser);
    else if (pParser->nPos + 4 <= pParser->nLength && CompareStringA(LOCALE_INVARIANT, 0, &pParser->pszJson[pParser->nPos], 4, "null", 4) == CSTR_EQUAL) 
    {
        pParser->nPos += 4;
        pValue = (PJSON_VALUE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JSON_VALUE));
        if (pValue) pValue->type = JSON_TYPE_NULL;
        return pValue;
    }
    else if (pParser->nPos + 4 <= pParser->nLength && CompareStringA(LOCALE_INVARIANT, 0, &pParser->pszJson[pParser->nPos], 4, "true", 4) == CSTR_EQUAL) 
    {
        pParser->nPos += 4;
        pValue = (PJSON_VALUE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JSON_VALUE));
        if (pValue) 
        {
            pValue->type = JSON_TYPE_BOOLEAN;
            pValue->bValue = TRUE;
        }
        return pValue;
    }
    else if (pParser->nPos + 5 <= pParser->nLength && CompareStringA(LOCALE_INVARIANT, 0, &pParser->pszJson[pParser->nPos], 5, "false", 5) == CSTR_EQUAL) 
    {
        pParser->nPos += 5;
        pValue = (PJSON_VALUE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JSON_VALUE));
        if (pValue) 
        {
            pValue->type = JSON_TYPE_BOOLEAN;
            pValue->bValue = FALSE;
        }
        return pValue;
    }
    
    return NULL;
}

static PJSON_VALUE JsonParseString(PJSON_PARSER pParser)
{
    if (pParser->pszJson[pParser->nPos] != '"')
        return NULL;

    pParser->nPos++;

    LPSTR           pszString       = NULL;
    SIZE_T          nStart          = pParser->nPos,
                    nLength         = 0x00,
                    nSource         = 0x00,
                    nDestination    = 0x00;
    PJSON_VALUE     pValue          = NULL;
    BOOL            bEscaped        = FALSE;

    while (pParser->nPos < pParser->nLength) 
    {
        if (!bEscaped && pParser->pszJson[pParser->nPos] == '"')
            break;

        bEscaped = (!bEscaped && pParser->pszJson[pParser->nPos] == '\\');
        pParser->nPos++;
        nLength++;
    }
    
    if (pParser->pszJson[pParser->nPos] != '"')
        return NULL;
    
    if (!(pszString = (LPSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nLength + 1)))
    {
        printf("[!] HeapAlloc Failed With Error: %lu\n", GetLastError());
        return NULL;

    }

    bEscaped    = FALSE; 
    nSource     = nStart;

    while (nSource < nStart + nLength)
    {
        if (bEscaped) 
        {
            switch (pParser->pszJson[nSource])
            {
                case '"':  pszString[nDestination++] = '"';  break;
                case '\\': pszString[nDestination++] = '\\'; break;
                case '/':  pszString[nDestination++] = '/';  break;
                case 'b':  pszString[nDestination++] = '\b'; break;
                case 'f':  pszString[nDestination++] = '\f'; break;
                case 'n':  pszString[nDestination++] = '\n'; break;
                case 'r':  pszString[nDestination++] = '\r'; break;
                case 't':  pszString[nDestination++] = '\t'; break;
                case 'u':
                {
                    if (nSource + 5 <= nStart + nLength)
                    {
                        CHAR    szHex[5]        = { 0 };
                        DWORD   dwCodepoint     = 0x00;

                        StringCbCopyNA(szHex, sizeof(szHex), &pParser->pszJson[nSource + 1], 4);
                        dwCodepoint = strtoul(szHex, NULL, 16);
                        
                        /*
                        0x0000-0x007F           ASCII - maps directly to single byte
                        0x0080-0x00FF           Latin-1Supplement - fits in a byte but not ASCII
                        0x0100-0xFFFF           BMP (Basic Multilingual Plane) - needs multi-byte or wide char
                        0xD800-0xDBFF           High surrogate (paired with low surrogate for supplementary planes)
                        0xDC00-0xDFFF           Low surrogate
                        */

                        if (dwCodepoint < 128) {
                            pszString[nDestination++] = (CHAR)dwCodepoint;
                        }
                        else if (dwCodepoint < 2048) {
                            pszString[nDestination++] = (CHAR)(192 | (dwCodepoint >> 6));
                            pszString[nDestination++] = (CHAR)(128 | (dwCodepoint & 63));
                        }
                        else {
                            pszString[nDestination++] = (CHAR)(224 | (dwCodepoint >> 12));
                            pszString[nDestination++] = (CHAR)(128 | ((dwCodepoint >> 6) & 63));
                            pszString[nDestination++] = (CHAR)(128 | (dwCodepoint & 63));
                        }

                        // Skip the 4 hex digits
                        nSource += 4; 
                    }
                    break;
                }
                default:
                    pszString[nDestination++] = pParser->pszJson[nSource]; // Unknown escape, keep as-is
                    break;
            }
            bEscaped = FALSE;
        } 
        else if (pParser->pszJson[nSource] == '\\')
            bEscaped = TRUE;
        else 
            pszString[nDestination++] = pParser->pszJson[nSource];
        
        nSource++;
    }
    
    // Null-terminate
    pszString[nDestination] = '\0';
    // Skip closing quote
    pParser->nPos++; 
    
    if (!(pValue = (PJSON_VALUE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JSON_VALUE))))
    {
        printf("[!] HeapAlloc Failed With Error: %lu\n", GetLastError());
        HeapFree(GetProcessHeap(), 0, pszString);
        return NULL;
    }
    
    pValue->type        = JSON_TYPE_STRING;
    pValue->pszValue    = pszString;
    
    return pValue;
}

static PJSON_VALUE JsonParseNumber(PJSON_PARSER pParser)
{
    SIZE_T          nStart          = pParser->nPos,
                    nLength         = 0x00;
    PJSON_VALUE     pValue          = NULL;
    BOOL            bNegative       = FALSE;
    CHAR            szNumber[32]    = { 0 };

    if (pParser->pszJson[pParser->nPos] == '-')
    {
        bNegative = TRUE;
        pParser->nPos++;
    }

    while (pParser->nPos < pParser->nLength)
    {
        if (pParser->pszJson[pParser->nPos] < '0' || pParser->pszJson[pParser->nPos] > '9')
            break;

        pParser->nPos++;
    }

    nLength = pParser->nPos - nStart;

    if (nLength == 0 || (nLength == 1 && bNegative))
        return NULL;

    if (nLength >= sizeof(szNumber))
        return NULL;

    StringCbCopyNA(szNumber, sizeof(szNumber), &pParser->pszJson[nStart], nLength);

    if (!(pValue = (PJSON_VALUE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JSON_VALUE))))
    {
        printf("[!] HeapAlloc Failed With Error: %lu\n", GetLastError());
        return NULL;
    }

    pValue->type    = JSON_TYPE_NUMBER;
    pValue->llValue = _atoi64(szNumber);

    return pValue;
}

static PJSON_VALUE JsonParseObject(PJSON_PARSER pParser)
{
    if (pParser->pszJson[pParser->nPos] != '{')
        return NULL;

    pParser->nPos++; // Skip '{'

    PJSON_VALUE     pObject     = NULL,
                    pKey        = NULL,
                    pVal        = NULL;
    PJSON_PAIR      pPair       = NULL;
    PJSON_PAIR*     ppNext      = NULL;

    if (!(pObject = (PJSON_VALUE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JSON_VALUE))))
    {
        printf("[!] HeapAlloc Failed With Error: %lu\n", GetLastError());
        return NULL;
    }

    pObject->type       = JSON_TYPE_OBJECT;
    pObject->pObject    = NULL;
    ppNext              = &pObject->pObject;

    while (pParser->nPos < pParser->nLength)
    {
        JsonSkipWhitespace(pParser);

        if (pParser->pszJson[pParser->nPos] == '}')
        {
            pParser->nPos++;
            break;
        }

        // Expect comma between pairs
        if (pObject->pObject != NULL)
        {
            if (pParser->pszJson[pParser->nPos] != ',')
                goto _END_OF_FUNC;

            pParser->nPos++;
            JsonSkipWhitespace(pParser);
        }

        // Parse key
        if (!(pKey = JsonParseString(pParser)) || pKey->type != JSON_TYPE_STRING)
            goto _END_OF_FUNC;

        JsonSkipWhitespace(pParser);

        // Expect colon
        if (pParser->pszJson[pParser->nPos] != ':')
            goto _END_OF_FUNC;

        pParser->nPos++;
        JsonSkipWhitespace(pParser);

        // Parse value
        if (!(pVal = JsonParseValue(pParser)))
            goto _END_OF_FUNC;

        // Create pair
        if (!(pPair = (PJSON_PAIR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JSON_PAIR))))
        {
            printf("[!] HeapAlloc Failed With Error: %lu\n", GetLastError());
            goto _END_OF_FUNC;
        }

        // Transfer key ownership
        pPair->pszKey   = pKey->pszValue;
        pPair->pValue   = pVal;
        pPair->pNext    = NULL;

        pKey->pszValue  = NULL;
        
        JsonFree(pKey);
        
        pKey            = NULL;
        pVal            = NULL;
        *ppNext         = pPair;
        ppNext          = &pPair->pNext;
        pPair           = NULL;
    }

    return pObject;

_END_OF_FUNC:
    if (pKey)
        JsonFree(pKey);
    if (pVal)
        JsonFree(pVal);
    if (pObject)
        JsonFree(pObject);
    return NULL;
}

static PJSON_VALUE JsonParseArray(PJSON_PARSER pParser)
{
    if (pParser->pszJson[pParser->nPos] != '[')
        return NULL;

    pParser->nPos++; // Skip '['

    PJSON_VALUE     pArray      = NULL,
                    pElement    = NULL;
    PJSON_VALUE*    ppNext      = NULL;

    if (!(pArray = (PJSON_VALUE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JSON_VALUE))))
    {
        printf("[!] HeapAlloc Failed With Error: %lu\n", GetLastError());
        return NULL;
    }

    pArray->type    = JSON_TYPE_ARRAY;
    pArray->pArray  = NULL;
    ppNext          = &pArray->pArray;

    while (pParser->nPos < pParser->nLength)
    {
        JsonSkipWhitespace(pParser);

        if (pParser->pszJson[pParser->nPos] == ']')
        {
            pParser->nPos++;
            break;
        }

        // Expect comma between elements
        if (pArray->pArray != NULL)
        {
            if (pParser->pszJson[pParser->nPos] != ',')
                goto _END_OF_FUNC;

            pParser->nPos++;
            JsonSkipWhitespace(pParser);
        }

        // Parse element
        if (!(pElement = JsonParseValue(pParser)))
            goto _END_OF_FUNC;

        *ppNext     = pElement;
        ppNext      = &pElement->pNext;
        pElement    = NULL;
    }

    return pArray;

_END_OF_FUNC:
    if (pArray)
        JsonFree(pArray);
    return NULL;
}

static SIZE_T JsonEscapeString(LPCSTR pszSource, LPSTR pszDest, SIZE_T nDestSize)
{
    if (!pszSource) return 0;

    SIZE_T  nWritten    = 0x00,
            nEscLength  = 0x00;
    LPCSTR  pszEscape   = NULL;
    CHAR    szEscape[7] = { 0 };

    while (*pszSource)
    {
        pszEscape   = NULL;
        nEscLength  = 0x00;

        switch (*pszSource)
        {
            case '\"':
                pszEscape   = "\\\"";
                nEscLength  = 2;
                break;

            case '\\':
                pszEscape   = "\\\\";
                nEscLength  = 2;
                break;

            case '\b':
                pszEscape   = "\\b";
                nEscLength  = 2;
                break;

            case '\f':
                pszEscape   = "\\f";
                nEscLength  = 2;
                break;

            case '\n':
                pszEscape   = "\\n";
                nEscLength  = 2;
                break;

            case '\r':
                pszEscape   = "\\r";
                nEscLength  = 2;
                break;

            case '\t':
                pszEscape   = "\\t";
                nEscLength  = 2;
                break;

            default:
                if ((BYTE)*pszSource < 32)
                {
                    wsprintfA(szEscape, "\\u%04X", (UINT)(BYTE)*pszSource);
                    pszEscape   = szEscape;
                    nEscLength  = 6;
                }
                break;
        }

        if (pszEscape)
        {
            if (pszDest && nWritten + nEscLength <= nDestSize)
                StringCbCopyNA(pszDest + nWritten, nDestSize - nWritten, pszEscape, nEscLength);

            nWritten += nEscLength;
        }
        else
        {
            if (pszDest && nWritten < nDestSize)
                pszDest[nWritten] = *pszSource;

            nWritten++;
        }

        pszSource++;
    }

    return nWritten;
}

static SIZE_T JsonStringifyValue(PJSON_VALUE pValue, LPSTR pszBuffer, SIZE_T nBufferSize)
{
    SIZE_T  nWritten    = 0x00;
    SIZE_T  nLength     = 0x00;

    if (!pValue)
        return 0;

    switch (pValue->type)
    {
        case JSON_TYPE_NULL:
        {
            LPCSTR pszNull = "null";

            nLength = 4;

            if (pszBuffer && nLength <= nBufferSize)
                StringCbCopyNA(pszBuffer, nBufferSize, pszNull, nLength);

            return nLength;
        }

        case JSON_TYPE_BOOLEAN:
        {
            LPCSTR pszBool = pValue->bValue ? "true" : "false";

            nLength = pValue->bValue ? 4 : 5;

            if (pszBuffer && nLength <= nBufferSize)
                StringCbCopyNA(pszBuffer, nBufferSize, pszBool, nLength);

            return nLength;
        }

        case JSON_TYPE_NUMBER:
        {
            CHAR szNumber[32] = { 0 };

            nLength = (SIZE_T)wsprintfA(szNumber, "%I64d", pValue->llValue);

            if (pszBuffer && nLength <= nBufferSize)
                StringCbCopyNA(pszBuffer, nBufferSize, szNumber, nLength);

            return nLength;
        }

        case JSON_TYPE_STRING:
        {
            SIZE_T nEscaped = 0x00;

            if (pszBuffer && nWritten < nBufferSize)
                pszBuffer[nWritten] = '"';

            nWritten++;

            nEscaped = JsonEscapeString(pValue->pszValue, pszBuffer ? &pszBuffer[nWritten] : NULL, nBufferSize > nWritten ? nBufferSize - nWritten : 0);

            nWritten += nEscaped;

            if (pszBuffer && nWritten < nBufferSize)
                pszBuffer[nWritten] = '"';

            nWritten++;

            return nWritten;
        }

        case JSON_TYPE_OBJECT:
            return JsonStringifyObject(pValue, pszBuffer, nBufferSize);

        case JSON_TYPE_ARRAY:
        {
            PJSON_VALUE     pElement    = pValue->pArray;
            BOOL            bFirst      = TRUE;

            if (pszBuffer && nWritten < nBufferSize)
                pszBuffer[nWritten] = '[';

            nWritten++;

            while (pElement)
            {
                if (!bFirst)
                {
                    if (pszBuffer && nWritten < nBufferSize)
                        pszBuffer[nWritten] = ',';

                    nWritten++;
                }

                bFirst      =   FALSE;
                nLength     =   JsonStringifyValue(pElement, pszBuffer ? &pszBuffer[nWritten] : NULL, nBufferSize > nWritten ? nBufferSize - nWritten : 0);
                
                nWritten    +=  nLength;
                pElement    =   pElement->pNext;
            }

            if (pszBuffer && nWritten < nBufferSize)
                pszBuffer[nWritten] = ']';

            nWritten++;

            return nWritten;
        }
    }

    return 0;
}

static SIZE_T JsonStringifyObject(PJSON_VALUE pObject, LPSTR pszBuffer, SIZE_T nBufferSize)
{
    SIZE_T      nWritten    = 0x00,
                nLength     = 0x00;
    PJSON_PAIR  pPair       = pObject->pObject;
    BOOL        bFirst      = TRUE;

    if (pszBuffer && nWritten < nBufferSize)
        pszBuffer[nWritten] = '{';

    nWritten++;

    while (pPair)
    {
        if (!bFirst)
        {
            if (pszBuffer && nWritten < nBufferSize)
                pszBuffer[nWritten] = ',';

            nWritten++;
        }

        bFirst = FALSE;

        // Write key
        if (pszBuffer && nWritten < nBufferSize)
            pszBuffer[nWritten] = '"';

        nWritten++;

        nLength = lstrlenA(pPair->pszKey);

        if (pszBuffer && nWritten + nLength <= nBufferSize)
            StringCbCopyNA(&pszBuffer[nWritten], nBufferSize - nWritten, pPair->pszKey, nLength);

        nWritten += nLength;

        if (pszBuffer && nWritten < nBufferSize)
            pszBuffer[nWritten] = '"';

        nWritten++;

        if (pszBuffer && nWritten < nBufferSize)
            pszBuffer[nWritten] = ':';

        nWritten++;

        nLength     =   JsonStringifyValue(pPair->pValue, pszBuffer ? &pszBuffer[nWritten] : NULL, nBufferSize > nWritten ? nBufferSize - nWritten : 0);
        
        nWritten    +=  nLength;
        pPair       =   pPair->pNext;
    }

    if (pszBuffer && nWritten < nBufferSize)
        pszBuffer[nWritten] = '}';

    nWritten++;

    return nWritten;
}

// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==

PJSON_VALUE JsonParse(LPCSTR pszJson)
{
    JSON_PARSER Parser = { 0 };

    if (!pszJson)
        return NULL;

    Parser.pszJson  = pszJson;
    Parser.nLength  = lstrlenA(pszJson);
    Parser.nPos     = 0x00;

    return JsonParseValue(&Parser);
}

VOID JsonFree(PJSON_VALUE pValue)
{
    if (!pValue) return;

    PJSON_PAIR  pPair       = NULL, pNextPair       = NULL,
                pElement    = NULL, pNextElement    = NULL;

    switch (pValue->type)
    {
        case JSON_TYPE_STRING:
        {
            if (pValue->pszValue)
                HeapFree(GetProcessHeap(), 0, pValue->pszValue);

            break;
        }

        case JSON_TYPE_OBJECT:
        {
            pPair = pValue->pObject;

            while (pPair)
            {
                pNextPair = pPair->pNext;

                if (pPair->pszKey)
                    HeapFree(GetProcessHeap(), 0, pPair->pszKey);

                JsonFree(pPair->pValue);
                HeapFree(GetProcessHeap(), 0, pPair);

                pPair = pNextPair;
            }

            break;
        }

        case JSON_TYPE_ARRAY:
        {
            pElement = pValue->pArray;

            while (pElement)
            {
                pNextElement = pElement->pNext;

                JsonFree(pElement);

                pElement = pNextElement;
            }

            break;
        }
    }

    HeapFree(GetProcessHeap(), 0, pValue);
}

PJSON_VALUE JsonGetObject(PJSON_VALUE pObject, LPCSTR pszKey)
{
    PJSON_PAIR pPair = NULL;

    if (!pObject || pObject->type != JSON_TYPE_OBJECT || !pszKey)
        return NULL;

    pPair = pObject->pObject;

    while (pPair)
    {
        if (pPair->pszKey && lstrcmpA(pPair->pszKey, pszKey) == 0)
            return pPair->pValue;

        pPair = pPair->pNext;
    }

    return NULL;
}

LPSTR JsonGetString(PJSON_VALUE pValue)
{
    if (!pValue || pValue->type != JSON_TYPE_STRING)
        return NULL;

    return pValue->pszValue;
}

LONGLONG JsonGetNumber(PJSON_VALUE pValue)
{
    if (!pValue || pValue->type != JSON_TYPE_NUMBER)
        return 0;

    return pValue->llValue;
}

// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==

PJSON_VALUE JsonCreateObject()
{
    PJSON_VALUE pValue = NULL;

    if (!(pValue = (PJSON_VALUE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JSON_VALUE))))
    {
        printf("[!] HeapAlloc Failed With Error: %lu\n", GetLastError());
        return NULL;
    }

    pValue->type    = JSON_TYPE_OBJECT;
    pValue->pObject = NULL;

    return pValue;
}

PJSON_VALUE JsonCreateString(LPCSTR pszString)
{
    PJSON_VALUE pValue  = NULL;
    SIZE_T      nLength = 0x00;

    if (!pszString)
        return NULL;

    if (!(pValue = (PJSON_VALUE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JSON_VALUE))))
    {
        printf("[!] HeapAlloc Failed With Error: %lu\n", GetLastError());
        return NULL;
    }

    nLength = lstrlenA(pszString);

    if (!(pValue->pszValue = (LPSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nLength + 1)))
    {
        printf("[!] HeapAlloc Failed With Error: %lu\n", GetLastError());
        HeapFree(GetProcessHeap(), 0, pValue);
        return NULL;
    }

    StringCbCopyA(pValue->pszValue, nLength + 1, pszString);
    pValue->type = JSON_TYPE_STRING;

    return pValue;
}

PJSON_VALUE JsonCreateNumber(LONGLONG llValue)
{
    PJSON_VALUE pValue = NULL;

    if (!(pValue = (PJSON_VALUE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JSON_VALUE))))
    {
        printf("[!] HeapAlloc Failed With Error: %lu\n", GetLastError());
        return NULL;
    }

    pValue->type    = JSON_TYPE_NUMBER;
    pValue->llValue = llValue;

    return pValue;
}

PJSON_VALUE JsonCreateBoolean(BOOL bValue)
{
    PJSON_VALUE pValue = NULL;

    if (!(pValue = (PJSON_VALUE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JSON_VALUE))))
    {
        printf("[!] HeapAlloc Failed With Error: %lu\n", GetLastError());
        return NULL;
    }

    pValue->type   = JSON_TYPE_BOOLEAN;
    pValue->bValue = bValue ? TRUE : FALSE;

    return pValue;
}

PJSON_VALUE JsonCreateArray()
{
    PJSON_VALUE pValue = NULL;

    if (!(pValue = (PJSON_VALUE)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JSON_VALUE))))
    {
        printf("[!] HeapAlloc Failed With Error: %lu\n", GetLastError());
        return NULL;
    }

    pValue->type   = JSON_TYPE_ARRAY;
    pValue->pArray = NULL;

    return pValue;
}

VOID JsonArrayAdd(PJSON_VALUE pArray, PJSON_VALUE pValue)
{
    if (!pArray || pArray->type != JSON_TYPE_ARRAY || !pValue)
        return;

    PJSON_VALUE pCurrent = NULL;

    if (!pArray->pArray)
    {
        pArray->pArray = pValue;
    }
    else
    {
        pCurrent = pArray->pArray;

        while (pCurrent->pNext)
            pCurrent = pCurrent->pNext;

        pCurrent->pNext = pValue;
    }
}

VOID JsonObjectAdd(PJSON_VALUE pObject, LPCSTR pszKey, PJSON_VALUE pValue)
{
    if (!pObject || pObject->type != JSON_TYPE_OBJECT || !pszKey || !pValue)
        return;

    PJSON_PAIR  pPair       = NULL,
                pCurrent    = NULL;
    SIZE_T      nKeyLength  = 0x00;


    if (!(pPair = (PJSON_PAIR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(JSON_PAIR))))
    {
        printf("[!] HeapAlloc Failed With Error: %lu\n", GetLastError());
        return;
    }

    nKeyLength = lstrlenA(pszKey);

    if (!(pPair->pszKey = (LPSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nKeyLength + 1)))
    {
        printf("[!] HeapAlloc Failed With Error: %lu\n", GetLastError());
        HeapFree(GetProcessHeap(), 0, pPair);
        return;
    }

    StringCbCopyA(pPair->pszKey, nKeyLength + 1, pszKey);

    pPair->pValue   = pValue;
    pPair->pNext    = NULL;

    if (!pObject->pObject)
    {
        pObject->pObject = pPair;
    }
    else
    {
        pCurrent = pObject->pObject;

        while (pCurrent->pNext)
            pCurrent = pCurrent->pNext;

        pCurrent->pNext = pPair;
    }
}

LPSTR JsonStringify(PJSON_VALUE pValue, PDWORD pdwLength)
{
    if (!pValue) return NULL;

    SIZE_T  nSize   = 0x00;
    LPSTR   pszJson = NULL;

    // First pass: calculate size
    nSize = JsonStringifyValue(pValue, NULL, 0);

    if (!(pszJson = (LPSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nSize + 1)))
    {
        printf("[!] HeapAlloc Failed With Error: %lu\n", GetLastError());
        return NULL;
    }

    // Second pass: write data
    JsonStringifyValue(pValue, pszJson, nSize);

    if (pdwLength)
        *pdwLength = (DWORD)nSize;

    return pszJson;
}

// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
// ==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==-==
