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