/*	DataUtil.c
 *
 *		Data manipulation utilities
 */
 
/*  YAAF - Yet another application framework
 *  Copyright (C) 1997, 1998 William Edward Woody
 *  
 *  This library is free software; you can redistribute it
 *  and/or modify it under the terms of the GNU Library
 *  General Public License as published by the Free Software
 *  Foundation; either version 2 of the License, or 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 MERCHANTABIILITY or FITNESS FOR A PARTICULAR
 *  PURPOSE. See the GNU Library General Public License for
 *  more details.
 *  
 *  You should have received a copy of the GNU Library 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.
 *  
 *  To contact the author, either e-mail me at woody@pandawave.com, 
 *	or write to
 *  
 *          William Edward Woody
 *          In Phase Consulting
 *          1545 Ard Eevin Avenue
 *          Glendale, CA 91202
 *
 *	Or visit us on the web at http://www.pandawave.com
 */

#include <XConfig.h>			// must be first to optimize compile on MSVC++
#include <XDataUtil.h>
#include <stdio.h>

/************************************************************************/
/*																		*/
/*	String Conversion													*/
/*																		*/
/************************************************************************/

/*	@F"cvtc2p"
 *	@H"XDataUtil.h"
 *	@I
 *
 *		Convert a string from C to Pascal. Returns the Pascal string.
 */

unsigned char *cvtc2p(unsigned char *dest, const char *src)
{
	register unsigned char *ptr;
	register unsigned char len = 0;
	
	ptr = dest+1;
	while (*src) {
		*ptr++ = (unsigned char)*src++;
		len++;
	}
	*dest = len;
	return dest;
}

/*	@F"cvtp2c"
 *	@H"XDataUtil.h"
 *	@I
 *
 *		Convert from Pascal to C format string. Returns the C string.
 */

char *cvtp2c(char *dest, const unsigned char *src)
{
	register char *ptr;
	register unsigned char len;
	
	ptr = dest;
	len = *src++;
	while (len--) *ptr++ = (char)*src++;
	*ptr = 0;
	
	return dest;
}

/************************************************************************/
/*																		*/
/*	Pascal string manipulation											*/
/*																		*/
/************************************************************************/

/*	@F"pstrcpy"
 *	@H"XDataUtil.h"
 *	@I
 *
 *		Pascal string copy operation.
 */

unsigned char *pstrcpy(unsigned char *dest, const unsigned char *src)
{
	register unsigned char len;
	register unsigned char *ptr;
	
	len = *src;
	ptr = dest;
	*ptr++ = *src++;
	while (len--) *ptr++ = *src++;
	
	return dest;
}

/*	@F"pstrcat"
 *	@H"XDataUtil.h"
 *	@I
 *
 *		Concatenate source string to the end of the dest string.
 */

unsigned char *pstrcat(unsigned char *dest, const unsigned char *src)
{
	register unsigned char len;
	register unsigned char *ptr;
	
	len = *src++;
	ptr = dest + *dest + 1;
	*dest += len;
	while (len--) *ptr++ = *src++;
	
	return dest;
}

/*	@F"pstrcmp"
 *	@H"XDataUtil.h"
 *	@I
 *
 *		Compare two pascal strings. This works like the C string compare
 */

long pstrcmp(const unsigned char *a, const unsigned char *b)
{
	register unsigned char alen;
	register unsigned char blen;
	
	alen = *a++;
	blen = *b++;
	while (alen && blen) {
		if (*a < *b) return -1;
		if (*a > *b) return 1;
		alen--;
		blen--;
	}
	if (alen < blen) return 1;
	if (alen > blen) return -1;
	return 0;
}

/************************************************************************/
/*																		*/
/*	Byte Swap Utilities													*/
/*																		*/
/************************************************************************/

/*	@F"SwapWord"
 *	@H"XDataUtil.h"
 *  @I
 *
 *		Swap this 2 byte word
 */

unsigned short SwapWord(unsigned short c)
{
	unsigned char tmp;
	unsigned char *ptr = (unsigned char *)&c;
	
	tmp = ptr[0];
	ptr[0] = ptr[1];
	ptr[1] = tmp;
	
	return c;
}

/*	@F"SwapLong"
 *	@H"XDataUtil.h"
 *  @I
 *
 *		Swap this four byte word
 */

unsigned long SwapLong(unsigned long c)
{
	unsigned char tmp;
	unsigned char *ptr = (unsigned char *)&c;
	
	tmp = ptr[0];
	ptr[0] = ptr[3];
	ptr[3] = tmp;
	tmp = ptr[1];
	ptr[1] = ptr[2];
	ptr[2] = tmp;
	
	return c;
}

/*	@F"SwapFloat"
 *	@H"XDataUtil.h"
 *  @I
 *
 *		Swap this 4 byte floating point value
 */

float SwapFloat(float c)
{
	unsigned char tmp;
	unsigned char *ptr = (unsigned char *)&c;
	
	tmp = ptr[0];
	ptr[0] = ptr[3];
	ptr[3] = tmp;
	tmp = ptr[1];
	ptr[1] = ptr[2];
	ptr[2] = tmp;
	
	return c;
}

/*	@F"SwapDouble"
 *	@H"XDataUtil.h"
 *  @I
 *
 *		Swap the byte order of this 8 byte double.
 */

double SwapDouble(double c)
{
	unsigned char tmp;
	unsigned char *ptr = (unsigned char *)&c;
	
	tmp = ptr[0];
	ptr[0] = ptr[7];
	ptr[7] = tmp;
	tmp = ptr[1];
	ptr[1] = ptr[6];
	ptr[6] = tmp;
	tmp = ptr[2];
	ptr[2] = ptr[5];
	ptr[5] = tmp;
	tmp = ptr[3];
	ptr[3] = ptr[4];
	ptr[4] = tmp;
	
	return c;
}

/************************************************************************/
/*																		*/
/*	GetResourceString													*/
/*																		*/
/************************************************************************/

/*	@F"GetResourceMessage"
 *	@H"XDataUtil.h"
 *	@I
 *
 *		This returns two strings, the short string and the long string
 *	for the resource ID specified. Normally this is used for errors and
 *	warnings; the short string is the title, the long string is the
 *	explanation. The long message will be no more than 256 characters
 *	(including the null terminator)
 *
 *		In order to simplify porting to various operating systems, I
 *	use a standard custom resource format generated by CompileMessages
 */
 
short GetResourceMessage(short res, short index, char *smsg, char *lmsg)
{
	short len;
	short result;
	
#if OPT_MACOS
	Handle h;
	unsigned char *ptr;

	h = Get1Resource('EMsg',res);
	if (h == NULL) return 0;
	HLock(h);
	ptr = (unsigned char *)(*h);
#endif

#if OPT_WINOS
	extern HINSTANCE _GInstance;
	HRSRC hrsrc;
	HGLOBAL h;
	unsigned char *ptr;
	
	hrsrc = FindResource(_GInstance,MAKEINTRESOURCE(res),"EMsg");
	if (hrsrc == NULL) return 0;
	h = LoadResource(_GInstance,hrsrc);
	if (h == NULL) return 0;
	ptr = (unsigned char *)LockResource(h);
#endif

#if OPT_XWIN
	unsigned char *ptr = GetResourceData('EMsg',res);
	if (ptr == NULL) return 0;
#endif

	/*
	 *	Now parse the resource pointed to by 'ptr'
	 */
	
	len = NSwapWord(*(short *)(ptr));
	ptr += sizeof(short);
	if ((index < 0) || (index >= len)) result = 0;
	else {
		while (index-- > 0) {
			ptr += *ptr + 1;
			ptr += *ptr + 1;
		}
		
		cvtp2c(smsg,ptr);
		ptr += *ptr + 1;
		cvtp2c(lmsg,ptr);
		result = 1;
	}
	
#if OPT_MACOS
	HUnlock(h);
	ReleaseResource(h);
#endif

#if OPT_WINOS
	UnlockResource(h);
	FreeResource(h);
#endif

	return result;
}


/************************************************************************/
/*																		*/
/*	Simple string formatting											*/
/*																		*/
/************************************************************************/

/*	@F"yformat"
 *	@H"XDataUtil.h"
 *	@I
 *
 *		This is the variable argument version of vyformat. This is
 *	similar in idea to sprintf(), in that you can print a string with
 *	formatting escapes corrisponding to the arguments following.
 */

void yformat(char *out, const char *msg, ...)
{
	va_list vlist;
	
	va_start(vlist,msg);
	vyformat(out,msg,vlist);
	va_end(vlist);
}

/*	@F"vyformat"
 *	@H"XDataUtil.h"
 *	@I
 *
 *		Formatting. The character formatting information is as:
 *
 *			#(N)[SPLXI]
 *
 *		N is the argument number (1..9)
 *		'S' formats strings,
 *		'P' formats pascal strings
 *		'L' formats long integers
 *		'X' formats long integers as hex
 *		'I' formats long integers as characters
 */

void vyformat(char *out, const char *msg, va_list vlist)
{
	int i;
	int l;
	int ix;
	int pos;
	struct {
		int f;
		union {
			char *s;
			long i;
		} u;
	} array[10];
	char c;
	
	/*
	 *	Scan forward and get the arguments
	 */
	
	for (i = 0; i < 10; i++) array[i].f = 0;
	
	l = 0;
	pos = 0;
	for (i = 0; msg[i]; i++) {
		if (msg[i] == '#') {
			if (('1' <= msg[i+1]) && (msg[i+1] <= '9')) {
				c = msg[i+2];
				
				if ((c == 'S') || (c == 'P') || (c == 'L') || 
						(c == 'X') || (c == 'I')) {
					ix = msg[i+1] - '0';
					pos = ix - 1;
					if (ix > l) l = ix;
					
					if ((c == 'S') || (c == 'P')) {
						array[pos].f = 1;
					}
				}
			} else {
				c = msg[i+1];
				
				if ((c == 'S') || (c == 'P') || (c == 'L') || 
						(c == 'X') || (c == 'I')) {
					if ((c == 'S') || (c == 'P')) {
						array[pos].f = 1;
					}

					pos++;
					if (pos > l) l = pos;
				}
			}
		}
	}
	
	if (l > 9) l = 9;
	
	/*
	 *	Now get the array contents
	 */
	
	for (i = 0; i < l; i++) {
		if (array[i].f) {
			array[i].u.s = va_arg(vlist,char *);
		} else {
			array[i].u.i = va_arg(vlist,long);
		}
	}		
	
	/*
	 *	now parse the string and format it
	 */
	
	pos = 0;
	while (*msg != 0) {
		if (*msg == '#') {
			msg++;
			
			if (('1' <= *msg) && (*msg <= '9')) {
				pos = *msg - '1';
				msg++;
			}
			
			if (*msg == 'S') {
				/*
				 *	Format C string
				 */
				
				char *s;
				
				s = array[pos++].u.s;
				while (*s != 0) *out++ = *s++;
			} else if (*msg == 'P') {
				/*
				 *	Format Pascal string
				 */
				
				unsigned char *s;
				unsigned char c;
				
				s = (unsigned char *)(array[pos++].u.s);
				c = *s++;
				while (c-- != 0) *out++ = (char)(*s++);
			} else if (*msg == 'L') {
				/*
				 *	Format integer
				 */
				
				long l;
				char buffer[32];
				int i;
				
				l = array[pos++].u.i;
				i = 0;
				if (l < 0) {
					l = -l;
					*out++ = '-';
				}
				if (l == 0) {
					*out++ = '0';
				} else {
					while (l > 0) {
						buffer[i++] = (l % 10) + '0';
						l /= 10;
					}
					while (i > 0) {
						*out++ = buffer[--i];
					}
				}
			} else if (*msg == 'X') {
				/*
				 *	Format integer as 8 digit hex (with no prefix)
				 */
				
				long l;
				int i;
				int j;
				
				l = array[pos++].u.i;
				for (i = 0; i < 8; i++) {
					j = 0x0F & (l >> (28 - i * 4));
					if (j < 0x0A) {
						*out++ = j + '0';
					} else {
						*out++ = j - 10 + 'A';
					}
				}
			} else if (*msg == 'I') {
				/*
				 *	Format integer as 4 character string
				 */
				
				long l;
				int i;
				int j;
				
				l = array[pos++].u.i;
				for (i = 0; i < 4; i++) {
					j = 0x00FF & (l >> (24 - i * 8));
					if ((j < ' ') || (j >= 0x7F)) {
						*out++ = '.';
					} else {
						*out++ = j;
					}
				}
			} else {
				*out++ = '#';
				*out++ = *msg;
			}
			msg++;
		} else {
			*out++ = *msg++;
		}
	}
	*out = 0;
}
