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
23 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
25 typedef struct _tagTT_OFFSET_TABLE
{
30 USHORT uEntrySelector
;
34 typedef struct _tagTT_TABLE_DIRECTORY
{
35 char szTag
[4]; /* table name */
36 ULONG uCheckSum
; /* Check sum */
37 ULONG uOffset
; /* Offset from beginning of file */
38 ULONG uLength
; /* length of the table in bytes */
41 typedef struct _tagTT_NAME_TABLE_HEADER
{
42 USHORT uFSelector
; /* format selector. Always 0 */
43 USHORT uNRCount
; /* Name Records count */
44 USHORT uStorageOffset
; /* Offset for strings storage,
45 * from start of the table */
46 } TT_NAME_TABLE_HEADER
;
48 #define NAME_ID_FULL_FONT_NAME 4
49 #define NAME_ID_VERSION 5
51 typedef struct _tagTT_NAME_RECORD
{
57 USHORT uStringOffset
; /* from start of storage area */
60 #define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
61 #define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x)))
63 static const WCHAR regfont1
[] =
64 {'S','o','f','t','w','a','r','e','\\',
65 'M','i','c','r','o','s','o','f','t','\\',
66 'W','i','n','d','o','w','s',' ','N','T','\\',
67 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
68 'F','o','n','t','s',0};
69 static const WCHAR regfont2
[] =
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','\\',
73 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
74 'F','o','n','t','s',0};
77 * Code based off of code located here
78 * http://www.codeproject.com/gdi/fontnamefromfile.asp
80 static WCHAR
*load_ttf_name_id( const WCHAR
*filename
, DWORD id
)
82 TT_TABLE_DIRECTORY tblDir
;
84 TT_OFFSET_TABLE ttOffsetTable
;
85 TT_NAME_TABLE_HEADER ttNTHeader
;
86 TT_NAME_RECORD ttRecord
;
92 handle
= CreateFileW(filename
,GENERIC_READ
, 0, NULL
, OPEN_EXISTING
,
93 FILE_ATTRIBUTE_NORMAL
, 0 );
94 if (handle
== INVALID_HANDLE_VALUE
)
96 ERR("Unable to open font file %s\n", debugstr_w(filename
));
100 if (!ReadFile(handle
,&ttOffsetTable
, sizeof(TT_OFFSET_TABLE
),&dwRead
,NULL
))
103 ttOffsetTable
.uNumOfTables
= SWAPWORD(ttOffsetTable
.uNumOfTables
);
104 ttOffsetTable
.uMajorVersion
= SWAPWORD(ttOffsetTable
.uMajorVersion
);
105 ttOffsetTable
.uMinorVersion
= SWAPWORD(ttOffsetTable
.uMinorVersion
);
107 if ((ttOffsetTable
.uMajorVersion
!= 1 || ttOffsetTable
.uMinorVersion
!= 0) &&
108 (ttOffsetTable
.uMajorVersion
!= 0x4f54 || ttOffsetTable
.uMinorVersion
!= 0x544f))
111 for (i
=0; i
< ttOffsetTable
.uNumOfTables
; i
++)
113 if (!ReadFile(handle
,&tblDir
, sizeof(TT_TABLE_DIRECTORY
),&dwRead
,NULL
))
115 if (memcmp(tblDir
.szTag
,"name",4)==0)
118 tblDir
.uLength
= SWAPLONG(tblDir
.uLength
);
119 tblDir
.uOffset
= SWAPLONG(tblDir
.uOffset
);
127 SetFilePointer(handle
, tblDir
.uOffset
, NULL
, FILE_BEGIN
);
128 if (!ReadFile(handle
,&ttNTHeader
, sizeof(TT_NAME_TABLE_HEADER
), &dwRead
,NULL
))
131 ttNTHeader
.uNRCount
= SWAPWORD(ttNTHeader
.uNRCount
);
132 ttNTHeader
.uStorageOffset
= SWAPWORD(ttNTHeader
.uStorageOffset
);
133 for(i
=0; i
<ttNTHeader
.uNRCount
; i
++)
135 if (!ReadFile(handle
,&ttRecord
, sizeof(TT_NAME_RECORD
),&dwRead
,NULL
))
138 ttRecord
.uNameID
= SWAPWORD(ttRecord
.uNameID
);
139 ttRecord
.uPlatformID
= SWAPWORD(ttRecord
.uPlatformID
);
140 ttRecord
.uEncodingID
= SWAPWORD(ttRecord
.uEncodingID
);
141 if (ttRecord
.uNameID
== id
&& ttRecord
.uPlatformID
== 3 &&
142 (ttRecord
.uEncodingID
== 0 || ttRecord
.uEncodingID
== 1))
147 ttRecord
.uStringLength
= SWAPWORD(ttRecord
.uStringLength
);
148 ttRecord
.uStringOffset
= SWAPWORD(ttRecord
.uStringOffset
);
149 SetFilePointer(handle
, tblDir
.uOffset
+ ttRecord
.uStringOffset
+ ttNTHeader
.uStorageOffset
,
151 if (!(buf
= msi_alloc_zero( ttRecord
.uStringLength
+ sizeof(WCHAR
) ))) goto end
;
153 ReadFile(handle
, buf
, ttRecord
.uStringLength
, &dwRead
, NULL
);
154 if (dwRead
% sizeof(WCHAR
))
159 for (i
= 0; i
< dwRead
/ sizeof(WCHAR
); i
++) buf
[i
] = SWAPWORD(buf
[i
]);
168 TRACE("Returning %s\n", debugstr_w(ret
));
172 static WCHAR
*font_name_from_file( const WCHAR
*filename
)
174 static const WCHAR truetypeW
[] = {' ','(','T','r','u','e','T','y','p','e',')',0};
175 WCHAR
*name
, *ret
= NULL
;
177 if ((name
= load_ttf_name_id( filename
, NAME_ID_FULL_FONT_NAME
)))
181 WARN("empty font name\n");
185 ret
= msi_alloc( (strlenW( name
) + strlenW( truetypeW
) + 1 ) * sizeof(WCHAR
) );
186 strcpyW( ret
, name
);
187 strcatW( ret
, truetypeW
);
193 WCHAR
*msi_font_version_from_file( const WCHAR
*filename
)
195 static const WCHAR fmtW
[] = {'%','u','.','%','u','.','0','.','0',0};
196 WCHAR
*version
, *p
, *q
, *ret
= NULL
;
198 if ((version
= load_ttf_name_id( filename
, NAME_ID_VERSION
)))
200 int len
, major
= 0, minor
= 0;
201 if ((p
= strchrW( version
, ';' ))) *p
= 0;
203 while (*p
&& !isdigitW( *p
)) p
++;
204 if ((q
= strchrW( p
, '.' )))
208 while (*q
&& isdigitW( *q
)) q
++;
209 if (!*q
|| *q
== ' ') minor
= atoiW( p
);
212 len
= strlenW( fmtW
) + 20;
213 ret
= msi_alloc( len
* sizeof(WCHAR
) );
214 sprintfW( ret
, fmtW
, major
, minor
);
220 static UINT
ITERATE_RegisterFonts(MSIRECORD
*row
, LPVOID param
)
222 MSIPACKAGE
*package
= param
;
231 filename
= MSI_RecordGetString( row
, 1 );
232 file
= msi_get_loaded_file( package
, filename
);
235 WARN("unable to find file %s\n", debugstr_w(filename
));
236 return ERROR_SUCCESS
;
238 comp
= msi_get_loaded_component( package
, file
->Component
->Component
);
241 WARN("unable to find component %s\n", debugstr_w(file
->Component
->Component
));
242 return ERROR_SUCCESS
;
244 comp
->Action
= msi_get_component_action( package
, comp
);
245 if (comp
->Action
!= INSTALLSTATE_LOCAL
)
247 TRACE("component not scheduled for installation %s\n", debugstr_w(comp
->Component
));
248 return ERROR_SUCCESS
;
251 RegCreateKeyW(HKEY_LOCAL_MACHINE
,regfont1
,&hkey1
);
252 RegCreateKeyW(HKEY_LOCAL_MACHINE
,regfont2
,&hkey2
);
254 if (MSI_RecordIsNull(row
,2))
255 name
= font_name_from_file( file
->TargetPath
);
257 name
= msi_dup_record_field(row
,2);
261 msi_reg_set_val_str( hkey1
, name
, file
->TargetPath
);
262 msi_reg_set_val_str( hkey2
, name
, file
->TargetPath
);
270 uirow
= MSI_CreateRecord( 1 );
271 uipath
= strdupW( file
->TargetPath
);
272 p
= strrchrW(uipath
,'\\');
275 MSI_RecordSetStringW( uirow
, 1, p
);
276 msi_ui_actiondata( package
, szRegisterFonts
, uirow
);
277 msiobj_release( &uirow
->hdr
);
279 /* FIXME: call msi_ui_progress? */
281 return ERROR_SUCCESS
;
284 UINT
ACTION_RegisterFonts(MSIPACKAGE
*package
)
286 static const WCHAR query
[] = {
287 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','o','n','t','`',0};
291 rc
= MSI_DatabaseOpenViewW(package
->db
, query
, &view
);
292 if (rc
!= ERROR_SUCCESS
)
293 return ERROR_SUCCESS
;
295 rc
= MSI_IterateRecords(view
, NULL
, ITERATE_RegisterFonts
, package
);
296 msiobj_release(&view
->hdr
);
300 static UINT
ITERATE_UnregisterFonts( MSIRECORD
*row
, LPVOID param
)
302 MSIPACKAGE
*package
= param
;
311 filename
= MSI_RecordGetString( row
, 1 );
312 file
= msi_get_loaded_file( package
, filename
);
315 WARN("unable to find file %s\n", debugstr_w(filename
));
316 return ERROR_SUCCESS
;
318 comp
= msi_get_loaded_component( package
, file
->Component
->Component
);
321 WARN("unable to find component %s\n", debugstr_w(file
->Component
->Component
));
322 return ERROR_SUCCESS
;
324 comp
->Action
= msi_get_component_action( package
, comp
);
325 if (comp
->Action
!= INSTALLSTATE_ABSENT
)
327 TRACE("component not scheduled for removal %s\n", debugstr_w(comp
->Component
));
328 return ERROR_SUCCESS
;
331 RegCreateKeyW( HKEY_LOCAL_MACHINE
, regfont1
, &hkey1
);
332 RegCreateKeyW( HKEY_LOCAL_MACHINE
, regfont2
, &hkey2
);
334 if (MSI_RecordIsNull( row
, 2 ))
335 name
= font_name_from_file( file
->TargetPath
);
337 name
= msi_dup_record_field( row
, 2 );
341 RegDeleteValueW( hkey1
, name
);
342 RegDeleteValueW( hkey2
, name
);
346 RegCloseKey( hkey1
);
347 RegCloseKey( hkey2
);
350 uirow
= MSI_CreateRecord( 1 );
351 uipath
= strdupW( file
->TargetPath
);
352 p
= strrchrW( uipath
,'\\' );
355 MSI_RecordSetStringW( uirow
, 1, p
);
356 msi_ui_actiondata( package
, szUnregisterFonts
, uirow
);
357 msiobj_release( &uirow
->hdr
);
359 /* FIXME: call msi_ui_progress? */
361 return ERROR_SUCCESS
;
364 UINT
ACTION_UnregisterFonts( MSIPACKAGE
*package
)
366 static const WCHAR query
[] = {
367 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','o','n','t','`',0};
371 r
= MSI_DatabaseOpenViewW( package
->db
, query
, &view
);
372 if (r
!= ERROR_SUCCESS
)
373 return ERROR_SUCCESS
;
375 r
= MSI_IterateRecords( view
, NULL
, ITERATE_UnregisterFonts
, package
);
376 msiobj_release( &view
->hdr
);