/*
 *        Module & Library functions
 */
static char Copyright[] = "Copyright 1993, 1994 Martin Ayotte, Robert J. Amstadt, Erik Bos";

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "neexe.h"
#include "segmem.h"
#include "dlls.h"
#include "wine.h"
#include "wineopts.h"
#include "arch.h"
#include "options.h"
#include "prototypes.h"
#include "windows.h"
#include "task.h"
#include "toolhelp.h"
#include "stddebug.h"
/* #define DEBUG_MODULE */
/* #undef DEBUG_MODULE  */
#include "debug.h"

extern char WindowsPath[256];
extern struct dll_name_table_entry_s dll_builtin_table[];
extern char *GetDosFileName(char *);

extern HANDLE hSysRes;

struct w_files *wine_files = NULL;
static char *DLL_Extensions[] = { "dll", NULL };
static char *EXE_Extensions[] = { "exe", NULL };

#define IS_BUILTIN_DLL(handle) ((handle >> 8) == 0xff) 

/**********************************************************************/

void ExtractDLLName(char *libname, char *temp)
{
    int i;
    
    strcpy(temp, libname);
    if (strchr(temp, '\\') || strchr(temp, '/'))
	for (i = strlen(temp) - 1; i ; i--) 
		if (temp[i] == '\\' || temp[i] == '/') {
			strcpy(temp, temp + i + 1);
			break;
		}
    for (i = strlen(temp) - 1; i ; i--) 
	if (temp[i] == '.') {
		temp[i] = 0;
		break;
	}
}

struct w_files *
GetFileInfo(unsigned short instance)
{
    register struct w_files *w = wine_files;

    while (w && w->hinstance != instance)
	w = w->next;
    
    return w;
}

int IsDLLLoaded(char *name)
{
	struct w_files *wpnt;

	if(FindDLLTable(name))
		return 1;

	for(wpnt = wine_files; wpnt; wpnt = wpnt->next)
		if(strcmp(wpnt->name, name) == 0)
			return 1;

	return 0;
}

void InitDLL(struct w_files *wpnt)
{
	if (wpnt->ne) 
		InitNEDLL(wpnt);
	else
		InitPEDLL(wpnt);
}

void InitializeLoadedDLLs(struct w_files *wpnt)
{
    static flagReadyToRun = 0;
    struct w_files *final_wpnt;

    dprintf_module(stddeb,"InitializeLoadedDLLs(%p)\n", wpnt);

    if (wpnt == NULL)
    {
	flagReadyToRun = 1;
	dprintf_module(stddeb,"Initializing DLLs\n");
    }
    
    if (!flagReadyToRun)
	return;

#if 1
    if (wpnt != NULL)
	dprintf_module(stddeb,"Initializing %s\n", wpnt->name);
#endif

    /*
     * Initialize libraries
     */
    if (!wpnt)
    {
	wpnt = wine_files;
	final_wpnt = NULL;
    }
    else
    {
	final_wpnt = wpnt->next;
    }
    
    for( ; wpnt != final_wpnt; wpnt = wpnt->next)
	InitDLL(wpnt);
}

/**********************************************************************
 *			LoadImage
 * Load one executable into memory
 */
HINSTANCE LoadImage(char *module, int filetype, int change_dir)
{
    HINSTANCE handle;
    struct w_files *wpnt, *wpnt1;
    char buffer[256], header[2], modulename[64], *fullname;

    ExtractDLLName(module, modulename);
    dprintf_module(stddeb,"LoadImage [%s]\n", module);
    /* built-in one ? */
    if (FindDLLTable(modulename)) {
	return GetModuleHandle(modulename);
    }
    
    /* already loaded ? */
    for (wpnt = wine_files ; wpnt ; wpnt = wpnt->next)
    	if (strcasecmp(wpnt->name, modulename) == 0)
    		return wpnt->hinstance;

    /*
     * search file
     */
    fullname = FindFile(buffer, sizeof(buffer), module, 
			(filetype == EXE ? EXE_Extensions : DLL_Extensions), 
			WindowsPath);
    if (fullname == NULL)
    {
    	fprintf(stderr, "LoadImage: I can't find %s.dll | %s.exe !\n",
		module, module);
	return 2;
    }

    fullname = GetDosFileName(fullname);
    
    dprintf_module(stddeb,"LoadImage: loading %s (%s)\n           [%s]\n", 
	    module, buffer, fullname);

    if (change_dir && fullname)
    {
	char dirname[256];
	char *p;

	strcpy(dirname, fullname);
	p = strrchr(dirname, '\\');
	*p = '\0';

	DOS_SetDefaultDrive(dirname[0] - 'A');
	DOS_ChangeDir(dirname[0] - 'A', dirname + 2);
    }

    /* First allocate a spot to store the info we collect, and add it to
     * our linked list if we could load the file.
     */

    wpnt = (struct w_files *) malloc(sizeof(struct w_files));

    /*
     * Open file for reading.
     */
    wpnt->fd = open(buffer, O_RDONLY);
    if (wpnt->fd < 0)
	return 2;

    /* 
     * Establish header pointers.
     */
    wpnt->filename = strdup(buffer);
    wpnt->name = strdup(modulename);

    /* read mz header */
    wpnt->mz_header = (struct mz_header_s *) malloc(sizeof(struct mz_header_s));;
    lseek(wpnt->fd, 0, SEEK_SET);
    if (read(wpnt->fd, wpnt->mz_header, sizeof(struct mz_header_s)) !=
	sizeof(struct mz_header_s))
    {
	myerror("Unable to read MZ header from file");
    }

      /* This field is ignored according to "Windows Internals", p.242 */
#if 0
    if (wpnt->mz_header->must_be_0x40 != 0x40)
	myerror("This is not a Windows program");
#endif

    /* read first two bytes to determine filetype */
    lseek(wpnt->fd, wpnt->mz_header->ne_offset, SEEK_SET);
    read(wpnt->fd, &header, sizeof(header));

    handle = 0;

    /* 
     * Stick this file into the list of loaded files so we don't try to reload
     * it again if another module references this module.  Do this before
     * calling loadNEImage because we might get back here before loadNEImage
     * returns.
     */
    if(wine_files == NULL)
       wine_files = wpnt;
    else {
	 wpnt1 = wine_files;
	 while(wpnt1->next)
	    wpnt1 = wpnt1->next;
	 wpnt1->next = wpnt;
    }
    wpnt->next = NULL;

    if (header[0] == 'N' && header[1] == 'E')
    	handle = LoadNEImage(wpnt);
    if (header[0] == 'P' && header[1] == 'E')
        handle = LoadPEImage(wpnt);
    wpnt->hinstance = handle;

    if (handle > 32) {
	return handle;
    } else {
	fprintf(stderr, "wine: (%s) unknown fileformat !\n", wpnt->filename);

	/* Remove this module from the list of loaded modules */
	if (wine_files == wpnt)
	    wine_files = NULL;
	else
	    wpnt1->next = NULL;
	close(wpnt->fd);
	free(wpnt->filename);
	free(wpnt->name);
	free(wpnt);

	return 14;
    }
}

/**********************************************************************
 *				GetModuleHandle	[KERNEL.47]
 */
HANDLE GetModuleHandle(LPSTR lpModuleName)
{
	register struct w_files *w = wine_files;
	int 	i;
	char dllname[256];

	if ((int) lpModuleName & 0xffff0000)
		ExtractDLLName(lpModuleName, dllname);

	if ((int) lpModuleName & 0xffff0000)
	 	dprintf_module(stddeb,"GetModuleHandle('%s');\n", lpModuleName);
	else
	 	dprintf_module(stddeb,"GetModuleHandle('%p');\n", lpModuleName);

/* 	dprintf_module(stddeb,"GetModuleHandle // searching in builtin libraries\n");*/
	for (i = 0; i < N_BUILTINS; i++) {
		if (dll_builtin_table[i].dll_name == NULL) break;
		if (((int) lpModuleName & 0xffff0000) == 0) {
			if (0xFF00 + i == (int) lpModuleName) {
				dprintf_module(stddeb,"GetModuleHandle('%s') return %04X \n",
				       lpModuleName, 0xff00 + i);
				return 0xFF00 + i;
				}
			}
		else if (strcasecmp(dll_builtin_table[i].dll_name, dllname) == 0) {
			dprintf_module(stddeb,"GetModuleHandle('%p') return %04X \n", 
							lpModuleName, 0xFF00 + i);
			return (0xFF00 + i);
			}
		}

 	dprintf_module(stddeb,"GetModuleHandle // searching in loaded modules\n");
	while (w) {
/*		dprintf_module(stddeb,"GetModuleHandle // '%x' \n", w->name);  */
		if (((int) lpModuleName & 0xffff0000) == 0) {
			if (w->hinstance == (int) lpModuleName) {
				dprintf_module(stddeb,"GetModuleHandle('%p') return %04X \n",
				       lpModuleName, w->hinstance);
				return w->hinstance;
				}
			}
		else if (strcasecmp(w->name, dllname) == 0) {
			dprintf_module(stddeb,"GetModuleHandle('%s') return %04X \n", 
							lpModuleName, w->hinstance);
			return w->hinstance;
			}
		w = w->next;
		}
	printf("GetModuleHandle('%p') not found !\n", lpModuleName);
	return 0;
}


/**********************************************************************
 *				GetModuleUsage	[KERNEL.48]
 */
int GetModuleUsage(HANDLE hModule)
{
	struct w_files *w;

	dprintf_module(stddeb,"GetModuleUsage(%04X);\n", hModule);

	/* built-in dll ? */
	if (IS_BUILTIN_DLL(hModule)) 
		return 2;
		
	w = GetFileInfo(hModule);
/*	return w->Usage; */
	return 1;
}


/**********************************************************************
 *				GetModuleFilename [KERNEL.49]
 */
int GetModuleFileName(HANDLE hModule, LPSTR lpFileName, short nSize)
{
    struct w_files *w;
    LPSTR str;
    char windir[256], temp[256];

    dprintf_module(stddeb,"GetModuleFileName(%04X, %p, %d);\n", hModule, lpFileName, nSize);

    if (lpFileName == NULL) return 0;
    if (nSize < 1) return 0;

    /* built-in dll ? */
    if (IS_BUILTIN_DLL(hModule)) {
	GetWindowsDirectory(windir, sizeof(windir));
	sprintf(temp, "%s\\%s.DLL", windir, dll_builtin_table[hModule & 0x00ff].dll_name);
	ToDos(temp);
	strncpy(lpFileName, temp, nSize);
        dprintf_module(stddeb,"GetModuleFileName copied '%s' (internal dll) return %d \n", lpFileName, nSize);
	return strlen(lpFileName);
    }

    /* check loaded dlls */
    if ((w = GetFileInfo(hModule)) == NULL)
    	return 0;
    str = GetDosFileName(w->filename);
    if (nSize > strlen(str)) nSize = strlen(str) + 1;
    strncpy(lpFileName, str, nSize);
    dprintf_module(stddeb,"GetModuleFileName copied '%s' return %d \n", lpFileName, nSize);
    return nSize - 1;
}


/**********************************************************************
 *				LoadLibrary	[KERNEL.95]
 */
HANDLE LoadLibrary(LPSTR libname)
{
	HANDLE h;
	
	if ((h = LoadImage(libname, DLL, 0)) < 32)
		return h;

	if (!IS_BUILTIN_DLL(h))
		InitDLL(GetFileInfo(h));
	
	return h;
}

/**********************************************************************
 *				FreeLibrary	[KERNEL.96]
 */
void FreeLibrary(HANDLE hLib)
{
	dprintf_module(stddeb,"FreeLibrary(%04X);\n", hLib);

	/* built-in dll ? */
	if (IS_BUILTIN_DLL(hLib) || hLib == 0 || hLib == hSysRes) 
	    	return;

/*
	while (lpMod != NULL) {
		if (lpMod->hInst == hLib) {
			if (lpMod->Count == 1) {
				wpnt = GetFileInfo(hLib);
				if (wpnt->ne)
					NEunloadImage(wpnt);
				else
					PEunloadImage(wpnt);
				if (hLib != (HANDLE)NULL) GlobalFree(hLib);
				if (lpMod->ModuleName != NULL) free(lpMod->ModuleName);
				if (lpMod->FileName != NULL) free(lpMod->FileName);
				GlobalFree(lpMod->hModule);
				dprintf_module(stddeb,"FreeLibrary // freed !\n");
				return;
				}
			lpMod->Count--;
			dprintf_module(stddeb,"FreeLibrary // Count decremented !\n");
			return;
			}
		lpMod = lpMod->lpNextModule;
		}
*/
}


/**********************************************************************
 *					GetProcAddress	[KERNEL.50]
 */
FARPROC GetProcAddress(HANDLE hModule, char *proc_name)
{
#ifdef WINELIB
    WINELIB_UNIMP ("GetProcAddress");
#else
    int		sel, addr, ret;
    register struct w_files *w = wine_files;
    int 	ordinal, len;
    char 	* cpnt;
    char	C[128];
    HTASK	hTask;
    LPTASKENTRY lpTask;

    /* built-in dll ? */
    if (IS_BUILTIN_DLL(hModule))
    {
	if ((int) proc_name & 0xffff0000) 
	{
	    dprintf_module(stddeb,"GetProcAddress: builtin %#04X, '%s'\n", 
		   hModule, proc_name);
	    if (GetEntryDLLName(dll_builtin_table[hModule - 0xFF00].dll_name,
				proc_name, &sel, &addr)) 
	    {
		printf("Address not found !\n");
	    }
	}
	else 
	{
	    dprintf_module(stddeb,"GetProcAddress: builtin %#04X, %d\n", 
		   hModule, (int)proc_name);
	    if (GetEntryDLLOrdinal(dll_builtin_table[hModule-0xFF00].dll_name,
				   (int)proc_name & 0x0000FFFF, &sel, &addr)) 
	    {
		printf("Address not found !\n");
	    }
	}
	ret = MAKELONG(addr, sel);
	dprintf_module(stddeb,"GetProcAddress // ret=%08X sel=%04X addr=%04X\n", 
	       ret, sel, addr);
	return (FARPROC)ret;
    }
    if (hModule == 0) 
    {
	hTask = GetCurrentTask();
	dprintf_module(stddeb,"GetProcAddress // GetCurrentTask()=%04X\n", hTask);
	lpTask = (LPTASKENTRY) GlobalLock(hTask);
	if (lpTask == NULL) 
	{
	    printf("GetProcAddress: can't find current module handle !\n");
	    return NULL;
	}
	hModule = lpTask->hInst;
	dprintf_module(stddeb,"GetProcAddress: current module=%04X instance=%04X!\n", 
	       lpTask->hModule, lpTask->hInst);
	GlobalUnlock(hTask);
    }
    while (w && w->hinstance != hModule) 
	w = w->next;
    if (w == NULL) 
	return NULL;
    dprintf_module(stddeb,"GetProcAddress // Module Found ! w->filename='%s'\n", w->filename);
    if ((int)proc_name & 0xFFFF0000) 
    {
	AnsiUpper(proc_name);
	dprintf_module(stddeb,"GetProcAddress: %04X, '%s'\n", hModule, proc_name);
	cpnt = w->ne->nrname_table;
	while(TRUE) 
	{
	    if (((int) cpnt)  - ((int)w->ne->nrname_table) >  
		w->ne->ne_header->nrname_tab_length)  return NULL;
	    len = *cpnt++;
	    strncpy(C, cpnt, len);
	    C[len] = '\0';
	    dprintf_module(stddeb,"pointing Function '%s' ordinal=%d !\n", 
		   C, *((unsigned short *)(cpnt +  len)));
	    if (strncmp(cpnt, proc_name, len) ==  0) 
	    {
		ordinal =  *((unsigned short *)(cpnt +  len));
		break;
	    }
	    cpnt += len + 2;
	}
	if (ordinal == 0) 
	{
	    printf("GetProcAddress // function '%s' not found !\n", proc_name);
	    return NULL;
	}
    }
    else 
    {
	dprintf_module(stddeb,"GetProcAddress: %#04x, %d\n", hModule, (int) proc_name);
	ordinal = (int)proc_name;
    }
    ret = GetEntryPointFromOrdinal(w, ordinal);
    if (ret == -1) 
    {
	printf("GetProcAddress // Function #%d not found !\n", ordinal);
	return NULL;
    }
    addr  = ret & 0xffff;
    sel = (ret >> 16);
    dprintf_module(stddeb,"GetProcAddress // ret=%08X sel=%04X addr=%04X\n", ret, sel, addr);
    return (FARPROC) ret;
#endif /* WINELIB */
}

/* internal dlls */
static void 
FillModStructBuiltIn(MODULEENTRY *lpModule, struct dll_name_table_entry_s *dll)
{
	lpModule->dwSize = dll->dll_table_length * 1024;
	strcpy(lpModule->szModule, dll->dll_name);
	lpModule->hModule = 0xff00 + dll->dll_number;
	lpModule->wcUsage = GetModuleUsage(lpModule->hModule);
	GetModuleFileName(lpModule->hModule, lpModule->szExePath, MAX_PATH + 1);
	lpModule->wNext = 0;
}

/* loaded dlls */
static void 
FillModStructLoaded(MODULEENTRY *lpModule, struct w_files *dll)
{
	lpModule->dwSize = 16384;
	strcpy(lpModule->szModule, dll->name);
	lpModule->hModule = dll->hinstance;
	lpModule->wcUsage = GetModuleUsage(lpModule->hModule);
	GetModuleFileName(lpModule->hModule, lpModule->szExePath, MAX_PATH + 1);
	lpModule->wNext = 0;
}

/**********************************************************************
 *		ModuleFirst [TOOLHELP.59]
 */
BOOL ModuleFirst(MODULEENTRY *lpModule)
{
	dprintf_module(stddeb,"ModuleFirst(%08X)\n", (int) lpModule);
	
	FillModStructBuiltIn(lpModule, &dll_builtin_table[0]);
	return TRUE;
}

/**********************************************************************
 *		ModuleNext [TOOLHELP.60]
 */
BOOL ModuleNext(MODULEENTRY *lpModule)
{
	struct w_files *w;

	dprintf_module(stddeb,"ModuleNext(%08X)\n", (int) lpModule);

	if (IS_BUILTIN_DLL(lpModule->hModule)) {
		/* last built-in ? */
		if ((lpModule->hModule & 0xff) == (N_BUILTINS - 1) ) {
			if (wine_files) {
				FillModStructLoaded(lpModule, wine_files);
				return TRUE;
			} else
				return FALSE;
		}
		FillModStructBuiltIn(lpModule, &dll_builtin_table[(lpModule->hModule & 0xff)+1]);
		return TRUE;
	}
	w = GetFileInfo(lpModule->hModule);
	if (w->next) {
		FillModStructLoaded(lpModule, w->next);
		return TRUE;
	}
	return FALSE;
}

/**********************************************************************
 *		ModuleFindHandle [TOOLHELP.62]
 */
HMODULE ModuleFindHandle(MODULEENTRY *lpModule, HMODULE hModule)
{
	struct w_files *w;

	dprintf_module(stddeb,"ModuleFindHandle(%08X, %04X)\n", (int) lpModule, (int)hModule);

	/* built-in dll ? */
	if (IS_BUILTIN_DLL(hModule)) {
		FillModStructBuiltIn(lpModule, &dll_builtin_table[hModule & 0xff]);
		return hModule;
	}

	/* check loaded dlls */
	if ((w = GetFileInfo(hModule)) == NULL)
	    	return (HMODULE) NULL;
	
	FillModStructLoaded(lpModule, w);
	return w->hinstance;
}

/**********************************************************************
 *		ModuleFindName [TOOLHELP.61]
 */
HMODULE ModuleFindName(MODULEENTRY *lpModule, LPCSTR lpstrName)
{
	return (ModuleFindHandle(lpModule, GetModuleHandle((char*)lpstrName)));
}

