mscms: Implement AssociateColorProfileWithDevice and DisassociateColorProfileFromDevice.
[wine] / dlls / mscms / profile.c
1 /*
2  * MSCMS - Color Management System for Wine
3  *
4  * Copyright 2004, 2005, 2006, 2008 Hans Leidekker
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/debug.h"
23 #include "wine/unicode.h"
24
25 #include <stdarg.h>
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winnls.h"
30 #include "wingdi.h"
31 #include "winuser.h"
32 #include "winreg.h"
33 #include "icm.h"
34
35 #include "mscms_priv.h"
36
37 #define IS_SEPARATOR(ch)  ((ch) == '\\' || (ch) == '/')
38
39 static void MSCMS_basename( LPCWSTR path, LPWSTR name )
40 {
41     INT i = lstrlenW( path );
42
43     while (i > 0 && !IS_SEPARATOR(path[i - 1])) i--;
44     lstrcpyW( name, &path[i] );
45 }
46
47 static inline LPWSTR MSCMS_strdupW( LPCSTR str )
48 {
49     LPWSTR ret = NULL;
50     if (str)
51     {
52         DWORD len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
53         if ((ret = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
54             MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len );
55     }
56     return ret;
57 }
58
59 static const char *MSCMS_dbgstr_tag( DWORD tag )
60 {
61     return wine_dbg_sprintf( "'%c%c%c%c'",
62         (char)(tag >> 24), (char)(tag >> 16), (char)(tag >> 8), (char)(tag) );
63 }
64
65 WINE_DEFAULT_DEBUG_CHANNEL(mscms);
66
67 /******************************************************************************
68  * AssociateColorProfileWithDeviceA               [MSCMS.@]
69  */
70 BOOL WINAPI AssociateColorProfileWithDeviceA( PCSTR machine, PCSTR profile, PCSTR device )
71 {
72     int len;
73     BOOL ret = FALSE;
74     WCHAR *profileW, *deviceW;
75
76     TRACE( "( %s, %s, %s )\n", debugstr_a(machine), debugstr_a(profile), debugstr_a(device) );
77
78     if (!profile || !device)
79     {
80         SetLastError( ERROR_INVALID_PARAMETER );
81         return FALSE;
82     }
83     if (machine)
84     {
85         SetLastError( ERROR_NOT_SUPPORTED );
86         return FALSE;
87     }
88
89     len = MultiByteToWideChar( CP_ACP, 0, profile, -1, NULL, 0 );
90     if (!(profileW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return FALSE;
91
92     MultiByteToWideChar( CP_ACP, 0, profile, -1, profileW, len );
93
94     len = MultiByteToWideChar( CP_ACP, 0, device, -1, NULL, 0 );
95     if ((deviceW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
96     {
97         MultiByteToWideChar( CP_ACP, 0, device, -1, deviceW, len );
98         ret = AssociateColorProfileWithDeviceW( NULL, profileW, deviceW );
99     }
100
101     HeapFree( GetProcessHeap(), 0, profileW );
102     HeapFree( GetProcessHeap(), 0, deviceW );
103     return ret;
104 }
105
106 static BOOL set_profile_device_key( PCWSTR file, const BYTE *value, DWORD size )
107 {
108     static const WCHAR fmtW[] = {'%','c','%','c','%','c','%','c',0};
109     static const WCHAR icmW[] = {'S','o','f','t','w','a','r','e','\\',
110                                  'M','i','c','r','o','s','o','f','t','\\',
111                                  'W','i','n','d','o','w','s',' ','N','T','\\',
112                                  'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
113                                  'I','C','M',0};
114     PROFILEHEADER header;
115     PROFILE profile;
116     HPROFILE handle;
117     HKEY icm_key, class_key;
118     WCHAR basenameW[MAX_PATH], classW[5];
119
120     profile.dwType = PROFILE_FILENAME;
121     profile.pProfileData = (PVOID)file;
122     profile.cbDataSize = (lstrlenW( file ) + 1) * sizeof(WCHAR);
123
124     /* FIXME is the profile installed? */
125     if (!(handle = OpenColorProfileW( &profile, PROFILE_READ, 0, OPEN_EXISTING )))
126     {
127         SetLastError( ERROR_INVALID_PROFILE );
128         return FALSE;
129     }
130
131     RegCreateKeyExW( HKEY_LOCAL_MACHINE, icmW, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &icm_key, NULL );
132     GetColorProfileHeader( handle, &header );
133
134     MSCMS_basename( file, basenameW );
135     sprintfW( classW, fmtW, (header.phClass >> 24) & 0xff, (header.phClass >> 16) & 0xff,
136                             (header.phClass >> 8) & 0xff,  header.phClass & 0xff );
137
138     RegCreateKeyExW( icm_key, classW, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &class_key, NULL );
139     if (value) RegSetValueExW( class_key, basenameW, 0, REG_BINARY, value, size );
140     else RegDeleteValueW( class_key, basenameW );
141
142     RegCloseKey( class_key );
143     RegCloseKey( icm_key );
144     CloseColorProfile( handle );
145     return TRUE;
146 }
147
148 /******************************************************************************
149  * AssociateColorProfileWithDeviceW               [MSCMS.@]
150  */
151 BOOL WINAPI AssociateColorProfileWithDeviceW( PCWSTR machine, PCWSTR profile, PCWSTR device )
152 {
153     static const BYTE dummy_value[12];
154
155     TRACE( "( %s, %s, %s )\n", debugstr_w(machine), debugstr_w(profile), debugstr_w(device) );
156
157     if (!profile || !device)
158     {
159         SetLastError( ERROR_INVALID_PARAMETER );
160         return FALSE;
161     }
162     if (machine)
163     {
164         SetLastError( ERROR_NOT_SUPPORTED );
165         return FALSE;
166     }
167
168     return set_profile_device_key( profile, dummy_value, sizeof(dummy_value) );
169 }
170
171 /******************************************************************************
172  * DisassociateColorProfileFromDeviceA            [MSCMS.@]
173  */
174 BOOL WINAPI DisassociateColorProfileFromDeviceA( PCSTR machine, PCSTR profile, PCSTR device )
175 {
176     int len;
177     BOOL ret = FALSE;
178     WCHAR *profileW, *deviceW;
179
180     TRACE( "( %s, %s, %s )\n", debugstr_a(machine), debugstr_a(profile), debugstr_a(device) );
181
182     if (!profile || !device)
183     {
184         SetLastError( ERROR_INVALID_PARAMETER );
185         return FALSE;
186     }
187     if (machine)
188     {
189         SetLastError( ERROR_NOT_SUPPORTED );
190         return FALSE;
191     }
192
193     len = MultiByteToWideChar( CP_ACP, 0, profile, -1, NULL, 0 );
194     if (!(profileW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) ))) return FALSE;
195
196     MultiByteToWideChar( CP_ACP, 0, profile, -1, profileW, len );
197
198     len = MultiByteToWideChar( CP_ACP, 0, device, -1, NULL, 0 );
199     if ((deviceW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) )))
200     {
201         MultiByteToWideChar( CP_ACP, 0, device, -1, deviceW, len );
202         ret = DisassociateColorProfileFromDeviceW( NULL, profileW, deviceW );
203     }
204
205     HeapFree( GetProcessHeap(), 0, profileW );
206     HeapFree( GetProcessHeap(), 0, deviceW );
207     return ret;
208 }
209
210 /******************************************************************************
211  * DisassociateColorProfileFromDeviceW            [MSCMS.@]
212  */
213 BOOL WINAPI DisassociateColorProfileFromDeviceW( PCWSTR machine, PCWSTR profile, PCWSTR device )
214 {
215     TRACE( "( %s, %s, %s )\n", debugstr_w(machine), debugstr_w(profile), debugstr_w(device) );
216
217     if (!profile || !device)
218     {
219         SetLastError( ERROR_INVALID_PARAMETER );
220         return FALSE;
221     }
222     if (machine)
223     {
224         SetLastError( ERROR_NOT_SUPPORTED );
225         return FALSE;
226     }
227
228     return set_profile_device_key( profile, NULL, 0 );
229 }
230
231 /******************************************************************************
232  * GetColorDirectoryA               [MSCMS.@]
233  *
234  * See GetColorDirectoryW.
235  */
236 BOOL WINAPI GetColorDirectoryA( PCSTR machine, PSTR buffer, PDWORD size )
237 {
238     INT len;
239     LPWSTR bufferW;
240     BOOL ret = FALSE;
241     DWORD sizeW;
242
243     TRACE( "( %p, %p )\n", buffer, size );
244
245     if (machine || !size) return FALSE;
246
247     if (!buffer)
248     {
249         ret = GetColorDirectoryW( NULL, NULL, &sizeW );
250         *size = sizeW / sizeof(WCHAR);
251         return FALSE;
252     }
253
254     sizeW = *size * sizeof(WCHAR);
255
256     bufferW = HeapAlloc( GetProcessHeap(), 0, sizeW );
257
258     if (bufferW)
259     {
260         if ((ret = GetColorDirectoryW( NULL, bufferW, &sizeW )))
261         {
262             *size = WideCharToMultiByte( CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL );
263             len = WideCharToMultiByte( CP_ACP, 0, bufferW, -1, buffer, *size, NULL, NULL );
264             if (!len) ret = FALSE;
265         }
266         else *size = sizeW / sizeof(WCHAR);
267
268         HeapFree( GetProcessHeap(), 0, bufferW );
269     }
270     return ret;
271 }
272
273 /******************************************************************************
274  * GetColorDirectoryW               [MSCMS.@]
275  *
276  * Get the directory where color profiles are stored.
277  *
278  * PARAMS
279  *  machine  [I]   Name of the machine for which to get the color directory.
280  *                 Must be NULL, which indicates the local machine.
281  *  buffer   [I]   Buffer to receive the path name.
282  *  size     [I/O] Size of the buffer in bytes. On return the variable holds
283  *                 the number of bytes actually needed.
284  */
285 BOOL WINAPI GetColorDirectoryW( PCWSTR machine, PWSTR buffer, PDWORD size )
286 {
287     WCHAR colordir[MAX_PATH];
288     static const WCHAR colorsubdir[] =
289         {'\\','s','p','o','o','l','\\','d','r','i','v','e','r','s','\\','c','o','l','o','r',0};
290     DWORD len;
291
292     TRACE( "( %p, %p )\n", buffer, size );
293
294     if (machine || !size) return FALSE;
295
296     GetSystemDirectoryW( colordir, sizeof(colordir) / sizeof(WCHAR) );
297     lstrcatW( colordir, colorsubdir );
298
299     len = lstrlenW( colordir ) * sizeof(WCHAR);
300
301     if (buffer && len <= *size)
302     {
303         lstrcpyW( buffer, colordir );
304         *size = len;
305         return TRUE;
306     }
307
308     SetLastError( ERROR_MORE_DATA );
309     *size = len;
310     return FALSE;
311 }
312
313 /******************************************************************************
314  * GetColorProfileElement               [MSCMS.@]
315  *
316  * Retrieve data for a specified tag type.
317  *
318  * PARAMS
319  *  profile  [I]   Handle to a color profile.
320  *  type     [I]   ICC tag type. 
321  *  offset   [I]   Offset in bytes to start copying from. 
322  *  size     [I/O] Size of the buffer in bytes. On return the variable holds
323  *                 the number of bytes actually needed.
324  *  buffer   [O]   Buffer to receive the tag data.
325  *  ref      [O]   Pointer to a BOOL that specifies whether more than one tag
326  *                 references the data.
327  *
328  * RETURNS
329  *  Success: TRUE
330  *  Failure: FALSE
331  */
332 BOOL WINAPI GetColorProfileElement( HPROFILE profile, TAGTYPE type, DWORD offset, PDWORD size,
333                                     PVOID buffer, PBOOL ref )
334 {
335     BOOL ret = FALSE;
336 #ifdef HAVE_LCMS
337     icProfile *iccprofile = MSCMS_hprofile2iccprofile( profile );
338     DWORD i, count;
339     icTag tag;
340
341     TRACE( "( %p, 0x%08x, %d, %p, %p, %p )\n", profile, type, offset, size, buffer, ref );
342
343     if (!iccprofile || !size || !ref) return FALSE;
344     count = MSCMS_get_tag_count( iccprofile );
345
346     for (i = 0; i < count; i++)
347     {
348         MSCMS_get_tag_by_index( iccprofile, i, &tag );
349
350         if (tag.sig == type)
351         {
352             if ((tag.size - offset) > *size || !buffer)
353             {
354                 *size = (tag.size - offset);
355                 return FALSE;
356             }
357
358             MSCMS_get_tag_data( iccprofile, &tag, offset, buffer );
359
360             *ref = FALSE; /* FIXME: calculate properly */
361             return TRUE;
362         }
363     }
364
365 #endif /* HAVE_LCMS */
366     return ret;
367 }
368
369 /******************************************************************************
370  * GetColorProfileElementTag               [MSCMS.@]
371  *
372  * Get the tag type from a color profile by index. 
373  *
374  * PARAMS
375  *  profile  [I]   Handle to a color profile.
376  *  index    [I]   Index into the tag table of the color profile.
377  *  type     [O]   Pointer to a variable that holds the ICC tag type on return.
378  *
379  * RETURNS
380  *  Success: TRUE
381  *  Failure: FALSE
382  *
383  * NOTES
384  *  The tag table index starts at 1.
385  *  Use GetCountColorProfileElements to retrieve a count of tagged elements.
386  */
387 BOOL WINAPI GetColorProfileElementTag( HPROFILE profile, DWORD index, PTAGTYPE type )
388 {
389     BOOL ret = FALSE;
390 #ifdef HAVE_LCMS
391     icProfile *iccprofile = MSCMS_hprofile2iccprofile( profile );
392     DWORD count;
393     icTag tag;
394
395     TRACE( "( %p, %d, %p )\n", profile, index, type );
396
397     if (!iccprofile || !type) return FALSE;
398
399     count = MSCMS_get_tag_count( iccprofile );
400     if (index > count || index < 1) return FALSE;
401
402     MSCMS_get_tag_by_index( iccprofile, index - 1, &tag );
403     *type = tag.sig;
404
405     ret = TRUE;
406
407 #endif /* HAVE_LCMS */
408     return ret;
409 }
410
411 /******************************************************************************
412  * GetColorProfileFromHandle               [MSCMS.@]
413  *
414  * Retrieve an ICC color profile by handle.
415  *
416  * PARAMS
417  *  profile  [I]   Handle to a color profile.
418  *  buffer   [O]   Buffer to receive the ICC profile.
419  *  size     [I/O] Size of the buffer in bytes. On return the variable holds the
420  *                 number of bytes actually needed.
421  *
422  * RETURNS
423  *  Success: TRUE
424  *  Failure: FALSE
425  *
426  * NOTES
427  *  The profile returned will be in big-endian format.
428  */
429 BOOL WINAPI GetColorProfileFromHandle( HPROFILE profile, PBYTE buffer, PDWORD size )
430 {
431     BOOL ret = FALSE;
432 #ifdef HAVE_LCMS
433     icProfile *iccprofile = MSCMS_hprofile2iccprofile( profile );
434     PROFILEHEADER header;
435
436     TRACE( "( %p, %p, %p )\n", profile, buffer, size );
437
438     if (!iccprofile || !size) return FALSE;
439     MSCMS_get_profile_header( iccprofile, &header );
440
441     if (!buffer || header.phSize > *size)
442     {
443         *size = header.phSize;
444         return FALSE;
445     }
446
447     /* No endian conversion needed */
448     memcpy( buffer, iccprofile, header.phSize );
449
450     *size = header.phSize;
451     ret = TRUE;
452
453 #endif /* HAVE_LCMS */
454     return ret;
455 }
456
457 /******************************************************************************
458  * GetColorProfileHeader               [MSCMS.@]
459  *
460  * Retrieve a color profile header by handle.
461  *
462  * PARAMS
463  *  profile  [I]   Handle to a color profile.
464  *  header   [O]   Buffer to receive the ICC profile header.
465  *
466  * RETURNS
467  *  Success: TRUE
468  *  Failure: FALSE
469  *
470  * NOTES
471  *  The profile header returned will be adjusted for endianess.
472  */
473 BOOL WINAPI GetColorProfileHeader( HPROFILE profile, PPROFILEHEADER header )
474 {
475 #ifdef HAVE_LCMS
476     icProfile *iccprofile = MSCMS_hprofile2iccprofile( profile );
477
478     TRACE( "( %p, %p )\n", profile, header );
479
480     if (!iccprofile || !header) return FALSE;
481
482     MSCMS_get_profile_header( iccprofile, header );
483     return TRUE;
484
485 #else
486     return FALSE;
487 #endif /* HAVE_LCMS */
488 }
489
490 /******************************************************************************
491  * GetCountColorProfileElements               [MSCMS.@]
492  *
493  * Retrieve the number of elements in a color profile.
494  *
495  * PARAMS
496  *  profile  [I] Handle to a color profile.
497  *  count    [O] Pointer to a variable which is set to the number of elements
498  *               in the color profile.
499  *
500  * RETURNS
501  *  Success: TRUE
502  *  Failure: FALSE
503  */
504 BOOL WINAPI GetCountColorProfileElements( HPROFILE profile, PDWORD count )
505 {
506     BOOL ret = FALSE;
507 #ifdef HAVE_LCMS
508     icProfile *iccprofile = MSCMS_hprofile2iccprofile( profile );
509
510     TRACE( "( %p, %p )\n", profile, count );
511
512     if (!iccprofile || !count) return FALSE;
513     *count = MSCMS_get_tag_count( iccprofile );
514     ret = TRUE;
515
516 #endif /* HAVE_LCMS */
517     return ret;
518 }
519
520 /******************************************************************************
521  * GetStandardColorSpaceProfileA               [MSCMS.@]
522  *
523  * See GetStandardColorSpaceProfileW.
524  */
525 BOOL WINAPI GetStandardColorSpaceProfileA( PCSTR machine, DWORD id, PSTR profile, PDWORD size )
526 {
527     INT len;
528     LPWSTR profileW;
529     BOOL ret = FALSE;
530     DWORD sizeW;
531
532     TRACE( "( 0x%08x, %p, %p )\n", id, profile, size );
533
534     if (machine) 
535     {
536         SetLastError( ERROR_NOT_SUPPORTED );
537         return FALSE;
538     }
539
540     if (!size) 
541     {
542         SetLastError( ERROR_INVALID_PARAMETER );
543         return FALSE;
544     }
545
546     sizeW = *size * sizeof(WCHAR);
547
548     if (!profile)
549     {
550         ret = GetStandardColorSpaceProfileW( NULL, id, NULL, &sizeW );
551         *size = sizeW / sizeof(WCHAR);
552         return FALSE;
553     }
554
555     profileW = HeapAlloc( GetProcessHeap(), 0, sizeW );
556
557     if (profileW)
558     {
559         if ((ret = GetStandardColorSpaceProfileW( NULL, id, profileW, &sizeW )))
560         {
561             *size = WideCharToMultiByte( CP_ACP, 0, profileW, -1, NULL, 0, NULL, NULL );
562             len = WideCharToMultiByte( CP_ACP, 0, profileW, -1, profile, *size, NULL, NULL );
563             if (!len) ret = FALSE;
564         }
565         else *size = sizeW / sizeof(WCHAR);
566
567         HeapFree( GetProcessHeap(), 0, profileW );
568     }
569     return ret;
570 }
571
572 /******************************************************************************
573  * GetStandardColorSpaceProfileW               [MSCMS.@]
574  *
575  * Retrieve the profile filename for a given standard color space id.
576  *
577  * PARAMS
578  *  machine  [I]   Name of the machine for which to get the standard color space.
579  *                 Must be NULL, which indicates the local machine.
580  *  id       [I]   Id of a standard color space.
581  *  profile  [O]   Buffer to receive the profile filename.
582  *  size     [I/O] Size of the filename buffer in bytes.
583  *
584  * RETURNS
585  *  Success: TRUE
586  *  Failure: FALSE
587  */
588 BOOL WINAPI GetStandardColorSpaceProfileW( PCWSTR machine, DWORD id, PWSTR profile, PDWORD size )
589 {
590     static const WCHAR rgbprofilefile[] =
591         { '\\','s','r','g','b',' ','c','o','l','o','r',' ',
592           's','p','a','c','e',' ','p','r','o','f','i','l','e','.','i','c','m',0 };
593     WCHAR rgbprofile[MAX_PATH];
594     DWORD len = sizeof(rgbprofile);
595
596     TRACE( "( 0x%08x, %p, %p )\n", id, profile, size );
597
598     if (machine) 
599     {
600         SetLastError( ERROR_NOT_SUPPORTED );
601         return FALSE;
602     }
603
604     if (!size) 
605     {
606         SetLastError( ERROR_INVALID_PARAMETER );
607         return FALSE;
608     }
609
610     if (!profile)
611     {
612         SetLastError( ERROR_INSUFFICIENT_BUFFER );
613         return FALSE;
614     }
615
616     GetColorDirectoryW( machine, rgbprofile, &len );
617
618     switch (id)
619     {
620         case SPACE_RGB: /* 'RGB ' */
621             lstrcatW( rgbprofile, rgbprofilefile );
622             len = lstrlenW( rgbprofile ) * sizeof(WCHAR);
623
624             if (*size < len || !profile)
625             {
626                 *size = len;
627                 SetLastError( ERROR_MORE_DATA );
628                 return FALSE;
629             }
630
631             lstrcpyW( profile, rgbprofile );
632             break;
633
634         default:
635             SetLastError( ERROR_FILE_NOT_FOUND );
636             return FALSE;
637     }
638     return TRUE;
639 }
640
641 static BOOL MSCMS_header_from_file( LPCWSTR file, PPROFILEHEADER header )
642 {
643     BOOL ret;
644     PROFILE profile;
645     WCHAR path[MAX_PATH], slash[] = {'\\',0};
646     DWORD size = sizeof(path);
647     HANDLE handle;
648
649     ret = GetColorDirectoryW( NULL, path, &size );
650     if (!ret)
651     {
652         WARN( "Can't retrieve color directory\n" );
653         return FALSE;
654     }
655     if (size + sizeof(slash) + sizeof(WCHAR) * lstrlenW( file ) > sizeof(path))
656     {
657         WARN( "Filename too long\n" );
658         return FALSE;
659     }
660
661     lstrcatW( path, slash );
662     lstrcatW( path, file );
663
664     profile.dwType = PROFILE_FILENAME;
665     profile.pProfileData = path;
666     profile.cbDataSize = lstrlenW( path ) + 1;
667
668     handle = OpenColorProfileW( &profile, PROFILE_READ, FILE_SHARE_READ, OPEN_EXISTING );
669     if (!handle)
670     {
671         WARN( "Can't open color profile\n" );
672         return FALSE;
673     }
674
675     ret = GetColorProfileHeader( handle, header );
676     if (!ret)
677         WARN( "Can't retrieve color profile header\n" );
678
679     CloseColorProfile( handle );
680     return ret;
681 }
682
683 static BOOL MSCMS_match_profile( PENUMTYPEW rec, PPROFILEHEADER hdr )
684 {
685     if (rec->dwFields & ET_DEVICENAME)
686     {
687         FIXME( "ET_DEVICENAME: %s\n", debugstr_w(rec->pDeviceName) );
688     }
689     if (rec->dwFields & ET_MEDIATYPE)
690     {
691         FIXME( "ET_MEDIATYPE: 0x%08x\n", rec->dwMediaType );
692     }
693     if (rec->dwFields & ET_DITHERMODE)
694     {
695         FIXME( "ET_DITHERMODE: 0x%08x\n", rec->dwDitheringMode );
696     }
697     if (rec->dwFields & ET_RESOLUTION)
698     {
699         FIXME( "ET_RESOLUTION: 0x%08x, 0x%08x\n",
700                rec->dwResolution[0], rec->dwResolution[1] );
701     }
702     if (rec->dwFields & ET_DEVICECLASS)
703     {
704         FIXME( "ET_DEVICECLASS: %s\n", MSCMS_dbgstr_tag(rec->dwMediaType) );
705     }
706     if (rec->dwFields & ET_CMMTYPE)
707     {
708         TRACE( "ET_CMMTYPE: %s\n", MSCMS_dbgstr_tag(rec->dwCMMType) );
709         if (rec->dwCMMType != hdr->phCMMType) return FALSE;
710     }
711     if (rec->dwFields & ET_CLASS)
712     {
713         TRACE( "ET_CLASS: %s\n", MSCMS_dbgstr_tag(rec->dwClass) );
714         if (rec->dwClass != hdr->phClass) return FALSE;
715     }
716     if (rec->dwFields & ET_DATACOLORSPACE)
717     {
718         TRACE( "ET_DATACOLORSPACE: %s\n", MSCMS_dbgstr_tag(rec->dwDataColorSpace) );
719         if (rec->dwDataColorSpace != hdr->phDataColorSpace) return FALSE;
720     }
721     if (rec->dwFields & ET_CONNECTIONSPACE)
722     {
723         TRACE( "ET_CONNECTIONSPACE: %s\n", MSCMS_dbgstr_tag(rec->dwConnectionSpace) );
724         if (rec->dwConnectionSpace != hdr->phConnectionSpace) return FALSE;
725     }
726     if (rec->dwFields & ET_SIGNATURE)
727     {
728         TRACE( "ET_SIGNATURE: %s\n", MSCMS_dbgstr_tag(rec->dwSignature) );
729         if (rec->dwSignature != hdr->phSignature) return FALSE;
730     }
731     if (rec->dwFields & ET_PLATFORM)
732     {
733         TRACE( "ET_PLATFORM: %s\n", MSCMS_dbgstr_tag(rec->dwPlatform) );
734         if (rec->dwPlatform != hdr->phPlatform) return FALSE;
735     }
736     if (rec->dwFields & ET_PROFILEFLAGS)
737     {
738         TRACE( "ET_PROFILEFLAGS: 0x%08x\n", rec->dwProfileFlags );
739         if (rec->dwProfileFlags != hdr->phProfileFlags) return FALSE;
740     }
741     if (rec->dwFields & ET_MANUFACTURER)
742     {
743         TRACE( "ET_MANUFACTURER: %s\n", MSCMS_dbgstr_tag(rec->dwManufacturer) );
744         if (rec->dwManufacturer != hdr->phManufacturer) return FALSE;
745     }
746     if (rec->dwFields & ET_MODEL)
747     {
748         TRACE( "ET_MODEL: %s\n", MSCMS_dbgstr_tag(rec->dwModel) );
749         if (rec->dwModel != hdr->phModel) return FALSE;
750     }
751     if (rec->dwFields & ET_ATTRIBUTES)
752     {
753         TRACE( "ET_ATTRIBUTES: 0x%08x, 0x%08x\n",
754                rec->dwAttributes[0], rec->dwAttributes[1] );
755         if (rec->dwAttributes[0] != hdr->phAttributes[0] || 
756             rec->dwAttributes[1] != hdr->phAttributes[1]) return FALSE;
757     }
758     if (rec->dwFields & ET_RENDERINGINTENT)
759     {
760         TRACE( "ET_RENDERINGINTENT: 0x%08x\n", rec->dwRenderingIntent );
761         if (rec->dwRenderingIntent != hdr->phRenderingIntent) return FALSE;
762     }
763     if (rec->dwFields & ET_CREATOR)
764     {
765         TRACE( "ET_CREATOR: %s\n", MSCMS_dbgstr_tag(rec->dwCreator) );
766         if (rec->dwCreator != hdr->phCreator) return FALSE;
767     }
768     return TRUE;
769 }
770
771 /******************************************************************************
772  * EnumColorProfilesA               [MSCMS.@]
773  *
774  * See EnumColorProfilesW.
775  */
776 BOOL WINAPI EnumColorProfilesA( PCSTR machine, PENUMTYPEA record, PBYTE buffer,
777                                 PDWORD size, PDWORD number )
778 {
779     BOOL match, ret = FALSE;
780     char spec[] = "\\*.icm";
781     char colordir[MAX_PATH], glob[MAX_PATH], **profiles = NULL;
782     DWORD i, len = sizeof(colordir), count = 0, totalsize = 0;
783     PROFILEHEADER header;
784     WIN32_FIND_DATAA data;
785     ENUMTYPEW recordW;
786     WCHAR *fileW = NULL, *deviceW = NULL;
787     HANDLE find;
788
789     TRACE( "( %p, %p, %p, %p, %p )\n", machine, record, buffer, size, number );
790
791     if (machine || !record || !size ||
792         record->dwSize != sizeof(ENUMTYPEA) ||
793         record->dwVersion != ENUM_TYPE_VERSION) return FALSE;
794
795     ret = GetColorDirectoryA( machine, colordir, &len );
796     if (!ret || len + sizeof(spec) > MAX_PATH)
797     {
798         WARN( "can't retrieve color directory\n" );
799         return FALSE;
800     }
801
802     lstrcpyA( glob, colordir );
803     lstrcatA( glob, spec );
804
805     find = FindFirstFileA( glob, &data );
806     if (find == INVALID_HANDLE_VALUE) return FALSE;
807
808     profiles = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(char *) + 1 );
809     if (!profiles) goto exit;
810
811     memcpy( &recordW, record, sizeof(ENUMTYPEA) );
812     if (record->pDeviceName)
813     {
814         deviceW = MSCMS_strdupW( record->pDeviceName );
815         if (!(recordW.pDeviceName = deviceW)) goto exit;
816     }
817
818     fileW = MSCMS_strdupW( data.cFileName );
819     if (!fileW) goto exit;
820
821     ret = MSCMS_header_from_file( fileW, &header );
822     if (ret)
823     {
824         match = MSCMS_match_profile( &recordW, &header );
825         if (match)
826         {
827             len = sizeof(char) * (lstrlenA( data.cFileName ) + 1);
828             profiles[count] = HeapAlloc( GetProcessHeap(), 0, len );
829
830             if (!profiles[count]) goto exit;
831             else
832             {
833                 TRACE( "matching profile: %s\n", debugstr_a(data.cFileName) );
834                 lstrcpyA( profiles[count], data.cFileName );
835                 totalsize += len;
836                 count++;
837             }
838         }
839     }
840     HeapFree( GetProcessHeap(), 0, fileW );
841     fileW = NULL;
842
843     while (FindNextFileA( find, &data ))
844     {
845         fileW = MSCMS_strdupW( data.cFileName );
846         if (!fileW) goto exit;
847
848         ret = MSCMS_header_from_file( fileW, &header );
849         if (!ret)
850         {
851             HeapFree( GetProcessHeap(), 0, fileW );
852             continue;
853         }
854
855         match = MSCMS_match_profile( &recordW, &header );
856         if (match)
857         {
858             char **tmp = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
859                                       profiles, sizeof(char *) * (count + 1) );
860             if (!tmp) goto exit;
861             else profiles = tmp;
862
863             len = sizeof(char) * (lstrlenA( data.cFileName ) + 1);
864             profiles[count] = HeapAlloc( GetProcessHeap(), 0, len );
865
866             if (!profiles[count]) goto exit;
867             else
868             {
869                 TRACE( "matching profile: %s\n", debugstr_a(data.cFileName) );
870                 lstrcpyA( profiles[count], data.cFileName );
871                 totalsize += len;
872                 count++;
873             }
874         }
875         HeapFree( GetProcessHeap(), 0, fileW );
876         fileW = NULL;
877     }
878
879     totalsize++;
880     if (buffer && *size >= totalsize)
881     {
882         char *p = (char *)buffer;
883
884         for (i = 0; i < count; i++)
885         {
886             lstrcpyA( p, profiles[i] );
887             p += lstrlenA( profiles[i] ) + 1;
888         }
889         *p = 0;
890         ret = TRUE;
891     }
892     else ret = FALSE;
893
894     *size = totalsize;
895     if (number) *number = count;
896
897 exit:
898     for (i = 0; i < count; i++)
899         HeapFree( GetProcessHeap(), 0, profiles[i] );
900     HeapFree( GetProcessHeap(), 0, profiles );
901     HeapFree( GetProcessHeap(), 0, deviceW );
902     HeapFree( GetProcessHeap(), 0, fileW );
903     FindClose( find );
904
905     return ret;
906 }
907
908 /******************************************************************************
909  * EnumColorProfilesW               [MSCMS.@]
910  *
911  * Enumerate profiles that match given criteria.
912  *
913  * PARAMS
914  *  machine  [I]   Name of the machine for which to enumerate profiles.
915  *                 Must be NULL, which indicates the local machine.
916  *  record   [I]   Record of criteria that a profile must match.
917  *  buffer   [O]   Buffer to receive a string array of profile filenames.
918  *  size     [I/O] Size of the filename buffer in bytes.
919  *  number   [O]   Number of filenames copied into buffer.
920  *
921  * RETURNS
922  *  Success: TRUE
923  *  Failure: FALSE
924  */
925 BOOL WINAPI EnumColorProfilesW( PCWSTR machine, PENUMTYPEW record, PBYTE buffer,
926                                 PDWORD size, PDWORD number )
927 {
928     BOOL match, ret = FALSE;
929     WCHAR spec[] = {'\\','*','i','c','m',0};
930     WCHAR colordir[MAX_PATH], glob[MAX_PATH], **profiles = NULL;
931     DWORD i, len = sizeof(colordir), count = 0, totalsize = 0;
932     PROFILEHEADER header;
933     WIN32_FIND_DATAW data;
934     HANDLE find;
935
936     TRACE( "( %p, %p, %p, %p, %p )\n", machine, record, buffer, size, number );
937
938     if (machine || !record || !size ||
939         record->dwSize != sizeof(ENUMTYPEW) ||
940         record->dwVersion != ENUM_TYPE_VERSION) return FALSE;
941
942     ret = GetColorDirectoryW( machine, colordir, &len );
943     if (!ret || len + sizeof(spec) > MAX_PATH)
944     {
945         WARN( "Can't retrieve color directory\n" );
946         return FALSE;
947     }
948
949     lstrcpyW( glob, colordir );
950     lstrcatW( glob, spec );
951
952     find = FindFirstFileW( glob, &data );
953     if (find == INVALID_HANDLE_VALUE) return FALSE;
954
955     profiles = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WCHAR *) + 1 );
956     if (!profiles) goto exit;
957
958     ret = MSCMS_header_from_file( data.cFileName, &header );
959     if (ret)
960     {
961         match = MSCMS_match_profile( record, &header );
962         if (match)
963         {
964             len = sizeof(WCHAR) * (lstrlenW( data.cFileName ) + 1);
965             profiles[count] = HeapAlloc( GetProcessHeap(), 0, len );
966
967             if (!profiles[count]) goto exit;
968             else
969             {
970                 TRACE( "matching profile: %s\n", debugstr_w(data.cFileName) );
971                 lstrcpyW( profiles[count], data.cFileName );
972                 totalsize += len;
973                 count++;
974             }
975         }
976     }
977
978     while (FindNextFileW( find, &data ))
979     {
980         ret = MSCMS_header_from_file( data.cFileName, &header );
981         if (!ret) continue;
982
983         match = MSCMS_match_profile( record, &header );
984         if (match)
985         {
986             WCHAR **tmp = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
987                                        profiles, sizeof(WCHAR *) * (count + 1) );
988             if (!tmp) goto exit;
989             else profiles = tmp;
990
991             len = sizeof(WCHAR) * (lstrlenW( data.cFileName ) + 1);
992             profiles[count] = HeapAlloc( GetProcessHeap(), 0, len );
993
994             if (!profiles[count]) goto exit;
995             else
996             {
997                 TRACE( "matching profile: %s\n", debugstr_w(data.cFileName) );
998                 lstrcpyW( profiles[count], data.cFileName );
999                 totalsize += len;
1000                 count++;
1001             }
1002         }
1003     }
1004
1005     totalsize++;
1006     if (buffer && *size >= totalsize)
1007     {
1008         WCHAR *p = (WCHAR *)buffer;
1009
1010         for (i = 0; i < count; i++)
1011         {
1012             lstrcpyW( p, profiles[i] );
1013             p += lstrlenW( profiles[i] ) + 1;
1014         }
1015         *p = 0;
1016         ret = TRUE;
1017     }
1018     else ret = FALSE;
1019
1020     *size = totalsize;
1021     if (number) *number = count;
1022
1023 exit:
1024     for (i = 0; i < count; i++)
1025         HeapFree( GetProcessHeap(), 0, profiles[i] );
1026     HeapFree( GetProcessHeap(), 0, profiles );
1027     FindClose( find );
1028
1029     return ret;
1030 }
1031
1032 /******************************************************************************
1033  * InstallColorProfileA               [MSCMS.@]
1034  *
1035  * See InstallColorProfileW.
1036  */
1037 BOOL WINAPI InstallColorProfileA( PCSTR machine, PCSTR profile )
1038 {
1039     UINT len;
1040     LPWSTR profileW;
1041     BOOL ret = FALSE;
1042
1043     TRACE( "( %s )\n", debugstr_a(profile) );
1044
1045     if (machine || !profile) return FALSE;
1046
1047     len = MultiByteToWideChar( CP_ACP, 0, profile, -1, NULL, 0 );
1048     profileW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
1049
1050     if (profileW)
1051     {
1052         MultiByteToWideChar( CP_ACP, 0, profile, -1, profileW, len );
1053
1054         ret = InstallColorProfileW( NULL, profileW );
1055         HeapFree( GetProcessHeap(), 0, profileW );
1056     }
1057     return ret;
1058 }
1059
1060 /******************************************************************************
1061  * InstallColorProfileW               [MSCMS.@]
1062  *
1063  * Install a color profile.
1064  *
1065  * PARAMS
1066  *  machine  [I] Name of the machine to install the profile on. Must be NULL,
1067  *               which indicates the local machine.
1068  *  profile  [I] Full path name of the profile to install.
1069  *
1070  * RETURNS
1071  *  Success: TRUE
1072  *  Failure: FALSE
1073  */
1074 BOOL WINAPI InstallColorProfileW( PCWSTR machine, PCWSTR profile )
1075 {
1076     WCHAR dest[MAX_PATH], base[MAX_PATH];
1077     DWORD size = sizeof(dest);
1078     static const WCHAR slash[] = { '\\', 0 };
1079
1080     TRACE( "( %s )\n", debugstr_w(profile) );
1081
1082     if (machine || !profile) return FALSE;
1083
1084     if (!GetColorDirectoryW( machine, dest, &size )) return FALSE;
1085
1086     MSCMS_basename( profile, base );
1087
1088     lstrcatW( dest, slash );
1089     lstrcatW( dest, base );
1090
1091     /* Is source equal to destination? */
1092     if (!lstrcmpW( profile, dest )) return TRUE;
1093
1094     return CopyFileW( profile, dest, TRUE );
1095 }
1096
1097 /******************************************************************************
1098  * IsColorProfileTagPresent               [MSCMS.@]
1099  *
1100  * Determine if a given ICC tag type is present in a color profile.
1101  *
1102  * PARAMS
1103  *  profile  [I] Color profile handle.
1104  *  tag      [I] ICC tag type.
1105  *  present  [O] Pointer to a BOOL variable. Set to TRUE if tag type is present,
1106  *               FALSE otherwise.
1107  *
1108  * RETURNS
1109  *  Success: TRUE
1110  *  Failure: FALSE
1111  */
1112 BOOL WINAPI IsColorProfileTagPresent( HPROFILE profile, TAGTYPE type, PBOOL present )
1113 {
1114     BOOL ret = FALSE;
1115 #ifdef HAVE_LCMS
1116     icProfile *iccprofile = MSCMS_hprofile2iccprofile( profile );
1117     DWORD i, count;
1118     icTag tag;
1119
1120     TRACE( "( %p, 0x%08x, %p )\n", profile, type, present );
1121
1122     if (!iccprofile || !present) return FALSE;
1123
1124     count = MSCMS_get_tag_count( iccprofile );
1125
1126     for (i = 0; i < count; i++)
1127     {
1128         MSCMS_get_tag_by_index( iccprofile, i, &tag );
1129
1130         if (tag.sig == type)
1131         {
1132             *present = ret = TRUE;
1133             break;
1134         }
1135     }
1136
1137 #endif /* HAVE_LCMS */
1138     return ret;
1139 }
1140
1141 /******************************************************************************
1142  * IsColorProfileValid               [MSCMS.@]
1143  *
1144  * Determine if a given color profile is valid.
1145  *
1146  * PARAMS
1147  *  profile  [I] Color profile handle.
1148  *  valid    [O] Pointer to a BOOL variable. Set to TRUE if profile is valid,
1149  *               FALSE otherwise.
1150  *
1151  * RETURNS
1152  *  Success: TRUE
1153  *  Failure: FALSE 
1154  */
1155 BOOL WINAPI IsColorProfileValid( HPROFILE profile, PBOOL valid )
1156 {
1157     BOOL ret = FALSE;
1158 #ifdef HAVE_LCMS
1159     icProfile *iccprofile = MSCMS_hprofile2iccprofile( profile );
1160
1161     TRACE( "( %p, %p )\n", profile, valid );
1162
1163     if (!valid) return FALSE;
1164     if (iccprofile) return *valid = TRUE;
1165
1166 #endif /* HAVE_LCMS */
1167     return ret;
1168 }
1169
1170 /******************************************************************************
1171  * SetColorProfileElement               [MSCMS.@]
1172  *
1173  * Set data for a specified tag type.
1174  *
1175  * PARAMS
1176  *  profile  [I]   Handle to a color profile.
1177  *  type     [I]   ICC tag type.
1178  *  offset   [I]   Offset in bytes to start copying to.
1179  *  size     [I/O] Size of the buffer in bytes. On return the variable holds the
1180  *                 number of bytes actually needed.
1181  *  buffer   [O]   Buffer holding the tag data.
1182  *
1183  * RETURNS
1184  *  Success: TRUE
1185  *  Failure: FALSE
1186  */
1187 BOOL WINAPI SetColorProfileElement( HPROFILE profile, TAGTYPE type, DWORD offset, PDWORD size,
1188                                     PVOID buffer )
1189 {
1190     BOOL ret = FALSE;
1191 #ifdef HAVE_LCMS
1192     icProfile *iccprofile = MSCMS_hprofile2iccprofile( profile );
1193     DWORD i, count, access = MSCMS_hprofile2access( profile );
1194     icTag tag;
1195
1196     TRACE( "( %p, 0x%08x, %d, %p, %p )\n", profile, type, offset, size, buffer );
1197
1198     if (!iccprofile || !size || !buffer) return FALSE;
1199     if (!(access & PROFILE_READWRITE)) return FALSE;
1200
1201     count = MSCMS_get_tag_count( iccprofile );
1202
1203     for (i = 0; i < count; i++)
1204     {
1205         MSCMS_get_tag_by_index( iccprofile, i, &tag );
1206
1207         if (tag.sig == type)
1208         {
1209             if (offset > tag.size) return FALSE;
1210
1211             MSCMS_set_tag_data( iccprofile, &tag, offset, buffer );
1212             return TRUE;
1213         }
1214     }
1215
1216 #endif /* HAVE_LCMS */
1217     return ret;
1218 }
1219
1220 /******************************************************************************
1221  * SetColorProfileHeader               [MSCMS.@]
1222  *
1223  * Set header data for a given profile.
1224  *
1225  * PARAMS
1226  *  profile  [I] Handle to a color profile.
1227  *  header   [I] Buffer holding the header data.
1228  *
1229  * RETURNS
1230  *  Success: TRUE
1231  *  Failure: FALSE
1232  */
1233 BOOL WINAPI SetColorProfileHeader( HPROFILE profile, PPROFILEHEADER header )
1234 {
1235 #ifdef HAVE_LCMS
1236     icProfile *iccprofile = MSCMS_hprofile2iccprofile( profile );
1237     DWORD access = MSCMS_hprofile2access( profile );
1238
1239     TRACE( "( %p, %p )\n", profile, header );
1240
1241     if (!iccprofile || !header) return FALSE;
1242     if (!(access & PROFILE_READWRITE)) return FALSE;
1243
1244     MSCMS_set_profile_header( iccprofile, header );
1245     return TRUE;
1246
1247 #else
1248     return FALSE;
1249 #endif /* HAVE_LCMS */
1250 }
1251
1252 /******************************************************************************
1253  * UninstallColorProfileA               [MSCMS.@]
1254  *
1255  * See UninstallColorProfileW.
1256  */
1257 BOOL WINAPI UninstallColorProfileA( PCSTR machine, PCSTR profile, BOOL delete )
1258 {
1259     UINT len;
1260     LPWSTR profileW;
1261     BOOL ret = FALSE;
1262
1263     TRACE( "( %s, %x )\n", debugstr_a(profile), delete );
1264
1265     if (machine || !profile) return FALSE;
1266
1267     len = MultiByteToWideChar( CP_ACP, 0, profile, -1, NULL, 0 );
1268     profileW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
1269
1270     if (profileW)
1271     {
1272         MultiByteToWideChar( CP_ACP, 0, profile, -1, profileW, len );
1273
1274         ret = UninstallColorProfileW( NULL, profileW , delete );
1275
1276         HeapFree( GetProcessHeap(), 0, profileW );
1277     }
1278     return ret;
1279 }
1280
1281 /******************************************************************************
1282  * UninstallColorProfileW               [MSCMS.@]
1283  *
1284  * Uninstall a color profile.
1285  *
1286  * PARAMS
1287  *  machine  [I] Name of the machine to uninstall the profile on. Must be NULL,
1288  *               which indicates the local machine.
1289  *  profile  [I] Full path name of the profile to uninstall.
1290  *  delete   [I] Bool that specifies whether the profile file should be deleted.
1291  *
1292  * RETURNS
1293  *  Success: TRUE
1294  *  Failure: FALSE
1295  */
1296 BOOL WINAPI UninstallColorProfileW( PCWSTR machine, PCWSTR profile, BOOL delete )
1297 {
1298     TRACE( "( %s, %x )\n", debugstr_w(profile), delete );
1299
1300     if (machine || !profile) return FALSE;
1301
1302     if (delete) return DeleteFileW( profile );
1303
1304     return TRUE;
1305 }
1306
1307 /******************************************************************************
1308  * OpenColorProfileA               [MSCMS.@]
1309  *
1310  * See OpenColorProfileW.
1311  */
1312 HPROFILE WINAPI OpenColorProfileA( PPROFILE profile, DWORD access, DWORD sharing, DWORD creation )
1313 {
1314     HPROFILE handle = NULL;
1315
1316     TRACE( "( %p, 0x%08x, 0x%08x, 0x%08x )\n", profile, access, sharing, creation );
1317
1318     if (!profile || !profile->pProfileData) return NULL;
1319
1320     /* No AW conversion needed for memory based profiles */
1321     if (profile->dwType & PROFILE_MEMBUFFER)
1322         return OpenColorProfileW( profile, access, sharing, creation );
1323
1324     if (profile->dwType & PROFILE_FILENAME)
1325     {
1326         UINT len;
1327         PROFILE profileW;
1328
1329         profileW.dwType = profile->dwType;
1330  
1331         len = MultiByteToWideChar( CP_ACP, 0, profile->pProfileData, -1, NULL, 0 );
1332         profileW.pProfileData = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
1333
1334         if (profileW.pProfileData)
1335         {
1336             profileW.cbDataSize = len * sizeof(WCHAR);
1337             MultiByteToWideChar( CP_ACP, 0, profile->pProfileData, -1, profileW.pProfileData, len );
1338
1339             handle = OpenColorProfileW( &profileW, access, sharing, creation );
1340             HeapFree( GetProcessHeap(), 0, profileW.pProfileData );
1341         }
1342     }
1343     return handle;
1344 }
1345
1346 /******************************************************************************
1347  * OpenColorProfileW               [MSCMS.@]
1348  *
1349  * Open a color profile.
1350  *
1351  * PARAMS
1352  *  profile   [I] Pointer to a color profile structure.
1353  *  access    [I] Desired access.
1354  *  sharing   [I] Sharing mode.
1355  *  creation  [I] Creation mode.
1356  *
1357  * RETURNS
1358  *  Success: Handle to the opened profile.
1359  *  Failure: NULL
1360  *
1361  * NOTES
1362  *  Values for access:   PROFILE_READ or PROFILE_READWRITE.
1363  *  Values for sharing:  0 (no sharing), FILE_SHARE_READ and/or FILE_SHARE_WRITE.
1364  *  Values for creation: one of CREATE_NEW, CREATE_ALWAYS, OPEN_EXISTING,
1365  *                       OPEN_ALWAYS, TRUNCATE_EXISTING.
1366  *  Sharing and creation flags are ignored for memory based profiles.
1367  */
1368 HPROFILE WINAPI OpenColorProfileW( PPROFILE profile, DWORD access, DWORD sharing, DWORD creation )
1369 {
1370 #ifdef HAVE_LCMS
1371     cmsHPROFILE cmsprofile = NULL;
1372     icProfile *iccprofile = NULL;
1373     HANDLE handle = NULL;
1374     DWORD size;
1375
1376     TRACE( "( %p, 0x%08x, 0x%08x, 0x%08x )\n", profile, access, sharing, creation );
1377
1378     if (!profile || !profile->pProfileData) return NULL;
1379
1380     if (profile->dwType & PROFILE_MEMBUFFER)
1381     {
1382         /* FIXME: access flags not implemented for memory based profiles */
1383
1384         iccprofile = profile->pProfileData;
1385         size = profile->cbDataSize;
1386     
1387         cmsprofile = cmsOpenProfileFromMem( iccprofile, size );
1388     }
1389
1390     if (profile->dwType & PROFILE_FILENAME)
1391     {
1392         DWORD read, flags = 0;
1393
1394         TRACE( "profile file: %s\n", debugstr_w( (WCHAR *)profile->pProfileData ) );
1395
1396         if (access & PROFILE_READ) flags = GENERIC_READ;
1397         if (access & PROFILE_READWRITE) flags = GENERIC_READ|GENERIC_WRITE;
1398
1399         if (!flags) return NULL;
1400
1401         handle = CreateFileW( profile->pProfileData, flags, sharing, NULL, creation, 0, NULL );
1402         if (handle == INVALID_HANDLE_VALUE)
1403         {
1404             WARN( "Unable to open color profile\n" );
1405             return NULL;
1406         }
1407
1408         if ((size = GetFileSize( handle, NULL )) == INVALID_FILE_SIZE)
1409         {
1410             ERR( "Unable to retrieve size of color profile\n" );
1411             CloseHandle( handle );
1412             return NULL;
1413         }
1414
1415         iccprofile = HeapAlloc( GetProcessHeap(), 0, size );
1416         if (!iccprofile)
1417         {
1418             ERR( "Unable to allocate memory for color profile\n" );
1419             CloseHandle( handle );
1420             return NULL;
1421         }
1422
1423         if (!ReadFile( handle, iccprofile, size, &read, NULL ) || read != size)
1424         {
1425             ERR( "Unable to read color profile\n" );
1426
1427             CloseHandle( handle );
1428             HeapFree( GetProcessHeap(), 0, iccprofile );
1429             return NULL;
1430         }
1431
1432         cmsprofile = cmsOpenProfileFromMem( iccprofile, size );
1433     }
1434
1435     if (cmsprofile)
1436         return MSCMS_create_hprofile_handle( handle, iccprofile, cmsprofile, access );
1437
1438 #endif /* HAVE_LCMS */
1439     return NULL;
1440 }
1441
1442 /******************************************************************************
1443  * CloseColorProfile               [MSCMS.@]
1444  *
1445  * Close a color profile.
1446  *
1447  * PARAMS
1448  *  profile  [I] Handle to the profile.
1449  *
1450  * RETURNS
1451  *  Success: TRUE
1452  *  Failure: FALSE
1453  */
1454 BOOL WINAPI CloseColorProfile( HPROFILE profile )
1455 {
1456     BOOL ret = FALSE;
1457 #ifdef HAVE_LCMS
1458     icProfile *iccprofile = MSCMS_hprofile2iccprofile( profile );
1459     HANDLE file = MSCMS_hprofile2handle( profile );
1460     DWORD access = MSCMS_hprofile2access( profile );
1461
1462     TRACE( "( %p )\n", profile );
1463
1464     if (file && (access & PROFILE_READWRITE))
1465     {
1466         DWORD written, size = MSCMS_get_profile_size( iccprofile );
1467
1468         if (SetFilePointer( file, 0, NULL, FILE_BEGIN ) ||
1469             !WriteFile( file, iccprofile, size, &written, NULL ) || written != size)
1470             ERR( "Unable to write color profile\n" );
1471     }
1472
1473     ret = cmsCloseProfile( MSCMS_hprofile2cmsprofile( profile ) );
1474     HeapFree( GetProcessHeap(), 0, MSCMS_hprofile2iccprofile( profile ) );
1475
1476     CloseHandle( MSCMS_hprofile2handle( profile ) );
1477     MSCMS_destroy_hprofile_handle( profile );
1478
1479 #endif /* HAVE_LCMS */
1480     return ret;
1481 }