/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * vsdump: test program to dump and parse content of vsd file
 *
 * Copyright (C) 2006-2007	Valek Filippov (frob@df.ru)
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 3 or later of the GNU General Public
 * License as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA
 *
 */
#include <gsf/gsf-input.h>		
#include <gsf/gsf-input-proxy.h>		
#include <gsf/gsf-utils.h>	
#include "vsd_inflate.h"
#include "vsdump.h"
#include "vsd_pointers.h"

/* define DEBUG_PNTRS */

static int
vsd_pointersv5_search (GByteArray *decomp, GArray *pointers, guint32 parent, guint32 type)
{
	unsigned int 			i;
	int			offset;
	strm_ptr		stp, *sp;
	guint32		data;
	guint16		data2,nump;
	
	sp = &stp;
    g_printf ("vsd_pointersv5_search... type: %x\n",type);   
   switch (type){ /* type */
   case 0x14:
   	offset = 0x88;
   	break;
   case 0x1d:
   case 0x4e:
   	offset = 0x24;
   	break;
  	case 0x1e:
   	offset = 0x3c;
   	break;
   case 0x27:
   case 0x3f:
   case 0x44:
   	offset = 0x10;	
   	break;
   default:
    	offset = 0x10; 
   }
    nump = GSF_LE_GET_GUINT16(decomp->data + offset - 2);
	for (i = 0; i < nump; i++) {
		memcpy(&data2, decomp->data + i*16+offset, 2);	
		sp->type = GSF_LE_GET_GUINT16(&data2);
		memcpy(&data2, decomp->data + i*16+offset + 2, 2);	
		sp->frmt = GSF_LE_GET_GUINT16(&data2);		
		memcpy(&data, decomp->data + i*16+offset + 4, 4);
		sp->addr = GSF_LE_GET_GUINT32(&data);
		memcpy(&data, decomp->data + i*16+offset + 8, 4);
		sp->offset = GSF_LE_GET_GUINT32(&data);
		memcpy(&data, decomp->data + i*16+offset + 12, 4);	
		sp->len = GSF_LE_GET_GUINT32(&data);
		sp->prnt = parent;	
		g_array_append_vals(pointers, sp, 1);
	}
return 0;
}

static int
vsd_pointers_search (GByteArray *decomp, GArray *pointers, guint32 parent)
{
	int 			i, size, offset;
	strm_ptr		stp, *sp;
	guint32		data;
	guint16		data2;
	
	sp = &stp;
	offset = GSF_LE_GET_GUINT32(decomp->data + 4) + 4; 	/* an offset to the number of pointers */
	size = 18*GSF_LE_GET_GUINT32(decomp->data + offset);  /* 18 is a length of a pointer as it stored in the file */
	offset = offset + 8;

	for (i = offset; i < (offset + size); i = i + 18) {
		if(GSF_LE_GET_GUINT32(decomp->data + i))
		{	
		memcpy(&data, decomp->data + i, 4);	
		sp->type = GSF_LE_GET_GUINT32(&data);
		memcpy(&data, decomp->data + i + 4, 4);	
		sp->addr = GSF_LE_GET_GUINT32(&data);
		memcpy(&data, decomp->data + i + 8, 4);	
		sp->offset = GSF_LE_GET_GUINT32(&data);
		memcpy(&data, decomp->data + i + 12, 4);	
		sp->len = GSF_LE_GET_GUINT32(&data);
		memcpy(&data2, decomp->data + i + 16, 2);	
		sp->frmt = GSF_LE_GET_GUINT16(&data2);
		sp->prnt = parent;	
		g_array_append_vals(pointers, sp, 1);
		}
	}
return 0;
}

/* ------------------------------------------------------------------------- */

int
vsd_pointers_print (GArray *pointers, FILE *fp)
{
	guint			i, found=0;
	strm_ptr		*sp;	
	
	fprintf(fp,"List of all pointers from VisioDocument\n");
	fprintf(fp,"=======================================\n");
	
	for (i = 0; i < pointers->len; i++) {
			sp = &g_array_index(pointers, strm_ptr, i);
			fprintf (fp, "Type: %02x\tAddr: %08x\tOffset: %x\tLen: %4x\tFormat: %02x\tFrom: %x\n",
								sp->type,  sp->addr, sp->offset,  sp->len,    sp->frmt,   sp->prnt);
			found+=sp->len;
	}
	
	/* here, 18 -- the length of the pointer to trailer */
	g_print("%x bytes of the file were covered.\n", found+TRLR_OFFSET+18);

	return 0;
}

/* ------------------------------------------------------------------------- */
/**
 * vsd_pointers_all:
 * Collect pointers
 * 
 **/
GArray *
vsd_pointers_collect (GsfInput *member, int version)
{
	GByteArray 	*decomp=NULL;   	/* temporary storage for decompressed streams */
	GArray 		*pointers; 			
	GsfInput		*input_pointers;	/* temporary storage for part of GsfInput to inflate from */
	guint32 		parent=TRLR_OFFSET;
	strm_ptr		strmp, *sp;	
	guint32		data;
	unsigned int	i;
	const guint8  *d = {0};			

	sp = &strmp;	
	pointers = g_array_new(FALSE, FALSE, sizeof(strmp));

/* extract pointer to trailer */
	if(5 < version){ 
		gsf_input_seek (member, TRLR_OFFSET, G_SEEK_SET);
		d = gsf_input_read (member, 4, NULL);
		sp->type = GSF_LE_GET_GUINT32(d);
		d = gsf_input_read (member, 4, NULL);
		sp->addr = GSF_LE_GET_GUINT32(d);
		d = gsf_input_read (member, 4, NULL);
		sp->offset = GSF_LE_GET_GUINT32(d);
		d = gsf_input_read (member, 4, NULL);
		sp->len = GSF_LE_GET_GUINT32(d);
		d = gsf_input_read (member, 2, NULL);
		sp->frmt = GSF_LE_GET_GUINT16(d);
		sp->prnt = parent;
	}else{
		gsf_input_seek (member, TRLR_OFFSET, G_SEEK_SET);
		d = gsf_input_read (member, 2, NULL);
		sp->type = GSF_LE_GET_GUINT16(d);
		d = gsf_input_read (member, 2, NULL);
		sp->frmt = GSF_LE_GET_GUINT16(d);
		d = gsf_input_read (member, 4, NULL);
		sp->addr = GSF_LE_GET_GUINT32(d);
		d = gsf_input_read (member, 4, NULL);
		sp->offset = GSF_LE_GET_GUINT32(d);
		d = gsf_input_read (member, 4, NULL);
		sp->len = GSF_LE_GET_GUINT32(d);
		sp->prnt = parent;
	}
	g_array_append_vals(pointers, sp, 1);

/* start to extract pointers from trailer stream and deeper */
	for (i = 0; i < pointers->len; i++) {
	sp = &g_array_index(pointers, strm_ptr, i);
	
#ifdef DEBUG_PNTRS
g_warning ("Type: %02x\tAddr: %08x\tOffset: %x\tLen: %4x\tFormat: %02x\tFrom: %x",
								sp->type,  sp->addr, sp->offset,  sp->len,    sp->frmt,   sp->prnt);		
#endif /* DEBUG_PNTRS */

		input_pointers = gsf_input_proxy_new_section(member, (gsf_off_t) sp->offset, (gsf_off_t) sp->len);
			
		if(5 == (sp->frmt>>4) &&(0x16 != sp->type)){ /* 0x16 is a ColorEntries stream  */
			/* pointers to non-compressed streams with pointers, like some of Visio 2k3 format == 0x5? and format&2 == 0 */
			if(!(sp->frmt&2)){
				decomp = g_byte_array_new ();
				/* prepend with 4 bytes -- length of 'decompressed' stream. */
				data = sp->len + 4;
				g_byte_array_append(decomp,(guint8 *) &data, 4);
				g_byte_array_append(decomp, gsf_input_read(input_pointers, sp->len, NULL), sp->len);
			}else{
			/* pointers to compressed streams with pointers, like some of Visio 2k */
				if(NULL == (decomp = vsd_inflate(input_pointers, 0))) return NULL;
			}
		
			if(5 < version){
				vsd_pointers_search(decomp, pointers, sp->offset);
			}else{
				vsd_pointersv5_search(decomp, pointers, sp->offset, sp->type);
			}
		}
		if(decomp->len) g_byte_array_free(decomp, TRUE);
	}	

	return pointers;		
}
