setupapi: Add tests for SetupDiCreateDeviceInfo.
[wine] / programs / progman / grpfile.c
1 /*
2  * Program Manager
3  *
4  * Copyright 1996 Ulrich Schmid
5  *           1997 Peter Schlaile
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #define WIN32_LEAN_AND_MEAN
23
24 #include "windows.h"
25 #include "wine/winuser16.h"
26 #include "progman.h"
27 #include "mmsystem.h"
28
29 #define MALLOCHUNK 1000
30
31 #define GET_USHORT(buffer, i)\
32   (((BYTE)((buffer)[(i)]) + 0x100 * (BYTE)((buffer)[(i)+1])))
33 #define GET_SHORT(buffer, i)\
34   (((BYTE)((buffer)[(i)]) + 0x100 * (signed char)((buffer)[(i)+1])))
35 #define PUT_SHORT(buffer, i, s)\
36   (((buffer)[(i)] = (s) & 0xff, (buffer)[(i)+1] = ((s) >> 8) & 0xff))
37
38 static BOOL   GRPFILE_ReadFileToBuffer(LPCSTR, HLOCAL*, INT*);
39 static HLOCAL GRPFILE_ScanGroup(LPCSTR, INT, LPCSTR, BOOL);
40 static HLOCAL GRPFILE_ScanProgram(LPCSTR, INT, LPCSTR, INT,
41                                   LPCSTR, HLOCAL,LPCSTR);
42 static BOOL GRPFILE_DoWriteGroupFile(HFILE file, PROGGROUP *group);
43
44 /***********************************************************************
45  *
46  *           GRPFILE_ModifyFileName
47  *
48  *  Change extension `.grp' to `.gr'
49  */
50
51 static VOID GRPFILE_ModifyFileName(LPSTR lpszNewName, LPCSTR lpszOrigName,
52                                    INT nSize, BOOL bModify)
53 {
54   lstrcpyn(lpszNewName, lpszOrigName, nSize);
55   lpszNewName[nSize-1] = '\0';
56   if (!bModify) return;
57   if (!lstrcmpi(lpszNewName + strlen(lpszNewName) - 4, ".grp"))
58     lpszNewName[strlen(lpszNewName) - 1] = '\0';
59 }
60
61 /***********************************************************************
62  *
63  *           GRPFILE_ReadGroupFile
64  */
65
66 HLOCAL GRPFILE_ReadGroupFile(LPCSTR lpszPath)
67 {
68   CHAR   szPath_gr[MAX_PATHNAME_LEN];
69   BOOL   bFileNameModified = FALSE;
70   OFSTRUCT dummy;
71   HLOCAL hBuffer, hGroup;
72   INT    size;
73
74   /* if `.gr' file exists use that */
75   GRPFILE_ModifyFileName(szPath_gr, lpszPath, MAX_PATHNAME_LEN, TRUE);
76   if (OpenFile(szPath_gr, &dummy, OF_EXIST) != HFILE_ERROR)
77     {
78       lpszPath = szPath_gr;
79       bFileNameModified = TRUE;
80     }
81
82   /* Read the whole file into a buffer */
83   if (!GRPFILE_ReadFileToBuffer(lpszPath, &hBuffer, &size))
84     {
85       MAIN_MessageBoxIDS_s(IDS_GRPFILE_READ_ERROR_s, lpszPath, IDS_ERROR, MB_YESNO);
86       return(0);
87     }
88
89   /* Interpret buffer */
90   hGroup = GRPFILE_ScanGroup(LocalLock(hBuffer), size,
91                              lpszPath, bFileNameModified);
92   if (!hGroup)
93     MAIN_MessageBoxIDS_s(IDS_GRPFILE_READ_ERROR_s, lpszPath, IDS_ERROR, MB_YESNO);
94
95   LocalFree(hBuffer);
96
97   return(hGroup);
98 }
99
100 /***********************************************************************
101  *
102  *           GRPFILE_ReadFileToBuffer
103  */
104
105 static BOOL GRPFILE_ReadFileToBuffer(LPCSTR path, HLOCAL *phBuffer,
106                                      INT *piSize)
107 {
108   UINT    len, size;
109   LPSTR  buffer;
110   HLOCAL hBuffer, hNewBuffer;
111   HFILE  file;
112
113   file=_lopen(path, OF_READ);
114   if (file == HFILE_ERROR) return FALSE;
115
116   size = 0;
117   hBuffer = LocalAlloc(LMEM_FIXED, size + MALLOCHUNK + 1);
118   if (!hBuffer) return FALSE;
119   buffer = LocalLock(hBuffer);
120
121   while ((len = _lread(file, buffer + size, MALLOCHUNK))
122          == MALLOCHUNK)
123     {
124       size += len;
125       hNewBuffer = LocalReAlloc(hBuffer, size + MALLOCHUNK + 1,
126                                 LMEM_FIXED);
127       if (!hNewBuffer)
128         {
129           LocalFree(hBuffer);
130           return FALSE;
131         }
132       hBuffer = hNewBuffer;
133       buffer = LocalLock(hBuffer);
134     }
135
136   _lclose(file);
137
138   if (len == (UINT)HFILE_ERROR)
139     {
140       LocalFree(hBuffer);
141       return FALSE;
142     }
143
144   size += len;
145   buffer[size] = 0;
146
147   *phBuffer = hBuffer;
148   *piSize   = size;
149   return TRUE;
150 }
151
152 /***********************************************************************
153  *           GRPFILE_ScanGroup
154  */
155
156 static HLOCAL GRPFILE_ScanGroup(LPCSTR buffer, INT size,
157                                 LPCSTR lpszGrpFile,
158                                 BOOL bModifiedFileName)
159 {
160   HLOCAL  hGroup;
161   INT     i, seqnum;
162   LPCSTR  extension;
163   LPCSTR  lpszName;
164   INT     x, y, width, height, iconx, icony, nCmdShow;
165   INT     number_of_programs;
166   BOOL    bOverwriteFileOk;
167
168   if (buffer[0] != 'P' || buffer[1] != 'M') return(0);
169   if (buffer[2] == 'C' && buffer[3] == 'C')
170     /* original with checksum */
171     bOverwriteFileOk = FALSE;
172   else if (buffer[2] == 'X' && buffer[3] == 'X')
173     /* modified without checksum */
174     bOverwriteFileOk = TRUE;
175   else return(0);
176
177   /* checksum = GET_USHORT(buffer, 4)   (ignored) */
178
179   extension = buffer + GET_USHORT(buffer, 6);
180   if (extension == buffer + size) extension = 0;
181   else if (extension + 6 > buffer + size) return(0);
182
183   nCmdShow = GET_USHORT(buffer,  8);
184   x        = GET_SHORT(buffer,  10);
185   y        = GET_SHORT(buffer,  12);
186   width    = GET_USHORT(buffer, 14);
187   height   = GET_USHORT(buffer, 16);
188   iconx    = GET_SHORT(buffer,  18);
189   icony    = GET_SHORT(buffer,  20);
190   lpszName = buffer + GET_USHORT(buffer, 22);
191   if (lpszName >= buffer + size) return(0);
192
193   /* unknown bytes 24 - 31 ignored */
194   /*
195     Unknown bytes should be:
196     wLogPixelsX = GET_SHORT(buffer, 24);
197     wLogPixelsY = GET_SHORT(buffer, 26);
198     byBitsPerPixel = byte at 28;
199     byPlanes     = byte at 29;
200     wReserved   = GET_SHORT(buffer, 30);
201     */
202
203   hGroup = GROUP_AddGroup(lpszName, lpszGrpFile, nCmdShow, x, y,
204                           width, height, iconx, icony,
205                           bModifiedFileName, bOverwriteFileOk,
206                           TRUE);
207   if (!hGroup) return(0);
208
209   number_of_programs = GET_USHORT(buffer, 32);
210   if (2 * number_of_programs + 34 > size) return(0);
211   for (i=0, seqnum=0; i < number_of_programs; i++, seqnum++)
212     {
213       LPCSTR program_ptr = buffer + GET_USHORT(buffer, 34 + 2*i);
214       if (program_ptr + 24 > buffer + size) return(0);
215       if (!GET_USHORT(buffer, 34 + 2*i)) continue;
216       if (!GRPFILE_ScanProgram(buffer, size, program_ptr, seqnum,
217                                extension, hGroup, lpszGrpFile))
218         {
219           GROUP_DeleteGroup(hGroup);
220           return(0);
221         }
222     }
223
224   /* FIXME shouldn't be necessary */
225   GROUP_ShowGroupWindow(hGroup);
226
227   return hGroup;
228 }
229
230 /***********************************************************************
231  *           GRPFILE_ScanProgram
232  */
233
234 static HLOCAL GRPFILE_ScanProgram(LPCSTR buffer, INT size,
235                                   LPCSTR program_ptr, INT seqnum,
236                                   LPCSTR extension, HLOCAL hGroup,
237                                   LPCSTR lpszGrpFile)
238 {
239   INT    icontype;
240   HICON  hIcon;
241   LPCSTR lpszName, lpszCmdLine, lpszIconFile, lpszWorkDir;
242   LPCSTR iconinfo_ptr, iconANDbits_ptr, iconXORbits_ptr;
243   INT    x, y, nIconIndex, iconANDsize, iconXORsize;
244   INT    nHotKey, nCmdShow;
245   CURSORICONINFO iconinfo;
246
247   x               = GET_SHORT(program_ptr, 0);
248   y               = GET_SHORT(program_ptr, 2);
249   nIconIndex      = GET_USHORT(program_ptr, 4);
250
251   /* FIXME is this correct ?? */
252   icontype = GET_USHORT(program_ptr,  6);
253   switch (icontype)
254     {
255     default:
256       MAIN_MessageBoxIDS_s(IDS_UNKNOWN_FEATURE_s, lpszGrpFile,
257                            IDS_WARNING, MB_OK);
258     case 0x048c:
259       iconXORsize     = GET_USHORT(program_ptr,  8);
260       iconANDsize     = GET_USHORT(program_ptr, 10) / 8;
261       iconinfo_ptr    = buffer + GET_USHORT(program_ptr, 12);
262       iconXORbits_ptr = buffer + GET_USHORT(program_ptr, 14);
263       iconANDbits_ptr = buffer + GET_USHORT(program_ptr, 16);
264       iconinfo.ptHotSpot.x   = GET_USHORT(iconinfo_ptr, 0);
265       iconinfo.ptHotSpot.y   = GET_USHORT(iconinfo_ptr, 2);
266       iconinfo.nWidth        = GET_USHORT(iconinfo_ptr, 4);
267       iconinfo.nHeight       = GET_USHORT(iconinfo_ptr, 6);
268       iconinfo.nWidthBytes   = GET_USHORT(iconinfo_ptr, 8);
269       iconinfo.bPlanes       = GET_USHORT(iconinfo_ptr, 10);
270       iconinfo.bBitsPerPixel = GET_USHORT(iconinfo_ptr, 11);
271       break;
272     case 0x000c:
273       iconANDsize     = GET_USHORT(program_ptr,  8);
274       iconXORsize     = GET_USHORT(program_ptr, 10);
275       iconinfo_ptr    = buffer + GET_USHORT(program_ptr, 12);
276       iconANDbits_ptr = buffer + GET_USHORT(program_ptr, 14);
277       iconXORbits_ptr = buffer + GET_USHORT(program_ptr, 16);
278       iconinfo.ptHotSpot.x   = GET_USHORT(iconinfo_ptr, 0);
279       iconinfo.ptHotSpot.y   = GET_USHORT(iconinfo_ptr, 2);
280       iconinfo.nWidth        = GET_USHORT(iconinfo_ptr, 4);
281       iconinfo.nHeight       = GET_USHORT(iconinfo_ptr, 6);
282       iconinfo.nWidthBytes = GET_USHORT(iconinfo_ptr, 8) * 8;
283       iconinfo.bPlanes       = GET_USHORT(iconinfo_ptr, 10);
284       iconinfo.bBitsPerPixel = GET_USHORT(iconinfo_ptr, 11);
285     }
286
287   if (iconANDbits_ptr + iconANDsize > buffer + size ||
288       iconXORbits_ptr + iconXORsize > buffer + size) return(0);
289
290   hIcon = CreateIcon( Globals.hInstance, iconinfo.nWidth, iconinfo.nHeight,
291                       iconinfo.bPlanes, iconinfo.bBitsPerPixel,
292                       iconANDbits_ptr, iconXORbits_ptr );
293
294   lpszName        = buffer + GET_USHORT(program_ptr, 18);
295   lpszCmdLine     = buffer + GET_USHORT(program_ptr, 20);
296   lpszIconFile    = buffer + GET_USHORT(program_ptr, 22);
297   if (iconinfo_ptr + 6 > buffer + size ||
298       lpszName         > buffer + size ||
299       lpszCmdLine      > buffer + size ||
300       lpszIconFile     > buffer + size) return(0);
301
302   /* Scan Extensions */
303   lpszWorkDir = "";
304   nHotKey     = 0;
305   nCmdShow    = SW_SHOWNORMAL;
306   if (extension)
307     {
308       LPCSTR ptr = extension;
309       while (ptr + 6 <= buffer + size)
310         {
311           UINT type   = GET_USHORT(ptr, 0);
312           UINT number = GET_USHORT(ptr, 2);
313           UINT skip   = GET_USHORT(ptr, 4);
314
315           if (number == seqnum)
316             {
317               switch (type)
318                 {
319                 case 0x8000:
320                   if (ptr + 10 > buffer + size) return(0);
321                   if (ptr[6] != 'P' || ptr[7] != 'M' ||
322                       ptr[8] != 'C' || ptr[9] != 'C') return(0);
323                   break;
324                 case 0x8101:
325                   lpszWorkDir = ptr + 6;
326                   break;
327                 case 0x8102:
328                   if (ptr + 8 > buffer + size) return(0);
329                   nHotKey = GET_USHORT(ptr, 6);
330                   break;
331                 case 0x8103:
332                   if (ptr + 8 > buffer + size) return(0);
333                   nCmdShow = GET_USHORT(ptr, 6);
334                   break;
335                 default:
336                   MAIN_MessageBoxIDS_s(IDS_UNKNOWN_FEATURE_s,
337                                        lpszGrpFile, IDS_WARNING, MB_OK);
338                 }
339             }
340           if (!skip) break;
341           ptr += skip;
342         }
343     }
344
345   return (PROGRAM_AddProgram(hGroup, hIcon, lpszName, x, y,
346                              lpszCmdLine, lpszIconFile,
347                              nIconIndex, lpszWorkDir,
348                              nHotKey, nCmdShow));
349 }
350
351 /***********************************************************************
352  *
353  *           GRPFILE_WriteGroupFile
354  */
355
356 BOOL GRPFILE_WriteGroupFile(HLOCAL hGroup)
357 {
358   CHAR szPath[MAX_PATHNAME_LEN];
359   PROGGROUP *group = LocalLock(hGroup);
360   OFSTRUCT dummy;
361   HFILE file;
362   BOOL ret;
363
364   GRPFILE_ModifyFileName(szPath, LocalLock(group->hGrpFile),
365                          MAX_PATHNAME_LEN,
366                          group->bFileNameModified);
367
368   /* Try not to overwrite original files */
369
370   /* group->bOverwriteFileOk == TRUE only if a file has the modified format */
371   if (!group->bOverwriteFileOk &&
372       OpenFile(szPath, &dummy, OF_EXIST) != HFILE_ERROR)
373     {
374       /* Original file exists, try `.gr' extension */
375       GRPFILE_ModifyFileName(szPath, LocalLock(group->hGrpFile),
376                              MAX_PATHNAME_LEN, TRUE);
377       if (OpenFile(szPath, &dummy, OF_EXIST) != HFILE_ERROR)
378         {
379           /* File exists. Do not overwrite */
380           MAIN_MessageBoxIDS_s(IDS_FILE_NOT_OVERWRITTEN_s, szPath,
381                                IDS_INFO, MB_OK);
382           return FALSE;
383         }
384       /* Inform about the modified file name */
385       if (IDCANCEL ==
386           MAIN_MessageBoxIDS_s(IDS_SAVE_GROUP_AS_s, szPath, IDS_INFO,
387                                MB_OKCANCEL | MB_ICONINFORMATION))
388         return FALSE;
389     }
390
391   {
392     /* Warn about the (possible) incompatibility */
393     CHAR msg[MAX_PATHNAME_LEN + 200];
394     wsprintf(msg,
395              "Group files written by this DRAFT Program Manager "
396              "possibly cannot be read by the Microsoft Program Manager!!\n"
397              "Are you sure to write %s?", szPath);
398     if (IDOK != MessageBox(Globals.hMainWnd, msg, "WARNING",
399                            MB_OKCANCEL | MB_DEFBUTTON2)) return FALSE;
400   }
401
402   /* Open file */
403   file = _lcreat(szPath, 0);
404   if (file != HFILE_ERROR)
405     {
406       ret = GRPFILE_DoWriteGroupFile(file, group);
407       _lclose(file);
408     }
409   else ret = FALSE;
410
411   if (!ret)
412     MAIN_MessageBoxIDS_s(IDS_FILE_WRITE_ERROR_s, szPath, IDS_ERROR, MB_OK);
413
414   return(ret);
415 }
416
417 /***********************************************************************
418  *
419  *           GRPFILE_CalculateSizes
420  */
421
422 static VOID GRPFILE_CalculateSizes(PROGRAM *program,
423                                    INT *Progs, INT *Icons)
424 {
425   CURSORICONINFO *iconinfo = LocalLock(program->hIcon);
426   INT sizeXor = iconinfo->nHeight * iconinfo->nWidthBytes;
427   INT sizeAnd = iconinfo->nHeight * ((iconinfo->nWidth + 15) / 16 * 2);
428
429   *Progs += 24;
430   *Progs += lstrlen(LocalLock(program->hName)) + 1;
431   *Progs += lstrlen(LocalLock(program->hCmdLine)) + 1;
432   *Progs += lstrlen(LocalLock(program->hIconFile)) + 1;
433
434   *Icons += 12; /* IconInfo */
435   *Icons += sizeAnd;
436   *Icons += sizeXor;
437 }
438
439 /***********************************************************************/
440 UINT16 GRPFILE_checksum;
441 BOOL GRPFILE_checksum_half_word;
442 BYTE GRPFILE_checksum_last_byte;
443 /***********************************************************************
444  *
445  *           GRPFILE_InitChecksum
446  */
447
448 static void GRPFILE_InitChecksum(void)
449 {
450         GRPFILE_checksum = 0;
451         GRPFILE_checksum_half_word = 0;
452 }
453
454 /***********************************************************************
455  *
456  *           GRPFILE_GetChecksum
457  */
458
459 static UINT16 GRPFILE_GetChecksum(void)
460 {
461         return GRPFILE_checksum;
462 }
463
464 /***********************************************************************
465  *
466  *           GRPFILE_WriteWithChecksum
467  *
468  * Looks crazier than it is:
469  *
470  * chksum = 0;
471  * chksum = cksum - 1. word;
472  * chksum = cksum - 2. word;
473  * ...
474  *
475  * if (filelen is even)
476  *      great I'm finished
477  * else
478  *      ignore last byte
479  */
480
481 static UINT GRPFILE_WriteWithChecksum(HFILE file, LPCSTR str, UINT size)
482 {
483         UINT i;
484         if (GRPFILE_checksum_half_word) {
485                 GRPFILE_checksum -= GRPFILE_checksum_last_byte;
486         }
487         for (i=0; i < size; i++) {
488                 if (GRPFILE_checksum_half_word) {
489                         GRPFILE_checksum -= str[i] << 8;
490                 } else {
491                         GRPFILE_checksum -= str[i];
492                 }
493                 GRPFILE_checksum_half_word ^= 1;
494         }
495
496         if (GRPFILE_checksum_half_word) {
497                 GRPFILE_checksum_last_byte = str[size-1];
498                 GRPFILE_checksum += GRPFILE_checksum_last_byte;
499         }
500
501         return _lwrite(file, str, size);
502 }
503
504
505 /***********************************************************************
506  *
507  *           GRPFILE_DoWriteGroupFile
508  */
509
510 static BOOL GRPFILE_DoWriteGroupFile(HFILE file, PROGGROUP *group)
511 {
512   CHAR buffer[34];
513   HLOCAL hProgram;
514   INT    NumProg, Title, Progs, Icons, Extension;
515   INT    CurrProg, CurrIcon, nCmdShow, ptr, seqnum;
516   BOOL   need_extension;
517   LPCSTR lpszTitle = LocalLock(group->hName);
518
519   UINT16 checksum;
520
521   GRPFILE_InitChecksum();
522
523   /* Calculate offsets */
524   NumProg = 0;
525   Icons   = 0;
526   Extension = 0;
527   need_extension = FALSE;
528   hProgram = group->hPrograms;
529   while(hProgram)
530     {
531       PROGRAM *program = LocalLock(hProgram);
532       LPCSTR lpszWorkDir = LocalLock(program->hWorkDir);
533
534       NumProg++;
535       GRPFILE_CalculateSizes(program, &Icons, &Extension);
536
537       /* Set a flag if an extension is needed */
538       if (lpszWorkDir[0] || program->nHotKey ||
539           program->nCmdShow != SW_SHOWNORMAL) need_extension = TRUE;
540
541       hProgram = program->hNext;
542     }
543   Title      = 34 + NumProg * 2;
544   Progs      = Title + lstrlen(lpszTitle) + 1;
545   Icons     += Progs;
546   Extension += Icons;
547
548   /* Header */
549   buffer[0] = 'P';
550   buffer[1] = 'M';
551   buffer[2] = 'C';
552   buffer[3] = 'C';
553
554   PUT_SHORT(buffer,  4, 0); /* Checksum zero for now, written later */
555   PUT_SHORT(buffer,  6, Extension);
556   /* Update group->nCmdShow */
557   if (IsIconic(group->hWnd))      nCmdShow = SW_SHOWMINIMIZED;
558   else if (IsZoomed(group->hWnd)) nCmdShow = SW_SHOWMAXIMIZED;
559   else                            nCmdShow = SW_SHOWNORMAL;
560   PUT_SHORT(buffer,  8, nCmdShow);
561   PUT_SHORT(buffer, 10, group->x);
562   PUT_SHORT(buffer, 12, group->y);
563   PUT_SHORT(buffer, 14, group->width);
564   PUT_SHORT(buffer, 16, group->height);
565   PUT_SHORT(buffer, 18, group->iconx);
566   PUT_SHORT(buffer, 20, group->icony);
567   PUT_SHORT(buffer, 22, Title);
568   PUT_SHORT(buffer, 24, 0x0020); /* unknown */
569   PUT_SHORT(buffer, 26, 0x0020); /* unknown */
570   PUT_SHORT(buffer, 28, 0x0108); /* unknown */
571   PUT_SHORT(buffer, 30, 0x0000); /* unknown */
572   PUT_SHORT(buffer, 32, NumProg);
573
574   if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 34)) return FALSE;
575
576   /* Program table */
577   CurrProg = Progs;
578   CurrIcon = Icons;
579   hProgram = group->hPrograms;
580   while(hProgram)
581     {
582       PROGRAM *program = LocalLock(hProgram);
583
584       PUT_SHORT(buffer, 0, CurrProg);
585       if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 2))
586               return FALSE;
587
588       GRPFILE_CalculateSizes(program, &CurrProg, &CurrIcon);
589       hProgram = program->hNext;
590     }
591
592   /* Title */
593   if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, lpszTitle,
594                                                lstrlen(lpszTitle) + 1))
595     return FALSE;
596
597   /* Program entries */
598   CurrProg = Progs;
599   CurrIcon = Icons;
600   hProgram = group->hPrograms;
601   while(hProgram)
602     {
603       PROGRAM *program = LocalLock(hProgram);
604       CURSORICONINFO *iconinfo = LocalLock(program->hIcon);
605       LPCSTR Name     = LocalLock(program->hName);
606       LPCSTR CmdLine  = LocalLock(program->hCmdLine);
607       LPCSTR IconFile = LocalLock(program->hIconFile);
608       INT sizeXor = iconinfo->nHeight * iconinfo->nWidthBytes;
609       INT sizeAnd = iconinfo->nHeight * ((iconinfo->nWidth + 15) / 16 * 2);
610
611       PUT_SHORT(buffer,  0, program->x);
612       PUT_SHORT(buffer,  2, program->y);
613       PUT_SHORT(buffer,  4, program->nIconIndex);
614       PUT_SHORT(buffer,  6, 0x048c);            /* unknown */
615       PUT_SHORT(buffer,  8, sizeXor);
616       PUT_SHORT(buffer, 10, sizeAnd * 8);
617       PUT_SHORT(buffer, 12, CurrIcon);
618       PUT_SHORT(buffer, 14, CurrIcon + 12 + sizeAnd);
619       PUT_SHORT(buffer, 16, CurrIcon + 12);
620       ptr = CurrProg + 24;
621       PUT_SHORT(buffer, 18, ptr);
622       ptr += lstrlen(Name) + 1;
623       PUT_SHORT(buffer, 20, ptr);
624       ptr += lstrlen(CmdLine) + 1;
625       PUT_SHORT(buffer, 22, ptr);
626
627       if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 24) ||
628           (UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, Name, lstrlen(Name) + 1) ||
629           (UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, CmdLine, lstrlen(CmdLine) + 1) ||
630           (UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, IconFile, lstrlen(IconFile) + 1))
631         return FALSE;
632
633       GRPFILE_CalculateSizes(program, &CurrProg, &CurrIcon);
634       hProgram = program->hNext;
635     }
636
637   /* Icons */
638 #if 0  /* FIXME: this is broken anyway */
639   hProgram = group->hPrograms;
640   while(hProgram)
641     {
642       PROGRAM *program = LocalLock(hProgram);
643       CURSORICONINFO *iconinfo = LocalLock(program->hIcon);
644       LPVOID XorBits, AndBits;
645       INT sizeXor = iconinfo->nHeight * iconinfo->nWidthBytes;
646       INT sizeAnd = iconinfo->nHeight * ((iconinfo->nWidth + 15) / 16 * 2);
647       /* DumpIcon16(LocalLock(program->hIcon), 0, &XorBits, &AndBits);*/
648
649       PUT_SHORT(buffer, 0, iconinfo->ptHotSpot.x);
650       PUT_SHORT(buffer, 2, iconinfo->ptHotSpot.y);
651       PUT_SHORT(buffer, 4, iconinfo->nWidth);
652       PUT_SHORT(buffer, 6, iconinfo->nHeight);
653       PUT_SHORT(buffer, 8, iconinfo->nWidthBytes);
654       buffer[10] = iconinfo->bPlanes;
655       buffer[11] = iconinfo->bBitsPerPixel;
656
657       if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 12) ||
658           (UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, AndBits, sizeAnd) ||
659           (UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, XorBits, sizeXor)) return FALSE;
660
661       hProgram = program->hNext;
662     }
663 #endif
664
665   if (need_extension)
666     {
667       /* write `PMCC' extension */
668       PUT_SHORT(buffer, 0, 0x8000);
669       PUT_SHORT(buffer, 2, 0xffff);
670       PUT_SHORT(buffer, 4, 0x000a);
671       buffer[6] = 'P', buffer[7] = 'M';
672       buffer[8] = 'C', buffer[9] = 'C';
673       if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 10))
674               return FALSE;
675
676       seqnum = 0;
677       hProgram = group->hPrograms;
678       while(hProgram)
679         {
680           PROGRAM *program = LocalLock(hProgram);
681           LPCSTR lpszWorkDir = LocalLock(program->hWorkDir);
682
683           /* Working directory */
684           if (lpszWorkDir[0])
685             {
686               PUT_SHORT(buffer, 0, 0x8101);
687               PUT_SHORT(buffer, 2, seqnum);
688               PUT_SHORT(buffer, 4, 7 + lstrlen(lpszWorkDir));
689               if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 6) ||
690                   (UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, lpszWorkDir, lstrlen(lpszWorkDir) + 1))
691                 return FALSE;
692             }
693
694           /* Hot key */
695           if (program->nHotKey)
696             {
697               PUT_SHORT(buffer, 0, 0x8102);
698               PUT_SHORT(buffer, 2, seqnum);
699               PUT_SHORT(buffer, 4, 8);
700               PUT_SHORT(buffer, 6, program->nHotKey);
701               if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 8)) return FALSE;
702             }
703
704           /* Show command */
705           if (program->nCmdShow)
706             {
707               PUT_SHORT(buffer, 0, 0x8103);
708               PUT_SHORT(buffer, 2, seqnum);
709               PUT_SHORT(buffer, 4, 8);
710               PUT_SHORT(buffer, 6, program->nCmdShow);
711               if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 8)) return FALSE;
712             }
713
714           seqnum++;
715           hProgram = program->hNext;
716         }
717
718       /* Write `End' extension */
719       PUT_SHORT(buffer, 0, 0xffff);
720       PUT_SHORT(buffer, 2, 0xffff);
721       PUT_SHORT(buffer, 4, 0x0000);
722       if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 6)) return FALSE;
723     }
724
725   checksum = GRPFILE_GetChecksum();
726   _llseek(file, 4, SEEK_SET);
727   PUT_SHORT(buffer, 0, checksum);
728   _lwrite(file, buffer, 2);
729
730   return TRUE;
731 }
732
733 /* Local Variables:    */
734 /* c-file-style: "GNU" */
735 /* End:                */