/* 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 <glib.h>
#include <glib/gstdio.h>
#include <gsf/gsf-input.h>
#include "vsdump.h"
#include "vsd_inflate.h"
#include "vsd_pointers.h"

/**
 * vsd_inflate:
 * @input: stream to read from
 * @offset: offset into it for start byte of compressed stream
 * Decompresses an LZ compressed stream.
 * 
 * Return value: A GByteArray that the caller is responsible for freeing
 **/
GByteArray *
vsd_inflate (GsfInput *input, gsf_off_t offset)
{
	GByteArray 		*res; 		   	
	unsigned			i, pos = 0; 	   
	unsigned			mask;				   
	guint8			flag, buffer [4096] = {0};
	guint8	const *tmp = 0;
	guint8			addr1, addr2, data;
	guint16			len, pntr;

	if (gsf_input_seek (input, offset, G_SEEK_SET))
		return NULL;

	res = g_byte_array_new ();
	while (NULL != gsf_input_read (input, 1, &flag))
	{
		for (mask = 1; mask < 0x100 ; mask <<= 1)
		{
			if (flag & mask){
			  	
				if (NULL != (tmp = gsf_input_read (input, 1, buffer + (pos & 4095))))
				{
					pos++;
					g_byte_array_append(res, tmp, 1);
				}
			} else {  		
					if (NULL == (tmp = gsf_input_read (input, 1, NULL)))
					break;	
				
					addr1 = *tmp;	
								
					if (NULL == (tmp = gsf_input_read (input, 1, NULL)))
					break;	
				
					addr2 = *tmp;
			
					len = (addr2 & 15) + 3;
					pntr = (addr2 & 240)*16 + addr1;
					
					if(pntr > 4078)
					{
						pntr = pntr - 4078;
					} else {
						pntr = pntr + 18;
					}
					
					
					for (i = 0; i < len; i++)
					{
						buffer [(pos + i) & 4095] = buffer [(pntr + i) & 4095];
						data = buffer[(pntr + i ) & 4095];
						g_byte_array_append(res, &data, 1);
					}
					
					pos = pos + len;
			}	
				if(0 == gsf_input_remaining(input))
				break;
		}
	}
	return res;
}
