Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

689 lines
17 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name:MF.C (Extensible Compound Documents - Metafile)
  3. *
  4. * PURPOSE:Handles all API routines for the metafile sub-dll of the ole dll.
  5. *
  6. * Created: 1990
  7. *
  8. * Copyright (c) 1990, 1991 Microsoft Corporation
  9. *
  10. * History:
  11. *
  12. * Raor, Srinik (../../1990,91) Designed, coded
  13. * Curts create NT version
  14. *
  15. \***************************************************************************/
  16. #include <windows.h>
  17. #include "dll.h"
  18. #include "pict.h"
  19. WORD wGDIds = 0;
  20. OLESTATUS FARINTERNAL wCreateDummyMetaFile (LPOBJECT_MF, int, int);
  21. OLEOBJECTVTBL vtblMF = {
  22. ErrQueryProtocol, // check whether the speced protocol is supported
  23. MfRelease, // Release
  24. ErrShow, // show
  25. ErrPlay, // play
  26. MfGetData, // Get the object data
  27. ErrSetData, // Set the object data
  28. ErrSetTargetDevice,//
  29. ErrSetBounds, // set viewport bounds
  30. MfEnumFormat, // enumerate supported formats
  31. ErrSetColorScheme, //
  32. MfRelease, // delete
  33. ErrSetHostNames, //
  34. MfSaveToStream, // write to file
  35. MfClone, // clone object
  36. ErrCopyFromLink, // Create embedded from Lnk
  37. MfEqual, // compares the given objects for data equality
  38. MfCopy, // copy to clip
  39. MfDraw, // draw the object
  40. ErrActivate, // open
  41. ErrExecute, // excute
  42. ErrClose, // stop
  43. ErrUpdate, // Update
  44. ErrReconnect, // Reconnect
  45. ErrObjectConvert, // convert object to specified type
  46. ErrGetUpdateOptions, // update options
  47. ErrSetUpdateOptions, // update options
  48. ObjRename, // Change Object name
  49. ObjQueryName, // Get current object name
  50. ObjQueryType, // Object type
  51. MfQueryBounds, // QueryBounds
  52. ObjQuerySize, // Find the size of the object
  53. ErrQueryOpen, // Query open
  54. ErrQueryOutOfDate, // query whether object is current
  55. ErrQueryRelease, // release related stuff
  56. ErrQueryRelease,
  57. ErrQueryReleaseMethod,
  58. ErrRequestData, // requestdata
  59. ErrObjectLong, // objectLong
  60. MfChangeData // change data of the existing object
  61. };
  62. OLESTATUS FARINTERNAL MfRelease (LPOLEOBJECT lpoleobj)
  63. {
  64. LPOBJECT_MF lpobj = (LPOBJECT_MF)lpoleobj;
  65. HOBJECT hobj;
  66. if (lpobj->mfp.hMF) {
  67. DeleteMetaFile (lpobj->mfp.hMF);
  68. lpobj->mfp.hMF = NULL;
  69. }
  70. if (lpobj->hmfp)
  71. GlobalFree (lpobj->hmfp);
  72. if (lpobj->head.lhclientdoc)
  73. DocDeleteObject ((LPOLEOBJECT)lpobj);
  74. if (hobj = lpobj->head.hobj) {
  75. lpobj->head.hobj = NULL;
  76. GlobalUnlock (hobj);
  77. GlobalFree (hobj);
  78. }
  79. return OLE_OK;
  80. }
  81. OLESTATUS FARINTERNAL MfSaveToStream (
  82. LPOLEOBJECT lpoleobj,
  83. LPOLESTREAM lpstream
  84. ){
  85. DWORD dwFileVer = GetFileVersion(lpoleobj);
  86. LPOBJECT_MF lpobj = (LPOBJECT_MF)lpoleobj;
  87. OLESTATUS retVal = OLE_ERROR_STREAM;
  88. HANDLE hBits;
  89. LPSTR lpBits;
  90. LONG lSizeBytes;
  91. lSizeBytes = lpobj->sizeBytes - sizeof(METAFILEPICT)
  92. + sizeof(WIN16METAFILEPICT);
  93. if (!lpobj->mfp.hMF)
  94. return OLE_ERROR_BLANK;
  95. if (PutBytes (lpstream, (LPSTR) &dwFileVer, sizeof(LONG)))
  96. return OLE_ERROR_STREAM;
  97. if (PutBytes (lpstream, (LPSTR) &lpobj->head.ctype, sizeof(LONG)))
  98. return OLE_ERROR_STREAM;
  99. if (PutStrWithLen(lpstream, (LPSTR)"METAFILEPICT"))
  100. return OLE_ERROR_STREAM;
  101. if (PutBytes (lpstream, (LPSTR) &lpobj->head.cx, sizeof(LONG)))
  102. return OLE_ERROR_STREAM;
  103. if (PutBytes (lpstream, (LPSTR) &lpobj->head.cy, sizeof(LONG)))
  104. return OLE_ERROR_STREAM;
  105. if (PutBytes (lpstream, (LPSTR) &lSizeBytes, sizeof(LONG)))
  106. return OLE_ERROR_STREAM;
  107. if (!(hBits = MGetMetaFileBits (lpobj->mfp.hMF)))
  108. return OLE_ERROR_MEMORY;
  109. if (lpBits = (LPSTR) GlobalLock (hBits)) {
  110. WIN16METAFILEPICT w16mfp;
  111. ConvertMF32to16(&lpobj->mfp, &w16mfp);
  112. if (!PutBytes (lpstream, (LPSTR)&w16mfp, sizeof(WIN16METAFILEPICT)))
  113. if (!PutBytes (lpstream, (LPSTR)lpBits,
  114. lSizeBytes - sizeof(WIN16METAFILEPICT)))
  115. retVal = OLE_OK;
  116. GlobalUnlock(hBits);
  117. }
  118. else
  119. retVal = OLE_ERROR_MEMORY;
  120. lpobj->mfp.hMF = MSetMetaFileBits (hBits);
  121. return retVal;
  122. }
  123. OLESTATUS FARINTERNAL MfClone (
  124. LPOLEOBJECT lpoleobjsrc,
  125. LPOLECLIENT lpclient,
  126. LHCLIENTDOC lhclientdoc,
  127. OLE_LPCSTR lpobjname,
  128. LPOLEOBJECT FAR * lplpoleobj
  129. ){
  130. LPOBJECT_MF lpobjsrc = (LPOBJECT_MF)lpoleobjsrc;
  131. LPOBJECT_MF lpobjMf;
  132. HANDLE hmf;
  133. *lplpoleobj = (LPOLEOBJECT)NULL;
  134. if (!CheckClientDoc ((LPCLIENTDOC) lhclientdoc))
  135. return OLE_ERROR_HANDLE;
  136. if (!(hmf = CopyMetaFile (lpobjsrc->mfp.hMF, NULL)))
  137. return OLE_ERROR_MEMORY;
  138. if (lpobjMf = MfCreateBlank (lhclientdoc, (LPSTR)lpobjname,
  139. lpobjsrc->head.ctype)) {
  140. lpobjMf->mfp = lpobjsrc->mfp;
  141. lpobjMf->sizeBytes = lpobjsrc->sizeBytes;
  142. lpobjMf->mfp.hMF = hmf;
  143. lpobjMf->head.lpclient = lpclient;
  144. lpobjMf->head.mm = lpobjMf->mfp.mm;
  145. MfSetExtents (lpobjMf);
  146. *lplpoleobj = (LPOLEOBJECT)lpobjMf;
  147. return OLE_OK;
  148. }
  149. return OLE_ERROR_MEMORY;
  150. }
  151. OLESTATUS FARINTERNAL MfEqual (
  152. LPOLEOBJECT lpoleobj1,
  153. LPOLEOBJECT lpoleobj2
  154. ){
  155. LPOBJECT_MF lpobj1 = (LPOBJECT_MF)lpoleobj1;
  156. LPOBJECT_MF lpobj2 = (LPOBJECT_MF)lpoleobj2;
  157. HANDLE hBits1 = NULL, hBits2 = NULL;
  158. OLESTATUS retval = OLE_ERROR_NOT_EQUAL;
  159. if (!(hBits1 = MGetMetaFileBits (lpobj1->mfp.hMF)))
  160. goto errEqual;
  161. if (!(hBits2 = MGetMetaFileBits (lpobj2->mfp.hMF)))
  162. goto errEqual;
  163. if (CmpGlobals (hBits1, hBits2))
  164. retval = OLE_OK;
  165. errEqual:
  166. if (hBits1)
  167. lpobj1->mfp.hMF = MSetMetaFileBits (hBits1);
  168. if (hBits2)
  169. lpobj2->mfp.hMF = MSetMetaFileBits (hBits2);
  170. return retval;
  171. }
  172. OLESTATUS FARINTERNAL MfCopy (LPOLEOBJECT lpoleobj)
  173. {
  174. LPOBJECT_MF lpobj = (LPOBJECT_MF)lpoleobj;
  175. HANDLE hMF;
  176. if (!(hMF = CopyMetaFile (lpobj->mfp.hMF, NULL)))
  177. return OLE_ERROR_MEMORY;
  178. return (MfCopyToClip (lpobj, hMF));
  179. }
  180. OLESTATUS FARINTERNAL MfQueryBounds (
  181. LPOLEOBJECT lpoleobj,
  182. LPRECT lpRc
  183. ){
  184. LPOBJECT_MF lpobj = (LPOBJECT_MF)lpoleobj;
  185. Puts("MfQueryBounds");
  186. if (!lpobj->mfp.hMF)
  187. return OLE_ERROR_BLANK;
  188. // Bounds are given in MM_HIMETRIC mode.
  189. lpRc->left = 0;
  190. lpRc->top = 0;
  191. lpRc->right = (int) lpobj->head.cx;
  192. lpRc->bottom = (int) lpobj->head.cy;
  193. return OLE_OK;
  194. }
  195. OLECLIPFORMAT FARINTERNAL MfEnumFormat (
  196. LPOLEOBJECT lpoleobj,
  197. OLECLIPFORMAT cfFormat
  198. ){
  199. LPOBJECT_MF lpobj = (LPOBJECT_MF)lpoleobj;
  200. if (!cfFormat)
  201. return CF_METAFILEPICT;
  202. return 0;
  203. }
  204. OLESTATUS FARINTERNAL MfGetData (
  205. LPOLEOBJECT lpoleobj,
  206. OLECLIPFORMAT cfFormat,
  207. LPHANDLE lphandle
  208. ){
  209. LPOBJECT_MF lpobj = (LPOBJECT_MF)lpoleobj;
  210. if (cfFormat != CF_METAFILEPICT)
  211. return OLE_ERROR_FORMAT;
  212. if (!(*lphandle = GetHmfp (lpobj)))
  213. return OLE_ERROR_BLANK;
  214. return OLE_OK;
  215. }
  216. LPOBJECT_MF FARINTERNAL MfCreateObject (
  217. HANDLE hMeta,
  218. LPOLECLIENT lpclient,
  219. BOOL fDelete,
  220. LHCLIENTDOC lhclientdoc,
  221. LPCSTR lpobjname,
  222. LONG objType
  223. ){
  224. LPOBJECT_MF lpobj;
  225. if (lpobj = MfCreateBlank (lhclientdoc, (LPSTR)lpobjname, objType)) {
  226. if (MfChangeData ((LPOLEOBJECT)lpobj, hMeta, lpclient, fDelete) != OLE_OK) {
  227. MfRelease ((LPOLEOBJECT)lpobj);
  228. lpobj = NULL;
  229. }
  230. }
  231. return lpobj;
  232. }
  233. // If the routine fails then the object will be left with it's old data.
  234. // If fDelete is TRUE, then hMeta, and the hMF it contains will be deleted
  235. // whether the routine is successful or not.
  236. OLESTATUS FARINTERNAL MfChangeData (
  237. LPOLEOBJECT lpoleobj,
  238. HANDLE hMeta,
  239. LPOLECLIENT lpclient,
  240. BOOL fDelete
  241. ){
  242. LPOBJECT_MF lpobj = (LPOBJECT_MF)lpoleobj;
  243. HANDLE hNewMF;
  244. LPMETAFILEPICT lpMetaPict;
  245. if ((lpMetaPict = (LPMETAFILEPICT) GlobalLock (hMeta)) == NULL) {
  246. if (fDelete)
  247. GlobalFree (hMeta);
  248. return OLE_ERROR_MEMORY;
  249. }
  250. GlobalUnlock (hMeta);
  251. if (!fDelete) {
  252. if (!(hNewMF = CopyMetaFile (lpMetaPict->hMF, NULL)))
  253. return OLE_ERROR_MEMORY;
  254. }
  255. else {
  256. hNewMF = lpMetaPict->hMF;
  257. }
  258. return MfUpdateStruct (lpobj, lpclient, hMeta, lpMetaPict, hNewMF, fDelete);
  259. }
  260. OLESTATUS INTERNAL MfUpdateStruct (
  261. LPOBJECT_MF lpobj,
  262. LPOLECLIENT lpclient,
  263. HANDLE hMeta,
  264. LPMETAFILEPICT lpMetaPict,
  265. HANDLE hMF,
  266. BOOL fDelete
  267. ){
  268. OLESTATUS retVal;
  269. DWORD size;
  270. HANDLE hOldMF;
  271. hOldMF = lpobj->mfp.hMF;
  272. ASSERT(lpMetaPict->mm == MM_ANISOTROPIC, "Wrong mapping mode")
  273. if (lpMetaPict->mm != MM_ANISOTROPIC)
  274. retVal = OLE_ERROR_METAFILE;
  275. else if (!(size = MfGetSize (&hMF)))
  276. retVal = OLE_ERROR_BLANK;
  277. else {
  278. lpobj->mfp = *lpMetaPict;
  279. lpobj->mfp.hMF = hMF;
  280. lpobj->sizeBytes = size + sizeof(METAFILEPICT);
  281. lpobj->head.lpclient = lpclient;
  282. lpobj->head.mm = lpobj->mfp.mm;
  283. if (lpobj->hmfp) {
  284. GlobalFree (lpobj->hmfp);
  285. lpobj->hmfp = NULL;
  286. }
  287. MfSetExtents (lpobj);
  288. if (hOldMF)
  289. DeleteMetaFile (hOldMF);
  290. retVal = OLE_OK;
  291. }
  292. if (retVal != OLE_OK)
  293. DeleteMetaFile (hMF);
  294. if (fDelete)
  295. GlobalFree (hMeta);
  296. return retVal;
  297. }
  298. LPOBJECT_MF FARINTERNAL MfCreateBlank(
  299. LHCLIENTDOC lhclientdoc,
  300. LPSTR lpobjname,
  301. LONG objType
  302. ){
  303. HOBJECT hobj;
  304. LPOBJECT_MF lpobj;
  305. if(!(hobj = GlobalAlloc (GMEM_MOVEABLE|GMEM_ZEROINIT,sizeof (OBJECT_MF))))
  306. return NULL;
  307. if (!(lpobj = (LPOBJECT_MF) GlobalLock (hobj))){
  308. GlobalFree (hobj);
  309. return NULL;
  310. }
  311. lpobj->head.objId[0] = 'L';
  312. lpobj->head.objId[1] = 'E';
  313. lpobj->head.ctype = objType;
  314. lpobj->head.lpvtbl = (LPOLEOBJECTVTBL)&vtblMF;
  315. lpobj->head.iTable = INVALID_INDEX;
  316. lpobj->head.mm = MM_TEXT;
  317. lpobj->head.hobj = hobj;
  318. if (objType == CT_STATIC)
  319. DocAddObject ((LPCLIENTDOC) lhclientdoc,
  320. (LPOLEOBJECT) lpobj, lpobjname);
  321. // Unlock will be done at object deletion time.
  322. return lpobj;
  323. }
  324. OLESTATUS FARINTERNAL MfLoadFromStream (
  325. LPOLESTREAM lpstream,
  326. LPOLECLIENT lpclient,
  327. LHCLIENTDOC lhclientdoc,
  328. LPSTR lpobjname,
  329. LPOLEOBJECT FAR * lplpobj,
  330. LONG objType
  331. ){
  332. LPOLEOBJECT lpoleobj = NULL;
  333. OLESTATUS retval = OLE_ERROR_STREAM;
  334. HANDLE hBytes = NULL;
  335. LPSTR lpBytes = NULL;
  336. DWORD dwSizeBytes;
  337. METAFILEPICT mfp;
  338. // Class name would've been read by this time.
  339. *lplpobj = NULL;
  340. switch (HIWORD(dwVerFromFile)) {
  341. case OS_WIN32:
  342. if (!(lpoleobj = (LPOLEOBJECT)EmfCreateBlank (lhclientdoc, lpobjname, objType)))
  343. return OLE_ERROR_MEMORY;
  344. break;
  345. case OS_WIN16:
  346. case OS_MAC:
  347. if (!(lpoleobj = (LPOLEOBJECT)MfCreateBlank (lhclientdoc, lpobjname, objType)))
  348. return OLE_ERROR_MEMORY;
  349. break;
  350. default:
  351. return OLE_ERROR_FILE_VER;
  352. }
  353. lpoleobj->lpclient = lpclient;
  354. if (GetBytes (lpstream, (LPSTR) &lpoleobj->cx, sizeof(LONG)))
  355. goto error;
  356. if (GetBytes (lpstream, (LPSTR) &lpoleobj->cy, sizeof(LONG)))
  357. goto error;
  358. if (GetBytes (lpstream, (LPSTR) &dwSizeBytes, sizeof(LONG)))
  359. goto error;
  360. if (!dwSizeBytes) {
  361. retval = OLE_ERROR_BLANK;
  362. goto error;
  363. }
  364. // if we are reading a MAC object we want to skip this
  365. if (HIWORD(dwVerFromFile) != OS_MAC) {
  366. WIN16METAFILEPICT w16mfp;
  367. if (GetBytes (lpstream, (LPSTR) &w16mfp, sizeof(WIN16METAFILEPICT)))
  368. goto error;
  369. ConvertMF16to32(&w16mfp, &mfp);
  370. }
  371. retval = OLE_ERROR_MEMORY;
  372. dwSizeBytes -= sizeof(WIN16METAFILEPICT);
  373. if (!(hBytes = GlobalAlloc (GMEM_MOVEABLE, dwSizeBytes)))
  374. goto error;
  375. if (!(lpBytes = (LPSTR)GlobalLock (hBytes)))
  376. goto error;
  377. if (GetBytes (lpstream, (LPSTR)lpBytes, dwSizeBytes))
  378. goto error;
  379. switch (HIWORD(dwVerFromFile)){
  380. case OS_WIN32: {
  381. LPOBJECT_EMF lpemfobj = (LPOBJECT_EMF)lpoleobj;
  382. lpemfobj->sizeBytes = dwSizeBytes;
  383. if (!((HENHMETAFILE)lpemfobj->hemf = SetWinMetaFileBits(dwSizeBytes, lpBytes, 0 , &mfp)))
  384. goto error;
  385. EmfSetExtents (lpemfobj);
  386. break;
  387. }
  388. case OS_WIN16: {
  389. LPOBJECT_MF lpmfobj = (LPOBJECT_MF)lpoleobj;
  390. lpmfobj->mfp = mfp;
  391. lpmfobj->sizeBytes = dwSizeBytes + sizeof(METAFILEPICT);
  392. lpmfobj->head.mm = lpmfobj->mfp.mm;
  393. GlobalUnlock (hBytes);
  394. lpBytes = NULL;
  395. if (!(lpmfobj->mfp.hMF = MSetMetaFileBits(hBytes)))
  396. goto error;
  397. hBytes = NULL;
  398. MfSetExtents (lpmfobj);
  399. break;
  400. }
  401. case OS_MAC: {
  402. LPOBJECT_MF lpmfobj = (LPOBJECT_MF)lpoleobj;
  403. lpmfobj->mfp.xExt = (int) lpmfobj->head.cx;
  404. lpmfobj->mfp.yExt = (int) lpmfobj->head.cy;
  405. if ((retval = wCreateDummyMetaFile (lpmfobj, lpmfobj->mfp.xExt,
  406. lpmfobj->mfp.yExt)) != OLE_OK)
  407. goto error;
  408. break;
  409. }
  410. }
  411. *lplpobj = lpoleobj;
  412. retval = OLE_OK;
  413. error:
  414. if (hBytes)
  415. {
  416. if (lpBytes)
  417. GlobalUnlock (hBytes);
  418. GlobalFree (hBytes);
  419. }
  420. if (retval != OLE_OK)
  421. OleDelete (lpoleobj);
  422. return retval;
  423. }
  424. OLESTATUS FARINTERNAL MfPaste (
  425. LPOLECLIENT lpclient,
  426. LHCLIENTDOC lhclientdoc,
  427. LPSTR lpobjname,
  428. LPOLEOBJECT FAR * lplpoleobject,
  429. LONG objType
  430. ){
  431. HANDLE hMeta;
  432. *lplpoleobject = NULL;
  433. if((hMeta = GetClipboardData (CF_METAFILEPICT)) == NULL)
  434. return OLE_ERROR_MEMORY;
  435. if (!(*lplpoleobject = (LPOLEOBJECT) MfCreateObject (hMeta, lpclient,
  436. FALSE, lhclientdoc,
  437. lpobjname, objType)))
  438. return OLE_ERROR_MEMORY;
  439. return OLE_OK;
  440. }
  441. OLESTATUS INTERNAL MfCopyToClip (
  442. LPOBJECT_MF lpobj,
  443. HANDLE hMF
  444. ){
  445. LPMETAFILEPICT lpMeta;
  446. HANDLE hMeta;
  447. if (!(hMeta = GlobalAlloc (GMEM_MOVEABLE, sizeof(METAFILEPICT))))
  448. return OLE_ERROR_MEMORY;
  449. if (lpMeta = (LPMETAFILEPICT) GlobalLock(hMeta)){
  450. *lpMeta = lpobj->mfp;
  451. if (hMF)
  452. lpMeta->hMF = hMF;
  453. else
  454. lpobj->mfp.hMF = NULL;
  455. GlobalUnlock (hMeta);
  456. SetClipboardData(CF_METAFILEPICT, hMeta);
  457. return OLE_OK;
  458. }
  459. GlobalFree(hMeta);
  460. return OLE_ERROR_MEMORY;
  461. }
  462. void FARINTERNAL MfSetExtents (LPOBJECT_MF lpobj)
  463. {
  464. if (lpobj->mfp.xExt > 0) {
  465. // The extents are assumed to be in MM_HIMETIRC units
  466. lpobj->head.cx = (LONG) lpobj->mfp.xExt;
  467. lpobj->head.cy = (LONG) - lpobj->mfp.yExt;
  468. }
  469. }
  470. DWORD INTERNAL MfGetSize (LPHANDLE lphmf)
  471. {
  472. HANDLE hBits;
  473. DWORD size;
  474. if ((hBits = MGetMetaFileBits (*lphmf)) == NULL)
  475. return 0;
  476. size = (DWORD)GlobalSize(hBits);
  477. *lphmf = MSetMetaFileBits (hBits);
  478. return size;
  479. }
  480. HANDLE INTERNAL GetHmfp (LPOBJECT_MF lpobj)
  481. {
  482. HANDLE hmfp;
  483. LPMETAFILEPICT lpmfp = NULL;
  484. if (lpobj->hmfp)
  485. return lpobj->hmfp;
  486. if (!(hmfp = GlobalAlloc (GMEM_MOVEABLE, sizeof(METAFILEPICT))))
  487. return NULL;
  488. if (!(lpmfp = (LPMETAFILEPICT) GlobalLock (hmfp))) {
  489. GlobalFree (hmfp);
  490. return NULL;
  491. }
  492. *lpmfp = lpobj->mfp;
  493. GlobalUnlock (hmfp);
  494. return (lpobj->hmfp = hmfp);
  495. }
  496. OLESTATUS FARINTERNAL wCreateDummyMetaFile (
  497. LPOBJECT_MF lpobj,
  498. int xExt,
  499. int yExt
  500. ){
  501. HDC hMetaDC;
  502. if (!(hMetaDC = CreateMetaFile (NULL)))
  503. return OLE_ERROR_MEMORY;
  504. MSetWindowOrg (hMetaDC, 0, 0);
  505. MSetWindowExt (hMetaDC, xExt, yExt);
  506. Rectangle (hMetaDC, 0, 0, xExt, yExt);
  507. if (!(lpobj->mfp.hMF = CloseMetaFile (hMetaDC)))
  508. return OLE_ERROR_MEMORY;
  509. lpobj->mfp.mm = MM_ANISOTROPIC;
  510. lpobj->sizeBytes = MfGetSize ( (LPHANDLE) &lpobj->mfp.hMF) + sizeof(METAFILEPICT);
  511. return OLE_OK;
  512. }