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