[PROGMAN] Properly import from Wine Staging 1.9.4. This fixes PCH usage, marks ReactO...
[reactos.git] / reactos / base / shell / 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 #include "progman.h"
23
24 #define MALLOCHUNK 1000
25
26 #define GET_USHORT(buffer, i)\
27 (((BYTE)((buffer)[(i)]) + 0x100 * (BYTE)((buffer)[(i)+1])))
28 #define GET_SHORT(buffer, i)\
29 (((BYTE)((buffer)[(i)]) + 0x100 * (signed char)((buffer)[(i)+1])))
30 #define PUT_SHORT(buffer, i, s)\
31 (((buffer)[(i)] = (s) & 0xff, (buffer)[(i)+1] = ((s) >> 8) & 0xff))
32
33 static BOOL GRPFILE_ReadFileToBuffer(LPCSTR, HLOCAL*, INT*);
34 static HLOCAL GRPFILE_ScanGroup(LPCSTR, INT, LPCSTR, BOOL);
35 static HLOCAL GRPFILE_ScanProgram(LPCSTR, INT, LPCSTR, INT,
36 LPCSTR, HLOCAL,LPCSTR);
37 static BOOL GRPFILE_DoWriteGroupFile(HFILE file, PROGGROUP *group);
38
39 /***********************************************************************
40 *
41 * GRPFILE_ModifyFileName
42 *
43 * Change extension `.grp' to `.gr'
44 */
45
46 static VOID GRPFILE_ModifyFileName(LPSTR lpszNewName, LPCSTR lpszOrigName,
47 INT nSize, BOOL bModify)
48 {
49 lstrcpynA(lpszNewName, lpszOrigName, nSize);
50 lpszNewName[nSize-1] = '\0';
51 if (!bModify) return;
52 if (!lstrcmpiA(lpszNewName + strlen(lpszNewName) - 4, ".grp"))
53 lpszNewName[strlen(lpszNewName) - 1] = '\0';
54 }
55
56 /***********************************************************************
57 *
58 * GRPFILE_ReadGroupFile
59 */
60
61 HLOCAL GRPFILE_ReadGroupFile(LPCSTR lpszPath)
62 {
63 CHAR szPath_gr[MAX_PATHNAME_LEN];
64 BOOL bFileNameModified = FALSE;
65 OFSTRUCT dummy;
66 HLOCAL hBuffer, hGroup;
67 INT size;
68
69 /* if `.gr' file exists use that */
70 GRPFILE_ModifyFileName(szPath_gr, lpszPath, MAX_PATHNAME_LEN, TRUE);
71 if (OpenFile(szPath_gr, &dummy, OF_EXIST) != HFILE_ERROR)
72 {
73 lpszPath = szPath_gr;
74 bFileNameModified = TRUE;
75 }
76
77 /* Read the whole file into a buffer */
78 if (!GRPFILE_ReadFileToBuffer(lpszPath, &hBuffer, &size))
79 {
80 MAIN_MessageBoxIDS_s(IDS_GRPFILE_READ_ERROR_s, lpszPath, IDS_ERROR, MB_YESNO);
81 return(0);
82 }
83
84 /* Interpret buffer */
85 hGroup = GRPFILE_ScanGroup(LocalLock(hBuffer), size,
86 lpszPath, bFileNameModified);
87 if (!hGroup)
88 MAIN_MessageBoxIDS_s(IDS_GRPFILE_READ_ERROR_s, lpszPath, IDS_ERROR, MB_YESNO);
89
90 LocalFree(hBuffer);
91
92 return(hGroup);
93 }
94
95 /***********************************************************************
96 *
97 * GRPFILE_ReadFileToBuffer
98 */
99
100 static BOOL GRPFILE_ReadFileToBuffer(LPCSTR path, HLOCAL *phBuffer,
101 INT *piSize)
102 {
103 UINT len, size;
104 LPSTR buffer;
105 HLOCAL hBuffer, hNewBuffer;
106 HFILE file;
107
108 file=_lopen(path, OF_READ);
109 if (file == HFILE_ERROR) return FALSE;
110
111 size = 0;
112 hBuffer = LocalAlloc(LMEM_FIXED, MALLOCHUNK + 1);
113 if (!hBuffer) return FALSE;
114 buffer = LocalLock(hBuffer);
115
116 while ((len = _lread(file, buffer + size, MALLOCHUNK))
117 == MALLOCHUNK)
118 {
119 size += len;
120 hNewBuffer = LocalReAlloc(hBuffer, size + MALLOCHUNK + 1,
121 LMEM_MOVEABLE);
122 if (!hNewBuffer)
123 {
124 LocalFree(hBuffer);
125 return FALSE;
126 }
127 hBuffer = hNewBuffer;
128 buffer = LocalLock(hBuffer);
129 }
130
131 _lclose(file);
132
133 if (len == (UINT)HFILE_ERROR)
134 {
135 LocalFree(hBuffer);
136 return FALSE;
137 }
138
139 size += len;
140 buffer[size] = 0;
141
142 *phBuffer = hBuffer;
143 *piSize = size;
144 return TRUE;
145 }
146
147 /***********************************************************************
148 * GRPFILE_ScanGroup
149 */
150
151 static HLOCAL GRPFILE_ScanGroup(LPCSTR buffer, INT size,
152 LPCSTR lpszGrpFile,
153 BOOL bModifiedFileName)
154 {
155 HLOCAL hGroup;
156 INT i, seqnum;
157 LPCSTR extension;
158 LPCSTR lpszName;
159 INT x, y, width, height, iconx, icony, nCmdShow;
160 INT number_of_programs;
161 BOOL bOverwriteFileOk;
162
163 if (buffer[0] != 'P' || buffer[1] != 'M') return(0);
164 if (buffer[2] == 'C' && buffer[3] == 'C')
165 /* original with checksum */
166 bOverwriteFileOk = FALSE;
167 else if (buffer[2] == 'X' && buffer[3] == 'X')
168 /* modified without checksum */
169 bOverwriteFileOk = TRUE;
170 else return(0);
171
172 /* checksum = GET_USHORT(buffer, 4) (ignored) */
173
174 extension = buffer + GET_USHORT(buffer, 6);
175 if (extension == buffer + size) extension = 0;
176 else if (extension + 6 > buffer + size) return(0);
177
178 nCmdShow = GET_USHORT(buffer, 8);
179 x = GET_SHORT(buffer, 10);
180 y = GET_SHORT(buffer, 12);
181 width = GET_USHORT(buffer, 14);
182 height = GET_USHORT(buffer, 16);
183 iconx = GET_SHORT(buffer, 18);
184 icony = GET_SHORT(buffer, 20);
185 lpszName = buffer + GET_USHORT(buffer, 22);
186 if (lpszName >= buffer + size) return(0);
187
188 /* unknown bytes 24 - 31 ignored */
189 /*
190 Unknown bytes should be:
191 wLogPixelsX = GET_SHORT(buffer, 24);
192 wLogPixelsY = GET_SHORT(buffer, 26);
193 byBitsPerPixel = byte at 28;
194 byPlanes = byte at 29;
195 wReserved = GET_SHORT(buffer, 30);
196 */
197
198 hGroup = GROUP_AddGroup(lpszName, lpszGrpFile, nCmdShow, x, y,
199 width, height, iconx, icony,
200 bModifiedFileName, bOverwriteFileOk,
201 TRUE);
202 if (!hGroup) return(0);
203
204 number_of_programs = GET_USHORT(buffer, 32);
205 if (2 * number_of_programs + 34 > size) return(0);
206 for (i=0, seqnum=0; i < number_of_programs; i++, seqnum++)
207 {
208 LPCSTR program_ptr = buffer + GET_USHORT(buffer, 34 + 2*i);
209 if (program_ptr + 24 > buffer + size) return(0);
210 if (!GET_USHORT(buffer, 34 + 2*i)) continue;
211 if (!GRPFILE_ScanProgram(buffer, size, program_ptr, seqnum,
212 extension, hGroup, lpszGrpFile))
213 {
214 GROUP_DeleteGroup(hGroup);
215 return(0);
216 }
217 }
218
219 /* FIXME shouldn't be necessary */
220 GROUP_ShowGroupWindow(hGroup);
221
222 return hGroup;
223 }
224
225 /***********************************************************************
226 * GRPFILE_ScanProgram
227 */
228
229 static HLOCAL GRPFILE_ScanProgram(LPCSTR buffer, INT size,
230 LPCSTR program_ptr, INT seqnum,
231 LPCSTR extension, HLOCAL hGroup,
232 LPCSTR lpszGrpFile)
233 {
234 INT icontype;
235 HICON hIcon;
236 LPCSTR lpszName, lpszCmdLine, lpszIconFile, lpszWorkDir;
237 LPCSTR iconinfo_ptr, iconANDbits_ptr, iconXORbits_ptr;
238 INT x, y, nIconIndex, iconANDsize, iconXORsize;
239 INT nHotKey, nCmdShow;
240 UINT width, height, planes, bpp;
241
242 x = GET_SHORT(program_ptr, 0);
243 y = GET_SHORT(program_ptr, 2);
244 nIconIndex = GET_USHORT(program_ptr, 4);
245
246 /* FIXME is this correct ?? */
247 icontype = GET_USHORT(program_ptr, 6);
248 switch (icontype)
249 {
250 default:
251 MAIN_MessageBoxIDS_s(IDS_UNKNOWN_FEATURE_s, lpszGrpFile,
252 IDS_WARNING, MB_OK);
253 case 0x048c:
254 iconXORsize = GET_USHORT(program_ptr, 8);
255 iconANDsize = GET_USHORT(program_ptr, 10) / 8;
256 iconinfo_ptr = buffer + GET_USHORT(program_ptr, 12);
257 iconXORbits_ptr = buffer + GET_USHORT(program_ptr, 14);
258 iconANDbits_ptr = buffer + GET_USHORT(program_ptr, 16);
259 width = GET_USHORT(iconinfo_ptr, 4);
260 height = GET_USHORT(iconinfo_ptr, 6);
261 planes = GET_USHORT(iconinfo_ptr, 10);
262 bpp = GET_USHORT(iconinfo_ptr, 11);
263 break;
264 case 0x000c:
265 iconANDsize = GET_USHORT(program_ptr, 8);
266 iconXORsize = GET_USHORT(program_ptr, 10);
267 iconinfo_ptr = buffer + GET_USHORT(program_ptr, 12);
268 iconANDbits_ptr = buffer + GET_USHORT(program_ptr, 14);
269 iconXORbits_ptr = buffer + GET_USHORT(program_ptr, 16);
270 width = GET_USHORT(iconinfo_ptr, 4);
271 height = GET_USHORT(iconinfo_ptr, 6);
272 planes = GET_USHORT(iconinfo_ptr, 10);
273 bpp = GET_USHORT(iconinfo_ptr, 11);
274 }
275
276 if (iconANDbits_ptr + iconANDsize > buffer + size ||
277 iconXORbits_ptr + iconXORsize > buffer + size) return(0);
278
279 #ifdef __REACTOS__
280 hIcon = CreateIcon(Globals.hInstance, width, height, planes, bpp, (PBYTE)iconANDbits_ptr, (PBYTE)iconXORbits_ptr);
281 #else
282 hIcon = CreateIcon( Globals.hInstance, width, height, planes, bpp, iconANDbits_ptr, iconXORbits_ptr );
283 #endif
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 wsprintfA(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 != MessageBoxA(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 += strlen(LocalLock(program->hName)) + 1;
429 *Progs += strlen(LocalLock(program->hCmdLine)) + 1;
430 *Progs += strlen(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 #ifdef __REACTOS__
515 UINT sizeAnd, sizeXor;
516 #else
517 DWORD sizeAnd, sizeXor;
518 #endif
519 BOOL need_extension;
520 LPCSTR lpszTitle = LocalLock(group->hName);
521
522 UINT16 checksum;
523
524 GRPFILE_InitChecksum();
525
526 /* Calculate offsets */
527 NumProg = 0;
528 Icons = 0;
529 Extension = 0;
530 need_extension = FALSE;
531 hProgram = group->hPrograms;
532 while(hProgram)
533 {
534 PROGRAM *program = LocalLock(hProgram);
535 LPCSTR lpszWorkDir = LocalLock(program->hWorkDir);
536
537 NumProg++;
538 GRPFILE_CalculateSizes(program, &Icons, &Extension, &sizeAnd, &sizeXor);
539
540 /* Set a flag if an extension is needed */
541 if (lpszWorkDir[0] || program->nHotKey ||
542 program->nCmdShow != SW_SHOWNORMAL) need_extension = TRUE;
543
544 hProgram = program->hNext;
545 }
546 Title = 34 + NumProg * 2;
547 Progs = Title + strlen(lpszTitle) + 1;
548 Icons += Progs;
549 Extension += Icons;
550
551 /* Header */
552 buffer[0] = 'P';
553 buffer[1] = 'M';
554 buffer[2] = 'C';
555 buffer[3] = 'C';
556
557 PUT_SHORT(buffer, 4, 0); /* Checksum zero for now, written later */
558 PUT_SHORT(buffer, 6, Extension);
559 /* Update group->nCmdShow */
560 if (IsIconic(group->hWnd)) nCmdShow = SW_SHOWMINIMIZED;
561 else if (IsZoomed(group->hWnd)) nCmdShow = SW_SHOWMAXIMIZED;
562 else nCmdShow = SW_SHOWNORMAL;
563 PUT_SHORT(buffer, 8, nCmdShow);
564 PUT_SHORT(buffer, 10, group->x);
565 PUT_SHORT(buffer, 12, group->y);
566 PUT_SHORT(buffer, 14, group->width);
567 PUT_SHORT(buffer, 16, group->height);
568 PUT_SHORT(buffer, 18, group->iconx);
569 PUT_SHORT(buffer, 20, group->icony);
570 PUT_SHORT(buffer, 22, Title);
571 PUT_SHORT(buffer, 24, 0x0020); /* unknown */
572 PUT_SHORT(buffer, 26, 0x0020); /* unknown */
573 PUT_SHORT(buffer, 28, 0x0108); /* unknown */
574 PUT_SHORT(buffer, 30, 0x0000); /* unknown */
575 PUT_SHORT(buffer, 32, NumProg);
576
577 if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 34)) return FALSE;
578
579 /* Program table */
580 CurrProg = Progs;
581 CurrIcon = Icons;
582 hProgram = group->hPrograms;
583 while(hProgram)
584 {
585 PROGRAM *program = LocalLock(hProgram);
586
587 PUT_SHORT(buffer, 0, CurrProg);
588 if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 2))
589 return FALSE;
590
591 GRPFILE_CalculateSizes(program, &CurrProg, &CurrIcon, &sizeAnd, &sizeXor);
592 hProgram = program->hNext;
593 }
594
595 /* Title */
596 if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, lpszTitle, strlen(lpszTitle) + 1))
597 return FALSE;
598
599 /* Program entries */
600 CurrProg = Progs;
601 CurrIcon = Icons;
602 hProgram = group->hPrograms;
603 while(hProgram)
604 {
605 PROGRAM *program = LocalLock(hProgram);
606 LPCSTR Name = LocalLock(program->hName);
607 LPCSTR CmdLine = LocalLock(program->hCmdLine);
608 LPCSTR IconFile = LocalLock(program->hIconFile);
609 INT next_prog = CurrProg;
610 INT next_icon = CurrIcon;
611
612 GRPFILE_CalculateSizes(program, &next_prog, &next_icon, &sizeAnd, &sizeXor);
613 PUT_SHORT(buffer, 0, program->x);
614 PUT_SHORT(buffer, 2, program->y);
615 PUT_SHORT(buffer, 4, program->nIconIndex);
616 PUT_SHORT(buffer, 6, 0x048c); /* unknown */
617 PUT_SHORT(buffer, 8, sizeXor);
618 PUT_SHORT(buffer, 10, sizeAnd * 8);
619 PUT_SHORT(buffer, 12, CurrIcon);
620 PUT_SHORT(buffer, 14, CurrIcon + 12 + sizeAnd);
621 PUT_SHORT(buffer, 16, CurrIcon + 12);
622 ptr = CurrProg + 24;
623 PUT_SHORT(buffer, 18, ptr);
624 ptr += strlen(Name) + 1;
625 PUT_SHORT(buffer, 20, ptr);
626 ptr += strlen(CmdLine) + 1;
627 PUT_SHORT(buffer, 22, ptr);
628
629 if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 24) ||
630 (UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, Name, strlen(Name) + 1) ||
631 (UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, CmdLine, strlen(CmdLine) + 1) ||
632 (UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, IconFile, strlen(IconFile) + 1))
633 return FALSE;
634
635 CurrProg = next_prog;
636 CurrIcon = next_icon;
637 hProgram = program->hNext;
638 }
639
640 /* Icons */
641 #if 0 /* FIXME: this is broken anyway */
642 hProgram = group->hPrograms;
643 while(hProgram)
644 {
645 PROGRAM *program = LocalLock(hProgram);
646 CURSORICONINFO *iconinfo = LocalLock(program->hIcon);
647 LPVOID XorBits, AndBits;
648 INT sizeXor = iconinfo->nHeight * iconinfo->nWidthBytes;
649 INT sizeAnd = iconinfo->nHeight * ((iconinfo->nWidth + 15) / 16 * 2);
650 /* DumpIcon16(LocalLock(program->hIcon), 0, &XorBits, &AndBits);*/
651
652 PUT_SHORT(buffer, 0, iconinfo->ptHotSpot.x);
653 PUT_SHORT(buffer, 2, iconinfo->ptHotSpot.y);
654 PUT_SHORT(buffer, 4, iconinfo->nWidth);
655 PUT_SHORT(buffer, 6, iconinfo->nHeight);
656 PUT_SHORT(buffer, 8, iconinfo->nWidthBytes);
657 buffer[10] = iconinfo->bPlanes;
658 buffer[11] = iconinfo->bBitsPerPixel;
659
660 if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 12) ||
661 (UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, AndBits, sizeAnd) ||
662 (UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, XorBits, sizeXor)) return FALSE;
663
664 hProgram = program->hNext;
665 }
666 #endif
667
668 if (need_extension)
669 {
670 /* write `PMCC' extension */
671 PUT_SHORT(buffer, 0, 0x8000);
672 PUT_SHORT(buffer, 2, 0xffff);
673 PUT_SHORT(buffer, 4, 0x000a);
674 buffer[6] = 'P', buffer[7] = 'M';
675 buffer[8] = 'C', buffer[9] = 'C';
676 if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 10))
677 return FALSE;
678
679 seqnum = 0;
680 hProgram = group->hPrograms;
681 while(hProgram)
682 {
683 PROGRAM *program = LocalLock(hProgram);
684 LPCSTR lpszWorkDir = LocalLock(program->hWorkDir);
685
686 /* Working directory */
687 if (lpszWorkDir[0])
688 {
689 PUT_SHORT(buffer, 0, 0x8101);
690 PUT_SHORT(buffer, 2, seqnum);
691 PUT_SHORT(buffer, 4, 7 + strlen(lpszWorkDir));
692 if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 6) ||
693 (UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, lpszWorkDir, strlen(lpszWorkDir) + 1))
694 return FALSE;
695 }
696
697 /* Hot key */
698 if (program->nHotKey)
699 {
700 PUT_SHORT(buffer, 0, 0x8102);
701 PUT_SHORT(buffer, 2, seqnum);
702 PUT_SHORT(buffer, 4, 8);
703 PUT_SHORT(buffer, 6, program->nHotKey);
704 if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 8)) return FALSE;
705 }
706
707 /* Show command */
708 if (program->nCmdShow)
709 {
710 PUT_SHORT(buffer, 0, 0x8103);
711 PUT_SHORT(buffer, 2, seqnum);
712 PUT_SHORT(buffer, 4, 8);
713 PUT_SHORT(buffer, 6, program->nCmdShow);
714 if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 8)) return FALSE;
715 }
716
717 seqnum++;
718 hProgram = program->hNext;
719 }
720
721 /* Write `End' extension */
722 PUT_SHORT(buffer, 0, 0xffff);
723 PUT_SHORT(buffer, 2, 0xffff);
724 PUT_SHORT(buffer, 4, 0x0000);
725 if ((UINT)HFILE_ERROR == GRPFILE_WriteWithChecksum(file, buffer, 6)) return FALSE;
726 }
727
728 checksum = GRPFILE_GetChecksum();
729 _llseek(file, 4, SEEK_SET);
730 PUT_SHORT(buffer, 0, checksum);
731 _lwrite(file, buffer, 2);
732
733 return TRUE;
734 }