/* A program to dump names from a MS-Windows executable

   Corey Minyard
   minyard@wf-rch.cirr.com
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/file.h>

#include "msw.h"
#include "exe.h"
#include "exedat.h"

BYTE data_block[0x10000];


void
print_sized_name(char *name, int minsize)
{
	char buff[21];
	unsigned int size;
	unsigned int offset;

	size = *((unsigned char *) name);
	offset = 0;
	name++;
	while (size > 20)
	{
		memcpy(buff, name, 20);
		buff[20] = '\0';
		printf("%s", buff);
		name = name + 20;
		minsize = minsize - 20;
		size = size - 20;
	}

	memcpy(buff, name, size);
	buff[size] = '\0';
	minsize = minsize - size;
	printf("%s", buff);

	while (minsize > 0)
	{
		printf(" ");
		minsize--;
	}
}
	

int
seek_n_read(
	int file, 
	void *data,
	int offset,
	int size,
	int exact_size)
{
	int retval;


	if (lseek(file, offset, 0) < 0)
	{
		perror("Unable to seek");
		retval = -1;
	}
	else if (read(file, data, size) != size)
	{
		if ((size < 0) || (exact_size))
		{
			perror("Unable to read");
			retval = -1;
		}
		else
		{
			retval = size;
		}
	}
	else
	{
		retval = size;
	}

	return(retval);
}


int
load_entry_table(
	struct s_exe_data *exe_data,
	char	*filename,
	int	file,
	struct s_info_block *info_block,
	int	header_off)
{
	struct s_bundle_data	*bundle;
	struct s_entry_table_movable	*movable;
	struct s_entry_table_fixed	*fixed;
	struct s_entry_table_const	*cond;
	struct s_entry_table	*entry_table;
	struct s_resname_table	*names;
	int	count;
	int	i;
	int	j;
	int	retval;
	int	found;

	
	if (seek_n_read(file, 
			data_block,
			info_block->entry_table_off + header_off,
			info_block->entry_table_length,
			TRUE) < 0)
	{
		printf("%s: could not seek or read\n", filename);
		retval = -1;
	}
	else
	{
		bundle = (struct s_bundle_data *) data_block;
		count = 0;
		while (bundle->num_entries != 0)
		{
			if (bundle->type == 0)
			{
				count = count + bundle->num_entries;
				bundle++;
			}
			else if (bundle->type == MOVABLE_SEG)
			{
				count = count + bundle->num_entries;
				bundle = (struct s_bundle_data *)
					 (  ((char *) bundle) +  
					    sizeof(*bundle)
				          + (  bundle->num_entries 
					     * sizeof(*movable)));
			}
			else if (bundle->type != CONST_ENTRY)
			{
				count = count + bundle->num_entries;
				bundle = (struct s_bundle_data *)
					 (  ((char *) bundle) +  
					    sizeof(*bundle)
				          + (  bundle->num_entries 
					     * sizeof(*fixed)));
			}
			else
			{
				count = count + bundle->num_entries;
				bundle = (struct s_bundle_data *)
					 (  ((char *) bundle) +  
					    sizeof(*bundle)
				          + (  bundle->num_entries 
					     * sizeof(*cond)));
			}
		}
		printf("names[%d] =\n{\n", count);

		entry_table = malloc((count+1) * sizeof(*entry_table));
		bundle = (struct s_bundle_data *) data_block;
		count = 1;
		while ((bundle->num_entries != 0) && (entry_table != NULL))
		{
			if (bundle->type == 0)
			{
				for (i=0; i<bundle->num_entries; i++)
				{
					entry_table[count].type = bundle->type;
					count++;
				}
				bundle++;
			}
			else if (bundle->type == MOVABLE_SEG)
			{
				movable = (struct s_entry_table_movable *) (bundle+1);
				for (i=0; i<bundle->num_entries; i++)
				{
					entry_table[count].type = bundle->type;
					entry_table[count].u.seg.flags = movable->flags;
					entry_table[count].u.seg.int3f = *((WORD *) movable->int3f);
					entry_table[count].u.seg.seg_num = movable->seg_num;
					entry_table[count].u.seg.seg_off = movable->seg_off;
					count++;
					movable++;
				}

				bundle = (struct s_bundle_data *)
					 (  ((char *) bundle) +  
					    sizeof(*bundle)
				          + (  bundle->num_entries 
					     * sizeof(*movable)));
			}
			else if (bundle->type != CONST_ENTRY)
			{
				fixed = (struct s_entry_table_fixed *) (bundle+1);
				for (i=0; i<bundle->num_entries; i++)
				{
					entry_table[count].type = bundle->type;
					entry_table[count].u.seg.flags = fixed->flags;
					entry_table[count].u.seg.seg_off = *((WORD *) fixed->seg_off);
					count++;
					fixed++;
				}

				bundle = (struct s_bundle_data *)
					 (  ((char *) bundle) +  
					    sizeof(*bundle)
				          + (  bundle->num_entries 
					     * sizeof(*fixed)));
			}
			else
			{
				cond = (struct s_entry_table_const *) (bundle+1);
				for (i=0; i<bundle->num_entries; i++)
				{
					entry_table[count].type = bundle->type;
					memcpy(entry_table[count].u.data, cond->data, sizeof(cond->data));
					count++;
					cond++;
				}

				bundle = (struct s_bundle_data *)
					 (  ((char *) bundle) +  
					    sizeof(*bundle)
				          + (  bundle->num_entries 
					     * sizeof(*cond)));
			}
		}

		if (entry_table != NULL)
		{
			for (i=1; i<count; i++)
			{
				found = 0;
				names = exe_data->resnames;
				for (j=0; j<exe_data->num_resnames; j++)
				{
					if (names[j].index == i)
					{
						found = 1;
						printf(" /* %3.3d */ \"", i);
						print_sized_name(names[j].name, 0);
						printf("\",\n");
					}
				}
				names = exe_data->nonresnames;
				for (j=0; j<exe_data->num_nonresnames; j++)
				{
					if (names[j].index == i)
					{
						found = 1;
						printf(" /* %3.3d */ \"", i);
						print_sized_name(names[j].name, 0);
						printf("\",\n");
					}
				}
				if (!found)
				{
					printf(" /* %3.3d */ NULL,\n", i);
				}
			}

			printf("};\n");
		}

		exe_data->entries = entry_table;
		exe_data->num_entries = count;
		retval = 0;
	}

	return retval;
}


int
load_resname_table(
	struct s_exe_data *exe_data,
	char	*filename,
	int	file,
	struct s_info_block *info_block,
	int	header_off)
{
	struct s_resname_table	*resnames;
	char			*resname_strings;
	int	count;
	int	num;
	int	i;
	int	retval;


	if (seek_n_read(file, 
			data_block,
			info_block->res_name_table_off + header_off,
			info_block->mod_ref_table_off - info_block->res_name_table_off + 1,
			TRUE) < 0)
	{
		fprintf(stderr, "Unable to get resident name data");
		retval = -1;
	}
	else
	{
		count = 0;
		num = 0;
		i = 0;
		while (data_block[i] != 0)
		{
			count = count + *((unsigned char *) &(data_block[i])) + 1;
			i = i + *((unsigned char *) &(data_block[i])) + 3;
			num++;
		}

		resnames = malloc(num * sizeof(struct s_resname_table));
		exe_data->resnames = resnames;
		exe_data->num_resnames = num;
		resname_strings = malloc(count);
		exe_data->resname_strings = resname_strings;

		num = 0;
		i = 0;
		while (data_block[i] != 0)
		{
			resnames[num].name = resname_strings;
			*resname_strings = data_block[i];

			resname_strings++;
			memcpy(resname_strings,
			       &(data_block[i+1]), 
			       *((unsigned char *) &(data_block[i])));
			resname_strings = resname_strings + 
				*((unsigned char *) &(data_block[i]));

			i = i + *((unsigned char *) &(data_block[i])) + 1;
			resnames[num].index = *((WORD *) &(data_block[i]));
			i = i + 2;
			num++;
		}

		retval = 0;
	}

	return(retval);
}


int
load_nonresname_table(
	struct s_exe_data *exe_data,
	char	*filename,
	int	file,
	struct s_info_block *info_block,
	int	header_off)
{
	struct s_resname_table	*nonresnames;
	char			*nonresname_strings;
	int	count;
	int	num;
	int	i;
	int	retval;


	if (seek_n_read(file, 
			data_block,
			info_block->nonres_name_table_off,
			info_block->num_nonres_name_entries,
			TRUE) < 0)
	{
		fprintf(stderr, "Unable to get resident name data");
		retval = -1;
	}
	else
	{
		count = 0;
		num = 0;
		i = 0;
		while (data_block[i] != 0)
		{
			count = count + *((unsigned char *) &(data_block[i])) + 1;
			i = i + *((unsigned char *) &(data_block[i])) + 3;
			num++;
		}

		nonresnames = malloc(num * sizeof(struct s_resname_table));
		exe_data->nonresnames = nonresnames;
		exe_data->num_nonresnames = num;
		nonresname_strings = malloc(count);
		exe_data->nonresname_strings = nonresname_strings;

		num = 0;
		i = 0;
		while (data_block[i] != 0)
		{
			nonresnames[num].name = nonresname_strings;
			*nonresname_strings = data_block[i];

			nonresname_strings++;
			memcpy(nonresname_strings,
			       &(data_block[i+1]), 
			       *((unsigned char *) &(data_block[i])));
			nonresname_strings = nonresname_strings + 
				*((unsigned char *) &(data_block[i]));

			i = i + *((unsigned char *) &(data_block[i])) + 1;
			nonresnames[num].index = *((WORD *) &(data_block[i]));
			i = i + 2;
			num++;
		}

		retval = 0;
	}

	return(retval);
}


int
process_msw_header(
	char	*filename,
	int	file,
	struct s_info_block *info_block,
	int	header_off)
{
	int	retval;
	struct s_entry_table	*entry_table;
	struct s_segment_table	*segments;
	struct s_exe_data	exe_data;


	if (info_block->sig != INFO_SIG)
	{
		fprintf(stderr,
			"%s is not a Windows executable file\n",
			filename);
		retval = -1;
	}
	else
	{
		exe_data.seg_mult = 1 << info_block->log_sector_size_log2;

		load_resname_table(&exe_data, filename, file, info_block, header_off);
		load_nonresname_table(&exe_data, filename, file, info_block, header_off);
		load_entry_table(&exe_data, filename, file, info_block, header_off);

		retval = 0;
	}
}


int
loadfile(char *filename)
{
	int 	file;
	int 	retval;
	struct s_exe_header exe_header;
	struct s_info_block info_block;


	file = open(filename, O_RDONLY);
	if (file < 0)
	{
		retval = file;
	}
	else
	{
		if (read(file, (char *) &exe_header, sizeof(exe_header))
                    != sizeof(exe_header))
		{
			fprintf(stderr,
				"%s: unable to read from file\n",
				filename);
			retval = -1;
		}
		else if (exe_header.sig != EXE_SIG)
		{
			fprintf(stderr,
				"%s is not an executable MS-DOS file\n",
				filename);
			retval = -1;
		}
		else if (exe_header.ident < IDENT_MIN_MSW)
		{
			fprintf(stderr,
				"%s is not a Windows executable file\n",
				filename);
			retval = -1;
		}
		else
		{
			if (lseek(file, exe_header.msw_header_off, 0) < 0)
			{
				fprintf(stderr,
					"%s: unable to seek to MSW header\n",
					filename);
				retval = -1;
			}
			else if (read(file, (char *) &info_block, 
                                      sizeof(info_block)) != sizeof(info_block))
			{
				fprintf(stderr,
					"%s: unable to read header from file\n",
					filename);
				retval = -1;
			}
			else
			{
				retval = process_msw_header(
						filename,
						file, 
						&info_block,
						exe_header.msw_header_off);
			}
		}
	}

	return(retval);
}

int
main(int argc, char *argv[])
{
	return(loadfile(argv[1]));
}
