/*
 * Write .res, .s and .h file(s) from a resource-tree
 *
 * Copyright 1998 Bertho A. Stultiens
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "config.h"
#include "wine/port.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "wine/unicode.h"
#include "wrc.h"
#include "writeres.h"
#include "genres.h"
#include "newstruc.h"
#include "utils.h"

static char s_file_head_str[] =
        "/* This file is generated with wrc version " WRC_FULLVERSION ". Do not edit! */\n"
        "/* Source : %s */\n"
        "/* Cmdline: %s */\n"
        "/* Date   : %s */\n"
        "\n"
	"\t.data\n"
        "\n"
	;

static char s_file_tail_str[] =
        "/* <eof> */\n"
        "\n"
	;

static char s_file_autoreg_str[] =
	"\t.text\n"
	".LAuto_Register:\n"
	"\tpushl\t$" __ASM_NAME("%s%s") "\n"
	"\tcall\t" __ASM_NAME("LIBRES_RegisterResources") "\n"
	"\taddl\t$4,%%esp\n"
	"\tret\n\n"
#ifdef __NetBSD__
	".stabs \"___CTOR_LIST__\",22,0,0,.LAuto_Register\n\n"
#else
	"\t.section .ctors,\"aw\"\n"
	"\t.long\t.LAuto_Register\n\n"
#endif
	;

static char h_file_head_str[] =
	"/*\n"
	" * This file is generated with wrc version " WRC_FULLVERSION ". Do not edit!\n"
	" * Source : %s\n"
	" * Cmdline: %s\n"
	" * Date   : %s"
	" */\n"
        "\n"
	"#ifndef __%08lx_H\n"	/* This becomes the date of compile */
	"#define __%08lx_H\n"
	"\n"
	"#include <wrc_rsc.h>\n"
	"\n"
	;

static char h_file_tail_str[] =
	"#endif\n"
	"/* <eof> */\n\n"
	;

char _NEResTab[] = "_NEResTab";
char _PEResTab[] = "_PEResTab";
char _ResTable[] = "_ResTable";

/* Variables used for resource sorting */
res_count_t *rcarray = NULL;	/* Type-level count array */
int rccount = 0;		/* Nr of entries in the type-level array */
int n_id_entries = 0;		/* win32 only: Nr of unique ids in the type-level array */
int n_name_entries = 0;		/* win32 only: Nr of unique namess in the type-level array */

static int direntries;		/* win32 only: Total number of unique resources */

/*
 *****************************************************************************
 * Function	: write_resfile
 * Syntax	: void write_resfile(char *outname, resource_t *top)
 * Input	:
 *	outname	- Filename to write to
 *	top	- The resource-tree to convert
 * Output	:
 * Description	:
 * Remarks	:
 *****************************************************************************
*/
void write_resfile(char *outname, resource_t *top)
{
	FILE *fo;
	int ret;
	char zeros[3] = {0, 0, 0};

	fo = fopen(outname, "wb");
	if(!fo)
	{
		error("Could not open %s\n", outname);
	}

	if(win32)
	{
		/* Put an empty resource first to signal win32 format */
		res_t *res = new_res();
		put_dword(res, 0);		/* ResSize */
		put_dword(res, 0x00000020);	/* HeaderSize */
		put_word(res, 0xffff);		/* ResType */
		put_word(res, 0);
		put_word(res, 0xffff);		/* ResName */
		put_word(res, 0);
		put_dword(res, 0);		/* DataVersion */
		put_word(res, 0);		/* Memory options */
		put_word(res, 0);		/* Language */
		put_dword(res, 0);		/* Version */
		put_dword(res, 0);		/* Charateristics */
		ret = fwrite(res->data, 1, res->size, fo);
		if(ret != res->size)
		{
			fclose(fo);
			error("Error writing %s", outname);
		}
		free(res);
	}

	for(; top; top = top->next)
	{
		if(!top->binres)
			continue;

		ret = fwrite(top->binres->data, 1, top->binres->size, fo);
		if(ret != top->binres->size)
		{
			fclose(fo);
			error("Error writing %s", outname);
		}
		if(win32 && (top->binres->size & 0x03))
		{
			/* Write padding */
			ret = fwrite(zeros, 1, 4 - (top->binres->size & 0x03), fo);
			if(ret != 4 - (top->binres->size & 0x03))
			{
				fclose(fo);
				error("Error writing %s", outname);
			}
		}
	}
	fclose(fo);
}

/*
 *****************************************************************************
 * Function	: write_s_res
 * Syntax	: void write_s_res(FILE *fp, res_t *res)
 * Input	:
 * Output	:
 * Description	:
 * Remarks	:
 *****************************************************************************
*/
#define BYTESPERLINE	8
static void write_s_res(FILE *fp, res_t *res)
{
	int idx = res->dataidx;
	int end = res->size;
	int rest = (end - idx) % BYTESPERLINE;
	int lines = (end - idx) / BYTESPERLINE;
	int i, j;

	for(i = 0 ; i < lines; i++)
	{
		fprintf(fp, "\t.byte\t");
		for(j = 0; j < BYTESPERLINE; j++, idx++)
		{
			fprintf(fp, "0x%02x%s", res->data[idx] & 0xff,
					j == BYTESPERLINE-1 ? "" : ", ");
		}
		fprintf(fp, "\n");
	}
	if(rest)
	{
		fprintf(fp, "\t.byte\t");
		for(j = 0; j < rest; j++, idx++)
		{
			fprintf(fp, "0x%02x%s", res->data[idx] & 0xff,
					j == rest-1 ? "" : ", ");
		}
		fprintf(fp, "\n");
	}
}
#undef BYTESPERLINE

/*
 *****************************************************************************
 * Function	: write_name_str
 * Syntax	: void write_name_str(FILE *fp, name_id_t *nid)
 * Input	:
 * Output	:
 * Description	:
 * Remarks	: One level self recursive for string type conversion
 *****************************************************************************
*/
static void write_name_str(FILE *fp, name_id_t *nid)
{
	res_t res;
	assert(nid->type == name_str);

	if(!win32 && nid->name.s_name->type == str_char)
	{
		res.size = strlen(nid->name.s_name->str.cstr);
		if(res.size > 254)
			error("Can't write strings larger than 254 bytes");
		if(res.size == 0)
			internal_error(__FILE__, __LINE__, "Attempt to write empty string");
		res.dataidx = 0;
		res.data = (char *)xmalloc(1 + res.size + 1);
		res.data[0] = (char)res.size;
		res.size++;	/* We need to write the length byte as well */
		strcpy(res.data+1, nid->name.s_name->str.cstr);
		write_s_res(fp, &res);
		free(res.data);
	}
	else if(!win32 && nid->name.s_name->type == str_unicode)
	{
		name_id_t lnid;

		lnid.type = name_str;
		lnid.name.s_name = convert_string( nid->name.s_name, str_char,
                                                   get_language_codepage(0,0) );
		write_name_str(fp, &lnid);
		free_string( lnid.name.s_name );
	}
	else if(win32 && nid->name.s_name->type == str_char)
	{
		name_id_t lnid;

		lnid.type = name_str;
		lnid.name.s_name = convert_string( nid->name.s_name, str_unicode,
                                                   get_language_codepage(0,0) );
		write_name_str(fp, &lnid);
		free_string( lnid.name.s_name );
	}
	else  if(win32 && nid->name.s_name->type == str_unicode)
	{
		res.size = strlenW(nid->name.s_name->str.wstr);
		if(res.size > 65534)
			error("Can't write strings larger than 65534 characters");
		if(res.size == 0)
			internal_error(__FILE__, __LINE__, "Attempt to write empty string");
		res.dataidx = 0;
		res.data = (char *)xmalloc(2 + (res.size + 1) * 2);
		((short *)res.data)[0] = (short)res.size;
		strcpyW((WCHAR *)(res.data+2), nid->name.s_name->str.wstr);
		res.size *= 2; /* Function writes bytes, not shorts... */
		res.size += 2; /* We need to write the length word as well */
		write_s_res(fp, &res);
		free(res.data);
	}
	else
	{
		internal_error(__FILE__, __LINE__, "Hmm, requested to write a string of unknown type %d",
				nid->name.s_name->type);
	}
}

/*
 *****************************************************************************
 * Function	: find_counter
 * Syntax	: res_count_t *find_counter(name_id_t *type)
 * Input	:
 * Output	:
 * Description	:
 * Remarks	:
 *****************************************************************************
*/
static res_count_t *find_counter(name_id_t *type)
{
	int i;
	for(i = 0; i < rccount; i++)
	{
		if(!compare_name_id(type, &(rcarray[i].type)))
			return &rcarray[i];
	}
	return NULL;
}

/*
 *****************************************************************************
 * Function	: count_resources
 * Syntax	: res_count_t *count_resources(resource_t *top)
 * Input	:
 * Output	:
 * Description	:
 * Remarks	: The whole lot is converted into arrays because they are
 *		  easy sortable. Makes the lot almost unreadable, but it
 *		  works (I hope). Basically you have to keep in mind that
 *		  the lot is a three-dimensional structure for win32 and a
 *		  two-dimensional structure for win16.
 *****************************************************************************
*/
#define RCT(v)	(*((resource_t **)(v)))
/* qsort sorting function */
static int sort_name_id(const void *e1, const void *e2)
{
	return compare_name_id(RCT(e1)->name, RCT(e2)->name);
}

static int sort_language(const void *e1, const void *e2)
{
	assert((RCT(e1)->lan) != NULL);
	assert((RCT(e2)->lan) != NULL);

	return MAKELANGID(RCT(e1)->lan->id, RCT(e1)->lan->sub)
	     - MAKELANGID(RCT(e2)->lan->id, RCT(e2)->lan->sub);
}
#undef RCT
#define RCT(v)	((res_count_t *)(v))
static int sort_type(const void *e1, const void *e2)
{
	return compare_name_id(&(RCT(e1)->type), &(RCT(e2)->type));
}
#undef RCT

static void count_resources(resource_t *top)
{
	resource_t *rsc;
	res_count_t *rcp;
	name_id_t nid;
	int i, j;

	for(rsc = top; rsc; rsc = rsc->next)
	{
		if(!rsc->binres)
			continue;
		switch(rsc->type)
		{
		case res_dlgex:
			nid.name.i_name = WRC_RT_DIALOG;
			nid.type = name_ord;
			break;
		case res_menex:
			nid.name.i_name = WRC_RT_MENU;
			nid.type = name_ord;
			break;
		case res_usr:
			nid = *(rsc->res.usr->type);
			break;
		default:
			nid.name.i_name = rsc->type;
			nid.type = name_ord;
		}

		if((rcp = find_counter(&nid)) == NULL)
		{
			/* Count the number of uniq ids and names */

			if(nid.type == name_ord)
				n_id_entries++;
			else
				n_name_entries++;

			if(!rcarray)
			{
				rcarray = (res_count_t *)xmalloc(sizeof(res_count_t));
				rccount = 1;
				rcarray[0].count = 1;
				rcarray[0].type = nid;
				rcarray[0].rscarray = (resource_t **)xmalloc(sizeof(resource_t *));
				rcarray[0].rscarray[0] = rsc;
			}
			else
			{
				rccount++;
				rcarray = (res_count_t *)xrealloc(rcarray, rccount * sizeof(res_count_t));
				rcarray[rccount-1].count = 1;
				rcarray[rccount-1].type = nid;
				rcarray[rccount-1].rscarray = (resource_t **)xmalloc(sizeof(resource_t *));
				rcarray[rccount-1].rscarray[0] = rsc;
			}
		}
		else
		{
			rcp->count++;
			rcp->rscarray = (resource_t **)xrealloc(rcp->rscarray, rcp->count * sizeof(resource_t *));
			rcp->rscarray[rcp->count-1] = rsc;
		}
	}

	if(!win32)
	{
		/* We're done, win16 requires no special sorting */
		return;
	}

	/* We now have a unsorted list of types with an array of res_count_t
	 * in rcarray[0..rccount-1]. And we have names of one type in the
	 * rcarray[x].rsc[0..rcarray[x].count-1] arrays.
	 * The list needs to be sorted for win32's top level tree structure.
	 */

	/* Sort the types */
	if(rccount > 1)
		qsort(rcarray, rccount, sizeof(rcarray[0]), sort_type);

	/* Now sort the name-id arrays */
	for(i = 0; i < rccount; i++)
	{
		if(rcarray[i].count > 1)
			qsort(rcarray[i].rscarray, rcarray[i].count, sizeof(rcarray[0].rscarray[0]), sort_name_id);
	}

	/* Now split the name-id arrays into name/language
	 * subs. Don't look at the awfull expressions...
	 * We do this by taking the array elements out of rscarray and putting
	 * together a new array in rsc32array.
	 */
	for(i = 0; i < rccount; i++)
	{
		res_count_t *rcap;

		assert(rcarray[i].count >= 1);

		/* rcap points to the current type we are dealing with */
		rcap = &(rcarray[i]);

		/* Insert the first name-id */
		rcap->rsc32array = (res32_count_t *)xmalloc(sizeof(res32_count_t));
		rcap->count32 = 1;
		rcap->rsc32array[0].rsc = (resource_t **)xmalloc(sizeof(resource_t *));
		rcap->rsc32array[0].count = 1;
		rcap->rsc32array[0].rsc[0] = rcap->rscarray[0];
		if(rcap->rscarray[0]->name->type == name_ord)
		{
			rcap->n_id_entries = 1;
			rcap->n_name_entries = 0;
		}
		else
		{
			rcap->n_id_entries = 0;
			rcap->n_name_entries = 1;
		}

		/* Now loop over the resting resources of the current type
		 * to find duplicate names (which should have different
		 * languages).
		 */
		for(j = 1; j < rcap->count; j++)
		{
			res32_count_t *r32cp;

			/* r32cp points to the current res32_count structure
			 * that holds the resource name we are processing.
			 */
			r32cp = &(rcap->rsc32array[rcap->count32-1]);

			if(!compare_name_id(r32cp->rsc[0]->name, rcarray[i].rscarray[j]->name))
			{
				/* Names are the same, add to list */
				r32cp->count++;
				r32cp->rsc = (resource_t **)xrealloc(r32cp->rsc, r32cp->count * sizeof(resource_t *));
				r32cp->rsc[r32cp->count-1] = rcap->rscarray[j];
			}
			else
			{
				/* New name-id, sort the old one by
				 * language and create new list
				 */
				if(r32cp->count > 1)
					qsort(r32cp->rsc, r32cp->count, sizeof(r32cp->rsc[0]), sort_language);
				rcap->count32++;
				rcap->rsc32array = (res32_count_t*)xrealloc(rcap->rsc32array, rcap->count32 * sizeof(res32_count_t));
				rcap->rsc32array[rcap->count32-1].rsc = (resource_t **)xmalloc(sizeof(resource_t *));
				rcap->rsc32array[rcap->count32-1].count = 1;
				rcap->rsc32array[rcap->count32-1].rsc[0] = rcap->rscarray[j];

				if(rcap->rscarray[j]->name->type == name_ord)
					rcap->n_id_entries++;
				else
					rcap->n_name_entries++;
			}
		}
		/* Also sort the languages of the last name group */
		if(rcap->rsc32array[rcap->count32-1].count > 1)
			qsort(rcap->rsc32array[rcap->count32-1].rsc,
			      rcap->rsc32array[rcap->count32-1].count,
			      sizeof(rcap->rsc32array[rcap->count32-1].rsc[0]),
			      sort_language);
	}
}

/*
 *****************************************************************************
 * Function	: write_pe_segment
 * Syntax	: void write_pe_segment(FILE *fp)
 * Input	:
 * Output	:
 * Description	:
 * Remarks	:
 *****************************************************************************
*/
static void write_pe_segment(FILE *fp)
{
	int i;

	fprintf(fp, "\t.align\t4\n");
	fprintf(fp, __ASM_NAME("%s%s") ":\n", prefix, _PEResTab);
	fprintf(fp, "\t.globl\t" __ASM_NAME("%s%s") "\n", prefix, _PEResTab);
	/* Flags */
	fprintf(fp, "\t.long\t0\n");
	/* Time/Date stamp */
	fprintf(fp, "\t.long\t0x%08lx\n", (long)now);
	/* Version */
	fprintf(fp, "\t.long\t0\n");	/* FIXME: must version be filled out? */
	/* # of id entries, # of name entries */
	fprintf(fp, "\t.short\t%d, %d\n", n_name_entries, n_id_entries);

	/* Write the type level of the tree */
	for(i = 0; i < rccount; i++)
	{
		res_count_t *rcp;
		char *label;

		rcp = &rcarray[i];

		/* TypeId */
		if(rcp->type.type == name_ord)
			fprintf(fp, "\t.long\t%d\n", rcp->type.name.i_name);
		else
		{
			char *name = prep_nid_for_label(&(rcp->type));
			fprintf(fp, "\t.long\t(" __ASM_NAME("%s_%s_typename") " - " __ASM_NAME("%s%s") ") | 0x80000000\n",
				prefix,
				name,
				prefix,
				_PEResTab);
		}
		/* Offset */
		label = prep_nid_for_label(&(rcp->type));
		fprintf(fp, "\t.long\t(.L%s - " __ASM_NAME("%s%s") ") | 0x80000000\n",
			label,
			prefix,
			_PEResTab);
	}

	/* Write the name level of the tree */

	for(i = 0; i < rccount; i++)
	{
		res_count_t *rcp;
		char *typelabel;
		char *namelabel;
		int j;

		rcp = &rcarray[i];

		typelabel = xstrdup(prep_nid_for_label(&(rcp->type)));
		fprintf(fp, ".L%s:\n", typelabel);

		fprintf(fp, "\t.long\t0\n");		/* Flags */
		fprintf(fp, "\t.long\t0x%08lx\n", (long)now);	/* TimeDate */
		fprintf(fp, "\t.long\t0\n");	/* FIXME: must version be filled out? */
		fprintf(fp, "\t.short\t%d, %d\n", rcp->n_name_entries, rcp->n_id_entries);
		for(j = 0; j < rcp->count32; j++)
		{
			resource_t *rsc = rcp->rsc32array[j].rsc[0];
			/* NameId */
			if(rsc->name->type == name_ord)
				fprintf(fp, "\t.long\t%d\n", rsc->name->name.i_name);
			else
			{
				fprintf(fp, "\t.long\t(" __ASM_NAME("%s%s_name") " - " __ASM_NAME("%s%s") ") | 0x80000000\n",
					prefix,
					rsc->c_name,
					prefix,
					_PEResTab);
			}
			/* Maybe FIXME: Unescape the tree (ommit 0x80000000) and
			 * put the offset to the resource data entry.
			 * ?? Is unescaping worth while ??
			 */
			/* Offset */
			namelabel = prep_nid_for_label(rsc->name);
			fprintf(fp, "\t.long\t(.L%s_%s - " __ASM_NAME("%s%s") ") | 0x80000000\n",
				typelabel,
				namelabel,
				prefix,
				_PEResTab);
		}
		free(typelabel);
	}

	/* Write the language level of the tree */

	for(i = 0; i < rccount; i++)
	{
		res_count_t *rcp;
		char *namelabel;
		char *typelabel;
		int j;

		rcp = &rcarray[i];
		typelabel = xstrdup(prep_nid_for_label(&(rcp->type)));

		for(j = 0; j < rcp->count32; j++)
		{
			res32_count_t *r32cp = &(rcp->rsc32array[j]);
			int k;

			namelabel = xstrdup(prep_nid_for_label(r32cp->rsc[0]->name));
			fprintf(fp, ".L%s_%s:\n", typelabel, namelabel);

			fprintf(fp, "\t.long\t0\n");		/* Flags */
			fprintf(fp, "\t.long\t0x%08lx\n", (long)now);	/* TimeDate */
			fprintf(fp, "\t.long\t0\n");	/* FIXME: must version be filled out? */
			fprintf(fp, "\t.short\t0, %d\n", r32cp->count);

			for(k = 0; k < r32cp->count; k++)
			{
				resource_t *rsc = r32cp->rsc[k];
				assert(rsc->lan != NULL);
				/* LanguageId */
				fprintf(fp, "\t.long\t0x%08x\n", rsc->lan ? MAKELANGID(rsc->lan->id, rsc->lan->sub) : 0);
				/* Offset */
				fprintf(fp, "\t.long\t.L%s_%s_%d - " __ASM_NAME("%s%s") "\n",
					typelabel,
					namelabel,
					rsc->lan ? MAKELANGID(rsc->lan->id, rsc->lan->sub) : 0,
					prefix,
					_PEResTab);
			}
			free(namelabel);
		}
		free(typelabel);
	}

	/* Write the resource table itself */
	fprintf(fp, __ASM_NAME("%s_ResourceDirectory") ":\n", prefix);
	fprintf(fp, "\t.globl\t" __ASM_NAME("%s_ResourceDirectory") "\n", prefix);
	direntries = 0;

	for(i = 0; i < rccount; i++)
	{
		res_count_t *rcp;
		char *namelabel;
		char *typelabel;
		int j;

		rcp = &rcarray[i];
		typelabel = xstrdup(prep_nid_for_label(&(rcp->type)));

		for(j = 0; j < rcp->count32; j++)
		{
			res32_count_t *r32cp = &(rcp->rsc32array[j]);
			int k;

			namelabel = xstrdup(prep_nid_for_label(r32cp->rsc[0]->name));

			for(k = 0; k < r32cp->count; k++)
			{
				resource_t *rsc = r32cp->rsc[k];

				assert(rsc->lan != NULL);

				fprintf(fp, ".L%s_%s_%d:\n",
					typelabel,
					namelabel,
					rsc->lan ? MAKELANGID(rsc->lan->id, rsc->lan->sub) : 0);

				/* Data RVA */
				fprintf(fp, "\t.long\t" __ASM_NAME("%s%s_data") " - " __ASM_NAME("%s%s") "\n",
					prefix,
					rsc->c_name,
					prefix,
					_PEResTab);
				/* Size */
				fprintf(fp, "\t.long\t%d\n",
					rsc->binres->size - rsc->binres->dataidx);
				/* CodePage */
				fprintf(fp, "\t.long\t%ld\n", codepage);
				/* Reserved */
				fprintf(fp, "\t.long\t0\n");

				direntries++;
			}
			free(namelabel);
		}
		free(typelabel);
	}
}

/*
 *****************************************************************************
 * Function	: write_ne_segment
 * Syntax	: void write_ne_segment(FILE *fp)
 * Input	:
 * Output	:
 * Description	:
 * Remarks	:
 *****************************************************************************
*/
static void write_ne_segment(FILE *fp)
{
	int i, j;

	fprintf(fp, "\t.align\t4\n");
	fprintf(fp, __ASM_NAME("%s%s") ":\n", prefix, _NEResTab);
	fprintf(fp, "\t.globl\t" __ASM_NAME("%s%s") "\n", prefix, _NEResTab);

	/* AlignmentShift */
	fprintf(fp, "\t.short\t%d\n", alignment_pwr);

	/* TypeInfo */
	for(i = 0; i < rccount; i++)
	{
		res_count_t *rcp = &rcarray[i];

		/* TypeId */
		if(rcp->type.type == name_ord)
			fprintf(fp, "\t.short\t0x%04x\n", rcp->type.name.i_name | 0x8000);
		else
			fprintf(fp, "\t.short\t" __ASM_NAME("%s_%s_typename") " - " __ASM_NAME("%s%s") "\n",
				prefix,
				rcp->type.name.s_name->str.cstr,
				prefix,
				_NEResTab);
		/* ResourceCount */
		fprintf(fp, "\t.short\t%d\n", rcp->count);
		/* Reserved */
		fprintf(fp, "\t.long\t0\n");
		/* NameInfo */
		for(j = 0; j < rcp->count; j++)
		{
/*
 * VERY IMPORTANT:
 * The offset is relative to the beginning of the NE resource segment
 * and _NOT_ to the file-beginning. This is because we do not have a
 * file based resource, but a simulated NE segment. The offset _is_
 * scaled by the AlignShift field.
 * All other things are as the MS doc describes (alignment etc.)
 */
			/* Offset */
			fprintf(fp, "\t.short\t(" __ASM_NAME("%s%s_data") " - " __ASM_NAME("%s%s") ") >> %d\n",
				prefix,
				rcp->rscarray[j]->c_name,
				prefix,
				_NEResTab,
				alignment_pwr);
			/* Length */
			fprintf(fp, "\t.short\t%d\n",
				(rcp->rscarray[j]->binres->size - rcp->rscarray[j]->binres->dataidx + alignment - 1) >> alignment_pwr);
			/* Flags */
			fprintf(fp, "\t.short\t0x%04x\n", (WORD)rcp->rscarray[j]->memopt);
			/* Id */
			if(rcp->rscarray[j]->name->type == name_ord)
				fprintf(fp, "\t.short\t0x%04x\n", rcp->rscarray[j]->name->name.i_name | 0x8000);
			else
				fprintf(fp, "\t.short\t" __ASM_NAME("%s%s_name") " - " __ASM_NAME("%s%s") "\n",
				prefix,
				rcp->rscarray[j]->c_name,
				prefix,
				_NEResTab);
			/* Handle and Usage */
			fprintf(fp, "\t.short\t0, 0\n");
		}
	}
	/* EndTypes */
	fprintf(fp, "\t.short\t0\n");
}

/*
 *****************************************************************************
 * Function	: write_rsc_names
 * Syntax	: void write_rsc_names(FILE *fp)
 * Input	:
 * Output	:
 * Description	:
 * Remarks	:
 *****************************************************************************
*/
static void write_rsc_names(FILE *fp)
{
	int i, j;

	if(win32)
	{
		/* Write the names */

		for(i = 0; i < rccount; i++)
		{
			res_count_t *rcp;

			rcp = &rcarray[i];

			if(rcp->type.type == name_str)
			{
				char *name = prep_nid_for_label(&(rcp->type));
				fprintf(fp, __ASM_NAME("%s_%s_typename") ":\n",
					prefix,
					name);
				write_name_str(fp, &(rcp->type));
			}

			for(j = 0; j < rcp->count32; j++)
			{
				resource_t *rsc = rcp->rsc32array[j].rsc[0];

				if(rsc->name->type == name_str)
				{
					fprintf(fp, __ASM_NAME("%s%s_name") ":\n",
						prefix,
						rsc->c_name);
					write_name_str(fp, rsc->name);
				}
			}
		}
	}
	else
	{
		/* ResourceNames */
		for(i = 0; i < rccount; i++)
		{
			res_count_t *rcp = &rcarray[i];

			if(rcp->type.type == name_str)
			{
				fprintf(fp, __ASM_NAME("%s_%s_typename") ":\n",
					prefix,
					rcp->type.name.s_name->str.cstr);
				write_name_str(fp, &(rcp->type));
			}
			for(j = 0; j < rcp->count; j++)
			{
				if(rcp->rscarray[j]->name->type == name_str)
				{
					fprintf(fp, __ASM_NAME("%s%s_name") ":\n",
						prefix,
						rcp->rscarray[j]->c_name);
					write_name_str(fp, rcp->rscarray[j]->name);
				}
			}
		}
		/* EndNames */

		/* This is to end the NE resource table */
		if(create_dir)
			fprintf(fp, "\t.byte\t0\n");
	}

	fprintf(fp, "\n");
}

/*
 *****************************************************************************
 * Function	: write_s_file
 * Syntax	: void write_s_file(char *outname, resource_t *top)
 * Input	:
 *	outname	- Filename to write to
 *	top	- The resource-tree to convert
 * Output	:
 * Description	:
 * Remarks	:
 *****************************************************************************
*/
void write_s_file(char *outname, resource_t *top)
{
	FILE *fo;
	resource_t *rsc;

	fo = fopen(outname, "wt");
	if(!fo)
	{
		error("Could not open %s\n", outname);
		return;
	}

	{
		char *s, *p;
		s = ctime(&now);
		p = strchr(s, '\n');
		if(p) *p = '\0';
		fprintf(fo, s_file_head_str, input_name ? input_name : "stdin",
			cmdline, s);
	}

	/* Get an idea how many we have and restructure the tables */
	count_resources(top);

	/* First write the segment tables */
	if(create_dir)
	{
		if(win32)
			write_pe_segment(fo);
		else
			write_ne_segment(fo);
	}

	/* Dump the names */
	write_rsc_names(fo);

	if(create_dir)
		fprintf(fo, ".LResTabEnd:\n");

	if(!indirect_only)
	{
		/* Write the resource data */
	        fprintf(fo, "\n/* Resource binary data */\n\n");
		for(rsc = top; rsc; rsc = rsc->next)
		{
			if(!rsc->binres)
				continue;

			fprintf(fo, "\t.align\t%d\n", win32 ? 4 : alignment);
			fprintf(fo, __ASM_NAME("%s%s_data") ":\n", prefix, rsc->c_name);
			if(global)
				fprintf(fo, "\t.globl\t" __ASM_NAME("%s%s_data") "\n", prefix, rsc->c_name);

			write_s_res(fo, rsc->binres);

			fprintf(fo, "\n");
		}

		if(create_dir)
		{
			/* Add a resource descriptor for built-in and elf-dlls */
			fprintf(fo, "\t.align\t4\n");
			fprintf(fo, __ASM_NAME("%s_ResourceDescriptor") ":\n", prefix);
			fprintf(fo, "\t.globl\t" __ASM_NAME("%s_ResourceDescriptor") "\n", prefix);
			fprintf(fo, __ASM_NAME("%s_ResourceTable") ":\n", prefix);
			if(global)
				fprintf(fo, "\t.globl\t" __ASM_NAME("%s_ResourceTable") "\n", prefix);
			fprintf(fo, "\t.long\t" __ASM_NAME("%s%s") "\n", prefix, win32 ? _PEResTab : _NEResTab);
			fprintf(fo, __ASM_NAME("%s_NumberOfResources") ":\n", prefix);
			if(global)
				fprintf(fo, "\t.globl\t" __ASM_NAME("%s_NumberOfResources") "\n", prefix);
			fprintf(fo, "\t.long\t%d\n", direntries);
			fprintf(fo, __ASM_NAME("%s_ResourceSectionSize") ":\n", prefix);
			if(global)
				fprintf(fo, "\t.globl\t" __ASM_NAME("%s_ResourceSectionSize") "\n", prefix);
			fprintf(fo, "\t.long\t.LResTabEnd - " __ASM_NAME("%s%s") "\n", prefix, win32 ? _PEResTab : _NEResTab);
			if(win32)
			{
				fprintf(fo, __ASM_NAME("%s_ResourcesEntries") ":\n", prefix);
				if(global)
					fprintf(fo, "\t.globl\t" __ASM_NAME("%s_ResourcesEntries") "\n", prefix);
				fprintf(fo, "\t.long\t" __ASM_NAME("%s_ResourceDirectory") "\n", prefix);
			}
		}
	}

	if(indirect)
	{
		/* Write the indirection structures */
	        fprintf(fo, "\n/* Resource indirection structures */\n\n");
		fprintf(fo, "\t.align\t4\n");
		for(rsc = top; rsc; rsc = rsc->next)
		{
			int type;
			char *type_name = NULL;

			if(!rsc->binres)
				continue;

			switch(rsc->type)
			{
			case res_menex:
				type = WRC_RT_MENU;
				break;
			case res_dlgex:
				type = WRC_RT_DIALOG;
				break;
			case res_usr:
				assert(rsc->res.usr->type != NULL);
				type_name = prep_nid_for_label(rsc->res.usr->type);
				type = 0;
				break;
			default:
				type = rsc->type;
			}

			/*
			 * This follows a structure like:
			 * struct wrc_resource {
			 * 	INT32	id;
			 *	RSCNAME	*resname;
			 *	INT32	restype;
			 *	RSCNAME	*typename;
			 *	void	*data;
			 *	UINT32	datasize;
			 * };
			 * The 'RSCNAME' is a pascal-style string where the
			 * first byte/word denotes the size and the rest the string
			 * itself.
			 */
			fprintf(fo, __ASM_NAME("%s%s") ":\n", prefix, rsc->c_name);
			if(global)
				fprintf(fo, "\t.globl\t" __ASM_NAME("%s%s") "\n", prefix, rsc->c_name);
                        if (rsc->name->type == name_ord)
                            fprintf(fo, "\t.long\t%d, 0, ", rsc->name->name.i_name );
                        else
                            fprintf(fo, "\t.long\t0, " __ASM_NAME("%s%s_name") ", ",
                                    prefix, rsc->c_name );
                        if (type)
                            fprintf(fo, "%d, 0, ", type);
                        else
                            fprintf(fo, "0, " __ASM_NAME("%s_%s_typename") ", ",
                                    prefix, type_name );

                        fprintf(fo, __ASM_NAME("%s%s_data") ", %d\n",
				prefix,
				rsc->c_name,
				rsc->binres->size - rsc->binres->dataidx);
			fprintf(fo, "\n");
		}
		fprintf(fo, "\n");

		/* Write the indirection table */
		fprintf(fo, "/* Resource indirection table */\n\n");
		fprintf(fo, "\t.align\t4\n");
		fprintf(fo, __ASM_NAME("%s%s") ":\n", prefix, _ResTable);
		fprintf(fo, "\t.globl\t" __ASM_NAME("%s%s") "\n", prefix, _ResTable);
		for(rsc = top; rsc; rsc = rsc->next)
		{
			fprintf(fo, "\t.long\t" __ASM_NAME("%s%s") "\n", prefix, rsc->c_name);
		}
		fprintf(fo, "\t.long\t0\n");
		fprintf(fo, "\n");
	}

	if(auto_register)
		fprintf(fo, s_file_autoreg_str, prefix, _ResTable);

	fprintf(fo, s_file_tail_str);
	fclose(fo);
}

/*
 *****************************************************************************
 * Function	: write_h_file
 * Syntax	: void write_h_file(char *outname, resource_t *top)
 * Input	:
 *	outname	- Filename to write to
 *	top	- The resource-tree to convert
 * Output	:
 * Description	:
 * Remarks	:
 *****************************************************************************
*/
void write_h_file(char *outname, resource_t *top)
{
	FILE *fo;
	resource_t *rsc;

	fo = fopen(outname, "wt");
	if(!fo)
	{
		error("Could not open %s\n", outname);
	}

	fprintf(fo, h_file_head_str, input_name ? input_name : "stdin",
                cmdline, ctime(&now), (long)now, (long)now);

	/* First write the segment tables reference */
	if(create_dir)
	{
		fprintf(fo, "extern %schar %s%s[];\n\n",
			constant ? "const " : "",
			prefix,
			win32 ? _PEResTab : _NEResTab);
	}

	/* Write the resource data */
	for(rsc = top; global && rsc; rsc = rsc->next)
	{
		if(!rsc->binres)
			continue;

		fprintf(fo, "extern %schar %s%s_data[];\n",
			constant ? "const " : "",
			prefix,
			rsc->c_name);
	}

	if(indirect)
	{
		if(global)
			fprintf(fo, "\n");

		/* Write the indirection structures */
		for(rsc = top; global && rsc; rsc = rsc->next)
		{
			fprintf(fo, "extern %swrc_resource%d_t %s%s;\n",
				constant ? "const " : "",
				win32 ? 32 : 16,
				prefix,
				rsc->c_name);
		}

		if(global)
			fprintf(fo, "\n");

		/* Write the indirection table */
		fprintf(fo, "extern %swrc_resource%d_t %s%s[];\n\n",
			constant ? "const " : "",
			win32 ? 32 : 16,
			prefix,
			_ResTable);
	}

	fprintf(fo, h_file_tail_str);
	fclose(fo);
}
