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