/*
Ce programme sert à extraire le script du jeu S.O.S. (Septentrion en japonais) sur Super Nintendo.
C'est une traduction "directe" du code du jeu.
Le jeu utilise un texte compressé sur 5 bits, permettant jusqu'à 32 caractères.
Une table ASCII située dans la rom permet de lire directement les lettres à afficher. 
Pour les majuscules/minuscules, le jeu soustrait (ou pas) 0x20 à la lettre courante (cf code).
Le code est bordélique, j'ai vraiment "collé" au code assembleur du jeu.

(c) Skeud, mais faites en vraiment ce que vous voulez...

*/

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

#define AsciiTable 0x80CD65

typedef unsigned char byte;
typedef unsigned int  word;
typedef unsigned long dword;

word cpt1; //compteur 5 en 5
word Y; //pointeur de lecture.
byte *RomBuffer;

word LocalTextBank; //Bank courant. Commence à TExtbank, et si il le faut, est incrémenté de 1.
word TextBank;  //bank de départ
/*On laisse ces variables en global, sinon, pête couilles...*/

byte ascii_table[30] = /*Plus utilisée. A la place, le programme lit directos dans la rom (youpi!)*/
//abcdefghijklmnopqrstuvwxyz ?..
{
	0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
	0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x20, 0x3F, 0x2E, 0x0d,
}; //0x0D

dword SnesToPc(dword SnesAdd)
/*Convertit une adresse Snes Lorom en adresse PC.*/
{
	byte bank;
	word rest;

	bank = SnesAdd >>16 & 0xFF;
	rest = SnesAdd & 0xFFFF;
	if(bank >=0x80) bank-=0x80;
	rest -= 0x8000;

	return(bank * 0x8000 + rest);
}

word ByteSwap(word ToReverse)
/*Intuitif...*/
{
	return ( ((ToReverse&0xff )<<8) | ((ToReverse&0xff00)>>8) );
}

word CD83()
//returns : X. Needed : Y, cpt1
{
	dword PcAdd;
	int X=0;
	word A;

	X = cpt1 & 7;

	PcAdd = SnesToPc(LocalTextBank<<16 |( Y + 0x8000));

	A = RomBuffer[PcAdd+1]<<8 | RomBuffer[PcAdd];
	if(X>=3)
	Y++;
	X--;
	while( X >=0)
	{
		A=A>>1;
		X--;
	}

	X = A & 0x1F;
	cpt1 += 5;
	return X;
}

word CDC3(word pt_low, word pt_high)
//Après cette routine X est tjs à 0.
//renvoie A.
{
	int tmpA;
	word X,A;
    LocalTextBank = TextBank;
	if(pt_low < 0x8000)
		LocalTextBank = TextBank+1;
    else
	   pt_high ^= 0x8000;

	Y = pt_low & 0x7FFF;
	cpt1=0;
	X=CD83();

	if(X<0x1E)
    	A=RomBuffer[SnesToPc(AsciiTable+X)] - 0x20;  /*Transforme en majuscule.*/
	else
	{
		tmpA=X;
		X=CD83();
		if(tmpA < 0x1F)
	       A=X+20;
		else
    		A=RomBuffer[SnesToPc(AsciiTable+X)];
	}
	A &= 0x007F;
	X=0;
	return A;
}

void CE16(word pt_high,word pt_low,byte *out_txt)
{
	word A,X;
	word tempX,tempA;
	Y=0;

	A=CDC3(pt_low,pt_high);
	X=0;

	if(pt_low>0x7fff) pt_high ^= 0x8000; /*Normalement fait dans CDC3, mais à refaire ici.*/

	while(Y<pt_high) //boucle qui parcourt tout le texte
	{

		if(A<=0x0D)
		{   //retour à la ligne
			out_txt[X]=0x0D;out_txt[++X]=0x0a;
		}
		else
		{
			out_txt[X]=A;
			X++;
		}
		tempX=X;
		X=CD83();
		if(X<0x1E)
		A=RomBuffer[SnesToPc(AsciiTable+X)];
		else
		{
			tempA=X;
			X=CD83();
			if (tempA >=0x1F)
			{
				A=RomBuffer[SnesToPc(AsciiTable+X)]-0x20;
			}
			else
			A=X + 0x20;
		}
		A &= 0x007F;
		X=tempX;
	} //fin While...
	out_txt[X]=0;	//fin de la phrase.
	return;
}

void dumptext(byte PointerBank,byte TextBank,word PointerOffset,word decalage,int nbr,FILE *Script)
{
	word pt_high,pt_low;
	dword PointerTable;
	int i=0,pt_cpt=0;
	char commentaires[30];

    byte *out_txt;

	out_txt = (byte*) malloc(262144*sizeof(byte));
	PointerTable = PointerBank<<16 | PointerOffset;

	while(i<nbr)
	{
		pt_low=RomBuffer[SnesToPc(PointerTable+i+1)]<<8 | RomBuffer[SnesToPc(PointerTable+i)];
		i=i+2;
		pt_high=RomBuffer[SnesToPc(PointerTable+i+1)]<<8 | RomBuffer[SnesToPc(PointerTable+i)];
		
		if(pt_low>pt_high) break; //On sort de la boucle de l'enfer...
		sprintf(commentaires,"<PT%03d> %04X - %04X\n",++pt_cpt,pt_low,pt_high);
		
		pt_low += decalage;
		pt_low &= 0xffff;
		pt_high += decalage;
		pt_high &= 0xffff;
		
		CE16(pt_high,pt_low,out_txt);

		fputs(commentaires,Script);
		fputs(out_txt,Script);
		fputs("\n\n",Script);
	}
	free(out_txt);
}


int main(void)
{
	FILE *Rom,*Script_out,*Script2_out,*Script3_out,*Script4_out,*Script5_out,*Script6_out,*Script7_out;

	int i=0,pt_cpt=0;
	int taille=0;

    byte PointerBank;
    word PointerOffset;
    
   
	word decalage;

	Rom = fopen("sos.smc","r");
	printf("\nRom ouverte");
	RomBuffer = (byte*) malloc(1048576*sizeof(byte));
	fread(RomBuffer,sizeof(byte),1048576,Rom);
	fclose(Rom);

	printf("\nRom lue puis fermée.\n");

	Script_out = fopen("Capris.txt","w+");
	if(Script_out ==NULL)
	{
		printf("\nError : can't open output file (check if any program have an handle on it.\n");
		return;
	}
	PointerBank = 0x88;
	PointerOffset = 0x8000;
	TextBank = 0x89;
	decalage = 0x8000;
	taille = 0x102C;
	dumptext(PointerBank,TextBank,PointerOffset,decalage,taille,Script_out);
	fclose(Script_out);
	printf("Script Capris écrit.\n");

	Script2_out = fopen("Redwin.txt","w+");
	if(Script2_out ==NULL)
	{
		printf("\nError : can't open output file (check if any program have an handle on it.\n");
		return;
	}
	PointerBank = 0x88;
	PointerOffset = 0x902c;//0x902C;
	TextBank = 0x89;
	decalage = 0xCBC0;
	taille = 0x1016;
    dumptext(PointerBank,TextBank,PointerOffset,decalage,taille,Script2_out);
    fclose(Script2_out);
	printf("Script Redwin écrit.\n");
 
	Script3_out = fopen("Various.txt","w+");
	if(Script3_out ==NULL)
	{
		printf("\nError : can't open output file (check if any program have an handle on it.\n");
		return;
	}
	PointerBank = 0x88;
	PointerOffset = 0xC3C8;
	TextBank = 0x8B;
	decalage = 0xE2EA;
	taille = 0x1016;
    dumptext(PointerBank,TextBank,PointerOffset,decalage,taille,Script3_out);
    fclose(Script3_out);
	printf("Script Various écrit.\n");
 
	Script4_out = fopen("Names.txt","w+");
	if(Script4_out ==NULL)
	{
		printf("\nError : can't open output file (check if any program have an handle on it.\n");
		return;
	}
	PointerBank = 0x88;
	PointerOffset = 0xC356; //C356
	TextBank = 0x8B;
	decalage = 0xDF9F;
	taille = 0x1016;
    dumptext(PointerBank,TextBank,PointerOffset,decalage,taille,Script4_out);
    fclose(Script4_out);
	printf("Script Names écrit.\n");

	Script5_out = fopen("Various2.txt","w+");
	if(Script5_out ==NULL)
	{
		printf("\nError : can't open output file (check if any program have an handle on it.\n");
		return;
	}
	PointerBank = 0x88;
	PointerOffset = 0xC184;
	TextBank = 0x8B;
	decalage = 0xE9B7;
	taille = 0x1016;
    dumptext(PointerBank,TextBank,PointerOffset,decalage,taille,Script5_out);
    fclose(Script5_out);
	printf("Script Various2 écrit.\n");

	Script6_out = fopen("Luke.txt","w+");
	if(Script6_out ==NULL)
	{
		printf("\nError : can't open output file (check if any program have an handle on it.\n");
		return;
	}
	PointerBank = 0x88;
	PointerOffset = 0xA042;
	TextBank = 0x8A;
	decalage = 0xAB51;
	taille = 0x1116;
    dumptext(PointerBank,TextBank,PointerOffset,decalage,taille,Script6_out);
    fclose(Script6_out);
	printf("Script Luke. écrit.\n");    

	Script7_out = fopen("Jeff.txt","w+");
	if(Script7_out ==NULL)
	{
		printf("\nError : can't open output file (check if any program have an handle on it.\n");
		return;
	}
	PointerBank = 0x88;
	PointerOffset = 0xB14C;
	TextBank = 0x8B;
	decalage = 0x83B9;
	taille = 0x1116;
    dumptext(PointerBank,TextBank,PointerOffset,decalage,taille,Script7_out);
    fclose(Script7_out);
	printf("Script Jeff écrit.\n");    
	free(RomBuffer);
	printf("\nMemoire de la rom libérée. Fin des opérations.\n");
    system("PAUSE");
	return 0;
}
