setupapi: Add stub for CM_Get_Device_Interface_Alias{A,W}.
[wine.git] / dlls / msi / font.c
blobf26d3fd99c33aa5534b2145747c14ef479dca31d
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
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.
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.
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
21 #include <stdarg.h>
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winerror.h"
25 #include "winreg.h"
26 #include "wine/debug.h"
27 #include "msipriv.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(msi);
31 typedef struct _tagTT_OFFSET_TABLE {
32 USHORT uMajorVersion;
33 USHORT uMinorVersion;
34 USHORT uNumOfTables;
35 USHORT uSearchRange;
36 USHORT uEntrySelector;
37 USHORT uRangeShift;
38 } TT_OFFSET_TABLE;
40 typedef struct _tagTT_TABLE_DIRECTORY {
41 char szTag[4]; /* table name */
42 ULONG uCheckSum; /* Check sum */
43 ULONG uOffset; /* Offset from beginning of file */
44 ULONG uLength; /* length of the table in bytes */
45 } TT_TABLE_DIRECTORY;
47 typedef struct _tagTT_NAME_TABLE_HEADER {
48 USHORT uFSelector; /* format selector. Always 0 */
49 USHORT uNRCount; /* Name Records count */
50 USHORT uStorageOffset; /* Offset for strings storage,
51 * from start of the table */
52 } TT_NAME_TABLE_HEADER;
54 #define NAME_ID_FULL_FONT_NAME 4
55 #define NAME_ID_VERSION 5
57 typedef struct _tagTT_NAME_RECORD {
58 USHORT uPlatformID;
59 USHORT uEncodingID;
60 USHORT uLanguageID;
61 USHORT uNameID;
62 USHORT uStringLength;
63 USHORT uStringOffset; /* from start of storage area */
64 } TT_NAME_RECORD;
66 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
67 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
69 static const WCHAR regfont1[] =
70 {'S','o','f','t','w','a','r','e','\\',
71 'M','i','c','r','o','s','o','f','t','\\',
72 'W','i','n','d','o','w','s',' ','N','T','\\',
73 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
74 'F','o','n','t','s',0};
75 static const WCHAR regfont2[] =
76 {'S','o','f','t','w','a','r','e','\\',
77 'M','i','c','r','o','s','o','f','t','\\',
78 'W','i','n','d','o','w','s','\\',
79 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
80 'F','o','n','t','s',0};
83 * Code based off of code located here
84 * http://www.codeproject.com/gdi/fontnamefromfile.asp
86 static WCHAR *load_ttf_name_id( MSIPACKAGE *package, const WCHAR *filename, DWORD id )
88 TT_TABLE_DIRECTORY tblDir;
89 BOOL bFound = FALSE;
90 TT_OFFSET_TABLE ttOffsetTable;
91 TT_NAME_TABLE_HEADER ttNTHeader;
92 TT_NAME_RECORD ttRecord;
93 DWORD dwRead;
94 HANDLE handle;
95 LPWSTR ret = NULL;
96 int i;
98 if (package)
99 handle = msi_create_file( package, filename, GENERIC_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL );
100 else
101 handle = CreateFileW( filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 );
102 if (handle == INVALID_HANDLE_VALUE)
104 ERR("Unable to open font file %s\n", debugstr_w(filename));
105 return NULL;
108 if (!ReadFile(handle,&ttOffsetTable, sizeof(TT_OFFSET_TABLE),&dwRead,NULL))
109 goto end;
111 ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables);
112 ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion);
113 ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion);
115 if ((ttOffsetTable.uMajorVersion != 1 || ttOffsetTable.uMinorVersion != 0) &&
116 (ttOffsetTable.uMajorVersion != 0x4f54 || ttOffsetTable.uMinorVersion != 0x544f))
117 goto end;
119 for (i=0; i< ttOffsetTable.uNumOfTables; i++)
121 if (!ReadFile(handle,&tblDir, sizeof(TT_TABLE_DIRECTORY),&dwRead,NULL))
122 break;
123 if (memcmp(tblDir.szTag,"name",4)==0)
125 bFound = TRUE;
126 tblDir.uLength = SWAPLONG(tblDir.uLength);
127 tblDir.uOffset = SWAPLONG(tblDir.uOffset);
128 break;
132 if (!bFound)
133 goto end;
135 SetFilePointer(handle, tblDir.uOffset, NULL, FILE_BEGIN);
136 if (!ReadFile(handle,&ttNTHeader, sizeof(TT_NAME_TABLE_HEADER), &dwRead,NULL))
137 goto end;
139 ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount);
140 ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset);
141 for(i=0; i<ttNTHeader.uNRCount; i++)
143 if (!ReadFile(handle,&ttRecord, sizeof(TT_NAME_RECORD),&dwRead,NULL))
144 break;
146 ttRecord.uNameID = SWAPWORD(ttRecord.uNameID);
147 ttRecord.uPlatformID = SWAPWORD(ttRecord.uPlatformID);
148 ttRecord.uEncodingID = SWAPWORD(ttRecord.uEncodingID);
149 if (ttRecord.uNameID == id && ttRecord.uPlatformID == 3 &&
150 (ttRecord.uEncodingID == 0 || ttRecord.uEncodingID == 1))
152 WCHAR *buf;
153 unsigned int i;
155 ttRecord.uStringLength = SWAPWORD(ttRecord.uStringLength);
156 ttRecord.uStringOffset = SWAPWORD(ttRecord.uStringOffset);
157 SetFilePointer(handle, tblDir.uOffset + ttRecord.uStringOffset + ttNTHeader.uStorageOffset,
158 NULL, FILE_BEGIN);
159 if (!(buf = msi_alloc_zero( ttRecord.uStringLength + sizeof(WCHAR) ))) goto end;
160 dwRead = 0;
161 ReadFile(handle, buf, ttRecord.uStringLength, &dwRead, NULL);
162 if (dwRead % sizeof(WCHAR))
164 msi_free(buf);
165 goto end;
167 for (i = 0; i < dwRead / sizeof(WCHAR); i++) buf[i] = SWAPWORD(buf[i]);
168 ret = strdupW(buf);
169 msi_free(buf);
170 break;
174 end:
175 CloseHandle(handle);
176 return ret;
179 static WCHAR *font_name_from_file( MSIPACKAGE *package, const WCHAR *filename )
181 static const WCHAR truetypeW[] = {' ','(','T','r','u','e','T','y','p','e',')',0};
182 WCHAR *name, *ret = NULL;
184 if ((name = load_ttf_name_id( package, filename, NAME_ID_FULL_FONT_NAME )))
186 if (!name[0])
188 WARN("empty font name\n");
189 msi_free( name );
190 return NULL;
192 ret = msi_alloc( (lstrlenW( name ) + lstrlenW( truetypeW ) + 1 ) * sizeof(WCHAR) );
193 lstrcpyW( ret, name );
194 lstrcatW( ret, truetypeW );
195 msi_free( name );
197 return ret;
200 WCHAR *msi_get_font_file_version( MSIPACKAGE *package, const WCHAR *filename )
202 static const WCHAR fmtW[] = {'%','u','.','%','u','.','0','.','0',0};
203 WCHAR *version, *p, *q, *ret = NULL;
205 if ((version = load_ttf_name_id( package, filename, NAME_ID_VERSION )))
207 int len, major = 0, minor = 0;
208 if ((p = wcschr( version, ';' ))) *p = 0;
209 p = version;
210 while (*p && !iswdigit( *p )) p++;
211 if ((q = wcschr( p, '.' )))
213 major = wcstol( p, NULL, 10 );
214 p = ++q;
215 while (*q && iswdigit( *q )) q++;
216 if (!*q || *q == ' ') minor = wcstol( p, NULL, 10 );
217 else major = 0;
219 len = lstrlenW( fmtW ) + 20;
220 ret = msi_alloc( len * sizeof(WCHAR) );
221 swprintf( ret, len, fmtW, major, minor );
222 msi_free( version );
224 return ret;
227 static UINT ITERATE_RegisterFonts(MSIRECORD *row, LPVOID param)
229 MSIPACKAGE *package = param;
230 LPWSTR name;
231 LPCWSTR filename;
232 MSIFILE *file;
233 MSICOMPONENT *comp;
234 HKEY hkey1, hkey2;
235 MSIRECORD *uirow;
236 LPWSTR uipath, p;
238 filename = MSI_RecordGetString( row, 1 );
239 file = msi_get_loaded_file( package, filename );
240 if (!file)
242 WARN("unable to find file %s\n", debugstr_w(filename));
243 return ERROR_SUCCESS;
245 comp = msi_get_loaded_component( package, file->Component->Component );
246 if (!comp)
248 WARN("unable to find component %s\n", debugstr_w(file->Component->Component));
249 return ERROR_SUCCESS;
251 comp->Action = msi_get_component_action( package, comp );
252 if (comp->Action != INSTALLSTATE_LOCAL)
254 TRACE("component not scheduled for installation %s\n", debugstr_w(comp->Component));
255 return ERROR_SUCCESS;
258 RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont1,&hkey1);
259 RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont2,&hkey2);
261 if (MSI_RecordIsNull(row,2))
262 name = font_name_from_file( package, file->TargetPath );
263 else
264 name = msi_dup_record_field(row,2);
266 if (name)
268 msi_reg_set_val_str( hkey1, name, file->TargetPath);
269 msi_reg_set_val_str( hkey2, name, file->TargetPath);
272 msi_free(name);
273 RegCloseKey(hkey1);
274 RegCloseKey(hkey2);
276 /* the UI chunk */
277 uirow = MSI_CreateRecord( 1 );
278 uipath = strdupW( file->TargetPath );
279 p = wcsrchr(uipath,'\\');
280 if (p) p++;
281 else p = uipath;
282 MSI_RecordSetStringW( uirow, 1, p );
283 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
284 msiobj_release( &uirow->hdr );
285 msi_free( uipath );
286 /* FIXME: call msi_ui_progress? */
288 return ERROR_SUCCESS;
291 UINT ACTION_RegisterFonts(MSIPACKAGE *package)
293 static const WCHAR query[] = {
294 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','o','n','t','`',0};
295 MSIQUERY *view;
296 UINT rc;
298 if (package->script == SCRIPT_NONE)
299 return msi_schedule_action(package, SCRIPT_INSTALL, szRegisterFonts);
301 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
302 if (rc != ERROR_SUCCESS)
303 return ERROR_SUCCESS;
305 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterFonts, package);
306 msiobj_release(&view->hdr);
307 return rc;
310 static UINT ITERATE_UnregisterFonts( MSIRECORD *row, LPVOID param )
312 MSIPACKAGE *package = param;
313 LPWSTR name;
314 LPCWSTR filename;
315 MSIFILE *file;
316 MSICOMPONENT *comp;
317 HKEY hkey1, hkey2;
318 MSIRECORD *uirow;
319 LPWSTR uipath, p;
321 filename = MSI_RecordGetString( row, 1 );
322 file = msi_get_loaded_file( package, filename );
323 if (!file)
325 WARN("unable to find file %s\n", debugstr_w(filename));
326 return ERROR_SUCCESS;
328 comp = msi_get_loaded_component( package, file->Component->Component );
329 if (!comp)
331 WARN("unable to find component %s\n", debugstr_w(file->Component->Component));
332 return ERROR_SUCCESS;
334 comp->Action = msi_get_component_action( package, comp );
335 if (comp->Action != INSTALLSTATE_ABSENT)
337 TRACE("component not scheduled for removal %s\n", debugstr_w(comp->Component));
338 return ERROR_SUCCESS;
341 RegCreateKeyW( HKEY_LOCAL_MACHINE, regfont1, &hkey1 );
342 RegCreateKeyW( HKEY_LOCAL_MACHINE, regfont2, &hkey2 );
344 if (MSI_RecordIsNull( row, 2 ))
345 name = font_name_from_file( package, file->TargetPath );
346 else
347 name = msi_dup_record_field( row, 2 );
349 if (name)
351 RegDeleteValueW( hkey1, name );
352 RegDeleteValueW( hkey2, name );
355 msi_free( name );
356 RegCloseKey( hkey1 );
357 RegCloseKey( hkey2 );
359 /* the UI chunk */
360 uirow = MSI_CreateRecord( 1 );
361 uipath = strdupW( file->TargetPath );
362 p = wcsrchr( uipath,'\\' );
363 if (p) p++;
364 else p = uipath;
365 MSI_RecordSetStringW( uirow, 1, p );
366 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
367 msiobj_release( &uirow->hdr );
368 msi_free( uipath );
369 /* FIXME: call msi_ui_progress? */
371 return ERROR_SUCCESS;
374 UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
376 static const WCHAR query[] = {
377 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','o','n','t','`',0};
378 MSIQUERY *view;
379 UINT r;
381 if (package->script == SCRIPT_NONE)
382 return msi_schedule_action(package, SCRIPT_INSTALL, szUnregisterFonts);
384 r = MSI_DatabaseOpenViewW( package->db, query, &view );
385 if (r != ERROR_SUCCESS)
386 return ERROR_SUCCESS;
388 r = MSI_IterateRecords( view, NULL, ITERATE_UnregisterFonts, package );
389 msiobj_release( &view->hdr );
390 return r;