/*	DataUtil.c
 *
 *		Data manipulation utilities
 */

/**********************************************************************

  YAAF is Copyright 1997-1999 by William Woody.

  Redistribution and use in source and binary forms, with or without 
  modification, are permitted provided that the following conditions 
  are met:

  Redistributions of source code must retain the above copyright notice, 
  this list of conditions and the following disclaimer.

  Neither the name of the copyright holder or other contributors may
  be used to promote products derived from this software without
  specific written prior permission.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
  SOFTWARE.
  
  Contact:   woody@alumni.caltech.edu
  Web page:  http://www.pandawave.com/yaaf/

**********************************************************************/

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

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

/*	SwapWord
 *
 *		Swap this 2 byte word
 */

uint16 SwapWord(uint16 c)
{
	uint8 tmp;
	uint8 *ptr = (uint8 *)&c;
	
	tmp = ptr[0];
	ptr[0] = ptr[1];
	ptr[1] = tmp;
	
	return c;
}

/*	SwapLong
 *
 *		Swap this four byte word
 */

uint32 SwapLong(uint32 c)
{
	uint8 tmp;
	uint8 *ptr = (uint8 *)&c;
	
	tmp = ptr[0];
	ptr[0] = ptr[3];
	ptr[3] = tmp;
	tmp = ptr[1];
	ptr[1] = ptr[2];
	ptr[2] = tmp;
	
	return c;
}

/*	SwapFloat
 *
 *		Swap this 4 byte floating point value
 */

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

/*	SwapDouble
 *
 *		Swap the byte order of this 8 byte double.
 */

double SwapDouble(double c)
{
	uint8 tmp;
	uint8 *ptr = (uint8 *)&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													*/
/*																		*/
/************************************************************************/

/*$	GetResourceMessage
 *
 *		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;
	uint8 *ptr;

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

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

#if OPT_XWIN
	uint8 *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											*/
/*																		*/
/************************************************************************/

/*$	yformat
 *
 *		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);
}

/*$	vyformat
 *
 *		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
 *
 *		The idea is that in order to get a small application footprint,
 *	sometimes it's desirable not to have to link in the entire ANSI
 *	library. It's also desirable to be able to reorder the arguments,
 *	for example, if you are performing internationalization. (A limit
 *	of sprintf() is that the arguments are inserted in same order they
 *	are encountered in the variable argument list.)
 */

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
				 */
				
				pchar *s;
				pchar c;
				
				s = (pchar *)(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;
}
