/************************************************
 *
 * Converting binary resources from/to *.rc files
 *
 * Copyright 1999 Juergen Schmied
 *
 * 11/99 first release
 *
 * 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"

#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>

#include <ctype.h>
#include <string.h>

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

#include <fcntl.h>
#include <unistd.h>
#ifdef HAVE_SYS_MMAN_H
# include <sys/mman.h>
#endif

#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"

extern char*   g_lpstrFileName;

/* global options */

char*	g_lpstrFileName = NULL;
char*   g_lpstrInputFile = NULL;
int	b_to_binary = 0;
int	b_force_overwrite = 0;

static char*    errorOpenFile = "Unable to open file.\n";
static char*    errorRCFormat = "Unexpexted syntax in rc file line %i\n";

void usage(void)
{
    printf("Usage: bin2res [-d bin] [input file]\n");
    printf("  -d bin convert a *.res back to a binary\n");
    printf("  -f force overwriting newer files\n");
    exit(-1);
}

void parse_options(int argc, char **argv)
{
  int i;

  switch( argc )
  {
    case 2:
	 g_lpstrInputFile = argv[1];
	 break;

    case 3:
    case 4:
    case 5:
	 for( i = 1; i < argc - 1; i++ )
	 {
	   if( argv[i][0] != '-' ||
	       strlen(argv[i]) != 2 ) break;

	   if( argv[i][1] == 'd')
	   {
	     if (strcmp ("bin", argv[i+1])==0)
	     {
	       b_to_binary =1;
	       i++;
	     }
	     else
	     {
	       usage();
	     }

	   }
	   else if ( argv[i][1] == 'f')
	   {
	     b_force_overwrite = 1;
	   }
	   else
	   {
	     usage();
	   }
	 }
	 if( i == argc - 1 )
	 {
	   g_lpstrInputFile = argv[i];
	   break;
	 }
    default: usage();
  }
}

int insert_hex (char * infile, FILE * outfile)
{
#ifdef	HAVE_MMAP
	unsigned int i;
	int 		fd;
	struct stat	st;
	LPBYTE p_in_file = NULL;

	if( (fd = open( infile, O_RDONLY))==-1 )
	{
	  fprintf(stderr, errorOpenFile );
	  exit(1);
	}
        if ((fstat(fd, &st) == -1) || (p_in_file = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == (void *)-1)
        {
	  fprintf(stderr, errorOpenFile );
          close(fd);
	  exit(1);
        }

	fprintf (outfile, "{\n '");
	i = 0;
	while (1)
	{
	  fprintf(outfile, "%02X", p_in_file[i]);
	  if (++i >= st.st_size) break;
	  fprintf(outfile, "%s", (i == (i & 0xfffffff0)) ? "'\n '" :" ");
	}
	fprintf (outfile, "'\n}");
        munmap(p_in_file, st.st_size);
        close(fd);
	return 1;
#else	/* HAVE_MMAP */
	FILE*	fp;
	struct stat	st;
	unsigned int	i;
	int		c;

	fp = fopen( infile, "r" );
	if ( fp == NULL )
	{
	  fprintf(stderr, errorOpenFile );
	  exit(1);
	}
	if (fstat(fileno(fp), &st) == -1)
	{
	  fprintf(stderr, errorOpenFile );
	  fclose(fp);
	  exit(1);
	}

	fprintf (outfile, "{\n '");
	i = 0;
	while (1)
	{
	  c = fgetc(fp);
	  if ( c == EOF )
	  {
	    fprintf(stderr, errorOpenFile );
	    fclose(fp);
	    exit(1);
	  }
	  fprintf(outfile, "%02X", c);
	  if (++i >= st.st_size) break;
	  fprintf(outfile, "%s", (i == (i & 0xfffffff0)) ? "'\n '" :" ");
	}
	fprintf (outfile, "'\n}");

	fclose(fp);
	return 1;
#endif	/* HAVE_MMAP */
}

int convert_to_res ()
{
	FILE 	*fin, *ftemp;
	char	buffer[255];
	char	infile[255];
	char	tmpfile[255];
	char	*pos;
	int	c, len;
	struct stat	st;
	int line = 0;
	time_t	tinput;
	long startpos, endpos;

        strcpy( tmpfile, g_lpstrInputFile );
        strcat( tmpfile, "-tmp" );
        /* FIXME: should use better tmp name and create with O_EXCL */
	if( (ftemp = fopen( tmpfile, "w")) == NULL ) goto error_open_file;

	if( (fin = fopen( g_lpstrInputFile, "r")) == NULL || stat(g_lpstrInputFile, &st)) goto error_open_file;
	tinput = st.st_ctime;

	while ( NULL != fgets(buffer, 255, fin))
	{
	  fputs(buffer, ftemp);
	  line++;
	  if ( (pos = strstr(buffer, "BINRES")) != NULL)
	  {
	    /* get the out-file name */
	    len = 0; pos += 6; startpos=0; endpos=0;
	    while ( *pos == ' ') pos++;
	    while ( pos[len] != ' ') len++;
	    strncpy(infile, pos, len);
	    infile[len]=0;

	    if ( (!stat(infile, &st) && st.st_ctime > tinput) || b_force_overwrite)
	    {
	      /* write a output file */
	      printf("[%s:c]", infile);
	      while((c = fgetc(fin))!='{' && c != EOF) fputc(c, ftemp);
	      if (c == EOF ) goto error_rc_format;
	      while((c = fgetc(fin))!='}' && c != EOF);
	      if (c == EOF ) goto error_rc_format;

	      insert_hex(infile, ftemp);
	    }
	    else
	    {
	      printf("[%s:s]", infile);
	    }
	  }
	}

        fclose(fin);
	fclose(ftemp);

	if (unlink(g_lpstrInputFile) == -1)
	{
            perror("unlink");
            unlink(tmpfile);
            return 0;
	}
	if (rename(tmpfile, g_lpstrInputFile) == -1)
        {
            perror("rename");
            unlink(tmpfile);
            return 0;
        }
	return 1;

error_open_file:
	fprintf(stderr, errorOpenFile );
	return 0;

error_rc_format:
	fprintf(stderr, errorRCFormat, line);
	return 0;
}

int convert_to_bin()
{
	FILE 	*fin, *fout;
	char	buffer[255];
	char	outfile[255];
	char	*pos;
	int	len, index, in_resource;
	unsigned int	byte;
	struct stat	st;
	int line = 0;
	time_t	tinput;

	if( (fin = fopen( g_lpstrInputFile, "r")) == NULL || stat(g_lpstrInputFile, &st)) goto error_open_file;
	tinput = st.st_ctime;

	while ( NULL != fgets(buffer, 255, fin))
	{
	  line++;
	  if ( (pos = strstr(buffer, "BINRES")) != NULL)
	  {
	    /* get the out-file name */
	    len = 0; pos += 6;
	    while ( *pos == ' ') pos++;
	    while ( pos[len] != ' ') len++;
	    strncpy(outfile, pos, len);
	    outfile[len]=0;

	    if ( stat(outfile, &st) || st.st_ctime < tinput || b_force_overwrite)
	    {
	      /* write a output file */
	      printf("[%s:c]", outfile);
	      if ( (fout = fopen( outfile, "w")) == NULL) goto error_open_file;

	      in_resource = 0;
	      while (1)
	      {
	        if ( NULL == fgets(buffer, 255, fin)) goto error_rc_format;
	        line++;

	        /* parse a line */
	        for ( index = 0; buffer[index] != 0; index++ )
	        {
	          if ( ! in_resource )
		  {
		    if ( buffer[index] == '{' ) in_resource = 1;
		    continue;
		  }

	          if ( buffer[index] == ' ' || buffer[index] == '\''|| buffer[index] == '\n' ) continue;
	          if ( buffer[index] == '}' ) goto end_of_resource;
	          if ( ! isxdigit(buffer[index])) goto error_rc_format;
		  index += sscanf(&buffer[index], "%02x", &byte);
		  fputc(byte, fout);
	        }
	      }
	      fclose(fout);
	    }
	    else
	    {
	      printf("[%s:s]", outfile);
	    }
end_of_resource: ;
	  }
	}

        fclose(fin);
	return 1;

error_open_file:
	fprintf(stderr, errorOpenFile );
	return 0;

error_rc_format:
	fprintf(stderr, errorRCFormat, line);
	return 0;
}

int main(int argc, char **argv)
{
	parse_options( argc, argv);

	if (b_to_binary == 0)
	{
	  convert_to_res();
	}
	else
	{
	  convert_to_bin();
	}
	printf("\n");
	return 0;
}
