#include "stdafx.h"

using namespace std;

#include "BK_Util.h"


/*
преобразование имени файла из кои8 в юникод. с удалением запрещённых символов
*/
wstring BKToUNICODE(uint8_t *pBuff, int size, const wchar_t table[])
{
	wstring strRet = L"";

	for (int i = 0; i < size; ++i)
	{
		wchar_t tch = BKToUNICODE_Byte(pBuff[i], table);

		if (tch == 0)
		{
			break;
		}

		strRet.push_back(tch);
	}

	return strRet;
}

wchar_t BKToUNICODE_Byte(uint8_t b, const wchar_t table[])
{
	if (b == 0)
	{
		return 0;
	}

	if (b < 32)
	{
		return L' ';
	}
	else if (b >= 32 && b < 127)
	{
		return wchar_t(b);
	}
	else if (b == 127)
	{
		return wchar_t(0x25a0);
	}
	else if (b >= 128)
	{
		return table[b - 128];
	}

	return 0; //этот return не должен выполниться в принципе.
}


/*
преобразование юникодной строки в бкшный кои8.
вход:
ustr - преобразуемая строка
pBuff - буфер, куда выводится результат
bufSize - размер буфера
bFillBuf - флаг. если строка короче размера буфера, буфер до конца забивается пробелами. (конца строки - 0 нету.)
*/
void UNICODEtoBK(const wstring &ustr, uint8_t *pBuff, size_t bufSize, bool bFillBuf)
{
	uint8_t b;
	size_t bn = 0;
	size_t len = ustr.length();

	for (size_t n = 0; n < len; ++n)
	{
		wchar_t ch = ustr[n]; //берём очередной символ

		if (ch < 32) //если символ меньше пробела,
		{
			b = 32;    //то будет пробел
		}
		else if ((32 <= ch) && (ch < 127)) //если буквы-цифры- знаки препинания
		{
			b = LOBYTE(ch); // то буквы-цифры- знаки препинания
		}
		else if (ch == 0x25a0) //если такое
		{
			b = 127; //то это. это единственное исключение в нижней половине аски кодов
		}
		else //если всякие другие символы
		{
			//то ищем в таблице нужный нам символ, а его номер - будет кодом кои8
			b = 32; //если такого символа нету в таблице - будет пробел

			for (uint8_t i = 0; i < 128; ++i)
			{
				if (ch == koi8tbl10[i])
				{
					b = i + 0200;
					break;
				}
			}
		}

		pBuff[bn++] = b;

		if (bn >= bufSize)
		{
			break;
		}
	}

	if (bFillBuf)
	{
		while (bn < bufSize)
		{
			pBuff[bn++] = 32;
		}
	}
}

//то же что и для дир + меняем страшные символы '\' и '/' на нестрашную '.'
void SetSafeName(wstring &str)
{
	SetSafeDir(str);
	replaceChars(&str, wstring(L"\\/"), L'.');
}

//меняем страшные символы '>','<',':','|','?','*' на нестрашную '_'
//и все коды меньше пробела - на '_'
void SetSafeDir(wstring &str)
{
	wchar_t rch = L'_';
	size_t l = str.length();

	if (l)
	{
		replaceChars(&str, wstring(L"><:|?*"), rch);

		//а теперь заменим все символы с кодом меньше 32
		for (size_t i = 0; i < l; ++i)
		{
			if (str[i] < 32)
			{
				str[i] = rch;
			}
		}
	}
}

//тупой класс wstring не имеет многих нужных функций.

void trim(wstring *str, wchar_t trim_char)
{
	if (str == nullptr)
	{
		return;
	}

	trimLeft(str, trim_char);
	trimRight(str, trim_char);
}

void trimLeft(wstring *str, wchar_t trim_char)
{
	if (str == nullptr)
	{
		return;
	}

	while (!str->empty() && (str->front() == trim_char))
	{
		str->erase(str->begin());
	}
}

void trimRight(wstring *str, wchar_t trim_char)
{
	if (str == nullptr)
	{
		return;
	}

	while (!str->empty() && (str->back() == trim_char))
	{
		str->pop_back();
	}
}

void replaceChar(wstring *str, wchar_t src, wchar_t dst)
{
	if (str == nullptr)
	{
		return;
	}

	if (!str->empty())
	{
		for (wchar_t &n : *str)
		{
			if (n == src)
			{
				n = dst;
			}
		}
	}
}

void strToUpper(wstring *str)
{
	if (str == nullptr)
	{
		return;
	}

	if (!str->empty())
	{
		_locale_t loc = _create_locale(LC_ALL, "Russian");

		for (wchar_t &n : *str)
		{
			n = _toupper_l(n, loc);
		}

		_free_locale(loc);
	}
}

void strToLower(wstring *str)
{
	if (str == nullptr)
	{
		return;
	}

	if (!str->empty())
	{
		_locale_t loc = _create_locale(LC_ALL, "Russian");

		for (wchar_t &n : *str)
		{
			n = _tolower_l(n, loc);
		}

		_free_locale(loc);
	}
}


void replaceChars(wstring *str, wstring &src, wchar_t dst)
{
	if (str == nullptr)
	{
		return;
	}

	size_t l = src.length();

	if (!str->empty() && !src.empty())
	{
		for (wchar_t &n : *str)
		{
			for (size_t i = 0; i < l; ++i)
			{
				if (n == src[i])
				{
					n = dst;
					break;
				}
			}
		}
	}
}


//сокращение строки до заданной длины.
//Вход: str - строка
// len - желаемая длина
// Выход: сформированая строка
wstring CropStr(wstring &str, size_t len)
{
	trim(&str);
	size_t strlen = str.length();

	if (strlen >= len)
	{
		wstring res = str.substr(0, len - 1);
		res += str[strlen - 1];
		return res;
	}
	else
	{
		return str;
	}
}


/*
анализ импортируемого файла, проверка bin это или нет
вход:
a - указатель на структуру данных, которые корректируются в процессе анализа
выход: поля в структуре AnalyseFileStr:
file - открытый файл
strName - имя файла
strExt - расширение файла
nAddr - адрес загрузки
nLen - длина файла

true - данные скорректированы
false - нет, можно игнорировать
*/
bool AnalyseImportFile(AnalyseFileStruct *a)
{
	bool bRet = false;
	uint16_t mBinHeader[2];
	int nOffset = 0;

	//проверим, а не бин формат ли подсунули?
	if (sizeof(mBinHeader) == fread(mBinHeader, 1, sizeof(mBinHeader), a->file))
	{
		a->bIsCRC = false;

		//если формат действительно бин, то nOffset будет != 0,
		//но есть конечно шанс случайного совпадения
		if (mBinHeader[1] == a->nLen - 4)
		{
			nOffset = 4;
		}
		else if (mBinHeader[1] == a->nLen - 6)
		{
			a->bIsCRC = true;
			nOffset = 4;
		}
		else if (mBinHeader[1] == a->nLen - 22)
		{
			a->bIsCRC = true;
			nOffset = 20;
		}

		//если действительно бин
		if (nOffset)
		{
			//да, бин однако
			a->nAddr = mBinHeader[0];
			a->nLen = mBinHeader[1];

			if (nOffset == 20) //прочитаем оригинальное имя
			{
				fread(a->OrigName, 1, 16, a->file);
			}

			if (a->bIsCRC)
			{
				fseek(a->file, 2, SEEK_END);
				fread(&a->nCRC, 1, 2, a->file);
			}
			//тут можно ещё попробовать убрать расширение .bin
			wstring ext = a->strExt;
			strToLower(&ext);

			if (ext == L".bin")
			{
				size_t t = a->strName.rfind(L'.'); //ищем, может ещё есть расширения

				if (t != wstring::npos) //если расширение есть
				{
					size_t l = a->strName.length();
					a->strExt = a->strName.substr(t, l);
					a->strName = a->strName.substr(0, t);
				}
				else //если расширения нету, то просто убираем бин
				{
					a->strExt.clear();
				}

				trim(&a->strName);
			}

			bRet = true;
		}
	}

	fseek(a->file, nOffset, SEEK_SET); //пропустить заголовок
	return bRet;
}

uint16_t CalcCRC(uint8_t *buffer, size_t len)
{
	int crc = 0;

	for (size_t i = 0; i < len; ++i)
	{
		crc += buffer[i];

		if (crc & 0xFFFF0000)
		{
			//если случился перенос в 17 разряд (т.е. бит С для word)
			crc &= 0x0000FFFF; //его обнулим
			crc++; //но прибавим к сумме
		}
	}

	return LOWORD(crc);
}

