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

/*
File: TapeUnit.cpp
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.
*/


// TapeUnit.cpp: implementation of the CTapeUnit class.
//


#include "stdafx.h"
#include "Globals.h"
#include "Tape.h"
#include "MSFManager.h"
#include "TapeUnit.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction


CTapeUnit::CTapeUnit() :
	m_type(TYPE_NONE),
	m_address(UNKNOWN),
	m_length(UNKNOWN),
	m_msTime(UNKNOWN),
	m_nWaveLength(UNKNOWN),
	m_bCRC(FALSE)
{
}

CTapeUnit::CTapeUnit(CTapeUnit &tapeUnit)
{
	m_type = tapeUnit.m_type;
	m_address = tapeUnit.m_address;
	m_length = tapeUnit.m_length;
	m_msTime = tapeUnit.m_msTime;
	m_nWaveLength = tapeUnit.m_nWaveLength;
	m_bCRC = tapeUnit.m_bCRC;
	m_strName = tapeUnit.m_strName;
	m_strPath = tapeUnit.m_strPath;
}

CTapeUnit::~CTapeUnit()
{
}


CTapeUnit &CTapeUnit::operator= (CTapeUnit &tapeUnit)
{
	m_type = tapeUnit.m_type;
	m_address = tapeUnit.m_address;
	m_length = tapeUnit.m_length;
	m_msTime = tapeUnit.m_msTime;
	m_nWaveLength = tapeUnit.m_nWaveLength;
	m_bCRC = tapeUnit.m_bCRC;
	m_strName = tapeUnit.m_strName;
	m_strPath = tapeUnit.m_strPath;
	return *this;
}


BOOL CTapeUnit::SetFile(CString &strPath)
{
	// Check file status
	CFileStatus fs;

	if (!CFile::GetStatus(strPath, fs))
	{
		return FALSE;
	}

	// All checks done. Define file type
	m_strName = _T("");
	m_bCRC = FALSE;

	if (IsFileWave(strPath))
	{
		// File is PCM Wave
		m_type = TYPE_WAV;
	}
	else if (IsFileMSF(strPath))
	{
		// File is MSF
		m_type = TYPE_MSF;
	}
	else if (IsFileBin(strPath))
	{
		// File is BIN
		m_type = TYPE_BIN;
		m_bCRC = TRUE;
	}
	else
	{
		return FALSE;
	}

	m_address = UNKNOWN;
	m_length = UNKNOWN;
	m_msTime = UNKNOWN;
	m_nWaveLength = UNKNOWN;
	m_strPath = strPath;
	return TRUE;
}


BOOL CTapeUnit::SetTmpFile(CString &strPath)
{
	// Check file status
	CFileStatus fs;

	if (!CFile::GetStatus(strPath, fs))
	{
		return FALSE;
	}

	// All checks done. Define file type
	// File is TMP captured Wave
	m_type = TYPE_TMP;
	m_address = UNKNOWN;
	m_length = UNKNOWN;
	m_msTime = UNKNOWN;
	m_nWaveLength = UNKNOWN;
	m_bCRC = FALSE;
	m_strName = _T("");
	m_strPath = strPath;
	return TRUE;
}


BOOL CTapeUnit::IsFileWave(CString &strPath)
{
	CFile waveFile;

	if (!waveFile.Open(strPath, CFile::modeRead))
	{
		return FALSE;
	}

	struct WaveHeader
	{
		long    riffTag;
		long    size;
		long    waveTag;
		long    fmtTag;
		long    fmtSize;
	} waveHeader;

	if (waveFile.Read(&waveHeader, sizeof(WaveHeader)) != sizeof(WaveHeader))
	{
		return FALSE;
	}

	if (waveHeader.riffTag != 0x46464952 || waveHeader.fmtTag != 0x20746d66)
	{
		return FALSE;
	}

	WAVEFORMATEX wfx;

	if (waveFile.Read(&wfx, waveHeader.fmtSize) != waveHeader.fmtSize)
	{
		return FALSE;
	}

	wfx.cbSize = 0;

	if (wfx.nAvgBytesPerSec != 44100 || wfx.nBlockAlign != 1
	        || wfx.nChannels != 1 || wfx.nSamplesPerSec != 44100
	        || wfx.wBitsPerSample != 8 || wfx.wFormatTag != WAVE_FORMAT_PCM)
	{
		return FALSE;
	}

	struct DataHeader
	{
		long    dataTag;
		long    dataSize;
	} dataHeader;

	while (1)
	{
		if (waveFile.Read(&dataHeader, sizeof(DataHeader)) != sizeof(DataHeader))
		{
			return FALSE;
		}

		if (dataHeader.dataTag == 0x61746164)
		{
			break;
		}

		waveFile.Seek(dataHeader.dataSize, SEEK_CUR);
	}

	return TRUE;
}


BOOL CTapeUnit::IsFileMSF(CString &strPath)
{
	CMSFManager msf;

	if (!msf.OpenFile(strPath, TRUE))
	{
		return FALSE;
	}

	MSF_BLOCK_INFO bi;

	if (!msf.FindBlock(MSF_BLOCKTYPE_WAVE, &bi))
	{
		return FALSE;
	}

	return TRUE;
}


BOOL CTapeUnit::IsFileBin(CString &strPath)
{
	CFileStatus fs;

	if (!CFile::GetStatus(strPath, fs))
	{
		return FALSE;
	}

	CFile binFile;

	if (!binFile.Open(strPath, CFile::modeRead))
	{
		return FALSE;
	}

	WORD length;

	if (binFile.Read(&length, sizeof(length)) != sizeof(length))
	{
		return FALSE;
	}

	if (binFile.Read(&length, sizeof(length)) != sizeof(length))
	{
		return FALSE;
	}

	if (length != fs.m_size - 4) //если не простой бин
	{
		if (length != fs.m_size - 22) //и не сложный
		{
			return FALSE;
		}
	}

	return TRUE;
}


BOOL CTapeUnit::RetrieveInfo()
{
	CTape *pTape = new CTape;
	TAPE_FILE_INFO tfi;

	if (m_type == TYPE_BIN)
	{
		// Open binary file
		if (!pTape->LoadBinFile(m_strPath, &tfi))
		{
			delete pTape;
			return FALSE;
		}
	}
	else if (m_type == TYPE_WAV)
	{
		// Open wave file
		if (!pTape->LoadWaveFile(m_strPath))
		{
			delete pTape;
			return FALSE;
		}
	}
	else if (m_type == TYPE_MSF)
	{
		// Open MSF file
		if (!pTape->LoadMSFFile(m_strPath))
		{
			delete pTape;
			return FALSE;
		}
	}
	else if (m_type == TYPE_TMP)
	{
		// Open TMP file
		if (!pTape->LoadTmpFile(m_strPath))
		{
			delete pTape;
			return FALSE;
		}
	}
	else
	{
		return FALSE;    // Unknown file format
	}

	pTape->GetWaveFile(&tfi, FALSE);

	if (tfi.marker2 == -1)
	{
		delete pTape;
		return FALSE; // File is truncated
	}

	// Get Full file information
	m_address = tfi.address;
	m_length = tfi.length;
	m_nWaveLength = pTape->GetWaveLength();
	m_msTime = (int)(m_nWaveLength / ((double)BUFFER_FREQUENCY / 1000.0));
	m_bCRC = pTape->CalcCRC(&tfi, TRUE);
	m_strName = BKToUNICODE(tfi.name, 16);
	m_strName.Trim();
	delete pTape;
	return TRUE;
}


BOOL CTapeUnit::SaveAs(CString strPath, int type)
{
	g_Config.NormalizePath(strPath);
	CString strNewPath = strPath + m_strName;
	CString strName = g_Config.GetFileName(m_strName);
	CString strExt = g_Config.GetFileExt(m_strName);
	CString strTypeExt;
	CFileStatus fs;
	TAPE_FILE_INFO tfi;
	CTape *ptape = NULL;
	BOOL bRet = TRUE;

	// Add new type extension
	switch (type)
	{
		case TYPE_BIN:
		{
			// Add bin extension
			strTypeExt.LoadString(IDS_FILEEXT_BINARY);
			strNewPath = strPath + strName;

			if (strExt.IsEmpty() || !strExt.CollateNoCase(strTypeExt))
			{
				strNewPath += strTypeExt;
			}
		}
		break;

		case TYPE_WAV:
		{
			// Add tape extension
			strTypeExt.LoadString(IDS_FILEEXT_WAVE);
			strNewPath = strPath + strName + strTypeExt;
		}
		break;

		case TYPE_MSF:
		{
			// Add tape extension
			strTypeExt.LoadString(IDS_FILEEXT_TAPE);
			strNewPath = strPath + strName + strTypeExt;
		}
		break;
	}

	// Check is file already exists
	if (CFile::GetStatus(strNewPath, fs))
	{
		return FALSE; // If exists - return error
	}

	ptape = new CTape;

	switch (m_type)
	{
		case TYPE_BIN: // If current type is bin
			if (type == TYPE_BIN)
			{
				// If type equal copy without conversion
				bRet = CopyFile(m_strPath, strNewPath, FALSE);
			}
			else
			{
				// Conver bin to wave
				bRet = ptape->LoadBinFile(m_strPath, &tfi);
			}

			break;

		case TYPE_WAV: // If current type is wave
			if (type == TYPE_WAV)
			{
				// If type equal copy without conversion
				bRet = CopyFile(m_strPath, strNewPath, FALSE);
			}
			else
			{
				bRet = ptape->LoadWaveFile(m_strPath);
			}

			break;

		case TYPE_MSF: // If current type is MSF
			if (type == TYPE_MSF)
			{
				// If type equal copy without conversion
				bRet = CopyFile(m_strPath, strNewPath, FALSE);
			}
			else
			{
				// Conver bin to wave
				bRet = ptape->LoadMSFFile(m_strPath);
			}

			break;

		case TYPE_TMP: // If current type is temp
			bRet = ptape->LoadTmpFile(m_strPath);
			break;

		default: // Unknown type
			ASSERT(FALSE);
			bRet = FALSE;
	}

	if (bRet)
	{
		// Convert to specified format
		switch (type)
		{
			case TYPE_BIN:
				// Save as bin
				bRet = ptape->GetWaveFile(&tfi, FALSE);

				if (bRet)
				{
					bRet = ptape->SaveBinFile(strNewPath, &tfi);
				}

				break;

			case TYPE_WAV:
				// Save as wave
				bRet = ptape->SaveWaveFile(strNewPath);
				break;

			case TYPE_MSF:
				// Save as tape
				bRet = ptape->SaveMSFFile(strNewPath);
				break;
		}
	}

	SAFE_DELETE(ptape);
	return bRet;
}
