/*
Copyright (C) 2007 Mark Olsen

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

/*
 * Changes:
 * v1.2: (2007/12/30)
 *  - Now strips away stuffcmds beginning with 'alias'. This fixes oversize
 *    messages produced by some versions of MVDSV.
 *  - Fixed a silly endian bug.
 *
 * v1.1: (2007/11/01)
 *  - Fixed a bug in the DEMO_MULTIPLE handling reported by qqshka.
 *  - Added support for DEMO_SET.
 */

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

#define DEMO_READ      1
#define DEMO_SET       2
#define DEMO_MULTIPLE  3
#define DEMO_SINGLE    4
#define DEMO_STATS     5
#define DEMO_ALL       6


#define SC_PRINT         8
#define SC_STUFFCMD      9
#define SC_CHOKECOUNT   44

union endiantest
{
	int i;
	char c;
};

static const union endiantest endiantest =
{
	.i = 1
};

unsigned int little32(unsigned int value)
{
	if (endiantest.c == 0)
		value = ((value&0xff000000)>>24)|((value&0x00ff0000)>>8)|((value&0x0000ff00)<<8)|((value&0x000000ff)<<24);
	
	return value;
}

static void stripmessage(unsigned char *buffer, unsigned int *size)
{
	unsigned int index;
	unsigned int j;

	index = 0;

	while(index < *size)
	{
		index++;

		switch(buffer[index-1])
		{
			case SC_PRINT:
				while(index < *size && buffer[index])
					index++;

				if (buffer[index] != 0)
				{
					printf("Warning: Premature end of printf data\n");
					break;
				}

				index++;

				break;

			case SC_STUFFCMD:
				j = index;

				while(index < *size && buffer[index])
					index++;

				if (buffer[index] != 0)
				{
					printf("Warning: Premature end of stuffcmd data\n");
					break;
				}

				index++;

#if 0
				printf("Stuffcmd: %s\n", buffer+j);
#endif

				if (strncmp(buffer+j, "alias", 5) == 0)
				{
					if (index != *size)
						memmove(buffer+j-1, buffer+index, *size-index);

					*size-= index - j + 1;

					index = j - 1;
				}

				break;

			case SC_CHOKECOUNT:
				index++;
				break;

			default:
#if 0
				printf("Unknown server command type %d\n", buffer[index-1]);
#endif

				index = *size;
				break;
		}
	}
}

static int demoprocessloop(FILE *infile, FILE *outfile)
{
	unsigned char *buffer;
	unsigned int buffersize;
	unsigned char command;
	unsigned char mvdtime;
	unsigned char tempchar;
	unsigned int size;
	unsigned int multiple;
	int writesize;
	int multiplepresent;
	int i;

	buffer = 0;
	buffersize = 0;

	mvdtime = 0;

	while(1)
	{
		i = fread(&tempchar, 1, 1, infile);
		if (i == 0)
		{
			printf("End of file. All good!\n");
			return 1;
		}

		mvdtime+= tempchar;

		i = fread(&command, 1, 1, infile);
		if (i != 1)
		{
			printf("Unexpected end of demo.\n");
			return 0;
		}

		multiplepresent = 0;
		writesize = 0;
		switch(command&0x7)
		{
			case DEMO_MULTIPLE:
				i = fread(&multiple, 4, 1, infile);
				/* Thanks qqshka! */
				if (i != 1)
				{
					printf("Unexpected end of demo.\n");
					return 0;
				}
				multiplepresent = 1;
			case DEMO_SINGLE:
			case DEMO_STATS:
			case DEMO_ALL:
			case DEMO_READ:
				i = fread(&size, 4, 1, infile);
				if (i != 1)
				{
					printf("Unexpected end of demo.\n");
					return 0;
				}

				size = little32(size);

				writesize = 1;

				break;

			case DEMO_SET:
				size = 8;
				break;

			default:
				printf("Unsupported packet type %d!\n", command&0x7);
				return 0;
		}

		if (size)
		{
			if (size > buffersize)
			{
				free(buffer);

				buffer = malloc(size);
				if (buffer == 0)
				{
					printf("Out of memory.\n");
					return 0;
				}

				buffersize = size;
			}

			i = fread(buffer, size, 1, infile);
			if (i != 1)
			{
				printf("Unexpected end of demo.\n");
				return 0;
			}

			stripmessage(buffer, &size);

			if (size)
			{
				i = fwrite(&mvdtime, 1, 1, outfile);
				i+= fwrite(&command, 1, 1, outfile);
				if (multiplepresent)
					i+= fwrite(&multiple, 4, 1, outfile);
				if (i != 2+multiplepresent)
				{
					printf("Unable to write to output file.\n");
					return 0;
				}

				i = 0;
				if (writesize)
				{
					size = little32(size);
					i = fwrite(&size, 4, 1, outfile);
					size = little32(size);
				}

				if (size > 1450*2)
				{
					printf("Packet size %d is too large!\n", size);
					printf("Please send this demo to bigfoot@private.dk\n");
					return 0;
				}

				i+= fwrite(buffer, size, 1, outfile);
				if (i != 1+writesize)
				{
					printf("Unable to write to output file.\n");
					return 0;
				}

				mvdtime = 0;
			}
		}
	}
}

int main(int argc, char **argv)
{
	FILE *infile;
	FILE *outfile;
	int error;

	if (argc != 3)
	{
		printf("Usage: %s <infile> <outfile>\n", argv[0]);
		return 0;
	}

	infile = fopen(argv[1], "rb");
	if (infile == 0)
	{
		printf("Unable to open \"%s\" for reading.\n", argv[1]);
	}
	else
	{
		outfile = fopen(argv[2], "wb");
		if (outfile == 0)
		{
			printf("Unable to open \"%s\" for writing.\n", argv[2]);
		}
		else
		{
			error = !demoprocessloop(infile, outfile);

			fclose(outfile);

			if (error)
				remove(argv[2]);
		}

		fclose(infile);
	}

	return 0;
}

