/*
 * COPYRIGHT:         See COPYING in the top level directory
 * PROJECT:           ReactOS system libraries
 * PURPOSE:           Unicode Conversion Routines
 * FILE:              lib/rtl/unicode.c
 * PROGRAMMER:        Alex Ionescu (alex@relsoft.net)
 *                    Emanuele Aliberti
 *                    Gunnar Dalsnes
 */

/* INCLUDES *****************************************************************/

#include <rtl.h>

#define NDEBUG
#include <debug.h>

#include <wine/unicode.h>

/* GLOBALS *******************************************************************/

extern BOOLEAN NlsMbCodePageTag;
extern BOOLEAN NlsMbOemCodePageTag;
extern PUSHORT NlsLeadByteInfo;
extern USHORT NlsOemDefaultChar;
extern USHORT NlsUnicodeDefaultChar;
extern PUSHORT NlsOemLeadByteInfo;
extern PWCHAR NlsOemToUnicodeTable;
extern PCHAR NlsUnicodeToOemTable;
extern PUSHORT NlsUnicodeToMbOemTable;


/* FUNCTIONS *****************************************************************/

NTSTATUS
NTAPI
RtlMultiAppendUnicodeStringBuffer(OUT PRTL_UNICODE_STRING_BUFFER StringBuffer,
                                  IN ULONG NumberOfAddends,
                                  IN PCUNICODE_STRING Addends)
{
    UNIMPLEMENTED;
    return STATUS_NOT_IMPLEMENTED;
}

/*
* @implemented
*/
WCHAR
NTAPI
RtlAnsiCharToUnicodeChar(IN OUT PUCHAR *AnsiChar)
{
    ULONG Size;
    NTSTATUS Status;
    WCHAR UnicodeChar = L' ';
    PAGED_CODE_RTL();

    if (NlsLeadByteInfo)
    {
        Size = (NlsLeadByteInfo[**AnsiChar] == 0) ? 1 : 2;
    }
    else
    {
        DPRINT("HACK::Shouldn't have happened! Consider fixing Usetup and registry entries it creates on install\n");
        Size = 1;
    }

    Status = RtlMultiByteToUnicodeN(&UnicodeChar,
                                    sizeof(WCHAR),
                                    NULL,
                                    (PCHAR)*AnsiChar,
                                    Size);

    if (!NT_SUCCESS(Status))
    {
        UnicodeChar = L' ';
    }

    *AnsiChar += Size;
    return UnicodeChar;
}

/*
 * @implemented
 *
 * NOTES
 *  This function always writes a terminating '\0'.
 *  If the dest buffer is too small a partial copy is NOT performed!
 */
NTSTATUS
NTAPI
RtlAnsiStringToUnicodeString(
    IN OUT PUNICODE_STRING UniDest,
    IN PANSI_STRING AnsiSource,
    IN BOOLEAN AllocateDestinationString)
{
    NTSTATUS Status;
    ULONG Length;
    ULONG Index;

    PAGED_CODE_RTL();

    if (NlsMbCodePageTag == FALSE)
    {
        Length = (AnsiSource->Length + 1) * sizeof(WCHAR);
    }
    else
    {
        Length = RtlxAnsiStringToUnicodeSize(AnsiSource);
    }
    if (Length > MAXUSHORT) return STATUS_INVALID_PARAMETER_2;
    UniDest->Length = (USHORT)Length - sizeof(WCHAR);

    if (AllocateDestinationString)
    {
        UniDest->Buffer = RtlpAllocateStringMemory(Length, TAG_USTR);
        UniDest->MaximumLength = (USHORT)Length;
        if (!UniDest->Buffer) return STATUS_NO_MEMORY;
    }
    else if (UniDest->Length >= UniDest->MaximumLength)
    {
        return STATUS_BUFFER_OVERFLOW;
    }

    /* UniDest->MaximumLength must be even due to sizeof(WCHAR) being 2 */
    ASSERT(!(UniDest->MaximumLength & 1) && UniDest->Length <= UniDest->MaximumLength);

    Status = RtlMultiByteToUnicodeN(UniDest->Buffer,
                                    UniDest->Length,
                                    &Index,
                                    AnsiSource->Buffer,
                                    AnsiSource->Length);

    if (!NT_SUCCESS(Status))
    {
        if (AllocateDestinationString)
        {
            RtlpFreeStringMemory(UniDest->Buffer, TAG_USTR);
            UniDest->Buffer = NULL;
        }

        return Status;
    }

    UniDest->Buffer[Index / sizeof(WCHAR)] = UNICODE_NULL;
    return Status;
}

/*
 * @implemented
 *
 * RETURNS
 *  The calculated size in bytes including nullterm.
 */
ULONG
NTAPI
RtlxAnsiStringToUnicodeSize(IN PCANSI_STRING AnsiString)
{
    ULONG Size;
    PAGED_CODE_RTL();

    /* Convert from Mb String to Unicode Size */
    RtlMultiByteToUnicodeSize(&Size,
                              AnsiString->Buffer,
                              AnsiString->Length);

    /* Return the size plus the null-char */
    return(Size + sizeof(WCHAR));
}

/*
 * @implemented
 *
 * NOTES
 *  If src->length is zero dest is unchanged.
 *  Dest is never nullterminated.
 */
NTSTATUS
NTAPI
RtlAppendStringToString(IN PSTRING Destination,
                        IN const STRING *Source)
{
    USHORT SourceLength = Source->Length;

    if (SourceLength)
    {
        if (Destination->Length + SourceLength > Destination->MaximumLength)
        {
            return STATUS_BUFFER_TOO_SMALL;
        }

        RtlMoveMemory(&Destination->Buffer[Destination->Length],
                      Source->Buffer,
                      SourceLength);

        Destination->Length += SourceLength;
    }

    return STATUS_SUCCESS;
}

/*
 * @implemented
 *
 * NOTES
 *  If src->length is zero dest is unchanged.
 *  Dest is nullterminated when the MaximumLength allowes it.
 *  When dest fits exactly in MaximumLength characters the nullterm is ommitted.
 */
NTSTATUS
NTAPI
RtlAppendUnicodeStringToString(
    IN OUT PUNICODE_STRING Destination,
    IN PCUNICODE_STRING Source)
{
    USHORT SourceLength = Source->Length;
    PWCHAR Buffer = &Destination->Buffer[Destination->Length / sizeof(WCHAR)];

    if (SourceLength)
    {
        if ((SourceLength + Destination->Length) > Destination->MaximumLength)
        {
            return STATUS_BUFFER_TOO_SMALL;
        }

        RtlMoveMemory(Buffer, Source->Buffer, SourceLength);
        Destination->Length += SourceLength;

        /* append terminating '\0' if enough space */
        if (Destination->MaximumLength > Destination->Length)
        {
            Buffer[SourceLength / sizeof(WCHAR)] = UNICODE_NULL;
        }
    }

    return STATUS_SUCCESS;
}

/**************************************************************************
 *      RtlCharToInteger   (NTDLL.@)
 * @implemented
 * Converts a character string into its integer equivalent.
 *
 * RETURNS
 *  Success: STATUS_SUCCESS. value contains the converted number
 *  Failure: STATUS_INVALID_PARAMETER, if base is not 0, 2, 8, 10 or 16.
 *           STATUS_ACCESS_VIOLATION, if value is NULL.
 *
 * NOTES
 *  For base 0 it uses 10 as base and the string should be in the format
 *      "{whitespace} [+|-] [0[x|o|b]] {digits}".
 *  For other bases the string should be in the format
 *      "{whitespace} [+|-] {digits}".
 *  No check is made for value overflow, only the lower 32 bits are assigned.
 *  If str is NULL it crashes, as the native function does.
 *
 * DIFFERENCES
 *  This function does not read garbage behind '\0' as the native version does.
 */
NTSTATUS
NTAPI
RtlCharToInteger(
    PCSZ str,      /* [I] '\0' terminated single-byte string containing a number */
    ULONG base,    /* [I] Number base for conversion (allowed 0, 2, 8, 10 or 16) */
    PULONG value)  /* [O] Destination for the converted value */
{
    CHAR chCurrent;
    int digit;
    ULONG RunningTotal = 0;
    char bMinus = 0;

    /* skip leading whitespaces */
    while (*str != '\0' && *str <= ' ') str++;

    /* Check for +/- */
    if (*str == '+')
    {
        str++;
    }
    else if (*str == '-')
    {
        bMinus = 1;
        str++;
    }

    /* base = 0 means autobase */
    if (base == 0)
    {
        base = 10;

        if (str[0] == '0')
        {
            if (str[1] == 'b')
            {
                str += 2;
                base = 2;
            }
            else if (str[1] == 'o')
            {
                str += 2;
                base = 8;
            }
            else if (str[1] == 'x')
            {
                str += 2;
                base = 16;
            }
        }
    }
    else if (base != 2 && base != 8 && base != 10 && base != 16)
    {
        return STATUS_INVALID_PARAMETER;
    }

    if (value == NULL) return STATUS_ACCESS_VIOLATION;

    while (*str != '\0')
    {
        chCurrent = *str;

        if (chCurrent >= '0' && chCurrent <= '9')
        {
            digit = chCurrent - '0';
        }
        else if (chCurrent >= 'A' && chCurrent <= 'Z')
        {
            digit = chCurrent - 'A' + 10;
        }
        else if (chCurrent >= 'a' && chCurrent <= 'z')
        {
            digit = chCurrent - 'a' + 10;
        }
        else
        {
            digit = -1;
        }

        if (digit < 0 || digit >= (int)base) break;

        RunningTotal = RunningTotal * base + digit;
        str++;
    }

    *value = bMinus ? (0 - RunningTotal) : RunningTotal;
    return STATUS_SUCCESS;
}

/*
 * @implemented
 */
LONG
NTAPI
RtlCompareString(
    IN const STRING *s1,
    IN const STRING *s2,
    IN BOOLEAN CaseInsensitive)
{
    unsigned int len;
    LONG ret = 0;
    LPCSTR p1, p2;

    len = min(s1->Length, s2->Length);
    p1 = s1->Buffer;
    p2 = s2->Buffer;

    if (CaseInsensitive)
    {
        while (!ret && len--)
            ret = RtlUpperChar(*p1++) - RtlUpperChar(*p2++);
    }
    else
    {
        while (!ret && len--) ret = *p1++ - *p2++;
    }

    if (!ret) ret = s1->Length - s2->Length;

    return ret;
}

/*
 * @implemented
 *
 * RETURNS
 *  TRUE if strings are equal.
 */
BOOLEAN
NTAPI
RtlEqualString(
    IN const STRING *s1,
    IN const STRING *s2,
    IN BOOLEAN CaseInsensitive)
{
    if (s1->Length != s2->Length) return FALSE;
    return !RtlCompareString(s1, s2, CaseInsensitive);
}

/*
 * @implemented
 *
 * RETURNS
 *  TRUE if strings are equal.
 */
BOOLEAN
NTAPI
RtlEqualUnicodeString(
    IN CONST UNICODE_STRING *s1,
    IN CONST UNICODE_STRING *s2,
    IN BOOLEAN  CaseInsensitive)
{
    if (s1->Length != s2->Length) return FALSE;
    return !RtlCompareUnicodeString(s1, s2, CaseInsensitive );
}

/*
 * @implemented
 */
VOID
NTAPI
RtlFreeAnsiString(IN PANSI_STRING AnsiString)
{
    PAGED_CODE_RTL();

    if (AnsiString->Buffer)
    {
        RtlpFreeStringMemory(AnsiString->Buffer, TAG_ASTR);
        RtlZeroMemory(AnsiString, sizeof(ANSI_STRING));
    }
}

/*
 * @implemented
 */
VOID
NTAPI
RtlFreeOemString(IN POEM_STRING OemString)
{
    PAGED_CODE_RTL();

    if (OemString->Buffer) RtlpFreeStringMemory(OemString->Buffer, TAG_OSTR);
}

/*
 * @implemented
 */
VOID
NTAPI
RtlFreeUnicodeString(IN PUNICODE_STRING UnicodeString)
{
    PAGED_CODE_RTL();

    if (UnicodeString->Buffer)
    {
        RtlpFreeStringMemory(UnicodeString->Buffer, TAG_USTR);
        RtlZeroMemory(UnicodeString, sizeof(UNICODE_STRING));
    }
}


/*
 * @implemented
 *
 * NOTES
 *  Check the OEM string to match the Unicode string.
 *
 *  Functions which convert Unicode strings to OEM strings will set a
 *  DefaultChar from the OEM codepage when the characters are unknown.
 *  So check it against the Unicode string and return false when the
 *  Unicode string does not contain a TransDefaultChar.
 */
BOOLEAN
NTAPI
RtlpDidUnicodeToOemWork(IN PCUNICODE_STRING UnicodeString,
                        IN POEM_STRING OemString)
{
    ULONG i = 0;

    if (NlsMbOemCodePageTag == FALSE)
    {
        /* single-byte code page */
        /* Go through all characters of a string */
        while (i < OemString->Length)
        {
            /* Check if it got translated into a default char,
             * but source char wasn't a default char equivalent
             */
            if ((OemString->Buffer[i] == NlsOemDefaultChar) &&
                (UnicodeString->Buffer[i] != NlsUnicodeDefaultChar))
            {
                /* Yes, it means unmappable characters were found */
                return FALSE;
            }

            /* Move to the next char */
            i++;
        }

        /* All chars were translated successfuly */
        return TRUE;
    }
    else
    {
        /* multibyte code page */

        /* FIXME */
        return TRUE;
    }
}

/*
* @implemented
*/
BOOLEAN
NTAPI
RtlIsValidOemCharacter(IN PWCHAR Char)
{
    WCHAR UnicodeChar;
    WCHAR OemChar;

    /* If multi-byte code page present */
    if (NlsMbOemCodePageTag)
    {
        USHORT Offset;

        OemChar = NlsUnicodeToMbOemTable[*Char];

        /* If character has Lead Byte */
        Offset = NlsOemLeadByteInfo[HIBYTE(OemChar)];
        if (Offset)
        {
            /* Use DBCS table */
            UnicodeChar = NlsOemLeadByteInfo[Offset + LOBYTE(OemChar)];
        }
        else
        {
            UnicodeChar = NlsOemToUnicodeTable[OemChar];
        }

        /* Upcase */
        UnicodeChar = RtlpUpcaseUnicodeChar(UnicodeChar);

        /* Receive OEM character from the table */
        OemChar = NlsUnicodeToMbOemTable[UnicodeChar];
    }
    else
    {
        /* Receive Unicode character from the table */
        UnicodeChar = RtlpUpcaseUnicodeChar(NlsOemToUnicodeTable[(UCHAR)NlsUnicodeToOemTable[*Char]]);

        /* Receive OEM character from the table */
        OemChar = NlsUnicodeToOemTable[UnicodeChar];
    }

    /* Not valid character, failed */
    if (OemChar == NlsOemDefaultChar)
    {
        DPRINT1("\\u%04x is not valid for OEM\n", *Char);
        return FALSE;
    }

    *Char = UnicodeChar;

    return TRUE;
}

/*
 * @implemented
 *
 * NOTES
 *  If source is NULL the length of source is assumed to be 0.
 */
VOID
NTAPI
RtlInitAnsiString(IN OUT PANSI_STRING DestinationString,
                  IN PCSZ SourceString)
{
    SIZE_T Size;

    if (SourceString)
    {
        Size = strlen(SourceString);
        if (Size > (MAXUSHORT - sizeof(CHAR))) Size = MAXUSHORT - sizeof(CHAR);
        DestinationString->Length = (USHORT)Size;
        DestinationString->MaximumLength = (USHORT)Size + sizeof(CHAR);
    }
    else
    {
        DestinationString->Length = 0;
        DestinationString->MaximumLength = 0;
    }

    DestinationString->Buffer = (PCHAR)SourceString;
}

NTSTATUS
NTAPI
RtlInitAnsiStringEx(IN OUT PANSI_STRING DestinationString,
                    IN PCSZ SourceString)
{
    SIZE_T Size;

    if (SourceString)
    {
        Size = strlen(SourceString);
        if (Size > (MAXUSHORT - sizeof(CHAR))) return STATUS_NAME_TOO_LONG;
        DestinationString->Length = (USHORT)Size;
        DestinationString->MaximumLength = (USHORT)Size + sizeof(CHAR);
    }
    else
    {
        DestinationString->Length = 0;
        DestinationString->MaximumLength = 0;
    }

    DestinationString->Buffer = (PCHAR)SourceString;
    return STATUS_SUCCESS;

}
/*
 * @implemented
 *
 * NOTES
 *  If source is NULL the length of source is assumed to be 0.
 */
VOID
NTAPI
RtlInitString(
    IN OUT PSTRING DestinationString,
    IN PCSZ SourceString)
{
    RtlInitAnsiString(DestinationString, SourceString);
}

/*
 * @implemented
 *
 * NOTES
 *  If source is NULL the length of source is assumed to be 0.
 */
VOID
NTAPI
RtlInitUnicodeString(
    IN OUT PUNICODE_STRING DestinationString,
    IN PCWSTR SourceString)
{
    SIZE_T Size;
    CONST SIZE_T MaxSize = (MAXUSHORT & ~1) - sizeof(UNICODE_NULL); // an even number

    if (SourceString)
    {
        Size = wcslen(SourceString) * sizeof(WCHAR);
        __analysis_assume(Size <= MaxSize);

        if (Size > MaxSize)
            Size = MaxSize;
        DestinationString->Length = (USHORT)Size;
        DestinationString->MaximumLength = (USHORT)Size + sizeof(UNICODE_NULL);
    }
    else
    {
        DestinationString->Length = 0;
        DestinationString->MaximumLength = 0;
    }

    DestinationString->Buffer = (PWCHAR)SourceString;
}

/*
 * @implemented
 */
NTSTATUS
NTAPI
RtlInitUnicodeStringEx(
    OUT PUNICODE_STRING DestinationString,
    IN PCWSTR SourceString)
{
    SIZE_T Size;
    CONST SIZE_T MaxSize = (MAXUSHORT & ~1) - sizeof(WCHAR); // an even number

    if (SourceString)
    {
        Size = wcslen(SourceString) * sizeof(WCHAR);
        if (Size > MaxSize) return STATUS_NAME_TOO_LONG;
        DestinationString->Length = (USHORT)Size;
        DestinationString->MaximumLength = (USHORT)Size + sizeof(WCHAR);
    }
    else
    {
        DestinationString->Length = 0;
        DestinationString->MaximumLength = 0;
    }

    DestinationString->Buffer = (PWCHAR)SourceString;
    return STATUS_SUCCESS;
}

/*
 * @implemented
 *
 * NOTES
 *  Writes at most length characters to the string str.
 *  Str is nullterminated when length allowes it.
 *  When str fits exactly in length characters the nullterm is ommitted.
 */
NTSTATUS NTAPI RtlIntegerToChar(
    ULONG value,   /* [I] Value to be converted */
    ULONG base,    /* [I] Number base for conversion (allowed 0, 2, 8, 10 or 16) */
    ULONG length,  /* [I] Length of the str buffer in bytes */
    PCHAR str)     /* [O] Destination for the converted value */
{
    CHAR buffer[33];
    PCHAR pos;
    CHAR digit;
    SIZE_T len;

    if (base == 0)
    {
        base = 10;
    }
    else if (base != 2 && base != 8 && base != 10 && base != 16)
    {
        return STATUS_INVALID_PARAMETER;
    }

    pos = &buffer[32];
    *pos = '\0';

    do
    {
        pos--;
        digit = (CHAR)(value % base);
        value = value / base;

        if (digit < 10)
        {
            *pos = '0' + digit;
        }
        else
        {
            *pos = 'A' + digit - 10;
        }
    }
    while (value != 0L);

    len = &buffer[32] - pos;

    if (len > length)
    {
        return STATUS_BUFFER_OVERFLOW;
    }
    else if (str == NULL)
    {
        return STATUS_ACCESS_VIOLATION;
    }
    else if (len == length)
    {
        RtlCopyMemory(str, pos, len);
    }
    else
    {
        RtlCopyMemory(str, pos, len + 1);
    }

    return STATUS_SUCCESS;
}

/*
 * @implemented
 */
NTSTATUS
NTAPI
RtlIntegerToUnicode(
    IN ULONG Value,
    IN ULONG Base  OPTIONAL,
    IN ULONG Length OPTIONAL,
    IN OUT LPWSTR String)
{
    ULONG Radix;
    WCHAR  temp[33];
    ULONG v = Value;
    ULONG i;
    PWCHAR tp;
    PWCHAR sp;

    Radix = Base;

    if (Radix == 0) Radix = 10;

    if ((Radix != 2) && (Radix != 8) &&
        (Radix != 10) && (Radix != 16))
    {
        return STATUS_INVALID_PARAMETER;
    }

    tp = temp;

    while (v || tp == temp)
    {
        i = v % Radix;
        v = v / Radix;

        if (i < 10) *tp = (WCHAR)(i + L'0');
        else *tp = (WCHAR)(i + L'a' - 10);

        tp++;
    }

    if ((ULONG)((ULONG_PTR)tp - (ULONG_PTR)temp) >= Length)
    {
        return STATUS_BUFFER_TOO_SMALL;
    }

    sp = String;

    while (tp > temp) *sp++ = *--tp;

    *sp = 0;

    return STATUS_SUCCESS;
}

/*
 * @implemented
 */
NTSTATUS
NTAPI
RtlIntegerToUnicodeString(
    IN ULONG Value,
    IN ULONG Base OPTIONAL,
    IN OUT PUNICODE_STRING String)
{
    ANSI_STRING AnsiString;
    CHAR Buffer[33];
    NTSTATUS Status;

    Status = RtlIntegerToChar(Value, Base, sizeof(Buffer), Buffer);
    if (NT_SUCCESS(Status))
    {
        AnsiString.Buffer = Buffer;
        AnsiString.Length = (USHORT)strlen(Buffer);
        AnsiString.MaximumLength = sizeof(Buffer);

        Status = RtlAnsiStringToUnicodeString(String, &AnsiString, FALSE);
    }

    return Status;
}

/*
 * @implemented
 */
NTSTATUS
NTAPI
RtlInt64ToUnicodeString (
    IN ULONGLONG Value,
    IN ULONG Base OPTIONAL,
    IN OUT PUNICODE_STRING String)
{
    LARGE_INTEGER LargeInt;
    ANSI_STRING AnsiString;
    CHAR Buffer[65];
    NTSTATUS Status;

    LargeInt.QuadPart = Value;

    Status = RtlLargeIntegerToChar(&LargeInt, Base, sizeof(Buffer), Buffer);
    if (NT_SUCCESS(Status))
    {
        AnsiString.Buffer = Buffer;
        AnsiString.Length = (USHORT)strlen(Buffer);
        AnsiString.MaximumLength = sizeof(Buffer);

        Status = RtlAnsiStringToUnicodeString(String, &AnsiString, FALSE);
    }

    return Status;
}

/*
 * @implemented
 *
 * RETURNS
 *  TRUE if String2 contains String1 as a prefix.
 */
BOOLEAN
NTAPI
RtlPrefixString(
    const STRING *String1,
    const STRING *String2,
    BOOLEAN CaseInsensitive)
{
    PCHAR pc1;
    PCHAR pc2;
    ULONG NumChars;

    if (String2->Length < String1->Length)
        return FALSE;

    NumChars = String1->Length;
    pc1 = String1->Buffer;
    pc2 = String2->Buffer;

    if (pc1 && pc2)
    {
        if (CaseInsensitive)
        {
            while (NumChars--)
            {
                if (RtlUpperChar(*pc1++) != RtlUpperChar(*pc2++))
                    return FALSE;
            }
        }
        else
        {
            while (NumChars--)
            {
                if (*pc1++ != *pc2++)
                    return FALSE;
            }
        }

        return TRUE;
    }

    return FALSE;
}

/*
 * @implemented
 *
 * RETURNS
 *  TRUE if String2 contains String1 as a prefix.
 */
BOOLEAN
NTAPI
RtlPrefixUnicodeString(
    PCUNICODE_STRING String1,
    PCUNICODE_STRING String2,
    BOOLEAN CaseInsensitive)
{
    PWCHAR pc1;
    PWCHAR pc2;
    ULONG  NumChars;

    if (String2->Length < String1->Length)
        return FALSE;

    NumChars = String1->Length / sizeof(WCHAR);
    pc1 = String1->Buffer;
    pc2 = String2->Buffer;

    if (pc1 && pc2)
    {
        if (CaseInsensitive)
        {
            while (NumChars--)
            {
                if (RtlpUpcaseUnicodeChar(*pc1++) !=
                    RtlpUpcaseUnicodeChar(*pc2++))
                    return FALSE;
            }
        }
        else
        {
            while (NumChars--)
            {
                if (*pc1++ != *pc2++)
                    return FALSE;
            }
        }

        return TRUE;
    }

    return FALSE;
}

/*
 * @implemented
 */
NTSTATUS
NTAPI
RtlUnicodeStringToInteger(
    const UNICODE_STRING *str, /* [I] Unicode string to be converted */
    ULONG base,                /* [I] Number base for conversion (allowed 0, 2, 8, 10 or 16) */
    ULONG *value)              /* [O] Destination for the converted value */
{
    LPWSTR lpwstr = str->Buffer;
    USHORT CharsRemaining = str->Length / sizeof(WCHAR);
    WCHAR wchCurrent;
    int digit;
    ULONG RunningTotal = 0;
    char bMinus = 0;

    while (CharsRemaining >= 1 && *lpwstr <= ' ')
    {
        lpwstr++;
        CharsRemaining--;
    }

    if (CharsRemaining >= 1)
    {
        if (*lpwstr == '+')
        {
            lpwstr++;
            CharsRemaining--;
        }
        else if (*lpwstr == '-')
        {
            bMinus = 1;
            lpwstr++;
            CharsRemaining--;
        }
    }

    if (base == 0)
    {
        base = 10;

        if (CharsRemaining >= 2 && lpwstr[0] == '0')
        {
            if (lpwstr[1] == 'b')
            {
                lpwstr += 2;
                CharsRemaining -= 2;
                base = 2;
            }
            else if (lpwstr[1] == 'o')
            {
                lpwstr += 2;
                CharsRemaining -= 2;
                base = 8;
            }
            else if (lpwstr[1] == 'x')
            {
                lpwstr += 2;
                CharsRemaining -= 2;
                base = 16;
            }
        }
    }
    else if (base != 2 && base != 8 && base != 10 && base != 16)
    {
        return STATUS_INVALID_PARAMETER;
    }

    if (value == NULL)
    {
        return STATUS_ACCESS_VIOLATION;
    }

    while (CharsRemaining >= 1)
    {
        wchCurrent = *lpwstr;

        if (wchCurrent >= '0' && wchCurrent <= '9')
        {
            digit = wchCurrent - '0';
        }
        else if (wchCurrent >= 'A' && wchCurrent <= 'Z')
        {
            digit = wchCurrent - 'A' + 10;
        }
        else if (wchCurrent >= 'a' && wchCurrent <= 'z')
        {
            digit = wchCurrent - 'a' + 10;
        }
        else
        {
            digit = -1;
        }

        if (digit < 0 || (ULONG)digit >= base) break;

        RunningTotal = RunningTotal * base + digit;
        lpwstr++;
        CharsRemaining--;
    }

    *value = bMinus ? (0 - RunningTotal) : RunningTotal;
    return STATUS_SUCCESS;
}

/*
 * @implemented
 *
 * RETURNS
 *  Bytes necessary for the conversion including nullterm.
 */
ULONG
NTAPI
RtlxUnicodeStringToOemSize(IN PCUNICODE_STRING UnicodeString)
{
    ULONG Size;

    /* Convert the Unicode String to Mb Size */
    RtlUnicodeToMultiByteSize(&Size,
                              UnicodeString->Buffer,
                              UnicodeString->Length);

    /* Return the size + the null char */
    return (Size + sizeof(CHAR));
}

/*
 * @implemented
 *
 * NOTES
 *  This function always writes a terminating '\0'.
 *  It performs a partial copy if ansi is too small.
 */
NTSTATUS
NTAPI
RtlUnicodeStringToAnsiString(
    IN OUT PANSI_STRING AnsiDest,
    IN PCUNICODE_STRING UniSource,
    IN BOOLEAN AllocateDestinationString)
{
    NTSTATUS Status = STATUS_SUCCESS;
    NTSTATUS RealStatus;
    ULONG Length;
    ULONG Index;

    PAGED_CODE_RTL();

    ASSERT(!(UniSource->Length & 1));

    if (NlsMbCodePageTag == FALSE)
    {
        Length = (UniSource->Length + sizeof(WCHAR)) / sizeof(WCHAR);
    }
    else
    {
        Length = RtlxUnicodeStringToAnsiSize(UniSource);
    }

    if (Length > MAXUSHORT) return STATUS_INVALID_PARAMETER_2;

    AnsiDest->Length = (USHORT)Length - sizeof(CHAR);

    if (AllocateDestinationString)
    {
        AnsiDest->Buffer = RtlpAllocateStringMemory(Length, TAG_ASTR);
        AnsiDest->MaximumLength = (USHORT)Length;

        if (!AnsiDest->Buffer) return STATUS_NO_MEMORY;
    }
    else if (AnsiDest->Length >= AnsiDest->MaximumLength)
    {
        if (!AnsiDest->MaximumLength) return STATUS_BUFFER_OVERFLOW;

        Status = STATUS_BUFFER_OVERFLOW;
        AnsiDest->Length = AnsiDest->MaximumLength - 1;
    }

    RealStatus = RtlUnicodeToMultiByteN(AnsiDest->Buffer,
                                        AnsiDest->Length,
                                        &Index,
                                        UniSource->Buffer,
                                        UniSource->Length);

    if (!NT_SUCCESS(RealStatus) && AllocateDestinationString)
    {
        RtlpFreeStringMemory(AnsiDest->Buffer, TAG_ASTR);
        return RealStatus;
    }

    AnsiDest->Buffer[Index] = ANSI_NULL;
    return Status;
}

/*
 * @implemented
 *
 * NOTES
 *  This function always writes a terminating '\0'.
 *  Does NOT perform a partial copy if unicode is too small!
 */
NTSTATUS
NTAPI
RtlOemStringToUnicodeString(
    IN OUT PUNICODE_STRING UniDest,
    IN PCOEM_STRING OemSource,
    IN BOOLEAN AllocateDestinationString)
{
    NTSTATUS Status;
    ULONG Length;
    ULONG Index;

    PAGED_CODE_RTL();

    Length = RtlOemStringToUnicodeSize(OemSource);

    if (Length > MAXUSHORT) return STATUS_INVALID_PARAMETER_2;

    UniDest->Length = (USHORT)Length - sizeof(WCHAR);

    if (AllocateDestinationString)
    {
        UniDest->Buffer = RtlpAllocateStringMemory(Length, TAG_USTR);
        UniDest->MaximumLength = (USHORT)Length;

        if (!UniDest->Buffer) return STATUS_NO_MEMORY;
    }
    else if (UniDest->Length >= UniDest->MaximumLength)
    {
        return STATUS_BUFFER_OVERFLOW;
    }

    Status = RtlOemToUnicodeN(UniDest->Buffer,
                              UniDest->Length,
                              &Index,
                              OemSource->Buffer,
                              OemSource->Length);

    if (!NT_SUCCESS(Status) && AllocateDestinationString)
    {
        RtlpFreeStringMemory(UniDest->Buffer, TAG_USTR);
        UniDest->Buffer = NULL;
        return Status;
    }

    UniDest->Buffer[Index / sizeof(WCHAR)] = UNICODE_NULL;
    return Status;
}

/*
 * @implemented
 *
 * NOTES
 *   This function always '\0' terminates the string returned.
 */
NTSTATUS
NTAPI
RtlUnicodeStringToOemString(
    IN OUT POEM_STRING OemDest,
    IN PCUNICODE_STRING UniSource,
    IN BOOLEAN  AllocateDestinationString)
{
    NTSTATUS Status;
    ULONG Length;
    ULONG Index;

    PAGED_CODE_RTL();

    Length = RtlUnicodeStringToOemSize(UniSource);

    if (Length > MAXUSHORT) return STATUS_INVALID_PARAMETER_2;

    OemDest->Length = (USHORT)Length - sizeof(CHAR);

    if (AllocateDestinationString)
    {
        OemDest->Buffer = RtlpAllocateStringMemory(Length, TAG_OSTR);
        OemDest->MaximumLength = (USHORT)Length;

        if (!OemDest->Buffer) return STATUS_NO_MEMORY;
    }
    else if (OemDest->Length >= OemDest->MaximumLength)
    {
        return STATUS_BUFFER_OVERFLOW;
    }

    Status = RtlUnicodeToOemN(OemDest->Buffer,
                              OemDest->Length,
                              &Index,
                              UniSource->Buffer,
                              UniSource->Length);

    if (!NT_SUCCESS(Status) && AllocateDestinationString)
    {
        RtlpFreeStringMemory(OemDest->Buffer, TAG_OSTR);
        OemDest->Buffer = NULL;
        return Status;
    }

    OemDest->Buffer[Index] = ANSI_NULL;
    return Status;
}

#define ITU_IMPLEMENTED_TESTS (IS_TEXT_UNICODE_ODD_LENGTH|IS_TEXT_UNICODE_SIGNATURE)

/*
 * @implemented
 *
 * RETURNS
 *  The length of the string if all tests were passed, 0 otherwise.
 */
BOOLEAN
NTAPI
RtlIsTextUnicode(CONST VOID* buf, INT len, INT* pf)
{
    static const WCHAR std_control_chars[] = {'\r', '\n', '\t', ' ', 0x3000, 0};
    static const WCHAR byterev_control_chars[] = {0x0d00, 0x0a00, 0x0900, 0x2000, 0};
    const WCHAR *s = buf;
    int i;
    unsigned int flags = MAXULONG, out_flags = 0;
    UCHAR last_lo_byte = 0;
    UCHAR last_hi_byte = 0;
    ULONG hi_byte_diff = 0;
    ULONG lo_byte_diff = 0;
    ULONG weight = 3;
    ULONG lead_byte = 0;

    if (len < sizeof(WCHAR))
    {
        /* FIXME: MSDN documents IS_TEXT_UNICODE_BUFFER_TOO_SMALL but there is no such thing... */
        if (pf) *pf = 0;

        return FALSE;
    }

    if (pf)
        flags = *pf;

    /*
     * Apply various tests to the text string. According to the
     * docs, each test "passed" sets the corresponding flag in
     * the output flags. But some of the tests are mutually
     * exclusive, so I don't see how you could pass all tests ...
     */

    /* Check for an odd length ... pass if even. */
    if (len & 1) out_flags |= IS_TEXT_UNICODE_ODD_LENGTH;

    if (((char *)buf)[len - 1] == 0)
        len--;  /* Windows seems to do something like that to avoid e.g. false IS_TEXT_UNICODE_NULL_BYTES  */

    len /= sizeof(WCHAR);

    /* Windows only checks the first 256 characters */
    if (len > 256) len = 256;

    /* Check for the special byte order unicode marks. */
    if (*s == 0xFEFF) out_flags |= IS_TEXT_UNICODE_SIGNATURE;
    if (*s == 0xFFFE) out_flags |= IS_TEXT_UNICODE_REVERSE_SIGNATURE;

    for (i = 0; i < len; i++)
    {
        UCHAR lo_byte = LOBYTE(s[i]);
        UCHAR hi_byte = HIBYTE(s[i]);

        lo_byte_diff += max(lo_byte, last_lo_byte) - min(lo_byte, last_lo_byte);
        hi_byte_diff += max(hi_byte, last_hi_byte) - min(hi_byte, last_hi_byte);

        last_lo_byte = lo_byte;
        last_hi_byte = hi_byte;

        switch (s[i])
        {
            case 0xFFFE: /* Reverse BOM */
            case UNICODE_NULL:
            case 0x0A0D: /* ASCII CRLF (packed into one word) */
            case 0xFFFF: /* Unicode 0xFFFF */
                out_flags |= IS_TEXT_UNICODE_ILLEGAL_CHARS;
                break;
        }
    }

    if (NlsMbCodePageTag && pf && (*pf & IS_TEXT_UNICODE_DBCS_LEADBYTE))
    {
        for (i = 0; i < len; i++)
        {
            if (NlsLeadByteInfo[s[i]])
            {
                ++lead_byte;
                ++i;
            }
        }

        if (lead_byte)
        {
            weight = (len / 2) - 1;

            if (lead_byte < (weight / 3))
                weight = 3;
            else if (lead_byte < ((weight * 2) / 3))
                weight = 2;
            else
                weight = 1;

            out_flags |= IS_TEXT_UNICODE_DBCS_LEADBYTE;
        }
    }

    if (lo_byte_diff < 127 && !hi_byte_diff)
    {
        out_flags |= IS_TEXT_UNICODE_ASCII16;
    }

    if (hi_byte_diff && !lo_byte_diff)
    {
        out_flags |= IS_TEXT_UNICODE_REVERSE_ASCII16;
    }

    if ((weight * lo_byte_diff) < hi_byte_diff)
    {
        out_flags |= IS_TEXT_UNICODE_REVERSE_STATISTICS;
    }

    /* apply some statistical analysis */
    if ((flags & IS_TEXT_UNICODE_STATISTICS) &&
        ((weight * hi_byte_diff) < lo_byte_diff))
    {
        out_flags |= IS_TEXT_UNICODE_STATISTICS;
    }

    /* Check for unicode NULL chars */
    if (flags & IS_TEXT_UNICODE_NULL_BYTES)
    {
        for (i = 0; i < len; i++)
        {
            if (!(s[i] & 0xff) || !(s[i] >> 8))
            {
                out_flags |= IS_TEXT_UNICODE_NULL_BYTES;
                break;
            }
        }
    }

    if (flags & IS_TEXT_UNICODE_CONTROLS)
    {
        for (i = 0; i < len; i++)
        {
            if (strchrW(std_control_chars, s[i]))
            {
                out_flags |= IS_TEXT_UNICODE_CONTROLS;
                break;
            }
        }
    }

    if (flags & IS_TEXT_UNICODE_REVERSE_CONTROLS)
    {
        for (i = 0; i < len; i++)
        {
            if (strchrW(byterev_control_chars, s[i]))
            {
                out_flags |= IS_TEXT_UNICODE_REVERSE_CONTROLS;
                break;
            }
        }
    }

    if (pf)
    {
        out_flags &= *pf;
        *pf = out_flags;
    }

    /* check for flags that indicate it's definitely not valid Unicode */
    if (out_flags & (IS_TEXT_UNICODE_REVERSE_MASK | IS_TEXT_UNICODE_NOT_UNICODE_MASK)) return FALSE;

    /* now check for invalid ASCII, and assume Unicode if so */
    if (out_flags & IS_TEXT_UNICODE_NOT_ASCII_MASK) return TRUE;

    /* now check for Unicode flags */
    if (out_flags & IS_TEXT_UNICODE_UNICODE_MASK) return TRUE;

    /* no flags set */
    return FALSE;
}


/*
 * @implemented
 *
 * NOTES
 *  Same as RtlOemStringToUnicodeString but doesn't write terminating null
 *  A partial copy is NOT performed if the dest buffer is too small!
 */
NTSTATUS
NTAPI
RtlOemStringToCountedUnicodeString(
    IN OUT PUNICODE_STRING UniDest,
    IN PCOEM_STRING OemSource,
    IN BOOLEAN AllocateDestinationString)
{
    NTSTATUS Status;
    ULONG Length;
    ULONG Index;

    PAGED_CODE_RTL();

    /* Calculate size of the string */
    Length = RtlOemStringToCountedUnicodeSize(OemSource);

    /* If it's 0 then zero out dest string and return */
    if (!Length)
    {
        RtlZeroMemory(UniDest, sizeof(UNICODE_STRING));
        return STATUS_SUCCESS;
    }

    /* Check if length is a sane value */
    if (Length > MAXUSHORT) return STATUS_INVALID_PARAMETER_2;

    /* Store it in dest string */
    UniDest->Length = (USHORT)Length;

    /* If we're asked to alloc the string - do so */
    if (AllocateDestinationString)
    {
        UniDest->Buffer = RtlpAllocateStringMemory(Length, TAG_USTR);
        UniDest->MaximumLength = (USHORT)Length;

        if (!UniDest->Buffer) return STATUS_NO_MEMORY;
    }
    else if (UniDest->Length > UniDest->MaximumLength)
    {
        return STATUS_BUFFER_OVERFLOW;
    }

    /* Do the conversion */
    Status = RtlOemToUnicodeN(UniDest->Buffer,
                              UniDest->Length,
                              &Index,
                              OemSource->Buffer,
                              OemSource->Length);

    if (!NT_SUCCESS(Status) && AllocateDestinationString)
    {
        /* Conversion failed, free dest string and return status code */
        RtlpFreeStringMemory(UniDest->Buffer, TAG_USTR);
        UniDest->Buffer = NULL;
        return Status;
    }

    return STATUS_SUCCESS;
}

/*
 * @implemented
 *
 * RETURNS
 *  TRUE if the names are equal, FALSE if not
 *
 * NOTES
 *  The comparison is case insensitive.
 */
BOOLEAN
NTAPI
RtlEqualComputerName(
    IN PUNICODE_STRING ComputerName1,
    IN PUNICODE_STRING ComputerName2)
{
    OEM_STRING OemString1;
    OEM_STRING OemString2;
    BOOLEAN Result = FALSE;

    if (NT_SUCCESS(RtlUpcaseUnicodeStringToOemString(&OemString1,
                                                     ComputerName1,
                                                     TRUE)))
    {
        if (NT_SUCCESS(RtlUpcaseUnicodeStringToOemString(&OemString2,
                                                         ComputerName2,
                                                         TRUE)))
        {
            Result = RtlEqualString(&OemString1, &OemString2, FALSE);
            RtlFreeOemString(&OemString2);
        }

        RtlFreeOemString(&OemString1);
    }

    return Result;
}

/*
 * @implemented
 *
 * RETURNS
 *  TRUE if the names are equal, FALSE if not
 *
 * NOTES
 *  The comparison is case insensitive.
 */
BOOLEAN
NTAPI
RtlEqualDomainName (
    IN PUNICODE_STRING DomainName1,
    IN PUNICODE_STRING DomainName2)
{
    return RtlEqualComputerName(DomainName1, DomainName2);
}

/*
 * @implemented
 *
 * RIPPED FROM WINE's ntdll\rtlstr.c rev 1.45
 *
 * Convert a string representation of a GUID into a GUID.
 *
 * PARAMS
 *  str  [I] String representation in the format "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"
 *  guid [O] Destination for the converted GUID
 *
 * RETURNS
 *  Success: STATUS_SUCCESS. guid contains the converted value.
 *  Failure: STATUS_INVALID_PARAMETER, if str is not in the expected format.
 *
 * SEE ALSO
 *  See RtlStringFromGUID.
 */
NTSTATUS
NTAPI
RtlGUIDFromString(
    IN UNICODE_STRING *str,
    OUT GUID* guid)
{
    int i = 0;
    const WCHAR *lpszCLSID = str->Buffer;
    BYTE* lpOut = (BYTE*)guid;

    //TRACE("(%s,%p)\n", debugstr_us(str), guid);

    /* Convert string: {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
     * to memory:       DWORD... WORD WORD BYTES............
     */
    while (i <= 37)
    {
        switch (i)
        {
            case 0:
                if (*lpszCLSID != '{')
                    return STATUS_INVALID_PARAMETER;
                break;

            case 9:
            case 14:
            case 19:
            case 24:
                if (*lpszCLSID != '-')
                    return STATUS_INVALID_PARAMETER;
                break;

            case 37:
                if (*lpszCLSID != '}')
                    return STATUS_INVALID_PARAMETER;

                break;

            default:
            {
                WCHAR ch = *lpszCLSID, ch2 = lpszCLSID[1];
                unsigned char byte;

                /* Read two hex digits as a byte value */
                if      (ch >= '0' && ch <= '9')
                    ch = ch - '0';
                else if (ch >= 'a' && ch <= 'f')
                    ch = ch - 'a' + 10;
                else if (ch >= 'A' && ch <= 'F')
                    ch = ch - 'A' + 10;
                else
                    return STATUS_INVALID_PARAMETER;

                if      (ch2 >= '0' && ch2 <= '9')
                    ch2 = ch2 - '0';
                else if (ch2 >= 'a' && ch2 <= 'f')
                    ch2 = ch2 - 'a' + 10;
                else if (ch2 >= 'A' && ch2 <= 'F')
                    ch2 = ch2 - 'A' + 10;
                else
                    return STATUS_INVALID_PARAMETER;

                byte = ch << 4 | ch2;

                switch (i)
                {
#ifndef WORDS_BIGENDIAN
                        /* For Big Endian machines, we store the data such that the
                         * dword/word members can be read as DWORDS and WORDS correctly. */
                        /* Dword */
                    case 1:
                        lpOut[3] = byte;
                        break;
                    case 3:
                        lpOut[2] = byte;
                        break;
                    case 5:
                        lpOut[1] = byte;
                        break;
                    case 7:
                        lpOut[0] = byte;
                        lpOut += 4;
                        break;
                        /* Word */
                    case 10:
                    case 15:
                        lpOut[1] = byte;
                        break;
                    case 12:
                    case 17:
                        lpOut[0] = byte;
                        lpOut += 2;
                        break;
#endif
                        /* Byte */
                    default:
                        lpOut[0] = byte;
                        lpOut++;
                        break;
                }

                lpszCLSID++; /* Skip 2nd character of byte */
                i++;
            }
        }

        lpszCLSID++;
        i++;
    }

    return STATUS_SUCCESS;
}

/*
 * @implemented
 */
VOID
NTAPI
RtlEraseUnicodeString(
    IN PUNICODE_STRING String)
{
    if (String->Buffer && String->MaximumLength)
    {
        RtlZeroMemory(String->Buffer, String->MaximumLength);
        String->Length = 0;
    }
}

/*
* @implemented
*/
NTSTATUS
NTAPI
RtlHashUnicodeString(
    IN CONST UNICODE_STRING *String,
    IN BOOLEAN CaseInSensitive,
    IN ULONG HashAlgorithm,
    OUT PULONG HashValue)
{
    if (String != NULL && HashValue != NULL)
    {
        switch (HashAlgorithm)
        {
            case HASH_STRING_ALGORITHM_DEFAULT:
            case HASH_STRING_ALGORITHM_X65599:
            {
                WCHAR *c, *end;

                *HashValue = 0;
                end = String->Buffer + (String->Length / sizeof(WCHAR));

                if (CaseInSensitive)
                {
                    for (c = String->Buffer; c != end; c++)
                    {
                        /* only uppercase characters if they are 'a' ... 'z'! */
                        *HashValue = ((65599 * (*HashValue)) +
                                      (ULONG)(((*c) >= L'a' && (*c) <= L'z') ?
                                              (*c) - L'a' + L'A' : (*c)));
                    }
                }
                else
                {
                    for (c = String->Buffer; c != end; c++)
                    {
                        *HashValue = ((65599 * (*HashValue)) + (ULONG)(*c));
                    }
                }

                return STATUS_SUCCESS;
            }
        }
    }

    return STATUS_INVALID_PARAMETER;
}

/*
 * @implemented
 *
 * NOTES
 *  Same as RtlUnicodeStringToOemString but doesn't write terminating null
 */
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSYSAPI
NTSTATUS
NTAPI
RtlUnicodeStringToCountedOemString(
    _When_(AllocateDestinationString, _Out_ _At_(DestinationString->Buffer, __drv_allocatesMem(Mem)))
    _When_(!AllocateDestinationString, _Inout_)
        POEM_STRING OemDest,
    _In_ PCUNICODE_STRING UniSource,
    _In_ BOOLEAN AllocateDestinationString)
{
    NTSTATUS Status;
    ULONG Length;
    ULONG Index;

    PAGED_CODE_RTL();

    /* Calculate size of the string */
    Length = RtlUnicodeStringToCountedOemSize(UniSource);

    /* If it's 0 then zero out dest string and return */
    if (!Length)
    {
        RtlZeroMemory(OemDest, sizeof(OEM_STRING));
        return STATUS_SUCCESS;
    }

    /* Check if length is a sane value */
    if (Length > MAXUSHORT) return STATUS_INVALID_PARAMETER_2;

    /* Store it in dest string */
    OemDest->Length = (USHORT)Length;

    /* If we're asked to alloc the string - do so */
    if (AllocateDestinationString)
    {
        OemDest->Buffer = RtlpAllocateStringMemory(Length, TAG_OSTR);
        OemDest->MaximumLength = (USHORT)Length;
        if (!OemDest->Buffer) return STATUS_NO_MEMORY;
    }
    else if (OemDest->Length > OemDest->MaximumLength)
    {
        return STATUS_BUFFER_OVERFLOW;
    }

    /* Do the conversion */
    Status = RtlUnicodeToOemN(OemDest->Buffer,
                              OemDest->Length,
                              &Index,
                              UniSource->Buffer,
                              UniSource->Length);

    /* Check for unmapped character */
    if (NT_SUCCESS(Status) && !RtlpDidUnicodeToOemWork(UniSource, OemDest))
        Status = STATUS_UNMAPPABLE_CHARACTER;

    if (!NT_SUCCESS(Status) && AllocateDestinationString)
    {
        /* Conversion failed, free dest string and return status code */
        RtlpFreeStringMemory(OemDest->Buffer, TAG_OSTR);
        OemDest->Buffer = NULL;
        return Status;
    }

    return Status;
}

/*
 * @implemented
 */
NTSTATUS
NTAPI
RtlLargeIntegerToChar(
    IN PLARGE_INTEGER Value,
    IN ULONG  Base,
    IN ULONG  Length,
    IN OUT PCHAR  String)
{
    ULONGLONG Val = Value->QuadPart;
    CHAR Buffer[65];
    CHAR Digit;
    SIZE_T Len;
    PCHAR Pos;

    if (Base == 0) Base = 10;

    if ((Base != 2) && (Base != 8) && (Base != 10) && (Base != 16))
    {
        return STATUS_INVALID_PARAMETER;
    }

    Pos = &Buffer[64];
    *Pos = '\0';

    do
    {
        Pos--;
        Digit = (CHAR)(Val % Base);
        Val = Val / Base;

        if (Digit < 10)
            *Pos = '0' + Digit;
        else
            *Pos = 'A' + Digit - 10;
    }
    while (Val != 0L);

    Len = &Buffer[64] - Pos;

    if (Len > Length)
        return STATUS_BUFFER_OVERFLOW;

    /* If possible, add the 0 termination */
    if (Len < Length)
        Len += 1;

    /* Copy the string to the target using SEH */
    return RtlpSafeCopyMemory(String, Pos, Len);
}

/*
 * @implemented
 *
 * NOTES
 *  dest is never '\0' terminated because it may be equal to src, and src
 *  might not be '\0' terminated. dest->Length is only set upon success.
 */
NTSTATUS
NTAPI
RtlUpcaseUnicodeString(
    IN OUT PUNICODE_STRING UniDest,
    IN PCUNICODE_STRING UniSource,
    IN BOOLEAN  AllocateDestinationString)
{
    ULONG i, j;

    PAGED_CODE_RTL();

    if (AllocateDestinationString)
    {
        UniDest->MaximumLength = UniSource->Length;
        UniDest->Buffer = RtlpAllocateStringMemory(UniDest->MaximumLength, TAG_USTR);
        if (UniDest->Buffer == NULL) return STATUS_NO_MEMORY;
    }
    else if (UniSource->Length > UniDest->MaximumLength)
    {
        return STATUS_BUFFER_OVERFLOW;
    }

    j = UniSource->Length / sizeof(WCHAR);

    for (i = 0; i < j; i++)
    {
        UniDest->Buffer[i] = RtlpUpcaseUnicodeChar(UniSource->Buffer[i]);
    }

    UniDest->Length = UniSource->Length;
    return STATUS_SUCCESS;
}

/*
 * @implemented
 *
 * NOTES
 *  This function always writes a terminating '\0'.
 *  It performs a partial copy if ansi is too small.
 */
NTSTATUS
NTAPI
RtlUpcaseUnicodeStringToAnsiString(
    IN OUT PANSI_STRING AnsiDest,
    IN PCUNICODE_STRING UniSource,
    IN BOOLEAN  AllocateDestinationString)
{
    NTSTATUS Status;
    ULONG Length;
    ULONG Index;
    PAGED_CODE_RTL();

    Length = RtlUnicodeStringToAnsiSize(UniSource);
    if (Length > MAXUSHORT) return STATUS_INVALID_PARAMETER_2;

    AnsiDest->Length = (USHORT)Length - sizeof(CHAR);

    if (AllocateDestinationString)
    {
        AnsiDest->Buffer = RtlpAllocateStringMemory(Length, TAG_ASTR);
        AnsiDest->MaximumLength = (USHORT)Length;
        if (!AnsiDest->Buffer) return STATUS_NO_MEMORY;
    }
    else if (AnsiDest->Length >= AnsiDest->MaximumLength)
    {
        if (!AnsiDest->MaximumLength) return STATUS_BUFFER_OVERFLOW;
    }

    Status = RtlUpcaseUnicodeToMultiByteN(AnsiDest->Buffer,
                                          AnsiDest->Length,
                                          &Index,
                                          UniSource->Buffer,
                                          UniSource->Length);

    if (!NT_SUCCESS(Status) && AllocateDestinationString)
    {
        RtlpFreeStringMemory(AnsiDest->Buffer, TAG_ASTR);
        AnsiDest->Buffer = NULL;
        return Status;
    }

    AnsiDest->Buffer[Index] = ANSI_NULL;
    return Status;
}

/*
 * @implemented
 *
 * NOTES
 *  This function always writes a terminating '\0'.
 *  It performs a partial copy if ansi is too small.
 */
NTSTATUS
NTAPI
RtlUpcaseUnicodeStringToCountedOemString(
    IN OUT POEM_STRING OemDest,
    IN PCUNICODE_STRING UniSource,
    IN BOOLEAN AllocateDestinationString)
{
    NTSTATUS Status;
    ULONG Length;
    ULONG Index;
    PAGED_CODE_RTL();

    Length = RtlUnicodeStringToCountedOemSize(UniSource);

    if (!Length)
    {
        RtlZeroMemory(OemDest, sizeof(OEM_STRING));
    }

    if (Length > MAXUSHORT) return STATUS_INVALID_PARAMETER_2;

    OemDest->Length = (USHORT)Length;

    if (AllocateDestinationString)
    {
        OemDest->Buffer = RtlpAllocateStringMemory(Length, TAG_OSTR);
        OemDest->MaximumLength = (USHORT)Length;
        if (!OemDest->Buffer) return STATUS_NO_MEMORY;
    }
    else if (OemDest->Length > OemDest->MaximumLength)
    {
        return STATUS_BUFFER_OVERFLOW;
    }

    Status = RtlUpcaseUnicodeToOemN(OemDest->Buffer,
                                    OemDest->Length,
                                    &Index,
                                    UniSource->Buffer,
                                    UniSource->Length);

    /* Check for unmapped characters */
    if (NT_SUCCESS(Status) && !RtlpDidUnicodeToOemWork(UniSource, OemDest))
        Status = STATUS_UNMAPPABLE_CHARACTER;

    if (!NT_SUCCESS(Status) && AllocateDestinationString)
    {
        RtlpFreeStringMemory(OemDest->Buffer, TAG_OSTR);
        OemDest->Buffer = NULL;
        return Status;
    }

    return Status;
}

/*
 * @implemented
 * NOTES
 *  OEM string is always nullterminated
 *  It performs a partial copy if oem is too small.
 */
NTSTATUS
NTAPI
RtlUpcaseUnicodeStringToOemString (
    IN OUT POEM_STRING OemDest,
    IN PCUNICODE_STRING UniSource,
    IN BOOLEAN  AllocateDestinationString)
{
    NTSTATUS Status;
    ULONG Length;
    ULONG Index;
    PAGED_CODE_RTL();

    Length = RtlUnicodeStringToOemSize(UniSource);
    if (Length > MAXUSHORT) return STATUS_INVALID_PARAMETER_2;

    OemDest->Length = (USHORT)Length - sizeof(CHAR);

    if (AllocateDestinationString)
    {
        OemDest->Buffer = RtlpAllocateStringMemory(Length, TAG_OSTR);
        OemDest->MaximumLength = (USHORT)Length;
        if (!OemDest->Buffer) return STATUS_NO_MEMORY;
    }
    else if (OemDest->Length >= OemDest->MaximumLength)
    {
        return STATUS_BUFFER_OVERFLOW;
    }

    Status = RtlUpcaseUnicodeToOemN(OemDest->Buffer,
                                    OemDest->Length,
                                    &Index,
                                    UniSource->Buffer,
                                    UniSource->Length);

    /* Check for unmapped characters */
    if (NT_SUCCESS(Status) && !RtlpDidUnicodeToOemWork(UniSource, OemDest))
        Status = STATUS_UNMAPPABLE_CHARACTER;

    if (!NT_SUCCESS(Status) && AllocateDestinationString)
    {
        RtlpFreeStringMemory(OemDest->Buffer, TAG_OSTR);
        OemDest->Buffer = NULL;
        return Status;
    }

    OemDest->Buffer[Index] = ANSI_NULL;
    return Status;
}

/*
 * @implemented
 *
 * RETURNS
 *  Bytes calculated including nullterm
 */
ULONG
NTAPI
RtlxOemStringToUnicodeSize(IN PCOEM_STRING OemString)
{
    ULONG Size;

    /* Convert the Mb String to Unicode Size */
    RtlMultiByteToUnicodeSize(&Size,
                              OemString->Buffer,
                              OemString->Length);

    /* Return the size + null-char */
    return (Size + sizeof(WCHAR));
}

/*
 * @implemented
 */
NTSTATUS
NTAPI
RtlStringFromGUID (IN REFGUID Guid,
                   OUT PUNICODE_STRING GuidString)
{
    /* Setup the string */
    GuidString->Length = 38 * sizeof(WCHAR);
    GuidString->MaximumLength = GuidString->Length + sizeof(UNICODE_NULL);
    GuidString->Buffer = RtlpAllocateStringMemory(GuidString->MaximumLength,
                                                  TAG_USTR);
    if (!GuidString->Buffer) return STATUS_NO_MEMORY;

    /* Now format the GUID */
    swprintf(GuidString->Buffer,
             L"{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
             Guid->Data1,
             Guid->Data2,
             Guid->Data3,
             Guid->Data4[0],
             Guid->Data4[1],
             Guid->Data4[2],
             Guid->Data4[3],
             Guid->Data4[4],
             Guid->Data4[5],
             Guid->Data4[6],
             Guid->Data4[7]);
    return STATUS_SUCCESS;
}

/*
 * @implemented
 *
 * RETURNS
 *  Bytes calculated including nullterm
 */
ULONG
NTAPI
RtlxUnicodeStringToAnsiSize(IN PCUNICODE_STRING UnicodeString)
{
    ULONG Size;
    PAGED_CODE_RTL();

    ASSERT(!(UnicodeString->Length & 1));

    /* Convert the Unicode String to Mb Size */
    RtlUnicodeToMultiByteSize(&Size,
                              UnicodeString->Buffer,
                              UnicodeString->Length);

    /* Return the size + null-char */
    return (Size + sizeof(CHAR));
}

/*
 * @implemented
 */
LONG
NTAPI
RtlCompareUnicodeString(
    IN PCUNICODE_STRING s1,
    IN PCUNICODE_STRING s2,
    IN BOOLEAN  CaseInsensitive)
{
    unsigned int len;
    LONG ret = 0;
    LPCWSTR p1, p2;

    len = min(s1->Length, s2->Length) / sizeof(WCHAR);
    p1 = s1->Buffer;
    p2 = s2->Buffer;

    if (CaseInsensitive)
    {
        while (!ret && len--) ret = RtlpUpcaseUnicodeChar(*p1++) - RtlpUpcaseUnicodeChar(*p2++);
    }
    else
    {
        while (!ret && len--) ret = *p1++ - *p2++;
    }

    if (!ret) ret = s1->Length - s2->Length;

    return ret;
}

/*
 * @implemented
 */
_IRQL_requires_max_(PASSIVE_LEVEL)
_Must_inspect_result_
NTSYSAPI
LONG
NTAPI
RtlCompareUnicodeStrings(
    _In_reads_(String1Length) PCWCH String1,
    _In_ SIZE_T String1Length,
    _In_reads_(String2Length) PCWCH String2,
    _In_ SIZE_T String2Length,
    _In_ BOOLEAN CaseInSensitive)
{
    LONG Result = 0;
    SIZE_T MinStringLength = min(String1Length, String2Length);
    SIZE_T Index;

    if (CaseInSensitive)
    {
        for (Index = 0; Index < MinStringLength; Index++)
        {
            WCHAR Char1 = RtlpUpcaseUnicodeChar(String1[Index]);
            WCHAR Char2 = RtlpUpcaseUnicodeChar(String2[Index]);
            Result = Char1 - Char2;
            if (Result != 0)
            {
                return Result;
            }
        }
    }
    else
    {
        for (Index = 0; Index < MinStringLength; Index++)
        {
            Result = String1[Index] - String2[Index];
            if (Result != 0)
            {
                return Result;
            }
        }
    }

    return String1Length - String2Length;
}

/*
 * @implemented
 */
VOID
NTAPI
RtlCopyString(
    IN OUT PSTRING DestinationString,
    IN const STRING *SourceString OPTIONAL)
{
    ULONG SourceLength;
    PCHAR p1, p2;

    /* Check if there was no source given */
    if (!SourceString)
    {
        /* Simply return an empty string */
        DestinationString->Length = 0;
    }
    else
    {
        /* Choose the smallest length */
        SourceLength = min(DestinationString->MaximumLength,
                           SourceString->Length);

        /* Set it */
        DestinationString->Length = (USHORT)SourceLength;

        /* Save the pointers to each buffer */
        p1 = DestinationString->Buffer;
        p2 = SourceString->Buffer;

        /* Loop the buffer */
        while (SourceLength)
        {
            /* Copy the character and move on */
            *p1++ = * p2++;
            SourceLength--;
        }
    }
}

/*
 * @implemented
 */
VOID
NTAPI
RtlCopyUnicodeString(
    IN OUT PUNICODE_STRING DestinationString,
    IN PCUNICODE_STRING SourceString)
{
    ULONG SourceLength;

    if (!SourceString)
    {
        DestinationString->Length = 0;
    }
    else
    {
        SourceLength = min(DestinationString->MaximumLength,
                           SourceString->Length);
        DestinationString->Length = (USHORT)SourceLength;

        RtlCopyMemory(DestinationString->Buffer,
                      SourceString->Buffer,
                      SourceLength);

        if (DestinationString->Length < DestinationString->MaximumLength)
        {
            DestinationString->Buffer[SourceLength / sizeof(WCHAR)] = UNICODE_NULL;
        }
    }
}

/*
 * @implemented
 *
 * NOTES
 * Creates a nullterminated UNICODE_STRING
 */
BOOLEAN
NTAPI
RtlCreateUnicodeString(
    IN OUT PUNICODE_STRING UniDest,
    IN PCWSTR  Source)
{
    SIZE_T Size;
    PAGED_CODE_RTL();

    Size = (wcslen(Source) + 1) * sizeof(WCHAR);
    if (Size > MAXUSHORT) return FALSE;

    UniDest->Buffer = RtlpAllocateStringMemory((ULONG)Size, TAG_USTR);

    if (UniDest->Buffer == NULL) return FALSE;

    RtlCopyMemory(UniDest->Buffer, Source, Size);
    UniDest->MaximumLength = (USHORT)Size;
    UniDest->Length = (USHORT)Size - sizeof (WCHAR);

    return TRUE;
}

/*
 * @implemented
 */
BOOLEAN
NTAPI
RtlCreateUnicodeStringFromAsciiz(
    OUT PUNICODE_STRING Destination,
    IN PCSZ Source)
{
    ANSI_STRING AnsiString;
    NTSTATUS Status;

    RtlInitAnsiString(&AnsiString, Source);

    Status = RtlAnsiStringToUnicodeString(Destination,
                                          &AnsiString,
                                          TRUE);

    return NT_SUCCESS(Status);
}

/*
 * @implemented
 *
 * NOTES
 *  Dest is never '\0' terminated because it may be equal to src, and src
 *  might not be '\0' terminated.
 *  Dest->Length is only set upon success.
 */
NTSTATUS
NTAPI
RtlDowncaseUnicodeString(
    IN OUT PUNICODE_STRING UniDest,
    IN PCUNICODE_STRING UniSource,
    IN BOOLEAN AllocateDestinationString)
{
    ULONG i;
    ULONG StopGap;
    PAGED_CODE_RTL();

    if (AllocateDestinationString)
    {
        UniDest->MaximumLength = UniSource->Length;
        UniDest->Buffer = RtlpAllocateStringMemory(UniSource->Length, TAG_USTR);
        if (UniDest->Buffer == NULL) return STATUS_NO_MEMORY;
    }
    else if (UniSource->Length > UniDest->MaximumLength)
    {
        return STATUS_BUFFER_OVERFLOW;
    }

    UniDest->Length = UniSource->Length;
    StopGap = UniSource->Length / sizeof(WCHAR);

    for (i = 0 ; i < StopGap; i++)
    {
        if (UniSource->Buffer[i] < L'A')
        {
            UniDest->Buffer[i] = UniSource->Buffer[i];
        }
        else if (UniSource->Buffer[i] <= L'Z')
        {
            UniDest->Buffer[i] = (UniSource->Buffer[i] + (L'a' - L'A'));
        }
        else
        {
            UniDest->Buffer[i] = RtlpDowncaseUnicodeChar(UniSource->Buffer[i]);
        }
    }

    return STATUS_SUCCESS;
}

/*
 * @implemented
 *
 * NOTES
 *  if src is NULL dest is unchanged.
 *  dest is '\0' terminated when the MaximumLength allowes it.
 *  When dest fits exactly in MaximumLength characters the '\0' is ommitted.
 */
NTSTATUS
NTAPI
RtlAppendUnicodeToString(IN OUT PUNICODE_STRING Destination,
                         IN PCWSTR Source)
{
    USHORT Length;
    PWCHAR DestBuffer;

    if (Source)
    {
        UNICODE_STRING UnicodeSource;

        RtlInitUnicodeString(&UnicodeSource, Source);
        Length = UnicodeSource.Length;

        if (Destination->Length + Length > Destination->MaximumLength)
        {
            return STATUS_BUFFER_TOO_SMALL;
        }

        DestBuffer = &Destination->Buffer[Destination->Length / sizeof(WCHAR)];
        RtlMoveMemory(DestBuffer, Source, Length);
        Destination->Length += Length;

        /* append terminating '\0' if enough space */
        if (Destination->MaximumLength > Destination->Length)
        {
            DestBuffer[Length / sizeof(WCHAR)] = UNICODE_NULL;
        }
    }

    return STATUS_SUCCESS;
}

/*
 * @implemented
 *
 * NOTES
 *  if src is NULL dest is unchanged.
 *  dest is never '\0' terminated.
 */
NTSTATUS
NTAPI
RtlAppendAsciizToString(
    IN OUT   PSTRING  Destination,
    IN PCSZ  Source)
{
    SIZE_T Size;

    if (Source)
    {
        Size = strlen(Source);

        if (Destination->Length + Size > Destination->MaximumLength)
        {
            return STATUS_BUFFER_TOO_SMALL;
        }

        RtlMoveMemory(&Destination->Buffer[Destination->Length], Source, Size);
        Destination->Length += (USHORT)Size;
    }

    return STATUS_SUCCESS;
}

/*
 * @implemented
 */
VOID
NTAPI
RtlUpperString(PSTRING DestinationString,
               const STRING *SourceString)
{
    USHORT Length;
    PCHAR Src, Dest;

    Length = min(SourceString->Length,
                 DestinationString->MaximumLength);

    Src = SourceString->Buffer;
    Dest = DestinationString->Buffer;
    DestinationString->Length = Length;

    while (Length)
    {
        *Dest++ = RtlUpperChar(*Src++);
        Length--;
    }
}

/*
 * @implemented
 *
 * NOTES
 *  See RtlpDuplicateUnicodeString
 */
NTSTATUS
NTAPI
RtlDuplicateUnicodeString(
    IN ULONG Flags,
    IN PCUNICODE_STRING SourceString,
    OUT PUNICODE_STRING DestinationString)
{
    PAGED_CODE_RTL();

    if (SourceString == NULL || DestinationString == NULL ||
        SourceString->Length > SourceString->MaximumLength ||
        (SourceString->Length == 0 && SourceString->MaximumLength > 0 && SourceString->Buffer == NULL) ||
        Flags == RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING || Flags >= 4)
    {
        return STATUS_INVALID_PARAMETER;
    }


    if ((SourceString->Length == 0) &&
        (Flags != (RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE |
                   RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING)))
    {
        DestinationString->Length = 0;
        DestinationString->MaximumLength = 0;
        DestinationString->Buffer = NULL;
    }
    else
    {
        UINT DestMaxLength = SourceString->Length;

        if (Flags & RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE)
            DestMaxLength += sizeof(UNICODE_NULL);

        DestinationString->Buffer = RtlpAllocateStringMemory(DestMaxLength, TAG_USTR);

        if (DestinationString->Buffer == NULL)
            return STATUS_NO_MEMORY;

        RtlCopyMemory(DestinationString->Buffer, SourceString->Buffer, SourceString->Length);
        DestinationString->Length = SourceString->Length;
        DestinationString->MaximumLength = DestMaxLength;

        if (Flags & RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE)
            DestinationString->Buffer[DestinationString->Length / sizeof(WCHAR)] = 0;
    }

    return STATUS_SUCCESS;
}

/*
 * @implemented
 */
NTSTATUS
NTAPI
RtlValidateUnicodeString(
    _In_ ULONG Flags,
    _In_ PCUNICODE_STRING String)
{
    /* In Windows <= 2003 no flags are supported yet! */
    if (Flags != 0)
        return STATUS_INVALID_PARAMETER;

    /* NOTE: a NULL Unicode string pointer is considered to be a valid one! */
    if (String == NULL)
    {
        return STATUS_SUCCESS;
    }
    else if (!((String->Buffer == NULL) && (String->Length != 0 || String->MaximumLength != 0)) &&
              (String->Length % sizeof(WCHAR) == 0) &&
              (String->MaximumLength % sizeof(WCHAR) == 0) &&
              (String->Length <= String->MaximumLength))
    {
        return STATUS_SUCCESS;
    }
    else
    {
        return STATUS_INVALID_PARAMETER;
    }
}

/*
 * @implemented
 */
NTSTATUS
NTAPI
RtlpEnsureBufferSize(
    IN ULONG Flags,
    IN OUT PRTL_BUFFER Buffer,
    IN SIZE_T RequiredSize)
{
    PUCHAR NewBuffer;

    /* Parameter checks */
    if (Flags & ~RTL_SKIP_BUFFER_COPY)
        return STATUS_INVALID_PARAMETER;
    if (Buffer == NULL)
        return STATUS_INVALID_PARAMETER;

    /*
     * We don't need to grow the buffer if its size
     * is already larger than the required size.
     */
    if (Buffer->Size >= RequiredSize)
        return STATUS_SUCCESS;

    /*
     * When we are using the static buffer as our buffer, we don't need
     * to grow it if its size is already larger than the required size.
     * In this case, just keep it but update the current buffer size to
     * the one requested.
     * (But NEVER EVER modify the size of the static buffer!!)
     * Otherwise, we'll need to create a new buffer and use this one instead.
     */
    if ( (Buffer->Buffer == Buffer->StaticBuffer) &&
         (Buffer->StaticSize >= RequiredSize) )
    {
        Buffer->Size = RequiredSize;
        return STATUS_SUCCESS;
    }

    /* The buffer we are using is not large enough, try to create a bigger one */
    NewBuffer = RtlpAllocateStringMemory(RequiredSize, TAG_USTR);
    if (NewBuffer == NULL)
        return STATUS_NO_MEMORY;

    /* Copy the original content if needed */
    if (!(Flags & RTL_SKIP_BUFFER_COPY))
    {
        RtlMoveMemory(NewBuffer, Buffer->Buffer, Buffer->Size);
    }

    /* Free the original buffer only if it's not the static buffer */
    if (Buffer->Buffer != Buffer->StaticBuffer)
    {
        RtlpFreeStringMemory(Buffer->Buffer, TAG_USTR);
    }

    /* Update the members */
    Buffer->Buffer = NewBuffer;
    Buffer->Size   = RequiredSize;

    /* Done */
    return STATUS_SUCCESS;
}

static
BOOLEAN
RtlpIsCharInUnicodeString(
    IN WCHAR Char,
    IN PCUNICODE_STRING MatchString,
    IN BOOLEAN CaseInSensitive)
{
    USHORT i;

    if (CaseInSensitive)
        Char = RtlpUpcaseUnicodeChar(Char);

    for (i = 0; i < MatchString->Length / sizeof(WCHAR); i++)
    {
        WCHAR OtherChar = MatchString->Buffer[i];
        if (CaseInSensitive)
            OtherChar = RtlpUpcaseUnicodeChar(OtherChar);

        if (Char == OtherChar)
            return TRUE;
    }

    return FALSE;
}

/*
 * @implemented
 */
NTSTATUS
NTAPI
RtlFindCharInUnicodeString(
    IN ULONG Flags,
    IN PCUNICODE_STRING SearchString,
    IN PCUNICODE_STRING MatchString,
    OUT PUSHORT Position)
{
    BOOLEAN Found;
    const BOOLEAN WantToFind = (Flags & RTL_FIND_CHAR_IN_UNICODE_STRING_COMPLEMENT_CHAR_SET) == 0;
    const BOOLEAN CaseInSensitive = (Flags & RTL_FIND_CHAR_IN_UNICODE_STRING_CASE_INSENSITIVE) != 0;
    USHORT i, Length;

    DPRINT("RtlFindCharInUnicodeString(%u, '%wZ', '%wZ', %p)\n",
           Flags, SearchString, MatchString, Position);

    /* Parameter checks */
    if (Position == NULL)
        return STATUS_INVALID_PARAMETER;

    *Position = 0;

    if (Flags & ~(RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END |
                  RTL_FIND_CHAR_IN_UNICODE_STRING_COMPLEMENT_CHAR_SET |
                  RTL_FIND_CHAR_IN_UNICODE_STRING_CASE_INSENSITIVE))
        return STATUS_INVALID_PARAMETER;

    /* Search */
    Length = SearchString->Length / sizeof(WCHAR);
    if (Flags & RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END)
    {
        for (i = Length - 1; (SHORT)i >= 0; i--)
        {
            Found = RtlpIsCharInUnicodeString(SearchString->Buffer[i], MatchString, CaseInSensitive);
            if (Found == WantToFind)
            {
                *Position = i * sizeof(WCHAR);
                return STATUS_SUCCESS;
            }
        }
    }
    else
    {
        for (i = 0; i < Length; i++)
        {
            Found = RtlpIsCharInUnicodeString(SearchString->Buffer[i], MatchString, CaseInSensitive);
            if (Found == WantToFind)
            {
                *Position = (i + 1) * sizeof(WCHAR);
                return STATUS_SUCCESS;
            }
        }
    }

    return STATUS_NOT_FOUND;
}

/*
 * @implemented
 *
 * NOTES
 *  Get the maximum of MAX_COMPUTERNAME_LENGTH characters from the dns.host name until the dot is found.
 *  Convert is to an uppercase oem string and check for unmapped characters.
 *  Then convert the oem string back to an unicode string.
 */
NTSTATUS
NTAPI
RtlDnsHostNameToComputerName(PUNICODE_STRING ComputerName, PUNICODE_STRING DnsHostName, BOOLEAN AllocateComputerNameString)
{
    NTSTATUS Status;
    ULONG Length;
    ULONG ComputerNameLength;
    ULONG ComputerNameOemNLength;
    OEM_STRING ComputerNameOem;
    CHAR ComputerNameOemN[MAX_COMPUTERNAME_LENGTH + 1];

    Status = STATUS_INVALID_COMPUTER_NAME;
    ComputerNameLength = DnsHostName->Length;

    /* find the first dot in the dns host name */
    for (Length = 0; Length < DnsHostName->Length / sizeof(WCHAR); Length++)
    {
        if (DnsHostName->Buffer[Length] == L'.')
        {
            /* dot found, so set the length for the oem translation */
            ComputerNameLength = Length * sizeof(WCHAR);
            break;
        }
    }

    /* the computername must have one character */
    if (ComputerNameLength > 0)
    {
        ComputerNameOemNLength = 0;
        /* convert to oem string and use uppercase letters */
        Status = RtlUpcaseUnicodeToOemN(ComputerNameOemN,
                                        MAX_COMPUTERNAME_LENGTH,
                                        &ComputerNameOemNLength,
                                        DnsHostName->Buffer,
                                        ComputerNameLength);

        /* status STATUS_BUFFER_OVERFLOW is not a problem since the computername shoud only
           have MAX_COMPUTERNAME_LENGTH characters */
        if ((Status == STATUS_SUCCESS) ||
                (Status == STATUS_BUFFER_OVERFLOW))
        {
            /* set the termination for the oem string */
            ComputerNameOemN[MAX_COMPUTERNAME_LENGTH] = 0;
            /* set status for the case the next function failed */
            Status = STATUS_INVALID_COMPUTER_NAME;
            /* fillup the oem string structure with the converted computername
               and check it for unmapped characters */
            ComputerNameOem.Buffer = ComputerNameOemN;
            ComputerNameOem.Length = (USHORT)ComputerNameOemNLength;
            ComputerNameOem.MaximumLength = (USHORT)(MAX_COMPUTERNAME_LENGTH + 1);

            if (RtlpDidUnicodeToOemWork(DnsHostName, &ComputerNameOem))
            {
                /* no unmapped character so convert it back to an unicode string */
                Status = RtlOemStringToUnicodeString(ComputerName,
                                                     &ComputerNameOem,
                                                     AllocateComputerNameString);
            }
        }
    }

    return Status;
}