Commit 13da21d5 authored by Michael Meeks's avatar Michael Meeks
Browse files

OLE2 library re-written, block based back-end completely discarded.

Seems to work for me :-), faster & cleaner hopefully.
parent 10c2fb14
1999-05-20 Michael Meeks <michael@imaginator.com>
* ole.c (main): Change ole_new to ole_open.
(list_files): Added assertion.
* boot.c (excel_load): Same.
1999-05-19 Michael Meeks <michael@imaginator.com>
* ole.c (main): Add New OLE file creation support.
* ms-excel-read.c (ms_excel_set_cell_xf): Cleaned silly
debug.
......
......@@ -30,7 +30,7 @@ excel_probe (const char *filename)
MS_OLE *f;
int res;
f = ms_ole_new (filename);
f = ms_ole_open (filename);
res = f != NULL;
......@@ -45,7 +45,7 @@ excel_load (const char *filename)
Workbook *wb;
MS_OLE *f;
f = ms_ole_new (filename);
f = ms_ole_open (filename);
if (!f)
return NULL;
......
1999-05-20 Michael Meeks <michael@imaginator.com>
* ms-ole.c: Whole Block structure totaly re-written for the
third time ! Lots cleaner, and should support non-mmapped
architectures with time.
1999-05-19 Michael Meeks <michael@imaginator.com>
* ms-ole.c (ms_ole_write_sb): Fix fatal bug with converting a
SB -> a BB file when mmap moves memory invalidating src ptr.
(ms_ole_create): Init header to UNUSED_BLOCK.
1999-05-18 Michael Meeks <michael@imaginator.com>
* ms-ole.c (init_pps): Constricted name writing further.
......
......@@ -7,3 +7,5 @@ Much useful information will be gleaned from the work on wine by.
Marcus Meissner, Francis Beaudet, Sylvain St-Germain
and Thuy Nguyen
This code is now being re-written for the 3rd and hopefully
final time.
......@@ -18,7 +18,7 @@
/* Implementational detail - not for global header */
#define OLE_DEBUG 0
#define OLE_DEBUG 1
/* These take a _guint8_ pointer */
#define GET_GUINT8(p) (*((const guint8 *)(p)+0))
......@@ -46,7 +46,9 @@
#if OLE_DEBUG > 0
/* Very grim, but quite necessary */
#define ms_array_index(a,b,c) (b)my_array_hack ((a), sizeof(b), (c))
# define ms_array_index(a,b,c) (b)my_array_hack ((a), sizeof(b), (c))
static int critical_section = 0;
static guint32
my_array_hack (GArray *a, guint s, guint32 idx)
......@@ -57,26 +59,42 @@ my_array_hack (GArray *a, guint s, guint32 idx)
g_assert (s==4);
return ((guint32 *)a->data)[idx];
}
#else
/* Far far faster... */
#define ms_array_index(a,b,c) g_array_index (a, b, c)
# define ms_array_index(a,b,c) g_array_index (a, b, c)
#endif
/* Bound checks guint8 pointer p inside f */
#define CHECK_VALID(f,ptr) (g_assert ((ptr) >= (f)->mem && \
(ptr) < (f)->mem + (f)->length))
#define BB_THRESHOLD 0x1000
/**
* These look _ugly_ but reduce to a few shifts, bit masks and adds
* Under optimisation these are _really_ quick !
* NB. Parameters are always: 'MS_OLE *', followed by a guint32 block or index
**/
#define PPS_ROOT_BLOCK 0
#define PPS_BLOCK_SIZE 0x80
#define PPS_END_OF_CHAIN 0xffffffff
/* Return total number of Big Blocks in file, NB. first block = root */
#define NUM_BB(f) (((f)->length/BB_BLOCK_SIZE) - 1)
typedef struct _PPS PPS;
struct _PPS {
char *name;
PPS_IDX next, prev, dir, pps;
guint32 size;
BLP start;
PPS_TYPE type;
};
#if OLE_MMAP
# define BBPTR(f,b) ((f)->mem + (b+1)*BB_BLOCK_SIZE)
# define GET_SB_START_PTR(f,b) (BBPTR(f, g_array_index ((f)->sbf, BLP, (b)/(BB_BLOCK_SIZE/SB_BLOCK_SIZE))) \
+ (((b)%(BB_BLOCK_SIZE/SB_BLOCK_SIZE))*SB_BLOCK_SIZE))
#else
# define BBPTR(f,b) (get_block_ptr (f, b))
#endif
static guint8 *
get_block_ptr (MS_OLE *f, BLP b)
{
/* Reads it in if neccessary */
return NULL;
}
/* This is a list of big blocks which contain a flat description of all blocks in the file.
Effectively inside these blocks is a FAT of chains of other BBs, so the theoretical max
......@@ -87,13 +105,8 @@ my_array_hack (GArray *a, guint s, guint32 idx)
/* The block locations of the Big Block Descriptor Blocks */
#define GET_BBD_LIST(f,i) (GET_GUINT32((f)->mem + 0x4c + (i)*4))
#define SET_BBD_LIST(f,i,n) (SET_GUINT32((f)->mem + 0x4c + (i)*4, (n)))
/* Find the position of the start in memory of a big block */
#define GET_BB_START_PTR(f,n) ((guint8 *)((f)->mem+((n)+1)*BB_BLOCK_SIZE))
/* Find the position of the start in memory of a small block */
#define GET_SB_START_PTR(f,n) ( GET_BB_START_PTR((f), ms_array_index ((f)->header.sbf_list, SBPtr, ((SB_BLOCK_SIZE*(n))/BB_BLOCK_SIZE))) + \
(SB_BLOCK_SIZE*(n)%BB_BLOCK_SIZE) )
#define NEXT_BB(f,n) (g_array_index ((f)->bb, BLP, n))
#define NEXT_SB(f,n) (g_array_index ((f)->sb, BLP, n))
/* Get the start block of the root directory ( PPS ) chain */
#define GET_ROOT_STARTBLOCK(f) (GET_GUINT32((f)->mem + 0x30))
#define SET_ROOT_STARTBLOCK(f,i) (SET_GUINT32((f)->mem + 0x30, i))
......@@ -101,99 +114,27 @@ my_array_hack (GArray *a, guint s, guint32 idx)
#define GET_SBD_STARTBLOCK(f) (GET_GUINT32((f)->mem + 0x3c))
#define SET_SBD_STARTBLOCK(f,i) (SET_GUINT32((f)->mem + 0x3c, i))
/* Gives the position in memory, as a GUINT8 *, of the BB entry ( in a chain ) */
#define GET_BB_CHAIN_PTR(f,n) (GET_BB_START_PTR((f), (GET_BBD_LIST((f), ( ((n)*sizeof(BBPtr)) / BB_BLOCK_SIZE)))) + \
(((n)*sizeof(BBPtr))%BB_BLOCK_SIZE))
/* Gives the position in memory, as a GUINT8 *, of the SB entry ( in a chain ) of a small block index */
#define GET_SB_CHAIN_PTR(f,n) (GET_BB_START_PTR(f, ms_array_index ((f)->header.sbd_list, SBPtr, (((n)*sizeof(SBPtr))/BB_BLOCK_SIZE))) + \
(((n)*sizeof(SBPtr))%BB_BLOCK_SIZE))
/* Gives the position in memory, as a GUINT8 *, of the SBD entry of a small block index */
#define GET_SBD_CHAIN_PTR(f,n) (GET_BB_CHAIN_PTR((f), ms_array_index ((f)->header.sbd_list, SBPtr, ((sizeof(SBPtr)*(n))/BB_BLOCK_SIZE))))
/* Gives the position in memory, as a GUINT8 *, of the SBF entry of a small block index */
#define GET_SBF_CHAIN_PTR(f,n) (GET_BB_CHAIN_PTR((f), ms_array_index ((f)->header.sbf_list, SBPtr, ((SB_BLOCK_SIZE*(n))/BB_BLOCK_SIZE))))
#define BB_THRESHOLD 0x1000
#define PPS_ROOT_BLOCK 0
#define PPS_BLOCK_SIZE 0x80
#define PPS_END_OF_CHAIN 0xffffffff
/* This takes a PPS_IDX and returns a guint8 * to its data */
#define PPS_PTR(f,n) (GET_BB_START_PTR((f),ms_array_index ((f)->header.root_list, BBPtr, (((n)*PPS_BLOCK_SIZE)/BB_BLOCK_SIZE))) + \
(((n)*PPS_BLOCK_SIZE)%BB_BLOCK_SIZE))
#define PPS_GET_NAME_LEN(f,n) (GET_GUINT16(PPS_PTR(f,n) + 0x40))
#define PPS_SET_NAME_LEN(f,n,i) (SET_GUINT16(PPS_PTR(f,n) + 0x40, (i)*2))
/* This takes a PPS_IDX and returns a char * to its rendered name */
#define PPS_NAME(f,n) (pps_get_text (PPS_PTR(f,n), PPS_GET_NAME_LEN(f,n)))
/* These takes a PPS_IDX and returns the corresponding functions PPS_IDX */
/* NB it is misleading to assume that Microsofts linked lists link correctly.
It is not the case that pps_next(f, pps_prev(f, n)) = n ! For the final list
item there are no valid links. Cretins. */
#define PPS_GET_PREV(f,n) ((PPS_IDX) GET_GUINT32(PPS_PTR(f,n) + 0x44))
#define PPS_GET_NEXT(f,n) ((PPS_IDX) GET_GUINT32(PPS_PTR(f,n) + 0x48))
#define PPS_GET_DIR(f,n) ((PPS_IDX) GET_GUINT32(PPS_PTR(f,n) + 0x4c))
#define PPS_SET_PREV(f,n,i) ((PPS_IDX) SET_GUINT32(PPS_PTR(f,n) + 0x44, i))
#define PPS_SET_NEXT(f,n,i) ((PPS_IDX) SET_GUINT32(PPS_PTR(f,n) + 0x48, i))
#define PPS_SET_DIR(f,n,i) ((PPS_IDX) SET_GUINT32(PPS_PTR(f,n) + 0x4c, i))
#define PPS_GET_NAME_LEN(p) (GET_GUINT16(p + 0x40))
#define PPS_SET_NAME_LEN(p,i) (SET_GUINT16(p + 0x40, (i)*2))
#define PPS_NAME(f,n) (pps_get_text (p, PPS_GET_NAME_LEN(f,n)))
#define PPS_GET_PREV(p) ((PPS_IDX) GET_GUINT32(p + 0x44))
#define PPS_GET_NEXT(p) ((PPS_IDX) GET_GUINT32(p + 0x48))
#define PPS_GET_DIR(p) ((PPS_IDX) GET_GUINT32(p + 0x4c))
#define PPS_SET_PREV(p,i) ((PPS_IDX) SET_GUINT32(p + 0x44, i))
#define PPS_SET_NEXT(p,i) ((PPS_IDX) SET_GUINT32(p + 0x48, i))
#define PPS_SET_DIR(p,i) ((PPS_IDX) SET_GUINT32(p + 0x4c, i))
/* These get other interesting stuff from the PPS record */
#define PPS_GET_STARTBLOCK(f,n) ( GET_GUINT32(PPS_PTR(f,n) + 0x74))
#define PPS_GET_SIZE(f,n) ( GET_GUINT32(PPS_PTR(f,n) + 0x78))
#define PPS_GET_TYPE(f,n) ((PPS_TYPE)(GET_GUINT8(PPS_PTR(f,n) + 0x42)))
#define PPS_SET_STARTBLOCK(f,n,i) ( SET_GUINT32(PPS_PTR(f,n) + 0x74, i))
#define PPS_SET_SIZE(f,n,i) ( SET_GUINT32(PPS_PTR(f,n) + 0x78, i))
#define PPS_SET_TYPE(f,n,i) ( SET_GUINT8(PPS_PTR(f,n) + 0x42, i))
/* Returns the next BB after this one in the chain */
#define NEXT_BB(f,n) (GET_GUINT32(GET_BB_CHAIN_PTR(f,n)))
/* Returns the next SB after this one in the chain */
#define NEXT_SB(f,n) (GET_GUINT32(GET_SB_CHAIN_PTR(f,n)))
#define GETRECDATA(a,n) ((a>>n)&0x1)
#define SETRECDATA(a,n) (a|=(&0x1<<n))
#define CLEARRECDATA(a,n) (a&=(0xf7f>>(n-7))
static BBPtr
get_block (MS_OLE_STREAM *s)
{
guint32 bl;
if (!s || !s->blocks) return END_OF_CHAIN;
if (s->strtype==MS_OLE_SMALL_BLOCK)
bl = s->position/SB_BLOCK_SIZE;
else
bl = s->position/BB_BLOCK_SIZE;
if (bl<s->blocks->len)
return ms_array_index (s->blocks, BBPtr, bl);
else
return END_OF_CHAIN;
}
void
dump (guint8 *ptr, guint32 len)
{
guint32 lp,lp2;
guint32 off;
#define PPS_GET_STARTBLOCK(p) ( GET_GUINT32(p + 0x74))
#define PPS_GET_SIZE(p) ( GET_GUINT32(p + 0x78))
#define PPS_GET_TYPE(p) ((PPS_TYPE)( GET_GUINT8(p + 0x42)))
#define PPS_SET_STARTBLOCK(p,i) ( SET_GUINT32(p + 0x74, i))
#define PPS_SET_SIZE(p,i) ( SET_GUINT32(p + 0x78, i))
#define PPS_SET_TYPE(p,i) ( SET_GUINT8 (p + 0x42, i))
for (lp = 0;lp<(len+15)/16;lp++)
{
printf ("%8x | ", lp*16);
for (lp2=0;lp2<16;lp2++) {
off = lp2 + (lp<<4);
off<len?printf("%2x ", ptr[off]):printf("XX ");
}
printf (" | ");
for (lp2=0;lp2<16;lp2++) {
off = lp2 + (lp<<4);
printf ("%c", off<len?(ptr[off]>'!'&&ptr[off]<127?ptr[off]:'.'):'*');
}
printf ("\n");
}
}
/* FIXME: This needs proper unicode support ! current support is a guess */
/* Length is in bytes == 1/2 the final text length */
/* NB. Different from biff_get_text, looks like a bug ! */
......@@ -205,7 +146,8 @@ pps_get_text (guint8 *ptr, int length)
guint16 c;
guint8 *inb;
if (!length)
if (length <= 0 ||
length > PPS_BLOCK_SIZE/2)
return 0;
length = (length+1)/2;
......@@ -230,238 +172,330 @@ static void
dump_header (MS_OLE *f)
{
int lp;
MS_OLE_HEADER *h = &f->header;
printf ("--------------------------MS_OLE HEADER-------------------------\n");
printf ("Num BBD Blocks : %d Root %d, SBD %d\n",
GET_NUM_BBD_BLOCKS(f),
(int)h->root_startblock,
(int)h->sbd_startblock);
for (lp=0;lp<GET_NUM_BBD_BLOCKS(f);lp++)
printf ("GET_BBD_LIST[%d] = %d\n", lp, GET_BBD_LIST(f,lp));
printf ("Num BBD Blocks : %d Root %d, SB blocks %d\n",
f->bb?f->bb->len:-1,
f->pps?f->pps->len:-1,
f->sb?f->sb->len:-1);
for (lp=0;lp<f->bb->len;lp++)
printf ("Block %d -> block %d\n", lp,
g_array_index (f->bb, BLP, lp));
printf ("Root blocks : %d\n", h->root_list->len);
/* printf ("Root blocks : %d\n", h->root_list->len);
for (lp=0;lp<h->root_list->len;lp++)
printf ("root_list[%d] = %d\n", lp, (int)ms_array_index (h->root_list, BBPtr, lp));
printf ("sbd blocks : %d\n", h->sbd_list->len);
for (lp=0;lp<h->sbd_list->len;lp++)
printf ("sbd_list[%d] = %d\n", lp, (int)ms_array_index (h->sbd_list, SBPtr, lp));
printf ("sbd_list[%d] = %d\n", lp, (int)ms_array_index (h->sbd_list, SBPtr, lp));*/
printf ("-------------------------------------------------------------\n");
}
static int
read_header (MS_OLE *f)
static BLP
get_next_block (MS_OLE *f, BLP blk)
{
MS_OLE_HEADER *header = &f->header;
header->root_startblock = GET_ROOT_STARTBLOCK(f);
header->sbd_startblock = GET_SBD_STARTBLOCK(f);
return 1;
BLP bbd = GET_BBD_LIST (f, blk/(BB_BLOCK_SIZE/4));
return GET_GUINT32 (BBPTR(f,bbd) + 4*(blk%(BB_BLOCK_SIZE/4)));
}
static void
dump_allocation (MS_OLE *f)
static int
read_bb (MS_OLE *f)
{
int blk, dep, lp;
printf ("Big block allocation %d blocks, length %d\n", NUM_BB(f), f->length);
guint32 numbbd;
BLP ptr, lp;
GArray *ans;
dep = 0;
blk = 0;
while (dep<GET_NUM_BBD_BLOCKS(f))
{
printf ("FAT block %d\n", dep);
for (lp=0;lp<BB_BLOCK_SIZE/sizeof(BBPtr);lp++)
{
guint32 type;
type = GET_GUINT32(GET_BB_CHAIN_PTR(f, blk));
if ((blk + dep*(BB_BLOCK_SIZE/sizeof(BBPtr))) == NUM_BB(f))
printf ("|");
else if (type == SPECIAL_BLOCK)
printf ("S");
else if (type == UNUSED_BLOCK)
printf (".");
else if (type == END_OF_CHAIN)
printf ("X");
else
printf ("O");
blk++;
if (!(blk%16))
printf (" - %d\n", blk-1);
}
dep++;
g_return_val_if_fail (f, 0);
g_return_val_if_fail (f->mem, 0);
ans = g_array_new (FALSE, FALSE, sizeof(BLP));
numbbd = GET_NUM_BBD_BLOCKS (f);
ptr = GET_ROOT_STARTBLOCK (f);
/* Sanity checks */
if (numbbd < ((f->length + ((BB_BLOCK_SIZE*BB_BLOCK_SIZE)/4) - 1) /
((BB_BLOCK_SIZE*BB_BLOCK_SIZE)/4))) {
printf ("Duff block descriptors\n");
return 0;
}
for (lp=0;lp<(f->length+BB_BLOCK_SIZE-1)/BB_BLOCK_SIZE;lp++) {
BLP tmp = get_next_block (f, lp);
g_array_append_val (ans, tmp);
}
printf ("Small block allocation\n");
dep = 0;
blk = 0;
while (dep<f->header.sbd_list->len)
{
printf ("SB block %d ( = BB block %d )\n", dep, ms_array_index (f->header.sbd_list, SBPtr, dep));
for (lp=0;lp<BB_BLOCK_SIZE/sizeof(SBPtr);lp++)
{
guint32 type;
type = GET_GUINT32(GET_SB_CHAIN_PTR(f, blk));
if (type == SPECIAL_BLOCK)
printf ("S");
else if (type == UNUSED_BLOCK)
printf (".");
else if (type == END_OF_CHAIN)
printf ("X");
else
printf ("O");
blk++;
if (!(blk%16))
printf (" - %d\n", blk-1);
g_assert ((f->length+BB_BLOCK_SIZE-1)/BB_BLOCK_SIZE <= ans->len);
/* More sanity checks */
/* for (lp=0;lp<numbbd;lp++) {
BLP bbdblk = GET_BBD_LIST(f, lp);
if (g_array_index(ans, BLP, bbdblk) != SPECIAL_BLOCK) {
printf ("Error - BBD blocks not marked correctly\n");
g_array_free (ans, TRUE);
return 0;
}
dep++;
}
}*/
f->bb = ans;
#if OLE_DEBUG > 1
dump_header (f);
#endif
return 1;
}
/* Things that should be true */
static void
check (MS_OLE *f)
static PPS *
pps_decode (guint8 *mem)
{
guint32 numbbd = GET_NUM_BBD_BLOCKS(f);
guint32 maxbb = numbbd*(BB_BLOCK_SIZE/sizeof(BBPtr));
guint32 lp;
for (lp=NUM_BB(f);lp<maxbb;lp++) {
BBPtr blk = GET_GUINT32(GET_BB_CHAIN_PTR(f,lp));
if (blk != UNUSED_BLOCK) {
printf ("Serious blocking error\n");
dump_allocation (f);
g_on_error_stack_trace (NULL);
}
}
PPS *pps = g_new (PPS, 1);
pps->name = pps_get_text (mem, PPS_GET_NAME_LEN(mem));
pps->type = PPS_GET_TYPE (mem);
pps->size = PPS_GET_SIZE (mem);
pps->next = PPS_GET_NEXT (mem);
pps->prev = PPS_GET_PREV (mem);
pps->dir = PPS_GET_DIR (mem);
pps->start = PPS_GET_STARTBLOCK (mem);
#if OLE_DEBUG > 1
printf ("PPS decode : '%s'\n", pps->name?pps->name:"Null");
dump (mem, PPS_BLOCK_SIZE);
#endif
return pps;
}
/* Pass a pointer to the raw memory at start of PPS */
static void
init_pps (guint8 *mem, char *name)
pps_code (guint8 *mem, PPS *pps)
{
int lp, max;
g_return_if_fail (name);
g_return_if_fail (pps);
/* Blank stuff I don't understand */
for (lp=0;lp<PPS_BLOCK_SIZE;lp++)
SET_GUINT8(mem+lp, 0);
max = strlen (name);
max = strlen (pps->name);
if (max >= (PPS_BLOCK_SIZE/4))
max = (PPS_BLOCK_SIZE/4);
for (lp=0;lp<max;lp++)
SET_GUINT16(mem + lp*2, name[lp]);
SET_GUINT16(mem + lp*2, pps->name[lp]);
PPS_SET_NAME_LEN(mem + 0x40, (max+1)*2);
/* Magic numbers */
SET_GUINT32 (mem + 0x50, 0x00020900);
SET_GUINT32 (mem + 0x58, 0x000000c0);
SET_GUINT32 (mem + 0x5c, 0x46000000);
PPS_SET_TYPE (mem, pps->type);
PPS_SET_SIZE (mem, pps->size);
PPS_SET_NEXT (mem, pps->next);
PPS_SET_PREV (mem, pps->prev);
PPS_SET_DIR (mem, pps->dir);
PPS_SET_STARTBLOCK(mem, END_OF_CHAIN);
}
printf ("Len of '%s' == %d\n", name, max);
SET_GUINT16(mem + 0x40, (max+1)*2);
static int
read_pps (MS_OLE *f)
{
BLP blk;
GPtrArray *ans = g_ptr_array_new ();
/* Magic numbers */
SET_GUINT32 (mem + 0x50, 0x00020900);
SET_GUINT32 (mem + 0x58, 0x000000c0);
SET_GUINT32 (mem + 0x5c, 0x46000000);
g_return_val_if_fail (f, 0);
blk = GET_ROOT_STARTBLOCK (f);
printf ("Root start block %d\n", blk);
while (blk != END_OF_CHAIN) {
int lp;
BLP last;
if (blk == SPECIAL_BLOCK ||
blk == UNUSED_BLOCK) {
printf ("Duff block in root chain\n");
return 0;
}
for (lp=0;lp<BB_BLOCK_SIZE/PPS_BLOCK_SIZE;lp++) {
PPS *p = pps_decode(BBPTR(f,blk) + lp*PPS_BLOCK_SIZE);
p->pps = lp;
g_ptr_array_add (ans, p);
}
last = blk;
blk = NEXT_BB(f, blk);
g_array_index (f->bb, BLP, last) = UNUSED_BLOCK;
}
f->pps = ans;
return 1;
}
/* Create a nice linear array and return count of the number in the array */
static GArray *
read_link_array (MS_OLE *f, BBPtr first)
static int
read_sb (MS_OLE *f)
{
BBPtr ptr = first;
int lp, num=0;
GArray *ans = g_array_new (0, 0, sizeof(BBPtr));
BLP ptr;
int lp;
PPS *root;
g_return_val_if_fail (f, 0);
g_return_val_if_fail (f->pps, 0);
ptr = first;
root = g_ptr_array_index (f->pps, 0);
g_return_val_if_fail (root, 0);
f->sbf = g_array_new (FALSE, FALSE, sizeof(BLP));
f->sb = g_array_new (FALSE, FALSE, sizeof(BLP));
/* List of big blocks in SB file */
ptr = root->start;
printf ("Starting Small block file at %d\n", root->start);
while (ptr != END_OF_CHAIN) {
if (ptr == UNUSED_BLOCK ||
ptr == SPECIAL_BLOCK) {
printf ("Corrupt file: serious error, invalid block in chain\n");
g_array_free (ans, TRUE);
printf ("Corrupt small block file: serious error, "
"invalid block in chain\n");
g_array_free (f->sbf, TRUE);
f->sbf = 0;
return 0;
}
g_array_append_val (ans, ptr);
g_array_append_val (f->sbf, ptr);
ptr = NEXT_BB (f, ptr);
}
return ans;
/* Description of small blocks */
ptr = GET_SBD_STARTBLOCK (f);
while (ptr != END_OF_CHAIN) {
guint32 lp;
if (ptr == UNUSED_BLOCK ||
ptr == SPECIAL_BLOCK) {
printf ("Corrupt file descriptor: serious error, "
"invalid block in chain\n");
g_array_free (f->sb, TRUE);
f->sb = 0;
return 0;
}
for (lp=0;lp<BB_BLOCK_SIZE/4;lp++) {
BLP p = GET_GUINT32 (BBPTR(f, ptr) + lp*4);
g_array_append_val (f->sb, p);
}
ptr = NEXT_BB (f, ptr);
}
return 1;
}
static int
read_root_list (MS_OLE *f)
ms_ole_setup (MS_OLE *f)
{
/* Find blocks belonging to root ... */
f->header.root_list = read_link_array(f, f->header.root_startblock);
if (!f->header.root_list)
return 0;
return (f->header.root_list->len!=0);
if (read_bb (f) &&
read_pps (f) &&
read_sb (f))
return 1;
return 0;
}
static int
read_sbf_list (MS_OLE *f)
ms_ole_cleanup (MS_OLE *f)
{