/* =======================
     low_level functions
   ======================= */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include "config.h"
/* ryk ==> */
#ifdef ZTOM_STATIC_LIB	/* Static lib */
	#include "etc.h"
	#include "setjmp.h"
	jmp_buf		g_ztommark;
	char*		g_ztommsgp = NULL;
	int			g_ztommsglen = 0;
#endif
/* <== ryk */
#define	FILENOTFOUND	1
#define	NONZMD			2
#define	ZMDVERERR		3
#define	READERR			4
#define	WRITEERR		5
#define	FLUSHERR		6
#define	SWERR			7
#define	TOOMANYFILES	8
#define	WAVNOTDEFIND	9
#define	NOMEMORY		10
#define	ILLEGALSWITCH	11
#define	ILLEGALMEMORY	12
typedef struct memDebugData {
	struct memDebugData *next;
	/* struct memDebugData *prev; */
	DWORD	size;		/* malloced size */
	void	*adrs;		/* malloced area */
	char	*label;		/* label name */
} MEMDEBUGDATA;
#ifdef MEMDEBUG
void memdebug(void);
#endif
MEMDEBUGDATA *searchHimself(void *adrs, char *label, char f);
void initMemMaster(void);
void efreeAll(void);
void efreeAll2(char *label);
void efree(void *ptr, char *label);
void *emalloc(size_t n, char *label);
void *erealloc(void *ptr,size_t n, char *label);
void *emarealloc(void *ptr,size_t n, char *label);
void fatal(int ret,char *fmt, ...);
void terminateProgram(int);
inline WORD  getWordAlign(UBYTE*);
inline DWORD getDwordAlign(UBYTE*);
inline void  putWordAlign(UBYTE*,WORD);
inline void  putDwordAlign(UBYTE*,DWORD);
inline WORD  getWord(UBYTE*);
inline DWORD getDword(UBYTE*);
inline void  putWord(UBYTE*,WORD);
inline void  putDword(UBYTE*,DWORD);
/* ryk ==> */
void ZTOMFormatMessage(FILE* fp, const char* fmt, ...);
void ZTOMAddMessage(FILE* fp, const char* pmsg);
#ifdef ZTOM_STATIC_LIB	/* Static lib */
#define	MAXERRMSG		16384
void ZTOMCreateMessageBuf(void);
char* ZTOMGetMessageBuf(void);
void ZTOMDisposeMessageBuf(void);
#endif
/* <== ryk */
#ifdef UNIXY_OS
	/* ---- for use of `ntohs' ,`ntohl', `htons', and `htonl' ---- */
	#ifdef	FreeBSD
		#include <sys/param.h>
	#else
		#include <sys/types.h>
		#include <netinet/in.h>
	#endif
#endif	/* UNIXY_OS */
#ifdef A
static MEMDEBUGDATA memMaster = {
	NULL,	/* next */
	0,		/* size */
	NULL,	/* adrs */
	NULL	/* label */
};
#endif
MEMDEBUGDATA *memMaster = NULL;
#ifdef MEMDEBUG
static int nn=0,cc=0;
#endif
void initMemMaster(void)
{
	memMaster = (MEMDEBUGDATA*)malloc(sizeof(MEMDEBUGDATA) * 1);
	if (memMaster==(MEMDEBUGDATA*)NULL) {
/* ryk ==> */
		ZTOMAddMessage(stderr, "memMaster initialize failed.\n");
#ifdef ZTOM_STATIC_LIB	/* Static lib */
		longjmp(g_ztommark, ILLEGALMEMORY);
#else
		exit(ILLEGALMEMORY);
#endif
/* <== ryk */
	}
	memMaster->next = NULL;
	memMaster->size = 0;
	memMaster->adrs = NULL;
	memMaster->label = NULL;
/* fprintf(stderr,"[%p]\n",memMaster); */
}
/* #ifdef MEMDEBUG */
void memdebug(void)
{
	MEMDEBUGDATA *m = memMaster->next;
#ifdef MEMDEBUG
	int c = 0;
#endif
	while (m != NULL) {
#ifdef MEMDEBUG
		fprintf(stderr,"%p %d %s/%p\n",m->adrs, m->size, m->label, m->next);
		c++;
#endif
		m = m->next;
	}
#ifdef MEMDEBUG
	if (c) {
		fprintf(stderr, "%d memory blocks are still allocated.\n", c);
	}
#endif
}
/* #endif */
void efreeAll(void)
{
	MEMDEBUGDATA *m;
	int c = 0;
	if (memMaster) {
		m = memMaster->next;
		while (m) {
			MEMDEBUGDATA *n = m->next;
#ifdef MEMDEBUG
			fprintf(stderr,"%p %d %s\n",m->adrs, m->size, m->label);
#endif
			efree(m->adrs, m->label);
			m = n;
			c++;
		}
		free(memMaster);
#ifdef MEMDEBUG
		if (c) {
			fprintf(stderr, "%d memory blocks are freed.\n", c);
		}
#endif
		memMaster = NULL;
	}
#ifdef MEMDEBUG
if (nn) {
	fprintf(stderr,"[%d,%d,%d]",nn,cc,cc/nn);
}
#endif
}
void efreeAll2(char *label)
{
	MEMDEBUGDATA *m, *n;
/* fprintf(stderr,"efreeAll2: %s\n",label); */
	if (memMaster) {
		m = memMaster;
		n = m->next;
		while (n) {
/* fprintf(stderr,"a[%s]",n->label); */
			if (!strcmp(n->label,label)) {
/* fprintf(stderr,"0/%p/",n); */
				m->next = n->next;
				free(n->label);
				free(n->adrs);
				free(n);
				n = m;
				/* n = m; */
			}
			m = n;
			if (n) {
/* fprintf(stderr,"!"); */
				n = n->next;
/* fprintf(stderr,"8\n"); */
			}
		}
	}
/* fprintf(stderr,"9"); */
}
void efree(void *ptr, char *label)
{
#ifdef MEMDEBUG
nn++;
#endif
	if (ptr) {
		MEMDEBUGDATA *p, *m = NULL, *n = NULL;
		p = searchHimself(ptr, label, 'F');
		if (p) {
			if (p->next) {
				m = p->next;
			}
			if (m && m->next) {
				n = m->next;
			}
			free(m->label);
			free(m);
			p->next = n;
			free(ptr);
		}
	}
}
/* =======================================
     malloc function with error handling
   ======================================= */
void *emalloc(size_t n, char *label)
{
	void *pt;
	MEMDEBUGDATA *m;
	if ( (pt = malloc(n)) == NULL ) {
		fatal(NOMEMORY,"malloc failed.\n");
	}
	if ( (m = malloc(sizeof(MEMDEBUGDATA) * 1)) == NULL) {
		free(pt);
		fatal(NOMEMORY,"malloc failed.\n");
	}
	m->next = memMaster->next;
	memMaster->next = m;
	m->size = n;
	m->adrs = pt;
#ifdef MEMDEBUG
if (pt == NULL) {
	fprintf(stderr,"!!!MALLOC NULL!!!\n");
}
#endif
	m->label = malloc(sizeof(char) * strlen(label) + 1);
	if (m->label == NULL) {
		fatal(NOMEMORY,"malloc failed.\n");
	}
	strcpy(m->label, label);
#ifdef MEMDEBUG
/* fprintf(stderr,"M: %p %d %s\n", pt, n, label); */
#endif
	return pt;
}
/* ========================================
     search malloced table before
     If it found himself, return the "previous" pointer
     or NULL
   ======================================== */
/* #ifdef MEMDEBUG */
MEMDEBUGDATA *searchHimself(void *adrs, char *label, char f)
{
	MEMDEBUGDATA *m = memMaster;
	MEMDEBUGDATA *p = NULL;		/* previous adrs */
	MEMDEBUGDATA *r = NULL;		/* return value */
	if (!memMaster) {
		return NULL;
	}
	while (m->next != NULL) {
		p = m;
		m = m->next;
#ifdef MEMDEBUG
if (f=='F')
cc++;
#endif
		if (m->adrs == adrs) {
			r = p;
			break;
		}
	}
	if (r == NULL) {
/*		memdebug();
*/
		fatal (ILLEGALMEMORY,"MEMORY LEAK FOUND: mallocated table search failed.\n(%p [%c]%s)\n",adrs,f,label);
	}
	return r;
}
/* #endif */
/* ========================================
     realloc function with error handling
   ======================================== */
void *erealloc(void *ptr, size_t n, char *label)
{
	void *pt;
	MEMDEBUGDATA *m = searchHimself(ptr, label, 'r');
	if ( (pt = realloc(ptr,n)) == NULL ) {
#ifdef MEMDEBUG
		/* memdebug(); */
#endif
		fprintf(stderr,"%s: %p -> %d",label, ptr,n);
		fatal(NOMEMORY,"realloc failed.\n");
	}
	m = m->next;
	m->size = n;
	m->adrs = pt;
#ifdef MEMDEBUG
if (pt == NULL) {
	fprintf(stderr,"!!!REALLOC NULL!!!\n");
}
#endif
	m->label = realloc(m->label, strlen(m->label) + strlen(label) + 1);
	if (m->label == NULL) {
		fatal(NOMEMORY,"realloc failed.\n");
	}
	strcat(m->label, label);
#ifdef MEMDEBUG
/* fprintf(stderr,"R: %p %d %s\n", pt, n, label); */
#endif
	return pt;
}
/* ========================
     fatal error
   ======================== */
void fatal(int ret,char *fmt, ...)
{
	va_list ap;
/* ryk ==> */
#ifdef ZTOM_STATIC_LIB	/* Static lib */
	extern jmp_buf g_ztommark;
	unsigned char msg[256];
	va_start(ap,fmt);
	vsprintf(msg,fmt,ap);
	va_end(ap);
	ZTOMAddMessage(stderr, msg);
	terminateProgram(ret);
	longjmp(g_ztommark, ret);
#else
	va_start(ap,fmt);
	vfprintf(stderr,fmt,ap);
	va_end(ap);
	terminateProgram(ret);
#endif
/* <== ryk */
}
/* ===============================================
     program termination with cleaning-up memory
   =============================================== */
void terminateProgram(int ret)
{
	efreeAll();
/* ryk ==> */
#ifdef ZMC2_STATIC_LIB	/* Static lib */
	return;
#else
	exit(ret);
#endif
/* <== ryk */
}
/* =========================
	 read WORD,DWORD data
	 big/little endian OK
		for even-byte align only
		input:pointer
		output:number
   ========================= */
inline WORD getWordAlign(UBYTE *zmd)
{
#ifdef X68000
	return *(WORD*)zmd;
#else
	WORD data;
	data  = *zmd++ << 8;
	data += *zmd;
	return  data;
#endif
}
inline DWORD getDwordAlign(UBYTE *zmd)
{
#ifdef X68000
	return *(DWORD*)zmd;
#else
	int data;
	data  = (*zmd++ << 24);
	data += (*zmd++ << 16);
	data += (*zmd++ <<  8);
	data +=  *zmd;
	return data;
#endif
}
/* ============================
	 write WORD,DWORD data
	 big/little endian OK
		for even-byte align only
		input:number,pointer
   ============================ */
inline void putWordAlign(UBYTE *pos,WORD data)
{
#ifdef UNIXY_OS
	WORD	val;
	UBYTE	*ptr;
	val = htons(data);
	ptr = (UBYTE*)&val;
	*pos++ = *ptr++;
	*pos++ = *ptr++;
#elif defined X68000
	*(WORD*)pos = data;
#else
	*pos++ = data >> 8;
	*pos   = (data & 0xFF);
#endif
}
inline void putDwordAlign(UBYTE *pos,DWORD data)
{
#ifdef UNIXY_OS
	DWORD	val;
	UBYTE	*ptr;
	val = htonl(data);
	ptr = (UBYTE*)&val;
	*pos++ = *ptr++;
	*pos++ = *ptr++;
	*pos++ = *ptr++;
	*pos++ = *ptr++;
#elif defined X68000
	*(DWORD*)pos = data;
#else
	*pos++ =  data >> 24;
	*pos++ = (data >> 16) & 0xFF;
	*pos++ = (data >>  8) & 0xFF;
	*pos   = (data & 0xFF);
#endif
}
/* ============================
	 read WORD,DWORD data
		input:pointer
		output:number
   ============================ */
inline WORD getWord(UBYTE *zmd)
{
	WORD data;
	data  = *zmd++ << 8;
	data |= *zmd;
	return  data;
}
inline DWORD getDword(UBYTE *zmd)
{
	DWORD data;
	data  = (*zmd++ << 24);
	data |= (*zmd++ << 16);
	data |= (*zmd++ <<  8);
	data |=  *zmd;
	return data;
}
/* ============================
	 write WORD,DWORD data
		input:number,pointer
   ============================ */
inline void putWord(UBYTE *pos,WORD data)
{
	*pos++ = data >> 8;
	*pos   = (data & 0xFF);
}
inline void putDword(UBYTE *pos,DWORD data)
{
	*pos++ =  data >> 24;
	*pos++ = (data >> 16) & 0xFF;
	*pos++ = (data >>  8) & 0xFF;
	*pos   = (data & 0xFF);
}
/* ryk ==> */
/* =============================
	 Buffering error messages
   ============================= */
#ifdef ZTOM_STATIC_LIB	/* Static lib */
void ZTOMCreateMessageBuf(void)
{
	g_ztommsgp = malloc(MAXERRMSG);
	g_ztommsglen = 0;
	
	if(g_ztommsgp != NULL) {
		*g_ztommsgp = '\0';
	}
}
#endif
/* ============================
	 Add error messages
   ============================ */
void ZTOMFormatMessage(FILE* fp, const char* fmt, ...)
{
	va_list ap;
	unsigned char msg[256];
	va_start(ap,fmt);
	vsprintf(msg,fmt,ap);
	va_end(ap);
#ifdef ZTOM_STATIC_LIB	/* Static lib */
	ZTOMAddMessage(stderr, msg);
#else
	fputs(msg, fp);
#endif
}
void ZTOMAddMessage(FILE* fp, const char* pmsg)
{
#ifdef ZTOM_STATIC_LIB	/* Static lib */
	int len;
	if(g_ztommsgp) {
		len = strlen(pmsg);
		if(g_ztommsglen + len > (MAXERRMSG - 37)) {
			if(g_ztommsglen < MAXERRMSG)
			strcat(g_ztommsgp, "--- Overflow message buffer ---\n");
			g_ztommsglen = MAXERRMSG;
		} else {
			strcat(g_ztommsgp, pmsg);
			g_ztommsglen += len;
		}
	}
#else
	fputs(pmsg, fp);
#endif
}
/* ============================
	 Get error messages
   ============================ */
#ifdef ZTOM_STATIC_LIB	/* Static lib */
char* ZTOMGetMessageBuf(void)
{
	return(g_ztommsgp);
}
#endif
/* ============================
	 Dispose error messages
   ============================ */
	
#ifdef ZTOM_STATIC_LIB	/* Static lib */
void ZTOMDisposeMessageBuf(void)
{
	if(g_ztommsgp) {
		free(g_ztommsgp);
	}
	g_ztommsglen = 0;
}	
#endif
/* <== ryk */