
/*
 *  CONFIG.C
 *
 *  (C)Copyright 1988, Matthew Dillon, All Rights Reserved.
 *  Permission is granted to distribute for non-profit only.
 *
 *  config file1 file2 file3.... filen
 *
 *  Configure one or more executables.	The executables must conform to
 *  the Static User Modifiable Data Standard	    (SUMDS)
 *
 *  This program configures the static parameters of C programs which
 *  contain the appropriate structures.  Unknown parameters may be
 *  configured in raw hex if you know the format.
 */

#include <stdio.h>

#define arysize(array)  (sizeof(array)/sizeof(array[0]))

extern void *malloc();

typedef unsigned char	ubyte;
typedef unsigned short	uword;
typedef unsigned long	ulong;

char Show;
char XDebug;

main(ac, av)
char *av[];
{
    register short i;
    FILE *fi;
    long pos, len;

    puts("CONFIG V1.00, Matthew Dillon.");
    for (i = 1; i < ac; ++i) {
	fi = fopen(av[i], "r+");
	if (fi == NULL) {
	    printf("Could not open %s for reading\n", av[i]);
	    continue;
	}
	switch(FindData(fi, &pos, &len)) {
	case -2:
	    printf("%s contains unknown hunk #%ld\n", av[i], pos);
	    break;
	case -1:
	    printf("%s is not an executable\n", av[i]);
	    break;
	case  0:
	    printf("could not find configuration header in %s\n", av[i]);
	    break;
	case  1:
	    switch(Configure(fi, pos, len*4)) {
	    case -3:
		printf("Unable to malloc %ld bytes\n", len);
		break;
	    case -2:
		puts("Error reading data block or badly formatted data");
		break;
	    case -1:
		puts("No 'END ' entry found in header");
		break;
	    case 0:
		puts("No modifications made to file");
		break;
	    case 1:
		puts("File updated");
		break;
	    }
	    break;
	}
	fclose(fi);
    }
}

/*
 *  Search through the beginning of all HUNK_DATA hunks for 'STRT', 0
 *
 *  symb:   object modules only, not defined here.
 *	HUNK_OVERLAY cannot be handled yet.
 */

#define HUNK_UNIT	999	/*  N, Nlws of name	    */
#define HUNK_NAME	1000	/*  N, Nlws of name	    */
#define HUNK_CODE	1001	/*  N, Nlws of code	    */
#define HUNK_DATA	1002	/*  N, Nlws of data	    */
#define HUNK_BSS	1003	/*  N	(Nlws of bss)       */
#define HUNK_RELOC32	1004	/*  N, (N+1)lws, N ... 0    */
#define HUNK_RELOC16	1005	/*  N, (N+1)lws, N ... 0    */
#define HUNK_RELOC8	1006	/*  N, (N+1)lws, N ... 0    */
#define HUNK_EXT	1007	/*  N, symb,symb... 0	    */
#define HUNK_SYMBOL	1008	/*  N, symb,symb... 0	    */
#define HUNK_DEBUG	1009	/*  N, Nlws of debug	    */
#define HUNK_END	1010	/*  -			    */
#define HUNK_HEADER	1011	/*  N, Nlwsname, N.. 0, tabsize, F, L, F-L+1 sizes  */
#define HUNK_OVERLAY	1013	/*  tabsize, M+2,M+1 ... ovr data table 	    */
#define HUNK_BREAK	1014	/*  -			    */

FindData(fi, ppos, plen)
FILE *fi;
long *ppos;
long *plen;
{
    long type;
    long len;
    long pos;
    long ary[3];
    static long buf[256];

    if (fread(&type, 4, 1, fi) != 1 || type != HUNK_HEADER)
	return(-1);
    for (;;) {
	if (fread(&len, 4, 1, fi) != 1)
	    return(-1);
	if (len == 0)
	    break;
	fseek(fi, len*4, 1);
    }
    if (fread(ary, 4, 3, fi) != 3)
	return(-1);
    fseek(fi, 4*(ary[2] - ary[1] + 1), 1);

    while (fread(&type, 4, 1, fi) == 1) {
	len = 0;
	if (XDebug)
	    printf("type: %ld\n", type);
	switch(type) {
	case HUNK_CODE:
	    fread(&len, 4, 1, fi);
	    break;
	case HUNK_DATA:
	    fread(&len, 4, 1, fi);      /*  # of longwords  */
	    pos = ftell(fi);
	    if (len >= 4) {
		register short i;
		register short j = (len > arysize(buf)) ? arysize(buf) : len;
		register long *ptr;
		fread(buf, j, 4, fi);
		for (i = 0; i < j; ++i) {
		    ptr = buf + i;
		    if (ptr[0] == 'STRT' && ptr[1] == 0) {
			*ppos = pos+i*4;
			*plen = len - i;
			fseek(fi, *ppos, 0);
			return(1);
		    }
		    ptr = (long *)((short *)ptr + 1);
		    if (ptr[0] == 'STRT' && ptr[1] == 0) {
			*ppos = pos+i*4+2;
			*plen = len - i;
			fseek(fi, *ppos, 0);
			return(1);
		    }
		}
	    }
	    fseek(fi, pos, 0);
	    break;
	case HUNK_BSS:
	    fread(&len, 4, 1, fi);
	    len = 0;
	    break;
	case HUNK_RELOC32:
	case HUNK_RELOC16:
	case HUNK_RELOC8:
	    for (;;) {
		if (fread(&len, 4, 1, fi) != 1)
		    return(-1);
		if (len == 0)
		    break;
		++len;
		fseek(fi, len*4, 1);
	    }
	    break;
	case HUNK_SYMBOL:
	    for (;;) {
		if (fread(&len, 4, 1, fi) != 1)
		    return(-1);
		if (len == 0)
		    break;
		len = (len + 1) & 0x00FFFFFF;
		if (len <= 32)                  /*  reading is faster */
		    fread(buf, len, 4, fi);
		else				/*  very long name?   */
		    fseek(fi, len*4, 1);
	    }
	    break;
	case HUNK_DEBUG:
	    if (fread(&len, 4, 1, fi) != 1)
		return(-1);
	    break;
	case HUNK_END:
	    break;
	default:
	    *ppos = type;
	    return(-2);
	}
	fseek(fi, len*4, 1);
    }
    return(0);
}

Configure(fi, pos, len)
FILE *fi;
long pos;
long len;		/*  bytes   */
{
    register uword *buf = malloc(len+1);
    register long i;
    short j;
    long modified = 0;
    short numentries = 0;

    len >>= 1;		/*  Words   */

    if (buf == NULL)
	return(-3);
    fseek(fi, pos, 0);
    if (fread(buf, len, 2, fi) != 2)
	return(-2);
    for (i = 4; i < len;) {
	if (buf[i] == 'EN' && buf[i+1] == 'D ' && buf[i+2] == 0)
	    break;
	++numentries;
	i += 4 + ((buf[i+3]+1)>>1);
    }
    if (i > len)
	return(-2);
    if (i == len)
	return(-1);

    len = i;
    printf("%ld entries found\n\n", numentries);

    for (i = 4; i < len;) {
	register long data = (buf[i]<<16)|buf[i+1];
	long dlen = buf[i+3];
	uword *ptr = buf + i + 4;

	printf("----(%c%c%c%c,%2ld): ", data>>24, data>>16, data>>8, data, dlen);
	switch(data) {
	case 'NW  ':
	    puts("NEW WINDOW STRUCTURE");
	    modified += Edit_WINP(ptr, dlen);
	    break;
	case 'TEXT':
	    puts("TEXT");
	    modified += Edit_TEXT(ptr, dlen);
	    break;
	default:
	    puts("UNKNOWN STRUCTURE, HEX EDIT");
	    modified += Edit_Hex(ptr, dlen);
	    break;
	}
	i += 4 + ((dlen+1)>>1);
	puts("");
    }
    {
	long response = (modified) ? 0 : 'n';
	char ibuf[64];

	while (response != 'y' && response != 'n') {
	    printf("Write data back to file (y/n): ");
	    fflush(stdout);
	    if (gets(ibuf) == NULL)
		break;
	    response = ibuf[0] | 0x20;
	}
	if (response == 'y') {
	    fseek(fi, pos, 0);
	    fwrite(buf, len, 2, fi);
	    return(1);
	} else {
	    return(0);
	}
    }
}

Edit_WINP(buf, bytes)
char *buf;
short bytes;
{
    short i;
    short modified = 0;

    for (i = 0; i < bytes;) {
	switch(i) {
	case 0:     /*	LeftEdge    */
	    modified += strscanvalue(buf+i, 2, "LeftEdge", "(Negative = Relative to Screen Right)");
	    i += 2;
	    break;
	case 2:     /*	TopEdge     */
	    modified += strscanvalue(buf+i, 2, "TopEdge", "(Negative = Relative to Screen Bottom)");
	    i += 2;
	    break;
	case 4:     /*	Width	    */
	    modified += strscanvalue(buf+i, 2, "Width", "(0 = Full Width, < 0 = ScreenWidth - (-width)");
	    i += 2;
	    break;
	case 6:     /*	Height	*/
	    modified += strscanvalue(buf+i, 2, "Height", "(0 = Full Height, < 0 = ScreenHeight - (-height)");
	    i += 2;
	    break;
	case 8:     /*	DetailPen   */
	    modified += strscanvalue(buf+i, 1, "DetailPen", NULL);
	    ++i;
	    break;
	case 9:     /*	BlockPen    */
	    modified += strscanvalue(buf+i, 1, "BlockPen", NULL);
	    ++i;
	    break;
	case 10:    /*	IDCMP	    */
	    modified += strscanvalue(buf+i, 4, "IDCMP", NULL);
	    i += 4;
	    break;
	case 14:    /*	Flags	    */
	    modified += strscanvalue(buf+i, 4, "Flags", NULL);
	    i += 4;
	    break;
	default:
	    i = bytes;
	    break;
	}
    }
    return(modified);
}

/*
 *  Edit TEXT.	The buffer holds a 'type' string, \0, then space for a text
 *  string.
 */

Edit_TEXT(lb, len)
char *lb;
{
    register short i, slen;
    char buf[256];
    char *prompt = lb;

    while (*lb) {
	++lb;
	--len;
    }
    ++lb;
    --len;
    if (len <= 0) {
	puts("TEXT ERROR: UNTERMINATED BUFFER");
	return(0);
    }
loop:
    printf("%-15s (%ld chars max) (%15s) :", prompt, len - 1, lb);
    if (gets(buf) == NULL || buf[0] == 0)
	strcpy(buf, lb);
    if (strcmp(buf, lb) != 0) {
	slen = strlen(buf);
	if (slen > len - 1) {
	    printf("Maximum of %ld chars!", len - 1);
	    goto loop;
	}
	strcpy(lb, buf);
	return(1);
    }
    return(0);
}

Edit_Extended(i, size, lb)
short i;
char *lb;
{
    printf("    #%ld (Blind entry) ", i);
    return(scanvalue(lb, size));
}

Edit_Hex(lb, len)
char *lb;
long len;
{
    short modified = 0;
    short i;

    puts("    (Unknown Type, blind data entry)");
    for (i = 0; i < len; ++i) {
	printf("    Entry %2ld/%2ld ", i, len-1);
	modified += scanvalue(lb+i, 1);
    }
    return(modified);
}

strscanvalue(lb, size, prompt, desc)
char *lb;
char *prompt;
char *desc;
{
    if (desc)
	printf(" note: %s\n", desc);
    printf("%15s ", prompt);
    return(scanvalue(lb, size));
}

scanvalue(lb, size)
ubyte *lb;
{
    register long val;
    register char *str;
    short neg;
    char buf[128];

top:
    switch(size) {
    case 1:
	val = *lb;
	printf("(      $%02x     %3ld) :", val, (char)val);
	break;
    case 2:
	val = *(uword *)lb;
	printf("(    $%04x  %6ld) :", val, (short)val);
	break;
    case 4:
	val = *(long *)lb;
	printf("($%08lx %7ld) :", val, val);
	break;
    }
    fflush(stdout);

    neg = 1;
    if (gets(buf) && buf[0]) {
	val = 0;
	str = buf;
	if (*str == '-') {
	    neg = -1;
	    ++str;
	}
						    /*	hex */
	if (str[0] == '$' || (str[0] == '0' && (str[1]|0x20) == 'x')) {
	    if (*str++ != '$')
		++str;
	    for (; *str; ++str) {
		*str |= 0x20;
		if (*str >= '0' && *str <= '9') {
		    val = (val << 4) | (*str & 15);
		    continue;
		}
		if (*str >= 'a' && *str <= 'f') {
		    val = (val << 4) | (*str - 'a' + 10);
		    continue;
		}
		break;
	    }
	} else
	if (*str == '%') {                          /*  binary  */
	    for (++str; *str >= '0' && *str <= '1'; ++str)
		val = (val << 1) | (*str & 1);
	} else if (*str) {                          /*  decimal */
	    for (; *str >= '0' && *str <= '9'; ++str)
		val = val * 10 + *str - '0';
	}
	if (*str) {
	    printf("Illegal char '%c', try again\n    ? ", *str);
	    goto top;
	}
    }
    if (neg < 0)
	val = -val;
    switch(size) {
    case 1:
	if ((ubyte)val != *lb) {
	    *lb = val;
	    return(1);
	}
	break;
    case 2:
	if ((uword)val != *(uword *)lb) {
	    *(uword *)lb = val;
	    return(1);
	}
	break;
    case 4:
	if (val != *(long *)lb) {
	    *(long *)lb = val;
	    return(1);
	}
	break;
    }
    return(0);
}


