#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "logger.h"
#include "pldstr.h"
#include "bytedecoders.h"
#include "olestream-unwrap.h"
#include "ole.h"

	// Main header accessors
#define header_id(x)						((x) +0)
#define header_clid(x)					((x) +0x08)
#define header_minor_version(x)		((x) +0x18)
#define header_dll_version(x)			((x) +0x1a)
#define header_byte_order(x)			((x) +0x1c)
#define header_sector_shift(x)		((x) +0x1e)
#define header_mini_sector_shift(x)	((x) +0x20)
#define header_fat_sector_count(x)	((x) +0x2c)
#define header_properties_start_sector(x)	((x) +0x30)
#define header_mini_cutoff_size(x)	((x) +0x38)
#define header_mini_fat_start(x)		((x) +0x3c)
#define header_mini_fat_sector_count(x)	((x) +0x40)
#define header_dif_start_sector(x)	((x) +0x44)
#define header_dif_sector_count(x)	((x) +0x48)
#define header_fat_sectors(x)			((x) +0x4c)

	//Property Storage accessor macros
#define pps_rawname(x)				((x) +0)
#define pps_sizeofname(x)			((x) +0x40)
#define pps_type(x)					((x) +0x42)
#define pps_previouspps(x)			((x) +0x44)
#define pps_nextpps(x)				((x) +0x48)
#define pps_directorypps(x)		((x) +0x4c)
#define pps_time1seconds(x)		((x) +0x64)
#define pps_time1days(x)			((x) +0x68)
#define pps_time2seconds(x)		((x) +0x6c)
#define pps_time2days(x)			((x) +0x70)
#define pps_propertystart(x)		((x) +0x74)
#define pps_sizeofproperty(x)		((x) +0x78)

	// Type lenghts
#define LEN_BYTE	1
#define LEN_USHORT 2
#define LEN_ULONG	4

	// Directory types
#define STGTY_INVALID 0
#define STGTY_STORAGE 1
#define STGTY_STREAM 2
#define STGTY_LOCKBYTES 3
#define STGTY_PROPERTY 4
#define STGTY_ROOT 5

	// Directory tag colours
#define DE_RED 0
#define DE_BLACK 1



unsigned int DIFSECT		= 0xFFFFFFFC;
unsigned int FATSECT		= 0xFFFFFFFD;
unsigned int ENDOFCHAIN = 0xFFFFFFFE;
unsigned int FREESECT	= 0xFFFFFFFF;

unsigned char OLE_id_v2[]={ 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 };
unsigned char OLE_id_v1[]={ 0x0e, 0x11, 0xfc, 0x0d, 0xd0, 0xcf, 0x11, 0xe0 };


/*-----------------------------------------------------------------\
 Function Name	: OLE_version
 Returns Type	: int
 	----Parameter List
	1. void , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_version( void )
{
	fprintf(stderr,"ripOLE: %s\n", LIBOLE_VERSION);

	return 0;
}


/*-----------------------------------------------------------------\
 Function Name	: OLE_init
 Returns Type	: int
 	----Parameter List
	1. struct OLE_object *ole , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_init( struct OLE_object *ole )
{
	ole->debug = 0;
	ole->verbose = 0;
	ole->quiet = 0;
	ole->filename_report_fn = NULL;
	ole->f = NULL;

	ole->FAT = NULL;
	ole->miniFAT = NULL;
	ole->header_block[0] = '\0';
	ole->ministream = NULL;
	ole->properties = NULL;

	ole->header.sector_shift = 0;
	ole->header.mini_sector_shift = 0;

	return OLE_OK;
}

/*-----------------------------------------------------------------\
 Function Name	: OLE_dir_init
 Returns Type	: int
 	----Parameter List
	1. struct OLE_directory_entry *dir , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_dir_init(struct OLE_directory_entry *dir )
{
	memset(dir->element_name,'\0', OLE_DIRECTORY_ELEMENT_NAME_SIZE);
	dir->element_name_char_count = OLE_DIRECTORY_ELEMENT_NAME_SIZE;

	dir->element_type = 0;
	dir->element_colour = 0;

	dir->left_sibling = 0;
	dir->right_sibling = 0;
	dir->child = 0;

	dir->class[0] = '\0';
	dir->userflags = 0;
	dir->timestamps[0] = '\0';
	dir->start_sector = 0;
	dir->stream_size = 0;

	return 0;
};

/*-----------------------------------------------------------------\
 Function Name	: OLE_set_verbose
 Returns Type	: int
 	----Parameter List
	1. struct OLE_object *ole, 
	2.  int level , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_set_verbose( struct OLE_object *ole, int level )
{
	ole->verbose = level;
	return OLE_OK;
}



/*-----------------------------------------------------------------\
 Function Name	: OLE_set_quiet
 Returns Type	: int
 	----Parameter List
	1. struct OLE_object *ole, 
	2.  int level , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_set_quiet( struct OLE_object *ole, int level )
{
	ole->quiet = level;
	ole->verbose = 0;
	ole->debug = 0;

	return OLE_OK;
}


/*-----------------------------------------------------------------\
 Function Name	: OLE_set_debug
 Returns Type	: int
 	----Parameter List
	1. struct OLE_object *ole, 
	2.  int level , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_set_debug( struct OLE_object *ole, int level )
{
	ole->debug = level;

	return OLE_OK;
}


/*-----------------------------------------------------------------\
 Function Name	: OLE_set_save_unknown_streams
 Returns Type	: int
 	----Parameter List
	1. struct OLE_object  *ole, 
	2.  int level , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_set_save_unknown_streams( struct OLE_object  *ole, int level )
{
	ole->save_unknown_streams = level;

	return OLE_OK;
}



/*-----------------------------------------------------------------\
 Function Name	: OLE_get_block
 Returns Type	: int
 	----Parameter List
	1. struct OLE_object *ole, 
	2.  unsigned int block_index, 
	3.  unsigned char *block_buffer , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_get_block( struct OLE_object *ole, unsigned int block_index, unsigned char *block_buffer )
{
	if (ole->f != NULL)
	{
		int read_count = 0;
		int fseek_result = 0;
		size_t offset = 0;

		offset = (block_index +1) << ole->header.sector_shift;
		fseek_result = fseek(ole->f, offset, SEEK_SET);
		if (fseek_result != 0)
		{
			LOGGER_log("%s:%d:OLE_get_block:ERROR: Seek failure (block=%d:%d)",FL, block_index,offset, strerror(errno));
			return OLEER_GET_BLOCK_SEEK;
		}

		read_count = fread(block_buffer, sizeof(unsigned char), ole->header.sector_size, ole->f);
		if (read_count != ole->header.sector_size)
		{
			printf("Mismatch in bytes read. Requested %d, got %d\n", ole->header.sector_size, read_count);
			return OLEER_GET_BLOCK_READ;
		}
	}

	return OLE_OK;
}


/*-----------------------------------------------------------------\
 Function Name	: OLE_get_miniblock
 Returns Type	: int
 	----Parameter List
	1. struct OLE_object *ole, 
	2.  unsigned int block_index, 
	3.  unsigned char *block_buffer , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_get_miniblock( struct OLE_object *ole, unsigned int block_index, unsigned char *block_buffer )
{
	if (ole->ministream)
	{
		int offset = block_index << ole->header.mini_sector_shift;

		memcpy( block_buffer, ole->ministream +offset, ole->header.mini_sector_size);
	}

	return OLE_OK;
}


/*-----------------------------------------------------------------\
 Function Name	: OLE_dbstosbs
 Returns Type	: int
 	----Parameter List
	1. char *raw_string, 
	2.  size_t char_count, 
	3.  char *clean_string , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_dbstosbs( char *raw_string, size_t char_count, char *clean_string, int clean_string_len )
{
	char *limit = raw_string +char_count -1;

	clean_string_len--;

	while ((raw_string < limit)&&(char_count >0)&&(char_count--)&&(clean_string_len--))
	{
		int v = (char)*raw_string;
		
		if (isprint(v))
		{
			*clean_string = v;
			clean_string++;
		}

		raw_string += 2;
	}
	*clean_string = '\0';

	return OLE_OK;
}

/*-----------------------------------------------------------------\
 Function Name	: OLE_print_string
 Returns Type	: int
 	----Parameter List
	1. char *string, 
	2.  size_t length , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_print_string( char *string, size_t char_count)
{
	while (char_count--)
	{
		printf("%c",*string);
		string += 2;
	}
	return OLE_OK;
}
/*-----------------------------------------------------------------\
 Function Name	: OLE_print_sector
 Returns Type	: int
 	----Parameter List
	1. struct OLE_object *ole, 
	2.  unsigned char *sector , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_print_sector( struct OLE_object *ole, unsigned char *sector, unsigned int bytes)
{
	int current_byte;

	printf("\n");
	for (current_byte = 0; current_byte < bytes; current_byte++ )
	{
		printf("%02X ", *(sector +current_byte));
		if (((current_byte+1) %32)==0) {
			int j;
			for (j = current_byte -31; j <=current_byte; j++)
			{
				if (isalnum(*(sector +j))) printf("%c",*(sector+j));
				else printf(".");
			}
		  	printf("\n");
		}
	}
	printf("\n");

	return OLE_OK;
}

/*-----------------------------------------------------------------\
 Function Name	: OLE_is_OLE_file
 Returns Type	: int
 	----Parameter List
	1. struct OLE_object *ole , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_is_file_OLE( struct OLE_object *ole )
{

	if (memcmp(OLE_id_v1, ole->header_block, sizeof(OLE_id_v1))==0) return 1;
	if (memcmp(OLE_id_v2, ole->header_block, sizeof(OLE_id_v2))==0) return 1;

	return 0;

}

/*-----------------------------------------------------------------\
 Function Name	: OLE_get_header
 Returns Type	: int
 	----Parameter List
	1. struct OLE_object *ole , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_get_header( struct OLE_object *ole )
{
	int result = 0;
	ole->header.sector_size = OLE_HEADER_BLOCK_SIZE;
	result = OLE_get_block( ole, -1, ole->header_block );

	if (OLE_is_file_OLE( ole ) == 0) return OLEER_NOT_OLE_FILE;

	return OLE_OK;
}

/*-----------------------------------------------------------------\
 Function Name	: OLE_convert_header
 Returns Type	: int
 	----Parameter List
	1. struct OLE_object *ole , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_convert_header( struct OLE_object *ole )
{
	struct OLE_header *h;
	unsigned char *hb;
	unsigned char *fat_start;
	int i;

	h = &(ole->header);
	hb = ole->header_block;

	h->minor_version = get_2byte_value(header_minor_version(hb));
	h->dll_version = get_2byte_value(header_dll_version(hb));
	h->byte_order = get_2byte_value(header_byte_order(hb));
	h->sector_shift = get_2byte_value(header_sector_shift(hb));
	h->sector_size = 1 << h->sector_shift;
	h->mini_sector_shift = get_2byte_value(header_mini_sector_shift(hb));
	h->mini_sector_size = 1 << h->mini_sector_shift;
	h->fat_sector_count = get_4byte_value(header_fat_sector_count(hb));
	h->properties_start_sector = get_4byte_value(header_properties_start_sector(hb));
	h->mini_cutoff_size = get_4byte_value(header_mini_cutoff_size(hb));
	h->mini_fat_start = get_4byte_value(header_mini_fat_start(hb));
	h->mini_fat_sector_count = get_4byte_value(header_mini_fat_sector_count(hb));
	h->dif_start_sector = get_4byte_value(header_dif_start_sector(hb));
	h->dif_sector_count = get_4byte_value(header_dif_sector_count(hb));

	fat_start = header_fat_sectors(hb);
	for (i = 0; i < h->fat_sector_count; i++)
	{
		if (i >= OLE_HEADER_FAT_SECTOR_COUNT_LIMIT) break;
		h->FAT[i] = get_4byte_value( fat_start +(LEN_ULONG *i));
	}

	return OLE_OK;
}

/*-----------------------------------------------------------------\
 Function Name	: OLE_print_header
 Returns Type	: int
 	----Parameter List
	1. struct OLE_object *ole , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_print_header( struct OLE_object *ole )
{
	int i;
	struct OLE_header *h;

	h = &(ole->header);

	printf( "Minor version = %d\n"
"DLL version = %d\n"
"Byte order = %d\n\n"

"Sector shift = %d\n"
"Sector size  = %d\n"
"Mini Sector shift = %d\n"
"Mini sector size  = %d\n\n"

"FAT sector count = %d\n"
"First FAT sector = %d\n\n"

"Maximum ministream size = %d\n\n"

"First MiniFAT sector = %d\n"
"MiniFAT sector count = %d\n\n"

"First DIF sector = %d\n"
"DIF sector count = %d\n"
"--------------------------------\n"
			,h->minor_version
			,h->dll_version
			,h->byte_order
			,h->sector_shift
			,h->sector_size
			,h->mini_sector_shift
			,h->mini_sector_size
			,h->fat_sector_count
			,h->properties_start_sector
			,h->mini_cutoff_size
			,h->mini_fat_start
			,h->mini_fat_sector_count
			,h->dif_start_sector
			,h->dif_sector_count
			);

	// Print out the FAT chain
	for (i = 0; i < h->fat_sector_count; i++)
	{
		if (i >= OLE_HEADER_FAT_SECTOR_COUNT_LIMIT) break; // We can't read beyond the 109th sector location
		printf("FAT[%d] = %d\n", i, h->FAT[i]);
	}

	return OLE_OK;
}


/*-----------------------------------------------------------------\
 Function Name	: OLE_convert_directory
 Returns Type	: int
 	----Parameter List
	1. unsigned char *buf, 
	2.  struct OLE_directory_entry *dir , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_convert_directory( unsigned char *buf, struct OLE_directory_entry *dir )
{
	memset( dir->element_name, '\0', OLE_DIRECTORY_ELEMENT_NAME_SIZE);

	memcpy( dir->element_name, buf, OLE_DIRECTORY_ELEMENT_NAME_SIZE );

	dir->element_name_char_count = get_2byte_value( buf + 0x40 );
	dir->element_type = get_1byte_value( buf +0x42 );
	dir->element_colour = get_1byte_value( buf +0x43 );

	dir->left_sibling = get_4byte_value( buf +0x44 );
	dir->right_sibling = get_4byte_value( buf +0x48 );
	dir->child = get_4byte_value( buf +0x4c );

	memcpy( dir->class, buf +0x50, 16 );
	dir->userflags = get_4byte_value( buf +0x60 );

	memcpy( dir->timestamps, buf +0x64, 16 );
	dir->start_sector = get_4byte_value( buf +0x74 );
	dir->stream_size = get_4byte_value( buf +0x78 );

	return OLE_OK;
}

/*-----------------------------------------------------------------\
 Function Name	: OLE_print_directory
 Returns Type	: int
 	----Parameter List
	1. struct OLE *ole, 
	2.  struct OLE_directory_entry *dir , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_print_directory( struct OLE_object *ole, struct OLE_directory_entry *dir )
{
	char element[64];
	OLE_dbstosbs( dir->element_name, dir->element_name_char_count, element, sizeof(element) );

	printf(
"Element Name = %s\n"
"Element type = %d\n"
"Element colour = %d\n"
"Left Sibling = %d\n"
"Right Siblign = %d\n"
"Child = %d\n"
"User flags = %d\n"
"Start sector = %d\n"
"Stream Size = %d\n"
			,element
			,dir->element_type
			,dir->element_colour
			,dir->left_sibling
			,dir->right_sibling
			,dir->child
			,dir->userflags
			,dir->start_sector
			,dir->stream_size
			);

	return OLE_OK;
}


/*-----------------------------------------------------------------\
 Function Name	: OLE_load_FAT
 Returns Type	: int
 	----Parameter List
	1. struct OLE_object *ole , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_load_FAT( struct OLE_object *ole )
{
	unsigned int FAT_size;
	unsigned char *FAT_limit;

	FAT_size = ole->header.fat_sector_count << ole->header.sector_shift;
	if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_load_FAT:DEBUG:Allocating for %d sectors (%d bytes) \n"
			,FL,ole->header.fat_sector_count, FAT_size);

	ole->FAT = malloc( FAT_size *sizeof(unsigned char));
	FAT_limit = ole->FAT +FAT_size;
	if (ole->FAT != NULL)
	{
		int i;
		unsigned char *fat_position = ole->FAT;
		int sector_count = ole->header.fat_sector_count;

		if (sector_count > OLE_HEADER_FAT_SECTOR_COUNT_LIMIT) sector_count = OLE_HEADER_FAT_SECTOR_COUNT_LIMIT;

		// Load in all our primary-FAT sectors from the OLE file
		for (i = 0; i < sector_count; i++)
		{
			int getblock_result = 0;

			getblock_result = OLE_get_block(ole, ole->header.FAT[i], fat_position);
			if (getblock_result != 0)
			{
				// if the get block fails, return the error - but keep the FAT
				//		pointer alive - so that we can facilitate debugging
				//		otherwise the caller is always going to get a NULL pointer
				//		and have no idea to what extent the data was read.
				//
				// This behavior may be changed later - but for now (beta development)
				//		it'll be okay to leave it here - just make sure we know to
				//		free the FAT block later.

				return getblock_result;
			}

			fat_position += ole->header.sector_size;
			if (fat_position > FAT_limit){ printf("FAT boundary limit exceeded %p > %p",fat_position,FAT_limit); exit(1);}
		}
	
		// If our DIF count is > 0, this means we have a pretty big
		//		file on hand (> 7Mb) and thus we now have to do some
		//		fancy double-dereferenced sector request - enough to 
		//		twist your brain if you're not alert, you have been
		//		warned.

		if (ole->header.dif_sector_count > 0)
		{
			unsigned char *fat_block = malloc( ole->header.sector_size );
			unsigned char *fat_block_end;
			unsigned int current_sector = ole->header.dif_start_sector;

			if (fat_block == NULL) 
			{
				printf("Unable to allocate %d bytes\n", ole->header.sector_size); fflush(stdout);
				exit(1);
			}

			// We need to know where the end of this block is - because it's
			//		used to show us where the NEXT FAT block is going to come from
			//		NOTE - this only occurs if we do have another block, else 
			//		we'll simply have to just realise that we don't need any more
			//		blocks and stop with this one.

			fat_block_end = fat_block +ole->header.sector_size -LEN_ULONG;

			// We know we've got 'dif_sector_count' blocks to read, each of
			//		these blocks hold no more than 127 sector addresses which
			//		contain the actual FAT data we're after (this is the double
			//		dereference bit that twists your brain )

			for (i = 0; i < ole->header.dif_sector_count; i++)
			{
				int import_sector;
				unsigned char *DIF = fat_block;
				int tick = 0;
				int getblock_result;

				if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: Reading DIF/XBAT index-data[%d] from sector %d",FL,i,current_sector);
				getblock_result = OLE_get_block(ole, current_sector, fat_block);
				if (getblock_result != OLE_OK)
				{
					return getblock_result;
				}

				if (OLE_DPEDANTIC(ole->debug)) OLE_print_sector(ole, fat_block, ole->header.sector_size);

				// Now, traverse this block until we hit a < 0
				//		If we get what is a non-valid sector value
				//		we know we've reached the end of the valid
				//		sectors from which to read more extended FAT
				//		data.

				do {
					import_sector = get_4byte_value( DIF );
					if (import_sector >= 0)
					{
						if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: Reading DIF/XBAT-data[%d] from sector %d",FL,tick,current_sector);
						getblock_result = OLE_get_block(ole, import_sector, fat_position);
						if (getblock_result != OLE_OK)
						{
							return getblock_result;
						}

						fat_position += ole->header.sector_size;
						if (fat_position > FAT_limit)
						{ 
							LOGGER_log("%s:%d:OLE_load_FAT:FAT memory boundary limit exceeded %p > %p",FL,fat_position,FAT_limit); 
							return OLEER_MEMORY_OVERFLOW;
						}
						tick++;
						DIF += LEN_ULONG;
					}
				} while ((import_sector >= 0)&&(DIF < fat_block_end));

				// Get the next sector of DIF/XBAT data ...
				//
				// If we still have more sectors full of extended FAT
				//		sectors that we have to read, then we neet to
				//		obtain the address of the next FAT-sector filled
				//		sector

				if ( i < ole->header.dif_sector_count -1 )
				{
					current_sector = get_4byte_value( fat_block_end );
					if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_load_FAT:DEBUG: Next DIF/XBAT index sector located at %d",FL,current_sector);
					if (current_sector < 0) break;
				}
			} // For every DIF/XBAT sector we're supposed to read

			if (fat_block) free(fat_block);
		} // If we have DIF/XBAT sectors to read into the FAT

	} // If we managed to allocate memory for our FAT table

	return OLE_OK;
}



/*-----------------------------------------------------------------\
 Function Name	: OLE_follow_chain
 Returns Type	: int
 	----Parameter List
	1. struct OLE_object *ole, 
	2.  int FAT_sector_start , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_follow_chain( struct OLE_object *ole, int FAT_sector_start )
{
	int current_sector = FAT_sector_start;
	int chain_length=0;

	if (FAT_sector_start < 0) return 0;

	if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_follow_chain:DEBUG: Starting chain follow at sector %d",FL, FAT_sector_start );
	do {
		int next_sector;

		next_sector = get_4byte_value( ole->FAT +(LEN_ULONG *current_sector));

		if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_follow_chain:DEBUG: 0x%0X:%d)->(0x%0X:%d)\n",FL, current_sector, current_sector, next_sector, next_sector);
//		fflush(stdout);
		current_sector = next_sector;
		chain_length++;
	} while ((current_sector != ENDOFCHAIN)&&(current_sector >= 0));

	return chain_length;
}

/*-----------------------------------------------------------------\
 Function Name	: OLE_follow_minichain
 Returns Type	: int
 	----Parameter List
	1. struct OLE_object *ole, 
	2.  int FAT_sector_start , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_follow_minichain( struct OLE_object *ole, int miniFAT_sector_start )
{
	unsigned int current_sector = miniFAT_sector_start;
	int chain_length=0;

	do {
		unsigned int next_sector;

		next_sector = get_4byte_value( ole->miniFAT +(LEN_ULONG *current_sector));

//		printf("Current Msector(0x%0X:%d)->next(0x%0X:%d)\n", current_sector, current_sector, next_sector, next_sector);
//		fflush(stdout);
		current_sector = next_sector;
		chain_length++;
	} while (current_sector != ENDOFCHAIN);

	return chain_length;
}

/*-----------------------------------------------------------------\
 Function Name	: char
 Returns Type	: unsigned
 	----Parameter List
	1. *OLE_load_minichain( struct OLE_object *ole, 
	2.  int FAT_sector_start , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
PLD:2003-Aug-28: Added sanity checking on the miniFAT_sector_start 
	value so that we didn't try to load up a miniFAT starting on a 
	negative value
 
\------------------------------------------------------------------*/
unsigned char *OLE_load_minichain( struct OLE_object *ole, int miniFAT_sector_start )
{

	int chain_length = 0;
	int current_sector = miniFAT_sector_start;
	unsigned char *buffer;
	unsigned char *bp;

	// Added this sanity checking 2003 Aug 28
	if (miniFAT_sector_start < 0) return NULL;

//	printf("Loading chain starting at miniFAT sector %d\n", miniFAT_sector_start);

	chain_length = OLE_follow_minichain( ole, miniFAT_sector_start );
//	printf("Found %d mini-sectors to load (%d bytes)\n", chain_length, chain_length *ole->header.mini_sector_size);
	bp = buffer = malloc( chain_length *ole->header.mini_sector_size *sizeof(unsigned char));
	if (buffer != NULL)
	{
		do {
			int next_sector;

//			printf("Loading sector %d,", current_sector); fflush(stdout);
			OLE_get_miniblock( ole, current_sector, bp );
			bp += ole->header.mini_sector_size;
			
			next_sector = get_4byte_value( ole->miniFAT +(LEN_ULONG *current_sector));
			current_sector = next_sector;
//			printf(" next sector == %d\n",current_sector); fflush(stdout);
		} while (current_sector != ENDOFCHAIN);
//		printf("Done loading sectors for this mini-chain\n");
	} else {
		printf("Failed to allocate enough memory for miniChain\n");
	}

//	printf("load miniChain done\n");
//	fflush(stdout);

	return buffer;
}


/*-----------------------------------------------------------------\
 Function Name	: char
 Returns Type	: unsigned
 	----Parameter List
	1. *OLE_load_chain( struct OLE_object *ole, 
	2.  int FAT_sector_start , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
unsigned char *OLE_load_chain( struct OLE_object *ole, int FAT_sector_start )
{

	int chain_length = 0;
	int current_sector = FAT_sector_start;
	unsigned char *buffer = NULL;
	unsigned char *bp = NULL;


	if (FAT_sector_start < 0) return NULL;

	if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_load_chain:DEBUG: Loading chain, starting at sector %d",FL,FAT_sector_start);

	chain_length = OLE_follow_chain( ole, FAT_sector_start );
	if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_load_chain:DEBUG: %d sectors need to be loaded",FL,chain_length);

	if (chain_length > 0)
	{
		size_t offset;

		offset = chain_length << ole->header.sector_shift;
		bp = buffer = malloc( offset *sizeof(unsigned char));
		if (buffer != NULL)
		{
			int tick = 0;
			unsigned char *bp_limit;
			bp_limit = bp +offset;

			do {
				int next_sector;
	
				if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_load_chain:DEBUG: Loading sector[%d] %d",FL, tick, current_sector );

				ole->error = OLE_get_block( ole, current_sector, bp );
				if (ole->error != OLE_OK) return NULL;

				bp += ole->header.sector_size;
				if (bp > bp_limit) { LOGGER_log("%s:%d:OLE_load_chain:ERROR: Load-chain went over memory limit",FL); exit(1); };
				
				next_sector = get_4byte_value( ole->FAT +(LEN_ULONG *current_sector));
				current_sector = next_sector;
				tick++;
			} while ((current_sector != ENDOFCHAIN)&&(current_sector >= 0));
		}
	}
	if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_load_chain:DEBUG: Done loading chain",FL);

	return buffer;
}

/*-----------------------------------------------------------------\
 Function Name	: OLE_open_file
 Returns Type	: int
 	----Parameter List
	1. struct OLE_object *ole, 
	2.  char *fullpath , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_open_file( struct OLE_object *ole, char *fullpath )
{
	struct stat st;
	int stat_result;
	FILE *f;

	stat_result = stat(fullpath, &st);
	if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_open_file:DEBUG: File size of %s = %ld",FL, fullpath, st.st_size);
	if ((stat_result == 0) && (st.st_size < 512)) return OLEER_NOT_OLE_FILE;

	f = fopen(fullpath,"r");
	if (f == NULL)
	{
		ole->f = NULL;
		if (ole->quiet == 0)
		{
			LOGGER_log("%s:%d:OLE_open_file:ERROR:Cannot open %s for reading (%s)\n",FL,fullpath, strerror(errno));
		}
		return -1;
	} else {
		ole->f = f;
	}
	return OLE_OK;
}


/*-----------------------------------------------------------------\
 Function Name	: OLE_open_directory
 Returns Type	: int
 	----Parameter List
	1. struct OLE_object *ole, 
	2.  char *directory , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_open_directory( struct OLE_object *ole, char *directory )
{
	int result=0;

	result = mkdir( directory, S_IRWXU );
	if ((result != 0)&&(errno != EEXIST))
	{
		LOGGER_log("%s:%d:OLE_open_directory:ERROR: %s",FL,strerror(errno));
	} else result = OLE_OK;

	return result;
}


/*-----------------------------------------------------------------\
 Function Name	: OLE_set_filename_report_fn
 Returns Type	: int
 	----Parameter List
	1. int (*ptr_to_fn)(char *) , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
	This is merely a passthrough function to the OLEUW one, we do
	this in order to avoid having to force the calling parent from
	having to #include the OLEUW headers as well
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_set_filename_report_fn( struct OLE_object *ole, int (*ptr_to_fn)(char *) )
{
	ole->filename_report_fn = ptr_to_fn;
	return OLE_OK;
}


/*-----------------------------------------------------------------\
 Function Name	: OLE_store_stream
 Returns Type	: int
 	----Parameter List
	1. struct OLE_object *ole, 
	2.  char *stream_name, 
	3.  char *directory, 
	4.  unsigned char *stream , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_store_stream( struct OLE_object *ole, char *stream_name, char *directory, char *stream, size_t stream_size )
{
	char *full_path;

	full_path = PLD_dprintf("%s/%s", directory, stream_name);
	if (full_path == NULL)
	{
		LOGGER_log("%s:%d:OLE_store_stream:ERROR: Cannot compose full filename string from '%s' and '%s'", FL, directory, stream_name);
		return -1;
	} else {
		FILE *f;

		f = fopen(full_path,"w");
		if (f == NULL)
		{
			LOGGER_log("%s:%d:OLE_store_stream:ERROR: Cannot open %s for writing (%s)",FL, full_path, strerror(errno));
			return -1;
		} else {
			size_t written_bytes;

			written_bytes = fwrite( stream, 1, stream_size, f );
			if (written_bytes != stream_size)
			{
				LOGGER_log("%s:%d:OLE_store_stream:WARNING: Only wrote %d of %d bytes to file %s",FL,written_bytes,stream_size,full_path);
			}
			fclose(f);

			if ((OLE_VNORMAL(ole->verbose))&&(ole->filename_report_fn != NULL))
			{
				ole->filename_report_fn( stream_name );
			}
		} // if file is valid
	} // if full_path is valid

	return OLE_OK;
}


/*-----------------------------------------------------------------\
 Function Name	: OLE_decode_file_done
 Returns Type	: int
 	----Parameter List
	1. struct OLE_object *ole , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_decode_file_done( struct OLE_object *ole )
{
	if(ole->f) fclose(ole->f);
	if (ole->FAT) free(ole->FAT);
	if (ole->miniFAT) free(ole->miniFAT);
	if (ole->ministream) free(ole->ministream);
	if (ole->properties) free(ole->properties);

	return 0;
}

/*-----------------------------------------------------------------\
 Function Name	: OLE_decode_file
 Returns Type	: int
 	----Parameter List
	1. char *fname, 
	2.  char *decode_path , 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
int OLE_decode_file( struct OLE_object *ole, char *fname, char *decode_path )
{
	unsigned char *current_property;
	int result = 0;
	int i;

	// Reject any bad paramters.
	if (ole == NULL) return OLEER_DECODE_NULL_OBJECT;
	if (fname == NULL) return OLEER_DECODE_NULL_FILENAME;
	if (decode_path == NULL) return OLEER_DECODE_NULL_PATH;

	// We need to gain access to the OLE2 data file, without
	//		this pretty much everything is pointless.
	if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_decode_file:DEBUG: opening %s", FL, fname );
	result = OLE_open_file( ole, fname );
	if (result != 0) return result;

	// Try create the output directory which we're using
	//		to write the decoded files out to.
	if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_decode_file:DEBUG: opening output directory %s", FL, decode_path);
	result = OLE_open_directory( ole, decode_path );
	if (result != 0) return result;

	// In order to successfully decode an OLE2 stream, we have to read
	//		and understand the first 512 bytes of the file, this is the 
	//		OLE2 header. 
	if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Getting main header", FL);
	result = OLE_get_header( ole );
	if (result != 0) return result;

	if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Converting main header", FL);
	result = OLE_convert_header( ole );
	if (result != 0) return result;

	if (OLE_DPEDANTIC(ole->debug)) OLE_print_header( ole );

	if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Loading FAT", FL);
	result = OLE_load_FAT( ole );
	if (result != 0) return result;

	if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Loading miniFAT chain", FL);
	ole->miniFAT = OLE_load_chain( ole, ole->header.mini_fat_start );
	if (ole->miniFAT == NULL) return OLEER_MINIFAT_READ_FAIL;

	if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Loading properties chain", FL);
	ole->properties = OLE_load_chain( ole, ole->header.properties_start_sector );
	if (ole->properties == NULL) return OLEER_PROPERTIES_READ_FAIL;

	i=0;
	current_property = ole->properties;
	while (1)
	{
		struct OLE_directory_entry a_dir_object, *adir;
		int property_value=0;

		adir = &a_dir_object;

		OLE_dir_init(adir);

		property_value = get_1byte_value(current_property);
		if (property_value < 1) break;

		if (OLE_DNORMAL(ole->debug)){ LOGGER_log("%s:%d:OLE_decode_file:DEBUG:---------INDEX: %d",FL,i);}

		OLE_convert_directory( current_property, adir );

		if (OLE_DNORMAL(ole->debug)) { OLE_print_directory( ole, adir);}

		if (adir->element_colour > 1) break;

		if ((adir->element_type == STGTY_INVALID)||(adir->element_type > STGTY_ROOT))
		{
			LOGGER_log("%s:%d:OLE_decode_file:DEBUG: breaking out due to element type %d",FL, adir->element_type);
			break;

		} else if (adir->element_type == STGTY_ROOT){
			if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Loading ministream/SmallBlockArray",FL);
			ole->ministream = OLE_load_chain( ole, adir->start_sector );
			if (ole->ministream == NULL) return OLEER_MINISTREAM_READ_FAIL;
			if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_decode_file:DEBUG: ministream done",FL);


		} else if (adir->element_type == STGTY_STORAGE) {
//			printf(">>>Item is directory, start child is at index %d\n",dir.child);


		} else if (adir->element_type == STGTY_STREAM) {
			char element_name[64];

			memset(element_name, '\0', 64);

			if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Decoding stream property...",FL);
			OLE_dbstosbs( adir->element_name, adir->element_name_char_count, element_name, 64 );
			if(1)
			{
				char *stream_data;
				struct OLEUNWRAP_object oleuw;
				int decode_result = OLEUW_STREAM_NOT_DECODED;

				OLEUNWRAP_init(&oleuw);
				OLEUNWRAP_set_debug(&oleuw,ole->debug);
				OLEUNWRAP_set_verbose(&oleuw,ole->verbose);
				OLEUNWRAP_set_filename_report_fn(&oleuw, ole->filename_report_fn);
				OLEUNWRAP_set_save_unknown_streams(&oleuw, ole->save_unknown_streams);
				if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Unwrap engine set.",FL);

				if (adir->stream_size >= ole->header.mini_cutoff_size)
				{
					stream_data = OLE_load_chain( ole, adir->start_sector );
					if (stream_data == NULL)
					{
						OLE_decode_file_done(ole);
						return OLEER_MINISTREAM_STREAM_READ_FAIL;
					}
					if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Normal decode START.",FL);
					decode_result = OLEUNWRAP_decodestream( &oleuw, element_name, stream_data, adir->stream_size, decode_path );
					if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Normal decode done.",FL);
				} else {
					stream_data = OLE_load_minichain( ole, adir->start_sector );
					if (stream_data == NULL)
					{
						OLE_decode_file_done(ole);
						return OLEER_NORMALSTREAM_STREAM_READ_FAIL;
					}
					if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Mini decode START.",FL);
					decode_result = OLEUNWRAP_decodestream( &oleuw, element_name, stream_data, adir->stream_size, decode_path );
					if (OLE_DNORMAL(ole->debug)) LOGGER_log("%s:%d:OLE_decode_file:DEBUG: Mini decode done.",FL);
				}

				if ((stream_data != NULL)&&(decode_result == OLEUW_STREAM_NOT_DECODED)&&(ole->save_unknown_streams))
				{
					char *fname;
					
					fname = PLD_dprintf("ole-stream.%d",i);
					if (fname != NULL)
					{
						OLE_store_stream( ole, fname, decode_path, stream_data, adir->stream_size );
						free(fname);
					} 
				} // If we needed to save an unknown stream

				// Clean up an stream_data which we may have 
				// read in from the chain-loader.
				if (stream_data) free(stream_data);

			} // if 1
		} else {
			// If we don't know what this is, then we break.
			break;
		}
		  	// if the OLE directory entry is a 'STREAM' type.

		// Jump to the next property record, which
		//		is always 128 bytes ahead.
		current_property += 128;
		i++;

	} // While there are still more directory entries to read in.


	if(ole->f) fclose(ole->f);
	if (ole->FAT) free(ole->FAT);
	if (ole->miniFAT) free(ole->miniFAT);
	if (ole->ministream) free(ole->ministream);
	if (ole->properties) free(ole->properties);

	return OLE_OK;
}
