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