Start implementing MsiOpenPackage.
[wine] / dlls / msi / table.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002-2004 Mike McCormack for CodeWeavers
5  *
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.
10  *
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.
15  *
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #define NONAMELESSUNION
22 #define NONAMELESSSTRUCT
23
24 #include <stdarg.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winerror.h"
29 #include "wine/debug.h"
30 #include "msi.h"
31 #include "msiquery.h"
32 #include "objbase.h"
33 #include "objidl.h"
34 #include "msipriv.h"
35 #include "winnls.h"
36
37 #include "query.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(msi);
40
41 typedef struct tagMSICOLUMNINFO
42 {
43     LPWSTR tablename;
44     UINT   number;
45     LPWSTR colname;
46     UINT   type;
47     UINT   offset;
48 } MSICOLUMNINFO;
49
50 struct tagMSITABLE
51 {
52     USHORT **data;
53     UINT ref_count;
54     UINT row_count;
55     struct tagMSITABLE *next;
56     struct tagMSITABLE *prev;
57     WCHAR name[1];
58 };
59
60 #define MAX_STREAM_NAME 0x1f
61
62 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name,
63        MSICOLUMNINFO **pcols, UINT *pcount );
64 static UINT get_tablecolumns( MSIDATABASE *db, 
65        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz);
66
67 static inline UINT bytes_per_column( MSICOLUMNINFO *col )
68 {
69     if( col->type & MSITYPE_STRING )
70         return 2;
71     if( (col->type & 0xff) > 4 )
72         ERR("Invalid column size!\n");
73     return col->type & 0xff;
74 }
75
76 static int utf2mime(int x)
77 {
78     if( (x>='0') && (x<='9') )
79         return x-'0';
80     if( (x>='A') && (x<='Z') )
81         return x-'A'+10;
82     if( (x>='a') && (x<='z') )
83         return x-'a'+10+26;
84     if( x=='.' )
85         return 10+26+26;
86     if( x=='_' )
87         return 10+26+26+1;
88     return -1;
89 }
90
91 static BOOL encode_streamname(BOOL bTable, LPCWSTR in, LPWSTR out)
92 {
93     DWORD count = MAX_STREAM_NAME;
94     DWORD ch, next;
95
96     if( bTable )
97     {
98          *out++ = 0x4840;
99          count --;
100     }
101     while( count -- ) 
102     {
103         ch = *in++;
104         if( !ch )
105         {
106             *out = ch;
107             return TRUE;
108         }
109         if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) )
110         {
111             ch = utf2mime(ch) + 0x4800;
112             next = *in;
113             if( next && (next<0x80) )
114             {
115                 next = utf2mime(next);
116                 if( next >= 0  )
117                 {
118                      next += 0x3ffffc0;
119                      ch += (next<<6);
120                      in++;
121                 }
122             }
123         }
124         *out++ = ch;
125     }
126     return FALSE;
127 }
128
129 static int mime2utf(int x)
130 {
131     if( x<10 )
132         return x + '0';
133     if( x<(10+26))
134         return x - 10 + 'A';
135     if( x<(10+26+26))
136         return x - 10 - 26 + 'a';
137     if( x == (10+26+26) )
138         return '.';
139     return '_';
140 }
141
142 static BOOL decode_streamname(LPWSTR in, LPWSTR out)
143 {
144     WCHAR ch;
145     DWORD count = 0;
146
147     while ( (ch = *in++) )
148     {
149         if( (ch >= 0x3800 ) && (ch < 0x4840 ) )
150         {
151             if( ch >= 0x4800 )
152                 ch = mime2utf(ch-0x4800);
153             else
154             {
155                 ch -= 0x3800;
156                 *out++ = mime2utf(ch&0x3f);
157                 count++;
158                 ch = mime2utf((ch>>6)&0x3f);
159             }
160         }
161         *out++ = ch;
162         count++;
163     }
164     *out = 0;
165     return count;
166 }
167
168 void enum_stream_names( IStorage *stg )
169 {
170     IEnumSTATSTG *stgenum = NULL;
171     HRESULT r;
172     STATSTG stat;
173     ULONG n, count;
174     WCHAR name[0x40];
175
176     r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum );
177     if( FAILED( r ) )
178         return;
179
180     n = 0;
181     while( 1 )
182     {
183         count = 0;
184         r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count );
185         if( FAILED( r ) || !count )
186             break;
187         decode_streamname( stat.pwcsName, name );
188         ERR("stream %2ld -> %s %s\n", n, 
189             debugstr_w(stat.pwcsName), debugstr_w(name) );
190         n++;
191     }
192
193     IEnumSTATSTG_Release( stgenum );
194 }
195
196 static UINT read_stream_data( IStorage *stg, LPCWSTR stname,
197                               USHORT **pdata, UINT *psz )
198 {
199     HRESULT r;
200     UINT ret = ERROR_FUNCTION_FAILED;
201     VOID *data;
202     ULONG sz, count;
203     IStream *stm = NULL;
204     STATSTG stat;
205     WCHAR encname[0x20];
206
207     encode_streamname(TRUE, stname, encname);
208
209     TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
210
211     r = IStorage_OpenStream(stg, encname, NULL, 
212             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
213     if( FAILED( r ) )
214     {
215         WARN("open stream failed r = %08lx - empty table?\n",r);
216         return ret;
217     }
218
219     r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
220     if( FAILED( r ) )
221     {
222         ERR("open stream failed r = %08lx!\n",r);
223         goto end;
224     }
225
226     if( stat.cbSize.QuadPart >> 32 )
227     {
228         ERR("Too big!\n");
229         goto end;
230     }
231         
232     sz = stat.cbSize.QuadPart;
233     data = HeapAlloc( GetProcessHeap(), 0, sz );
234     if( !data )
235     {
236         ERR("couldn't allocate memory r=%08lx!\n",r);
237         ret = ERROR_NOT_ENOUGH_MEMORY;
238         goto end;
239     }
240         
241     r = IStream_Read(stm, data, sz, &count );
242     if( FAILED( r ) || ( count != sz ) )
243     {
244         HeapFree( GetProcessHeap(), 0, data );
245         ERR("read stream failed r = %08lx!\n",r);
246         goto end;
247     }
248
249     *pdata = data;
250     *psz = sz;
251     ret = ERROR_SUCCESS;
252
253 end:
254     IStream_Release( stm );
255
256     return ret;
257 }
258
259 UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm )
260 {
261     WCHAR encname[0x20];
262     HRESULT r;
263
264     encode_streamname(FALSE, stname, encname);
265
266     TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
267
268     r = IStorage_OpenStream(db->storage, encname, NULL, 
269             STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm);
270     if( FAILED( r ) )
271     {
272         WARN("open stream failed r = %08lx - empty table?\n",r);
273         return ERROR_FUNCTION_FAILED;
274     }
275
276     return ERROR_SUCCESS;
277 }
278
279 /* FIXME: we should be passing around pointers to db structures internally */
280 UINT get_raw_stream( MSIHANDLE hdb, LPCWSTR stname, IStream **stm )
281 {
282     MSIDATABASE *db = msihandle2msiinfo(hdb, MSIHANDLETYPE_DATABASE );
283
284     if ( !db )
285         return ERROR_INVALID_HANDLE;
286
287     return db_get_raw_stream( db, stname, stm );
288 }
289
290 UINT read_raw_stream_data( MSIHANDLE hdb, LPCWSTR stname,
291                               USHORT **pdata, UINT *psz )
292 {
293     HRESULT r;
294     UINT ret = ERROR_FUNCTION_FAILED;
295     VOID *data;
296     ULONG sz, count;
297     IStream *stm = NULL;
298     STATSTG stat;
299
300     r = get_raw_stream( hdb, stname, &stm );
301     if( r != ERROR_SUCCESS)
302         goto end;
303     ret = ERROR_FUNCTION_FAILED;
304     r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
305     if( FAILED( r ) )
306     {
307         ERR("open stream failed r = %08lx!\n",r);
308         goto end;
309     }
310
311     if( stat.cbSize.QuadPart >> 32 )
312     {
313         ERR("Too big!\n");
314         goto end;
315     }
316         
317     sz = stat.cbSize.QuadPart;
318     data = HeapAlloc( GetProcessHeap(), 0, sz );
319     if( !data )
320     {
321         ERR("couldn't allocate memory r=%08lx!\n",r);
322         ret = ERROR_NOT_ENOUGH_MEMORY;
323         goto end;
324     }
325         
326     r = IStream_Read(stm, data, sz, &count );
327     if( FAILED( r ) || ( count != sz ) )
328     {
329         HeapFree( GetProcessHeap(), 0, data );
330         ERR("read stream failed r = %08lx!\n",r);
331         goto end;
332     }
333
334     *pdata = data;
335     *psz = sz;
336     ret = ERROR_SUCCESS;
337
338 end:
339     IStream_Release( stm );
340
341     return ret;
342 }
343
344 static UINT write_stream_data( IStorage *stg, LPCWSTR stname,
345                                LPVOID data, UINT sz )
346 {
347     HRESULT r;
348     UINT ret = ERROR_FUNCTION_FAILED;
349     ULONG count;
350     IStream *stm = NULL;
351     ULARGE_INTEGER size;
352     LARGE_INTEGER pos;
353
354     WCHAR encname[0x20];
355
356     encode_streamname(TRUE, stname, encname);
357     r = IStorage_OpenStream( stg, encname, NULL, 
358             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm);
359     if( FAILED(r) )
360     {
361         r = IStorage_CreateStream( stg, encname,
362                 STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
363     }
364     if( FAILED( r ) )
365     {
366         ERR("open stream failed r = %08lx\n",r);
367         return ret;
368     }
369
370     size.QuadPart = sz;
371     r = IStream_SetSize( stm, size );
372     if( FAILED( r ) )
373     {
374         ERR("Failed to SetSize\n");
375         goto end;
376     }
377
378     pos.QuadPart = 0;
379     r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
380     if( FAILED( r ) )
381     {
382         ERR("Failed to Seek\n");
383         goto end;
384     }
385
386     r = IStream_Write(stm, data, sz, &count );
387     if( FAILED( r ) || ( count != sz ) )
388     {
389         ERR("Failed to Write\n");
390         goto end;
391     }
392
393     ret = ERROR_SUCCESS;
394
395 end:
396     IStream_Release( stm );
397
398     return ret;
399 }
400
401 UINT read_table_from_storage( MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
402 {
403     MSITABLE *t;
404     USHORT *rawdata = NULL;
405     UINT rawsize = 0, r, i, j, row_size = 0, num_cols = 0;
406     MSICOLUMNINFO *cols, *last_col;
407
408     TRACE("%s\n",debugstr_w(name));
409
410     /* non-existing tables should be interpretted as empty tables */
411     t = HeapAlloc( GetProcessHeap(), 0, 
412                    sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
413     if( !t )
414         return ERROR_NOT_ENOUGH_MEMORY;
415
416     r = table_get_column_info( db, name, &cols, &num_cols );
417     if( r != ERROR_SUCCESS )
418     {
419         HeapFree( GetProcessHeap(), 0, t );
420         return r;
421     }
422     last_col = &cols[num_cols-1];
423     row_size = last_col->offset + bytes_per_column( last_col );
424
425     t->row_count = 0;
426     t->data = NULL;
427     lstrcpyW( t->name, name );
428     t->ref_count = 1;
429     *ptable = t;
430
431     /* if we can't read the table, just assume that it's empty */
432     read_stream_data( db->storage, name, &rawdata, &rawsize );
433     if( !rawdata )
434         return ERROR_SUCCESS;
435
436     TRACE("Read %d bytes\n", rawsize );
437
438     if( rawsize % row_size )
439     {
440         ERR("Table size is invalid %d/%d\n", rawsize, row_size );
441         return ERROR_FUNCTION_FAILED;
442     }
443
444     t->row_count = rawsize / row_size;
445     t->data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
446                          t->row_count * sizeof (USHORT*) );
447     if( !t->data )
448         return ERROR_NOT_ENOUGH_MEMORY;  /* FIXME: memory leak */
449
450     /* transpose all the data */
451     TRACE("Transposing data from %d columns\n", t->row_count );
452     for( i=0; i<t->row_count; i++ )
453     {
454         t->data[i] = HeapAlloc( GetProcessHeap(), 0, row_size );
455         if( !t->data[i] )
456             return ERROR_NOT_ENOUGH_MEMORY;  /* FIXME: memory leak */
457         for( j=0; j<num_cols; j++ )
458         {
459             UINT ofs = cols[j].offset/2;
460             UINT n = bytes_per_column( &cols[j] );
461
462             switch( n )
463             {
464             case 2:
465                 t->data[i][ofs] = rawdata[ofs*t->row_count + i ];
466                 break;
467             case 4:
468                 t->data[i][ofs] = rawdata[ofs*t->row_count + i ];
469                 t->data[i][ofs+1] = rawdata[ofs*t->row_count + i + 1];
470                 break;
471             default:
472                 ERR("oops - unknown column width %d\n", n);
473                 return ERROR_FUNCTION_FAILED;
474             }
475         }
476     }
477
478     HeapFree( GetProcessHeap(), 0, cols );
479     HeapFree( GetProcessHeap(), 0, rawdata );
480
481     return ERROR_SUCCESS;
482 }
483
484 /* add this table to the list of cached tables in the database */
485 void add_table(MSIDATABASE *db, MSITABLE *table)
486 {
487     table->next = db->first_table;
488     table->prev = NULL;
489     if( db->first_table )
490         db->first_table->prev = table;
491     else
492         db->last_table = table;
493     db->first_table = table;
494 }
495  
496 /* remove from the list of cached tables */
497 void remove_table( MSIDATABASE *db, MSITABLE *table )
498 {
499     if( table->next )
500         table->next->prev = table->prev;
501     else
502         db->last_table = table->prev;
503     if( table->prev )
504         table->prev->next = table->next;
505     else
506         db->first_table = table->next;
507     table->next = NULL;
508     table->prev = NULL;
509 }
510
511 void release_table( MSIDATABASE *db, MSITABLE *table )
512 {
513     if( !table->ref_count )
514         ERR("Trying to destroy table with refcount 0\n");
515     table->ref_count --;
516     if( !table->ref_count )
517     {
518         remove_table( db, table );
519         HeapFree( GetProcessHeap(), 0, table->data );
520         HeapFree( GetProcessHeap(), 0, table );
521         TRACE("Destroyed table %s\n", debugstr_w(table->name));
522     }
523 }
524
525 void free_cached_tables( MSIDATABASE *db )
526 {
527     while( db->first_table )
528     {
529         MSITABLE *t = db->first_table;
530
531         if ( --t->ref_count )
532             ERR("table ref count not zero for %s\n", debugstr_w(t->name));
533         remove_table( db, t );
534         HeapFree( GetProcessHeap(), 0, t->data );
535         HeapFree( GetProcessHeap(), 0, t );
536     }
537 }
538
539 UINT find_cached_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
540 {
541     MSITABLE *t;
542
543     for( t = db->first_table; t; t=t->next )
544     {
545         if( !lstrcmpW( name, t->name ) )
546         {
547             *ptable = t;
548             return ERROR_SUCCESS;
549         }
550     }
551
552     return ERROR_FUNCTION_FAILED;
553 }
554
555 static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount )
556 {
557     UINT r, column_count;
558     MSICOLUMNINFO *columns;
559
560     /* get the number of columns in this table */
561     column_count = 0;
562     r = get_tablecolumns( db, name, NULL, &column_count );
563     if( r != ERROR_SUCCESS )
564         return r;
565
566     /* if there's no columns, there's no table */
567     if( column_count == 0 )
568         return ERROR_INVALID_PARAMETER;
569
570     TRACE("Table %s found\n", debugstr_w(name) );
571
572     columns = HeapAlloc( GetProcessHeap(), 0, column_count*sizeof (MSICOLUMNINFO));
573     if( !columns )
574         return ERROR_FUNCTION_FAILED;
575
576     r = get_tablecolumns( db, name, columns, &column_count );
577     if( r != ERROR_SUCCESS )
578     {
579         HeapFree( GetProcessHeap(), 0, columns );
580         return ERROR_FUNCTION_FAILED;
581     }
582
583     *pcols = columns;
584     *pcount = column_count;
585
586     return r;
587 }
588
589 UINT get_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
590 {
591     UINT r;
592
593     *ptable = NULL;
594
595     /* first, see if the table is cached */
596     r = find_cached_table( db, name, ptable );
597     if( r == ERROR_SUCCESS )
598     {
599         (*ptable)->ref_count++;
600         return r;
601     }
602
603     r = read_table_from_storage( db, name, ptable );
604     if( r != ERROR_SUCCESS )
605         return r;
606
607     /* add the table to the list */
608     add_table( db, *ptable );
609     (*ptable)->ref_count++;
610
611     return ERROR_SUCCESS;
612 }
613
614 UINT save_table( MSIDATABASE *db, MSITABLE *t )
615 {
616     USHORT *rawdata = NULL, *p;
617     UINT rawsize, r, i, j, row_size, num_cols = 0;
618     MSICOLUMNINFO *cols, *last_col;
619
620     TRACE("Saving %s\n", debugstr_w( t->name ) );
621
622     r = table_get_column_info( db, t->name, &cols, &num_cols );
623     if( r != ERROR_SUCCESS )
624         return r;
625     
626     last_col = &cols[num_cols-1];
627     row_size = last_col->offset + bytes_per_column( last_col );
628
629     rawsize = t->row_count * row_size;
630     rawdata = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, rawsize );
631     if( !rawdata )
632         return ERROR_NOT_ENOUGH_MEMORY;
633
634     p = rawdata;
635     for( i=0; i<num_cols; i++ )
636     {
637         for( j=0; j<t->row_count; j++ )
638         {
639             UINT offset = cols[i].offset;
640
641             *p++ = t->data[j][offset/2];
642             if( 4 == bytes_per_column( &cols[i] ) )
643                 *p++ = t->data[j][offset/2+1];
644         }
645     }
646
647     TRACE("writing %d bytes\n", rawsize);
648     r = write_stream_data( db->storage, t->name, rawdata, rawsize );
649
650     HeapFree( GetProcessHeap(), 0, rawdata );
651
652     return r;
653 }
654
655 HRESULT init_string_table( IStorage *stg )
656 {
657     HRESULT r;
658     static const WCHAR szStringData[] = {
659         '_','S','t','r','i','n','g','D','a','t','a',0 };
660     static const WCHAR szStringPool[] = {
661         '_','S','t','r','i','n','g','P','o','o','l',0 };
662     USHORT zero[2] = { 0, 0 };
663     ULONG count = 0;
664     IStream *stm = NULL;
665     WCHAR encname[0x20];
666
667     encode_streamname(TRUE, szStringPool, encname);
668
669     /* create the StringPool stream... add the zero string to it*/
670     r = IStorage_CreateStream( stg, encname,
671             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
672     if( r ) 
673     {
674         TRACE("Failed\n");
675         return r;
676     }
677
678     r = IStream_Write(stm, zero, sizeof zero, &count );
679     IStream_Release( stm );
680
681     if( FAILED( r ) || ( count != sizeof zero ) )
682     {
683         TRACE("Failed\n");
684         return E_FAIL;
685     }
686
687     /* create the StringData stream... make it zero length */
688     encode_streamname(TRUE, szStringData, encname);
689     r = IStorage_CreateStream( stg, encname,
690             STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm);
691     if( r ) 
692     {
693         TRACE("Failed\n");
694         return E_FAIL;
695     }
696     IStream_Release( stm );
697
698     return r;
699 }
700
701 UINT load_string_table( MSIDATABASE *db )
702 {
703     CHAR *data;
704     USHORT *pool;
705     UINT r, ret = ERROR_FUNCTION_FAILED, datasize = 0, poolsize = 0, codepage;
706     DWORD i, count, offset, len, n;
707     static const WCHAR szStringData[] = {
708         '_','S','t','r','i','n','g','D','a','t','a',0 };
709     static const WCHAR szStringPool[] = {
710         '_','S','t','r','i','n','g','P','o','o','l',0 };
711
712     if( db->strings )
713     {
714         msi_destroy_stringtable( db->strings );
715         db->strings = NULL;
716     }
717
718     r = read_stream_data( db->storage, szStringPool, &pool, &poolsize );
719     if( r != ERROR_SUCCESS)
720         goto end;
721     r = read_stream_data( db->storage, szStringData, (USHORT**)&data, &datasize );
722     if( r != ERROR_SUCCESS)
723         goto end;
724
725     count = poolsize/4;
726     if( poolsize > 4 )
727         codepage = pool[0] | ( pool[1] << 16 );
728     else
729         codepage = CP_ACP;
730     db->strings = msi_init_stringtable( count, codepage );
731
732     offset = 0;
733     for( i=1; i<count; i++ )
734     {
735         len = pool[i*2];
736         n = msi_addstring( db->strings, i, data+offset, len, pool[i*2+1] );
737         if( n != i )
738             ERR("Failed to add string %ld\n", i );
739         offset += len;
740     }
741
742     TRACE("Loaded %ld strings\n", count);
743
744     ret = ERROR_SUCCESS;
745
746 end:
747     if( pool )
748         HeapFree( GetProcessHeap(), 0, pool );
749     if( data )
750         HeapFree( GetProcessHeap(), 0, data );
751
752     return ret;
753 }
754
755 UINT save_string_table( MSIDATABASE *db )
756 {
757     UINT i, count, datasize, poolsize, sz, used, r, codepage;
758     UINT ret = ERROR_FUNCTION_FAILED;
759     static const WCHAR szStringData[] = {
760         '_','S','t','r','i','n','g','D','a','t','a',0 };
761     static const WCHAR szStringPool[] = {
762         '_','S','t','r','i','n','g','P','o','o','l',0 };
763     CHAR *data = NULL;
764     USHORT *pool = NULL;
765
766     TRACE("\n");
767
768     /* construct the new table in memory first */
769     datasize = msi_string_totalsize( db->strings, &count );
770     poolsize = count*2*sizeof(USHORT);
771
772     pool = HeapAlloc( GetProcessHeap(), 0, poolsize );
773     if( ! pool )
774     {
775         ERR("Failed to alloc pool %d bytes\n", poolsize );
776         goto err;
777     }
778     data = HeapAlloc( GetProcessHeap(), 0, datasize );
779     if( ! data )
780     {
781         ERR("Failed to alloc data %d bytes\n", poolsize );
782         goto err;
783     }
784
785     used = 0;
786     codepage = msi_string_get_codepage( db->strings );
787     pool[0]=codepage&0xffff;
788     pool[1]=(codepage>>16);
789     for( i=1; i<count; i++ )
790     {
791         sz = datasize - used;
792         r = msi_id2stringA( db->strings, i, data+used, &sz );
793         if( r != ERROR_SUCCESS )
794         {
795             ERR("failed to fetch string\n");
796             sz = 0;
797         }
798         if( sz && (sz < (datasize - used ) ) )
799             sz--;
800         TRACE("adding %u bytes %s\n", sz, data+used );
801         pool[ i*2 ] = sz;
802         pool[ i*2 + 1 ] = msi_id_refcount( db->strings, i );
803         used += sz;
804         if( used > datasize  )
805         {
806             ERR("oops overran %d >= %d\n", used, datasize);
807             goto err;
808         }
809     }
810
811     if( used != datasize )
812     {
813         ERR("oops used %d != datasize %d\n", used, datasize);
814         goto err;
815     }
816
817     /* write the streams */
818     r = write_stream_data( db->storage, szStringData, data, datasize );
819     TRACE("Wrote StringData r=%08x\n", r);
820     if( r )
821         goto err;
822     r = write_stream_data( db->storage, szStringPool, pool, poolsize );
823     TRACE("Wrote StringPool r=%08x\n", r);
824     if( r )
825         goto err;
826
827     ret = ERROR_SUCCESS;
828
829 err:
830     if( data )
831         HeapFree( GetProcessHeap(), 0, data );
832     if( pool )
833         HeapFree( GetProcessHeap(), 0, pool );
834
835     return ret;
836 }
837
838 static LPWSTR strdupW( LPCWSTR str )
839 {
840     UINT len = lstrlenW( str ) + 1;
841     LPWSTR ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) );
842     if( ret )
843         lstrcpyW( ret, str );
844     return ret;
845 }
846
847 /* information for default tables */
848 static const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
849 static const WCHAR szTable[]  = { 'T','a','b','l','e',0 };
850 static const WCHAR szName[]    = { 'N','a','m','e',0 };
851 static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
852 static const WCHAR szColumn[]  = { 'C','o','l','u','m','n',0 };
853 static const WCHAR szNumber[]  = { 'N','u','m','b','e','r',0 };
854 static const WCHAR szType[]    = { 'T','y','p','e',0 };
855
856 struct standard_table {
857     LPCWSTR tablename;
858     LPCWSTR columnname;
859     UINT number;
860     UINT type;
861 } MSI_standard_tables[] =
862 {
863   { szTables,  szName,   1, MSITYPE_VALID | MSITYPE_STRING | 32},
864   { szColumns, szTable,  1, MSITYPE_VALID | MSITYPE_STRING | 32},
865   { szColumns, szNumber, 2, MSITYPE_VALID | 2},
866   { szColumns, szName,   3, MSITYPE_VALID | MSITYPE_STRING | 32},
867   { szColumns, szType,   4, MSITYPE_VALID | 2},
868 };
869
870 #define STANDARD_TABLE_COUNT \
871      (sizeof(MSI_standard_tables)/sizeof(struct standard_table))
872
873 UINT get_defaulttablecolumns( LPCWSTR szTable, MSICOLUMNINFO *colinfo, UINT *sz)
874 {
875     DWORD i, n=0;
876
877     for(i=0; i<STANDARD_TABLE_COUNT; i++)
878     {
879         if( lstrcmpW( szTable, MSI_standard_tables[i].tablename ) )
880             continue;
881         if(colinfo && (n < *sz) )
882         {
883             colinfo[n].tablename = strdupW(MSI_standard_tables[i].tablename);
884             colinfo[n].colname = strdupW(MSI_standard_tables[i].columnname);
885             colinfo[n].number = MSI_standard_tables[i].number;
886             colinfo[n].type = MSI_standard_tables[i].type;
887             /* ERR("Table %s has column %s\n",debugstr_w(colinfo[n].tablename),
888                     debugstr_w(colinfo[n].colname)); */
889             if( n )
890                 colinfo[n].offset = colinfo[n-1].offset
891                                   + bytes_per_column( &colinfo[n-1] );
892             else
893                 colinfo[n].offset = 0;
894         }
895         n++;
896         if( colinfo && (n >= *sz) )
897             break;
898     }
899     *sz = n;
900     return ERROR_SUCCESS;
901 }
902
903 LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid)
904 {
905     UINT sz=0, r;
906     LPWSTR str;
907
908     r = msi_id2stringW( db->strings, stringid, NULL, &sz );
909     if( r != ERROR_SUCCESS )
910         return NULL;
911     str = HeapAlloc( GetProcessHeap(), 0, sz*sizeof (WCHAR));
912     if( !str )
913         return str;
914     r = msi_id2stringW( db->strings, stringid, str, &sz );
915     if( r == ERROR_SUCCESS )
916         return str;
917     HeapFree(  GetProcessHeap(), 0, str );
918     return NULL;
919 }
920
921 static UINT get_tablecolumns( MSIDATABASE *db, 
922        LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz)
923 {
924     UINT r, i, n=0, table_id, count, maxcount = *sz;
925     MSITABLE *table = NULL;
926     static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
927
928     /* first check if there is a default table with that name */
929     r = get_defaulttablecolumns( szTableName, colinfo, sz );
930     if( ( r == ERROR_SUCCESS ) && *sz )
931         return r;
932
933     r = get_table( db, szColumns, &table);
934     if( r != ERROR_SUCCESS )
935     {
936         ERR("table %s not available\n", debugstr_w(szColumns));
937         return r;
938     }
939
940     /* convert table and column names to IDs from the string table */
941     r = msi_string2idW( db->strings, szTableName, &table_id );
942     if( r != ERROR_SUCCESS )
943     {
944         release_table( db, table );
945         ERR("Couldn't find id for %s\n", debugstr_w(szTableName));
946         return r;
947     }
948
949     TRACE("Table id is %d\n", table_id);
950
951     count = table->row_count;
952     for( i=0; i<count; i++ )
953     {
954         if( table->data[ i ][ 0 ] != table_id )
955             continue;
956         if( colinfo )
957         {
958             UINT id = table->data[ i ] [ 2 ];
959             colinfo[n].tablename = MSI_makestring( db, table_id );
960             colinfo[n].number = table->data[ i ][ 1 ] - (1<<15);
961             colinfo[n].colname = MSI_makestring( db, id );
962             colinfo[n].type = table->data[ i ] [ 3 ];
963             /* this assumes that columns are in order in the table */
964             if( n )
965                 colinfo[n].offset = colinfo[n-1].offset
966                                   + bytes_per_column( &colinfo[n-1] );
967             else
968                 colinfo[n].offset = 0;
969             TRACE("table %s column %d is [%s] (%d) with type %08x "
970                   "offset %d at row %d\n", debugstr_w(szTableName),
971                    colinfo[n].number, debugstr_w(colinfo[n].colname),
972                    id, colinfo[n].type, colinfo[n].offset, i);
973             if( n != (colinfo[n].number-1) )
974             {
975                 ERR("oops. data in the _Columns table isn't in the right "
976                     "order for table %s\n", debugstr_w(szTableName));
977                 return ERROR_FUNCTION_FAILED;
978             }
979         }
980         n++;
981         if( colinfo && ( n >= maxcount ) )
982             break;
983     }
984     *sz = n;
985
986     release_table( db, table );
987
988     return ERROR_SUCCESS;
989 }
990
991 /* try to find the table name in the _Tables table */
992 BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name )
993 {
994     static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 };
995     static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
996     UINT r, table_id = 0, i, count;
997     MSITABLE *table = NULL;
998
999     if( !lstrcmpW( name, szTables ) )
1000         return TRUE;
1001     if( !lstrcmpW( name, szColumns ) )
1002         return TRUE;
1003
1004     r = msi_string2idW( db->strings, name, &table_id );
1005     if( r != ERROR_SUCCESS )
1006     {
1007         TRACE("Couldn't find id for %s\n", debugstr_w(name));
1008         return FALSE;
1009     }
1010
1011     r = get_table( db, szTables, &table);
1012     if( r != ERROR_SUCCESS )
1013     {
1014         ERR("table %s not available\n", debugstr_w(szTables));
1015         return FALSE;
1016     }
1017
1018     /* count = table->size/2; */
1019     count = table->row_count;
1020     for( i=0; i<count; i++ )
1021         if( table->data[ i ][ 0 ] == table_id )
1022             break;
1023
1024     release_table( db, table );
1025
1026     if (i!=count)
1027         return TRUE;
1028
1029     ERR("Searched %d tables, but %d was not found\n", count, table_id );
1030
1031     return FALSE;
1032 }
1033
1034 /* below is the query interface to a table */
1035
1036 typedef struct tagMSITABLEVIEW
1037 {
1038     MSIVIEW        view;
1039     MSIDATABASE   *db;
1040     MSITABLE      *table;
1041     MSICOLUMNINFO *columns;
1042     UINT           num_cols;
1043     UINT           row_size;
1044     WCHAR          name[1];
1045 } MSITABLEVIEW;
1046
1047 static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
1048 {
1049     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1050     UINT offset, num_rows, n;
1051
1052     if( !tv->table )
1053         return ERROR_INVALID_PARAMETER;
1054
1055     if( (col==0) || (col>tv->num_cols) )
1056         return ERROR_INVALID_PARAMETER;
1057
1058     /* how many rows are there ? */
1059     num_rows = tv->table->row_count;
1060     if( row >= num_rows )
1061         return ERROR_NO_MORE_ITEMS;
1062
1063     if( tv->columns[col-1].offset >= tv->row_size )
1064     {
1065         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1066         ERR("%p %p\n", tv, tv->columns );
1067         return ERROR_FUNCTION_FAILED;
1068     }
1069
1070     offset = row + (tv->columns[col-1].offset/2) * num_rows;
1071     n = bytes_per_column( &tv->columns[col-1] );
1072     switch( n )
1073     {
1074     case 4:
1075         offset = tv->columns[col-1].offset/2;
1076         *val = tv->table->data[row][offset] + 
1077                (tv->table->data[row][offset + 1] << 16);
1078         break;
1079     case 2:
1080         offset = tv->columns[col-1].offset/2;
1081         *val = tv->table->data[row][offset];
1082         break;
1083     default:
1084         ERR("oops! what is %d bytes per column?\n", n );
1085         return ERROR_FUNCTION_FAILED;
1086     }
1087
1088     TRACE("Data [%d][%d] = %d \n", row, col, *val );
1089
1090     return ERROR_SUCCESS;
1091 }
1092
1093 static UINT TABLE_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val )
1094 {
1095     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1096     UINT offset, num_rows, n;
1097
1098     if( !tv->table )
1099         return ERROR_INVALID_PARAMETER;
1100
1101     if( (col==0) || (col>tv->num_cols) )
1102         return ERROR_INVALID_PARAMETER;
1103
1104     if( tv->columns[col-1].offset >= tv->row_size )
1105     {
1106         ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
1107         ERR("%p %p\n", tv, tv->columns );
1108         return ERROR_FUNCTION_FAILED;
1109     }
1110
1111     offset = row + (tv->columns[col-1].offset/2) * num_rows;
1112     n = bytes_per_column( &tv->columns[col-1] );
1113     switch( n )
1114     {
1115     case 4:
1116         offset = tv->columns[col-1].offset/2;
1117         tv->table->data[row][offset]     = val & 0xffff;
1118         tv->table->data[row][offset + 1] = (val>>16)&0xffff;
1119         break;
1120     case 2:
1121         offset = tv->columns[col-1].offset/2;
1122         tv->table->data[row][offset] = val;
1123         break;
1124     default:
1125         ERR("oops! what is %d bytes per column?\n", n );
1126         return ERROR_FUNCTION_FAILED;
1127     }
1128     return ERROR_SUCCESS;
1129 }
1130
1131 UINT TABLE_insert_row( struct tagMSIVIEW *view, UINT *num )
1132 {
1133     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1134     USHORT **p, *row;
1135     UINT sz;
1136
1137     TRACE("%p\n", view);
1138
1139     if( !tv->table )
1140         return ERROR_INVALID_PARAMETER;
1141
1142     row = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, tv->row_size );
1143     if( !row )
1144         return ERROR_NOT_ENOUGH_MEMORY;
1145
1146     sz = (tv->table->row_count + 1) * sizeof (UINT*);
1147     if( tv->table->data )
1148         p = HeapReAlloc( GetProcessHeap(), 0, tv->table->data, sz );
1149     else
1150         p = HeapAlloc( GetProcessHeap(), 0, sz );
1151     if( !p )
1152         return ERROR_NOT_ENOUGH_MEMORY;
1153
1154     tv->table->data = p;
1155     tv->table->data[tv->table->row_count] = row;
1156     *num = tv->table->row_count;
1157     tv->table->row_count++;
1158
1159     return ERROR_SUCCESS;
1160 }
1161
1162 static UINT TABLE_execute( struct tagMSIVIEW *view, MSIHANDLE record )
1163 {
1164     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1165     UINT r;
1166
1167     TRACE("%p %ld\n", tv, record);
1168
1169     if( tv->table )
1170         return ERROR_FUNCTION_FAILED;
1171
1172     r = get_table( tv->db, tv->name, &tv->table );
1173     if( r != ERROR_SUCCESS )
1174         return r;
1175     
1176     return ERROR_SUCCESS;
1177 }
1178
1179 static UINT TABLE_close( struct tagMSIVIEW *view )
1180 {
1181     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1182
1183     TRACE("%p\n", view );
1184
1185     if( !tv->table )
1186         return ERROR_FUNCTION_FAILED;
1187
1188     release_table( tv->db, tv->table );
1189     tv->table = NULL;
1190     
1191     return ERROR_SUCCESS;
1192 }
1193
1194 static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
1195 {
1196     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1197
1198     TRACE("%p %p %p\n", view, rows, cols );
1199
1200     if( cols )
1201         *cols = tv->num_cols;
1202     if( rows )
1203     {
1204         if( !tv->table )
1205             return ERROR_INVALID_PARAMETER;
1206         *rows = tv->table->row_count;
1207     }
1208
1209     return ERROR_SUCCESS;
1210 }
1211
1212 static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
1213                 UINT n, LPWSTR *name, UINT *type )
1214 {
1215     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1216
1217     TRACE("%p %d %p %p\n", tv, n, name, type );
1218
1219     if( ( n == 0 ) || ( n > tv->num_cols ) )
1220         return ERROR_INVALID_PARAMETER;
1221
1222     if( name )
1223     {
1224         *name = strdupW( tv->columns[n-1].colname );
1225         if( !*name )
1226             return ERROR_FUNCTION_FAILED;
1227     }
1228     if( type )
1229         *type = tv->columns[n-1].type;
1230
1231     return ERROR_SUCCESS;
1232 }
1233
1234 static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
1235 {
1236     FIXME("%p %d %ld\n", view, eModifyMode, hrec );
1237     return ERROR_CALL_NOT_IMPLEMENTED;
1238 }
1239
1240 static UINT TABLE_delete( struct tagMSIVIEW *view )
1241 {
1242     MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
1243
1244     TRACE("%p\n", view );
1245
1246     if( tv->table )
1247         release_table( tv->db, tv->table );
1248     tv->table = NULL;
1249
1250     if( tv->columns )
1251     {
1252         UINT i;
1253         for( i=0; i<tv->num_cols; i++)
1254         {
1255             HeapFree( GetProcessHeap(), 0, tv->columns[i].colname );
1256             HeapFree( GetProcessHeap(), 0, tv->columns[i].tablename );
1257         }
1258         HeapFree( GetProcessHeap(), 0, tv->columns );
1259     }
1260     tv->columns = NULL;
1261
1262     HeapFree( GetProcessHeap(), 0, tv );
1263
1264     return ERROR_SUCCESS;
1265 }
1266
1267
1268 MSIVIEWOPS table_ops =
1269 {
1270     TABLE_fetch_int,
1271     TABLE_set_int,
1272     TABLE_insert_row,
1273     TABLE_execute,
1274     TABLE_close,
1275     TABLE_get_dimensions,
1276     TABLE_get_column_info,
1277     TABLE_modify,
1278     TABLE_delete
1279 };
1280
1281 UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view )
1282 {
1283     MSITABLEVIEW *tv ;
1284     UINT r, sz, column_count;
1285     MSICOLUMNINFO *columns, *last_col;
1286
1287     TRACE("%p %s %p\n", db, debugstr_w(name), view );
1288
1289     /* get the number of columns in this table */
1290     column_count = 0;
1291     r = get_tablecolumns( db, name, NULL, &column_count );
1292     if( r != ERROR_SUCCESS )
1293         return r;
1294
1295     /* if there's no columns, there's no table */
1296     if( column_count == 0 )
1297         return ERROR_INVALID_PARAMETER;
1298
1299     TRACE("Table found\n");
1300
1301     sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ;
1302     tv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sz );
1303     if( !tv )
1304         return ERROR_FUNCTION_FAILED;
1305     
1306     columns = HeapAlloc( GetProcessHeap(), 0, column_count*sizeof (MSICOLUMNINFO));
1307     if( !columns )
1308     {
1309         HeapFree( GetProcessHeap(), 0, tv );
1310         return ERROR_FUNCTION_FAILED;
1311     }
1312
1313     r = get_tablecolumns( db, name, columns, &column_count );
1314     if( r != ERROR_SUCCESS )
1315     {
1316         HeapFree( GetProcessHeap(), 0, columns );
1317         HeapFree( GetProcessHeap(), 0, tv );
1318         return ERROR_FUNCTION_FAILED;
1319     }
1320
1321     TRACE("Table has %d columns\n", column_count);
1322
1323     last_col = &columns[column_count-1];
1324
1325     /* fill the structure */
1326     tv->view.ops = &table_ops;
1327     tv->db = db;
1328     tv->columns = columns;
1329     tv->num_cols = column_count;
1330     tv->table = NULL;
1331     tv->row_size = last_col->offset + bytes_per_column( last_col );
1332
1333     TRACE("one row is %d bytes\n", tv->row_size );
1334
1335     *view = (MSIVIEW*) tv;
1336     lstrcpyW( tv->name, name );
1337
1338     return ERROR_SUCCESS;
1339 }
1340
1341 UINT MSI_CommitTables( MSIDATABASE *db )
1342 {
1343     UINT r;
1344     MSITABLE *table = NULL;
1345
1346     TRACE("%p\n",db);
1347
1348     r = save_string_table( db );
1349     if( r != ERROR_SUCCESS )
1350     {
1351         ERR("failed to save string table r=%08x\n",r);
1352         return r;
1353     }
1354
1355     for( table = db->first_table; table; table = table->next )
1356     {
1357         r = save_table( db, table );
1358         if( r != ERROR_SUCCESS )
1359         {
1360             ERR("failed to save table %s (r=%08x)\n",
1361                   debugstr_w(table->name), r);
1362             return r;
1363         }
1364     }
1365
1366     /* force everything to reload next time */
1367     free_cached_tables( db );
1368
1369     return ERROR_SUCCESS;
1370 }