//
// /home/ms/source/sidplay/libsidplay/include/RCS/sidtune.h,v
//
// This class is independent from the rest of the emulator engine
// and therefore can be used separately.
// It provides an interface to sidtunes in various file formats.
//

#ifndef SIDPLAY1_SIDTUNE_H
#define SIDPLAY1_SIDTUNE_H


#include <fstream>
using std::ofstream;
#include "mytypes.h"

const uint classMaxSongs = 256;   // also file format limit
const uint infoStringNum = 5;     // maximum
const uint infoStringLen = 80+1;  // 80 characters plus terminating zero

const udword maxSidtuneFileLen = 65536+2+0x7C;  // C64KB+LOAD+PSID

const int SIDTUNE_SPEED_VBI = 0;      // Vertical-Blanking-Interrupt
const int SIDTUNE_SPEED_CIA_1A = 60;  // CIA 1 Timer A

const int SIDTUNE_CLOCK_UNKNOWN = 0;  // These are also used in the
const int SIDTUNE_CLOCK_PAL = 0x01;   // emulator engine!
const int SIDTUNE_CLOCK_NTSC = 0x02;  // (binary)
const int SIDTUNE_CLOCK_ANY = (SIDTUNE_CLOCK_PAL|SIDTUNE_CLOCK_NTSC);

const int SIDTUNE_SIDMODEL_UNKNOWN = 0;
const int SIDTUNE_SIDMODEL_6581 = 0x01;
const int SIDTUNE_SIDMODEL_8580 = 0x02;
const int SIDTUNE_SIDMODEL_ANY = (SIDTUNE_SIDMODEL_6581|SIDTUNE_SIDMODEL_8580);

// An instance of this structure is used to transport values to
// and from the ``sidTune-class'';
struct sidTuneInfo
{
	// Consider the following fields as read-only, because the sidTune class
	// does not provide an implementation of: bool setInfo(sidTuneInfo&).
	// Currently, the only way to get the class to accept values which
	// are written to these fields is by creating a derived class.
	//
	const char* formatString;   // the name of the identified file format
	const char* speedString;    // describing the speed a song is running at
	uword loadAddr;
	uword initAddr;
	uword playAddr;
	uword startSong;
	uword songs;
	//
	// Available after song initialization.
	//
	uword irqAddr;              // if (playAddr == 0), interrupt handler has been
	                            // installed and starts calling the C64 player
	                            // at this address
	uword currentSong;          // the one that has been initialized
	ubyte songSpeed;            // initialized playing speed
	ubyte clockSpeed;           // initialized clock chip speed
	bool musPlayer;             // whether Sidplayer routine has been installed
    bool psidSpecific;          // whether PlaySID specific extensions are used
    ubyte clock;                // required clock chip speed (PAL, NTSC)
    ubyte sidModel;             // required SID chip model
    bool fixLoad;               // whether load address might be duplicate
	uword lengthInSeconds;      // --- reserved ---
    
    ubyte relocStartPage;       // first available page for relocation
    ubyte relocPages;           // number of pages available for relocation
    
    uword reserved;             // for copying (remainder of) PSID "reserved" field
	//
	// Song title, credits, ...
	//
	ubyte numberOfInfoStrings;  // the number of available text info lines
	char* infoString[infoStringNum];
	char* nameString;           // name, author and copyright strings
	char* authorString;         // are duplicates of infoString[?]
	char* copyrightString;
	//
	uword numberOfCommentStrings;  // --- not yet supported ---
	char ** commentString;         // -"-
	//
	udword dataFileLen;         // length of single-file sidtune file
	udword c64dataLen;          // length of raw C64 data without load address
        char* path;                 // path to sidtune files; 0, if cwd
	char* dataFileName;         // a first file: e.g. ``*.c64''
	char* infoFileName;         // a second file: e.g. ``*.sid''
	//
	const char* statusString;   // error/status message of last operation
};


class emuEngine;


class sidTune
{

 public:  // ----------------------------------------------------------------

	// --- constructors ---

	// If your opendir() and readdir()->d_name return path names
    // that contain the forward slash (/) as file separator, but
    // your operating system uses a different character, there are
    // extra functions that can deal with this special case. Set
    // separatorIsSlash to true if you like path names to be split
    // correctly.
    // You don't need these extra functions if your systems file
    // separator is the forward slash.
    //
	// Load a sidtune from a file.
    //
	// To retrieve data from standard input pass in filename "-".
	// If you want to override the default filename extensions use this
	// contructor. Please note, that if the specified ``sidTuneFileName''
	// does exist and the loader is able to determine its file format,
	// this function does not try to append any file name extension.
	// See ``sidtune.cpp'' for the default list of file name extensions.
	// You can specific ``sidTuneFileName = 0'', if you do not want to
	// load a sidtune. You can later load one with open().
	sidTune(const char* sidTuneFileName, const char **fileNameExt = 0);
	sidTune(const char* sidTuneFileName, const bool separatorIsSlash,
            const char **fileNameExt = 0);
    
	// Load a single-file sidtune from a memory buffer.
	// Currently supported: PSID format
	sidTune(const ubyte* oneFileFormatSidtune, udword sidtuneLength);

	virtual ~sidTune();  // destructor

	// --- member functions ---

	// The sidTune class does not copy the list of file name extensions,
	// so make sure you keep it. If the provided pointer is 0, the
	// default list will be activated.
	void setFileNameExtensions(const char **fileNameExt);
	
	// Load a sidtune into an existing object.
	// From a file.
	bool open(const char* sidTuneFileName);
	bool open(const char* sidTuneFileName, const bool separatorIsSlash);
    
	// From a buffer.
	bool load(const ubyte* oneFileFormatSidtune, udword sidtuneLength);

	bool getInfo( struct sidTuneInfo& );
	virtual bool setInfo( struct sidTuneInfo& );  // dummy

	ubyte getSongSpeed()  { return info.songSpeed; }
	uword getInitAddr() { return info.initAddr; }
	uword getPlayAddr() { return info.playAddr; }

	// This function initializes the SID emulator engine to play the given
	// sidtune song.
	friend bool sidEmuInitializeSong(emuEngine &, sidTune &, uword songNum);

	// This is an old non-obsolete sub-function, that does not scan the sidtune
	// for digis. If (emuConfig.digiPlayerScan == 0), this functions does the
	// same as the one above.
	friend bool sidEmuInitializeSongOld(emuEngine &, sidTune &, uword songNum);

	// Determine current state of object (true = okay, false = error).
	// Upon error condition use ``getInfo'' to get a descriptive
	// text string in ``sidTuneInfo.statusString''.
	operator bool()  { return status; }
	bool getStatus()  { return status; }

	// --- file save & format conversion ---

	// These functions work for any successfully created object.
	// overWriteFlag: true  = Overwrite existing file.
	//                false = Default, return error when file already
	//                        exists.
	// One could imagine an "Are you sure ?"-checkbox before overwriting
	// any file.
	// returns: true = Successful, false = Error condition.
	bool saveC64dataFile( const char* destFileName, bool overWriteFlag = false );
	bool saveSIDfile( const char* destFileName, bool overWriteFlag = false );
	bool savePSIDfile( const char* destFileName, bool overWriteFlag = false );

	// This function can be used to remove a duplicate C64 load address in
	// the C64 data (example: FE 0F 00 10 4C ...). A duplicate load address
	// of offset 0x02 is indicated by the ``fixLoad'' flag in the sidTuneInfo
	// structure.
	//
	// The ``force'' flag here can be used to remove the first load address
	// and set new INIT/PLAY addresses regardless of whether a duplicate
	// load address has been detected and indicated by ``fixLoad''.
	// For instance, some position independent sidtunes contain a load address
	// of 0xE000, but are loaded to 0x0FFE and call the player code at 0x1000.
	//
	// Don't forget to save the sidtune file.
	void fixLoadAddress(bool force = false, uword initAddr = 0, uword playAddr = 0);
	

 protected:  // -------------------------------------------------------------

	bool status;
	sidTuneInfo info;

	ubyte songSpeed[classMaxSongs];
	ubyte clockSpeed[classMaxSongs];  // not fully used by file formats
	uword songLength[classMaxSongs];  // reserved

	// holds text info from the format headers etc.
	char infoString[infoStringNum][infoStringLen];

	bool isCached;
	ubyte* cachePtr;
	udword cacheLen;

    bool isSlashedFileName;

	// Using external buffers for loading files instead of the interpreter
	// memory. This separates the sidtune objects from the interpreter.
	ubyte* fileBuf;
	ubyte* fileBuf2;

	udword fileOffset;  // for files with header: offset to real data

	// Filename extensions to append for various file types.
	const char **fileNameExtensions;

	// --- protected member functions ---

	// Convert 32-bit PSID-style speed word to internal tables.
	void convertOldStyleSpeedToTables(udword oldStyleSpeed);

	// Copy C64 data from internal cache to C64 memory.
	bool placeSidTuneInC64mem( ubyte* c64buf );

	udword loadFile(const char* fileName, ubyte** bufferRef);
	bool saveToOpenFile( ofstream& toFile, const ubyte* buffer, udword bufLen );

    bool fileExists(const char* fileName);
   
	// Data caching.
	bool cacheRawData(const void* sourceBuffer, udword sourceBufLen);
	bool getCachedRawData(void* destBuffer, udword destBufLen);

	// Support for various file formats.

	virtual bool PSID_fileSupport(const void* buffer, udword bufLen);
	virtual bool PSID_fileSupportSave(ofstream& toFile, const ubyte* dataBuffer);

        virtual bool MUS_fileSupport(const void* buffer, udword bufLen);
        virtual void MUS_installPlayer(ubyte *c64buf);

	virtual bool INFO_fileSupport(const void* dataBuffer, udword dataBufLen,
                                      const void* infoBuffer, udword infoBufLen);

	virtual bool SID_fileSupport(const void* dataBuffer, udword dataBufLen,
                                     const void* sidBuffer, udword sidBufLen);
	virtual bool SID_fileSupportSave(ofstream& toFile);

	
 private:  // ---------------------------------------------------------------
	
	void safeConstructor();
	void safeDestructor();
#if !defined(SID_NO_STDIN_LOADER)
	void stdinConstructor();
#endif
	void filesConstructor(const char* name);
        void bufferConstructor(const ubyte* data, udword dataLen);
	
	uword selectSong(uword selectedSong);
	void setIRQaddress(uword address);
	
        void clearCache();

	void deleteFileBuffers();
        void deleteFileNameCopies();
	// Try to retrieve single-file sidtune from specified buffer.
	bool getSidtuneFromFileBuffer(const ubyte* buffer, udword bufferLen);
	// Cache the data of a single-file or two-file sidtune and its
	// corresponding file names.
	void acceptSidTune(const char* dataFileName, const char* infoFileName,
                           const ubyte* dataFileBuf, udword dataLen );
	bool createNewFileName(char** destStringPtr,
                               const char* sourceName, const char* sourceExt);
};
	

#endif  /* SIDPLAY1_SIDTUNE_H */
