/*****

      $Source: d:\gunther\source\grc/RCS/rcompile.c,v $
      $Author: gunther $
      $Date: 1996/01/02 23:17:24 $
      $Revision: 1.6 $

	Changed by: Fred Kiefer (kiefer@isys.de) 1996/6/2

 *****/

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

#include "rcompile.h"
#include "wstring.h"
#include "rcutils.h"
#include "portio.h"
#include "resfile.h"

FILE *res;
node *stringTable;
node *fontTable;
int iconComponents = 1;
int cursorComponents = 1;

int
readFullFile(char *filename, char **buffer, DWORD *size)
{
	FILE *bmp;

	*size = filesize(filename);
	*buffer = (char *) xmalloc(*size);

	bmp = fopen(filename, "rb");

	if(!bmp)
	{   
		open_error(filename);
		free(*buffer);
		return GRC_ERR_OPEN;
	}

	if(fread(*buffer, 1, *size, bmp) != *size)
	{
		read_error(filename);
		free(*buffer);
		fclose(bmp);
		return GRC_ERR_READ;
	}

	fclose(bmp);
	return GRC_OK;
}

/*--------------------------------------------------
 getBitmap()
 
 create the bitmap resource format 

 Parameters:
    name        UNICODE or WCHAR id
    filename    name of .bmp file
    memflags    some optional memory flags

 Returns:
    GRC_OK, if successful
    an error code otherwise
 ---------------------------------------------------*/
int 
getBitmap(wchar_t *name, int memflags, ResourceTail *tail, char *filename)
{
	DWORD HeaderSize;
	DWORD DataSize;
	wchar_t resourceType[2] = { INVAL_WCHAR, RT_BITMAP };
	char *buffer = NULL;
	char *ptr;
	int retcode;
	int padding = 0;
	int delta = 14; /* sizeof(BITMAPFILEHEADER) */
	char *fullfilename = search_include(filename);

	if((retcode = readFullFile(fullfilename, &buffer, &DataSize)) != GRC_OK)
		return retcode;

	tail->MemoryFlags = memflags;

	DataSize -= delta;
	ptr = buffer + delta;

	retcode = writeResourceHeader(&DataSize, &HeaderSize, resourceType, name, tail, res);

	if(retcode != HeaderSize){
		write_error("resource file");
		free(buffer);
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	if (fwrite(ptr, sizeof(char), DataSize, res) != DataSize) {
		write_error("resource file");
		free(buffer);
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}    

	free(buffer);

	padding = PADDING(DataSize, 4);
    
	if (!writePadding(padding, res)) {
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	return GRC_OK;
}


/*--------------------------------------------------
 getIcon()
 
 create the icon resource format 

 Parameters:
    name        UNICODE or WCHAR id
    filename    name of .ico file
    memflags   some optional memory flags

 Returns:
    GRC_OK, if successful
    an error code otherwise
 ---------------------------------------------------*/
int 
getIcon(wchar_t *name, int memflags, ResourceTail *tail, char *filename)
{
	DWORD HeaderSize;
	DWORD DataSize;
	wchar_t resourceType[2] = { INVAL_WCHAR, RT_ICON };
	char *buffer = NULL;
	int padding = 0;
	FILE *ico;
	IconHeader fileheader;
	ICONDIRENTRY *file_components;
	IconResourceDirectory *group_component;
	int i;
	int retcode;
	char *fullfilename = search_include(filename);

	tail->MemoryFlags = memflags;

	/* read the ICONHEADER structure */
	if(!(ico = fopen(fullfilename, "rb"))) {
		open_error(filename);
		return GRC_ERR_OPEN | GRC_ERR_FATAL;
	}

	if(readIconHeader(&fileheader, ico) != ICONHEADER_SIZE) {
		fclose(ico);
		read_error(filename);
		return GRC_ERR_READ | GRC_ERR_FATAL;
	}

	if(fileheader.wType != 1) {
		fclose(ico);
		err_msg(GRC_err_invalid_icon_file);
		return GRC_ERR_INVALID_FILE | GRC_ERR_FATAL;
	}

	file_components = (ICONDIRENTRY *) xmalloc(ICONDIRENTRY_SIZE * fileheader.wCount);

	/* read the component header entries */
	for(i = 0; i < fileheader.wCount; i++) {
		if(readICONDIRENTRY(&file_components[i], ico) != ICONDIRENTRY_SIZE) {
			fclose(ico);
			free(file_components);
			read_error(filename);
			return GRC_ERR_READ | GRC_ERR_FATAL;
		}
	}

	/* write the component resources to the .res file */
	for(i = 0; i < fileheader.wCount; i++) {
		wchar_t resourceName[2] = {INVAL_WCHAR, (wchar_t) (i + iconComponents)};
		HeaderSize = 0x20;
		DataSize = file_components[i].dwBytesInRes;
		buffer = (char *) xmalloc(DataSize);
        
		if(fseek(ico, file_components[i].dwImageOffset, SEEK_SET)) {
			fclose(ico);
			free(file_components);
			free(buffer);
			seek_error(filename);
			return GRC_ERR_WRITE | GRC_ERR_FATAL;
		}

		/* read the ICON bitmap data */
		if(fread(buffer, sizeof(char), DataSize, ico) != DataSize) {
			fclose(ico);
			free(file_components);
			free(buffer);
			read_error(filename);
			return GRC_ERR_WRITE | GRC_ERR_FATAL;
		}

		/* write the resource header for ICON component */
		retcode = writeResourceHeader(&DataSize, &HeaderSize, resourceType, 
			resourceName, tail, res);
		if(retcode != HeaderSize) {
			fclose(ico);
			free(file_components);
			free(buffer);
			write_error("resource file");
			return GRC_ERR_WRITE | GRC_ERR_FATAL;
		}

		/* write the bitmap data for ICON component */
		if(fwrite(buffer, 1, DataSize, res) != DataSize) {
			fclose(ico);
			free(file_components);
			free(buffer);
			write_error("resource file");
			return GRC_ERR_WRITE | GRC_ERR_FATAL;
		}

		free(buffer);

		/* add DWORD padding stuff */
		padding = PADDING(DataSize, 4);
		if(!writePadding(padding, res))
		{
			fclose(ico);
			free(file_components);
			write_error("resource file");
			return GRC_ERR_WRITE | GRC_ERR_FATAL;
		}
	}

	fclose(ico);

	/* write the icon-group resource */
	resourceType[0] = INVAL_WCHAR;
	resourceType[1] =  RT_GROUP_ICON;
	group_component = (IconResourceDirectory *) xmalloc(IconResourceDirectory_SIZE * fileheader.wCount);

	DataSize = IconResourceDirectory_SIZE * fileheader.wCount + ICONHEADER_SIZE;

	for(i = 0; i < fileheader.wCount; i++) {
		int colors;
		group_component[i].bWidth       = file_components[i].bWidth;
		group_component[i].bHeight      = file_components[i].bHeight;
		group_component[i].bColorCount  = colors = file_components[i].bColorCount;
		group_component[i].bReserved    = file_components[i].bReserved;
		group_component[i].wPlanes      = 1; /* file_components[i].wPlanes; */
		group_component[i].wBitCount    = int_log2(colors); /* file_components[i].wBitCount; */
		group_component[i].lBytesInRes  = file_components[i].dwBytesInRes;
		group_component[i].wNameOrdinal = i + iconComponents;
	}

	free(file_components);
	tail->MemoryFlags = DISCARDABLE | MOVEABLE | PURE;

	/* write the resource header */
	retcode = writeResourceHeader(&DataSize, &HeaderSize, resourceType, name, tail, res);
	if(retcode != HeaderSize) {
		free(group_component);
		write_error("resource file");
		return GRC_ERR_MEM | GRC_ERR_FATAL;
	}

	/* write the icon header block */
	if(writeIconHeader(&fileheader, res) != ICONHEADER_SIZE) {
		free(group_component);
		write_error("resource file");
		return GRC_ERR_MEM | GRC_ERR_FATAL;
	}
    
	/* write the group component blocks */
	for(i = 0; i < fileheader.wCount; i++) {
		if(writeResourceDirectory(&group_component[i], res) != IconResourceDirectory_SIZE) {
			free(group_component);
			write_error("resource file");
			return GRC_ERR_MEM | GRC_ERR_FATAL;
		}
	}

	free(group_component);
	iconComponents += fileheader.wCount;

	padding = PADDING(DataSize, 4);
	if(!writePadding(padding, res))
	{
		write_error("resource file");
		return  GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	return GRC_OK;
}



/*--------------------------------------------------
 getCursor()
 
 create the cursor resource format 

 Parameters:
    name        UNICODE or WCHAR id
    filename    name of .cur file
    memflags   some optional memory flags

 Returns:
    GRC_OK, if successful
    an error code otherwise
 ---------------------------------------------------*/
int 
getCursor(wchar_t *name, int memflags, ResourceTail *tail, char *filename)
{
	DWORD HeaderSize;
	DWORD DataSize;
	wchar_t resourceType[2] = { INVAL_WCHAR, RT_CURSOR };
	char *buffer = NULL;
	int padding = 0;
	FILE *ico;
	IconHeader fileheader;
	CURSORDIRENTRY *file_components;
	CursorResourceDirectory *group_component;
	CursorComponent hotspot;
	int i;
	int retcode;
	char *fullfilename = search_include(filename);

	tail->MemoryFlags = memflags;

	/* read the ICONHEADER structure */
	if(!(ico = fopen(fullfilename, "rb"))) {
		open_error(filename);
		return GRC_ERR_OPEN | GRC_ERR_FATAL;
	}

	if(readIconHeader(&fileheader, ico) != ICONHEADER_SIZE) {
		read_error(filename);
		fclose(ico);
		return GRC_ERR_OPEN | GRC_ERR_FATAL;
	}
    
	if(fileheader.wType != 2) {
		fclose(ico);
		err_msg(GRC_err_invalid_cursor_file);
		return GRC_ERR_INVALID_FILE;
	}

	file_components = (CURSORDIRENTRY *) xmalloc(sizeof(CURSORDIRENTRY) * fileheader.wCount);

	/* read the component header entries */
	for(i = 0; i < fileheader.wCount; i++) {
		if(readCURSORDIRENTRY(&file_components[i], ico) != CURSORDIRENTRY_SIZE) {
			read_error(filename);
			free(file_components);
			fclose(ico);
			return GRC_ERR_SEEK | GRC_ERR_FATAL;
		}
	}

	/* write the component resources to the .res file */
	for(i = 0; i < fileheader.wCount; i++) {
		wchar_t resourceName[2] = {INVAL_WCHAR, (wchar_t) (i + cursorComponents)};
		DataSize = file_components[i].lBytesInRes;
		buffer = (char *) xmalloc(DataSize);

		hotspot.xHotspot = file_components[i].wXHotspot;
		hotspot.yHotspot = file_components[i].wYHotspot;

		if(fseek(ico, file_components[i].dwImageOffset, SEEK_SET)) {
			seek_error(filename);
			free(buffer);
			free(file_components);
			fclose(ico);
			return GRC_ERR_SEEK | GRC_ERR_FATAL;
		}

		if(fread(buffer, 1, DataSize, ico) != DataSize) {
			read_error(filename);
			free(buffer);
			free(file_components);
			fclose(ico);
			return GRC_ERR_READ | GRC_ERR_FATAL;            
		}

		DataSize += CursorComponent_SIZE;
		retcode = writeResourceHeader(&DataSize, &HeaderSize, resourceType, 
			resourceName, tail, res);
		if(retcode != HeaderSize) {
			write_error("resource file");
			free(buffer);
			free(file_components);
			fclose(ico);
			return GRC_ERR_WRITE | GRC_ERR_FATAL;
		}

		DataSize -= CursorComponent_SIZE;

		if(writeCursorComponent(&hotspot, res) != CursorComponent_SIZE) {
			write_error("resource file");
			free(buffer);
			free(file_components);
			fclose(ico);
			return GRC_ERR_WRITE | GRC_ERR_FATAL;
		}

		if(fwrite(buffer, 1, DataSize, res) != DataSize) {
			write_error("resource file");
			free(buffer);
			free(file_components);
			fclose(ico);
			return GRC_ERR_WRITE | GRC_ERR_FATAL;
		}

		free(buffer);

		padding = PADDING(DataSize, 4);
		if(!writePadding(padding, res)) {
			write_error("resource file");
			free(file_components);
			fclose(ico);
			return GRC_ERR_WRITE | GRC_ERR_FATAL;
		}
	}

	fclose(ico);

	/* write the cursor-group resource */
	resourceType[0] = INVAL_WCHAR;
	resourceType[1] = RT_GROUP_CURSOR;
	group_component = (CursorResourceDirectory *) xmalloc(CursorResourceDirectory_SIZE * fileheader.wCount);

	DataSize = CursorResourceDirectory_SIZE * fileheader.wCount + ICONHEADER_SIZE;

	for(i = 0; i < fileheader.wCount; i++) {
		group_component[i].wWidth       = file_components[i].bWidth;
		group_component[i].wHeight      = 2 * file_components[i].bHeight;
		group_component[i].wPlanes      = 1;
		group_component[i].wBitCount = 1;
		/* group_component[i].wBitCount    = 4; */
		group_component[i].lBytesInRes  = file_components[i].lBytesInRes + sizeof(DWORD);
		group_component[i].wNameOrdinal = i + cursorComponents;
	}

	free(file_components);
	tail->MemoryFlags = DISCARDABLE | MOVEABLE | PURE;

	retcode = writeResourceHeader(&DataSize, &HeaderSize, resourceType, name, tail, res);
	if(retcode != HeaderSize) {
		write_error("resource file");
		free(group_component);
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	if(writeIconHeader(&fileheader, res) != ICONHEADER_SIZE) {
		write_error("resource file");
		free(group_component);
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	for(i = 0; i < fileheader.wCount; i++) {
		if(writeCursorResourceDirectory(&group_component[i], res) != CursorResourceDirectory_SIZE) {
			write_error("resource file");
			free(group_component);
			return GRC_ERR_WRITE | GRC_ERR_FATAL;
		}
	}

	free(group_component);
	cursorComponents += fileheader.wCount;

	padding = PADDING(DataSize, 4);
	if(!writePadding(padding, res))
	{
		write_error("resource file");
		return  GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	return GRC_OK;
}

/*--------------------------------------------------
 getFont()
 
create the font resource format 
and also saves up the font description in a list
for the generation of the font dir

 Parameters:
    name        UNICODE or WCHAR id
    filename    name of .fon file
    memflags    some optional memory flags

 Returns:
    GRC_OK, if successful
    an error code otherwise
 ---------------------------------------------------*/
int 
getFont(wchar_t *name, int memflags, ResourceTail *tail, char *filename)
{
/*    DWORD HeaderSize;
    DWORD DataSize;
    wchar_t resourceType[2] = { INVAL_WCHAR, RT_FONT };
    char *buffer;
    int padding = 0;
*/	
	return GRC_OK;
}


/*--------------------------------------------------
 addControl()
 
 create the control resource format and add it to
 a temporary file which will be used by getDialog()

 Parameters:
    ctrl        points to an YYSTYPE structure which
                contains the parsing results 

 Returns:
    GRC_OK, if successful
    an error code otherwise
 ---------------------------------------------------*/
int
addControl(struct_control *ctrl)
{
	int padding = 0;
	int size = 0;

	if(writeDLGITEMTEMPLATE(&ctrl->control, res) != sizeof(DLGITEMTEMPLATE))
	{
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}
	size += sizeof(DLGITEMTEMPLATE);

	/* type information */
	if(writeNameOrId(ctrl->typeID, res) != getIDlength(ctrl->typeID))
	{
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}
	size += getIDlength(ctrl->typeID);

	/* name information */
	if(writeNameOrId(ctrl->nameID, res) != getIDlength(ctrl->nameID))
	{
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}
	size += getIDlength(ctrl->nameID);

	/* extra stuff */
	if(!writePadding(sizeof(wchar_t), res))
	{
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}
	size += sizeof(wchar_t);

	/* add padding bytes for DWORD alignment */
	padding = PADDING(size, 4);

	if(!writePadding(padding, res))
	{
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}
    
	return GRC_OK;
}

int 
getControlSize(node *controls)
{
	node *p;
	struct_control *ctrl;
	int size = 0;

	for(p = controls->next; p; p = p->next)
	{
		ctrl = (struct_control *)p->u.data;
		size += sizeof(DLGITEMTEMPLATE) + getIDlength(ctrl->typeID) +
			getIDlength(ctrl->nameID) + sizeof(wchar_t);
		if (p->next)
			size = ROUNDUP(size, 4);
	}
	return size;
}


/*--------------------------------------------------
 getDialog()
 
 create the dialog resource structures

 Parameters:
    name            UNICODE or WORD id
    memflags        some optional memory flags
    x, y, cx, cy    position and size of dialog
    options         some additional options like
                    language, version, menu, font, ...
 Returns:
    GRC_OK, if successful
    an error code otherwise
 ---------------------------------------------------*/
int 
getDialog(wchar_t *name, int memflags, int x, int y, int cx, int cy, struct_dialog *options, node *controls)
{
	DWORD HeaderSize;
	DWORD DataSize;
	wchar_t resourceType[2] = { INVAL_WCHAR, RT_DIALOG };
	int dlgHeaderSize;
	int padding = 0;
	DLGTEMPLATE dlg;
	ResourceTail *tail = &options->tail;
	int retcode;
	int ctrlCount = controls->u.count;
	node *p;
	
	DataSize = getControlSize(controls);

	dlg.style = options->style ? options->style : WS_POPUP | WS_BORDER | WS_SYSMENU;
	dlg.dwExtendedStyle = options->dwExtendedStyle;
	dlg.x = x;
	dlg.y = y;
	dlg.cx = cx;
	dlg.cy = cy;
	dlg.cdit = ctrlCount;
	tail->MemoryFlags = memflags ? memflags : 4144;

	dlgHeaderSize = sizeof(DLGTEMPLATE) + 
		getIDlength(options->menuID) +
		getIDlength(options->typeID) + 
		getIDlength(options->nameID) + 
		((dlg.style & DS_SETFONT) ? 
			sizeof(wchar_t) + getIDlength(options->font.TypefaceName) : 0);

	DataSize += ROUNDUP(dlgHeaderSize, 4);
	
	retcode = writeResourceHeader(&DataSize, &HeaderSize, resourceType, name, tail, res);
	if(retcode != HeaderSize)
	{
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	retcode =   writeDLGTEMPLATE(&dlg, res);
	retcode +=  writeNameOrId(options->menuID, res);
	retcode +=  writeNameOrId(options->typeID, res);
	retcode +=  writeNameOrId(options->nameID, res);

	if(retcode != sizeof(DLGTEMPLATE) + getIDlength(options->menuID) +
		getIDlength(options->typeID) + getIDlength(options->nameID))
	{
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}
    
	if(dlg.style & DS_SETFONT)
		writeDLGFONT(&options->font, res);

	padding = PADDING(dlgHeaderSize, 4);

	if(!writePadding(padding, res))
	{
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	for(p = controls->next; p; p = p->next)
	{
		struct_control *ctrl = (struct_control *)p->u.data;
		if (addControl(ctrl) != GRC_OK)
		{
			return GRC_ERR_WRITE | GRC_ERR_FATAL;
		}

	}
	list_free(controls, TRUE);
	free(options);

	return GRC_OK;
}


/*--------------------------------------------------
 getMenuSize()
 
 determines the size of the resource structure of
 a complete menu tree

 Parameters:
    menu        points to the root of the menu tree

 Returns:
    size of the menu resource data, in bytes
 ---------------------------------------------------*/
int
getMenuSize(node *menu)
{
	int size = 0;
	menuItem *mi;
	node *p;

	for(p = menu->next; p; p = p->next)
	{
		mi = (menuItem*) p->u.data;
		if(mi->flags & MENUFLAG_POPUP)
		{
			size += 2;
			size += getIDlength(mi->title);
			size += getMenuSize(mi->items);
		}
		else if (mi->flags & MENUFLAG_SEPARATOR)
		{
			size += 3 * sizeof(wchar_t);
		}
		else
		{
			size += 4;
			size += getIDlength(mi->title);
		}
	}
	return size;
}


/*--------------------------------------------------
 getMenuItems()
 
 converts the menu tree to the resource format

 Parameters:
    menu        points to the root of the menu tree

 Returns:
    GRC_OK, if successful
    an error code otherwise
 ---------------------------------------------------*/
int
getMenuItems(node *menu)
{
	menuItem *mi;
	node *p;
	wchar_t null[4] = { 0, 0, 0, 0 };
	int retcode;

	for(p = menu->next; p; p = p->next)
	{
		mi = (menuItem*) p->u.data;

		if (!p->next)
		{
			mi->flags |= MENUFLAG_ENDMENU;
		}

		if(mi->flags & MENUFLAG_POPUP)
		{
			/* write item data */
			retcode =  write_int16(&mi->flags, res);
			retcode += writeNameOrId(mi->title, res);

			if(retcode != 2 + getIDlength(mi->title))
			{
				write_error("resource file");
				return GRC_ERR_WRITE | GRC_ERR_FATAL;
			}

			/* write submenu data */
			getMenuItems(mi->items);
		}
		else if(mi->flags & MENUFLAG_SEPARATOR)
		{
			if(fwrite(&null, sizeof(wchar_t), 3, res) != 3)
			{
				write_error("resource file");
				return GRC_ERR_WRITE | GRC_ERR_FATAL;
			}
		}
		else
		{
			retcode  = write_int16(&mi->flags, res);
			retcode += write_int16(&mi->id, res);
			retcode += writeNameOrId(mi->title, res);

			if(retcode != 4 + getIDlength(mi->title)) 
			{
				write_error("resource file");
				return GRC_ERR_WRITE | GRC_ERR_FATAL;
			}
		}

	}
    
	list_free(menu, TRUE);
	return GRC_OK;
}


/*--------------------------------------------------
 getMenu()
 
 creates the menu resource structure

 Parameters:
    resourceName        UNICODE or WCHAR id
    memflags             some optional memory flags
    tail                part of the resource header
    menu                parsing result: the menu tree

 Returns:
    GRC_OK, if successful
    an error code otherwise
 ---------------------------------------------------*/
int
getMenu(wchar_t *resourceName, int memflags, ResourceTail *tail, node *menu)
{
    DWORD HeaderSize;
    DWORD DataSize;
    wchar_t resourceType[2] = { INVAL_WCHAR, RT_MENU };
    MenuHeader header;
    int padding = 0;
    int retcode;

    DataSize = getMenuSize(menu) + sizeof(MenuHeader);

    header.wVersion = 0;
    header.cbHeaderSize = 0;
    tail->MemoryFlags = memflags;

    retcode = writeResourceHeader(&DataSize, &HeaderSize, resourceType, resourceName, tail, res);

    if(retcode != HeaderSize)
    {
        write_error("resource file");
        return GRC_ERR_WRITE | GRC_ERR_FATAL;
    }
    
    if(writeMenuHeader(&header, res) != MenuHeader_SIZE)
    {
        write_error("resource file");
        return GRC_ERR_WRITE | GRC_ERR_FATAL;
    }
    
	if((retcode = getMenuItems(menu)) != GRC_OK)
		return retcode;

	padding = PADDING(DataSize, 4);

	if(!writePadding(padding, res))
	{
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	return GRC_OK;
}



int getStringtableSize(wchar_t **items)
{
	int i;
	int size = 0;
    
	for(i = 0; i < 16; i++)
	{
		if(items[i]){
			size += sizeof(wchar_t) * (items[i][0] + 1);
		}
		else {
			size += sizeof(wchar_t);
		}
	}
    
	return size;
}

int
addTable(int memflags, ResourceTail *tail, node *table)
{
	node *p;
	stringEntry *entry;

	for(p = table->next; p; p = p->next)
	{
		entry = (stringEntry *)p->u.data;
		entry->tail = *tail;
		entry->tail.MemoryFlags = memflags;

		list_add(stringTable, entry);
	}
	list_free(table, FALSE);
	return GRC_OK;
}


/*--------------------------------------------------
 getString()
 
 creates all string resources 

 Returns:
    GRC_OK, if successful
    an error code otherwise
 ---------------------------------------------------*/
int
getString(node *table)
{
	DWORD HeaderSize = 4 * sizeof(DWORD) + sizeof(ResourceTail);   /* should be 0x20 */
	wchar_t resourceType[2] = { INVAL_WCHAR, RT_STRING };
	ResourceTail tail;
	wchar_t *block[16];
	int j;
	int padding;
	int retcode;
	node *p;
	stringEntry *entry;

	for(p = table->next; p; p = p->next)
	{
		entry = (stringEntry *) p->u.data;
		if (entry)	
		{
			DWORD DataSize;
			wchar_t resourceName[2] = { INVAL_WCHAR, entry->block };
			int language = entry->tail.LanguageId;
			int blocknr = entry->block;
			node *q;
			stringEntry *entry2;
			int i;

			tail = entry->tail;
			for (i = 0; i < 16; i++) {
				block[i] = NULL;
			}

			/* the current item has to be the first item in loop */
			for(q = p; q; q = q->next){
				entry2 = (stringEntry *) q->u.data;
				if(entry2) {
					if((entry2->block == blocknr) &&
						(entry2->tail.LanguageId == language))
					{
						if(block[entry2->pos]) {
							yyerror("Duplicate string identifiers are not allowed.block");
							verbose(("Duplicate %d pos %d", blocknr, entry2->pos));
						}
						else {
							block[entry2->pos] = entry2->string;
						}
						/* remove it from table */
						free(entry2);
						q->u.data = NULL;
					}
				}
			}
			/* write the general resource header */
			DataSize = getStringtableSize(block);
			retcode = writeResourceHeader(&DataSize, &HeaderSize, 
				resourceType, resourceName, &tail, res);
			if(retcode != HeaderSize)
			{
				write_error("resource file");
				return GRC_ERR_WRITE | GRC_ERR_FATAL;
			}

		/* the header is already DWORD-aligned */

		for(j = 0; j < 16; j++) {
			if(block[j]) {
				wchar_t *string = block[j];
				int k;
	
    				/* write and free the string data */
    				for(k = 0; k <= string[0]; k++) {
					if(write_int16(&string[k], res) != sizeof(wchar_t)) {
						write_error("resource file");
						return GRC_ERR_WRITE | GRC_ERR_FATAL;
					}
				}
				free(block[j]);
			}
			else
				/* write a '00 00' word if there is no string at position j */
				if(!writePadding(sizeof(wchar_t), res)) {
					write_error("resource file");
					return GRC_ERR_WRITE | GRC_ERR_FATAL;
				}
		}

			/* add padding stuff */
			padding = PADDING(DataSize, 4);
			if(!writePadding(padding, res))
			{
				write_error("resource file");
				return GRC_ERR_WRITE | GRC_ERR_FATAL;
			}
		}
	}

	list_free(table, TRUE);
	return GRC_OK;
}


int
getAccelerators(wchar_t *name, int memflags, ResourceTail *tail, node *table)
{
	DWORD HeaderSize;
	wchar_t resourceType[2] = { INVAL_WCHAR, RT_ACCELERATOR };
	DWORD DataSize = sizeof(ACCELTABLEENTRY) * table->u.count;
	ACCELTABLEENTRY*entry;
	node *p;

	tail->MemoryFlags = memflags;

	if(writeResourceHeader(&DataSize, &HeaderSize, resourceType, name, tail, res) != HeaderSize)
	{   
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	for(p = table->next; p; p = p->next)
	{
		entry = (ACCELTABLEENTRY*)p->u.data;

		if (!p->next)
		{
			entry->fFlags |= ACC_LAST;
		} 
		if(writeaccel_entry(entry, res) != sizeof(ACCELTABLEENTRY))
		{
			write_error("resource file");
			return GRC_ERR_WRITE | GRC_ERR_FATAL;
		}
	}
	list_free(table, TRUE);

	return GRC_OK;
}

int
getRcdata(wchar_t *resourceType, wchar_t *name, int memflags, ResourceTail *tail, node *entries)
{
	DWORD HeaderSize;
	DWORD DataSize = 0;
	int padding;
	rcdata_entry *entry;
	node *p;

	tail->MemoryFlags = memflags;

	for(p = entries->next; p; p = p->next)
	{
		entry = (rcdata_entry *)p->u.data;
		DataSize += entry->size;
	}

	if(writeResourceHeader(&DataSize, &HeaderSize, resourceType, name, tail, res) != HeaderSize) {   
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	for(p = entries->next; p; p = p->next)
	{
		entry = (rcdata_entry *)p->u.data;

		if (fwrite(entry->field, 1, entry->size, res) != entry->size) {
			write_error("resource file");
			return GRC_ERR_WRITE | GRC_ERR_FATAL;
		}
	}

	list_free(entries, TRUE);

	/* add padding stuff */
	padding = PADDING(DataSize, 4);
	if(!writePadding(padding, res)) {
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	return GRC_OK;
}

int
getToolbar(wchar_t *resourceType, wchar_t *name, int memflags, ResourceTail *tail, int width, int height, node *entries)
{
	DWORD HeaderSize;
	DWORD DataSize = 0;
	int padding;
	rcdata_entry *entry;
	short head[4] = {1, 0, 0, 0};
	node *p;

	tail->MemoryFlags = memflags;

	DataSize = 8 + 2 * entries->u.count;

	if(writeResourceHeader(&DataSize, &HeaderSize, resourceType, name, tail, res) != HeaderSize) {   
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	head[1] = width;
	head[2] = height;
	head[3] = entries->u.count;
	if (fwrite(head, sizeof(short), 4, res) != 4) {
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	for(p = entries->next; p; p = p->next)
	{
		entry = (rcdata_entry *)p->u.data;

		if (fwrite(entry->field, 1, entry->size, res) != entry->size) {
			write_error("resource file");
			return GRC_ERR_WRITE | GRC_ERR_FATAL;
		}
	}

	list_free(entries, TRUE);

	/* add padding stuff */
	padding = PADDING(DataSize, 4);
	if(!writePadding(padding, res)) {
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	return GRC_OK;
}

int
getUserRC(wchar_t *resourceType, wchar_t *name, int memflags, ResourceTail *tail, char *filename)
{
	DWORD HeaderSize;
	DWORD DataSize;
	int padding;
	char *buffer = NULL;
	int retcode;
	char *fullfilename = search_include(filename);

	if((retcode = readFullFile(fullfilename, &buffer, &DataSize)) != GRC_OK)
		return retcode;

	tail->MemoryFlags = memflags;

	if(writeResourceHeader(&DataSize, &HeaderSize, resourceType, name, tail, res) != HeaderSize) {   
		write_error("resource file");
		free(buffer);
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	if (fwrite(buffer, sizeof(char), DataSize, res) != DataSize) {
		write_error("resource file");
		free(buffer);
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}    

	free(buffer);

	/* add padding stuff */
	padding = PADDING(DataSize, 4);
	if(!writePadding(padding, res)) {
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	return GRC_OK;
}

int
getVersionEntrySize(version_entry *vs)
{
	version_entry *entry;
	node *p;
	int Size = 6;

	if (vs->szKey)
		Size += sizeof(wchar_t) * (wstrlen(vs->szKey) + 1);

	Size = ROUNDUP(Size, 4);
	Size += vs->wValueLength * (vs->wType ? sizeof(wchar_t) : 1);

	if (vs->entries) {
		for (p = vs->entries->next; p; p = p->next){
			entry = (version_entry *) p->u.data;
			Size += getVersionEntrySize(entry);
			if (p->next)
				Size = ROUNDUP(Size, 4);
		}
	}

	vs->wLength = Size;
	return Size;
}

int
getVersionEntry(version_entry *vs)
{
	int padding;
	version_entry *entry;
	node *p;
	int Size;
		
	Size = writeVersionEntry(vs, res);

	/* add padding stuff */
	padding = PADDING(Size, 4);
	if(!writePadding(padding, res)) {
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	if (vs->entries) {
		for (p = vs->entries->next; p; p = p->next){
			entry = (version_entry *) p->u.data;
			Size = getVersionEntry(entry);
		}
		list_free(vs->entries, TRUE);
	}

	return GRC_OK;
}

int
getVersioninfo(wchar_t *name, int memflags, ResourceTail *tail, version_entry *vs)
{
	DWORD HeaderSize;
	wchar_t resourceType[2] = { INVAL_WCHAR, RT_VERSION };
	DWORD DataSize;
	int padding;

	tail->MemoryFlags = memflags;
	DataSize = getVersionEntrySize(vs);

	if(writeResourceHeader(&DataSize, &HeaderSize, resourceType, name, tail, res) != HeaderSize)
	{   
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	getVersionEntry(vs);

	/* add padding stuff */
	padding = PADDING(DataSize, 4);
	if(!writePadding(padding, res)) {
		write_error("resource file");
		return GRC_ERR_WRITE | GRC_ERR_FATAL;
	}

	return GRC_OK;
}

int
StartRes(const char *resfile)
{
	DWORD resfilePrefix[8] = { 0, 32, MAKELONG(-1, 0), MAKELONG(-1, 0), 0, 0, 0, 0 };
	
	stringTable = list_new();
	fontTable = list_new();

	res = fopen(resfile, "wb");

	if(!res) {
		open_error(resfile);
		return GRC_ERR_OPEN;
	}

	/* write the WIN32 resource file header */
	fwrite(resfilePrefix, sizeof(DWORD), 8, res);
	return GRC_OK;
}

int
FinishRes(int ok)
{
	int retcode = GRC_OK;

	if (ok){
	  retcode = getString(stringTable);
	}

	fclose(res);
	return retcode;
}
