// =============================================================================
// -----------------------------------------------------------------------------
// Dreamcast Sound Data Extractor
// -----------------------------------------------------------------------------

#include <stdio.h>
#include <windows.h>
#include <direct.h>

// =============================================================================
// -----------------------------------------------------------------------------
// Universal data
// -----------------------------------------------------------------------------

char END_ [] = "END_";

char STXT [] = "STXT";
char SMLT [] = "SMLT";
char SFPW [] = "SFPW";
char SFPB [] = "SFPB";
char SFOB [] = "SFOB";
char SMPB [] = "SMPB";
char SMDB [] = "SMDB";
char SMSB [] = "SMSB";
char SPSR [] = "SPSR";
char SOSB [] = "SOSB";

char *TypeList [] = {	STXT, SMLT, SFPW, SFPB, SFOB, SMPB, SMDB, SMSB, SPSR, SOSB, NULL	};

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to compare two memory strings together
// -----------------------------------------------------------------------------

bool CompareString (char *String0A, int Loc0A, char *String0B, int Loc0B, int Size)

{
	int i0, i1, i2, i3, i4, i5, i6, i7;
	if (Size == 0x00)
	{
		// --- Detecting null termination ---
		do
		{
			i0 = String0A [Loc0A++] & 0xFF;
			i1 = String0B [Loc0B++] & 0xFF;
		}
		while (i0 != 0x00 && i1 != 0x00 && i0 == i1);
		if (i0 != 0x00 && i1 != 0x00)
		{
			return (FALSE);
		}
	}
	else
	{
		// --- Detecting until size is met ---
		do
		{
			i0 = String0A [Loc0A++] & 0xFF;
			i1 = String0B [Loc0B++] & 0xFF;
			Size--;
		}
		while (Size >= 0x00 && i0 == i1);
		if (Size >= 0x00)
		{
			return (FALSE);
		}
	}
	return (TRUE);
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to copy a string from one memory space to another
// -----------------------------------------------------------------------------

void CopyString (char *String0A, int Loc0A, char *String0B, int &Loc0B, int Size)

{
	int i0, i1, i2, i3, i4, i5, i6, i7;
	if (Size == 0x00)
	{
		i0 = String0A [Loc0A++] & 0xFF;
		while (i0 != 0x00)
		{
			String0B [Loc0B++] = i0;
			i0 = String0A [Loc0A++] & 0xFF;
		}
	}
	else
	{
		while (Size != 0x00)
		{
			String0B [Loc0B++] = String0A [Loc0A++];
			Size--;
		}
	}
	String0B [Loc0B] = 0x00;
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to write string data to a file
// -----------------------------------------------------------------------------

void WriteString (char *String, int StringLoc, FILE *File, int Size)

{
	int i0, i1, i2, i3, i4, i5, i6, i7;
	if (Size == 0x00)
	{
		i0 = String [StringLoc++] & 0xFF;
		while (i0 != 0x00)
		{
			fputc (i0, File);
			i0 = String [StringLoc++] & 0xFF;
		}
	}
	else
	{
		while (Size != 0x00)
		{
			fputc (String [StringLoc++], File);
			Size--;
		}
	}
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to convert a hexadecimal value to a hex represented decimal value
// -----------------------------------------------------------------------------

int HexDec (unsigned int Hexadecimal)

{
	int Decimal = 0x00000000, DecimalDigit, DecimalPlaceLoc = 0x00000000;
	int DecimalPlace [] = {	10000000,
							1000000,
							100000,
							10000,
							1000,
							100,
							10,
							1,
							0x00 };
	DecimalDigit = DecimalPlace [DecimalPlaceLoc++];
	while (Hexadecimal > 99999999)
	{
		Hexadecimal = 99999999;
	}
	while (DecimalDigit != 0x00)
	{
		Decimal <<= 0x04;
		Hexadecimal -= DecimalDigit;
		while (Hexadecimal < 0x80000000)
		{
			Decimal++;
			Hexadecimal -= DecimalDigit;
		}
		Hexadecimal += DecimalDigit;
		DecimalDigit = DecimalPlace [DecimalPlaceLoc++];
	}
	return (Decimal);
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to convert a hex represented decimal value to a hexadecimal value
// -----------------------------------------------------------------------------

int DecHex (unsigned int Decimal)

{
	int Hexadecimal = 0x00000000, DecimalDigit, HexadecimalPlaceLoc = 0x00000000;
	int HexadecimalPlace [] = {	1,
								10,
								100,
								1000,
								10000,
								100000,
								1000000,
								10000000,
								0x00 };
	while (Decimal > 0x99999999)
	{
		Decimal = 0x99999999;
	}
	while (HexadecimalPlace [HexadecimalPlaceLoc] != 0x00)
	{
		DecimalDigit = Decimal & 0x0F;
		Decimal >>= 0x04;
		while (DecimalDigit != 0x00)
		{
			Hexadecimal += HexadecimalPlace [HexadecimalPlaceLoc];
			DecimalDigit--;
		}
		HexadecimalPlaceLoc++;
	}
	return (Hexadecimal);
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to convert a hex value to an ASCII set of characters (WITHOUT leading zeros)
// -----------------------------------------------------------------------------

void HexASCII (int Value, char *String, int &StringLoc)

{
	int i0, i1, i2, i3, i4, i5, i6, i7;
	for (i7 = 0x08; i7 > 0x00; i7--)
	{
		i0 = ((Value >> 0x1C) & 0x0F) + '0';
		Value <<= 0x04;
		if (i0 > '9')
		{
			i0 += 'A' - ('9' + 0x01);
		}
		if (i0 != '0' && i1 == 0x00)
		{
			i1 = 0xFF;
		}
		if (i1 != 0x00 || i7 == 0x01)
		{
			String [StringLoc++] = i0;
		}
	}
	String [StringLoc] = 0x00;
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to write string data to the link file
// -----------------------------------------------------------------------------

void WriteLinkASCII (FILE *File, char *String, char *Comment)

{
	fputs (String, File);
	fputs ("    ; ", File);
	fputs (Comment, File);
	fputc (0x0D, File);
	fputc (0x0A, File);
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to read string data from the link file
// -----------------------------------------------------------------------------

void ReadLinkASCII (char *Memory, int &MemoryLoc, char *String, int &StringLoc)

{
	int i0, i1, i2, i3, i4, i5, i6, i7;
	do
	{
		i0 = Memory [MemoryLoc++] & 0xFF;
		if (i0 == ';')
		{
			do
			{
				i0 =  Memory [MemoryLoc++] & 0xFF;
			}
			while (i0 != 0x0A && i0 != 0x0D);
		}
	}
	while (i0 == 0x0A || i0 == 0x0D || i0 == '	' || i0 == ' ');
	do
	{
		String [StringLoc++] = i0;
		i0 =  Memory [MemoryLoc++] & 0xFF;
	}
	while (i0 != 0x0A && i0 != 0x0D && i0 != '	' && i0 != ' ' && i0 != ';');
	String [StringLoc] = 0x00;
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to write string data (in quotes) to the link file
// -----------------------------------------------------------------------------

void WriteLinkASCIIQ (FILE *File, char *String, char *Comment)

{
	fputc ('"', File);
	fputs (String, File);
	fputc ('"', File);
	fputs ("    ; ", File);
	fputs (Comment, File);
	fputc (0x0D, File);
	fputc (0x0A, File);
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to read string data from the link file
// -----------------------------------------------------------------------------

void ReadLinkASCIIQ (char *Memory, int &MemoryLoc, char *String, int &StringLoc)

{
	int i0, i1, i2, i3, i4, i5, i6, i7;
	do
	{
		do
		{
			i0 = Memory [MemoryLoc++] & 0xFF;
			if (i0 == ';')
			{
				do
				{
					i0 =  Memory [MemoryLoc++] & 0xFF;
				}
				while (i0 != 0x0A && i0 != 0x0D);
			}
		}
		while (i0 == 0x0A || i0 == 0x0D || i0 == '	' || i0 == ' ');
	}
	while (i0 != '"');
	i0 =  Memory [MemoryLoc++] & 0xFF;
	while (i0 != '"')
	{
		String [StringLoc++] = i0;
		i0 =  Memory [MemoryLoc++] & 0xFF;
	}
	String [StringLoc] = 0x00;
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to write a long-word value to the link file
// -----------------------------------------------------------------------------

void WriteLinkLong (FILE *File, int Value, char *Comment)

{
	int i0, i1;
	for (i1 = 0x08; i1 > 0x00; i1--)
	{
		i0 = ((Value >> 0x1C) & 0x0F) + '0';
		Value <<= 0x04;
		if (i0 > '9')
		{
			i0 += 'A' - ('9' + 0x01);
		}
		fputc (i0, File);
	}
	fputs ("    ; ", File);
	fputs (Comment, File);
	fputc (0x0D, File);
	fputc (0x0A, File);
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to write a word value to the link file
// -----------------------------------------------------------------------------

void WriteLinkWord (FILE *File, int Value, char *Comment)

{
	int i0, i1;
	for (i1 = 0x04; i1 > 0x00; i1--)
	{
		i0 = ((Value >> 0x0C) & 0x0F) + '0';
		Value <<= 0x04;
		if (i0 > '9')
		{
			i0 += 'A' - ('9' + 0x01);
		}
		fputc (i0, File);
	}
	fputs ("    ; ", File);
	fputs (Comment, File);
	fputc (0x0D, File);
	fputc (0x0A, File);
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to write a byte value to the link file
// -----------------------------------------------------------------------------

void WriteLinkByte (FILE *File, int Value, char *Comment)

{
	int i0, i1;
	for (i1 = 0x02; i1 > 0x00; i1--)
	{
		i0 = ((Value >> 0x04) & 0x0F) + '0';
		Value <<= 0x04;
		if (i0 > '9')
		{
			i0 += 'A' - ('9' + 0x01);
		}
		fputc (i0, File);
	}
	fputs ("    ; ", File);
	fputs (Comment, File);
	fputc (0x0D, File);
	fputc (0x0A, File);
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to read a long-word value from the link file
// -----------------------------------------------------------------------------

int ReadLinkLong (char *Memory, int &MemoryLoc)

{
	int i0, i1 = 0x00;
	do
	{
		i0 = Memory [MemoryLoc++] & 0xFF;
		if (i0 == ';')
		{
			do
			{
				i0 =  Memory [MemoryLoc++] & 0xFF;
			}
			while (i0 != 0x0A && i0 != 0x0D);
		}
	}
	while (i0 == 0x0A || i0 == 0x0D || i0 == '	' || i0 == ' ');
	do
	{
		i0 -= '0';
		if (i0 > 0x09)
		{
			i0 -= 'A' - ('9' + 0x01);
		}
		i1 <<= 0x04;
		i1 |= i0;
		i0 = Memory [MemoryLoc++] & 0xFF;
	}
	while (i0 != 0x0A && i0 != 0x0D && i0 != '	' && i0 != ' ' && i0 != ';');
	return (i1);
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to find the next occuring valid value in the linked file
// -----------------------------------------------------------------------------

void FindLink (char *Memory, int &MemoryLoc)

{
	int i0, i1 = 0x00;
	do
	{
		i0 = Memory [MemoryLoc++] & 0xFF;
		if (i0 == ';')
		{
			do
			{
				i0 =  Memory [MemoryLoc++] & 0xFF;
			}
			while (i0 != 0x0A && i0 != 0x0D);
		}
	}
	while (i0 == 0x0A || i0 == 0x0D || i0 == '	' || i0 == ' ');
	MemoryLoc--;
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to collect a long-word from memory
// -----------------------------------------------------------------------------

int LoadLong (char *Memory, int &MemoryLoc)

{
	int Value = Memory [MemoryLoc++] & 0xFF;
	Value |= (Memory [MemoryLoc++] & 0xFF) << 0x08;
	Value |= (Memory [MemoryLoc++] & 0xFF) << 0x10;
	Value |= (Memory [MemoryLoc++] & 0xFF) << 0x18;
	return (Value);
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to collect a word from memory
// -----------------------------------------------------------------------------

int LoadWord (char *Memory, int &MemoryLoc)

{
	int Value = Memory [MemoryLoc++] & 0xFF;
	Value |= (Memory [MemoryLoc++] & 0xFF) << 0x08;
	return (Value);
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to save a long-word to file
// -----------------------------------------------------------------------------

void SaveLong (int Value, FILE *File)

{
	fputc (Value, File);
	fputc (Value >> 0x08, File);
	fputc (Value >> 0x10, File);
	fputc (Value >> 0x18, File);
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to dump memory contents to a file
// -----------------------------------------------------------------------------

void DumpFile (char *FileName, char *Memory, int MemorySize, int &DataLoc, int DataSize)

{
	if (DataLoc < 0x00 || DataLoc > MemorySize)
	{
		printf ("      Error; data is out of memory range (Offset 0x%0.8X)\n", DataLoc);
		return;
	}
	if ((DataLoc + DataSize) > MemorySize)
	{
		printf ("      Error; data extends outside memory range (Offset 0x%0.8X)\n", DataLoc + DataSize);
		return;
	}
	FILE *File;
	if ((File = fopen (FileName, "wb")) == NULL)
	{
		printf ("      Error; coult not create file\n");
		return;
	}
	for (DataSize += DataLoc; DataLoc < DataSize; DataLoc++)
	{
		fputc (Memory [DataLoc], File);
	}
	fclose (File);
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to create a wav format memory space
// -----------------------------------------------------------------------------

char* MakeWave (char *Memory, int MemorySize, int SampleLoc, int &SampleSize, int Frequency, int Channels, int BitRate, int Format)

{
	int i0, i1;
	char *Wave = (char*) malloc (SampleSize + 0x3A);
	if (Wave == NULL)
	{
		printf ("      Error; could not allocate space for the wave data\n");
		return (Wave);
	}
	int WaveLoc = 0x00;
	CopyString ("RIFF", 0x00, Wave, WaveLoc, 0x04);
	int RIFFSizeLoc = WaveLoc;
	WaveLoc += 0x04;
	CopyString ("WAVEfmt ", 0x00, Wave, WaveLoc, 0x08);
	int WAVESizeLoc = WaveLoc;
	WaveLoc += 0x04;
	Wave [WaveLoc++] = Format; Wave [WaveLoc++] = Format >> 0x08;
	Wave [WaveLoc++] = Channels; Wave [WaveLoc++] = Channels >> 0x08;
	Wave [WaveLoc++] = Frequency; Wave [WaveLoc++] = Frequency >> 0x08; Wave [WaveLoc++] = Frequency >> 0x10; Wave [WaveLoc++] = Frequency >> 0x18;
	int ByteRate = Frequency * Channels * BitRate/8;
	Wave [WaveLoc++] = ByteRate; Wave [WaveLoc++] = ByteRate >> 0x08; Wave [WaveLoc++] = ByteRate >> 0x10; Wave [WaveLoc++] = ByteRate >> 0x18;
	int BlockAlign = Channels * BitRate/8;
	Wave [WaveLoc++] = BlockAlign; Wave [WaveLoc++] = BlockAlign >> 0x08;
	Wave [WaveLoc++] = BitRate; Wave [WaveLoc++] = BitRate >> 0x08;
	Wave [WaveLoc++] = 0x00; // Unknown blank space
	Wave [WaveLoc++] = 0x00; // Unknown blank space
	i0 = WaveLoc - (WAVESizeLoc + 0x04);
	Wave [WAVESizeLoc++] = i0; Wave [WAVESizeLoc++] = i0 >> 0x08; Wave [WAVESizeLoc++] = i0 >> 0x10; Wave [WAVESizeLoc++] = i0 >> 0x18;
	CopyString ("fact", 0x00, Wave, WaveLoc, 0x04);
	int FACTSizeLoc = WaveLoc;
	WaveLoc += 0x04;
	int FACTDataSize = WaveLoc;
	WaveLoc += 0x04;
	i0 = WaveLoc - (FACTSizeLoc + 0x04);
	Wave [FACTSizeLoc++] = i0; Wave [FACTSizeLoc++] = i0 >> 0x08; Wave [FACTSizeLoc++] = i0 >> 0x10; Wave [FACTSizeLoc++] = i0 >> 0x18;
	CopyString ("data", 0x00, Wave, WaveLoc, 0x04);
	int DATASizeLoc = WaveLoc;
	WaveLoc += 0x04;
	for (i1 = 0x00; i1 < SampleSize; i1++)
	{
		Wave [WaveLoc++] = Memory [SampleLoc++];
	}
	i0 = WaveLoc - (RIFFSizeLoc + 0x04);
	Wave [RIFFSizeLoc++] = i0; Wave [RIFFSizeLoc++] = i0 >> 0x08; Wave [RIFFSizeLoc++] = i0 >> 0x10; Wave [RIFFSizeLoc++] = i0 >> 0x18;
	i0 = WaveLoc - (DATASizeLoc + 0x04);
	Wave [DATASizeLoc++] = i0; Wave [DATASizeLoc++] = i0 >> 0x08; Wave [DATASizeLoc++] = i0 >> 0x10; Wave [DATASizeLoc++] = i0 >> 0x18;
	i0 = WaveLoc - (FACTDataSize + 0x04);
	Wave [FACTDataSize++] = i0; Wave [FACTDataSize++] = i0 >> 0x08; Wave [FACTDataSize++] = i0 >> 0x10; Wave [FACTDataSize++] = i0 >> 0x18;
	SampleSize += 0x3A;
	return (Wave);
}

// =============================================================================
// -----------------------------------------------------------------------------
// The decoding (decompressing) process
// -----------------------------------------------------------------------------

void Decode (int &Nybble, int &Sample, int &StepIndex)

{
	int MaximumWidth = 0x7FFF;
	int StepSize [] = {		 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F,
							-0x01,-0x03,-0x05,-0x07,-0x09,-0x0B,-0x0D,-0x0F	};
	int Magnatude [] = {	 0x00E6, 0x00E6, 0x00E6, 0x00E6, 0x0133, 0x0199, 0x0200, 0x0266 };
	Sample += ((StepIndex * StepSize [Nybble]) / 8); // Has to be division (shift has a problem)
	if (Sample > MaximumWidth)
	{
		Sample = MaximumWidth;
	}
	else if (Sample < -(MaximumWidth+0x01))
	{
		Sample = -(MaximumWidth+0x01);
	}
	StepIndex = ((StepIndex * Magnatude [Nybble & 0x07]) >> 0x08);
	if (StepIndex > 0x6000)
	{
		StepIndex = 0x6000;
	}
	else if (StepIndex < 0x007F)
	{
		StepIndex = 0x007F;
	}
}

// -----------------------------------------------------------------------------
// Subroutine to decode the Dreamcast's inbuilt ADPCM format to PCM 16-bit
// -----------------------------------------------------------------------------

char* DecodeADPCM (char *Memory, int MemorySize, int SampleLoc, int &SampleSize)

{
	int i0, i1, i2, i3, i4, i5, i6, i7;
	char *PCM = (char*) malloc (SampleSize * 0x04);
	int PCMSize;
	if (PCM == NULL)
	{
		printf ("      Error; could not allocate space for decoding ADPCM\n");
		return (PCM);
	}
	int Nybble;
	int Sample = 0x0000;
	int StepIndex = 0x7F;
	for (SampleSize += SampleLoc, PCMSize = 0x00; SampleLoc < SampleSize; )
	{
		Nybble = Memory [SampleLoc] & 0x0F;
		Decode (Nybble, Sample, StepIndex);
		PCM [PCMSize++] = Sample;
		PCM [PCMSize++] = Sample >> 0x08;

		Nybble = (Memory [SampleLoc++] & 0xF0) >> 0x04;
		Decode (Nybble, Sample, StepIndex);
		PCM [PCMSize++] = Sample;
		PCM [PCMSize++] = Sample >> 0x08;
	}
	SampleSize = PCMSize;
	return (PCM);
}

// =============================================================================
// -----------------------------------------------------------------------------
// Extracting SMPB files
// -----------------------------------------------------------------------------

void ExtractSMPB (char *Memory, int MemorySize, FILE *TextLink, char *Folder, int FolderLoc, int DirectLoc)

{
	int i0, i1, i2, i3, i4, i5, i6, i7;
	int MemoryLoc = 0x04;
	WriteLinkLong (TextLink, LoadLong (Memory, MemoryLoc), "Unknown (usually 00000002 or 00000101, I'd say byte specific settings)");
	i0 = LoadLong (Memory, MemoryLoc); // Size (useless to me)
	WriteLinkLong (TextLink, LoadLong (Memory, MemoryLoc), "Unknown");
	int SamplePointers = LoadLong (Memory, MemoryLoc);
	i7 = LoadLong (Memory, MemoryLoc);
	printf ("    Number of samples: %X\n", i7);
	char Type [] = "####";
	fputc (0x0D, TextLink); fputc (0x0A, TextLink);

	// --- Unknown waves ---

	int WaveLocation = LoadLong (Memory, MemoryLoc);
	i6 = LoadLong (Memory, MemoryLoc);
	printf ("    Number of unknown waves: %X\n", i6);
	for (i5 = 0x00; i5 < i6; i5++)
	{
		int FileLoc = FolderLoc;
		CopyString ("UnkWave_", 0x00, Folder, FileLoc, 0x00);
		HexASCII (HexDec (i5), Folder, FileLoc);
		CopyString (".bin", 0x00, Folder, FileLoc, 0x00);
		WriteLinkASCIIQ (TextLink, Folder + DirectLoc, "Directory of unknown wave");
		DumpFile (Folder, Memory, MemorySize, WaveLocation, 0x80);
	}
	WriteLinkASCII (TextLink, END_, "End of unknown waves");
	fputc (0x0D, TextLink); fputc (0x0A, TextLink);

	// --- Unknown data 01 ---

	int UnknownLoc01 = LoadLong (Memory, MemoryLoc);
	i6 = LoadLong (Memory, MemoryLoc);
	printf ("    Unknown counter 01: %X\n", i6);
	for (i5 = 0x00; i5 < i6; i5++)
	{
		int FileLoc = FolderLoc;
		CopyString ("UnkData01_", 0x00, Folder, FileLoc, 0x00);
		HexASCII (HexDec (i5), Folder, FileLoc);
		CopyString (".bin", 0x00, Folder, FileLoc, 0x00);
		WriteLinkASCIIQ (TextLink, Folder + DirectLoc, "Directory of unknown data");
		DumpFile (Folder, Memory, MemorySize, UnknownLoc01, 0x0C);
	}
	WriteLinkASCII (TextLink, END_, "End of unknown data");
	fputc (0x0D, TextLink); fputc (0x0A, TextLink);

	// --- Unknown data 02 ---

	int UnknownLoc02 = LoadLong (Memory, MemoryLoc);
	i6 = LoadLong (Memory, MemoryLoc);
	printf ("    Unknown counter 02: %X\n", i6);
	for (i5 = 0x00; i5 < i6; i5++)
	{
		int FileLoc = FolderLoc;
		CopyString ("UnkData02_", 0x00, Folder, FileLoc, 0x00);
		HexASCII (HexDec (i5), Folder, FileLoc);
		CopyString (".bin", 0x00, Folder, FileLoc, 0x00);
		WriteLinkASCIIQ (TextLink, Folder + DirectLoc, "Directory of unknown data");
		DumpFile (Folder, Memory, MemorySize, UnknownLoc02, 0x04);
	}
	WriteLinkASCII (TextLink, END_, "End of unknown data");
	fputc (0x0D, TextLink); fputc (0x0A, TextLink);

	// --- Sample data ---

	for (i6 = 0x00; i6 < i7; i6++)
	{
		printf ("\n    > Sample %d\n", i6);
		int SampleEntry = LoadLong (Memory, SamplePointers);
		MemoryLoc = LoadLong (Memory, SampleEntry);
		while (SampleEntry < MemoryLoc)
		{
			WriteLinkLong (TextLink, LoadLong (Memory, SampleEntry), "Unknown");
		}
		WriteLinkASCII (TextLink, "#", "End of unknown values 01");
		int Entries = LoadLong (Memory, SampleEntry);
		MemoryLoc = LoadLong (Memory, SampleEntry);
		while (SampleEntry < MemoryLoc)
		{
			WriteLinkLong (TextLink, LoadLong (Memory, SampleEntry), "Unknown");
		}
		WriteLinkASCII (TextLink, "#", "End of unknown values 02");
		for (i5 = 0x00; i5 < Entries; i5++)
		{
			i0 = LoadLong (Memory, SampleEntry);
			WriteLinkByte (TextLink, (i0 & 0xC0), "Additional address bit settings (Gets OR'd with byte 02 of the address)");
			int AudioFormat = (i0 >> 0x08) & 0xFF;
			WriteLinkByte (TextLink, AudioFormat, "Audio format (00 PCM 16-bit signed | 01 ADPCM 4-bit | 02 PCM 16-bit signed (looping) | 03 ADPCM 4-bit (looping)");
			int SampleLoc = (i0 & 0x3F) << 0x10;
			SampleLoc |= (i0 >> 0x10) & 0xFF;
			SampleLoc |= (i0 >> 0x10) & 0xFF00;
			int LoopSize = LoadWord (Memory, SampleEntry);
			WriteLinkWord (TextLink, LoopSize, "Loop back size (Use this to determin how far to jump back and loop by (DO NOT USE THIS TO CHANGE THE SIZE IF THE \"Size of sample\" IS FFFF, DreamSDE USES THE WAVE FILE'S SIZE)");
			int SampleSize = LoadWord (Memory, SampleEntry);
			WriteLinkWord (TextLink, SampleSize, "Size of sample (DreamSDE USES THE WAVE FILE'S SIZE TO DETERMIN THE SIZE, DO NOT USE THIS TO CHANGE THE SIZE (Unless it is FFFF for null)");
			for (i4 = 0x0F; i4 > 0x00; i4--)
			{
				WriteLinkWord (TextLink, LoadWord (Memory, SampleEntry), "Unknown (needs more research)");
			}
			WriteLinkWord (TextLink, LoadWord (Memory, SampleEntry), "Assumed frequency (0000 to 00FF == 172hz to 44100hz");
			for (i4 = 0x04; i4 > 0x00; i4--)
			{
				WriteLinkWord (TextLink, LoadWord (Memory, SampleEntry), "Unknown (needs more research)");
			}
			int Frequency = ((44100 - 172) / 2) + 172; // Default frequency set to the middle (between 172hz and 44100hz)
			printf ("      Address:   %8X\n", SampleLoc);
			printf ("      Format:    %8X\n", AudioFormat);
			printf ("      Size:      %8X\n", SampleSize);
			printf ("      Loop Size: %8X\n", LoopSize);
			int FileLoc = FolderLoc;
			CopyString ("Sample_", 0x00, Folder, FileLoc, 0x00);
			HexASCII (HexDec (i6), Folder, FileLoc);
			CopyString ("_Entry_", 0x00, Folder, FileLoc, 0x00);
			HexASCII (HexDec (i5), Folder, FileLoc);
			if (SampleSize == 0xFFFF)
			{
				CopyString ("_Blank", 0x00, Folder, FileLoc, 0x00);
				SampleSize = LoopSize;
			}
			CopyString (".wav", 0x00, Folder, FileLoc, 0x00);
			WriteLinkASCIIQ (TextLink, Folder + DirectLoc, "Directory of sample");
			if (AudioFormat == 0x00 || AudioFormat == 0x02)
			{
	
				// --- PCM 16-bit signed ---

				SampleSize = SampleSize * 0x02;
				char *Sample = MakeWave (Memory, MemorySize, SampleLoc, SampleSize, Frequency, 1, 16, 0x0001);
				if (Sample != NULL)
				{
					DumpFile (Folder, Sample, SampleSize, SampleLoc = 0x00, SampleSize);
					free (Sample);
				}
			}
			else if (AudioFormat == 0x01 || AudioFormat == 0x03)
			{
	
				// --- ADPCM 4-bit ---
	
				SampleSize = SampleSize / 0x02;
				char *PCM = DecodeADPCM (Memory, MemorySize, SampleLoc, SampleSize);
				if (PCM != NULL)
				{
					char *Sample = MakeWave (PCM, SampleSize, 0x00, SampleSize, Frequency, 1, 16, 0x0001);
					if (Sample != NULL)
					{
						DumpFile (Folder, Sample, SampleSize, SampleLoc = 0x00, SampleSize);
						free (Sample);
					}
					free (PCM);
				}
			}
			else
			{
				printf ("      Error; Unsupported audio format\n");
			}
			WriteLinkASCII (TextLink, "#", "End of entry");
		}
		WriteLinkASCII (TextLink, "#", "End of sample");
		fputc (0x0D, TextLink); fputc (0x0A, TextLink);
	}
}

// =============================================================================
// -----------------------------------------------------------------------------
// Extracting SMLT files
// -----------------------------------------------------------------------------

void ExtractSMLT (char *Memory, int MemorySize, FILE *TextLink, char *Folder, int FolderLoc, int DirectLoc)

{
	int i0, i1, i2, i3, i4, i5, i6, i7;
	int MemoryLoc = 0x04;
	WriteLinkLong (TextLink, LoadLong (Memory, MemoryLoc), "Unknown (usually 00000002 or 00000101, I'd say byte specific settings)");
	i7 = LoadLong (Memory, MemoryLoc);
	printf ("    Number of entries: %X\n\n", i7);
	char Type [] = "####";
	fputc (0x0D, TextLink); fputc (0x0A, TextLink);
	for (i6 = 0x00; i6 < i7; i6++)
	{
		MemoryLoc = (i6 + 0x01) * 0x20;
		Type [0x00] = Memory [MemoryLoc++];
		Type [0x01] = Memory [MemoryLoc++];
		Type [0x02] = Memory [MemoryLoc++];
		Type [0x03] = Memory [MemoryLoc++];
		printf ("    > %s\n", Type);
		WriteLinkASCII (TextLink, Type, "Entry type ID");
		WriteLinkLong (TextLink, LoadLong (Memory, MemoryLoc), "Unknown");
		WriteLinkLong (TextLink, LoadLong (Memory, MemoryLoc), "Possibly write offset (copy to location)");
		WriteLinkLong (TextLink, LoadLong (Memory, MemoryLoc), "Possibly write size");
		int DataLoc = LoadLong (Memory, MemoryLoc);
		int DataSize = LoadLong (Memory, MemoryLoc);
		if (DataLoc == -0x01)
		{
			WriteLinkLong (TextLink, DataLoc, "Null offset of data (This entry does not have an offset, my theory is this sets a clear/fill at the write offset of write size)");
			WriteLinkLong (TextLink, DataSize, "Null size of data ('')");
		}
		else
		{
			int FileLoc = FolderLoc;
			CopyString ("Entry_", 0x00, Folder, FileLoc, 0x00);
			HexASCII (HexDec (i6), Folder, FileLoc);
			CopyString (Type, 0x00, Folder, FileLoc, 0x00);
			Folder [FileLoc - 0x04] = '.';
			Folder [FileLoc] = 0x00;
			WriteLinkASCIIQ (TextLink, Folder + DirectLoc, "Directory of file");
			DumpFile (Folder, Memory, MemorySize, DataLoc, DataSize);
		}
		WriteLinkLong (TextLink, LoadLong (Memory, MemoryLoc), "Unknown (Always seems to be 00000000, reserved probably)");
		WriteLinkLong (TextLink, LoadLong (Memory, MemoryLoc), "''");
		fputc (0x0D, TextLink); fputc (0x0A, TextLink);
	}
	WriteLinkASCII (TextLink, END_, "End of list");
}

// =============================================================================
// -----------------------------------------------------------------------------
// Compacting SMLT files
// -----------------------------------------------------------------------------

void CompactSMLT (char *Memory, int MemorySize, int MemoryLoc, FILE *Output, char *Folder, int FolderLoc, int DirectLoc)

{
	int i0, i1, i2, i3, i4, i5, i6, i7;
	char Type [] = "####";
	SaveLong (ReadLinkLong (Memory, MemoryLoc), Output);
	i7 = ftell (Output);

	// --- Counting number of entries ---

	i6 = MemoryLoc;
	fseek (Output, 0x20, SEEK_SET);
	ReadLinkASCII (Memory, MemoryLoc, Type, i0 = 0x00);
	int EntryCount = 0x00;
	while ((CompareString (END_, 0x00, Type, 0x00, 0x04)) == FALSE)
	{
		ReadLinkLong (Memory, MemoryLoc);
		ReadLinkLong (Memory, MemoryLoc);
		ReadLinkLong (Memory, MemoryLoc);
		FindLink (Memory, MemoryLoc);
		i0 = Memory [MemoryLoc];
		if (i0 != '"')
		{
			ReadLinkLong (Memory, MemoryLoc);
			ReadLinkLong (Memory, MemoryLoc);
		}
		else
		{
			int FileLoc = DirectLoc;
			ReadLinkASCIIQ (Memory, MemoryLoc, Folder, FileLoc);
		}
		ReadLinkLong (Memory, MemoryLoc);
		ReadLinkLong (Memory, MemoryLoc);
		ReadLinkASCII (Memory, MemoryLoc, Type, i0 = 0x00);
		EntryCount++;
	}
	printf ("    Number of entries: %X\n\n", EntryCount);
	int DataLoc = (EntryCount + 0x01) * 0x20;

	// --- Packing entries ---

	MemoryLoc = i6;
	fseek (Output, 0x20, SEEK_SET);
	ReadLinkASCII (Memory, MemoryLoc, Type, i0 = 0x00);
	EntryCount = 0x00;
	while ((CompareString (END_, 0x00, Type, 0x00, 0x04)) == FALSE)
	{
		printf ("    > %s\n", Type);
		WriteString (Type, 0x00, Output, 0x04);
		SaveLong (ReadLinkLong (Memory, MemoryLoc), Output);
		SaveLong (ReadLinkLong (Memory, MemoryLoc), Output);
		SaveLong (ReadLinkLong (Memory, MemoryLoc), Output);
		FindLink (Memory, MemoryLoc);
		i0 = Memory [MemoryLoc];
		if (i0 != '"')
		{
			SaveLong (ReadLinkLong (Memory, MemoryLoc), Output);
			SaveLong (ReadLinkLong (Memory, MemoryLoc), Output);
		}
		else
		{
			int FileLoc = DirectLoc;
			ReadLinkASCIIQ (Memory, MemoryLoc, Folder, FileLoc);
			FILE *Input;
			if ((Input = fopen (Folder, "rb")) == NULL)
			{
				printf ("      Error; could not open file:\n       %s\n", Folder);
				return;
			}
			else
			{
				fseek (Input, 0x00, SEEK_END);
				int DataSize = ftell (Input);
				rewind (Input);
				SaveLong (DataLoc, Output);
				SaveLong (DataSize, Output);
				i5 = ftell (Output);
				fseek (Output, DataLoc, SEEK_SET);
				for (DataSize += DataLoc; DataLoc < DataSize; DataLoc++)
				{
					fputc (fgetc (Input), Output);
				}
				fclose (Input);
				fseek (Output, i5, SEEK_SET);
			}
		}
		SaveLong (ReadLinkLong (Memory, MemoryLoc), Output);
		SaveLong (ReadLinkLong (Memory, MemoryLoc), Output);
		ReadLinkASCII (Memory, MemoryLoc, Type, i0 = 0x00);
		EntryCount++;
	}
	fseek (Output, i7, SEEK_SET);
	SaveLong (EntryCount, Output);
	SaveLong (-0x01, Output);
	SaveLong (-0x01, Output);
	SaveLong (-0x01, Output);
	SaveLong (-0x01, Output);
	SaveLong (-0x01, Output);
}

// =============================================================================
// -----------------------------------------------------------------------------
// Subroutine to open, load, and check a file before processing
// -----------------------------------------------------------------------------

void LoadFile (char *FileName, char *Folder, int FolderLoc, int DirectLoc)

{
	FILE *File;
	if ((File = fopen (FileName, "rb")) == NULL)
	{
		printf ("    Error; could not open the file\n");
		return;
	}
	fseek (File, 0x00, SEEK_END);
	int MemorySize = ftell (File);
	char *Memory = (char*) malloc (MemorySize);
	if (Memory == NULL)
	{
		fclose (File);
		printf ("    Error; could not allocate memory required for the file\n");
		return;
	}
	rewind (File);
	int i0, i1, i2, i3, i4, i5, i6, i7;
	i7 = fread (Memory, 0x01, MemorySize, File);
	if (i7 != MemorySize)
	{
		fclose (File);
		free (Memory);
		printf ("    Error; only %0.8X out of %0.8X was copied, perhaps try again?\n", i7, MemorySize);
		return;
	}
	fclose (File);
	int TypeCount = -0x01;
	bool Result;
	char *TypeEntry = NULL;
	do
	{
		TypeEntry = TypeList [++TypeCount];
		if (TypeEntry != NULL)
		{
			Result = CompareString (TypeEntry, 0x00, Memory, 0x00, 0x04);
		}
	}
	while (Result == FALSE && TypeEntry != NULL);
	if (TypeEntry == NULL)
	{
		Memory [0x04] = 0x00;
		printf ("    Error, the file type ID is \"%s\" which does not appear to be supported\n    Either the format is unknown/unsupported, or you're a dolt and are\n    attempting to extract something irrelevant\n", Memory);
		free (Memory);
		return;
	}
	printf ("    Type ID: %s\n", TypeEntry);
	int FileLoc = FolderLoc;
	if (TypeCount == 0x00)
	{

		// --- Compacting ---

		int MemoryLoc = 0x04;
		char Type [] = "####";
		ReadLinkASCII (Memory, MemoryLoc, Type, i0 = 0x00);
		printf ("    Compacting: %s\n", Type);
		TypeCount = -0x01;
		Result;
		do
		{
			TypeEntry = TypeList [++TypeCount];
			if (TypeEntry != NULL)
			{
				Result = CompareString (TypeEntry, 0x00, Type, 0x00, 0x04);
			}
		}
		while (Result == FALSE && TypeEntry != NULL);
		if (TypeEntry == NULL)
		{
			printf ("    Error, the file type ID \"%s\" does not appear to be supported\n", Type);
			free (Memory);
			return;
		}
		i0 = FileLoc;
		CopyString (Type, 0x00, Folder, i0, 0x00);
		Folder [FileLoc] = '.';
		if ((File = fopen (Folder, "wb")) == NULL)
		{
			printf ("    Error, could not create output file\n");
			free (Memory);
			return;
		}
		WriteString (Type, 0x00, File, 0x04);
		switch (TypeCount)
		{
			case 0x01: // SMLT
			{
				CompactSMLT (Memory, MemorySize, MemoryLoc, File, Folder, FolderLoc, DirectLoc);
			}
			break;
			case 0x02: // SFPW
			{
			}
			break;
			case 0x03: // SFPB
			{
			}
			break;
			case 0x04: // SFOB
			{
			}
			break;
			case 0x05: // SMPB
			{
			}
			break;
			case 0x06: // SMDB
			{
			}
			break;
			case 0x07: // SMSB
			{
			}
			break;
			case 0x08: // SPSR
			{
			}
			break;
			case 0x09: // SOSB
			{
			}
			break;
		}
		fclose (File);
	}
	else
	{

		// --- Extracting ---

		Folder [FileLoc++] = '.';
		Folder [FileLoc++] = 't';
		Folder [FileLoc++] = 'x';
		Folder [FileLoc++] = 't';
		Folder [FileLoc] = 0x00;
		if ((File = fopen (Folder, "wb")) == NULL)
		{
			printf ("    Error; could not create \"txt\" link file\n");
			free (Memory);
			return;
		}
		WriteLinkASCII (File, STXT, "Indentifier for DreamSDE");
		fputc (0x0D, File); fputc (0x0A, File);
		WriteLinkASCII (File, TypeEntry, "Type ID extracted");
		Folder [FolderLoc++] = '\\';
		Folder [FolderLoc] = 0x00;
		mkdir (Folder);
		switch (TypeCount)
		{
			case 0x01: // SMLT
			{
				ExtractSMLT (Memory, MemorySize, File, Folder, FolderLoc, DirectLoc);
			}
			break;
			case 0x02: // SFPW
			{
			}
			break;
			case 0x03: // SFPB
			{
			}
			break;
			case 0x04: // SFOB
			{
			}
			break;
			case 0x05: // SMPB
			{
				ExtractSMPB (Memory, MemorySize, File, Folder, FolderLoc, DirectLoc);
			}
			break;
			case 0x06: // SMDB
			{
				ExtractSMPB (Memory, MemorySize, File, Folder, FolderLoc, DirectLoc);
			}
			break;
			case 0x07: // SMSB
			{
			}
			break;
			case 0x08: // SPSR
			{
			}
			break;
			case 0x09: // SOSB
			{
			}
			break;
		}
		fclose (File);
	}
	free (Memory);
}

// =============================================================================
// -----------------------------------------------------------------------------
// Entry point
// -----------------------------------------------------------------------------

int main (int ArgCount, char **ArgList, char **EnvList)

{
	printf ("DreamSDE (Dreamcast Sound Data Extractor) - by MarkeyJester\n");
	if (ArgCount <= 0x01)
	{
		printf ("\n -> Drag and drop Dreamcast related files (as listed) to extract them:\n\n");
		int TypeCount = -0x01;
		char *TypeEntry = NULL;
		do
		{
			TypeEntry = TypeList [++TypeCount];
			if (TypeEntry != NULL)
			{
				printf ("    %s\n", TypeEntry);
			}
		}
		while (TypeEntry != NULL);
	}
	else
	{
		int ArgNumber;
		char *ArgEntry, *ArgName, *ArgExt;
		char Folder [0x0400]; int FolderLoc, DirectLoc;
		for (ArgNumber = 0x01; ArgNumber < ArgCount; ArgNumber++)
		{
			int i0, i1, i2, i3, i4, i5, i6, i7;
			ArgEntry = ArgList [ArgNumber];
			FolderLoc = 0x00;
			i7 = -0x01;
			ArgExt = NULL;
			do
			{
				i0 = ArgEntry [++i7] & 0xFF;
				Folder [FolderLoc++] = i0;
				if (i0 == '\\' || i0 == '/')
				{
					ArgName = ArgEntry + i7 + 0x01;
				}
				else if (i0 == '.')
				{
					ArgExt = ArgEntry + i7;
				}
			}
			while (i0 != 0x00);
			if (ArgExt == NULL)
			{
				ArgExt = ArgEntry + i7;
			}
			FolderLoc = ArgExt - ArgEntry;
			DirectLoc = ArgName - ArgEntry;
			printf ("\n -> %s\n", ArgName);
			LoadFile (ArgEntry, Folder, FolderLoc, DirectLoc);
		}
	}
	printf ("\nPress enter key to exit...\n");
	fflush (stdin);
	getc (stdin);
	return (0x00);
}

// =============================================================================

