grpfile.c 21 KB

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