// OpenCP Module Player
// copyright (c) '94-'98 Niklas Beisert <nbeisert@physik.tu-muenchen.de>
//
// SIDPlay PSID file loader
//
// revision history: (please note changes here)
//  -kb980717  Tammo Hinrichs <opencp@gmx.net>
//    -first release

//
// 1997/05/11 11:29:06
//

#include <alloc.h>
#include <string.h>
#include "binfile.h"
#include "psid_.h"


static const char text_notEnoughMemory[] = "ERROR: Not enough free memory";
static const char text_format[] = "PlaySID one-file format (PSID)";
static const char text_psidTruncated[] = "ERROR: PSID file is most likely truncated";

struct psidHeader
{
	//
	// All values in big-endian order.
	//
	char id[4];          // 'PSID'
	ubyte version[2];    // 0x0001 or 0x0002
	ubyte data[2];       // 16-bit offset to binary data in file
	ubyte load[2];       // 16-bit C64 address to load file to
	ubyte init[2];       // 16-bit C64 address of init subroutine
	ubyte play[2];       // 16-bit C64 address of play subroutine
	ubyte songs[2];      // number of songs
	ubyte start[2];      // start song (1-256 !)
	ubyte speed[4];      // 32-bit speed info
						 // bit: 0=50 Hz, 1=CIA 1 Timer A (default: 60 Hz)
	char name[32];       // ASCII strings, 31 characters long and
	char author[32];     // terminated by a trailing zero
	char copyright[32];  //
	ubyte flags[2];      // only version 0x0002
	ubyte reserved[4];   // only version 0x0002
};


char sidTune::PSID_fileSupport(binfile &f)
{
	struct psidHeader header;
	binfilepos fileLen;

	// Remove any format description or format error string.
	info.formatString = 0;

	fileLen = f.length();
	f.seek(0);
	if (f.read((void huge *)&header, sizeof(psidHeader)) < 6) {
		return false;
	}

	// Now it is safe to access the first bytes.
	// Require a valid ID and version number.
	if ( (readBEdword((ubyte huge *)header.id) != 0x50534944)  // "PSID" ?
		|| (readBEword(header.version) >= 3) )
	{
		return false;
	}
	// Due to security concerns, input must be at least as long as version 1
	// plus C64 load address data. That is the area which will be accessed.
	if ( fileLen < (sizeof(psidHeader)+2) )
	{
		info.formatString = text_psidTruncated;
		return false;
	}

	fileOffset = readBEword(header.data);
	info.loadAddr = readBEword(header.load);
	info.initAddr = readBEword(header.init);
	info.playAddr = readBEword(header.play);
	info.songs = readBEword(header.songs);
	info.startSong = readBEword(header.start);

	if (info.songs > classMaxSongs)
	{
		info.songs = classMaxSongs;
	}

	// Create the speed/clock setting table.
	udword oldStyleSpeed = readBEdword(header.speed);
	convertOldStyleSpeedToTables(oldStyleSpeed);

	info.musPlayer = false;
	if ( readBEword(header.version) >= 2 )
	{
		if (( readBEword(header.flags) & 1 ) == 1 )
		{
			info.musPlayer = true;
		}
	}

	if ( info.loadAddr == 0 )
	{
		ubyte loadAddrBuf[2];
		if ((f.seek(fileOffset) != fileOffset) ||
				(f.read((void far *)&loadAddrBuf, 2) != 2)) {
			info.formatString = text_psidTruncated;
			return false;
		}
		info.loadAddr = readEndian(loadAddrBuf[1], loadAddrBuf[0]);
		fileOffset += 2;
	}
	if ( info.initAddr == 0 )
	{
		info.initAddr = info.loadAddr;
	}
	if ( info.startSong == 0 )
	{
		info.startSong = 1;
	}

	// Now adjust MUS songs.
	if ( info.musPlayer )
	{
		info.loadAddr = 0x1000;
		info.initAddr = 0xc7b0;
		info.playAddr = 0;
	}

	// Correctly terminate the info strings.
	header.name[31] = 0;
	header.author[31] = 0;
	header.copyright[31] = 0;

	// Copy info strings, so they will not get lost.
	_fstrcpy( &infoString[0][0], header.name );
	info.nameString = &infoString[0][0];
	info.infoString[0] = &infoString[0][0];
	_fstrcpy( &infoString[1][0], header.author );
	info.authorString = &infoString[1][0];
	info.infoString[1] = &infoString[1][0];
	_fstrcpy( &infoString[2][0], header.copyright );
	info.copyrightString = &infoString[2][0];
	info.infoString[2] = &infoString[2][0];
	info.numberOfInfoStrings = 3;

	info.formatString = text_format;
	info.dataFileLen = fileLen;

	return true;
}

