1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
5 * Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2.1 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
11 * This program 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
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Outc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22 #include <gsf-config.h>
23 #include <gsf/gsf-outfile-impl.h>
24 #include <gsf/gsf-outfile-msole.h>
25 #include <gsf/gsf-impl-utils.h>
26 #include <gsf/gsf-msole-impl.h>
27 #include <gsf/gsf-utils.h>
32 static GObjectClass *parent_class;
33 static GsfOutputClass *gsf_output_class;
36 #define G_LOG_DOMAIN "libgsf:msole"
38 typedef enum { MSOLE_DIR, MSOLE_SMALL_BLOCK, MSOLE_BIG_BLOCK } MSOleOutfileType;
40 /* The most common values */
41 #define OLE_DEFAULT_THRESHOLD 0x1000
42 #define OLE_DEFAULT_BB_SIZE (1 << OLE_DEFAULT_BB_SHIFT)
43 #define OLE_DEFAULT_SB_SIZE (1 << OLE_DEFAULT_SB_SHIFT)
45 struct _GsfOutfileMSOle {
49 GsfOutfileMSOle *root;
51 MSOleOutfileType type;
64 GPtrArray *root_order; /* only valid for the root */
70 size_t start_offset; /* in bytes */
73 unsigned char clsid[16]; /* 16 byte GUID used by some apps */
75 typedef GsfOutfileClass GsfOutfileMSOleClass;
78 gsf_outfile_msole_finalize (GObject *obj)
80 GsfOutfileMSOle *ole = GSF_OUTFILE_MSOLE (obj);
81 GsfOutput *output = GSF_OUTPUT (obj);
83 if (!gsf_output_is_closed (output))
84 gsf_output_close (output);
86 if (ole->sink != NULL) {
87 g_object_unref (G_OBJECT (ole->sink));
92 g_slist_free (ole->content.dir.children);
93 ole->content.dir.children = NULL;
94 if (ole->content.dir.root_order != NULL)
95 g_warning ("Finalizing a MSOle Outfile without closing it.");
98 case MSOLE_SMALL_BLOCK:
99 g_free (ole->content.small_block.buf);
100 ole->content.small_block.buf = NULL;
103 case MSOLE_BIG_BLOCK:
106 g_warning ("Unknown file type");
108 parent_class->finalize (obj);
112 gsf_outfile_msole_seek (GsfOutput *output, gsf_off_t offset,
115 GsfOutfileMSOle *ole = (GsfOutfileMSOle *)output;
118 case G_SEEK_SET : break;
119 case G_SEEK_CUR : offset += output->cur_offset; break;
120 case G_SEEK_END : offset += output->cur_size; break;
122 break; /*checked in GsfOutput wrapper */
128 g_warning ("Attempt to seek a directory");
133 case MSOLE_SMALL_BLOCK:
134 /* it is ok to seek past the big block threshold
135 * we don't convert until they _write_ something
139 case MSOLE_BIG_BLOCK:
140 return gsf_output_seek (ole->sink,
141 (gsf_off_t)(ole->content.big_block.start_offset + offset),
151 /* Globals to support variable OLE sector size. */
152 /* 512 and 4096 bytes are the only known values for sector size on */
153 /* Win2k/XP platforms. Attempts to create OLE files on Win2k/XP with */
154 /* other values using StgCreateStorageEx() fail with invalid parameter. */
155 /* This code has been tested with 128,256,512,4096,8192 sizes for */
156 /* libgsf read/write. Interoperability with MS OLE32.DLL has been */
157 /* tested with 512 and 4096 block size for filesizes up to 2GB. */
159 #define ZERO_PAD_BUF_SIZE 4096
161 /* static objects are zero-initialized as per C/C++ standards */
162 static guint8 const zero_buf [ZERO_PAD_BUF_SIZE];
164 /* Calculate the block of the current offset in the file. A useful idiom is to
165 * pad_zero to move to the start of the next block, then get the block number.
166 * This avoids fence post type problems with partial blocks. */
167 static inline guint32
168 ole_cur_block (GsfOutfileMSOle const *ole)
170 return (gsf_output_tell (ole->sink) - OLE_HEADER_SIZE) >> ole->bb.shift;
173 static inline unsigned
174 ole_bytes_left_in_block (GsfOutfileMSOle *ole)
176 /* blocks are multiples of bb.size (the header is padded out to bb.size) */
177 unsigned r = gsf_output_tell (ole->sink) % ole->bb.size;
178 return (r != 0) ? (ole->bb.size - r) : 0;
182 ole_pad_zero (GsfOutfileMSOle *ole)
184 /* no need to bounds check. len will always be less than bb.size, and
185 * we already check that zero_buf is big enough at creation */
186 unsigned len = ole_bytes_left_in_block (ole);
188 gsf_output_write (ole->sink, len, zero_buf);
191 /* Utility routine to generate a BAT for a file known to be sequential and
194 ole_write_bat (GsfOutput *sink, guint32 block, unsigned blocks)
196 guint8 buf [BAT_INDEX_SIZE];
198 /* FIXME FIXME FIXME optimize this to dump a buffer in 1 step */
199 while (blocks-- > 1) {
201 GSF_LE_SET_GUINT32 (buf, block);
202 gsf_output_write (sink, BAT_INDEX_SIZE, buf);
204 GSF_LE_SET_GUINT32 (buf, BAT_MAGIC_END_OF_CHAIN);
205 gsf_output_write (sink, BAT_INDEX_SIZE, buf);
209 ole_write_const (GsfOutput *sink, guint32 value, unsigned n)
211 guint8 buf [BAT_INDEX_SIZE];
213 GSF_LE_SET_GUINT32 (buf, value);
215 gsf_output_write (sink, BAT_INDEX_SIZE, buf);
219 ole_pad_bat_unused (GsfOutfileMSOle *ole, unsigned residual)
221 ole_write_const (ole->sink, BAT_MAGIC_UNUSED,
222 (ole_bytes_left_in_block (ole) / BAT_INDEX_SIZE) - residual);
225 /* write the metadata (dirents, small block, xbats) and close the sink */
227 gsf_outfile_msole_close_root (GsfOutfileMSOle *ole)
230 guint8 buf [OLE_HEADER_SIZE];
231 guint32 sbat_start, num_sbat, sb_data_start, sb_data_size, sb_data_blocks;
232 guint32 bat_start, num_bat, dirent_start, num_dirent_blocks, next, child_index;
233 unsigned i, j, blocks, num_xbat, xbat_pos;
235 unsigned metabat_size = ole->bb.size / BAT_INDEX_SIZE - 1;
236 GPtrArray *elem = ole->root->content.dir.root_order;
238 /* write small block data */
240 sb_data_start = ole_cur_block (ole);
241 data_size = gsf_output_tell (ole->sink);
242 for (i = 0 ; i < elem->len ; i++) {
243 GsfOutfileMSOle *child = g_ptr_array_index (elem, i);
244 if (child->type == MSOLE_SMALL_BLOCK) {
245 gsf_off_t size = gsf_output_size (GSF_OUTPUT (child));
247 child->blocks = ((size - 1) >> ole->sb.shift) + 1;
248 gsf_output_write (ole->sink,
249 child->blocks << ole->sb.shift,
250 child->content.small_block.buf);
251 child->first_block = blocks;
252 blocks += child->blocks;
255 child->first_block = BAT_MAGIC_END_OF_CHAIN;
259 data_size = gsf_output_tell (ole->sink) - data_size;
260 sb_data_size = data_size;
261 if ((gsf_off_t) sb_data_size != data_size) {
262 /* Check for overflow */
263 g_warning ("File too big");
267 sb_data_blocks = ole_cur_block (ole) - sb_data_start;
269 /* write small block BAT (the meta bat is in a file) */
270 sbat_start = ole_cur_block (ole);
271 for (i = 0 ; i < elem->len ; i++) {
272 GsfOutfileMSOle *child = g_ptr_array_index (elem, i);
273 if (child->type == MSOLE_SMALL_BLOCK && child->blocks > 0)
274 ole_write_bat (ole->sink, child->first_block, child->blocks);
276 ole_pad_bat_unused (ole, 0);
277 num_sbat = ole_cur_block (ole) - sbat_start;
280 dirent_start = ole_cur_block (ole);
281 for (i = 0 ; i < elem->len ; i++) {
282 GsfOutfileMSOle *child = g_ptr_array_index (elem, i);
283 glong j, name_len = 0;
285 memset (buf, 0, DIRENT_SIZE);
287 /* Hard code 'Root Entry' for the root */
288 if (i == 0 || gsf_output_name (GSF_OUTPUT (child)) != NULL) {
289 char const *name = (i == 0)
290 ? "Root Entry" : gsf_output_name (GSF_OUTPUT (child));
291 gunichar2 *name_utf16 = g_utf8_to_utf16 (name,
292 -1, NULL, &name_len, NULL);
293 if (name_len >= DIRENT_MAX_NAME_SIZE)
294 name_len = DIRENT_MAX_NAME_SIZE-1;
296 /* be wary about endianness */
297 for (j = 0 ; j < name_len ; j++)
298 GSF_LE_SET_GUINT16 (buf + j*2, name_utf16 [j]);
302 GSF_LE_SET_GUINT16 (buf + DIRENT_NAME_LEN, name_len*2);
304 if (child->root == child) {
305 GSF_LE_SET_GUINT8 (buf + DIRENT_TYPE, DIRENT_TYPE_ROOTDIR);
306 GSF_LE_SET_GUINT32 (buf + DIRENT_FIRSTBLOCK,
307 (sb_data_size > 0) ? sb_data_start : BAT_MAGIC_END_OF_CHAIN);
308 GSF_LE_SET_GUINT32 (buf + DIRENT_FILE_SIZE, sb_data_size);
309 memcpy (buf + DIRENT_CLSID, child->clsid, sizeof (child->clsid));
310 } else if (child->type == MSOLE_DIR) {
311 GSF_LE_SET_GUINT8 (buf + DIRENT_TYPE, DIRENT_TYPE_DIR);
312 GSF_LE_SET_GUINT32 (buf + DIRENT_FIRSTBLOCK, BAT_MAGIC_END_OF_CHAIN);
313 GSF_LE_SET_GUINT32 (buf + DIRENT_FILE_SIZE, 0);
314 /* write the class id */
315 memcpy (buf + DIRENT_CLSID, child->clsid, sizeof (child->clsid));
317 guint32 size = child->parent.parent.cur_size;
319 if ((gsf_off_t) size != child->parent.parent.cur_size)
320 g_warning ("File too big");
321 GSF_LE_SET_GUINT8 (buf + DIRENT_TYPE, DIRENT_TYPE_FILE);
322 GSF_LE_SET_GUINT32 (buf + DIRENT_FIRSTBLOCK, child->first_block);
323 GSF_LE_SET_GUINT32 (buf + DIRENT_FILE_SIZE, size);
325 /* make everything black (red == 0) */
326 GSF_LE_SET_GUINT8 (buf + DIRENT_COLOUR, 1);
328 tmp = gsf_output_container (GSF_OUTPUT (child));
329 next = DIRENT_MAGIC_END;
330 if (child->root != child && tmp != NULL) {
331 GSList *ptr = GSF_OUTFILE_MSOLE (tmp)->content.dir.children;
332 for (; ptr != NULL ; ptr = ptr->next)
333 if (ptr->data == child) {
334 if (ptr->next != NULL) {
335 GsfOutfileMSOle *sibling = ptr->next->data;
336 next = sibling->child_index;
341 /* make linked list rather than tree, only use next */
342 GSF_LE_SET_GUINT32 (buf + DIRENT_PREV, DIRENT_MAGIC_END);
343 GSF_LE_SET_GUINT32 (buf + DIRENT_NEXT, next);
345 child_index = DIRENT_MAGIC_END;
346 if (child->type == MSOLE_DIR && child->content.dir.children != NULL) {
347 GsfOutfileMSOle *first = child->content.dir.children->data;
348 child_index = first->child_index;
350 GSF_LE_SET_GUINT32 (buf + DIRENT_CHILD, child_index);
352 gsf_output_write (ole->sink, DIRENT_SIZE, buf);
355 num_dirent_blocks = ole_cur_block (ole) - dirent_start;
358 bat_start = ole_cur_block (ole);
359 for (i = 0 ; i < elem->len ; i++) {
360 GsfOutfileMSOle *child = g_ptr_array_index (elem, i);
361 if (child->type == MSOLE_BIG_BLOCK)
362 ole_write_bat (ole->sink, child->first_block, child->blocks);
364 if (sb_data_blocks > 0)
365 ole_write_bat (ole->sink, sb_data_start, sb_data_blocks);
367 ole_write_bat (ole->sink, sbat_start, num_sbat);
368 ole_write_bat (ole->sink, dirent_start, num_dirent_blocks);
370 /* List the BAT and meta-BAT blocks in the BAT. Doing this may
371 * increase the size of the bat and hence the metabat, so be
372 * prepared to iterate.
377 i = ((ole->sink->cur_size
378 + BAT_INDEX_SIZE * (num_bat + num_xbat)
379 - OLE_HEADER_SIZE - 1) >> ole->bb.shift) + 1;
386 if (num_bat > OLE_HEADER_METABAT_SIZE)
387 i = 1 + ((num_bat - OLE_HEADER_METABAT_SIZE - 1)
394 ole_write_const (ole->sink, BAT_MAGIC_BAT, num_bat);
395 ole_write_const (ole->sink, BAT_MAGIC_METABAT, num_xbat);
396 ole_pad_bat_unused (ole, 0);
399 xbat_pos = ole_cur_block (ole);
400 blocks = OLE_HEADER_METABAT_SIZE;
402 xbat_pos = BAT_MAGIC_END_OF_CHAIN;
406 /* fix up the header */
407 if (ole->bb.size == 4096) {
408 /* set _cSectDir for 4k sector files */
409 GSF_LE_SET_GUINT32 (buf, num_dirent_blocks);
410 gsf_output_seek (ole->sink,
411 (gsf_off_t) OLE_HEADER_CSECTDIR, G_SEEK_SET);
412 gsf_output_write (ole->sink, 4, buf);
414 GSF_LE_SET_GUINT32 (buf, num_bat);
415 GSF_LE_SET_GUINT32 (buf+4, dirent_start);
416 gsf_output_seek (ole->sink,
417 (gsf_off_t) OLE_HEADER_NUM_BAT, G_SEEK_SET);
418 gsf_output_write (ole->sink, 8, buf);
420 GSF_LE_SET_GUINT32 (buf+0x0,
421 (num_sbat > 0) ? sbat_start : BAT_MAGIC_END_OF_CHAIN);
422 GSF_LE_SET_GUINT32 (buf+0x4, num_sbat);
423 GSF_LE_SET_GUINT32 (buf+0x8, xbat_pos);
424 GSF_LE_SET_GUINT32 (buf+0xc, num_xbat);
425 gsf_output_seek (ole->sink, (gsf_off_t) OLE_HEADER_SBAT_START,
427 gsf_output_write (ole->sink, 0x10, buf);
429 /* write initial Meta-BAT */
430 for (i = 0 ; i < blocks ; i++) {
431 GSF_LE_SET_GUINT32 (buf, bat_start + i);
432 gsf_output_write (ole->sink, BAT_INDEX_SIZE, buf);
435 /* write extended Meta-BAT */
437 gsf_output_seek (ole->sink, 0, G_SEEK_END);
438 for (i = 0 ; i++ < num_xbat ; ) {
441 blocks = (num_bat > metabat_size) ? metabat_size : num_bat;
442 for (j = 0 ; j < blocks ; j++) {
443 GSF_LE_SET_GUINT32 (buf, bat_start + j);
444 gsf_output_write (ole->sink, BAT_INDEX_SIZE, buf);
448 ole_pad_bat_unused (ole, 1);
449 xbat_pos = BAT_MAGIC_END_OF_CHAIN;
452 GSF_LE_SET_GUINT32 (buf, xbat_pos);
453 gsf_output_write (ole->sink, BAT_INDEX_SIZE, buf);
457 /* free the children */
458 for (i = 0 ; i < elem->len ; i++)
459 g_object_unref (G_OBJECT (g_ptr_array_index (elem, i)));
460 g_ptr_array_free (elem, TRUE);
461 ole->content.dir.root_order = NULL;
463 return gsf_output_close (ole->sink);
467 gsf_outfile_msole_close (GsfOutput *output)
469 GsfOutfileMSOle *ole = (GsfOutfileMSOle *)output;
471 if (gsf_output_container (output) == NULL) /* The root dir */
472 return gsf_outfile_msole_close_root (ole);
474 if (ole->type == MSOLE_BIG_BLOCK) {
475 gsf_outfile_msole_seek (output, 0, G_SEEK_END);
477 ole->blocks = ole_cur_block (ole) - ole->first_block;
478 return gsf_output_unwrap (G_OBJECT (output), ole->sink);
485 gsf_outfile_msole_write (GsfOutput *output,
486 size_t num_bytes, guint8 const *data)
488 GsfOutfileMSOle *ole = (GsfOutfileMSOle *)output;
491 g_return_val_if_fail (ole->type != MSOLE_DIR, FALSE);
492 if (ole->type == MSOLE_SMALL_BLOCK) {
495 gsf_off_t start_offset;
497 if ((output->cur_offset + num_bytes) < OLE_DEFAULT_THRESHOLD) {
498 memcpy (ole->content.small_block.buf + output->cur_offset,
502 ok = gsf_output_wrap (G_OBJECT (output), ole->sink);
506 buf = ole->content.small_block.buf;
507 ole->content.small_block.buf = NULL;
508 start_offset = gsf_output_tell (ole->sink);
509 ole->content.big_block.start_offset = start_offset;
510 if ((gsf_off_t) ole->content.big_block.start_offset
512 /* Check for overflow */
513 g_warning ("File too big");
517 ole->first_block = ole_cur_block (ole);
518 ole->type = MSOLE_BIG_BLOCK;
519 wsize = output->cur_size;
520 if ((gsf_off_t) wsize != output->cur_size) {
521 /* Check for overflow */
522 g_warning ("File too big");
525 gsf_output_write (ole->sink, wsize, buf);
529 g_return_val_if_fail (ole->type == MSOLE_BIG_BLOCK, FALSE);
531 gsf_output_write (ole->sink, num_bytes, data);
536 static gsf_off_t gsf_outfile_msole_vprintf (GsfOutput *output,
537 char const *format, va_list args) G_GNUC_PRINTF (2, 0);
540 gsf_outfile_msole_vprintf (GsfOutput *output, char const *format, va_list args)
542 GsfOutfileMSOle *ole = (GsfOutfileMSOle *)output;
544 /* An optimization. */
545 if (ole->type == MSOLE_BIG_BLOCK)
546 return gsf_output_vprintf (ole->sink, format, args);
548 /* In other cases, use the gsf_output_real_vprintf fallback method.
549 * (This eventually calls gsf_outfile_msole_write, which will also
550 * check that ole->type != MSOLE_DIR.)
552 return gsf_output_class->Vprintf (output, format, args);
557 ole_register_child (GsfOutfileMSOle *root, GsfOutfileMSOle *child)
560 g_object_ref (G_OBJECT (child));
561 child->child_index = root->content.dir.root_order->len;
562 g_ptr_array_add (root->content.dir.root_order, child);
566 ole_name_cmp (GsfOutfileMSOle const *a, GsfOutfileMSOle const *b)
568 /* According to the docs length is more important than lexical order */
569 char const *a_name = gsf_output_name ((GsfOutput const *)a);
570 char const *b_name = gsf_output_name ((GsfOutput const *)b);
574 return (b_name == NULL) ? 0 : -1;
575 else if (b_name == NULL)
578 unsigned a_len = g_utf8_strlen (a_name, -1);
579 unsigned b_len = g_utf8_strlen (b_name, -1);
582 return a_len - b_len;
583 return g_utf8_collate (a_name, b_name);
588 gsf_outfile_msole_set_block_shift (GsfOutfileMSOle *ole,
589 unsigned bb_shift, unsigned sb_shift)
591 ole->bb.shift = bb_shift;
592 ole->bb.size = (1 << ole->bb.shift);
593 ole->sb.shift = sb_shift;
594 ole->sb.size = (1 << ole->sb.shift);
598 gsf_outfile_msole_new_child (GsfOutfile *parent,
599 char const *name, gboolean is_dir,
600 char const *first_property_name, va_list args)
602 GsfOutfileMSOle *ole_parent = (GsfOutfileMSOle *)parent;
603 GsfOutfileMSOle *child;
605 g_return_val_if_fail (ole_parent != NULL, NULL);
606 g_return_val_if_fail (ole_parent->type == MSOLE_DIR, NULL);
608 child = (GsfOutfileMSOle *)g_object_new_valist (
609 GSF_OUTFILE_MSOLE_TYPE, first_property_name, args);
611 child->type = MSOLE_DIR;
612 child->content.dir.children = NULL;
614 /* start as small block */
615 child->type = MSOLE_SMALL_BLOCK;
616 child->content.small_block.buf = g_new0 (guint8, OLE_DEFAULT_THRESHOLD);
618 g_object_ref (G_OBJECT (ole_parent->sink));
619 child->sink = ole_parent->sink;
620 child->root = ole_parent->root;
621 gsf_outfile_msole_set_block_shift (child,
622 ole_parent->bb.shift, ole_parent->sb.shift);
623 gsf_output_set_name (GSF_OUTPUT (child), name);
624 gsf_output_set_container (GSF_OUTPUT (child), parent);
626 ole_parent->content.dir.children = g_slist_insert_sorted (
627 ole_parent->content.dir.children, child,
628 (GCompareFunc)ole_name_cmp);
629 ole_register_child (ole_parent->root, child);
631 return GSF_OUTPUT (child);
635 gsf_outfile_msole_init (GObject *obj)
637 GsfOutfileMSOle *ole = GSF_OUTFILE_MSOLE (obj);
641 ole->type = MSOLE_DIR;
643 gsf_outfile_msole_set_block_shift (ole,
644 OLE_DEFAULT_BB_SHIFT, OLE_DEFAULT_SB_SHIFT);
646 ole->content.dir.children = NULL;
647 ole->content.dir.root_order = NULL;
648 memset (ole->clsid, 0, sizeof (ole->clsid));
652 gsf_outfile_msole_class_init (GObjectClass *gobject_class)
654 GsfOutputClass *output_class = GSF_OUTPUT_CLASS (gobject_class);
655 GsfOutfileClass *outfile_class = GSF_OUTFILE_CLASS (gobject_class);
657 gobject_class->finalize = gsf_outfile_msole_finalize;
658 output_class->Close = gsf_outfile_msole_close;
659 output_class->Seek = gsf_outfile_msole_seek;
660 output_class->Write = gsf_outfile_msole_write;
661 output_class->Vprintf = gsf_outfile_msole_vprintf;
662 outfile_class->new_child = gsf_outfile_msole_new_child;
664 parent_class = g_type_class_peek_parent (gobject_class);
665 gsf_output_class = g_type_class_peek (GSF_OUTPUT_TYPE);
668 GSF_CLASS (GsfOutfileMSOle, gsf_outfile_msole,
669 gsf_outfile_msole_class_init, gsf_outfile_msole_init,
672 /* returns the number of times 1 must be shifted left to reach value */
674 compute_shift (unsigned value)
677 while ((value >> i) > 1)
683 * gsf_outfile_msole_new_full :
684 * @sink : a #GsfOutput to hold the OLE2 file.
685 * @bb_size : size of large blocks.
686 * @sb_size : size of small blocks.
688 * Creates the root directory of an MS OLE file and manages the addition of
691 * <note>This adds a reference to @sink.</note>
693 * Returns: the new ole file handler.
696 gsf_outfile_msole_new_full (GsfOutput *sink, guint bb_size, guint sb_size)
698 static guint8 const default_header [] = {
699 /* 0x00 */ 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1,
700 /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
701 /* 0x18 */ 0x3e, 0x00, 0x03, 0x00, 0xfe, 0xff, 0x09, 0x00,
702 /* 0x20 */ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
703 /* 0x28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
704 /* 0x30 */ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
705 /* 0x38 */ 0x00, 0x10, 0x00, 0x00 /* 0x3c-0x4b: filled on close */
708 GsfOutfileMSOle *ole;
710 g_return_val_if_fail (GSF_IS_OUTPUT (sink), NULL);
712 ole = g_object_new (GSF_OUTFILE_MSOLE_TYPE, NULL);
713 if (G_UNLIKELY (NULL == ole)) return NULL;
715 g_object_ref (G_OBJECT (sink));
717 ole->type = MSOLE_DIR;
718 ole->content.dir.root_order = g_ptr_array_new ();
719 ole_register_child (ole, ole);
721 gsf_outfile_msole_set_block_shift (ole,
722 compute_shift (bb_size), compute_shift (sb_size));
723 if (ole->bb.size != bb_size ||
724 ole->sb.size != sb_size ||
725 bb_size <= sb_size ||
726 bb_size < DIRENT_SIZE ||
728 ZERO_PAD_BUF_SIZE < ole->bb.size) {
729 if (ZERO_PAD_BUF_SIZE < ole->bb.size)
730 g_warning ("Block size is too big, failing back to defaults.");
732 g_warning ("Incorrect block sizes, failing back to defaults.");
733 gsf_outfile_msole_set_block_shift (ole,
734 OLE_DEFAULT_BB_SHIFT, OLE_DEFAULT_SB_SHIFT);
737 /* The names are the same */
738 gsf_output_set_name (GSF_OUTPUT (ole), gsf_output_name (sink));
739 gsf_output_set_container (GSF_OUTPUT (ole), NULL);
741 /* build the header */
742 buf = g_new (guint8, OLE_HEADER_SIZE);
743 memcpy (buf, default_header, sizeof (default_header));
744 memset (buf + sizeof (default_header), 0xff,
745 OLE_HEADER_SIZE - sizeof (default_header));
746 GSF_LE_SET_GUINT16 (buf + OLE_HEADER_BB_SHIFT, ole->bb.shift);
747 GSF_LE_SET_GUINT16 (buf + OLE_HEADER_SB_SHIFT, ole->sb.shift);
748 /* 4k sector OLE files seen in the wild have version 4 */
749 if (ole->bb.size == 4096)
750 GSF_LE_SET_GUINT16 (buf + OLE_HEADER_MAJOR_VER, 4);
751 gsf_output_write (sink, OLE_HEADER_SIZE, buf);
754 /* header must be padded out to bb.size with zeros */
757 return GSF_OUTFILE (ole);
761 * gsf_outfile_msole_new :
762 * @sink : a #GsfOutput to hold the OLE2 file
764 * Creates the root directory of an MS OLE file and manages the addition of
767 * <note>This adds a reference to @sink.</note>
769 * Returns: the new ole file handler.
772 gsf_outfile_msole_new (GsfOutput *sink)
774 return gsf_outfile_msole_new_full (sink,
775 OLE_DEFAULT_BB_SIZE, OLE_DEFAULT_SB_SIZE);
779 * gsf_outfile_msole_set_class_id :
780 * @ole: a #GsfOutfileMSOle
781 * @clsid: 16 byte identifier (often a GUID in MS Windows apps)
783 * Write @clsid to the directory associated with @ole.
785 * Returns: TRUE on success.
788 gsf_outfile_msole_set_class_id (GsfOutfileMSOle *ole, guint8 const *clsid)
790 g_return_val_if_fail (ole != NULL && ole->type == MSOLE_DIR, FALSE);
791 memcpy (ole->clsid, clsid, sizeof (ole->clsid));