#include "StdAfx.h"
#include "BKDImage.h"
#include "SprWnd.h"

CBKDImage::CBKDImage(const PARSE_RESULT &image)
{
	m_nLastErrorNumber = IMAGE_ERROR_OK;
	m_sPImg = image;
	m_bFileROMode = FALSE;
	Reset();
}

CBKDImage::~CBKDImage(void)
{
	CloseFile();
}

void CBKDImage::Reset()
{
	m_sDiskCat.nFreeRecs = 0;
	m_sDiskCat.nFreeBlocks = 0;
	OpenFile();
}

BOOL CBKDImage::OpenFile(void)
{
	m_nLastErrorNumber = IMAGE_ERROR_OK;
	CloseFile();

	if (m_ImgFile.Open(m_sPImg.strName, CFile::modeReadWrite | CFile::shareDenyWrite))
	{
		m_bFileROMode = FALSE;
		return TRUE;
	}
	else if (m_ImgFile.Open(m_sPImg.strName, CFile::modeRead | CFile::shareDenyWrite))
	{
		m_bFileROMode = TRUE;
		return TRUE;
	}

	m_nLastErrorNumber = IMAGE_ERROR_FILE_CANNOT_OPEN;
	return FALSE;
}

void CBKDImage::CloseFile(void)
{
	if (m_ImgFile.m_hFile != CFile::hFileNull)
	{
		m_ImgFile.Close();
	}
}

//виртуальная функция, для каждой ФС - своя реализация.
BOOL CBKDImage::ReadCatalog()
{
	m_nLastErrorNumber = IMAGE_ERROR_OK;
	m_sDiskCat.vecFC.clear();
	m_sDiskCat.nVectorPos = 0;
	m_sDiskCat.nTotalRecs = m_sDiskCat.nTotalBlocks = m_sDiskCat.nFreeRecs = m_sDiskCat.nFreeBlocks = -1;

	if (m_ImgFile.m_hFile != CFile::hFileNull)
	{
		return TRUE;
	}
	else
	{
		m_nLastErrorNumber = IMAGE_ERROR_FILE_NOT_OPEN;
		return FALSE;
	}
}

CString CBKDImage::GetInfo()
{
	CString str, strf;
	strf.LoadString(IDS_INFO_FREE_BLK);
	unsigned int bytes = (unsigned int)m_sDiskCat.nFreeBlocks * 512;
	unsigned int totalbytes = (unsigned int)m_sDiskCat.nTotalBlocks * 512;
	str.Format(strf, (unsigned int)m_sDiskCat.nFreeRecs, tblStrRec[GetWordEndIdx((unsigned int)m_sDiskCat.nFreeRecs)],
	           (unsigned int)m_sDiskCat.nTotalRecs,
	           (unsigned int)m_sDiskCat.nFreeBlocks, tblStrBlk[GetWordEndIdx((unsigned int)m_sDiskCat.nFreeBlocks)],
	           bytes, tblStrBlk[GetWordEndIdx(bytes)],
	           (unsigned int)m_sDiskCat.nTotalBlocks, totalbytes);
	return str;
}

void CBKDImage::SetStorePath(const CString &str)
{
	m_strStorePath = str;
	m_strStorePath.TrimRight(_T('\\'));
	m_strStorePath += _T('\\');
}

BOOL CBKDImage::ExtractObject(FileRecord *sFile, BOOL bExtractAsBin, BOOL bExtractLog)
{
	m_nLastErrorNumber = IMAGE_ERROR_OK;
	BOOL bRet = TRUE;

	//если /*плохой или удалённый или*/ ссылка - ничего не делаем
	if (sFile->nAttr & FR_ATTR_LINK)
	{
		return bRet;
	}

	if (sFile->nAttr & FR_ATTR_DIR)
	{
		//если директория, надо в неё зайти и получить список всех записей.
		int cur_dir = sFile->nDirDir;
		FileRecord cur_fr;
		CString tmpStorePath = m_strStorePath;
		CString strDirName = sFile->strName;
		SetSafeDir(strDirName);
		SetStorePath(m_strStorePath + strDirName);

		if (_tmkdir(m_strStorePath))
		{
			m_nLastErrorNumber = IMAGE_ERROR_CANNOT_CREATE_DIR;
			bRet = FALSE;
		}

		BOOL bEnd = GetStartFileName(&cur_fr, cur_dir);

		if (bEnd)
		{
			do
			{
				if (cur_fr.strName != _T(".."))
				{
					bRet = ExtractObject(&cur_fr, bExtractAsBin, bExtractLog);
				}
			}
			while (bEnd = GetNextFileName(&cur_fr, cur_dir));
		}

		m_strStorePath = tmpStorePath;
	}
	else
	{
		//если файл
		bRet = ExtractFile(sFile, bExtractAsBin, bExtractLog);
	}

	return bRet;
}

//процедура извлечения файла для большинства форматов такая, поэтому это будет процедура по умолчанию
BOOL CBKDImage::ExtractFile(FileRecord *sFile, BOOL bExtractAsBin, BOOL bExtractLog)
{
	m_nLastErrorNumber = IMAGE_ERROR_OK;
	BOOL bRet = TRUE;

	//если /*плохой или удалённый или*/ директория - ничего не делаем
	if (sFile->nAttr & FR_ATTR_DIR)
	{
		return bRet;
	}

	AnalyseFileStr sAFS;
	sAFS.bUseAsBin = bExtractAsBin;
	sAFS.nAddr = sFile->nAddress;
	sAFS.nLen = sFile->nSize;
	sAFS.strName = sFile->strName;
	SetSafeName(sAFS.strName);
	sAFS.strName += ((bExtractAsBin) ? _T(".bin") : _T(""));

	if (sAFS.file.Open(m_strStorePath + sAFS.strName, CFile::modeCreate | CFile::modeReadWrite))
	{
		int len = sFile->nSize;
		int out_len;
		int sector = sFile->nStartSector;

		if (m_sPImg.nBaseOffset + sector * 512 == m_ImgFile.Seek(m_sPImg.nBaseOffset + sector * 512, CFile::begin)) //перемещаемся к началу файла
		{
			//поскольку файл располагается единым куском, копируем его по частям
			if (bExtractAsBin)
			{
				m_mBinHeader[0] = (WORD)sFile->nAddress;
				m_mBinHeader[1] = (WORD)len; //и размер файла, если было больше 64кб, тут усекается.
				sAFS.file.Write(m_mBinHeader, sizeof(m_mBinHeader));
			}

			while (len > 0)
			{
				//тут не будем проверять на ошибки, будь что будет
				m_ImgFile.Read(&m_mBlock, COPY_BLOCK_SIZE);

				if (len >= COPY_BLOCK_SIZE)
				{
					out_len = COPY_BLOCK_SIZE;
				}
				else
				{
					out_len = len;
				}

				sAFS.file.Write(&m_mBlock, out_len);
				len -= out_len;
			}

			if (bRet)
			{
				if (bExtractLog)
				{
					bRet = AnalyseExportFile(&sAFS);
				}
			}
		}
		else
		{
			m_nLastErrorNumber = IMAGE_ERROR_CANNOT_SEEK_FILE;
			bRet = FALSE;
		}

		sAFS.file.Close();
	}
	else
	{
		m_nLastErrorNumber = IMAGE_ERROR_FILE_CANNOT_CREATE;
		bRet = FALSE;
	}

	return bRet;
}

//на входе запись с файлом
BOOL CBKDImage::ViewFile(FileRecord *sFile)
{
	m_nLastErrorNumber = IMAGE_ERROR_OK;
	BOOL bRet = TRUE;

	//если это не файл - выходим ничего не делая
	if (sFile->nAttr & (FR_ATTR_DIR | FR_ATTR_LOGDISK))
	{
		return bRet;
	}

	TCHAR pTmpPath[MAX_PATH];
	CFile tmpFile;
	CString strFName = sFile->strName;
	SetSafeName(strFName);
	_tsplitpath_s(strFName, NULL, 0, NULL, 0, NULL, 0, pTmpPath, MAX_PATH); //узнаем расширение
	CString strExt = CString(pTmpPath).MakeUpper();
	GetTempPath(MAX_PATH, pTmpPath);
	strFName = CString(pTmpPath) + strFName;

	//чудно, но CFile::typeText не поддерживается, поэтому файл открываем бинарно, и переводы строк формируем сами.
	if (tmpFile.Open(strFName, CFile::modeCreate | CFile::modeWrite | CFile::shareExclusive))
	{
		int len = sFile->nSize;
		SeekToBeginFile(&m_ImgFile, sFile); //перемещаемся к началу файла
		//поскольку файл располагается единым куском, копируем его по частям,
		//а для андоса сделали свои виртуальные функции
		BYTE b;
		int counter = 0;
		TCHAR tch = 0xfeff; //BOM

		if (strExt == _T(".VXT"))
		{
			PutTchar(&tmpFile, tch, counter); //запишем сперва BOM
			SaveCurrPos(&m_ImgFile, len);

			if (GetWord(&m_ImgFile, &len) == 0x0c40)
			{
				//это мы пропускаем всякие шрифты и т.п.
				counter = GetWord(&m_ImgFile, &len);
			}
			else
			{
				counter = 0;
			}

			//имитируем перемещение на нужную позицию относительно начала файла
			len = SeekToPos(&m_ImgFile, counter);
			int tabs = 0;
			counter = 0;

			while (len > 0)
			{
				b = GetByte(&m_ImgFile, &len);

				if (len < 0) //доп.проверка на всякий случай
				{
					break;
				}

				if (b == 0 || b == 10) //конец строки
				{
					PutTchar(&tmpFile, _T('\r'), counter);
					counter = -1;
					tch = _T('\n');
				}
				else if (b < 9) //обычно это просто количество пробелов, но бывает и количество табуляторов.
				{
					if (tabs == 0)
					{
						tabs++;
					}

					for (int i = 0; i < b; i++)
					{
						for (int j = 0; j < tabs; j++)
						{
							PutTchar(&tmpFile, _T(' '), counter);
						} //что-то я не понимаю, как это работает, хотя и сам писал когда-то
					}

					tabs *= 8;
					continue;
				}
				else if (b == 9) //табуляция, по 8 символов на табулятор
				{
					tabs = 0;

					do
					{
						PutTchar(&tmpFile, _T(' '), counter);
					}
					while ((counter & 7) != 0);

					continue;
				}
				else if (b < 32)
				{
					tabs = 0;
					continue; //эти коды в vxt используются для визуального форматирования, типа жирный текст, курсив и т.п.
				}
				else
				{
					tch = BKToUNICODE_Byte(b);
				}

				tabs = 0;
				PutTchar(&tmpFile, tch, counter);
			}
		}
		else
		{
			BOOL bFlg = FALSE;
			PutTchar(&tmpFile, tch, counter); //запишем сперва BOM

			while (len > 0)
			{
				b = GetByte(&m_ImgFile, &len);

				if (b == 0 || b == 10) //перевод строки
				{
					PutTchar(&tmpFile, _T('\r'), counter);
					tch = _T('\n');
				}
				else if (b == 9) //табуляция
				{
					tch = _T('\t');
				}
				else if (b < 9) //это количество пробелов, или табуляций
				{
					if (bFlg) //если число второй раз подряд - это уже число табуляций
					{
						for (int i = 0; i < b; i++)
						{
							for (int j = 0; j < 8; j++)
							{
								PutTchar(&tmpFile, _T(' '), counter);
							}
						}

						bFlg = FALSE;
					}
					else //если число один раз - это кол-во пробелов
					{
						for (int i = 0; i < b; i++)
						{
							PutTchar(&tmpFile, _T(' '), counter);
						}

						bFlg = TRUE;
					}

					continue;
				}
				else
				{
					tch = BKToUNICODE_Byte(b);
				}

				PutTchar(&tmpFile, tch, counter);
				bFlg = FALSE;
			}
		}

		tmpFile.Close();
		//запускаем блокнот с этим файлом в качестве параметра
		STARTUPINFO si;
		PROCESS_INFORMATION pi;
		ZeroMemory(&si, sizeof(si));
		si.cb = sizeof(si);
		ZeroMemory(&pi, sizeof(pi));
		/*
		*если запускать текстовый файл через explorer, т.е. пытаться открыть прогой,
		которая сассоциирована с *.txt, то процесс explorer успешно завершается передачей файла
		например блокноту, и прежде чем блокнот запустится, файл удаляется.
		Можно не удалять файлы во временной папке, но тогда перед повторным просмотром
		надо искать файл и удалять его, если он там есть, в общем всячески усложнять алгоритм
		*/
		CString strCmdLine = _T("notepad \"") + strFName + _T("\"");

		if (CreateProcess(NULL,           // No module name (use command line)
		                  strCmdLine.GetBuffer(), // Command line
		                  NULL,           // Process handle not inheritable
		                  NULL,           // Thread handle not inheritable
		                  FALSE,          // Set handle inheritance to FALSE
		                  0,              // No creation flags
		                  NULL,           // Use parent's environment block
		                  NULL,           // Use parent's starting directory
		                  &si,            // Pointer to STARTUPINFO structure
		                  &pi))           // Pointer to PROCESS_INFORMATION structure
		{
			// Wait until child process exits.
			WaitForSingleObject(pi.hProcess, INFINITE);
			// Close process and thread handles.
			CloseHandle(pi.hProcess);
			CloseHandle(pi.hThread);
		}

		//и удалим временный файл
		CFile::Remove(strFName);
	}
	else
	{
		m_nLastErrorNumber = IMAGE_ERROR_FILE_CANNOT_CREATE;
		bRet = FALSE;
	}

	/*
	идея такая: создаём временный файл и туда сохраняем файл с преобразованием,
	из .vxt, .edp с конвертированием и преобразованием текста,
	для всех остальных - просто преобразование из кои8 в юникод
	в результате, бинарные файлы естественно портятся, так что этот метод не подходит
	для сохранения. только для просмотра содержимого.
	*/
	return bRet;
}

BOOL CBKDImage::ViewFileAsSprite(FileRecord *sFile)
{
	m_nLastErrorNumber = IMAGE_ERROR_OK;
	BOOL bRet = TRUE;

	//если это не файл - выходим ничего не делая
	if (sFile->nAttr & (FR_ATTR_DIR | FR_ATTR_LOGDISK))
	{
		return bRet;
	}

	int len = sFile->nSize;

	if (len > 0)
	{
		BYTE *pBuffer = new BYTE[len];

		if (pBuffer)
		{
			int l = len;
			int t = 0;
			SeekToBeginFile(&m_ImgFile, sFile);

			while (l > 0)
			{
				pBuffer[t++] = GetByte(&m_ImgFile, &l);
			}

			CSprWnd *m_pSprWnd = new CSprWnd(pBuffer, len); //обязательно создавать динамически.

			if (m_pSprWnd)
			{
				WNDCLASS wc;
				memset(&wc, 0, sizeof(wc));
				wc.style = CS_HREDRAW | CS_VREDRAW;
				wc.hInstance = AfxGetInstanceHandle();
				wc.lpfnWndProc = ::DefWindowProc;
				wc.lpszClassName = _T("MyClass");
				wc.hbrBackground = (HBRUSH)::GetStockObject(GRAY_BRUSH);
				AfxRegisterClass(&wc);

				if (m_pSprWnd->Create(
				            _T("MyClass"),           //Имя класса виндовс
				            _T("SpriteView: ") + sFile->strName,   //имя окна
				            WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME, // window styles
				            CRect(0, 0, 200, 200),   //размер окна
				            theApp.GetMainWnd(),     //родитель окна
				            0,
				            WS_EX_TOOLWINDOW /*| WS_EX_OVERLAPPEDWINDOW*/ //extended window styles
				        ))
				{
					m_pSprWnd->ShowWindow(SW_SHOW);
				}

				//Объект pBuffer удаляется внутри CSprWnd. Потому что после создания окна, оно начинает жить своей жизнью,
				//и всё это время пользуется pBuffer.
			}
			else
			{
				delete [] pBuffer;
			}
		}
	}

	return bRet;
}

BOOL CBKDImage::GetStartFileName(FileRecord *sFile, int nDirNum)
{
	m_sDiskCat.vecPC.push_back(m_sDiskCat.nVectorPos); //сохраним предыдущую позицию
	m_sDiskCat.nVectorPos = 0; //инициализация - именно поэтому надо сперва эту функцию вызывать, а потом другую.
	//если это не корень, надо вывести ..
	//в корне тоже будем выводить .. - оно будет вызывать закрытие текущего образа.
	sFile->clear();
	sFile->strName = _T("..");
	sFile->nAttr = FR_ATTR_DIR;
	sFile->nRecType = RECORD_TYPE_UP;
	sFile->nDirNum = nDirNum;
	return TRUE;
}

BOOL CBKDImage::GetNextFileName(FileRecord *sFile, int nDirNum)
{
	sFile->clear();
	vector<FileRecord>::size_type size = m_sDiskCat.vecFC.size();

	//и выводим всё что найдём. но по одному.
	while (m_sDiskCat.nVectorPos < size)
	{
		if (m_sDiskCat.vecFC[m_sDiskCat.nVectorPos].nDirNum == nDirNum)
		{
			//нашли нашу директорию
			*sFile = m_sDiskCat.vecFC[m_sDiskCat.nVectorPos];
			m_sDiskCat.nVectorPos++;
			return TRUE;
		}

		m_sDiskCat.nVectorPos++;
	}

	if (!m_sDiskCat.vecPC.empty())
	{
		m_sDiskCat.nVectorPos = m_sDiskCat.vecPC.back();
		m_sDiskCat.vecPC.pop_back();
	}

	return FALSE;
}

void CBKDImage::SaveCurrPos(CFile *f, int len)
{
	m_tPos = f->GetPosition();
	m_tLen = len;
}

int CBKDImage::SeekToPos(CFile *f, int pos)
{
	if (m_tPos + pos != f->Seek(m_tPos + pos, CFile::begin))
	{
		m_nLastErrorNumber = IMAGE_ERROR_CANNOT_SEEK_FILE;
	}

	return m_tLen - pos;
}

BOOL CBKDImage::SeekToBeginFile(CFile *f, FileRecord *fr)
{
	BOOL bRet = TRUE;
	ULONGLONG t = m_sPImg.nBaseOffset + fr->nStartSector * 512;

	if (t != f->Seek(t, CFile::begin))
	{
		m_nLastErrorNumber = IMAGE_ERROR_CANNOT_SEEK_FILE;
		bRet = FALSE;
	}

	return bRet;
}

BYTE CBKDImage::GetByte(CFile *f, int *len)
{
	BYTE b;

	if (sizeof(b) != f->Read(&b, sizeof(b)))
	{
		m_nLastErrorNumber = IMAGE_ERROR_CANNOT_READ_FILE;
	}

	*len -= sizeof(b);
	return b;
}

WORD CBKDImage::GetWord(CFile *f, int *len)
{
	WORD w;

	if (sizeof(w) != f->Read(&w, sizeof(w)))
	{
		m_nLastErrorNumber = IMAGE_ERROR_CANNOT_READ_FILE;
	}

	*len -= sizeof(w);
	return w;
}

void CBKDImage::PutTchar(CFile *f, TCHAR tch, int &counter)
{
	f->Write(&tch, sizeof(tch));
	counter++;
}

FILESAVE_STATUS CBKDImage::AddFile(const CString &str, int nDirNum, BOOL bAddAsBin)
{
	return FS_IO_ERROR;
}

/*
анализ импортируемого файла, проверка bin это или нет
вход - поля в структуре AnalyseFileStr:
    file - указатель на открытый ранее файл
    bUseAsBin - флаг, что добавляем как бин
выход - поля в структуре AnalyseFileStr:
    strName - имя файла
    strExt - расширение файла
    nAddr - адрес загрузки
    nLen - длина файла
*/
BOOL CBKDImage::AnalyseImportFile(AnalyseFileStr *pAFS)
{
	BOOL bRet = TRUE;
	m_nLastErrorNumber = IMAGE_ERROR_OK;
	pAFS->nLen = (int)pAFS->file.GetLength();
	pAFS->strName = pAFS->file.GetFileName(); //выдаёт имя файла с расширением без пути
	pAFS->strExt = _T("");
	int l = pAFS->strName.GetLength();
	int t = pAFS->strName.ReverseFind(_T('.'));

	if (t >= 0) //если расширение есть
	{
		pAFS->strExt = pAFS->strName.Right(l - t - 1);
		pAFS->strName = pAFS->strName.Left(t);
	}

	int nOffset = 0;

	//проверим, а не бин формат ли подсунули?
	if (sizeof(m_mBinHeader) == pAFS->file.Read(m_mBinHeader, sizeof(m_mBinHeader)))
	{
		//если формат действительно бин, то nOffset будет != 0,
		//но есть конечно шанс случайного совпадения
		if (m_mBinHeader[1] == pAFS->nLen - 4)
		{
			nOffset = 4;
		}
		else if (m_mBinHeader[1] == pAFS->nLen - 22)
		{
			nOffset = 20;
		}

		//если расширение бин, или стоит флажок "обрабатывать как бин" и действительно бин
		if (((pAFS->strExt.MakeUpper() == _T("BIN")) || pAFS->bUseAsBin) && nOffset != 0)
		{
			//да, бин однако
			pAFS->nAddr = m_mBinHeader[0];
			pAFS->nLen = m_mBinHeader[1];
			//тут можно ещё попробовать убрать расширение .bin
			t = pAFS->strName.ReverseFind(_T('.')); //ищем, может ещё есть расширения

			if (t >= 0)
			{
				l = pAFS->strName.GetLength();
				pAFS->strExt = pAFS->strName.Right(l - t - 1);
				pAFS->strName = pAFS->strName.Left(t);
			}
		}
		else
		{
			//нет, не бин
			pAFS->nAddr = 01000; //по умолчанию, зададим вот такой адрес
		}

		if (nOffset != pAFS->file.Seek(nOffset, CFile::begin))
		{
			m_nLastErrorNumber = IMAGE_ERROR_CANNOT_SEEK_FILE;
			bRet = FALSE;
		}
	}
	else
	{
		m_nLastErrorNumber = IMAGE_ERROR_CANNOT_READ_FILE;
		bRet = FALSE;
	}

	return bRet;
}

/*
анализ экспортируемого файла
вход - поля в структуре AnalyseFileStr:
    file - указатель на открытый ранее файл
    bUseAsBin - флаг, что добавляем как бин
    strName - имя файла
    nAddr - адрес загрузки
    nLen - длина файла
чтобы не писать отдельные функции для каждой ОС, анализировать будем уже извлечённый файл.
выход: TRUE - успешно
       FALSE - ошибка
*/
BOOL CBKDImage::AnalyseExportFile(AnalyseFileStr *pAFS)
{
	BOOL bRet = TRUE;
	m_nLastErrorNumber = IMAGE_ERROR_OK;
	int nStartAddr = pAFS->nAddr; //в идеале вот так, адрес запуска == адресу загрузки
	struct autorun
	{
		WORD nWord770;
		WORD nWord772;
		WORD nWord774;
	};
	autorun sAuto = { 0 };

	if (0300 <= pAFS->nAddr && pAFS->nAddr < 01000)  //если есть блок автозапуска, или что-то похожее
	{
		if ((pAFS->nAddr <= 0770) && (pAFS->nAddr + pAFS->nLen >= 0776)) //если файл перекрывает область, откуда брать адрес запуска
			//ведь это может быть просто кусок с адресом загрузки 0400 и длиной 0200
		{
			int nOffset = 0770 - pAFS->nAddr; //адрес запуска будем брать из ячейки 0774 (хотя возможно надо из другой)

			//если содержимое ячеек 770, 772 и 774 равно между собой
			if (pAFS->nAddr & 1)
			{
				nOffset++;    //на всякий случай, если адрес загрузки нечётный, скорректируем
			}

			if (pAFS->bUseAsBin)
			{
				nOffset += 4;
			}

			if (nOffset == pAFS->file.Seek(nOffset, CFile::begin))
			{
				if (sizeof(sAuto) == pAFS->file.Read(&sAuto, sizeof(sAuto)))
				{
					if ((sAuto.nWord770 == sAuto.nWord772) && (sAuto.nWord770 == sAuto.nWord774))
					{
						nStartAddr = (int)sAuto.nWord774;
					}
				}
				else
				{
					m_nLastErrorNumber = IMAGE_ERROR_CANNOT_READ_FILE;
					bRet = FALSE;
				}
			}
			else
			{
				m_nLastErrorNumber = IMAGE_ERROR_CANNOT_SEEK_FILE;
				bRet = FALSE;
			}
		}
	}

	if (pAFS->nAddr < 0100)
	{
		nStartAddr = 0;
	}

	FILE *f = _tfopen(m_strStorePath + _T("extractlog.txt"), _T("at"));

	if (f)
	{
		_ftprintf(f, _T("%-20s\tload:%06o\tlen:%06o\tstart:%06o\n"), pAFS->strName, pAFS->nAddr, pAFS->nLen, nStartAddr);
		fclose(f);
	}

	return bRet;
}

