/*
 * PE->NE resource conversion functions
 *
 * Copyright 1998 Ulrich Weigand
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <string.h>
#include "windef.h"
#include "wingdi.h"
#include "wine/winuser16.h"
#include "wine/unicode.h"
#include "module.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(resource);

/**********************************************************************
 *	    ConvertDialog32To16   (KERNEL.615)
 */
VOID WINAPI ConvertDialog32To16( LPVOID dialog32, DWORD size, LPVOID dialog16 )
{
    LPVOID p = dialog32;
    WORD nbItems, data, dialogEx;
    DWORD style;

    style = *((DWORD *)dialog16)++ = *((DWORD *)p)++;
    dialogEx = (style == 0xffff0001);  /* DIALOGEX resource */
    if (dialogEx)
    {
        *((DWORD *)dialog16)++ = *((DWORD *)p)++; /* helpID */
        *((DWORD *)dialog16)++ = *((DWORD *)p)++; /* exStyle */
        style = *((DWORD *)dialog16)++ = *((DWORD *)p)++; /* style */
    }
    else
        ((DWORD *)p)++; /* exStyle ignored in 16-bit standard dialog */

    nbItems = *((BYTE *)dialog16)++ = (BYTE)*((WORD *)p)++;
    *((WORD *)dialog16)++ = *((WORD *)p)++; /* x */
    *((WORD *)dialog16)++ = *((WORD *)p)++; /* y */
    *((WORD *)dialog16)++ = *((WORD *)p)++; /* cx */
    *((WORD *)dialog16)++ = *((WORD *)p)++; /* cy */

    /* Transfer menu name */
    switch (*((WORD *)p))
    {
    case 0x0000:  ((WORD *)p)++; *((BYTE *)dialog16)++ = 0; break;
    case 0xffff:  ((WORD *)p)++; *((BYTE *)dialog16)++ = 0xff;
                  *((WORD *)dialog16)++ = *((WORD *)p)++; break;
    default:      WideCharToMultiByte( CP_ACP, 0, (LPWSTR)p, -1, (LPSTR)dialog16, 0x7fffffff, NULL,NULL );
                  ((LPSTR)dialog16) += strlen( (LPSTR)dialog16 ) + 1;
                  ((LPWSTR)p) += strlenW( (LPWSTR)p ) + 1;
                  break;
    }

    /* Transfer class name */
    switch (*((WORD *)p))
    {
    case 0x0000:  ((WORD *)p)++; *((BYTE *)dialog16)++ = 0; break;
    case 0xffff:  ((WORD *)p)++; *((BYTE *)dialog16)++ = 0xff;
                  *((WORD *)dialog16)++ = *((WORD *)p)++; break;
    default:      WideCharToMultiByte( CP_ACP, 0, (LPWSTR)p, -1, (LPSTR)dialog16, 0x7fffffff, NULL,NULL );
                  ((LPSTR)dialog16) += strlen( (LPSTR)dialog16 ) + 1;
                  ((LPWSTR)p) += strlenW( (LPWSTR)p ) + 1;
                  break;
    }

    /* Transfer window caption */
    WideCharToMultiByte( CP_ACP, 0, (LPWSTR)p, -1, (LPSTR)dialog16, 0x7fffffff, NULL,NULL );
    ((LPSTR)dialog16) += strlen( (LPSTR)dialog16 ) + 1;
    ((LPWSTR)p) += strlenW( (LPWSTR)p ) + 1;

    /* Transfer font info */
    if (style & DS_SETFONT)
    {
        *((WORD *)dialog16)++ = *((WORD *)p)++;  /* pointSize */
        if (dialogEx)
        {
            *((WORD *)dialog16)++ = *((WORD *)p)++; /* weight */
            *((WORD *)dialog16)++ = *((WORD *)p)++; /* italic */
        }
        WideCharToMultiByte( CP_ACP, 0, (LPWSTR)p, -1, (LPSTR)dialog16, 0x7fffffff, NULL,NULL );  /* faceName */
        ((LPSTR)dialog16) += strlen( (LPSTR)dialog16 ) + 1;
        ((LPWSTR)p) += strlenW( (LPWSTR)p ) + 1;
    }

    /* Transfer dialog items */
    while (nbItems)
    {
        /* align on DWORD boundary (32-bit only) */
        p = (LPVOID)((((int)p) + 3) & ~3);

        if (dialogEx)
        {
            *((DWORD *)dialog16)++ = *((DWORD *)p)++; /* helpID */
            *((DWORD *)dialog16)++ = *((DWORD *)p)++; /* exStyle */
            *((DWORD *)dialog16)++ = *((DWORD *)p)++; /* style */
        }
        else
        {
            style = *((DWORD *)p)++; /* save style */
            ((DWORD *)p)++;          /* ignore exStyle */
        }

        *((WORD *)dialog16)++ = *((WORD *)p)++; /* x */
        *((WORD *)dialog16)++ = *((WORD *)p)++; /* y */
        *((WORD *)dialog16)++ = *((WORD *)p)++; /* cx */
        *((WORD *)dialog16)++ = *((WORD *)p)++; /* cy */

        if (dialogEx)
            *((DWORD *)dialog16)++ = *((DWORD *)p)++; /* ID */
        else
        {
            *((WORD *)dialog16)++ = *((WORD *)p)++; /* ID */
            *((DWORD *)dialog16)++ = style;  /* style from above */
        }

        /* Transfer class name */
        switch (*((WORD *)p))
        {
        case 0x0000:  ((WORD *)p)++; *((BYTE *)dialog16)++ = 0; break;
        case 0xffff:  ((WORD *)p)++;
                      *((BYTE *)dialog16)++ = (BYTE)*((WORD *)p)++; break;
        default:      WideCharToMultiByte( CP_ACP, 0, (LPWSTR)p, -1, (LPSTR)dialog16, 0x7fffffff, NULL,NULL );
                      ((LPSTR)dialog16) += strlen( (LPSTR)dialog16 ) + 1;
                      ((LPWSTR)p) += strlenW( (LPWSTR)p ) + 1;
                      break;
        }

        /* Transfer window name */
        switch (*((WORD *)p))
        {
        case 0x0000:  ((WORD *)p)++; *((BYTE *)dialog16)++ = 0; break;
        case 0xffff:  ((WORD *)p)++; *((BYTE *)dialog16)++ = 0xff;
                      *((WORD *)dialog16)++ = *((WORD *)p)++; break;
        default:      WideCharToMultiByte( CP_ACP, 0, (LPWSTR)p, -1, (LPSTR)dialog16, 0x7fffffff, NULL,NULL );
                      ((LPSTR)dialog16) += strlen( (LPSTR)dialog16 ) + 1;
                      ((LPWSTR)p) += strlenW( (LPWSTR)p ) + 1;
                      break;
        }

        /* Transfer data */
        data = *((WORD *)p)++;
        if (dialogEx)
            *((WORD *)dialog16)++ = data;
        else
            *((BYTE *)dialog16)++ = (BYTE)data;

        if (data)
        {
            memcpy( dialog16, p, data );
            (LPSTR)dialog16 += data;
            (LPSTR)p += data;
        }

        /* Next item */
        nbItems--;
    }
}

/**********************************************************************
 *	    GetDialog32Size   (KERNEL.618)
 */
WORD WINAPI GetDialog32Size16( LPVOID dialog32 )
{
    LPVOID p = dialog32;
    WORD nbItems, data, dialogEx;
    DWORD style;

    style = *((DWORD *)p)++;
    dialogEx = (style == 0xffff0001);  /* DIALOGEX resource */
    if (dialogEx)
    {
        ((DWORD *)p)++; /* helpID */
        ((DWORD *)p)++; /* exStyle */
        style = *((DWORD *)p)++; /* style */
    }
    else
        ((DWORD *)p)++; /* exStyle */

    nbItems = *((WORD *)p)++;
    ((WORD *)p)++; /* x */
    ((WORD *)p)++; /* y */
    ((WORD *)p)++; /* cx */
    ((WORD *)p)++; /* cy */

    /* Skip menu name */
    switch (*((WORD *)p))
    {
    case 0x0000:  ((WORD *)p)++; break;
    case 0xffff:  ((WORD *)p) += 2; break;
    default:      ((LPWSTR)p) += strlenW( (LPWSTR)p ) + 1; break;
    }

    /* Skip class name */
    switch (*((WORD *)p))
    {
    case 0x0000:  ((WORD *)p)++; break;
    case 0xffff:  ((WORD *)p) += 2; break;
    default:      ((LPWSTR)p) += strlenW( (LPWSTR)p ) + 1; break;
    }

    /* Skip window caption */
    ((LPWSTR)p) += strlenW( (LPWSTR)p ) + 1;

    /* Skip font info */
    if (style & DS_SETFONT)
    {
        ((WORD *)p)++;  /* pointSize */
        if (dialogEx)
        {
            ((WORD *)p)++; /* weight */
            ((WORD *)p)++; /* italic */
        }
        ((LPWSTR)p) += strlenW( (LPWSTR)p ) + 1;  /* faceName */
    }

    /* Skip dialog items */
    while (nbItems)
    {
        /* align on DWORD boundary */
        p = (LPVOID)((((int)p) + 3) & ~3);

        if (dialogEx)
        {
            ((DWORD *)p)++; /* helpID */
            ((DWORD *)p)++; /* exStyle */
            ((DWORD *)p)++; /* style */
        }
        else
        {
            ((DWORD *)p)++; /* style */
            ((DWORD *)p)++; /* exStyle */
        }

        ((WORD *)p)++; /* x */
        ((WORD *)p)++; /* y */
        ((WORD *)p)++; /* cx */
        ((WORD *)p)++; /* cy */

        if (dialogEx)
            ((DWORD *)p)++; /* ID */
        else
            ((WORD *)p)++; /* ID */

        /* Skip class name */
        switch (*((WORD *)p))
        {
        case 0x0000:  ((WORD *)p)++; break;
        case 0xffff:  ((WORD *)p) += 2; break;
        default:      ((LPWSTR)p) += strlenW( (LPWSTR)p ) + 1; break;
        }

        /* Skip window name */
        switch (*((WORD *)p))
        {
        case 0x0000:  ((WORD *)p)++; break;
        case 0xffff:  ((WORD *)p) += 2; break;
        default:      ((LPWSTR)p) += strlenW( (LPWSTR)p ) + 1; break;
        }

        /* Skip data */
        data = *((WORD *)p)++;
        (LPSTR)p += data;

        /* Next item */
        nbItems--;
    }

    return (WORD)((LPSTR)p - (LPSTR)dialog32);
}

/**********************************************************************
 *	    ConvertMenu32To16   (KERNEL.616)
 */
VOID WINAPI ConvertMenu32To16( LPVOID menu32, DWORD size, LPVOID menu16 )
{
    LPVOID p = menu32;
    WORD version, headersize, flags, level = 1;

    version = *((WORD *)menu16)++ = *((WORD *)p)++;
    headersize = *((WORD *)menu16)++ = *((WORD *)p)++;
    if ( headersize )
    {
        memcpy( menu16, p, headersize );
        ((LPSTR)menu16) += headersize;
        ((LPSTR)p) += headersize;
    }

    while ( level )
        if ( version == 0 )  /* standard */
        {
            flags = *((WORD *)menu16)++ = *((WORD *)p)++;
            if ( !(flags & MF_POPUP) )
                *((WORD *)menu16)++ = *((WORD *)p)++;  /* ID */
            else
                level++;

            WideCharToMultiByte( CP_ACP, 0, (LPWSTR)p, -1, (LPSTR)menu16, 0x7fffffff, NULL,NULL );
            ((LPSTR)menu16) += strlen( (LPSTR)menu16 ) + 1;
            ((LPWSTR)p) += strlenW( (LPWSTR)p ) + 1;

            if ( flags & MF_END )
                level--;
        }
        else  /* extended */
        {
            *((DWORD *)menu16)++ = *((DWORD *)p)++;  /* fType */
            *((DWORD *)menu16)++ = *((DWORD *)p)++;  /* fState */
            *((WORD *)menu16)++ = (WORD)*((DWORD *)p)++; /* ID */
            flags = *((BYTE *)menu16)++ = (BYTE)*((WORD *)p)++;

            WideCharToMultiByte( CP_ACP, 0, (LPWSTR)p, -1, (LPSTR)menu16, 0x7fffffff, NULL,NULL );
            ((LPSTR)menu16) += strlen( (LPSTR)menu16 ) + 1;
            ((LPWSTR)p) += strlenW( (LPWSTR)p ) + 1;

            /* align on DWORD boundary (32-bit only) */
            p = (LPVOID)((((int)p) + 3) & ~3);

            /* If popup, transfer helpid */
            if ( flags & 1)
            {
                *((DWORD *)menu16)++ = *((DWORD *)p)++;
                level++;
            }

            if ( flags & MF_END )
                level--;
        }
}

/**********************************************************************
 *	    GetMenu32Size   (KERNEL.617)
 */
WORD WINAPI GetMenu32Size16( LPVOID menu32 )
{
    LPVOID p = menu32;
    WORD version, headersize, flags, level = 1;

    version = *((WORD *)p)++;
    headersize = *((WORD *)p)++;
    ((LPSTR)p) += headersize;

    while ( level )
        if ( version == 0 )  /* standard */
        {
            flags = *((WORD *)p)++;
            if ( !(flags & MF_POPUP) )
                ((WORD *)p)++;  /* ID */
            else
                level++;

            ((LPWSTR)p) += strlenW( (LPWSTR)p ) + 1;

            if ( flags & MF_END )
                level--;
        }
        else  /* extended */
        {
            ((DWORD *)p)++; /* fType */
            ((DWORD *)p)++; /* fState */
            ((DWORD *)p)++; /* ID */
            flags = *((WORD *)p)++;

            ((LPWSTR)p) += strlenW( (LPWSTR)p ) + 1;

            /* align on DWORD boundary (32-bit only) */
            p = (LPVOID)((((int)p) + 3) & ~3);

            /* If popup, skip helpid */
            if ( flags & 1)
            {
                ((DWORD *)p)++;
                level++;
            }

            if ( flags & MF_END )
                level--;
        }

    return (WORD)((LPSTR)p - (LPSTR)menu32);
}

/**********************************************************************
 *	    ConvertAccelerator32To16
 */
VOID ConvertAccelerator32To16( LPVOID acc32, DWORD size, LPVOID acc16 )
{
    int type;

    do
    {
        /* Copy type */
        type = *((BYTE *)acc16)++ = *((BYTE *)acc32)++;
        /* Skip padding */
        ((BYTE *)acc32)++;
        /* Copy event and IDval */
        *((WORD *)acc16)++ = *((WORD *)acc32)++;
        *((WORD *)acc16)++ = *((WORD *)acc32)++;
        /* Skip padding */
        ((WORD *)acc32)++;

    } while ( !( type & 0x80 ) );
}

/**********************************************************************
 *	    NE_LoadPEResource
 */
HGLOBAL16 NE_LoadPEResource( NE_MODULE *pModule, WORD type, LPVOID bits, DWORD size )
{
    HGLOBAL16 handle;

    TRACE("module=%04x type=%04x\n", pModule->self, type );
    if (!pModule || !bits || !size) return 0;

    handle = GlobalAlloc16( 0, size );

    switch (type)
    {
    case RT_MENU16:
        ConvertMenu32To16( bits, size, GlobalLock16( handle ) );
        break;

    case RT_DIALOG16:
        ConvertDialog32To16( bits, size, GlobalLock16( handle ) );
        break;

    case RT_ACCELERATOR16:
        ConvertAccelerator32To16( bits, size, GlobalLock16( handle ) );
        break;

    case RT_STRING16:
        FIXME("not yet implemented!\n" );
        /* fall through */

    default:
        memcpy( GlobalLock16( handle ), bits, size );
        break;
    }

    return handle;
}
