| /* | 
 |  * Program Manager | 
 |  * | 
 |  * Copyright 1996 Ulrich Schmid | 
 |  *           1997 Peter Schlaile | 
 |  * | 
 |  * 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 "windows.h" | 
 | #include "wine/winuser16.h" | 
 | #include "progman.h" | 
 | #include "mmsystem.h" | 
 |  | 
 | #define MALLOCHUNK 1000 | 
 |  | 
 | #define GET_USHORT(buffer, i)\ | 
 |   (((BYTE)((buffer)[(i)]) + 0x100 * (BYTE)((buffer)[(i)+1]))) | 
 | #define GET_SHORT(buffer, i)\ | 
 |   (((BYTE)((buffer)[(i)]) + 0x100 * (signed char)((buffer)[(i)+1]))) | 
 | #define PUT_SHORT(buffer, i, s)\ | 
 |   (((buffer)[(i)] = (s) & 0xff, (buffer)[(i)+1] = ((s) >> 8) & 0xff)) | 
 |  | 
 | static BOOL   GRPFILE_ReadFileToBuffer(LPCSTR, HLOCAL*, INT*); | 
 | static HLOCAL GRPFILE_ScanGroup(LPCSTR, INT, LPCSTR, BOOL); | 
 | static HLOCAL GRPFILE_ScanProgram(LPCSTR, INT, LPCSTR, INT, | 
 | 				  LPCSTR, HLOCAL,LPCSTR); | 
 | static BOOL GRPFILE_DoWriteGroupFile(HFILE file, PROGGROUP *group); | 
 |  | 
 | /*********************************************************************** | 
 |  * | 
 |  *           GRPFILE_ModifyFileName | 
 |  * | 
 |  *  Change extension `.grp' to `.gr' | 
 |  */ | 
 |  | 
 | static VOID GRPFILE_ModifyFileName(LPSTR lpszNewName, LPCSTR lpszOrigName, | 
 | 				   INT nSize, BOOL bModify) | 
 | { | 
 |   lstrcpyn(lpszNewName, lpszOrigName, nSize); | 
 |   lpszNewName[nSize-1] = '\0'; | 
 |   if (!bModify) return; | 
 |   if (!lstrcmpi(lpszNewName + strlen(lpszNewName) - 4, ".grp")) | 
 |     lpszNewName[strlen(lpszNewName) - 1] = '\0'; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  * | 
 |  *           GRPFILE_ReadGroupFile | 
 |  */ | 
 |  | 
 | HLOCAL GRPFILE_ReadGroupFile(LPCSTR lpszPath) | 
 | { | 
 |   CHAR   szPath_gr[MAX_PATHNAME_LEN]; | 
 |   BOOL   bFileNameModified = FALSE; | 
 |   OFSTRUCT dummy; | 
 |   HLOCAL hBuffer, hGroup; | 
 |   INT    size; | 
 |  | 
 |   /* if `.gr' file exists use that */ | 
 |   GRPFILE_ModifyFileName(szPath_gr, lpszPath, MAX_PATHNAME_LEN, TRUE); | 
 |   if (OpenFile(szPath_gr, &dummy, OF_EXIST) != HFILE_ERROR) | 
 |     { | 
 |       lpszPath = szPath_gr; | 
 |       bFileNameModified = TRUE; | 
 |     } | 
 |  | 
 |   /* Read the whole file into a buffer */ | 
 |   if (!GRPFILE_ReadFileToBuffer(lpszPath, &hBuffer, &size)) | 
 |     { | 
 |       MAIN_MessageBoxIDS_s(IDS_GRPFILE_READ_ERROR_s, lpszPath, IDS_ERROR, MB_YESNO); | 
 |       return(0); | 
 |     } | 
 |  | 
 |   /* Interpret buffer */ | 
 |   hGroup = GRPFILE_ScanGroup(LocalLock(hBuffer), size, | 
 | 			     lpszPath, bFileNameModified); | 
 |   if (!hGroup) | 
 |     MAIN_MessageBoxIDS_s(IDS_GRPFILE_READ_ERROR_s, lpszPath, IDS_ERROR, MB_YESNO); | 
 |  | 
 |   LocalFree(hBuffer); | 
 |  | 
 |   return(hGroup); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  * | 
 |  *           GRPFILE_ReadFileToBuffer | 
 |  */ | 
 |  | 
 | static BOOL GRPFILE_ReadFileToBuffer(LPCSTR path, HLOCAL *phBuffer, | 
 | 				     INT *piSize) | 
 | { | 
 |   UINT    len, size; | 
 |   LPSTR  buffer; | 
 |   HLOCAL hBuffer, hNewBuffer; | 
 |   HFILE  file; | 
 |  | 
 |   file=_lopen(path, OF_READ); | 
 |   if (file == HFILE_ERROR) return FALSE; | 
 |  | 
 |   size = 0; | 
 |   hBuffer = LocalAlloc(LMEM_FIXED, size + MALLOCHUNK + 1); | 
 |   if (!hBuffer) return FALSE; | 
 |   buffer = LocalLock(hBuffer); | 
 |  | 
 |   while ((len = _lread(file, buffer + size, MALLOCHUNK)) | 
 | 	 == MALLOCHUNK) | 
 |     { | 
 |       size += len; | 
 |       hNewBuffer = LocalReAlloc(hBuffer, size + MALLOCHUNK + 1, | 
 | 				LMEM_FIXED); | 
 |       if (!hNewBuffer) | 
 | 	{ | 
 | 	  LocalFree(hBuffer); | 
 | 	  return FALSE; | 
 | 	} | 
 |       hBuffer = hNewBuffer; | 
 |       buffer = LocalLock(hBuffer); | 
 |     } | 
 |  | 
 |   _lclose(file); | 
 |  | 
 |   if (len == (UINT)HFILE_ERROR) | 
 |     { | 
 |       LocalFree(hBuffer); | 
 |       return FALSE; | 
 |     } | 
 |  | 
 |   size += len; | 
 |   buffer[size] = 0; | 
 |  | 
 |   *phBuffer = hBuffer; | 
 |   *piSize   = size; | 
 |   return TRUE; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GRPFILE_ScanGroup | 
 |  */ | 
 |  | 
 | static HLOCAL GRPFILE_ScanGroup(LPCSTR buffer, INT size, | 
 | 				LPCSTR lpszGrpFile, | 
 | 				BOOL bModifiedFileName) | 
 | { | 
 |   HLOCAL  hGroup; | 
 |   INT     i, seqnum; | 
 |   LPCSTR  extension; | 
 |   LPCSTR  lpszName; | 
 |   INT     x, y, width, height, iconx, icony, nCmdShow; | 
 |   INT     number_of_programs; | 
 |   BOOL    bOverwriteFileOk; | 
 |  | 
 |   if (buffer[0] != 'P' || buffer[1] != 'M') return(0); | 
 |   if (buffer[2] == 'C' && buffer[3] == 'C') | 
 |     /* original with checksum */ | 
 |     bOverwriteFileOk = FALSE; | 
 |   else if (buffer[2] == 'X' && buffer[3] == 'X') | 
 |     /* modified without checksum */ | 
 |     bOverwriteFileOk = TRUE; | 
 |   else return(0); | 
 |  | 
 |   /* checksum = GET_USHORT(buffer, 4)   (ignored) */ | 
 |  | 
 |   extension = buffer + GET_USHORT(buffer, 6); | 
 |   if (extension == buffer + size) extension = 0; | 
 |   else if (extension + 6 > buffer + size) return(0); | 
 |  | 
 |   nCmdShow = GET_USHORT(buffer,  8); | 
 |   x        = GET_SHORT(buffer,  10); | 
 |   y        = GET_SHORT(buffer,  12); | 
 |   width    = GET_USHORT(buffer, 14); | 
 |   height   = GET_USHORT(buffer, 16); | 
 |   iconx    = GET_SHORT(buffer,  18); | 
 |   icony    = GET_SHORT(buffer,  20); | 
 |   lpszName = buffer + GET_USHORT(buffer, 22); | 
 |   if (lpszName >= buffer + size) return(0); | 
 |  | 
 |   /* unknown bytes 24 - 31 ignored */ | 
 |   /* | 
 |     Unknown bytes should be: | 
 |     wLogPixelsX = GET_SHORT(buffer, 24); | 
 |     wLogPixelsY = GET_SHORT(buffer, 26); | 
 |     byBitsPerPixel = byte at 28; | 
 |     byPlanes     = byte at 29; | 
 |     wReserved   = GET_SHORT(buffer, 30); | 
 |     */ | 
 |  | 
 |   hGroup = GROUP_AddGroup(lpszName, lpszGrpFile, nCmdShow, x, y, | 
 | 			  width, height, iconx, icony, | 
 | 			  bModifiedFileName, bOverwriteFileOk, | 
 | 			  TRUE); | 
 |   if (!hGroup) return(0); | 
 |  | 
 |   number_of_programs = GET_USHORT(buffer, 32); | 
 |   if (2 * number_of_programs + 34 > size) return(0); | 
 |   for (i=0, seqnum=0; i < number_of_programs; i++, seqnum++) | 
 |     { | 
 |       LPCSTR program_ptr = buffer + GET_USHORT(buffer, 34 + 2*i); | 
 |       if (program_ptr + 24 > buffer + size) return(0); | 
 |       if (!GET_USHORT(buffer, 34 + 2*i)) continue; | 
 |       if (!GRPFILE_ScanProgram(buffer, size, program_ptr, seqnum, | 
 | 			       extension, hGroup, lpszGrpFile)) | 
 | 	{ | 
 | 	  GROUP_DeleteGroup(hGroup); | 
 | 	  return(0); | 
 | 	} | 
 |     } | 
 |  | 
 |   /* FIXME shouldn't be necessary */ | 
 |   GROUP_ShowGroupWindow(hGroup); | 
 |  | 
 |   return hGroup; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  *           GRPFILE_ScanProgram | 
 |  */ | 
 |  | 
 | static HLOCAL GRPFILE_ScanProgram(LPCSTR buffer, INT size, | 
 | 				  LPCSTR program_ptr, INT seqnum, | 
 | 				  LPCSTR extension, HLOCAL hGroup, | 
 | 				  LPCSTR lpszGrpFile) | 
 | { | 
 |   INT    icontype; | 
 |   HICON  hIcon; | 
 |   LPCSTR lpszName, lpszCmdLine, lpszIconFile, lpszWorkDir; | 
 |   LPCSTR iconinfo_ptr, iconANDbits_ptr, iconXORbits_ptr; | 
 |   INT    x, y, nIconIndex, iconANDsize, iconXORsize; | 
 |   INT    nHotKey, nCmdShow; | 
 |   CURSORICONINFO iconinfo; | 
 |  | 
 |   x               = GET_SHORT(program_ptr, 0); | 
 |   y               = GET_SHORT(program_ptr, 2); | 
 |   nIconIndex      = GET_USHORT(program_ptr, 4); | 
 |  | 
 |   /* FIXME is this correct ?? */ | 
 |   icontype = GET_USHORT(program_ptr,  6); | 
 |   switch (icontype) | 
 |     { | 
 |     default: | 
 |       MAIN_MessageBoxIDS_s(IDS_UNKNOWN_FEATURE_s, lpszGrpFile, | 
 | 			   IDS_WARNING, MB_OK); | 
 |     case 0x048c: | 
 |       iconXORsize     = GET_USHORT(program_ptr,  8); | 
 |       iconANDsize     = GET_USHORT(program_ptr, 10) / 8; | 
 |       iconinfo_ptr    = buffer + GET_USHORT(program_ptr, 12); | 
 |       iconXORbits_ptr = buffer + GET_USHORT(program_ptr, 14); | 
 |       iconANDbits_ptr = buffer + GET_USHORT(program_ptr, 16); | 
 |       iconinfo.ptHotSpot.x   = GET_USHORT(iconinfo_ptr, 0); | 
 |       iconinfo.ptHotSpot.y   = GET_USHORT(iconinfo_ptr, 2); | 
 |       iconinfo.nWidth        = GET_USHORT(iconinfo_ptr, 4); | 
 |       iconinfo.nHeight       = GET_USHORT(iconinfo_ptr, 6); | 
 |       iconinfo.nWidthBytes   = GET_USHORT(iconinfo_ptr, 8); | 
 |       iconinfo.bPlanes       = GET_USHORT(iconinfo_ptr, 10); | 
 |       iconinfo.bBitsPerPixel = GET_USHORT(iconinfo_ptr, 11); | 
 |       break; | 
 |     case 0x000c: | 
 |       iconANDsize     = GET_USHORT(program_ptr,  8); | 
 |       iconXORsize     = GET_USHORT(program_ptr, 10); | 
 |       iconinfo_ptr    = buffer + GET_USHORT(program_ptr, 12); | 
 |       iconANDbits_ptr = buffer + GET_USHORT(program_ptr, 14); | 
 |       iconXORbits_ptr = buffer + GET_USHORT(program_ptr, 16); | 
 |       iconinfo.ptHotSpot.x   = GET_USHORT(iconinfo_ptr, 0); | 
 |       iconinfo.ptHotSpot.y   = GET_USHORT(iconinfo_ptr, 2); | 
 |       iconinfo.nWidth        = GET_USHORT(iconinfo_ptr, 4); | 
 |       iconinfo.nHeight       = GET_USHORT(iconinfo_ptr, 6); | 
 |       iconinfo.nWidthBytes = GET_USHORT(iconinfo_ptr, 8) * 8; | 
 |       iconinfo.bPlanes       = GET_USHORT(iconinfo_ptr, 10); | 
 |       iconinfo.bBitsPerPixel = GET_USHORT(iconinfo_ptr, 11); | 
 |     } | 
 |  | 
 |   if (iconANDbits_ptr + iconANDsize > buffer + size || | 
 |       iconXORbits_ptr + iconXORsize > buffer + size) return(0); | 
 |  | 
 |   hIcon = CreateIcon( Globals.hInstance, iconinfo.nWidth, iconinfo.nHeight, | 
 |                       iconinfo.bPlanes, iconinfo.bBitsPerPixel, | 
 |                       iconANDbits_ptr, iconXORbits_ptr ); | 
 |  | 
 |   lpszName        = buffer + GET_USHORT(program_ptr, 18); | 
 |   lpszCmdLine     = buffer + GET_USHORT(program_ptr, 20); | 
 |   lpszIconFile    = buffer + GET_USHORT(program_ptr, 22); | 
 |   if (iconinfo_ptr + 6 > buffer + size || | 
 |       lpszName         > buffer + size || | 
 |       lpszCmdLine      > buffer + size || | 
 |       lpszIconFile     > buffer + size) return(0); | 
 |  | 
 |   /* Scan Extensions */ | 
 |   lpszWorkDir = ""; | 
 |   nHotKey     = 0; | 
 |   nCmdShow    = SW_SHOWNORMAL; | 
 |   if (extension) | 
 |     { | 
 |       LPCSTR ptr = extension; | 
 |       while (ptr + 6 <= buffer + size) | 
 | 	{ | 
 | 	  UINT type   = GET_USHORT(ptr, 0); | 
 | 	  UINT number = GET_USHORT(ptr, 2); | 
 | 	  UINT skip   = GET_USHORT(ptr, 4); | 
 |  | 
 | 	  if (number == seqnum) | 
 | 	    { | 
 | 	      switch (type) | 
 | 		{ | 
 | 		case 0x8000: | 
 | 		  if (ptr + 10 > buffer + size) return(0); | 
 | 		  if (ptr[6] != 'P' || ptr[7] != 'M' || | 
 | 		      ptr[8] != 'C' || ptr[9] != 'C') return(0); | 
 | 		  break; | 
 | 		case 0x8101: | 
 | 		  lpszWorkDir = ptr + 6; | 
 | 		  break; | 
 | 		case 0x8102: | 
 | 		  if (ptr + 8 > buffer + size) return(0); | 
 | 		  nHotKey = GET_USHORT(ptr, 6); | 
 | 		  break; | 
 | 		case 0x8103: | 
 | 		  if (ptr + 8 > buffer + size) return(0); | 
 | 		  nCmdShow = GET_USHORT(ptr, 6); | 
 | 		  break; | 
 | 		default: | 
 | 		  MAIN_MessageBoxIDS_s(IDS_UNKNOWN_FEATURE_s, | 
 | 				       lpszGrpFile, IDS_WARNING, MB_OK); | 
 | 		} | 
 | 	    } | 
 | 	  if (!skip) break; | 
 | 	  ptr += skip; | 
 | 	} | 
 |     } | 
 |  | 
 |   return (PROGRAM_AddProgram(hGroup, hIcon, lpszName, x, y, | 
 | 			     lpszCmdLine, lpszIconFile, | 
 | 			     nIconIndex, lpszWorkDir, | 
 | 			     nHotKey, nCmdShow)); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  * | 
 |  *           GRPFILE_WriteGroupFile | 
 |  */ | 
 |  | 
 | BOOL GRPFILE_WriteGroupFile(HLOCAL hGroup) | 
 | { | 
 |   CHAR szPath[MAX_PATHNAME_LEN]; | 
 |   PROGGROUP *group = LocalLock(hGroup); | 
 |   OFSTRUCT dummy; | 
 |   HFILE file; | 
 |   BOOL ret; | 
 |  | 
 |   GRPFILE_ModifyFileName(szPath, LocalLock(group->hGrpFile), | 
 | 			 MAX_PATHNAME_LEN, | 
 | 			 group->bFileNameModified); | 
 |  | 
 |   /* Try not to overwrite original files */ | 
 |  | 
 |   /* group->bOverwriteFileOk == TRUE only if a file has the modified format */ | 
 |   if (!group->bOverwriteFileOk && | 
 |       OpenFile(szPath, &dummy, OF_EXIST) != HFILE_ERROR) | 
 |     { | 
 |       /* Original file exists, try `.gr' extension */ | 
 |       GRPFILE_ModifyFileName(szPath, LocalLock(group->hGrpFile), | 
 | 			     MAX_PATHNAME_LEN, TRUE); | 
 |       if (OpenFile(szPath, &dummy, OF_EXIST) != HFILE_ERROR) | 
 | 	{ | 
 | 	  /* File exists. Do not overwrite */ | 
 | 	  MAIN_MessageBoxIDS_s(IDS_FILE_NOT_OVERWRITTEN_s, szPath, | 
 | 			       IDS_INFO, MB_OK); | 
 | 	  return FALSE; | 
 | 	} | 
 |       /* Inform about the modified file name */ | 
 |       if (IDCANCEL == | 
 | 	  MAIN_MessageBoxIDS_s(IDS_SAVE_GROUP_AS_s, szPath, IDS_INFO, | 
 | 			       MB_OKCANCEL | MB_ICONINFORMATION)) | 
 | 	return FALSE; | 
 |     } | 
 |  | 
 |   { | 
 |     /* Warn about the (possible) incompatibility */ | 
 |     CHAR msg[MAX_PATHNAME_LEN + 200]; | 
 |     wsprintf(msg, | 
 | 	     "Group files written by this DRAFT Program Manager " | 
 | 	     "possibly cannot be read by the Microsoft Program Manager!!\n" | 
 | 	     "Are you sure to write %s?", szPath); | 
 |     if (IDOK != MessageBox(Globals.hMainWnd, msg, "WARNING", | 
 | 			   MB_OKCANCEL | MB_DEFBUTTON2)) return FALSE; | 
 |   } | 
 |  | 
 |   /* FIXME */ | 
 |   if (OpenFile(szPath, &dummy, OF_EXIST) == HFILE_ERROR) | 
 |     { | 
 |       CHAR msg[MAX_PATHNAME_LEN + 200]; | 
 |       wsprintf(msg, "Cause of a bug you must now touch the file %s\n", szPath); | 
 |       MessageBox(Globals.hMainWnd, msg, "", MB_OK); | 
 |     } | 
 |  | 
 |   /* Open file */ | 
 |   file = _lopen(szPath, OF_WRITE); | 
 |   if (file != HFILE_ERROR) | 
 |     { | 
 |       ret = GRPFILE_DoWriteGroupFile(file, group); | 
 |       _lclose(file); | 
 |     } | 
 |   else ret = FALSE; | 
 |  | 
 |   if (!ret) | 
 |     MAIN_MessageBoxIDS_s(IDS_FILE_WRITE_ERROR_s, szPath, IDS_ERROR, MB_OK); | 
 |  | 
 |   return(ret); | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  * | 
 |  *           GRPFILE_CalculateSizes | 
 |  */ | 
 |  | 
 | static VOID GRPFILE_CalculateSizes(PROGRAM *program, | 
 | 				   INT *Progs, INT *Icons) | 
 | { | 
 |   CURSORICONINFO *iconinfo = LocalLock(program->hIcon); | 
 |   INT sizeXor = iconinfo->nHeight * iconinfo->nWidthBytes; | 
 |   INT sizeAnd = iconinfo->nHeight * ((iconinfo->nWidth + 15) / 16 * 2); | 
 |  | 
 |   *Progs += 24; | 
 |   *Progs += lstrlen(LocalLock(program->hName)) + 1; | 
 |   *Progs += lstrlen(LocalLock(program->hCmdLine)) + 1; | 
 |   *Progs += lstrlen(LocalLock(program->hIconFile)) + 1; | 
 |  | 
 |   *Icons += 12; /* IconInfo */ | 
 |   *Icons += sizeAnd; | 
 |   *Icons += sizeXor; | 
 | } | 
 |  | 
 | /***********************************************************************/ | 
 | UINT16 GRPFILE_checksum; | 
 | BOOL GRPFILE_checksum_half_word; | 
 | BYTE GRPFILE_checksum_last_byte; | 
 | /*********************************************************************** | 
 |  * | 
 |  *           GRPFILE_InitChecksum | 
 |  */ | 
 |  | 
 | static void GRPFILE_InitChecksum() | 
 | { | 
 | 	GRPFILE_checksum = 0; | 
 | 	GRPFILE_checksum_half_word = 0; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  * | 
 |  *           GRPFILE_GetChecksum | 
 |  */ | 
 |  | 
 | static UINT16 GRPFILE_GetChecksum() | 
 | { | 
 | 	return GRPFILE_checksum; | 
 | } | 
 |  | 
 | /*********************************************************************** | 
 |  * | 
 |  *           GRPFILE_WriteWithChecksum | 
 |  * | 
 |  * Looks crazier than it is: | 
 |  * | 
 |  * chksum = 0; | 
 |  * chksum = cksum - 1. word; | 
 |  * chksum = cksum - 2. word; | 
 |  * ... | 
 |  * | 
 |  * if (filelen is even) | 
 |  *      great I'm finished | 
 |  * else | 
 |  *      ignore last byte | 
 |  */ | 
 |  | 
 | static UINT GRPFILE_WriteWithChecksum(HFILE file, LPCSTR str, UINT size) | 
 | { | 
 | 	UINT i; | 
 | 	if (GRPFILE_checksum_half_word) { | 
 | 		GRPFILE_checksum -= GRPFILE_checksum_last_byte; | 
 | 	} | 
 | 	for (i=0; i < size; i++) { | 
 | 		if (GRPFILE_checksum_half_word) { | 
 | 			GRPFILE_checksum -= str[i] << 8; | 
 | 		} else { | 
 | 			GRPFILE_checksum -= str[i]; | 
 | 		} | 
 | 		GRPFILE_checksum_half_word ^= 1; | 
 | 	} | 
 |  | 
 | 	if (GRPFILE_checksum_half_word) { | 
 | 		GRPFILE_checksum_last_byte = str[size-1]; | 
 | 		GRPFILE_checksum += GRPFILE_checksum_last_byte; | 
 | 	} | 
 |  | 
 | 	return _lwrite(file, str, size); | 
 | } | 
 |  | 
 |  | 
 | /*********************************************************************** | 
 |  * | 
 |  *           GRPFILE_DoWriteGroupFile | 
 |  */ | 
 |  | 
 | static BOOL GRPFILE_DoWriteGroupFile(HFILE file, PROGGROUP *group) | 
 | { | 
 |   BYTE buffer[34]; | 
 |   HLOCAL hProgram; | 
 |   INT    NumProg, Title, Progs, Icons, Extension; | 
 |   INT    CurrProg, CurrIcon, nCmdShow, ptr, seqnum; | 
 |   BOOL   need_extension; | 
 |   LPCSTR lpszTitle = LocalLock(group->hName); | 
 |  | 
 |   UINT16 checksum; | 
 |  | 
 |   GRPFILE_InitChecksum(); | 
 |  | 
 |   /* Calculate offsets */ | 
 |   NumProg = 0; | 
 |   Icons   = 0; | 
 |   Extension = 0; | 
 |   need_extension = FALSE; | 
 |   hProgram = group->hPrograms; | 
 |   while(hProgram) | 
 |     { | 
 |       PROGRAM *program = LocalLock(hProgram); | 
 |       LPCSTR lpszWorkDir = LocalLock(program->hWorkDir); | 
 |  | 
 |       NumProg++; | 
 |       GRPFILE_CalculateSizes(program, &Icons, &Extension); | 
 |  | 
 |       /* Set a flag if an extension is needed */ | 
 |       if (lpszWorkDir[0] || program->nHotKey || | 
 | 	  program->nCmdShow != SW_SHOWNORMAL) need_extension = TRUE; | 
 |  | 
 |       hProgram = program->hNext; | 
 |     } | 
 |   Title      = 34 + NumProg * 2; | 
 |   Progs      = Title + lstrlen(lpszTitle) + 1; | 
 |   Icons     += Progs; | 
 |   Extension += Icons; | 
 |  | 
 |   /* Header */ | 
 |   buffer[0] = 'P'; | 
 |   buffer[1] = 'M'; | 
 |   buffer[2] = 'C'; | 
 |   buffer[3] = 'C'; | 
 |  | 
 |   PUT_SHORT(buffer,  4, 0); /* Checksum zero for now, written later */ | 
 |   PUT_SHORT(buffer,  6, Extension); | 
 |   /* Update group->nCmdShow */ | 
 |   if (IsIconic(group->hWnd))      nCmdShow = SW_SHOWMINIMIZED; | 
 |   else if (IsZoomed(group->hWnd)) nCmdShow = SW_SHOWMAXIMIZED; | 
 |   else                            nCmdShow = SW_SHOWNORMAL; | 
 |   PUT_SHORT(buffer,  8, nCmdShow); | 
 |   PUT_SHORT(buffer, 10, group->x); | 
 |   PUT_SHORT(buffer, 12, group->y); | 
 |   PUT_SHORT(buffer, 14, group->width); | 
 |   PUT_SHORT(buffer, 16, group->height); | 
 |   PUT_SHORT(buffer, 18, group->iconx); | 
 |   PUT_SHORT(buffer, 20, group->icony); | 
 |   PUT_SHORT(buffer, 22, Title); | 
 |   PUT_SHORT(buffer, 24, 0x0020); /* unknown */ | 
 |   PUT_SHORT(buffer, 26, 0x0020); /* unknown */ | 
 |   PUT_SHORT(buffer, 28, 0x0108); /* unknown */ | 
 |   PUT_SHORT(buffer, 30, 0x0000); /* unknown */ | 
 |   PUT_SHORT(buffer, 32, NumProg); | 
 |  | 
 |   if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 34)) return FALSE; | 
 |  | 
 |   /* Program table */ | 
 |   CurrProg = Progs; | 
 |   CurrIcon = Icons; | 
 |   hProgram = group->hPrograms; | 
 |   while(hProgram) | 
 |     { | 
 |       PROGRAM *program = LocalLock(hProgram); | 
 |  | 
 |       PUT_SHORT(buffer, 0, CurrProg); | 
 |       if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 2)) | 
 | 	      return FALSE; | 
 |  | 
 |       GRPFILE_CalculateSizes(program, &CurrProg, &CurrIcon); | 
 |       hProgram = program->hNext; | 
 |     } | 
 |  | 
 |   /* Title */ | 
 |   if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, lpszTitle, | 
 | 					       lstrlen(lpszTitle) + 1)) | 
 |     return FALSE; | 
 |  | 
 |   /* Program entries */ | 
 |   CurrProg = Progs; | 
 |   CurrIcon = Icons; | 
 |   hProgram = group->hPrograms; | 
 |   while(hProgram) | 
 |     { | 
 |       PROGRAM *program = LocalLock(hProgram); | 
 |       CURSORICONINFO *iconinfo = LocalLock(program->hIcon); | 
 |       LPCSTR Name     = LocalLock(program->hName); | 
 |       LPCSTR CmdLine  = LocalLock(program->hCmdLine); | 
 |       LPCSTR IconFile = LocalLock(program->hIconFile); | 
 |       INT sizeXor = iconinfo->nHeight * iconinfo->nWidthBytes; | 
 |       INT sizeAnd = iconinfo->nHeight * ((iconinfo->nWidth + 15) / 16 * 2); | 
 |  | 
 |       PUT_SHORT(buffer,  0, program->x); | 
 |       PUT_SHORT(buffer,  2, program->y); | 
 |       PUT_SHORT(buffer,  4, program->nIconIndex); | 
 |       PUT_SHORT(buffer,  6, 0x048c);            /* unknown */ | 
 |       PUT_SHORT(buffer,  8, sizeXor); | 
 |       PUT_SHORT(buffer, 10, sizeAnd * 8); | 
 |       PUT_SHORT(buffer, 12, CurrIcon); | 
 |       PUT_SHORT(buffer, 14, CurrIcon + 12 + sizeAnd); | 
 |       PUT_SHORT(buffer, 16, CurrIcon + 12); | 
 |       ptr = CurrProg + 24; | 
 |       PUT_SHORT(buffer, 18, ptr); | 
 |       ptr += lstrlen(Name) + 1; | 
 |       PUT_SHORT(buffer, 20, ptr); | 
 |       ptr += lstrlen(CmdLine) + 1; | 
 |       PUT_SHORT(buffer, 22, ptr); | 
 |  | 
 |       if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 24) || | 
 | 	  (UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, Name, lstrlen(Name) + 1) || | 
 | 	  (UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, CmdLine, lstrlen(CmdLine) + 1) || | 
 | 	  (UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, IconFile, lstrlen(IconFile) + 1)) | 
 | 	return FALSE; | 
 |  | 
 |       GRPFILE_CalculateSizes(program, &CurrProg, &CurrIcon); | 
 |       hProgram = program->hNext; | 
 |     } | 
 |  | 
 |   /* Icons */ | 
 | #if 0  /* FIXME: this is broken anyway */ | 
 |   hProgram = group->hPrograms; | 
 |   while(hProgram) | 
 |     { | 
 |       PROGRAM *program = LocalLock(hProgram); | 
 |       CURSORICONINFO *iconinfo = LocalLock(program->hIcon); | 
 |       LPVOID XorBits, AndBits; | 
 |       INT sizeXor = iconinfo->nHeight * iconinfo->nWidthBytes; | 
 |       INT sizeAnd = iconinfo->nHeight * ((iconinfo->nWidth + 15) / 16 * 2); | 
 |       /* DumpIcon16(LocalLock(program->hIcon), 0, &XorBits, &AndBits);*/ | 
 |  | 
 |       PUT_SHORT(buffer, 0, iconinfo->ptHotSpot.x); | 
 |       PUT_SHORT(buffer, 2, iconinfo->ptHotSpot.y); | 
 |       PUT_SHORT(buffer, 4, iconinfo->nWidth); | 
 |       PUT_SHORT(buffer, 6, iconinfo->nHeight); | 
 |       PUT_SHORT(buffer, 8, iconinfo->nWidthBytes); | 
 |       buffer[10] = iconinfo->bPlanes; | 
 |       buffer[11] = iconinfo->bBitsPerPixel; | 
 |  | 
 |       if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 12) || | 
 | 	  (UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, AndBits, sizeAnd) || | 
 | 	  (UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, XorBits, sizeXor)) return FALSE; | 
 |  | 
 |       hProgram = program->hNext; | 
 |     } | 
 | #endif | 
 |  | 
 |   if (need_extension) | 
 |     { | 
 |       /* write `PMCC' extension */ | 
 |       PUT_SHORT(buffer, 0, 0x8000); | 
 |       PUT_SHORT(buffer, 2, 0xffff); | 
 |       PUT_SHORT(buffer, 4, 0x000a); | 
 |       buffer[6] = 'P', buffer[7] = 'M'; | 
 |       buffer[8] = 'C', buffer[9] = 'C'; | 
 |       if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 10)) | 
 | 	      return FALSE; | 
 |  | 
 |       seqnum = 0; | 
 |       hProgram = group->hPrograms; | 
 |       while(hProgram) | 
 | 	{ | 
 | 	  PROGRAM *program = LocalLock(hProgram); | 
 | 	  LPCSTR lpszWorkDir = LocalLock(program->hWorkDir); | 
 |  | 
 | 	  /* Working directory */ | 
 | 	  if (lpszWorkDir[0]) | 
 | 	    { | 
 | 	      PUT_SHORT(buffer, 0, 0x8101); | 
 | 	      PUT_SHORT(buffer, 2, seqnum); | 
 | 	      PUT_SHORT(buffer, 4, 7 + lstrlen(lpszWorkDir)); | 
 | 	      if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 6) || | 
 | 		  (UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, lpszWorkDir, lstrlen(lpszWorkDir) + 1)) | 
 | 		return FALSE; | 
 | 	    } | 
 |  | 
 | 	  /* Hot key */ | 
 | 	  if (program->nHotKey) | 
 | 	    { | 
 | 	      PUT_SHORT(buffer, 0, 0x8102); | 
 | 	      PUT_SHORT(buffer, 2, seqnum); | 
 | 	      PUT_SHORT(buffer, 4, 8); | 
 | 	      PUT_SHORT(buffer, 6, program->nHotKey); | 
 | 	      if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 8)) return FALSE; | 
 | 	    } | 
 |  | 
 | 	  /* Show command */ | 
 | 	  if (program->nCmdShow) | 
 | 	    { | 
 | 	      PUT_SHORT(buffer, 0, 0x8103); | 
 | 	      PUT_SHORT(buffer, 2, seqnum); | 
 | 	      PUT_SHORT(buffer, 4, 8); | 
 | 	      PUT_SHORT(buffer, 6, program->nCmdShow); | 
 | 	      if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 8)) return FALSE; | 
 | 	    } | 
 |  | 
 | 	  seqnum++; | 
 | 	  hProgram = program->hNext; | 
 | 	} | 
 |  | 
 |       /* Write `End' extension */ | 
 |       PUT_SHORT(buffer, 0, 0xffff); | 
 |       PUT_SHORT(buffer, 2, 0xffff); | 
 |       PUT_SHORT(buffer, 4, 0x0000); | 
 |       if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 6)) return FALSE; | 
 |     } | 
 |  | 
 |   checksum = GRPFILE_GetChecksum(); | 
 |   _llseek(file, 4, SEEK_SET); | 
 |   PUT_SHORT(buffer, 0, checksum); | 
 |   _lwrite(file, buffer, 2); | 
 |  | 
 |   return TRUE; | 
 | } | 
 |  | 
 | /* Local Variables:    */ | 
 | /* c-file-style: "GNU" */ | 
 | /* End:                */ |