/*************************************************
*            BK Emulator for Window 3.0          *
*************************************************/

/*
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>


#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;
};


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

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

		BYTE               *m_pBin;
		int                 m_pos;
		SAMPLE_TYPE         m_average;
		int                 m_last_len;
		int                 m_avg_length;

		BYTE               *m_pPackBits;
		int                 m_bitsLength;

		WORD               *m_pPackLZW;
		int                 m_lzwLength;

		enum                {RECORD_BUFFER = 4 * 1024 * 1024};
		enum                {RECORD_GROW = 2 * 1024 * 1024};
		enum                {BAD_CODE = 65535};
		enum                {RESET_CODE = 256};
		enum                {PREFIX = 256};
		enum                {CODE = 257};
		enum                {MAX_TABLE_SIZE = 65000};

		WORD               *m_scan_table[MAX_TABLE_SIZE];
		int                 m_table_size;

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

		enum                {BAD_LENGTH = -1};

		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(SAMPLE_TYPE *pWave, int pos);
		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(int dwLength);

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

		// Wave pack/unpack methods
		int                 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_waveLength;
		}
		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_play_pos;
		}
		void                StartPlay();
		void                StopPlay();
		BOOL                IsPlaying();
		void                StartRecord(BOOL bAutoBeginRecord, BOOL bAutoEndRecord);
		void                RecordWaveGetBuffer(BYTE *pBuff, int buffLength);
		void                StopRecord();
		BOOL                IsRecording();
};
