gdi32/tests: Fix a compiler warning.
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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         if ((ret = GetColorDirectoryW( NULL, bufferW, &sizeW )))
95         {
96             *size = WideCharToMultiByte( CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL );
97             len = WideCharToMultiByte( CP_ACP, 0, bufferW, -1, buffer, *size, NULL, NULL );
98             if (!len) ret = FALSE;
99         }
100         else *size = sizeW / sizeof(WCHAR);
101
102         HeapFree( GetProcessHeap(), 0, bufferW );
103     }
104     return ret;
105 }
106
107 /******************************************************************************
108  * GetColorDirectoryW               [MSCMS.@]
109  *
110  * Get the directory where color profiles are stored.
111  *
112  * PARAMS
113  *  machine  [I]   Name of the machine for which to get the color directory.
114  *                 Must be NULL, which indicates the local machine.
115  *  buffer   [I]   Buffer to receive the path name.
116  *  size     [I/O] Size of the buffer in bytes. On return the variable holds
117  *                 the number of bytes actually needed.
118  */
119 BOOL WINAPI GetColorDirectoryW( PCWSTR machine, PWSTR buffer, PDWORD size )
120 {
121     WCHAR colordir[MAX_PATH];
122     static const WCHAR colorsubdir[] = { '\\','c','o','l','o','r',0 };
123     DWORD len;
124
125     TRACE( "( %p, %p )\n", buffer, size );
126
127     if (machine || !size) return FALSE;
128
129     GetSystemDirectoryW( colordir, sizeof(colordir) / sizeof(WCHAR) );
130     lstrcatW( colordir, colorsubdir );
131
132     len = lstrlenW( colordir ) * sizeof(WCHAR);
133
134     if (buffer && len <= *size)
135     {
136         lstrcpyW( buffer, colordir );
137         *size = len;
138         return TRUE;
139     }
140
141     SetLastError( ERROR_MORE_DATA );
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%08x, %d, %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, %d, %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 #ifdef HAVE_LCMS
309     icProfile *iccprofile = MSCMS_hprofile2iccprofile( profile );
310
311     TRACE( "( %p, %p )\n", profile, header );
312
313     if (!iccprofile || !header) return FALSE;
314
315     MSCMS_get_profile_header( iccprofile, header );
316     return TRUE;
317
318 #else
319     return FALSE;
320 #endif /* HAVE_LCMS */
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%08x, %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         if ((ret = GetStandardColorSpaceProfileW( NULL, id, profileW, &sizeW )))
393         {
394             *size = WideCharToMultiByte( CP_ACP, 0, profileW, -1, NULL, 0, NULL, NULL );
395             len = WideCharToMultiByte( CP_ACP, 0, profileW, -1, profile, *size, NULL, NULL );
396             if (!len) ret = FALSE;
397         }
398         else *size = sizeW / sizeof(WCHAR);
399
400         HeapFree( GetProcessHeap(), 0, profileW );
401     }
402     return ret;
403 }
404
405 /******************************************************************************
406  * GetStandardColorSpaceProfileW               [MSCMS.@]
407  *
408  * Retrieve the profile filename for a given standard color space id.
409  *
410  * PARAMS
411  *  machine  [I]   Name of the machine for which to get the standard color space.
412  *                 Must be NULL, which indicates the local machine.
413  *  id       [I]   Id of a standard color space.
414  *  profile  [O]   Buffer to receive the profile filename.
415  *  size     [I/O] Size of the filename buffer in bytes.
416  *
417  * RETURNS
418  *  Success: TRUE
419  *  Failure: FALSE
420  */
421 BOOL WINAPI GetStandardColorSpaceProfileW( PCWSTR machine, DWORD id, PWSTR profile, PDWORD size )
422 {
423     static const WCHAR rgbprofilefile[] =
424         { '\\','s','r','g','b',' ','c','o','l','o','r',' ',
425           's','p','a','c','e',' ','p','r','o','f','i','l','e','.','i','c','m',0 };
426     WCHAR rgbprofile[MAX_PATH];
427     DWORD len = sizeof(rgbprofile);
428
429     TRACE( "( 0x%08x, %p, %p )\n", id, profile, size );
430
431     if (machine) 
432     {
433         SetLastError( ERROR_NOT_SUPPORTED );
434         return FALSE;
435     }
436
437     if (!size) 
438     {
439         SetLastError( ERROR_INVALID_PARAMETER );
440         return FALSE;
441     }
442
443     if (!profile)
444     {
445         SetLastError( ERROR_INSUFFICIENT_BUFFER );
446         return FALSE;
447     }
448
449     GetColorDirectoryW( machine, rgbprofile, &len );
450
451     switch (id)
452     {
453         case SPACE_RGB: /* 'RGB ' */
454             lstrcatW( rgbprofile, rgbprofilefile );
455             len = lstrlenW( rgbprofile ) * sizeof(WCHAR);
456
457             if (*size < len || !profile)
458             {
459                 *size = len;
460                 SetLastError( ERROR_MORE_DATA );
461                 return FALSE;
462             }
463
464             lstrcpyW( profile, rgbprofile );
465             break;
466
467         default:
468             SetLastError( ERROR_FILE_NOT_FOUND );
469             return FALSE;
470     }
471     return TRUE;
472 }
473
474 static BOOL MSCMS_header_from_file( LPCWSTR file, PPROFILEHEADER header )
475 {
476     BOOL ret;
477     PROFILE profile;
478     WCHAR path[MAX_PATH], slash[] = {'\\',0};
479     DWORD size = sizeof(path);
480     HANDLE handle;
481
482     ret = GetColorDirectoryW( NULL, path, &size );
483     if (!ret)
484     {
485         WARN( "Can't retrieve color directory\n" );
486         return FALSE;
487     }
488     if (size + sizeof(slash) + sizeof(WCHAR) * lstrlenW( file ) > sizeof(path))
489     {
490         WARN( "Filename too long\n" );
491         return FALSE;
492     }
493
494     lstrcatW( path, slash );
495     lstrcatW( path, file );
496
497     profile.dwType = PROFILE_FILENAME;
498     profile.pProfileData = path;
499     profile.cbDataSize = lstrlenW( path ) + 1;
500
501     handle = OpenColorProfileW( &profile, PROFILE_READ, FILE_SHARE_READ, OPEN_EXISTING );
502     if (!handle)
503     {
504         WARN( "Can't open color profile\n" );
505         return FALSE;
506     }
507
508     ret = GetColorProfileHeader( handle, header );
509     if (!ret)
510         WARN( "Can't retrieve color profile header\n" );
511
512     CloseColorProfile( handle );
513     return ret;
514 }
515
516 static BOOL MSCMS_match_profile( PENUMTYPEW rec, PPROFILEHEADER hdr )
517 {
518     if (rec->dwFields & ET_DEVICENAME)
519     {
520         FIXME( "ET_DEVICENAME: %s\n", debugstr_w(rec->pDeviceName) );
521     }
522     if (rec->dwFields & ET_MEDIATYPE)
523     {
524         FIXME( "ET_MEDIATYPE: 0x%08x\n", rec->dwMediaType );
525     }
526     if (rec->dwFields & ET_DITHERMODE)
527     {
528         FIXME( "ET_DITHERMODE: 0x%08x\n", rec->dwDitheringMode );
529     }
530     if (rec->dwFields & ET_RESOLUTION)
531     {
532         FIXME( "ET_RESOLUTION: 0x%08x, 0x%08x\n",
533                rec->dwResolution[0], rec->dwResolution[1] );
534     }
535     if (rec->dwFields & ET_DEVICECLASS)
536     {
537         FIXME( "ET_DEVICECLASS: %s\n", MSCMS_dbgstr_tag(rec->dwMediaType) );
538     }
539     if (rec->dwFields & ET_CMMTYPE)
540     {
541         TRACE( "ET_CMMTYPE: %s\n", MSCMS_dbgstr_tag(rec->dwCMMType) );
542         if (rec->dwCMMType != hdr->phCMMType) return FALSE;
543     }
544     if (rec->dwFields & ET_CLASS)
545     {
546         TRACE( "ET_CLASS: %s\n", MSCMS_dbgstr_tag(rec->dwClass) );
547         if (rec->dwClass != hdr->phClass) return FALSE;
548     }
549     if (rec->dwFields & ET_DATACOLORSPACE)
550     {
551         TRACE( "ET_DATACOLORSPACE: %s\n", MSCMS_dbgstr_tag(rec->dwDataColorSpace) );
552         if (rec->dwDataColorSpace != hdr->phDataColorSpace) return FALSE;
553     }
554     if (rec->dwFields & ET_CONNECTIONSPACE)
555     {
556         TRACE( "ET_CONNECTIONSPACE: %s\n", MSCMS_dbgstr_tag(rec->dwConnectionSpace) );
557         if (rec->dwConnectionSpace != hdr->phConnectionSpace) return FALSE;
558     }
559     if (rec->dwFields & ET_SIGNATURE)
560     {
561         TRACE( "ET_SIGNATURE: %s\n", MSCMS_dbgstr_tag(rec->dwSignature) );
562         if (rec->dwSignature != hdr->phSignature) return FALSE;
563     }
564     if (rec->dwFields & ET_PLATFORM)
565     {
566         TRACE( "ET_PLATFORM: %s\n", MSCMS_dbgstr_tag(rec->dwPlatform) );
567         if (rec->dwPlatform != hdr->phPlatform) return FALSE;
568     }
569     if (rec->dwFields & ET_PROFILEFLAGS)
570     {
571         TRACE( "ET_PROFILEFLAGS: 0x%08x\n", rec->dwProfileFlags );
572         if (rec->dwProfileFlags != hdr->phProfileFlags) return FALSE;
573     }
574     if (rec->dwFields & ET_MANUFACTURER)
575     {
576         TRACE( "ET_MANUFACTURER: %s\n", MSCMS_dbgstr_tag(rec->dwManufacturer) );
577         if (rec->dwManufacturer != hdr->phManufacturer) return FALSE;
578     }
579     if (rec->dwFields & ET_MODEL)
580     {
581         TRACE( "ET_MODEL: %s\n", MSCMS_dbgstr_tag(rec->dwModel) );
582         if (rec->dwModel != hdr->phModel) return FALSE;
583     }
584     if (rec->dwFields & ET_ATTRIBUTES)
585     {
586         TRACE( "ET_ATTRIBUTES: 0x%08x, 0x%08x\n",
587                rec->dwAttributes[0], rec->dwAttributes[1] );
588         if (rec->dwAttributes[0] != hdr->phAttributes[0] || 
589             rec->dwAttributes[1] != hdr->phAttributes[1]) return FALSE;
590     }
591     if (rec->dwFields & ET_RENDERINGINTENT)
592     {
593         TRACE( "ET_RENDERINGINTENT: 0x%08x\n", rec->dwRenderingIntent );
594         if (rec->dwRenderingIntent != hdr->phRenderingIntent) return FALSE;
595     }
596     if (rec->dwFields & ET_CREATOR)
597     {
598         TRACE( "ET_CREATOR: %s\n", MSCMS_dbgstr_tag(rec->dwCreator) );
599         if (rec->dwCreator != hdr->phCreator) return FALSE;
600     }
601     return TRUE;
602 }
603
604 /******************************************************************************
605  * EnumColorProfilesA               [MSCMS.@]
606  *
607  * See EnumColorProfilesW.
608  */
609 BOOL WINAPI EnumColorProfilesA( PCSTR machine, PENUMTYPEA record, PBYTE buffer,
610                                 PDWORD size, PDWORD number )
611 {
612     BOOL match, ret = FALSE;
613     char spec[] = "\\*";
614     char colordir[MAX_PATH], glob[MAX_PATH], **profiles = NULL;
615     DWORD i, len = sizeof(colordir), count = 0, totalsize = 0;
616     PROFILEHEADER header;
617     WIN32_FIND_DATAA data;
618     ENUMTYPEW recordW;
619     WCHAR *fileW = NULL, *deviceW = NULL;
620     HANDLE find;
621
622     TRACE( "( %p, %p, %p, %p, %p )\n", machine, record, buffer, size, number );
623
624     if (machine || !record || !size ||
625         record->dwSize != sizeof(ENUMTYPEA) ||
626         record->dwVersion != ENUM_TYPE_VERSION) return FALSE;
627
628     ret = GetColorDirectoryA( machine, colordir, &len );
629     if (!ret || len + sizeof(spec) > MAX_PATH)
630     {
631         WARN( "can't retrieve color directory\n" );
632         return FALSE;
633     }
634
635     lstrcpyA( glob, colordir );
636     lstrcatA( glob, spec );
637
638     find = FindFirstFileA( glob, &data );
639     if (find == INVALID_HANDLE_VALUE) return FALSE;
640
641     profiles = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(char *) + 1 );
642     if (!profiles) goto exit;
643
644     memcpy( &recordW, record, sizeof(ENUMTYPEA) );
645     if (record->pDeviceName)
646     {
647         deviceW = MSCMS_strdupW( record->pDeviceName );
648         if (!(recordW.pDeviceName = deviceW)) goto exit;
649     }
650
651     fileW = MSCMS_strdupW( data.cFileName );
652     if (!fileW) goto exit;
653
654     ret = MSCMS_header_from_file( fileW, &header );
655     if (ret)
656     {
657         match = MSCMS_match_profile( &recordW, &header );
658         if (match)
659         {
660             len = sizeof(char) * (lstrlenA( data.cFileName ) + 1);
661             profiles[count] = HeapAlloc( GetProcessHeap(), 0, len );
662
663             if (!profiles[count]) goto exit;
664             else
665             {
666                 TRACE( "matching profile: %s\n", debugstr_a(data.cFileName) );
667                 lstrcpyA( profiles[count], data.cFileName );
668                 totalsize += len;
669                 count++;
670             }
671         }
672     }
673     HeapFree( GetProcessHeap(), 0, fileW );
674     fileW = NULL;
675
676     while (FindNextFileA( find, &data ))
677     {
678         fileW = MSCMS_strdupW( data.cFileName );
679         if (!fileW) goto exit;
680
681         ret = MSCMS_header_from_file( fileW, &header );
682         if (!ret)
683         {
684             HeapFree( GetProcessHeap(), 0, fileW );
685             continue;
686         }
687
688         match = MSCMS_match_profile( &recordW, &header );
689         if (match)
690         {
691             char **tmp = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
692                                       profiles, sizeof(char *) * (count + 1) );
693             if (!tmp) goto exit;
694             else profiles = tmp;
695
696             len = sizeof(char) * (lstrlenA( data.cFileName ) + 1);
697             profiles[count] = HeapAlloc( GetProcessHeap(), 0, len );
698
699             if (!profiles[count]) goto exit;
700             else
701             {
702                 TRACE( "matching profile: %s\n", debugstr_a(data.cFileName) );
703                 lstrcpyA( profiles[count], data.cFileName );
704                 totalsize += len;
705                 count++;
706             }
707         }
708         HeapFree( GetProcessHeap(), 0, fileW );
709         fileW = NULL;
710     }
711
712     totalsize++;
713     if (buffer && *size >= totalsize)
714     {
715         char *p = (char *)buffer;
716
717         for (i = 0; i < count; i++)
718         {
719             lstrcpyA( p, profiles[i] );
720             p += lstrlenA( profiles[i] ) + 1;
721         }
722         *p = 0;
723         ret = TRUE;
724     }
725     else ret = FALSE;
726
727     *size = totalsize;
728     if (number) *number = count;
729
730 exit:
731     for (i = 0; i < count; i++)
732         HeapFree( GetProcessHeap(), 0, profiles[i] );
733     HeapFree( GetProcessHeap(), 0, profiles );
734     HeapFree( GetProcessHeap(), 0, deviceW );
735     HeapFree( GetProcessHeap(), 0, fileW );
736     FindClose( find );
737
738     return ret;
739 }
740
741 /******************************************************************************
742  * EnumColorProfilesW               [MSCMS.@]
743  *
744  * Enumerate profiles that match given criteria.
745  *
746  * PARAMS
747  *  machine  [I]   Name of the machine for which to enumerate profiles.
748  *                 Must be NULL, which indicates the local machine.
749  *  record   [I]   Record of criteria that a profile must match.
750  *  buffer   [O]   Buffer to receive a string array of profile filenames.
751  *  size     [I/O] Size of the filename buffer in bytes.
752  *  number   [O]   Number of filenames copied into buffer.
753  *
754  * RETURNS
755  *  Success: TRUE
756  *  Failure: FALSE
757  */
758 BOOL WINAPI EnumColorProfilesW( PCWSTR machine, PENUMTYPEW record, PBYTE buffer,
759                                 PDWORD size, PDWORD number )
760 {
761     BOOL match, ret = FALSE;
762     WCHAR spec[] = {'\\','*',0};
763     WCHAR colordir[MAX_PATH], glob[MAX_PATH], **profiles = NULL;
764     DWORD i, len = sizeof(colordir), count = 0, totalsize = 0;
765     PROFILEHEADER header;
766     WIN32_FIND_DATAW data;
767     HANDLE find;
768
769     TRACE( "( %p, %p, %p, %p, %p )\n", machine, record, buffer, size, number );
770
771     if (machine || !record || !size ||
772         record->dwSize != sizeof(ENUMTYPEW) ||
773         record->dwVersion != ENUM_TYPE_VERSION) return FALSE;
774
775     ret = GetColorDirectoryW( machine, colordir, &len );
776     if (!ret || len + sizeof(spec) > MAX_PATH)
777     {
778         WARN( "Can't retrieve color directory\n" );
779         return FALSE;
780     }
781
782     lstrcpyW( glob, colordir );
783     lstrcatW( glob, spec );
784
785     find = FindFirstFileW( glob, &data );
786     if (find == INVALID_HANDLE_VALUE) return FALSE;
787
788     profiles = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WCHAR *) + 1 );
789     if (!profiles) goto exit;
790
791     ret = MSCMS_header_from_file( data.cFileName, &header );
792     if (ret)
793     {
794         match = MSCMS_match_profile( record, &header );
795         if (match)
796         {
797             len = sizeof(WCHAR) * (lstrlenW( data.cFileName ) + 1);
798             profiles[count] = HeapAlloc( GetProcessHeap(), 0, len );
799
800             if (!profiles[count]) goto exit;
801             else
802             {
803                 TRACE( "matching profile: %s\n", debugstr_w(data.cFileName) );
804                 lstrcpyW( profiles[count], data.cFileName );
805                 totalsize += len;
806                 count++;
807             }
808         }
809     }
810
811     while (FindNextFileW( find, &data ))
812     {
813         ret = MSCMS_header_from_file( data.cFileName, &header );
814         if (!ret) continue;
815
816         match = MSCMS_match_profile( record, &header );
817         if (match)
818         {
819             WCHAR **tmp = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
820                                        profiles, sizeof(WCHAR *) * (count + 1) );
821             if (!tmp) goto exit;
822             else profiles = tmp;
823
824             len = sizeof(WCHAR) * (lstrlenW( data.cFileName ) + 1);
825             profiles[count] = HeapAlloc( GetProcessHeap(), 0, len );
826
827             if (!profiles[count]) goto exit;
828             else
829             {
830                 TRACE( "matching profile: %s\n", debugstr_w(data.cFileName) );
831                 lstrcpyW( profiles[count], data.cFileName );
832                 totalsize += len;
833                 count++;
834             }
835         }
836     }
837
838     totalsize++;
839     if (buffer && *size >= totalsize)
840     {
841         WCHAR *p = (WCHAR *)buffer;
842
843         for (i = 0; i < count; i++)
844         {
845             lstrcpyW( p, profiles[i] );
846             p += lstrlenW( profiles[i] ) + 1;
847         }
848         *p = 0;
849         ret = TRUE;
850     }
851     else ret = FALSE;
852
853     *size = totalsize;
854     if (number) *number = count;
855
856 exit:
857     for (i = 0; i < count; i++)
858         HeapFree( GetProcessHeap(), 0, profiles[i] );
859     HeapFree( GetProcessHeap(), 0, profiles );
860     FindClose( find );
861
862     return ret;
863 }
864
865 /******************************************************************************
866  * InstallColorProfileA               [MSCMS.@]
867  *
868  * See InstallColorProfileW.
869  */
870 BOOL WINAPI InstallColorProfileA( PCSTR machine, PCSTR profile )
871 {
872     UINT len;
873     LPWSTR profileW;
874     BOOL ret = FALSE;
875
876     TRACE( "( %s )\n", debugstr_a(profile) );
877
878     if (machine || !profile) return FALSE;
879
880     len = MultiByteToWideChar( CP_ACP, 0, profile, -1, NULL, 0 );
881     profileW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
882
883     if (profileW)
884     {
885         MultiByteToWideChar( CP_ACP, 0, profile, -1, profileW, len );
886
887         ret = InstallColorProfileW( NULL, profileW );
888         HeapFree( GetProcessHeap(), 0, profileW );
889     }
890     return ret;
891 }
892
893 /******************************************************************************
894  * InstallColorProfileW               [MSCMS.@]
895  *
896  * Install a color profile.
897  *
898  * PARAMS
899  *  machine  [I] Name of the machine to install the profile on. Must be NULL,
900  *               which indicates the local machine.
901  *  profile  [I] Full path name of the profile to install.
902  *
903  * RETURNS
904  *  Success: TRUE
905  *  Failure: FALSE
906  */
907 BOOL WINAPI InstallColorProfileW( PCWSTR machine, PCWSTR profile )
908 {
909     WCHAR dest[MAX_PATH], base[MAX_PATH];
910     DWORD size = sizeof(dest);
911     static const WCHAR slash[] = { '\\', 0 };
912
913     TRACE( "( %s )\n", debugstr_w(profile) );
914
915     if (machine || !profile) return FALSE;
916
917     if (!GetColorDirectoryW( machine, dest, &size )) return FALSE;
918
919     MSCMS_basename( profile, base );
920
921     lstrcatW( dest, slash );
922     lstrcatW( dest, base );
923
924     /* Is source equal to destination? */
925     if (!lstrcmpW( profile, dest )) return TRUE;
926
927     return CopyFileW( profile, dest, TRUE );
928 }
929
930 /******************************************************************************
931  * IsColorProfileTagPresent               [MSCMS.@]
932  *
933  * Determine if a given ICC tag type is present in a color profile.
934  *
935  * PARAMS
936  *  profile  [I] Color profile handle.
937  *  tag      [I] ICC tag type.
938  *  present  [O] Pointer to a BOOL variable. Set to TRUE if tag type is present,
939  *               FALSE otherwise.
940  *
941  * RETURNS
942  *  Success: TRUE
943  *  Failure: FALSE
944  */
945 BOOL WINAPI IsColorProfileTagPresent( HPROFILE profile, TAGTYPE type, PBOOL present )
946 {
947     BOOL ret = FALSE;
948 #ifdef HAVE_LCMS
949     icProfile *iccprofile = MSCMS_hprofile2iccprofile( profile );
950     DWORD i, count;
951     icTag tag;
952
953     TRACE( "( %p, 0x%08x, %p )\n", profile, type, present );
954
955     if (!iccprofile || !present) return FALSE;
956
957     count = MSCMS_get_tag_count( iccprofile );
958
959     for (i = 0; i < count; i++)
960     {
961         MSCMS_get_tag_by_index( iccprofile, i, &tag );
962
963         if (tag.sig == type)
964         {
965             *present = ret = TRUE;
966             break;
967         }
968     }
969
970 #endif /* HAVE_LCMS */
971     return ret;
972 }
973
974 /******************************************************************************
975  * IsColorProfileValid               [MSCMS.@]
976  *
977  * Determine if a given color profile is valid.
978  *
979  * PARAMS
980  *  profile  [I] Color profile handle.
981  *  valid    [O] Pointer to a BOOL variable. Set to TRUE if profile is valid,
982  *               FALSE otherwise.
983  *
984  * RETURNS
985  *  Success: TRUE
986  *  Failure: FALSE 
987  */
988 BOOL WINAPI IsColorProfileValid( HPROFILE profile, PBOOL valid )
989 {
990     BOOL ret = FALSE;
991 #ifdef HAVE_LCMS
992     icProfile *iccprofile = MSCMS_hprofile2iccprofile( profile );
993
994     TRACE( "( %p, %p )\n", profile, valid );
995
996     if (!valid) return FALSE;
997     if (iccprofile) return *valid = TRUE;
998
999 #endif /* HAVE_LCMS */
1000     return ret;
1001 }
1002
1003 /******************************************************************************
1004  * SetColorProfileElement               [MSCMS.@]
1005  *
1006  * Set data for a specified tag type.
1007  *
1008  * PARAMS
1009  *  profile  [I]   Handle to a color profile.
1010  *  type     [I]   ICC tag type.
1011  *  offset   [I]   Offset in bytes to start copying to.
1012  *  size     [I/O] Size of the buffer in bytes. On return the variable holds the
1013  *                 number of bytes actually needed.
1014  *  buffer   [O]   Buffer holding the tag data.
1015  *
1016  * RETURNS
1017  *  Success: TRUE
1018  *  Failure: FALSE
1019  */
1020 BOOL WINAPI SetColorProfileElement( HPROFILE profile, TAGTYPE type, DWORD offset, PDWORD size,
1021                                     PVOID buffer )
1022 {
1023     BOOL ret = FALSE;
1024 #ifdef HAVE_LCMS
1025     icProfile *iccprofile = MSCMS_hprofile2iccprofile( profile );
1026     DWORD i, count, access = MSCMS_hprofile2access( profile );
1027     icTag tag;
1028
1029     TRACE( "( %p, 0x%08x, %d, %p, %p )\n", profile, type, offset, size, buffer );
1030
1031     if (!iccprofile || !size || !buffer) return FALSE;
1032     if (!(access & PROFILE_READWRITE)) return FALSE;
1033
1034     count = MSCMS_get_tag_count( iccprofile );
1035
1036     for (i = 0; i < count; i++)
1037     {
1038         MSCMS_get_tag_by_index( iccprofile, i, &tag );
1039
1040         if (tag.sig == type)
1041         {
1042             if (offset > tag.size) return FALSE;
1043
1044             MSCMS_set_tag_data( iccprofile, &tag, offset, buffer );
1045             return TRUE;
1046         }
1047     }
1048
1049 #endif /* HAVE_LCMS */
1050     return ret;
1051 }
1052
1053 /******************************************************************************
1054  * SetColorProfileHeader               [MSCMS.@]
1055  *
1056  * Set header data for a given profile.
1057  *
1058  * PARAMS
1059  *  profile  [I] Handle to a color profile.
1060  *  header   [I] Buffer holding the header data.
1061  *
1062  * RETURNS
1063  *  Success: TRUE
1064  *  Failure: FALSE
1065  */
1066 BOOL WINAPI SetColorProfileHeader( HPROFILE profile, PPROFILEHEADER header )
1067 {
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 #else
1081     return FALSE;
1082 #endif /* HAVE_LCMS */
1083 }
1084
1085 /******************************************************************************
1086  * UninstallColorProfileA               [MSCMS.@]
1087  *
1088  * See UninstallColorProfileW.
1089  */
1090 BOOL WINAPI UninstallColorProfileA( PCSTR machine, PCSTR profile, BOOL delete )
1091 {
1092     UINT len;
1093     LPWSTR profileW;
1094     BOOL ret = FALSE;
1095
1096     TRACE( "( %s, %x )\n", debugstr_a(profile), delete );
1097
1098     if (machine || !profile) return FALSE;
1099
1100     len = MultiByteToWideChar( CP_ACP, 0, profile, -1, NULL, 0 );
1101     profileW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
1102
1103     if (profileW)
1104     {
1105         MultiByteToWideChar( CP_ACP, 0, profile, -1, profileW, len );
1106
1107         ret = UninstallColorProfileW( NULL, profileW , delete );
1108
1109         HeapFree( GetProcessHeap(), 0, profileW );
1110     }
1111     return ret;
1112 }
1113
1114 /******************************************************************************
1115  * UninstallColorProfileW               [MSCMS.@]
1116  *
1117  * Uninstall a color profile.
1118  *
1119  * PARAMS
1120  *  machine  [I] Name of the machine to uninstall the profile on. Must be NULL,
1121  *               which indicates the local machine.
1122  *  profile  [I] Full path name of the profile to uninstall.
1123  *  delete   [I] Bool that specifies whether the profile file should be deleted.
1124  *
1125  * RETURNS
1126  *  Success: TRUE
1127  *  Failure: FALSE
1128  */
1129 BOOL WINAPI UninstallColorProfileW( PCWSTR machine, PCWSTR profile, BOOL delete )
1130 {
1131     TRACE( "( %s, %x )\n", debugstr_w(profile), delete );
1132
1133     if (machine || !profile) return FALSE;
1134
1135     if (delete) return DeleteFileW( profile );
1136
1137     return TRUE;
1138 }
1139
1140 /******************************************************************************
1141  * OpenColorProfileA               [MSCMS.@]
1142  *
1143  * See OpenColorProfileW.
1144  */
1145 HPROFILE WINAPI OpenColorProfileA( PPROFILE profile, DWORD access, DWORD sharing, DWORD creation )
1146 {
1147     HPROFILE handle = NULL;
1148
1149     TRACE( "( %p, 0x%08x, 0x%08x, 0x%08x )\n", profile, access, sharing, creation );
1150
1151     if (!profile || !profile->pProfileData) return NULL;
1152
1153     /* No AW conversion needed for memory based profiles */
1154     if (profile->dwType & PROFILE_MEMBUFFER)
1155         return OpenColorProfileW( profile, access, sharing, creation );
1156
1157     if (profile->dwType & PROFILE_FILENAME)
1158     {
1159         UINT len;
1160         PROFILE profileW;
1161
1162         profileW.dwType = profile->dwType;
1163  
1164         len = MultiByteToWideChar( CP_ACP, 0, profile->pProfileData, -1, NULL, 0 );
1165         profileW.pProfileData = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
1166
1167         if (profileW.pProfileData)
1168         {
1169             profileW.cbDataSize = len * sizeof(WCHAR);
1170             MultiByteToWideChar( CP_ACP, 0, profile->pProfileData, -1, profileW.pProfileData, len );
1171
1172             handle = OpenColorProfileW( &profileW, access, sharing, creation );
1173             HeapFree( GetProcessHeap(), 0, profileW.pProfileData );
1174         }
1175     }
1176     return handle;
1177 }
1178
1179 /******************************************************************************
1180  * OpenColorProfileW               [MSCMS.@]
1181  *
1182  * Open a color profile.
1183  *
1184  * PARAMS
1185  *  profile   [I] Pointer to a color profile structure.
1186  *  access    [I] Desired access.
1187  *  sharing   [I] Sharing mode.
1188  *  creation  [I] Creation mode.
1189  *
1190  * RETURNS
1191  *  Success: Handle to the opened profile.
1192  *  Failure: NULL
1193  *
1194  * NOTES
1195  *  Values for access:   PROFILE_READ or PROFILE_READWRITE.
1196  *  Values for sharing:  0 (no sharing), FILE_SHARE_READ and/or FILE_SHARE_WRITE.
1197  *  Values for creation: one of CREATE_NEW, CREATE_ALWAYS, OPEN_EXISTING,
1198  *                       OPEN_ALWAYS, TRUNCATE_EXISTING.
1199  *  Sharing and creation flags are ignored for memory based profiles.
1200  */
1201 HPROFILE WINAPI OpenColorProfileW( PPROFILE profile, DWORD access, DWORD sharing, DWORD creation )
1202 {
1203 #ifdef HAVE_LCMS
1204     cmsHPROFILE cmsprofile = NULL;
1205     icProfile *iccprofile = NULL;
1206     HANDLE handle = NULL;
1207     DWORD size;
1208
1209     TRACE( "( %p, 0x%08x, 0x%08x, 0x%08x )\n", profile, access, sharing, creation );
1210
1211     if (!profile || !profile->pProfileData) return NULL;
1212
1213     if (profile->dwType & PROFILE_MEMBUFFER)
1214     {
1215         /* FIXME: access flags not implemented for memory based profiles */
1216
1217         iccprofile = profile->pProfileData;
1218         size = profile->cbDataSize;
1219     
1220         cmsprofile = cmsOpenProfileFromMem( iccprofile, size );
1221     }
1222
1223     if (profile->dwType & PROFILE_FILENAME)
1224     {
1225         DWORD read, flags = 0;
1226
1227         TRACE( "profile file: %s\n", debugstr_w( (WCHAR *)profile->pProfileData ) );
1228
1229         if (access & PROFILE_READ) flags = GENERIC_READ;
1230         if (access & PROFILE_READWRITE) flags = GENERIC_READ|GENERIC_WRITE;
1231
1232         if (!flags) return NULL;
1233
1234         handle = CreateFileW( profile->pProfileData, flags, sharing, NULL, creation, 0, NULL );
1235         if (handle == INVALID_HANDLE_VALUE)
1236         {
1237             WARN( "Unable to open color profile\n" );
1238             return NULL;
1239         }
1240
1241         if ((size = GetFileSize( handle, NULL )) == INVALID_FILE_SIZE)
1242         {
1243             ERR( "Unable to retrieve size of color profile\n" );
1244             CloseHandle( handle );
1245             return NULL;
1246         }
1247
1248         iccprofile = HeapAlloc( GetProcessHeap(), 0, size );
1249         if (!iccprofile)
1250         {
1251             ERR( "Unable to allocate memory for color profile\n" );
1252             CloseHandle( handle );
1253             return NULL;
1254         }
1255
1256         if (!ReadFile( handle, iccprofile, size, &read, NULL ) || read != size)
1257         {
1258             ERR( "Unable to read color profile\n" );
1259
1260             CloseHandle( handle );
1261             HeapFree( GetProcessHeap(), 0, iccprofile );
1262             return NULL;
1263         }
1264
1265         cmsprofile = cmsOpenProfileFromMem( iccprofile, size );
1266     }
1267
1268     if (cmsprofile)
1269         return MSCMS_create_hprofile_handle( handle, iccprofile, cmsprofile, access );
1270
1271 #endif /* HAVE_LCMS */
1272     return NULL;
1273 }
1274
1275 /******************************************************************************
1276  * CloseColorProfile               [MSCMS.@]
1277  *
1278  * Close a color profile.
1279  *
1280  * PARAMS
1281  *  profile  [I] Handle to the profile.
1282  *
1283  * RETURNS
1284  *  Success: TRUE
1285  *  Failure: FALSE
1286  */
1287 BOOL WINAPI CloseColorProfile( HPROFILE profile )
1288 {
1289     BOOL ret = FALSE;
1290 #ifdef HAVE_LCMS
1291     icProfile *iccprofile = MSCMS_hprofile2iccprofile( profile );
1292     HANDLE file = MSCMS_hprofile2handle( profile );
1293     DWORD access = MSCMS_hprofile2access( profile );
1294
1295     TRACE( "( %p )\n", profile );
1296
1297     if (file && (access & PROFILE_READWRITE))
1298     {
1299         DWORD written, size = MSCMS_get_profile_size( iccprofile );
1300
1301         if (SetFilePointer( file, 0, NULL, FILE_BEGIN ) ||
1302             !WriteFile( file, iccprofile, size, &written, NULL ) || written != size)
1303             ERR( "Unable to write color profile\n" );
1304     }
1305
1306     ret = cmsCloseProfile( MSCMS_hprofile2cmsprofile( profile ) );
1307     HeapFree( GetProcessHeap(), 0, MSCMS_hprofile2iccprofile( profile ) );
1308
1309     CloseHandle( MSCMS_hprofile2handle( profile ) );
1310     MSCMS_destroy_hprofile_handle( profile );
1311
1312 #endif /* HAVE_LCMS */
1313     return ret;
1314 }