/* $Id: loadres.cpp,v 1.41 2003-02-13 13:10:49 sandervl Exp $ */

/*
 * Win32 resource API functions for OS/2
 *
 * Copyright 1998 Sander van Leeuwen
 *
 * Parts based on Wine code (objects\bitmap.c, loader\resource.c, objects\cursoricon.c):
 *
 * Copyright 1993 Alexandre Julliard
 *           1993 Robert J. Amstadt
 *           1996 Martin Von Loewis
 *           1997 Alex Korobka
 *           1998 Turchanov Sergey
 *           1998 Huw D M Davies
 *
 * Project Odin Software License can be found in LICENSE.TXT
 *
 */
#include <os2win.h>
#include <user32.h>
#include <heapstring.h>
#include <oslibres.h>
#include <win\virtual.h>
#include "dib.h"
#include "initterm.h"
#include <winres.h>
#include <custombuild.h>
#include "pmwindow.h"

#define DBG_LOCALLOG    DBG_loadres
#include "dbglocal.h"

//******************************************************************************
//******************************************************************************
INT WIN32API LoadStringA(HINSTANCE instance, UINT resource_id,
                         LPSTR buffer, INT buflen )
{
  INT retval;
  LPWSTR buffer2 = NULL;

    if (buffer && buflen)
        buffer2 = (LPWSTR)HeapAlloc( GetProcessHeap(), 0, buflen * 2 );

    retval = LoadStringW(instance,resource_id,buffer2,buflen);

    if (buffer2)
    {
        if (retval) {
            lstrcpynWtoA( buffer, buffer2, buflen );
            retval = lstrlenA( buffer );
        }
        else
            *buffer = 0; //NT4, SP6 clears first character
        HeapFree( GetProcessHeap(), 0, buffer2 );
    }
    return retval;
}
//******************************************************************************
//******************************************************************************
int WIN32API LoadStringW(HINSTANCE hinst, UINT wID, LPWSTR lpBuffer, int cchBuffer)
{
 WCHAR *p;
 int string_num;
 int i = 0;
 HRSRC hRes;

    /* Use bits 4 - 19 (incremented by 1) as resourceid, mask out
     * 20 - 31. */
    hRes = FindResourceW(hinst, (LPWSTR)(((wID>>4)&0xffff)+1), RT_STRINGW);
    if(hRes == NULL) {
        dprintf(("LoadStringW NOT FOUND from %X, id %d buffersize %d\n", hinst, wID, cchBuffer)); 
        *lpBuffer = 0;  //NT4, SP6 clears first character
        return 0;
    }

    p = (LPWSTR)LockResource(LoadResource(hinst, hRes));
    if(p) {
        string_num = wID & 0x000f;
        for (i = 0; i < string_num; i++)
                p += *p + 1;

        if (lpBuffer == NULL) return *p;
        i = min(cchBuffer - 1, *p);
        if (i > 0) {
                memcpy(lpBuffer, p + 1, i * sizeof (WCHAR));
                lpBuffer[i] = (WCHAR) 0;
        }
        else {
                if (cchBuffer > 1) {
                        lpBuffer[0] = (WCHAR) 0;  //NT4, SP6 clears first character
                        return 0;
                }
        }
    }

    if(i) {
         dprintf(("LoadStringW from %X, id %d %ls buffersize %d", hinst, wID, lpBuffer, cchBuffer));
    }
    else dprintf(("LoadStringW from %X, id %d buffersize %d", hinst, wID, cchBuffer));
    return(i);
}
//******************************************************************************
//******************************************************************************
HICON WIN32API LoadIconA(HINSTANCE hinst, LPCSTR lpszIcon)
{
    if(HIWORD(lpszIcon)) {
         dprintf(("LoadIconA %x %s", hinst, lpszIcon));
    }
    else dprintf(("LoadIconA %x %x", hinst, lpszIcon));
    return LoadImageA(hinst, lpszIcon, IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE);
}
//******************************************************************************
//******************************************************************************
HICON WIN32API LoadIconW(HINSTANCE hinst, LPCWSTR lpszIcon)
{
    if(HIWORD(lpszIcon)) {
         dprintf(("LoadIconW %x %ls", hinst, lpszIcon));
    }
    else dprintf(("LoadIconW %x %x", hinst, lpszIcon));
    return LoadImageW(hinst, lpszIcon, IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE);
}
//******************************************************************************
//******************************************************************************
HCURSOR WIN32API LoadCursorA(HINSTANCE hinst, LPCSTR lpszCursor)
{
    return LoadImageA(hinst, lpszCursor, IMAGE_CURSOR, 0, 0,
                      LR_SHARED | LR_DEFAULTSIZE );
}
//******************************************************************************
//******************************************************************************
HCURSOR WIN32API LoadCursorW(HINSTANCE hinst, LPCWSTR lpszCursor)
{
    return LoadImageW(hinst, lpszCursor, IMAGE_CURSOR, 0, 0,
                      LR_SHARED | LR_DEFAULTSIZE );
}
/***********************************************************************
*            LoadCursorFromFileW    (USER32.361)
*/
HCURSOR WIN32API LoadCursorFromFileW (LPCWSTR name)
{
    return LoadImageW(0, name, IMAGE_CURSOR, 0, 0,
                      LR_LOADFROMFILE | LR_DEFAULTSIZE );
}
/***********************************************************************
*            LoadCursorFromFileA    (USER32.360)
*/
HCURSOR WIN32API LoadCursorFromFileA (LPCSTR name)
{
    return LoadImageA(0, name, IMAGE_CURSOR, 0, 0,
                      LR_LOADFROMFILE | LR_DEFAULTSIZE );
}
//******************************************************************************
//NOTE: LR_CREATEDIBSECTION flag doesn't work (crash in GDI32)! (still??)
//******************************************************************************
HANDLE LoadBitmapW(HINSTANCE hinst, LPCWSTR lpszName, int cxDesired, int cyDesired,
                   UINT fuLoad)
{
    HBITMAP hbitmap = 0;
    HDC hdc;
    HRSRC hRsrc;
    HGLOBAL handle, hMapping = 0;
    char *ptr = NULL;
    BITMAPINFO *info, *fix_info=NULL;
    HGLOBAL hFix;
    int size;

    //if in OS/2 mode, then we must replace the standard button bitmaps
    //(min, max, restore, close)
    //(NOTE: if hBmpCloseButton present until WGSS has been updated)
    if(fOS2Look && (hinst == hInstanceUser32 || !hinst) && hBmpCloseButton) {
        switch((ULONG)lpszName) {
        case OBM_CLOSE:
            return CopyImage(hBmpCloseButton, IMAGE_BITMAP, 0, 0, 0);
        case OBM_CLOSED:
            return CopyImage(hBmpCloseButtonDown, IMAGE_BITMAP, 0, 0, 0);
        case OBM_RESTORE:
            return CopyImage(hBmpRestoreButton, IMAGE_BITMAP, 0, 0, 0);
        case OBM_RESTORED:
            return CopyImage(hBmpRestoreButtonDown, IMAGE_BITMAP, 0, 0, 0);
        case OBM_REDUCE:
            return CopyImage(hBmpMinButton, IMAGE_BITMAP, 0, 0, 0);
        case OBM_REDUCED:
            return CopyImage(hBmpMinButtonDown, IMAGE_BITMAP, 0, 0, 0);
        case OBM_ZOOM:
            return CopyImage(hBmpMaxButton, IMAGE_BITMAP, 0, 0, 0);
        case OBM_ZOOMD:
            return CopyImage(hBmpMaxButtonDown, IMAGE_BITMAP, 0, 0, 0);
        }
    }
    if (!(fuLoad & LR_LOADFROMFILE))
    {
        handle = 0;
        if(!hinst)
        {
            hRsrc = FindResourceW( hInstanceUser32, lpszName, RT_BITMAPW );
            if(hRsrc) {
                handle = LoadResource( hInstanceUser32, hRsrc );
            }
        }
        if(handle == 0)
        {
            if (!(hRsrc = FindResourceW( hinst, lpszName, RT_BITMAPW ))) return 0;
            if (!(handle = LoadResource( hinst, hRsrc ))) return 0;
        }

        if ((info = (BITMAPINFO *)LockResource( handle )) == NULL) return 0;
    }
    else
    {
        hMapping = VIRTUAL_MapFileW( lpszName, (LPVOID *)&ptr, TRUE);
        if (hMapping == INVALID_HANDLE_VALUE) {
            //TODO: last err set to ERROR_OPEN_FAILED if file not found; correct??
            dprintf(("LoadBitmapW: failed to load file %x (lasterr=%x)", lpszName, GetLastError()));
            return 0;
        }
        info = (BITMAPINFO *)(ptr + sizeof(BITMAPFILEHEADER));
    }

    //TODO: This has to be removed once pe2lx stores win32 resources!!!
    if (info->bmiHeader.biSize != sizeof(BITMAPCOREHEADER) &&
        info->bmiHeader.biSize != sizeof(BITMAPINFOHEADER))
    {//assume it contains a file header first
        info = (BITMAPINFO *)((char *)info + sizeof(BITMAPFILEHEADER));
    }

    if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
    {//determine size of converted header
         BITMAPCOREHEADER *core = (BITMAPCOREHEADER *)info;

         int colors = 0;
         if (core->bcBitCount <= 8) {
                 colors = (1 << core->bcBitCount);
         }
         size =  sizeof(BITMAPINFOHEADER) + colors * sizeof(RGBQUAD);
    }
    else size = DIB_BitmapInfoSize(info, DIB_RGB_COLORS);

    if ((hFix = GlobalAlloc(0, size)) != NULL) fix_info = (BITMAPINFO *)GlobalLock(hFix);
    if (fix_info)
    {
      BYTE pix;

      if (info->bmiHeader.biSize == sizeof(BITMAPCOREHEADER))
      {//convert old bitmap header to new format
            ULONG colors;
            ULONG *p, *q;

            memset (fix_info, 0, sizeof (BITMAPINFOHEADER));
            fix_info->bmiHeader.biSize     = sizeof (BITMAPINFOHEADER);
            fix_info->bmiHeader.biWidth    = ((BITMAPCOREHEADER *)info)->bcWidth;
            fix_info->bmiHeader.biHeight   = ((BITMAPCOREHEADER *)info)->bcHeight;
            fix_info->bmiHeader.biPlanes   = ((BITMAPCOREHEADER *)info)->bcPlanes;
            fix_info->bmiHeader.biBitCount = ((BITMAPCOREHEADER *)info)->bcBitCount;

            if(fix_info->bmiHeader.biBitCount <= 8)
            {
                p = (PULONG)((char *)info + sizeof(BITMAPCOREHEADER));
                q = (PULONG)((char *)fix_info + sizeof(BITMAPINFOHEADER));
                //convert RGBTRIPLE to RGBQUAD
                for (colors = 1 << fix_info->bmiHeader.biBitCount; colors > 0; colors--) {
                    *q = *p & 0x00FFFFFFUL;
                    q++;
                    p = (PULONG)((char *)p + sizeof (RGBTRIPLE));
                }
            }
      }
      else {
            memcpy(fix_info, info, size);
      }

      size = DIB_BitmapInfoSize(info, DIB_RGB_COLORS);

      pix = *((LPBYTE)info + size);
      DIB_FixColorsToLoadflags(fix_info, fuLoad, pix);
      if ((hdc = CreateCompatibleDC(0)) != 0)
      {
        char *bits = (char *)info + size;
        if (fuLoad & LR_CREATEDIBSECTION) {
          DIBSECTION dib;
          hbitmap = CreateDIBSection(hdc, fix_info, DIB_RGB_COLORS, NULL, 0, 0);
          GetObjectA(hbitmap, sizeof(DIBSECTION), &dib);
          SetDIBits(hdc, hbitmap, 0, dib.dsBm.bmHeight, bits, info,
                    DIB_RGB_COLORS);
        }
        else
        {
#if 0
          if(fix_info->bmiHeader.biBitCount == 1) {
              hbitmap = CreateBitmap(fix_info->bmiHeader.biWidth,
                                     fix_info->bmiHeader.biHeight,
                                     fix_info->bmiHeader.biPlanes,
                                     fix_info->bmiHeader.biBitCount,
                                     (PVOID)bits);
          }
          else {
#endif
              hbitmap = CreateDIBitmap(hdc, &fix_info->bmiHeader, CBM_INIT,
                                       bits, fix_info, DIB_RGB_COLORS );
//          }
          if(hbitmap == 0) {
              dprintf(("LoadBitmapW: CreateDIBitmap failed!!"));
          }
        }
        DeleteDC(hdc);
      }
      GlobalUnlock(hFix);
      GlobalFree(hFix);
    }
    if(fuLoad & LR_LOADFROMFILE) {
        UnmapViewOfFile(ptr);
        CloseHandle(hMapping);
    }
    return hbitmap;
}
//******************************************************************************
//TODO: scale bitmap
//******************************************************************************
HANDLE CopyBitmap(HANDLE hBitmap, DWORD desiredx, DWORD desiredy)
{
    HBITMAP res = 0;
    BITMAP bm;

    if(GetObjectA(hBitmap, sizeof(BITMAP), &bm) == FALSE) {
        dprintf(("CopyBitmap: GetObject failed!!"));
        return 0;
    }

#ifdef __WIN32OS2__
    BITMAPINFO* pInfo;
    HBITMAP oldbmp;
    HDC     hdc;
    int     colortablesize, bmpsize, headersize;
    char   *pBitmapData;

    colortablesize = 0;

    if(bm.bmBitsPixel <= 8) {
        colortablesize = sizeof(RGBQUAD)*(1<<bm.bmBitsPixel);
    }
    bmpsize = DIB_GetDIBImageBytes(bm.bmWidth, bm.bmHeight, bm.bmBitsPixel);
    headersize = sizeof(BITMAPINFO)+colortablesize+3*sizeof(DWORD); //+ extra space for > 8bpp images

    pInfo = (BITMAPINFO *)malloc(headersize+bmpsize); 
    if(pInfo == NULL) {
        DebugInt3();
        return 0;
    }
    pBitmapData = (char*)((char *)pInfo + headersize);
    memset(pInfo, 0, headersize+bmpsize);

    hdc = CreateCompatibleDC(0);

    pInfo->bmiHeader.biSize     = sizeof(BITMAPINFOHEADER);
    pInfo->bmiHeader.biPlanes   = bm.bmPlanes;
    pInfo->bmiHeader.biBitCount = bm.bmBitsPixel;
    pInfo->bmiHeader.biWidth    = bm.bmWidth;
    pInfo->bmiHeader.biHeight   = bm.bmHeight;

    GetDIBits(hdc, hBitmap, 0, bm.bmHeight, pBitmapData, pInfo, DIB_RGB_COLORS);

    res = CreateDIBitmap(hdc, &pInfo->bmiHeader, CBM_INIT, pBitmapData, 
                         pInfo, DIB_RGB_COLORS );

    DeleteDC(hdc);
    free(pInfo);
#else
    bm.bmBits = NULL;
    res = CreateBitmapIndirect(&bm);

    if(res)
    {
        char *buf = (char *)HeapAlloc( GetProcessHeap(), 0, bm.bmWidthBytes *
                                       bm.bmHeight );
        GetBitmapBits (hBitmap, bm.bmWidthBytes * bm.bmHeight, buf);
        SetBitmapBits (res, bm.bmWidthBytes * bm.bmHeight, buf);
        HeapFree( GetProcessHeap(), 0, buf );
    }
#endif
    return res;
}
//******************************************************************************
//TODO: No support for RT_NEWBITMAP
//******************************************************************************
HBITMAP WIN32API LoadBitmapA(HINSTANCE hinst, LPCSTR lpszBitmap)
{
 HBITMAP hBitmap = 0;

  hBitmap = LoadImageA(hinst, lpszBitmap, IMAGE_BITMAP, 0, 0, 0);

  if(HIWORD(lpszBitmap)) {
        dprintf(("LoadBitmapA %x %s returned %08xh\n", hinst, lpszBitmap, hBitmap));
  }
  else  dprintf(("LoadBitmapA %x %x returned %08xh\n", hinst, lpszBitmap, hBitmap));

  return(hBitmap);
}
//******************************************************************************
//TODO: No support for RT_NEWBITMAP
//******************************************************************************
HBITMAP WIN32API LoadBitmapW(HINSTANCE hinst, LPCWSTR lpszBitmap)
{
 HBITMAP hBitmap = 0;

  hBitmap = LoadBitmapW((hinst == 0) ? hInstanceUser32:hinst, lpszBitmap, 0, 0, 0);

  if(HIWORD(lpszBitmap)) {
        dprintf(("LoadBitmapW %x %ls returned %08xh\n", hinst, lpszBitmap, hBitmap));
  }
  else  dprintf(("LoadBitmapW %x %x returned %08xh\n", hinst, lpszBitmap, hBitmap));

  return(hBitmap);
}
//******************************************************************************
//******************************************************************************
static PFNLOADIMAGEW pfnCustomLoadImageW  = NULL;
//******************************************************************************
//Called by custom Odin builds to hook LoadImageW
//******************************************************************************
BOOL WIN32API SetCustomLoadImage(PFNLOADIMAGEW pfnLoadImageW)
{
    pfnCustomLoadImageW = pfnLoadImageW;
    return TRUE;
}
//******************************************************************************
//******************************************************************************
HANDLE WIN32API LoadImageA(HINSTANCE hinst, LPCSTR lpszName, UINT uType,
                           int cxDesired, int cyDesired, UINT fuLoad)
{
 HANDLE res = 0;
 LPCWSTR u_name;

  if(HIWORD(lpszName)) {
        dprintf(("LoadImageA %x %s %d (%d,%d)\n", hinst, lpszName, uType, cxDesired, cyDesired));
  }
  else  dprintf(("LoadImageA %x %x %d (%d,%d)\n", hinst, lpszName, uType, cxDesired, cyDesired));

  if (HIWORD(lpszName)) {
        u_name = HEAP_strdupAtoW(GetProcessHeap(), 0, lpszName);
  }
  else  u_name=(LPWSTR)lpszName;

  res = LoadImageW(hinst, u_name, uType, cxDesired, cyDesired, fuLoad);

  if (HIWORD(lpszName))
        HeapFree(GetProcessHeap(), 0, (LPVOID)u_name);

  return res;
}
//******************************************************************************
//******************************************************************************
HANDLE WIN32API LoadImageW(HINSTANCE hinst, LPCWSTR lpszName, UINT uType,
                           int cxDesired, int cyDesired, UINT fuLoad)
{
 HANDLE hRet = 0;

  if(pfnCustomLoadImageW) {
      pfnCustomLoadImageW(&hinst, (LPWSTR *)&lpszName, &uType);
  }

  if(HIWORD(lpszName)) {
       dprintf(("LoadImageW %x %ls %d (%d,%d)\n", hinst, lpszName, uType, cxDesired, cyDesired));
  }
  else dprintf(("LoadImageW %x %x %d (%d,%d)\n", hinst, lpszName, uType, cxDesired, cyDesired));

  if (fuLoad & LR_DEFAULTSIZE) {
        if (uType == IMAGE_ICON) {
            if (!cxDesired) cxDesired = GetSystemMetrics(SM_CXICON);
            if (!cyDesired) cyDesired = GetSystemMetrics(SM_CYICON);
        }
        else if (uType == IMAGE_CURSOR) {
            if (!cxDesired) cxDesired = GetSystemMetrics(SM_CXCURSOR);
            if (!cyDesired) cyDesired = GetSystemMetrics(SM_CYCURSOR);
        }
  }
  if (fuLoad & LR_LOADFROMFILE) fuLoad &= ~LR_SHARED;

  switch (uType)
  {
        case IMAGE_BITMAP:
        {
            hRet = (HANDLE)LoadBitmapW(hinst, lpszName, cxDesired, cyDesired, fuLoad);
            break;
        }
        case IMAGE_ICON:
        {
#ifdef __WIN32OS2__
            ULONG palEnts = (1 << ScreenBitsPerPel);
#else
            HDC hdc = GetDC(0);
            UINT palEnts = GetSystemPaletteEntries(hdc, 0, 0, NULL);
            if (palEnts == 0)
                palEnts = 256;
            ReleaseDC(0, hdc);
#endif

            hRet = CURSORICON_Load(hinst, lpszName, cxDesired, cyDesired,  palEnts, FALSE, fuLoad);
            break;
        }

        case IMAGE_CURSOR:
            return CURSORICON_Load(hinst, lpszName, cxDesired, cyDesired, 1, TRUE, fuLoad);

        default:
            dprintf(("LoadImageW: unsupported type %d!!", uType));
            return 0;
  }
  dprintf(("LoadImageW returned %x\n", (int)hRet));

  return(hRet);
}
/******************************************************************************
 * CopyImage32 [USER32.61]  Creates new image and copies attributes to it
 *
 * PARAMS
 *    hnd      [I] Handle to image to copy
 *    type     [I] Type of image to copy
 *    desiredx [I] Desired width of new image
 *    desiredy [I] Desired height of new image
 *    flags    [I] Copy flags
 *
 * RETURNS
 *    Success: Handle to newly created image
 *    Failure: NULL
 *
 * FIXME: implementation still lacks nearly all features, see LR_*
 * defines in windows.h
 *
 */
HICON WINAPI CopyImage(HANDLE hnd, UINT type, INT desiredx,
                       INT desiredy, UINT flags )
{
    dprintf(("CopyImage %x %d (%d,%d) %x", hnd, type, desiredx, desiredy, flags));
    switch (type)
    {
        case IMAGE_BITMAP:
                return CopyBitmap(hnd, desiredx, desiredy);
        case IMAGE_ICON:
                return (HANDLE)CURSORICON_ExtCopy(hnd, type, desiredx, desiredy, flags);
        case IMAGE_CURSOR:
                /* Should call CURSORICON_ExtCopy but more testing
                 * needs to be done before we change this
                 */
                return CURSORICON_ExtCopy(hnd,type, desiredx, desiredy, flags);
        default:
                dprintf(("CopyImage: Unsupported type"));
    }
    return 0;
}
//******************************************************************************
//******************************************************************************
