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