
/***************************************************************************)
(*                                                                         *)
(*  Professional Adventure Writing System extractor.                       *)
(*  Author: Jose Luis Cebri n Page                                      *)
(*  Pascal version : Carlos Sanchez (csanchez@temple.subred.org)           *)
(*                                                                         *)
(***************************************************************************)
(*                                                                         *)
(*  Z80 files support and other minor changes added by Carlos Sanchez      *)
(*  when translating the code from C to pascal                             *)
(*                                                                         *)
(***************************************************************************)
(* Linux version compilable under gpc 2.91.60-5                            *)
(*									   *)
(* 27/12/1999 some minor changes added to make it compilable under linux   *)
(***************************************************************************/


#include "z80load.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/stat.h>

#define Version "0.4 Beta"
#define MAXTVOC 6

typedef unsigned char spBYTE;
typedef unsigned short spWORD;
typedef int bool;
#define true (1==1)
#define false (1==0)

spBYTE Spectrum[65535];
FILE * fOut;
FILE * f;
long fSize;
spWORD vocPtr, conPtr, proPtr, resPtr;
int i, n, p;
char inputFileName[256];
char outputFileName[256];
spWORD offAbreviaturas, offVoc, offCon, offMsg, offSys, 
	offLoc, offObj, offWObj, offLObj, offXObj, offPro;
int numLoc, numMsg, numSys, numObj, numPro;
bool abrevOn;
char s[256];
char res[256];

typedef struct _Cond {
	char condact[21];
	int params;
} Cond;

Cond condacts[108] = {
{"AT",1},
{"NOTAT",1},
{"ATGT",1},
{"ATLT",1},
{"PRESENT",1},
{"ABSENT",1},
{"WORN",1},
{"NOTWORN",1},
{"CARRIED",1},
{"NOTCARR",1},
{"CHANCE",1},
{"ZERO",1},
{"NOTZERO",1},
{"EQ",2},
{"GT",2},
{"LT",2},
{"ADJECT1",1},
{"ADVERB",1},
{"INVEN",0},
{"DESC",0},
{"QUIT",0},
{"END",0},
{"DONE",0},
{"OK",0},
{"ANYKEY",0},
{"SAVE",0},
{"LOAD",0},
{"TURNS",0},
{"SCORE",0},
{"CLS",0},
{"DROPALL",0},
{"AUTOG",0},
{"AUTOD",0},
{"AUTOW",0},
{"AUTOR",0},
{"PAUSE",1},
{"TIMEOUT",0},
{"GOTO",1},
{"MESSAGE",1},
{"REMOVE",1},
{"GET",1},
{"DROP",1},
{"WEAR",1},
{"DESTROY",1},
{"CREATE",1},
{"SWAP",2},
{"PLACE",2},
{"SET",1},
{"CLEAR",1},
{"PLUS",2},
{"MINUS",2},
{"LET",2},
{"NEWLINE",0},
{"PRINT",1},
{"SYSMESS",1},
{"ISAT",2},
{"COPYOF",2},
{"COPYOO",2},
{"COPYFO",2},
{"COPYFF",2},
{"LISTOBJ",0},
{"EXTERN",1},
{"RAMSAVE",0},
{"RAMLOAD",1},
{"BEEP",2},
{"PAPER",1},
{"INK",1},
{"BORDER",1},
{"PREP",1},
{"NOUN2",1},
{"ADJECT2",1},
{"ADD",2},
{"SUB",2},
{"PARSE",0},
{"LISTAT",1},
{"PROCESS",1},
{"SAME",2},
{"MES",1},
{"CHARSET",1},
{"NOTEQ",2},
{"NOTSAME",2},
{"MODE",2},
{"LINE",1},
{"TIME",2},
{"PICTURE",1},
{"DOALL",1},
{"PROMPT",1},
{"GRAPHIC",1},
{"ISNOTAT",2},
{"WEIGH",2},
{"PUTIN",2},
{"TAKEOUT",1},
{"NEWTEXT",0},
{"ABILITY",2},
{"WEIGHT",1},
{"RANDOM",1},
{"INPUT",1},
{"SAVEAT",0},
{"BACKAT",0},
{"PRINTAT",2},
{"WHATO",0},
{"RESET",1},
{"PUTO",1},
{"NOTDONE",0},
{"AUTOP",1},
{"AUTOT",1},
{"MOVE",1},
{"PROTECT",0}};

char tVocs[7][256] = {"verb", "adverb", "noun", "adjective", "preposition", "conjugation", "pronoun"};

char * trim(char * p) {
	while (p[strlen(p)-1]==' ')
		p[strlen(p)-1]='\0';
	return p;
}

spWORD dPeek(int c){
	return(Spectrum[c]|(Spectrum[c+1]<<8));
}

spBYTE peek(int c) {
	return Spectrum[c];
}

spBYTE peekNeg(int c) {
	return Spectrum[c]^255;
}

char * justify(char * s, spBYTE n) {
	strcpy(res, s);
	while(strlen(res)<n)
		strcat(res, " ");
	return strdup(res);
}

char * intToStr(long l) {
	sprintf(res, "%d", l);
	return strdup(res);
}

void error(spBYTE n) {
	if(n!=0)
		printf("Error: ");
	switch(n){
	case 1:printf("File not found.\n");
		break;
	case 2:printf("Must be a .SP, .SNA or .Z80 file.\n");
		break;
	case 3:printf("It doesn't seem to be a PAW game.\n");
		break;
	case 4:printf("Parameter missing.\n");
		break;
	case 5:printf("The .Z80 format version of this file is not valid.\n");
		break;
	case 6:printf("The .Z80 file is not a 48K snapshot.\n");
	}
	exit(n);
}

char * abreviatura(int c) {
	spWORD offs;
	char s[256];

	s[0]='\0';
	offs = offAbreviaturas;
	c-=164;
	while(c!=0){
		while((Spectrum[offs]&128)==0)
			offs++;
		offs++;
		c--;
	}
	while((Spectrum[offs]&128)==0) {
		s[strlen(s)+1]=0;
		s[strlen(s)]=Spectrum[offs];
		offs++;
	}
	s[strlen(s)+1]=0;
	s[strlen(s)]=Spectrum[offs]&127;
	return strdup(s);
};

char * tipo_voc(int c) {
	if((c>=0)&&(c<=MAXTVOC))
		return tVocs[c];
	else
		return "reserved";
}

char * palabra(int num, int tipo) {
	spWORD vocPtr;
	char pal[256];

	vocPtr = offVoc;
	strcpy(pal,"     ");
	while (peek(vocPtr)!=0) {
		if((peek(vocPtr+5)==num)&&(peek(vocPtr+6)==tipo)) {
			pal[0] = peekNeg(vocPtr);
			pal[1] = peekNeg(vocPtr+1);
			pal[2] = peekNeg(vocPtr+2);
			pal[3] = peekNeg(vocPtr+3);
			pal[4] = peekNeg(vocPtr+4);
			return strdup(pal);
		}
		vocPtr+=7;
	}
	return "";
}

void ver_string(char * s) {
	spBYTE i;

	for(i=0;i<strlen(s);i++) {
		if (s[i]==7)
			fprintf(fOut, "\n\n");
		else if (((unsigned char)s[i]>31)&&((unsigned char)s[i]!='^')&&((unsigned char)s[i]!='"')&&((unsigned char)s[i]<164))
		{
			switch((char)s[i]) {
			case '$': fprintf(fOut, "á"); break;
			case '(': fprintf(fOut, "ú"); break;
			case '!': fprintf(fOut, "ó"); break;
			case '&': fprintf(fOut, "í"); break;
			case ')': fprintf(fOut, "ñ"); break;
			case '%': fprintf(fOut, "é"); break;
			case ']': fprintf(fOut, "¿"); break;
			case '[': fprintf(fOut, "¡"); break;
			default: fprintf(fOut,"%c", s[i]);
			}
		}
	}
}

void ver_mensajes(char * titulo, spWORD offTab, int num) {
	int i;
	spBYTE c;
	spWORD offs;

	i = 0;
	while (i<num) {
		offs=dPeek(offTab+2*i);
		fprintf(fOut, "/%d\n",i);
		i++;
		while((Spectrum[offs]!=(31^255))&&(offs<65520)) {
			c=peekNeg(offs);
			offs++;
			if(c==7)
				fprintf(fOut,"\n\n");
			if ((c>31)&&(c!='^')&&(c!='"')&&(c<164)) {
				switch((char)c) {
			case '$': fprintf(fOut, "á"); break;
			case '(': fprintf(fOut, "ú"); break;
			case '!': fprintf(fOut, "ó"); break;
			case '&': fprintf(fOut, "í"); break;
			case ')': fprintf(fOut, "ñ"); break;
			case '%': fprintf(fOut, "é"); break;
			case ']': fprintf(fOut, "¿"); break;
			case '[': fprintf(fOut, "¡"); break;
				default: fprintf(fOut,"%c", c);
				}
			}
			else if ((c>=164)&&(c<=255))
				ver_string(abreviatura(c));
		}
		offs++;
		fprintf(fOut, "\n");
	}
}

void ver_mensaje(char * titulo, spWORD offTab, int num) {
	int i;
	spBYTE c;
	spWORD offs;

	offs=dPeek(offTab+2*num);
	while ((Spectrum[offs]!=(31^255))&&(offs<65520)) {
		c=peekNeg(offs);
		offs++;
		if(c==7)
			fprintf(fOut,"\n\n");
		if ((c>31)&&(c!='^')&&(c!='"')&&(c<164)) {
			switch((char)c) {
			case '$': fprintf(fOut, "á"); break;
			case '(': fprintf(fOut, "ú"); break;
			case '!': fprintf(fOut, "ó"); break;
			case '&': fprintf(fOut, "í"); break;
			case ')': fprintf(fOut, "ñ"); break;
			case '%': fprintf(fOut, "é"); break;
			case ']': fprintf(fOut, "¿"); break;
			case '[': fprintf(fOut, "¡"); break;
			default: fprintf(fOut,"%c", c);
			}
		}
		else if ((c>=164)&&(c<=255))
			ver_string(abreviatura(c));
	}
}

char * upSt(char * s) {
	spBYTE i;

	strcpy(res, s);
	for(i=0;i<strlen(res);i++)
		res[i] = toupper(res[i]);
	return strdup(res);
}

void syntax() {
	printf("\nSYNTAX: sp2sce -i<filename> [-o<filename>] [-a]\n\n");
	printf("-i must be a .SP, .SNA or .Z80 file\n\n");
	printf("-o Text file where output is written. If you don't include\n");
	printf("   this parameter result will be sent to standard output.\n\n");
	printf("-a Include compress (PAW database compression) information.\n\n");
	printf("Example :    sp2sce -iripper1.z80 -oripper1.sce\n\n");
	exit(0);
}

void checkParameters(int argc, char *argv[]) {
	spBYTE i;
	char s[256];
	char opt[3]={0,0,0};

	if (argc==0)
		syntax();
	strcpy(inputFileName, "");
	strcpy(outputFileName, "");
	abrevOn = false;
	for(i=1;i<argc;i++) {
		strcpy(s,argv[i]);
		if(!strcasecmp(strncpy(opt,s,2),"-I"))
			strcpy(inputFileName,s+2);
		if(!strcasecmp(strncpy(opt,s,2),"-O"))
			strcpy(outputFileName,s+2);
		if(!strcasecmp(strncpy(opt,s,2),"-A"))
			abrevOn = true;
		opt[0]=0;opt[1]=0;opt[2]=0;
	}
	if (!strcmp(inputFileName, ""))
		syntax();
}

int main(int argc, char *argv[]) {
	struct stat arch;
	printf("UNPAWS %s\n", Version);
	checkParameters(argc, argv);
	if((f=fopen(inputFileName, "rb"))==NULL)
		error(1);
	stat(inputFileName, &arch);
	fSize = arch.st_size;
	if(fSize==49179) {
		fseek(f,7195,0);
		fread((spBYTE *)Spectrum+23552,41984,1,f);
	}
	else if (fSize==49190) {
		fseek(f,7206,0);
		fread((spBYTE *)Spectrum+23552,41984,1,f);
	}
	else if((strstr(inputFileName, ".Z80"))||(strstr(inputFileName, ".z80")))
	{switch(loadZ80(inputFileName, Spectrum)) {
		case 1: error(5); break;
		case 2: error(6);
	}}
	else error(2);
	fclose(f);
	if(peek(37943)!=16)
		error(3);

	if(!strcmp(outputFileName,""))
		fOut=stdout;
	else
		fOut=fopen(outputFileName, "wb");

	offMsg=dPeek(65503U);
	numMsg=peek(37958U);
	offSys=dPeek(65505U);
	numSys=peek(37959U);
	offLoc=dPeek(65501U);
	numLoc=peek(37957U);
	offObj=dPeek(65499U);
	numObj=peek(37956U);
	numPro=peek(37960U);
	offPro=dPeek(65497U);
	offAbreviaturas=dPeek(37964U);

	printf("\nAnalysing %s\n", inputFileName);

	/* Control section */

	fprintf(fOut, "/CTL\n_\n");

	/* Vocabulary */

	printf("Extracting vocabulary\n");

	fprintf(fOut, "/VOC\n");
	vocPtr=dPeek(65509U);
	offVoc=dPeek(65509U);
	while((vocPtr<65509)&&(peek(vocPtr)!=0)) {
		if((peekNeg(vocPtr)!='_')&&(peekNeg(vocPtr)!='*'))
			fprintf(fOut,"%c%c%c%c%c %d %s\n",peekNeg(vocPtr),
			peekNeg(vocPtr+1),peekNeg(vocPtr+2),peekNeg(vocPtr+3),
			peekNeg(vocPtr+4),peek(vocPtr+5),tipo_voc(peek(vocPtr+6)));
		vocPtr+=7;
	}

	/* System messages */
	
	printf("Extracting %d system message(s)\n",numSys);

	fprintf(fOut, "/STX\n");
	ver_mensajes("Message", offSys, numSys);

	/* Messages */

	printf("Extracting %d message(s)\n", numMsg);

	fprintf(fOut, "/MTX\n");
	ver_mensajes("Message", offMsg, numMsg);

	/* Object names */

	printf("Extracting %d object(s)\n", numObj);

	fprintf(fOut, "/OTX\n");
	ver_mensajes("Object",offObj, numObj);

	/* Locations */

	printf ("Extracting %d location(s)\n", numLoc);

	fprintf(fOut, "/LTX\n");
	ver_mensajes("Location", offLoc, numLoc);

	/* Connections */

	printf("Extracting connections\n");

	fprintf(fOut, "/CON\n");
	offCon = dPeek(65507U);
	for(i=0;i<numLoc;i++) {
		fprintf(fOut, "/%d\n", i);
		conPtr = dPeek(offCon+2*i);
		n=0;
		while(peek(conPtr)!=255) {
			strcpy(s,palabra(peek(conPtr),0));
			if(!strcmp(s,""))
				strcpy(s,palabra(peek(conPtr), 2));
			n++;
			if(s!="")
				fprintf(fOut, "%s %d\n", s, peek(conPtr+1));
			else
				fprintf(fOut, "%d %d\n", peek(conPtr), peek(conPtr+1));
			conPtr+=2;
		}
	}

	/* Object spWORDs */

	printf("Extracting object spWORDs\n");

	fprintf(fOut, "/OBJ\n");
	offWObj = dPeek(65513U);
	offLObj = dPeek(65511U);
	offXObj = dPeek(65515U);
	for(i=0;i<numObj;i++) {
		fprintf(fOut, "/%d ", i);
		switch(peek(offLObj+i)) {
		case 252: fprintf(fOut, "_ "); break;
		case 253: fprintf(fOut, "Worn "); break;
		case 254: fprintf(fOut, "Carried "); break;
		default: fprintf(fOut, "%d ", peek(offLObj+i)); break;
		}
		fprintf(fOut, "%d %s %s ", peek(offXObj+1)&63, (peek(offXObj+i)&64)?"Y":"_", (peek(offXObj+i)&128)?"Y":"_");
		strcpy(s,palabra(peek(offWObj+2*i),2));
		if(!strcmp(s,""))
			strcpy(s,intToStr(peek(offWObj+2*i)));
		if(peek(offWObj+2*i)==255)
			strcpy(s,"_");
		fprintf(fOut, "%s ", s);
		strcpy(s,palabra(peek(offWObj+2*i+1),3));
		if(!strcmp(s,""))
			strcpy(s,intToStr(peek(offWObj+2*i+1)));
		if(peek(offWObj+2*i+1)==255)
			strcpy(s,"_");
		fprintf(fOut, "%s\n", s);
	}

	/* Processes */

	printf("Extracting response table and processes\n");

	for (p=0;p<numPro;p++) {
		fprintf(fOut, "/PRO %d\n", p);
		proPtr=dPeek(offPro+p*2);
		while(peek(proPtr)!=0) {
			strcpy(s, palabra(peek(proPtr),0));
			if(peek(proPtr)==1)
				strcat(s,"_ ");
			else if(peek(proPtr)==255)
				strcat(s,"_ ");
			if(!strcmp(s,"")) {
				if(peek(proPtr)<20)
					strcpy(s, palabra(peek(proPtr),2));
				if(!strcmp(s,""))
					strcpy(s,intToStr(peek(proPtr)));
			}
			fprintf(fOut, "%s ", s);
			proPtr++;
			strcpy(s,palabra(peek(proPtr),2));
			if(peek(proPtr)==1)
				strcat(s,"_ ");
			if(peek(proPtr)==255)
				strcat(s,"_ ");
			if(!strcmp(s,"")) {
				if(peek(proPtr)<20)
					strcpy(s, palabra(peek(proPtr),2));
				if(!strcmp(s,""))
					strcpy(s, intToStr(peek(proPtr)));
			}
			fprintf(fOut, "%s ", s);
			proPtr++;		
			resPtr=dPeek(proPtr);
			proPtr+=2;
			n=0;
			while(peek(resPtr)!=255) {
				if(n!=0)
					fprintf(fOut, " ");
				n++;
				if(peek(resPtr)<107) {
					fprintf(fOut, "%s", condacts[peek(resPtr)].condact);
					if(condacts[peek(resPtr)].params > 0)
						fprintf(fOut," %d",peek(resPtr+1));
					if(condacts[peek(resPtr)].params > 1)
						fprintf(fOut," %d",peek(resPtr+2));
					resPtr+=condacts[peek(resPtr)].params;
				}
				resPtr++;
				fprintf(fOut, "\n");
			}
		}
	}
	printf("Done.\n");
	return 0;
}


