2 * winemsibuilder - tool to build MSI packages
4 * Copyright 2010 Hans Leidekker 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 #define WIN32_LEAN_AND_MEAN
30 #include "wine/debug.h"
31 #include "wine/unicode.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(winemsibuilder
);
35 static UINT
open_database( const WCHAR
*msifile
, MSIHANDLE
*handle
)
40 if (GetFileAttributesW( msifile
) == INVALID_FILE_ATTRIBUTES
)
42 r
= MsiOpenDatabaseW( msifile
, MSIDBOPEN_CREATE
, &hdb
);
43 if (r
!= ERROR_SUCCESS
)
45 WINE_ERR( "failed to create package database %s (%u)\n", wine_dbgstr_w(msifile
), r
);
48 r
= MsiDatabaseCommit( hdb
);
49 if (r
!= ERROR_SUCCESS
)
51 WINE_ERR( "failed to commit database (%u)\n", r
);
52 MsiCloseHandle( hdb
);
58 r
= MsiOpenDatabaseW( msifile
, MSIDBOPEN_TRANSACT
, &hdb
);
59 if (r
!= ERROR_SUCCESS
)
61 WINE_ERR( "failed to open package database %s (%u)\n", wine_dbgstr_w(msifile
), r
);
70 static int import_tables( const WCHAR
*msifile
, WCHAR
**tables
)
77 r
= open_database( msifile
, &hdb
);
78 if (r
!= ERROR_SUCCESS
) return 1;
80 len
= GetCurrentDirectoryW( 0, NULL
);
81 if (!(dir
= HeapAlloc( GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
) )))
83 MsiCloseHandle( hdb
);
86 GetCurrentDirectoryW( len
+ 1, dir
);
90 r
= MsiDatabaseImportW( hdb
, dir
, *tables
);
91 if (r
!= ERROR_SUCCESS
)
93 WINE_ERR( "failed to import table %s (%u)\n", wine_dbgstr_w(*tables
), r
);
99 if (r
== ERROR_SUCCESS
)
101 r
= MsiDatabaseCommit( hdb
);
102 if (r
!= ERROR_SUCCESS
)
103 WINE_ERR( "failed to commit changes (%u)\n", r
);
106 HeapFree( GetProcessHeap(), 0, dir
);
107 MsiCloseHandle( hdb
);
108 return (r
!= ERROR_SUCCESS
);
111 /* taken from dlls/msi/table.c */
112 static int utf2mime( int x
)
114 if (x
>= '0' && x
<= '9')
116 if (x
>= 'A' && x
<= 'Z')
118 if (x
>= 'a' && x
<= 'z')
119 return x
- 'a' + 10 + 26;
123 return 10 + 26 + 26 + 1;
127 #define MAX_STREAM_NAME 0x1f
129 static WCHAR
*encode_stream( const WCHAR
*in
)
131 DWORD c
, next
, count
;
134 count
= strlenW( in
);
135 if (count
> MAX_STREAM_NAME
)
139 if (!(out
= HeapAlloc( GetProcessHeap(), 0, count
* sizeof(WCHAR
) ))) return NULL
;
149 if (c
< 0x80 && utf2mime( c
) >= 0)
151 c
= utf2mime( c
) + 0x4800;
153 if (next
&& next
< 0x80)
155 next
= utf2mime( next
);
166 HeapFree( GetProcessHeap(), 0, out
);
170 static int add_stream( const WCHAR
*msifile
, const WCHAR
*stream
, const WCHAR
*file
)
180 DWORD low
, high
, read
;
184 /* make sure we have the right type of file */
185 r
= open_database( msifile
, &hdb
);
186 if (r
!= ERROR_SUCCESS
) return 1;
187 MsiCloseHandle( hdb
);
189 hr
= StgOpenStorage( msifile
, NULL
, STGM_TRANSACTED
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
192 WINE_WARN( "failed to open storage %s (0x%08x)\n", wine_dbgstr_w(msifile
), hr
);
195 encname
= encode_stream( stream
);
198 WINE_WARN( "failed to encode stream name %s\n", wine_dbgstr_w(stream
) );
201 hr
= IStorage_CreateStream( stg
, encname
, STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
, 0, 0, &stm
);
204 WINE_WARN( "failed to create stream %s (0x%08x)\n", wine_dbgstr_w(encname
), hr
);
207 handle
= CreateFileW( file
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
208 if (handle
== INVALID_HANDLE_VALUE
)
210 WINE_WARN( "failed to open file %s (%u)\n", wine_dbgstr_w(file
), GetLastError() );
213 low
= GetFileSize( handle
, &high
);
214 if (low
== INVALID_FILE_SIZE
|| high
)
216 WINE_WARN( "file %s too big\n", wine_dbgstr_w(file
) );
217 CloseHandle( handle
);
221 hr
= IStream_SetSize( stm
, size
);
222 if (hr
!= S_OK
) goto done
;
224 while (ReadFile( handle
, buffer
, sizeof(buffer
), &read
, NULL
) && read
)
226 hr
= IStream_Write( stm
, buffer
, read
, NULL
);
227 if (hr
!= S_OK
) break;
228 size
.QuadPart
-= read
;
230 CloseHandle( handle
);
233 WINE_WARN( "failed to write stream contents\n" );
236 IStorage_Commit( stg
, 0 );
240 HeapFree( GetProcessHeap(), 0, encname
);
241 if (stm
) IStream_Release( stm
);
242 IStorage_Release( stg
);
246 static void show_usage( void )
249 "Usage: winemsibuilder [OPTION] [MSIFILE] ...\n"
251 " -i package.msi table1.idt [table2.idt ...] Import one or more tables into the database.\n"
252 " -a package.msi stream file Add 'stream' to storage with contents of 'file'.\n"
253 "\nExisting tables or streams will be overwritten. If package.msi does not exist a new file\n"
254 "will be created with an empty database.\n"
258 int wmain( int argc
, WCHAR
*argv
[] )
260 if (argc
< 3 || argv
[1][0] != '-')
270 return import_tables( argv
[2], argv
+ 3 );
273 return add_stream( argv
[2], argv
[3], argv
[4] );
275 WINE_WARN( "unknown option\n" );