From cde86309c43cf9ebd9ff1e6c12869cdf34ede607 Mon Sep 17 00:00:00 2001
From: "Erich E. Hoover" <erich.e.hoover@wine-staging.com>
Date: Fri, 18 Sep 2015 11:07:43 -0600
Subject: msi: Add support for exporting binary streams (Binary/Icon tables).

Signed-off-by: Erich E. Hoover <erich.e.hoover@wine-staging.com>
---
 dlls/msi/database.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 79 insertions(+), 6 deletions(-)

diff --git a/dlls/msi/database.c b/dlls/msi/database.c
index 7c511c9..6627fd0 100644
--- a/dlls/msi/database.c
+++ b/dlls/msi/database.c
@@ -53,6 +53,13 @@ WINE_DEFAULT_DEBUG_CHANNEL(msi);
 
 #define IS_INTMSIDBOPEN(x)      (((ULONG_PTR)(x) >> 16) == 0)
 
+struct row_export_info
+{
+    HANDLE handle;
+    LPCWSTR folder;
+    LPCWSTR table;
+};
+
 static void free_transforms( MSIDATABASE *db )
 {
     while( !list_empty( &db->transforms ) )
@@ -964,8 +971,61 @@ static UINT msi_export_field( HANDLE handle, MSIRECORD *row, UINT field )
     return r;
 }
 
-static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
+static UINT msi_export_stream( LPCWSTR folder, LPCWSTR table, MSIRECORD *row, UINT field,
+                               UINT start )
 {
+    static const WCHAR fmt_file[] = { '%','s','/','%','s','/','%','s',0 };
+    static const WCHAR fmt_folder[] = { '%','s','/','%','s',0 };
+    WCHAR stream_name[256], stream_filename[MAX_PATH];
+    DWORD sz, read_size, write_size;
+    char buffer[1024];
+    HANDLE file;
+    UINT r;
+
+    /* get the name of the file */
+    sz = sizeof(stream_name)/sizeof(WCHAR);
+    r = MSI_RecordGetStringW( row, start, stream_name, &sz );
+    if (r != ERROR_SUCCESS)
+        return r;
+
+    /* if the destination folder does not exist then create it (folder name = table name) */
+    snprintfW( stream_filename, sizeof(stream_filename)/sizeof(WCHAR), fmt_folder, folder, table );
+    if (GetFileAttributesW( stream_filename ) == INVALID_FILE_ATTRIBUTES)
+    {
+        if (!CreateDirectoryW( stream_filename, NULL ))
+            return ERROR_PATH_NOT_FOUND;
+    }
+
+    /* actually create the file */
+    snprintfW( stream_filename, sizeof(stream_filename)/sizeof(WCHAR), fmt_file, folder, table, stream_name );
+    file = CreateFileW( stream_filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+                        NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
+    if (file == INVALID_HANDLE_VALUE)
+        return ERROR_FILE_NOT_FOUND;
+
+    /* copy the stream to the file */
+    read_size = sizeof(buffer);
+    while (read_size == sizeof(buffer))
+    {
+        r = MSI_RecordReadStream( row, field, buffer, &read_size );
+        if (r != ERROR_SUCCESS)
+        {
+            CloseHandle( file );
+            return r;
+        }
+        if (!WriteFile( file, buffer, read_size, &write_size, NULL ) || read_size != write_size)
+        {
+            CloseHandle( file );
+            return ERROR_WRITE_FAULT;
+        }
+    }
+    CloseHandle( file );
+    return r;
+}
+
+static UINT msi_export_record( struct row_export_info *row_export_info, MSIRECORD *row, UINT start )
+{
+    HANDLE handle = row_export_info->handle;
     UINT i, count, r = ERROR_SUCCESS;
     const char *sep;
     DWORD sz;
@@ -974,7 +1034,18 @@ static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
     for (i = start; i <= count; i++)
     {
         r = msi_export_field( handle, row, i );
-        if (r != ERROR_SUCCESS)
+        if (r == ERROR_INVALID_PARAMETER)
+        {
+            r = msi_export_stream( row_export_info->folder, row_export_info->table, row, i, start );
+            if (r != ERROR_SUCCESS)
+                return r;
+
+            /* exporting a binary stream, repeat the "Name" field */
+            r = msi_export_field( handle, row, start );
+            if (r != ERROR_SUCCESS)
+                return r;
+        }
+        else if (r != ERROR_SUCCESS)
             return r;
 
         sep = (i < count) ? "\t" : "\r\n";
@@ -1070,11 +1141,13 @@ static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
     r = MSI_OpenQuery( db, &view, query, table );
     if (r == ERROR_SUCCESS)
     {
+        struct row_export_info row_export_info = { handle, folder, table };
+
         /* write out row 1, the column names */
         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
         if (r == ERROR_SUCCESS)
         {
-            msi_export_record( handle, rec, 1 );
+            msi_export_record( &row_export_info, rec, 1 );
             msiobj_release( &rec->hdr );
         }
 
@@ -1082,7 +1155,7 @@ static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
         r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
         if (r == ERROR_SUCCESS)
         {
-            msi_export_record( handle, rec, 1 );
+            msi_export_record( &row_export_info, rec, 1 );
             msiobj_release( &rec->hdr );
         }
 
@@ -1091,12 +1164,12 @@ static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
         if (r == ERROR_SUCCESS)
         {
             MSI_RecordSetStringW( rec, 0, table );
-            msi_export_record( handle, rec, 0 );
+            msi_export_record( &row_export_info, rec, 0 );
             msiobj_release( &rec->hdr );
         }
 
         /* write out row 4 onwards, the data */
-        r = MSI_IterateRecords( view, 0, msi_export_row, handle );
+        r = MSI_IterateRecords( view, 0, msi_export_row, &row_export_info );
         msiobj_release( &view->hdr );
     }
 
-- 
2.6.2

