/*
 * Built-in modules
 *
 * Copyright 1996 Alexandre Julliard
 *
 * 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 "config.h"
#include "wine/port.h"

#include <assert.h>
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include "winbase.h"
#include "wine/winbase16.h"
#include "builtin16.h"
#include "global.h"
#include "file.h"
#include "module.h"
#include "miscemu.h"
#include "stackframe.h"
#include "wine/library.h"
#include "wine/debug.h"
#include "toolhelp.h"

WINE_DEFAULT_DEBUG_CHANNEL(module);

typedef struct
{
    void       *module_start;      /* 32-bit address of the module data */
    int         module_size;       /* Size of the module data */
    void       *code_start;        /* 32-bit address of DLL code */
    void       *data_start;        /* 32-bit address of DLL data */
    const char *owner;             /* 32-bit dll that contains this dll */
    const void *rsrc;              /* resources data */
} BUILTIN16_DESCRIPTOR;

/* Table of all built-in DLLs */

#define MAX_DLLS 50

static const BUILTIN16_DESCRIPTOR *builtin_dlls[MAX_DLLS];


/* patch all the flat cs references of the code segment if necessary */
inline static void patch_code_segment( void *code_segment )
{
#ifdef __i386__
    CALLFROM16 *call = code_segment;
    if (call->flatcs == wine_get_cs()) return;  /* nothing to patch */
    while (call->pushl == 0x68)
    {
        call->flatcs = wine_get_cs();
        call++;
    }
#endif
}


/***********************************************************************
 *           BUILTIN_DoLoadModule16
 *
 * Load a built-in Win16 module. Helper function for BUILTIN_LoadModule.
 */
static HMODULE16 BUILTIN_DoLoadModule16( const BUILTIN16_DESCRIPTOR *descr )
{
    NE_MODULE *pModule;
    int minsize;
    SEGTABLEENTRY *pSegTable;
    HMODULE16 hModule;

    hModule = GLOBAL_CreateBlock( GMEM_MOVEABLE, descr->module_start,
                                  descr->module_size, 0, WINE_LDT_FLAGS_DATA );
    if (!hModule) return 0;
    FarSetOwner16( hModule, hModule );

    pModule = (NE_MODULE *)GlobalLock16( hModule );
    pModule->self = hModule;
    /* NOTE: (Ab)use the hRsrcMap parameter for resource data pointer */
    pModule->hRsrcMap = (void *)descr->rsrc;

    /* Allocate the code segment */

    pSegTable = NE_SEG_TABLE( pModule );
    pSegTable->hSeg = GLOBAL_CreateBlock( GMEM_FIXED, descr->code_start,
                                          pSegTable->minsize, hModule,
                                          WINE_LDT_FLAGS_CODE|WINE_LDT_FLAGS_32BIT );
    if (!pSegTable->hSeg) return 0;
    patch_code_segment( descr->code_start );
    pSegTable++;

    /* Allocate the data segment */

    minsize = pSegTable->minsize ? pSegTable->minsize : 0x10000;
    minsize += pModule->heap_size;
    if (minsize > 0x10000) minsize = 0x10000;
    pSegTable->hSeg = GlobalAlloc16( GMEM_FIXED, minsize );
    if (!pSegTable->hSeg) return 0;
    FarSetOwner16( pSegTable->hSeg, hModule );
    if (pSegTable->minsize) memcpy( GlobalLock16( pSegTable->hSeg ),
                                    descr->data_start, pSegTable->minsize);
    if (pModule->heap_size)
        LocalInit16( GlobalHandleToSel16(pSegTable->hSeg),
		pSegTable->minsize, minsize );

    if (descr->rsrc) NE_InitResourceHandler(hModule);

    NE_RegisterModule( pModule );

    /* make sure the 32-bit library containing this one is loaded too */
    LoadLibraryA( descr->owner );

    return hModule;
}


/***********************************************************************
 *           find_dll_descr
 *
 * Find a descriptor in the list
 */
static const BUILTIN16_DESCRIPTOR *find_dll_descr( const char *dllname )
{
    int i;
    for (i = 0; i < MAX_DLLS; i++)
    {
        const BUILTIN16_DESCRIPTOR *descr = builtin_dlls[i];
        if (descr)
        {
            NE_MODULE *pModule = (NE_MODULE *)descr->module_start;
            OFSTRUCT *pOfs = (OFSTRUCT *)((LPBYTE)pModule + pModule->fileinfo);
            BYTE *name_table = (BYTE *)pModule + pModule->name_table;

            /* check the dll file name */
            if (!FILE_strcasecmp( pOfs->szPathName, dllname )) return descr;
            /* check the dll module name (without extension) */
            if (!FILE_strncasecmp( dllname, name_table+1, *name_table ) &&
                !strcmp( dllname + *name_table, ".dll" ))
                return descr;
        }
    }
    return NULL;
}


/***********************************************************************
 *           BUILTIN_IsPresent
 *
 * Check if a builtin dll descriptor is present (because we loaded its 32-bit counterpart).
 */
BOOL BUILTIN_IsPresent( LPCSTR name )
{
    char dllname[20], *p;

    if (strlen(name) >= sizeof(dllname)-4) return FALSE;
    strcpy( dllname, name );
    p = strrchr( dllname, '.' );
    if (!p) strcat( dllname, ".dll" );
    for (p = dllname; *p; p++) *p = FILE_tolower(*p);

    return (find_dll_descr( dllname ) != NULL);
}


/***********************************************************************
 *           BUILTIN_LoadModule
 *
 * Load a built-in module.
 */
HMODULE16 BUILTIN_LoadModule( LPCSTR name )
{
    const BUILTIN16_DESCRIPTOR *descr;
    char dllname[20], *p;
    void *handle;

    /* Fix the name in case we have a full path and extension */

    if ((p = strrchr( name, '\\' ))) name = p + 1;
    if ((p = strrchr( name, '/' ))) name = p + 1;

    if (strlen(name) >= sizeof(dllname)-4) return (HMODULE16)2;

    strcpy( dllname, name );
    p = strrchr( dllname, '.' );
    if (!p) strcat( dllname, ".dll" );
    for (p = dllname; *p; p++) *p = FILE_tolower(*p);

    if ((descr = find_dll_descr( dllname )))
        return BUILTIN_DoLoadModule16( descr );

    if ((handle = BUILTIN32_dlopen( dllname )))
    {
        if ((descr = find_dll_descr( dllname )))
            return BUILTIN_DoLoadModule16( descr );

        ERR( "loaded .so but dll %s still not found\n", dllname );
        BUILTIN32_dlclose( handle );
    }

    return (HMODULE16)2;
}


/***********************************************************************
 *           __wine_register_dll_16 (KERNEL32.@)
 *
 * Register a built-in DLL descriptor.
 */
void __wine_register_dll_16( const BUILTIN16_DESCRIPTOR *descr )
{
    int i;

    for (i = 0; i < MAX_DLLS; i++)
    {
        if (builtin_dlls[i]) continue;
        builtin_dlls[i] = descr;
        break;
    }
    assert( i < MAX_DLLS );
}


/***********************************************************************
 *           __wine_unregister_dll_16 (KERNEL32.@)
 *
 * Unregister a built-in DLL descriptor.
 */
void __wine_unregister_dll_16( const BUILTIN16_DESCRIPTOR *descr )
{
    int i;

    for (i = 0; i < MAX_DLLS; i++)
    {
        if (builtin_dlls[i] != descr) continue;
        builtin_dlls[i] = NULL;
        break;
    }
}