/*************************************************
*           BK Emulator v3.8 for Windows         *
*************************************************/

/*
File: Tape.h
Version: 1.0.0
Written by: Yuriy Kalmykov <kalmykov@stoik.com>
    Copyright (c) 2002-2004 Yuriy Kalmykov

    BK Emulator is a program emulated hardware environment for running
code for BK 0010(01) in different configurations.

    This code may be used in compiled form in any way you desire.
This file or it's parts can't be redistributed without the authors
written consent, but can be modified for your private needs.
    Providing that this notice and the authors name and all copyright
notices remains intact.

    Please, an email me to know how you are using it and where. You can
ask me for any information about this below code or any attendant
knowledge.

    This file is provided "as is" with no expressed or implied warranty.
The author accepts no liability for any damage or loss of business that
this product may cause.
*/


// Tape.h: interface for the CTape class.
//


#pragma once

#include "Defines.h"
#include "LockVarType.h"

#include <mmsystem.h>

//"RIFF"
#define RIFF_TAG 0x46464952
// "fmt "
#define FMT_TAG 0x20746d66
// "data"
#define DATA_TAG 0x61746164
// "WAVE"
#define WAVE_TAG 0x45564157

#pragma pack(push)
#pragma pack(1) // envelope period (envT, R13-R14) has invalid word alignment
struct TAPE_FILE_INFO
{
	DWORD               start_tuning;
	DWORD               synchro_start;
	DWORD               marker1;
	DWORD               synchro_header;
	DWORD               header;
	WORD                address;
	WORD                length;
	BYTE                name[16];
	DWORD               marker2;
	DWORD               synchro_data;
	DWORD               data;
	WORD                crc;
	DWORD               synchro6;
	DWORD               end_tuning;
	DWORD               end;
};
#pragma pack(pop)

struct WaveHeader
{
	DWORD               riffTag;
	DWORD               size;
	DWORD               waveTag;
	DWORD               fmtTag;
	DWORD               fmtSize;
};

struct DataHeader
{
	DWORD               dataTag;
	DWORD               dataSize;
};

#define MAX_TABLE_SIZE 65000
#define RECORD_BUFFER (4 * 1024 * 1024)
#define RECORD_GROW (2 * 1024 * 1024)
#define BAD_CODE 65535
#define RESET_CODE 256
#define PREFIX 256
#define CODE 257
#define BAD_LENGTH -1

class CTape
{
		LockVarType     m_lockPlay;
		SAMPLE_TYPE    *m_pWave;
		int             m_nWaveLength;  //размер массива m_pWave в сэмплах
		int             m_nPlayPos;     //текущая позиция воспроизведения в сэмплах

		LockVarType     m_lockRecord;   //флаг записи
		SAMPLE_TYPE    *m_pRecord;      //массив, куда записывается звук
		int             m_nRecordPos;   //текущая позиция в массиве в сэмплах
		int             m_nRecordLength; //длина массива в сэмплах
		bool            m_bAutoBeginRecord;
		bool            m_bAutoEndRecord;

		BYTE           *m_pBin;
		int             m_nPos;
		SAMPLE_TYPE     m_nAverage, m_nPeakMin, m_nPeakMax;
		int             m_nLastLen;
		int             m_nAvgLength;

		BYTE           *m_pPackBits;
		int             m_nPackBitsLength;

		WORD           *m_pPackLZW;
		int             m_nPackLZWLength;

		WORD           *m_pScanTable[MAX_TABLE_SIZE];
		int             m_nScanTableSize;

		void            InitTables();
		void            ClearTables();
		int             TableLookup_Fast(WORD prefix, int end_pos);
		void            AddWord_Fast(WORD prefix, int end_pos);
		bool            UnpackWord(WORD code);

		bool            FindTuning(int length, DWORD &wave_pos, int &wave_length);
		bool            FindSyncro();
		bool            FindSyncro6();
		int             CalcImpLength(SAMPLE_TYPE *pWave, int &pos, int length);
		inline SAMPLE_TYPE GetCurrentSampleMono(register SAMPLE_TYPE *pWave, register int pos);
		void            SetCurrentSampleMono(SAMPLE_TYPE *pWave, int pos, SAMPLE_TYPE sample);
		int             DefineLength(int length);
		bool            ReadBit(bool &bBit);
		bool            ReadByte(BYTE &byte);

		bool            SaveTuning(int length);
		bool            SaveSyncro();
		bool            SaveSyncro6();
		bool            SaveImp(int size);
		bool            SaveBit(bool bBit);
		bool            SaveByte(BYTE byte);

		bool            FindRecordBegin(int buffLength);
		bool            FindRecordEnd();
		void            CalculateAverage();

	public:
		CTape();
		virtual ~CTape();

		// Wave pack/unpack methods
		void            PackBits();
		void            UnpackBits();

		int             PackLZW_Fast();
		void            UnpackLZW_Fast();

		// Wave read/write methods
		bool            SetWaveFile(TAPE_FILE_INFO *pTfi);
		bool            GetWaveFile(TAPE_FILE_INFO *pTfi, bool bHeaderOnly = false);
		bool            CalcCRC(TAPE_FILE_INFO *pTfi, bool bCheck);

		bool            LoadWaveFile(const CString &strPath);
		bool            SaveWaveFile(const CString &strPath);

		bool            LoadBinFile(const CString &strPath, TAPE_FILE_INFO *pTfi);
		bool            SaveBinFile(const CString &strPath, TAPE_FILE_INFO *pTfi);

		bool            LoadMSFFile(const CString &strPath);
		bool            SaveMSFFile(const CString &strPath);

		bool            LoadTmpFile(const CString &strPath);

		// Wave buffer methods
		bool            AllocWaveBuffer(int nSize);
		bool            LoadBuffer(BYTE *pBuff, int nSize);
		inline int      GetWaveLength()
		{
			return m_nWaveLength;
		}
		inline BYTE    *GetWaveBuffer()
		{
			return (BYTE *)m_pWave;
		}

		// Playing methods
		void            SetPlayWavePos(int play_pos);
		void            PlayWaveGetBuffer(BYTE *pBuff, int buffLength);

		inline int      GetPlayWavePos()
		{
			return m_nPlayPos;
		}
		void            StartPlay();
		void            StopPlay();
		bool            IsPlaying();
		void            StartRecord(bool bAutoBeginRecord, bool bAutoEndRecord);
		void            RecordWaveGetBuffer(BYTE *pBuff, int buffLength);
		void            StopRecord();
		bool            IsRecording();
};
