/*    SCCS Id: @(#)splitter.c		3.1   93/01/08
/*    Copyright (c) Kenneth Lorber, Bethesda, Maryland, 1992, 1993 */
/* NetHack may be freely redistributed.  See license for details. */

#define SOUT	/* split output files */
#define SPLITSIZE (800 * 1024)		/* somewhat < one floppy */

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

#include <proto/exec.h>
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>

#include "split.h"
#include "amiout.h"
#include "arg.h"

int main(int,char **);

char *code_proto="%n.c%C";
char *data_proto="%n.d%D";
char *dir_proto="%n.dir";

char trace[MAXTRACEVAR];		/* debugging info */
char *basename;
int datacount;	/* for ssubst - should be replaced */
int codecount;
int data_file_count=0;	/* actual maxima */
int code_file_count=0;
struct hheader hheader;
struct shunk (*hlist)[];
char buf[80];
int wsf_count;

main(argc,argv)
	int argc;
	char **argv;
{
	int cur_arg;

	arg_init("C:D:d:t:T",argc,argv);
	while((cur_arg=arg_next())!=ARG_DONE){
		switch(cur_arg){
		case 'C':	/* code prototype */
			code_proto=strdup(argarg);break;
		case 'D':	/* data prototype */
			data_proto=strdup(argarg);break;
		case 'd':	/* directions prototype */
			dir_proto=strdup(argarg);break;
		case 't':	/* trace (debug) */
			{
			int dtype=0,dlevel=0;	/* rude defaults */
			sscanf(argarg,"%d=%d",&dtype,&dlevel);
			if(dtype<0 || dtype>=MAXTRACEVAR){
				fprintf(stderr,"-t: bad trace num ignored\n");
			}else{
				trace[dtype]=dlevel?dlevel:1;
			}
			break;
			}
		case 'T':	/* trace everything */
			{
			int dtype;
			for(dtype=0;dtype<MAXTRACEVAR;dtype++)trace[dtype]=255;
			}
			break;
		default:
			fprintf(stderr,"Unrecognized option.\n");
			/* FALLTHROUGH */
		case ARG_ERROR:
			panic("Error processing arguments.");
		case ARG_FREE:
			basename=strdup(argarg);
			read_load_file(basename);break;
		}
	}
	renumber();
	out_start(code_proto);
	write_header();
	write_code_file();
	out_stop();
	out_start(data_proto);
	write_data_file();
	out_stop();
	write_dir_file();
	exit(0);
}

char *
ssubst(buf,pat)
	char *buf;
	const char *pat;
{
	char *buf1=buf;

	while(*pat){
		if(*pat!='%'){
			*buf++=*pat++;
		} else {
			pat++;
			switch(*pat++){
			case '%': *buf++='%';break;
			case 'n': strcpy(buf,basename);buf=eos(buf);break;
			case 'D': sprintf(buf,"%02d",datacount);buf=eos(buf);
				  break;
			case 'C': sprintf(buf,"%02d",codecount);buf=eos(buf);
				  break;
			default:  panic("pattern substitution error");
			}
		}
	}
	*buf='\0';
	return buf1;
}

void
panic(s)
	char *s;
{
	fprintf(stderr,"\npanic: %s\n",s);
	exit(1);
}

char *
eos(s)
	char *s;
{
	while(*s)s++;
	return s;
}

/* input routines */

	/* macro for reading the next long.  If e==EOF_OK, caller MUST check
	 * for EOF condition via hreadval or assure it can't occur */
static int hreadval=0;		/* macro internal temporary */
#define EOF_OK	1
#define EOF_BAD	0
#define READLONG(e)	\
	((4!=(hreadval=read(f->fd,&(READLONGx),4)))		\
	?((0==hreadval && (e)					\
		?0						\
		:rderror()))					\
	:READLONGx)
static long READLONGx;
#define READSHORT(e)	\
	((2!=(hreadval=read(f->fd,&(READSHORTx),2)))		\
	?((0==hreadval && (e)					\
		?0						\
		:rderror()))					\
	:READSHORTx)
static short READSHORTx;

#define LONGLEN(x)	(strlen(x)+3 >>2)	/* # longs for a string */

void
read_load_file(name)
	char *name;
{
	int t;
	int hc;
	file *f=NewFile(name);

		/* read HUNK_HEADER */
	t=READLONG(EOF_BAD);if(t!=HUNK_HEADER)panic("no HUNK_HEADER");
	t=READLONG(EOF_BAD);if(t)while(t--)READLONG(EOF_BAD); /* eat any name */
	hheader.hcount=READLONG(EOF_BAD);
	hheader.first=READLONG(EOF_BAD);
	hheader.last=READLONG(EOF_BAD);
	if(hheader.hcount !=(hheader.last-hheader.first+1))panic("can't count");
	hheader.sizes=calloc(hheader.hcount,sizeof(int*));
	for(t=0;t<hheader.hcount;t++)
		(*hheader.sizes)[t]=READLONG(EOF_BAD);

	hlist=calloc(hheader.hcount,sizeof(struct shunk));
	for(hc=0;hc<hheader.hcount;hc++){
		struct shunk *th = &(*hlist)[hc];
				/* read each hunk */
		th->h=ReadHunk(f);
	}
	close(f->fd);
}

/* write routines */
#define S_CODE	0
#define S_DATA	1

void
write_header(){
	int x;
	int target=0;

	owrite_long(HUNK_HEADER);
	owrite_long(0);
	owrite_long(hheader.hcount);
	owrite_long(hheader.first);
	owrite_long(hheader.last);

	for(x=0;x<hheader.hcount;x++){
		hunk *hp = (*hlist)[x].h;
		if(hp->hunknum==target){
			owrite_long((*hheader.sizes)[x]);
			target++;
		}
	}
	for(x=0;x<hheader.hcount;x++){
		hunk *hp = (*hlist)[x].h;
		if(hp->hunknum==target){
			owrite_long((*hheader.sizes)[x]);
			target++;
		}
	}
	if(target!=hheader.hcount)panic("lost hunks?");
}

void
write_code_file(){
	code_file_count=write_split_file(S_CODE)-1;
}

void
write_data_file(){
	data_file_count=write_split_file(S_DATA)-1;
}

void
write_dir_file(){
	int x;
	FILE *fp=fopen(ssubst(buf,dir_proto),"w");

	/*fprintf(fp,"%d %d\n",code_file_count,data_file_count);*/
	for(x=0;x<=code_file_count;x++){
		codecount=x;
		fprintf(fp,"C%s\n",ssubst(buf,code_proto));
	}
	for(x=0;x<=data_file_count;x++){
		datacount=x;
		fprintf(fp,"D%s\n",ssubst(buf,data_proto));
	}
	fclose(fp);
}

/* BUGFIX: 9/23/92: see HT() above */
#define HT(x)	((x) & ~MEM_OBJ_EXTEND)

int
write_split_file(fl)
	int fl;
{
	int hc;
	for(hc=0;hc<hheader.hcount;hc++){
		hunk *hp = (*hlist)[hc].h;
		if(fl==S_CODE && HT(hp->rb->id)==HUNK_CODE){
			wsf_hunk(hp);
		} else if(fl==S_DATA && HT(hp->rb->id)==HUNK_DATA){
			wsf_hunk(hp);
		} else if(fl==S_DATA && HT(hp->rb->id)==HUNK_BSS){
			wsf_hunk(hp);
		}
	}
	return wsf_count;
}

/* BUGFIX: 9/23/92: see HT() below */
void
wsf_hunk(hp)
	hunk *hp;
{
	listlist *el;

	switch(HT(hp->rb->id)){
	case HUNK_CODE:
	case HUNK_DATA:
		owrite(hp->rb->b,(2+hp->rb->b[1])*sizeof(long));
		break;
	case HUNK_BSS:
		owrite(hp->rb->b,2*sizeof(long));
		break;
	default:panic("wsf_hunk: bad type");
	}
	foreach(el,&(hp->reloc),(listlist*)){
		write_lreloc(hp,el);
	}
	owrite_long(HUNK_END);
}

void
write_lreloc(hp,ll)
	hunk *hp;listlist *ll;
	{
	block *bp;
	owrite_long(HUNK_RELOC32);
	foreach(bp,&(ll->list),(block*)){
		owrite_long(bp->b[0]);
		owrite_long(((*hlist)[bp->b[1]]).h->hunknum);
		owrite(&(bp->b[2]),bp->b[0]*sizeof(long));
	}
	owrite_long(0);
}

void
renumber()
{
	int n;
	n=renumber2(S_CODE,0);
	renumber2(S_DATA,n);
}

/* BUGFIX 9/23/92: hp->rb->id must be wrapped with a bit stripper to ignore
 * memory type bits still in that longword.
 */

renumber2(fl,n)
	int fl;
	int n;
{
	int hc;
	for(hc=0;hc<hheader.hcount;hc++){
		hunk *hp = (*hlist)[hc].h;
		if(fl==S_CODE && HT(hp->rb->id)==HUNK_CODE){
			hp->hunknum=n++;
		} else if(fl==S_DATA && HT(hp->rb->id)==HUNK_DATA){
			hp->hunknum=n++;
		} else if(fl==S_DATA && HT(hp->rb->id)==HUNK_BSS){
			hp->hunknum=n++;
		}
	}
	return n;
}

/* output package */
#ifndef SOUT
/* NB - this version does NOT cope with multiple output files per type */
int ofile;

void
out_start(prot)
	char *prot;
{
	datacount=codecount=0;
	ofile=open(ssubst(buf,prot),O_WRONLY|O_CREAT|O_TRUNC);
	if(ofile<0)panic("can't open output file");
}

void
out_stop(){
	close(ofile);
}

void
owrite_long(literal)
	long literal;
{
	long x=literal;
	owrite(&x,sizeof(x));
}

void
owrite(where,len)
	void *where;
	long len;
{
	write(ofile,where,len);
}
#else /* SOUT */
int ofile=0;
int osize;
char *oprot;
void
out_start(prot)
	char *prot;
{
	datacount=codecount=wsf_count=0;
	oprot=prot;
	new_file();
}

void
out_stop(){
	close(ofile);
	ofile=0;
}

void
owrite_long(literal)
	long literal;
{
	long x=literal;
	if((osize+sizeof(x))>SPLITSIZE)new_file();
	owrite(&x,sizeof(x));
	osize += sizeof(x);
}

void
owrite(where,len)
	void *where;
	long len;
{
	if((osize+len)>SPLITSIZE)new_file();
	write(ofile,where,len);
	osize += len;
}

void
new_file(){
	if(ofile)close(ofile);
	ofile=open(ssubst(buf,oprot),O_WRONLY|O_CREAT|O_TRUNC);
	if(ofile<0)panic("can't open output file");
	wsf_count++,datacount++,codecount++;
	osize=0;
}
#endif /* SOUT */

struct Node *Head(l)
	struct List *l;
	{
	if(!l)panic("Head(NULL)\n");
	return l->lh_Head->ln_Succ?l->lh_Head:0;
}
struct Node *Tail(l)
	struct List *l;
{
	if(!l)panic("Tail(NULL)\n");
	return (l->lh_TailPred==(NODE_P)l)?0:l->lh_TailPred;
}
struct Node *Next(n)
	struct Node *n;
{
	if(!n)printf("Warning: Next(NULL)\n");
	return n?(n->ln_Succ->ln_Succ?n->ln_Succ:0):0;
}
struct Node *Prev(n)
	struct Node *n;
{
	if(!n)printf("Warning: Prev(NULL)\n");
	return n?(n->ln_Pred->ln_Pred?n->ln_Pred:0):0;
}
struct List *_fortemp;	/* scratch for foreach macro */

void
dump_after_read(struct List *root){
    file *f;
    foreach(f,root,(file *)){
        punit *p;
        printf("FILE '%s'\n",f->name);
        foreach(p,&(f->punits),(punit *)){
	    hunk *h;
	    print_text_block("\tPUNIT %.*s\n",p->unit_header);
	    if(p->libsize){
		printf("\tlibsize=%08x\n",p->libsize);
	    } else {
		/* */
	    }
	    foreach(h,&(p->hunks),(hunk *)){
		print_text_block("\t\tHUNK %.*s",h->name);
		printf(" @%08x\n",h);
		print_bin_block(h->rb);
		printf("\t\t\tCode Reloc\n");
		printf("\t\t\tData Reloc\n");
		if(h->merge)printf("\t\t\tmerge(%08x)\n",h->merge);
		if(h->hunkstart)printf("\t\t\thunkstart\n");
		if(h->hunkchain)printf("\t\t\thunkchain\n");
		if(h->hunkgone)printf("\t\t\thunkgone\n");
		printf("\t\t\toverlay(%08x) hunknum(%08x) offset(%08x)\n",
		  h->overlay,h->hunknum,h->hunkoffset);
	    }
        }
    }
}

void
print_text_block(char *fmt,block *b){
	if(!b){
		printf(fmt,10,"(no block)");
	} else {
		if(b->sw){
			printf(fmt,13,"(swapped out)");
		} else {
			if(!(b->b[1]) || !*(char*)&(b->b[2])){
				printf(fmt,6,"(null)");
			} else {
				printf(fmt,b->b[1]*4,&(b->b[2]));
			}
		}
	}
}

void
print_bin_block(block *b){
	if(b->sw){
		printf("\t\t\t(swapped out)\n");
	} else {
		printf("\t\t\tid1=%08x id2=%08x len=%08x\n",
		  b->id,b->b[0],b->b[1]);
	}
}

/*
 * read routines
 */

/*
 * ReadSimpleBlock
 * If the given id is recognized as a simple block (id, length, data),
 * allocate and fill in a block structure.  Include the id in the block.
 */
block *ReadSimpleBlock(f,id)
	file *f;
	long id;
	{
	long len;
	long hid;
	block *b;

	hid=id & 0x0fffffff;
	if(	hid !=HUNK_UNIT && hid != HUNK_NAME && hid != HUNK_CODE &&
		hid != HUNK_DATA && hid != HUNK_BSS && hid != HUNK_DEBUG
	  ){
		printf("%08x\n",id);
		panic("ReadSImpleBlock");
	}

	len=READLONG(EOF_BAD);
	b=NewBlock();
	b->id=id;
	b->sw=0;
	b->b=NewData((hid==HUNK_BSS)?2:len+2);
	b->b[0]=id;
	b->b[1]=len;
	if(hid != HUNK_BSS)read(f->fd,&(b->b[2]),len*4);
	return(b);
}

/*
 * TossSimpleBlock
 * Skip past something we don't need.
 */
int TossSimpleBlock(f)
	file *f;
	{
	long len=READLONG(EOF_BAD);
	if(len)if( lseek(f->fd,len*4,1) == -1)panic("Toss failed\n");
	return(len);
}

/*
 * ReadHunk
 * Read an entire hunk, building lists of each block type in the given hunk
 * structure.  If we are listing, do the listing as we read so we can see
 * where things die if we hit a type code we don't recognize.
 */
hunk *ReadHunk(f)
	file *f;
	{
	long id;
	hunk *h=NewHunk();
	while(1){
		id=READLONG(EOF_OK);
		switch(id & 0x0fffffff){	/* ignore memory type bits */
		case 0: return 0;		/* EOF - not good test */
		case HUNK_RELOC32:
			LIST{printf("Reloc32:\n");}
			ReadReloc(f,id,&h->reloc);break;
		case HUNK_CODE:
			h->rb=ReadSimpleBlock(f,id);
			LIST{printf("Code size %d\n",block_size(h->rb)*4);};
			break;
		case HUNK_DATA:
			h->rb=ReadSimpleBlock(f,id);
			LIST{printf("Data size %d\n",block_size(h->rb)*4);};
			break;
		case HUNK_BSS:
			h->rb=ReadSimpleBlock(f,id);
			LIST{printf("Bss size %d\n",block_size(h->rb)*4);};
			break;
		case HUNK_SYMBOL:
			while(TossSimpleBlock(f))READLONG(EOF_BAD);
			LIST{printf("Symbols skipped\n");};
			break;
		case HUNK_DEBUG:
			(void)TossSimpleBlock(f);
			LIST{printf("Debug hunk skipped\n");};
			break;
		case HUNK_END:	LIST{printf("End of hunk\n");};return h;
		case HUNK_BREAK:LIST{printf("End of overlay\n");};break;
		default:
			printf("Lost id=0x%x\n",id);exit(2);
		}
	}
	return 0;
}

/*
 * ReadReloc
 * Read a relocation block and build a linked list of the sections.
 * If we are listing, do that now.
 */
void ReadReloc(f,id,ls)
	file *f;
	long id;
	struct List *ls;
	{
	long len;
	block *cur;
	listlist *blist=NewListList();
	AddTail(ls,blist);
	blist->id=id;
	len=READLONG(EOF_BAD);
	while(len){
		cur=NewBlock();
		cur->b=NewData(len+2);
		read(f->fd,&(cur->b[1]),len*4+4);
		cur->b[0]=len;
		LIST{printf("\thunk #%d - %d items\n",cur->b[1],len);}
		AddTail(&blist->list,cur);
		/*ReadLong(len);*/
		len=READLONG(EOF_BAD);
	}
}

int rderror(){
	panic("read error\n");
	return 0;	/* just to make it quiet - NOTREACHED */
}

long block_size(blk)
	block *blk;
{
	return(blk->b[1]);
}

/* Allocation routines - if this was C++ then this code would be buried in the
 * constructors.  Doing it this way means we can re-write the allocation later
 * to allocate things we'll need lots of in larger blocks to avoid the time and
 * space penalties of malloc. */
file *NewFile(fname)
	char *fname;
	{
	file *ret=calloc(sizeof(file),1);
	NewList(&ret->punits);
	ret->name=strdup(fname);
	ret->fd= open(fname,O_RDONLY);
	return(ret);
}

punit *NewPunit(){
	punit *ret=calloc(sizeof(punit),1);
	NewList(&ret->hunks);
	return(ret);
}

hunk *NewHunk(){
	hunk *ret=calloc(sizeof(hunk),1);
	NewList(&ret->reloc);
	NewList(&ret->dreloc);
	NewList(&ret->extsym);
	ret->overlay=UNASSIGNED_HUNK;
	return(ret);
}

block *NewBlock()
	{
	return calloc(sizeof(block),1);
}

listlist *NewListList(){
	listlist *ret=calloc(sizeof(listlist),1);
	NewList(&ret->list);
	return(ret);
}

long *NewData(longs)
	long longs;	
	{
	return(malloc(longs*4));
}
