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"
32 WINE_DEFAULT_DEBUG_CHANNEL(winemsibuilder
);
34 static UINT
open_database( const WCHAR
*msifile
, MSIHANDLE
*handle
)
39 if (GetFileAttributesW( msifile
) == INVALID_FILE_ATTRIBUTES
)
41 r
= MsiOpenDatabaseW( msifile
, MSIDBOPEN_CREATE
, &hdb
);
42 if (r
!= ERROR_SUCCESS
)
44 WINE_ERR( "failed to create package database %s (%u)\n", wine_dbgstr_w(msifile
), r
);
47 r
= MsiDatabaseCommit( hdb
);
48 if (r
!= ERROR_SUCCESS
)
50 WINE_ERR( "failed to commit database (%u)\n", r
);
51 MsiCloseHandle( hdb
);
57 r
= MsiOpenDatabaseW( msifile
, MSIDBOPEN_TRANSACT
, &hdb
);
58 if (r
!= ERROR_SUCCESS
)
60 WINE_ERR( "failed to open package database %s (%u)\n", wine_dbgstr_w(msifile
), r
);
69 static int import_tables( const WCHAR
*msifile
, WCHAR
**tables
)
76 r
= open_database( msifile
, &hdb
);
77 if (r
!= ERROR_SUCCESS
) return 1;
79 len
= GetCurrentDirectoryW( 0, NULL
);
80 if (!(dir
= malloc( (len
+ 1) * sizeof(WCHAR
) )))
82 MsiCloseHandle( hdb
);
85 GetCurrentDirectoryW( len
+ 1, dir
);
89 r
= MsiDatabaseImportW( hdb
, dir
, *tables
);
90 if (r
!= ERROR_SUCCESS
)
92 WINE_ERR( "failed to import table %s (%u)\n", wine_dbgstr_w(*tables
), r
);
98 if (r
== ERROR_SUCCESS
)
100 r
= MsiDatabaseCommit( hdb
);
101 if (r
!= ERROR_SUCCESS
)
102 WINE_ERR( "failed to commit changes (%u)\n", r
);
106 MsiCloseHandle( hdb
);
107 return (r
!= ERROR_SUCCESS
);
110 /* taken from dlls/msi/table.c */
111 static int utf2mime( int x
)
113 if (x
>= '0' && x
<= '9')
115 if (x
>= 'A' && x
<= 'Z')
117 if (x
>= 'a' && x
<= 'z')
118 return x
- 'a' + 10 + 26;
122 return 10 + 26 + 26 + 1;
126 #define MAX_STREAM_NAME 0x1f
128 static WCHAR
*encode_stream( const WCHAR
*in
)
130 DWORD c
, next
, count
;
133 count
= lstrlenW( in
);
134 if (count
> MAX_STREAM_NAME
)
138 if (!(out
= malloc( count
* sizeof(WCHAR
) ))) return NULL
;
148 if (c
< 0x80 && utf2mime( c
) >= 0)
150 c
= utf2mime( c
) + 0x4800;
152 if (next
&& next
< 0x80)
154 next
= utf2mime( next
);
169 static int add_stream( const WCHAR
*msifile
, const WCHAR
*stream
, const WCHAR
*file
)
179 DWORD low
, high
, read
;
183 /* make sure we have the right type of file */
184 r
= open_database( msifile
, &hdb
);
185 if (r
!= ERROR_SUCCESS
) return 1;
186 MsiCloseHandle( hdb
);
188 hr
= StgOpenStorage( msifile
, NULL
, STGM_TRANSACTED
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
191 WINE_WARN( "failed to open storage %s (0x%08lx)\n", wine_dbgstr_w(msifile
), hr
);
194 encname
= encode_stream( stream
);
197 WINE_WARN( "failed to encode stream name %s\n", wine_dbgstr_w(stream
) );
200 hr
= IStorage_CreateStream( stg
, encname
, STGM_CREATE
|STGM_WRITE
|STGM_SHARE_EXCLUSIVE
, 0, 0, &stm
);
203 WINE_WARN( "failed to create stream %s (0x%08lx)\n", wine_dbgstr_w(encname
), hr
);
206 handle
= CreateFileW( file
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
207 if (handle
== INVALID_HANDLE_VALUE
)
209 WINE_WARN( "failed to open file %s (%lu)\n", wine_dbgstr_w(file
), GetLastError() );
212 low
= GetFileSize( handle
, &high
);
213 if (low
== INVALID_FILE_SIZE
|| high
)
215 WINE_WARN( "file %s too big\n", wine_dbgstr_w(file
) );
216 CloseHandle( handle
);
220 hr
= IStream_SetSize( stm
, size
);
221 if (hr
!= S_OK
) goto done
;
223 while (ReadFile( handle
, buffer
, sizeof(buffer
), &read
, NULL
) && read
)
225 hr
= IStream_Write( stm
, buffer
, read
, NULL
);
226 if (hr
!= S_OK
) break;
227 size
.QuadPart
-= read
;
229 CloseHandle( handle
);
232 WINE_WARN( "failed to write stream contents\n" );
235 IStorage_Commit( stg
, 0 );
240 if (stm
) IStream_Release( stm
);
241 IStorage_Release( stg
);
245 static void show_usage( void )
248 "Usage: winemsibuilder [OPTION] [MSIFILE] ...\n"
250 " -i package.msi table1.idt [table2.idt ...] Import one or more tables into the database.\n"
251 " -a package.msi stream file Add 'stream' to storage with contents of 'file'.\n"
252 "\nExisting tables or streams will be overwritten. If package.msi does not exist a new file\n"
253 "will be created with an empty database.\n"
257 int __cdecl
wmain( int argc
, WCHAR
*argv
[] )
259 if (argc
< 3 || argv
[1][0] != '-')
269 return import_tables( argv
[2], argv
+ 3 );
272 return add_stream( argv
[2], argv
[3], argv
[4] );
274 WINE_WARN( "unknown option\n" );