void EntityFactory::LoadItems(const char* filename)
{
	TiXmlDocument doc;
	if (!doc.LoadFile(filename))
	{
		DIE("can't open items definition %s\n%s", filename, doc.ErrorDesc());
	}
	TiXmlHandle handle(&doc);
	TiXmlElement* elem = handle.FirstChildElement().FirstChildElement().Element();
	const char* name;
	while (elem != NULL)
	{
		QueryPtr(elem, "name", name);
		Item::Type item_type = Item::StringToType(name);

		ItemPattern item;

		QueryStr(elem, "label", item.label);
		QueryInt(elem, "x", item.x);
		QueryInt(elem, "y", item.y);
		QueryInt(elem, "w", item.w);
		QueryInt(elem, "h", item.h);

		items_[Item::StringToType(name)] = item;
		elem = elem->NextSiblingElement();
	}
}
BOOL
ISAPI_STRINGW::CopyToOffset(
    WCHAR * szStringW,
    DWORD   cchStringW,
    DWORD   cchOffset
    )
/*++

Purpose:

    Copies a Unicode string into the ISAPI_STRINGW's data, starting at the
    specified offset, in characters.

Arguments:

    szStringW  - The Unicode string to copy
    cchStringW - The number of characters to copy
    cchOffset  - The offset, in chracters, at which to copy

Returns:

    TRUE on success, FALSE on failure

--*/
{
    DWORD   cbData;

    //
    // Ensure that we have enough storage available
    //

    cbData = ( cchStringW + 1 ) * sizeof(WCHAR);

    if ( ResizeBuffer( cbData + cchOffset * sizeof(WCHAR) ) == FALSE )
    {
        return FALSE;
    }

    //
    // Copy the data and ensure proper termination
    //

    CopyMemory( QueryStr() + cchOffset, szStringW, cbData );

    SetLen( QueryCCH() + cchStringW );

    return TRUE;
}
BOOL
ISAPI_STRING::CopyToOffset(
    CHAR *  szString,
    DWORD   cchString,
    DWORD   dwOffset
    )
/*++

Purpose:

    Copies a string into the ISAPI_STRING's data, starting at the
    specified offset.

Arguments:

    szString  - The string to copy
    cchString - The number of characters to copy
    dwOffset  - The offset at which to copy

Returns:

    TRUE on success, FALSE on failure

--*/
{
    DWORD   cbData;

    //
    // Ensure that we have enough storage available
    //

    cbData = cchString + sizeof(CHAR);

    if ( ResizeBuffer( cbData + dwOffset ) == FALSE )
    {
        return FALSE;
    }

    //
    // Copy the data and ensure proper termination
    //

    CopyMemory( QueryStr() + dwOffset, szString, cbData );

    SetLen( QueryCCH() + cbData - sizeof(CHAR) );

    return TRUE;
}
BOOL
ISAPI_STRING::SetLen(
    DWORD   cchNewLength
    )
/*++

Purpose:

    Sets the length of the data in the ISAPI_STRING object,
    truncating the current data if necessary.

Arguments:

    cchNewLength - The new data length.

Returns:

    TRUE on success, FALSE on failure.

--*/
{
    CHAR *  pCursor;

    //
    // Ensure that we don't set the length to
    // something longer than the current buffer
    //

    if ( cchNewLength > QueryBufferSize() - 1 )
    {
        SetLastError( ERROR_INVALID_PARAMETER );
        return FALSE;
    }

    pCursor = QueryStr();

    pCursor[cchNewLength] = '\0';

    _Buffer.SetDataSize( cchNewLength + sizeof(CHAR) );

    return TRUE;
}
VOID
ISAPI_STRINGW::CalcLen(
    VOID
    )
/*++

Purpose:

    Calculates the size in characters of the ISAPI_STRINGW data by
    doing a wcslen.

Arguments:

    None

Returns:

    None

--*/
{
    _Buffer.SetDataSize( wcslen( QueryStr() ) + 1 );
}
BOOL
ISAPI_STRINGW::CopyAToOffset(
    CHAR *  szString,
    DWORD   cchString,
    DWORD   cchOffset
    )
/*++

Purpose:

    Copies an ANSI string into the ISAPI_STRINGW's data, starting at the
    specified offset, in characters.

Arguments:

    szString  - The ANSI string to copy
    cchString - The number of characters to copy
    cchOffset - The offset, in chracters, at which to copy

Returns:

    TRUE on success, FALSE on failure

--*/
{
    WCHAR * pCursor;
    DWORD   cbString;
    DWORD   cchCopied;
    DWORD   cchData;
    DWORD   cbData;

    cchData = QueryCCH();
    cbData = QueryCB();

    //
    // Ensure that we have enough storage available
    //

    cbString = ( cchString + 1 ) * sizeof(WCHAR);

    if ( ResizeBuffer( cbString + cchOffset * sizeof(WCHAR) ) == FALSE )
    {
        goto Failed;
    }

    //
    // Set the point into which the copy will occur
    //

    pCursor = QueryStr() + cchData;

    //
    // Convert the ANSI string into Unicode using
    // the default system code page.
    //

    cchCopied = MultiByteToWideChar( CP_ACP,
                                     MB_PRECOMPOSED,
                                     szString,
                                     cchString,
                                     pCursor,
                                     cbData );

    if ( cchCopied == 0 )
    {
        goto Failed;
    }

    SetLen( cchData + cchCopied );

    return TRUE;

Failed:

    //
    // No special cleanup to be done
    //

    return FALSE;
}
BOOL
ISAPI_STRINGW::vsprintf_s(
    WCHAR * szFormatW,
    va_list args
    )
/*++

Purpose:

    Sets the specified printf-style formatted Unicode string
    into the ISAPI_STRINGW object using a va_list of args.

Arguments:

    szFormat - The Unicode string format
    ...      - Zero or more additional arguments

Returns:

    TRUE on success, FALSE on failure

--*/
{
    INT     cchWritten;
    DWORD   cbBuffer;
    BOOL    fResult;

    //
    // Build the formatted string
    //

    do
    {
        cbBuffer = QueryBufferSize();

        cchWritten = (DWORD)_vsnwprintf_s( QueryStr(),
										sizeof(QueryStr()),
                                         cbBuffer,
                                         szFormatW,
                                         args );

        if ( cchWritten < 0 )
        {
            //
            // If we just failed, and the buffer
            // is already at its maximum size,
            // then fail.
            //

            if ( cbBuffer >= QueryMaxAlloc() )
            {
                goto Failed;
            }

            //
            // Grow the buffer and try again
            //

            cbBuffer *= 2;

            //
            // Don't exceed the max buffer size
            //

            if ( cbBuffer > QueryMaxAlloc() )
            {
                cbBuffer = QueryMaxAlloc();
            }

            fResult = ResizeBuffer( cbBuffer );

            if ( !fResult )
            {
                goto Failed;
            }
        }

    } while ( cchWritten < 0 );

    SetLen( cchWritten );

    return TRUE;

Failed:

    //
    // No special cleanup needed
    //

    return FALSE;
}
CHAR *
ISAPI_STRING::FindStr(
    CHAR *  szSubString,
    BOOL    fCaseInsensitive,
    DWORD   dwStartingOffset
    )
/*++

Purpose:

    Finds the specified sub-string in the ISAPI_STRING's
    data.

Arguments:

    szSubString      - The sub-string to find
    fCaseInsensitive - If TRUE, does a case insensitive search
    dwStartingOffset - The starting offset in the ISAPI_STRING to start search

Returns:

    Pointer to the instance of the sub-string if found, NULL if not found

--*/
{
    CHAR *  pCursor;
    CHAR *  pEnd;
    CHAR *  szFound;
    CHAR *  szString;
    CHAR    c1;
    CHAR    c2;
    DWORD   cchString;
    DWORD   cchSubString;
    DWORD   cchData;
    DWORD   n;

    //
    // If we are trying to search beyond the length
    // of the string, then we aren't going to find it
    //

    cchData = QueryCCH();

    if ( dwStartingOffset > cchData )
    {
        goto NotFound;
    }

    szString = QueryStr() + dwStartingOffset;

    cchString = cchData - dwStartingOffset;

    //
    // If szString is smaller than szSubString, then
    // we aren't going to find it.
    //

    cchSubString = strlen( szSubString );

    if ( cchSubString > cchString )
    {
        goto NotFound;
    }

    //
    // Set boundaries for the search
    //

    pEnd = szString + cchString - cchSubString;

    //
    // Search now
    //

    szFound = NULL;

    for ( pCursor = szString; pCursor <= pEnd; pCursor++ )
    {
        c1 = fCaseInsensitive ? UPCASE_ANSI_CHAR( *pCursor ) : *pCursor;
        c2 = fCaseInsensitive ? UPCASE_ANSI_CHAR( *szSubString ) : *szSubString;

        if ( c1 == c2 )
        {
            szFound = pCursor;

            for ( n = 1; n < cchSubString; n++ )
            {
                c1 = fCaseInsensitive ? UPCASE_ANSI_CHAR( *(pCursor+n) ) : *(pCursor+n);
                c2 = fCaseInsensitive ? UPCASE_ANSI_CHAR( *(szSubString+n) ) : *(szSubString+n);

                if ( c1 != c2 )
                {
                    szFound = NULL;
                    break;
                }
            }

            if ( szFound )
            {
                return szFound;
            }
        }
    }

NotFound:

    SetLastError( ERROR_INVALID_INDEX );

    return NULL;
}
BOOL
ISAPI_STRING::CopyWToOffset(
    WCHAR * szStringW,
    DWORD   cchStringW,
    DWORD   dwOffset
    )
/*++

Purpose:

    Copies a Unicode string into the ISAPI_STRING's data,
    starting at the specified offset.

Arguments:

    szStringW  - The Unicode string to copy
    cchStringW - The number of characters to copy
    dwOffset   - The offset at which to copy

Returns:

    TRUE on success, FALSE on failure

--*/
{
    CHAR *  pCursor;
    DWORD   cbData;
    DWORD   cbCopied;
    DWORD   cchData;

    cchData = QueryCCH();

    //
    // Ensure that we have enough storage available
    //

    cbData = cchStringW + sizeof(CHAR);

    if ( QueryBufferSize() < cbData + dwOffset &&
         ResizeBuffer( cbData + dwOffset ) == FALSE )
    {
        goto Failed;
    }

    //
    // Set the point into which the copy will occur
    //

    pCursor = QueryStr() + cchData;

    //
    // Convert the wide string into ANSI using
    // the default system code page.
    //

    cbCopied = WideCharToMultiByte( CP_ACP,
                                    0,
                                    szStringW,
                                    cchStringW,
                                    pCursor,
                                    cbData,
                                    NULL,
                                    NULL );

    if ( cbCopied == 0 )
    {
        goto Failed;
    }

    SetLen( cchData + cbCopied );

    return TRUE;

Failed:

    //
    // No special cleanup to be done
    //

    return FALSE;
}