/*
	! file encoding: Windows-1251


------------------------------------------

	Самый быстрый INI ридер/райтер ( mxINI )

------------------


	ЧТО ЭТО
		- Это инклуд-файл для PAWN скриптов мультиплеера GTA San Andreas (SA-MP)

	КАК ИСПОЛЬЗОВАТЬ
		- Поместить этот файл в папку include, которая лежит рядом с компилятором
		- В самом верху вашего скрипта добавить #include <mxINI.inc>

	АВТОР
		MX_Master

	ВЕРСИЯ
		0.5 от 20.09.2010


	ДОСТУПНЫЕ ИНСТРУМЕНТЫ
	более подробные описания даны перед каждой функцией ниже в коде

		ini_createFile ( "путь/к/файлу.ini", "Содержимое файла по умолчанию" )

		ini_openFile ( "путь/к/файлу.ini" )

		ini_closeFile ( ИД_открытого_файла )
	--
		ini_setString ( ИД_открытого_файла, "имя ключа", "текстовое значение" )

		ini_setInteger ( ИД_открытого_файла, "имя ключа", 123456 )

		ini_setFloat ( ИД_открытого_файла, "имя ключа", 3.1416 )
	--
		ini_getString ( ИД_открытого_файла, "имя ключа", returnValue )

		ini_getInteger ( ИД_открытого_файла, "имя ключа", returnValue )

		ini_getFloat ( ИД_открытого_файла, "имя ключа", returnValue )
	--
		ini_removeKey ( ИД_открытого_файла, "имя ключа" )

		ini_getErrorInfo ( Код_ошибки )


	ПРИМЕРЫ

		new iniFile = ini_createFile ( "test4268.ini" );

		if ( iniFile < 0 )
			iniFile = ini_openFile ( "test4268.ini" );

		if ( iniFile >= 0 )
		{
			new returnString[32], returnNumber, Float: returnFloat;

			ini_setString  ( iniFile, "ключ со строкой", "текстовое значение" );
			ini_setInteger ( iniFile, "ключ с числом",   123456 );
			ini_setFloat   ( iniFile, "ключ с дробью",   3.1416 );

			ini_removeKey  ( iniFile, "ключ с числом" );

			ini_getString  ( iniFile, "ключ со строкой", returnString );
			ini_getInteger ( iniFile, "ключ с числом",   returnNumber );
			ini_getFloat   ( iniFile, "ключ с дробью",   returnFloat );


			ini_closeFile  ( iniFile );

			printf ( "\n `ключ_со_строкой` = `%s`,\n `ключ_с_числом` = `%d`,\n `ключ_с_дробью` = `%f` \n",
				returnString, returnNumber, returnFloat );
		}
		else print( "\n Не удалось открыть INI файл \n" );
*/











const

// настройки

// РЕКОМЕНДУЕТСЯ ИЗМЕНИТЬ ПОД СЕБЯ
INI_MAX_OPENED_FILES    = 2,        // максимум, открытых одновременно, файлов
INI_MAX_FILE_SIZE       = 65536,    // байт,        макс. размер файла
INI_MAX_FILENAME_SIZE   = 128,      // символов,    макс. размер пути к файлу
INI_MAX_KEYS_IN_FILE    = 2048,     // максимум ключей в открытом файле

// в ОЗУ будет выделено для временного хранения файлов примерно вот столько байт:
// INI_MAX_OPENED_FILES * ( INI_MAX_FILE_SIZE + INI_MAX_FILENAME_SIZE*4 + INI_MAX_KEYS_IN_FILE*4 )


// НЕЛЬЗЯ МЕНЯТЬ
INI_INTEGER_SIZE        = 12,       // размер строки с целочисленным значением
INI_FLOAT_SIZE          = 40,       // размер строки с дробным числовым значением

INI_STRING_DELIMITER    = '\n',     // разделитель строк
INI_DELIMITER           = '=',      // разделитель ключа и значения


// коды ошибок, возвращаемые функциями

// РЕКОМЕНДУЕТСЯ НЕ ИЗМЕНЯТЬ
INI_OK                  =  0,       // функция успешно выполнена

// проверять на ошибку можно так:
// if ( возвращаемое_значение_функции < 0 ) ...

INI_FILE_NOT_FOUND      = -1,       // файл не найден по указанному пути
INI_FILE_ALREADY_EXIST  = -2,       // файл не найден по указанному пути
INI_TOO_LARGE_FILE      = -3,       // размер файла превысил допустимый лимит
INI_WRONG_PATH_SIZE     = -4,       // неправильный размер пути к файлу
INI_READ_ERROR          = -5,       // ошибка чтения файла
INI_WRITE_ERROR         = -6,       // ошибка при записи в файл
INI_NO_FREE_SLOT        = -7,       // нет свободного слота для открытия файла
INI_WRONG_SLOT          = -8,       // указан неверный слот открытого файла
INI_KEY_NOT_FOUND       = -9,       // ключ в открытом файле не найден
INI_WRONG_RETURN_SIZE   = -10,      // размер строки, в которую будет помещено значение ключа - указан неверно (<= 0)


// другие вспомогательные константы

// НЕЛЬЗЯ МЕНЯТЬ
cellbytes = cellbits / charbits;    // кол-во байт в одной ячейке





// списки разных символов для оператора case в одной из функций

// РЕКОМЕНДУЕТСЯ НЕ ИЗМЕНЯТЬ
#define INI_SPACE_CHAR  ' ', '\t'                       // строковые пробельные символы
#define INI_KEY_STARTS  ' ', '\t', '\r', '\n', '\0'     // символы, перед началом ключа
#define INI_STRING_ENDS '\r', '\n', '\0'                // символы, завершающие значение
#define INI_NUMBER_ENDS ' ', '\t', '\r', '\n', '\0'     // символы, завершающие численное значение





static stock

// временное хранилище открытых файлов

_ini_nSlotUsed      [ INI_MAX_OPENED_FILES ],                               // флаг: занят ли слот
_ini_nFileChanged   [ INI_MAX_OPENED_FILES ],                               // флаг: был ли изменен файл
_ini_nFileBusy      [ INI_MAX_OPENED_FILES ],                               // флаг: изменяется ли в данный момент содержимое файла
_ini_nFileSize      [ INI_MAX_OPENED_FILES ],                               // размер открытого файла
_ini_nDelimPos      [ INI_MAX_OPENED_FILES ] [ INI_MAX_KEYS_IN_FILE ],      // список позиций INI_DELIMITER
_ini_nKeysCount     [ INI_MAX_OPENED_FILES ],                               // кол-во ключей открытого файла
_ini_szFilePath     [ INI_MAX_OPENED_FILES ] [ INI_MAX_FILENAME_SIZE ],     // путь к файлу
_ini_szFileContent  [ INI_MAX_OPENED_FILES ] [ INI_MAX_FILE_SIZE char ];    // контент файла





















/*
	Создает и сразу открывает INI файл для чтения/записи.

	ПОДРОБНЕЕ
		Файл создается только в ОЗУ, и в него записывается строка szDefaultContent.
		В szDefaultContent могут быть и ключи, которые потом парсер также будет видеть.
		Только при закрытии файла, его содержимое будет записано на диск
		по указанному пути к файлу.

	ПАРАМЕТРЫ:
		szFilePath[]        путь к файлу
		szDefaultContent    контент файла по умолчанию, обычно, можно не указывать

	ВЕРНЕТ:
		код ошибки < 0 или ИД_открытого_файла
*/

stock ini_createFile ( szFilePath[], szDefaultContent[] = "" )
{
	//
	//  несколько блоков с проверками
	//

	// ------------
	new nFileNameSize = strlen( szFilePath ); // узнаем размер пути

	// если размер пути неправильный
	if ( nFileNameSize <= 0 || nFileNameSize >= INI_MAX_FILENAME_SIZE )
	return INI_WRONG_PATH_SIZE; // вернем код ошибки
	// ------------


	// ------------
	if ( fexist( szFilePath ) ) // если файл уже существует
	return INI_FILE_ALREADY_EXIST; // вернем код ошибки - файл уже существует

	for ( new slot = 0; slot < INI_MAX_OPENED_FILES; slot++ ) // перебор всех слотов ОЗУ
	if // если уже есть такой открытый файл
	(
	_ini_nSlotUsed[slot] != 0
	&&  strcmp( szFilePath, _ini_szFilePath[slot], false ) == 0
	)
	return INI_FILE_ALREADY_EXIST; // вернем код ошибки - файл уже существует
	// ------------



	// ------------
	new nFileSize = strlen( szDefaultContent ); // узнаем размер контентa файла по умолчанию

	// если размер неправильный
	if ( nFileSize < 0 || nFileSize >= INI_MAX_FILE_SIZE )
	return INI_TOO_LARGE_FILE; // вернем код ошибки
	// ------------




	//
	//  поиск свободного слота для записи в ОЗУ
	//

	for ( new slot = 0; slot < INI_MAX_OPENED_FILES; slot++ ) // перебор всех слотов ОЗУ
	{
		if ( _ini_nSlotUsed[slot] != 1 ) // если слот найден
		{
			// ------------
			_ini_nSlotUsed[slot]        = 1; // застолбим найденное место
			_ini_nFileChanged[slot]     = 0; // файл не был изменен
			_ini_nFileSize[slot]        = nFileSize; // скопируем размер файла в слот
			_ini_nKeysCount[slot]       = 0; // кол-во ключей выставим 0
			// ------------


			// ------------
			// скопируем в ОЗУ весь файл,
			// запомнив все позиции INI_DELIMITER и их кол-во
			for ( new i = 0;  i < nFileSize && i < INI_MAX_FILE_SIZE;  i++ )
			{
				if (
						szDefaultContent[i] == INI_DELIMITER // если символ это INI_DELIMITER
						&&  _ini_nKeysCount[slot] < INI_MAX_KEYS_IN_FILE // и лимит ключей еще не исчерпан
						) {
					_ini_nDelimPos[slot][ _ini_nKeysCount[slot] ] = i; // добавим еще одну позицию INI_DELIMITER
					_ini_nKeysCount[slot]++; // увеличим кол-во найденных ключей
				}

				_ini_szFileContent[slot]{i} = szDefaultContent[i];
			}

			_ini_szFileContent[slot]{nFileSize} = 0; // символ конца строки для контента
			// ------------


			// ------------
			// скопируем в озу путь к файлу
			memcpy( _ini_szFilePath[slot], szFilePath, 0, nFileNameSize * cellbytes );

			_ini_szFilePath[slot][nFileNameSize] = 0; // символ конца строки для пути
			// ------------


			return slot; // вернем ИД слота
		}
	}




	//
	//  если свободный слот в памяти не найден
	//

	return INI_NO_FREE_SLOT; // вернем код ошибки
	// ------------
}




/*
	Открывает INI файл для чтения/записи, если он существует.

	ПОДРОБНЕЕ
		Копирует в ОЗУ с диска всё содержимое файла, если его размер не превышает
		допустимый. Если кол-во ключей в файле больше допустимого, ошибки никакой не будет,
		просто при чтении/записи значений, парсер не будет видеть лишние ключи.

	ПАРАМЕТРЫ:
		szFilePath[]    путь к файлу

	ВЕРНЕТ:
		код ошибки < 0 или ИД_открытого_файла
*/

stock ini_openFile ( szFilePath[] )
{
	//
	//  несколько блоков с проверками
	//

	// ------------
	new nFileNameSize = strlen( szFilePath ); // узнаем размер пути

	// если размер пути неправильный
	if ( nFileNameSize <= 0 || nFileNameSize >= INI_MAX_FILENAME_SIZE )
	return INI_WRONG_PATH_SIZE; // вернем код ошибки
	// ------------


	if ( ! fexist( szFilePath ) ) // если файл не найден
	return INI_FILE_NOT_FOUND; // вернем код ошибки


	// ------------
	// проверка - открыт ли уже файл с таким именем
	for ( new slot = 0; slot < INI_MAX_OPENED_FILES; slot++ ) // перебор всех слотов ОЗУ
	if // если уже есть такой открытый файл
	(
	_ini_nSlotUsed[slot] != 0
	&&  strcmp( szFilePath, _ini_szFilePath[slot], false ) == 0
	)
	return slot; // просто вернем его слот
	// ------------


	// ------------
	new File: pFile = fopen( szFilePath, io_read ); // пытаемся открыть файл для чтения

	if ( ! pFile ) // если файл не открылся
	return INI_READ_ERROR; // вернем код ошибки
	// ------------


	// ------------
	new nFileSize = flength( pFile ); // узнаем размер файла

	if ( nFileSize >= INI_MAX_FILE_SIZE ) // если размер файла слишком большой
	{
		fclose(pFile); // закроем файл
		return INI_TOO_LARGE_FILE; // вернем код ошибки
	}
	// ------------




	//
	//  поиск свободного слота для записи в ОЗУ
	//

	// ------------
	for ( new slot = 0; slot < INI_MAX_OPENED_FILES; slot++ ) // перебор всех слотов ОЗУ
	{
		if ( _ini_nSlotUsed[slot] != 1 ) // если слот найден
		{
			// ------------
			_ini_nSlotUsed[slot]        = 1; // застолбим найденное место
			_ini_nFileChanged[slot]     = 0; // файл не был изменен
			_ini_nFileSize[slot]        = nFileSize; // скопируем размер файла в слот
			_ini_nKeysCount[slot]       = 0; // кол-во ключей выставим 0
			// ------------


			// ------------
			// скопируем в ОЗУ весь файл,
			// запомнив все позиции INI_DELIMITER и их кол-во
			for ( new i = 0, symbol; i < nFileSize && i < INI_MAX_FILE_SIZE; i++ )
			{
				symbol = fgetchar( pFile, 0, false ); // читаем из файла следующий символ

				if (
						symbol == INI_DELIMITER // если символ это INI_DELIMITER
						&&  _ini_nKeysCount[slot] < INI_MAX_KEYS_IN_FILE // и лимит ключей еще не исчерпан
						) {
					_ini_nDelimPos[slot][ _ini_nKeysCount[slot] ] = i; // добавим еще одну позицию INI_DELIMITER
					_ini_nKeysCount[slot]++; // увеличим кол-во найденных ключей
				}

				_ini_szFileContent[slot]{i} = symbol;
			}

			_ini_szFileContent[slot]{nFileSize} = 0; // символ конца строки для контента

			fclose(pFile); // закроем файл
			// ------------


			// ------------
			// скопируем в озу путь к файлу
			memcpy( _ini_szFilePath[slot], szFilePath, 0, nFileNameSize * cellbytes );

			_ini_szFilePath[slot][nFileNameSize] = 0; // символ конца строки для пути
			// ------------


			return slot; // вернем ИД слота
		}
	}
	// ------------




	//
	//  если свободный слот в памяти не найден
	//

	// ------------
	fclose(pFile); // закроем файл

	return INI_NO_FREE_SLOT; // вернем код ошибки
	// ------------
}




/*
	Закрывает INI файл, если он был открыт.

	ПОДРОБНЕЕ
		Если файл не был изменен - освободит слот для хранения файла в ОЗУ.
		Если файл был изменен - полностью перезапишет файл на диске.

	ПАРАМЕТРЫ:
		nFilePointer    ИД_открытого_файла, полученный от ini_openFile / ini_createFile

	ВЕРНЕТ:
		код ошибки < 0 или 0 при успехе
*/

stock ini_closeFile ( nFilePointer )
{
	if
	(   // если ИД открытого файла указан верно
	nFilePointer >= 0
	&&  nFilePointer < INI_MAX_OPENED_FILES
	&&  _ini_nSlotUsed[nFilePointer] != 0
	)
	{
		if ( _ini_nFileChanged[nFilePointer] != 0 ) // если файл был изменен
		{
			new File: pFile = fopen( _ini_szFilePath[nFilePointer], io_write ); // пытаемся открыть файл для записи

			if ( ! pFile ) // если файл не открылся
			return INI_WRITE_ERROR; // вернем код ошибки

			// запишем контент файла из ОЗУ на диск
			for ( new i = 0;  i < _ini_nFileSize[nFilePointer];  i++ )
			fputchar( pFile, _ini_szFileContent[nFilePointer]{i}, false );

			fclose(pFile); // закроем файл
		}

		_ini_nSlotUsed[nFilePointer] = 0; // освободить слот открытого файла

		return INI_OK; // вернуть код об успешном выполнении функции
	}

	return INI_WRONG_SLOT; // вернуть код: неверный указатель на открытый файл
}











/*
	Получает из открытого INI файла значение указанного ключа.

	ПОДРОБНЕЕ
		Парсер ищет в ОЗУ в контенте файла указанный ключ и помещает в
		szReturnValue его строковое значение. szReturnValue нужно создать заранее.

		nSizeOfReturn можно не указывать, если только не нужно точное кол-во
		возвращаемых символов в строке (в описании этого параметра ниже даны
		дополнительные указания).

		Как ключ так и значение в файле, могут быть любой длины и
		могут содержать любые символы, кроме 2 символов конца строки \r и \n.

		Парсер не видит пробелы и знаки табуляции вокруг имени ключа и
		перед значением, он считает их отступами.

	ПАРАМЕТРЫ:
		nFilePointer        ИД_открытого_файла, полученный от ini_openFile / ini_createFile
		szKeyName[]         имя ключа
		szReturnValue[]     сюда будет помещено значение ключа в виде строки
		nSizeOfReturn       макс. размер возвращаемой строки, обычно, это размер szReturnValue.
							если ваша строка szReturnValue является частью массива, который был создан с помощью enum,
							этот параметр нужно обязательно указывать как число

	ВЕРНЕТ:
		код ошибки < 0 или 0 при успехе
*/

stock ini_getString ( nFilePointer, szKeyName[], szReturnValue[], nSizeOfReturn = sizeof(szReturnValue) )
{
	// ----------------
	if // если ИД открытого файла указан неверно
	(
	nFilePointer < 0
	||  nFilePointer >= INI_MAX_OPENED_FILES
	||  _ini_nSlotUsed[nFilePointer] != 1
	)
	return INI_WRONG_SLOT; // вернуть код: неверный указатель на открытый файл
	// ----------------


	if ( nSizeOfReturn <= 0 ) // если по какой-то причине размер возвращаемого значения указан/рассчитан как 0
	return INI_WRONG_RETURN_SIZE;


	// ----------------
	new nKeyLen = strlen(szKeyName); // узнаем длину имени указанного ключа

	if ( nKeyLen <= 0 ) // если указан пустой ключ
	return INI_KEY_NOT_FOUND;
	// ----------------


	// ----------------
	for // перебор и сравнение всех ключей файла с указанным ключом
	(
	new kPos = 0, curFilePos, found = 0;
	kPos < _ini_nKeysCount[nFilePointer];
	kPos++
	)
	{
		// ----------------
		found = 0; // флаг, найдена ли позиция конца ключа = 0

		for // ищем позицию конца ключа
		(
		curFilePos = _ini_nDelimPos[nFilePointer][kPos] - 1; // текущ. поз. файла = поз. текущ. INI_DELIMITER - 1
		curFilePos >= 0; // продолжать пока поз. файла >= 0
		curFilePos-- // после каждого повтора текущ. поз. файла -= 1
		)
		{
			switch ( _ini_szFileContent[nFilePointer]{curFilePos} ) // узнаем что за символ в текущ. поз. файла
			{
			case INI_SPACE_CHAR :           continue; // если это пробельный символ, перейдем к пред. символу файла
			case INI_STRING_DELIMITER :     break; // если это конец строки
				default : // если это другой символ
				{
					found = 1; // позиция конца ключа найдена
					break; // конец цикла
				}
			}
		}

		// если позиция конца ключа не найдена, переход к след. позиции INI_DELIMITER
		if ( found != 1 ) continue;
		// ----------------


		// сравниваем посимвольно текущий ключ файла и указанный ключ
		for ( new curKeyPos = nKeyLen - 1;  curKeyPos >= 0;  curFilePos--, curKeyPos-- )
		{
			if
			(
			curFilePos < 0 // если поз файла стала < 0
			||  _ini_szFileContent[nFilePointer]{curFilePos} != szKeyName[curKeyPos] // если символы из ключей не равны
			||  _ini_szFileContent[nFilePointer]{curFilePos} == INI_STRING_DELIMITER // если символ из ключа это INI_STRING_DELIMITER
			)
			{
				found = 0; // флаг, ключ не найден
				break; // конец сравнения
			}
		}


		// ----------------
		if ( found != 0 ) // если указанный ключ найден в файле
		{
			// если найдено совпадение не целого ключа файла, а его окончания с указанным ключом
			if ( curFilePos >= 0 )
			{
				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_KEY_STARTS : {}
				default: continue;
				}
			}


			// ----------------
			// текущая позиция в файле будет на 1 больше текущей позиции INI_DELIMITER
			curFilePos = _ini_nDelimPos[nFilePointer][kPos] + 1;

			// ищем позицию начала значения, она будет помещена в curFilePos
			for ( ; ; curFilePos++ )
			{
				if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;

				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_SPACE_CHAR :   continue; // если это пробельный символ, перейдем к след. символу
					default :               break; // если это другой символ
				}
			}
			// ----------------


			// ----------------
			// скопируем посимвольно в szReturnValue значение ключа из файла
			// воспользуемся созданной переменной found как позицией в возвращаемом значении
			for ( found = 0;  found < nSizeOfReturn;  found++, curFilePos++ )
			{
				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_STRING_ENDS : // если это символ конца значения
					{
						szReturnValue[found] = 0; // запишем символ конца строки
						break; // конец копирования
					}
					default :
					// копируем символ из файла в szReturnValue
					szReturnValue[found] = _ini_szFileContent[nFilePointer]{curFilePos};
				}
			}

			szReturnValue[nSizeOfReturn - 1] = 0; // на всякий случай обрежем правильно строку

			return INI_OK;
			// ----------------
		}
		// ----------------
	}
	// ----------------


	// ----------------
	return INI_KEY_NOT_FOUND;
	// ----------------
}




/*
	Получает из открытого INI файла целочисленное значение указанного ключа.

	ПОДРОБНЕЕ
		Парсер ищет в ОЗУ в контенте файла указанный ключ и помещает в
		nReturnValue его целочисленное значение. nReturnValue нужно создать заранее.

		Имя ключа в файле может быть любой длины, а также может содержать
		любые символы, кроме 2 символов конца строки \r и \n.

		Значение может быть только фиксированной длины, которая равна INI_INTEGER_SIZE - 1.
		Парсер не видит пробелы и знаки табуляции вокруг имени ключа/значения,
		он считает их отступами.

	ПАРАМЕТРЫ:
		nFilePointer        ИД_открытого_файла, полученный от ini_openFile / ini_createFile
		szKeyName[]         имя ключа
		nReturnValue        сюда будет помещено значение ключа в виде целого числа

	ВЕРНЕТ:
		код ошибки < 0 или 0 при успехе
*/

stock ini_getInteger ( nFilePointer, szKeyName[], & nReturnValue )
{
	// ----------------
	if // если ИД открытого файла указан неверно
	(
	nFilePointer < 0
	||  nFilePointer >= INI_MAX_OPENED_FILES
	||  _ini_nSlotUsed[nFilePointer] != 1
	)
	return INI_WRONG_SLOT; // вернуть код: неверный указатель на открытый файл
	// ----------------


	// ----------------
	new nKeyLen = strlen(szKeyName); // узнаем длину имени указанного ключа

	if ( nKeyLen <= 0 ) // если указан пустой ключ
	return INI_KEY_NOT_FOUND;
	// ----------------


	// ----------------
	for // перебор и сравнение всех ключей файла с указанным ключом
	(
	new kPos = 0, curFilePos, found = 0;
	kPos < _ini_nKeysCount[nFilePointer];
	kPos++
	)
	{
		// ----------------
		found = 0; // флаг, найдена ли позиция конца ключа = 0

		for // ищем позицию конца ключа
		(
		curFilePos = _ini_nDelimPos[nFilePointer][kPos] - 1; // текущ. поз. файла = поз. текущ. INI_DELIMITER - 1
		curFilePos >= 0; // продолжать пока поз. файла >= 0
		curFilePos-- // после каждого повтора текущ. поз. файла -= 1
		)
		{
			switch ( _ini_szFileContent[nFilePointer]{curFilePos} ) // узнаем что за символ в текущ. поз. файла
			{
			case INI_SPACE_CHAR :           continue; // если это пробельный символ, перейдем к пред. символу файла
			case INI_STRING_DELIMITER :     break; // если это конец строки
				default : // если это другой символ
				{
					found = 1; // позиция конца ключа найдена
					break; // конец цикла
				}
			}
		}

		// если позиция конца ключа не найдена, переход к след. позиции INI_DELIMITER
		if ( found != 1 ) continue;
		// ----------------


		// сравниваем посимвольно текущий ключ файла и указанный ключ
		for ( new curKeyPos = nKeyLen - 1;  curKeyPos >= 0;  curFilePos--, curKeyPos-- )
		{
			if
			(
			curFilePos < 0 // если поз файла стала < 0
			||  _ini_szFileContent[nFilePointer]{curFilePos} != szKeyName[curKeyPos] // если символы из ключей не равны
			||  _ini_szFileContent[nFilePointer]{curFilePos} == INI_STRING_DELIMITER // если символ из ключа это INI_STRING_DELIMITER
			)
			{
				found = 0; // флаг, ключ не найден
				break; // конец сравнения
			}
		}


		// ----------------
		if ( found != 0 ) // если указанный ключ найден в файле
		{
			// если найдено совпадение не целого ключа файла, а его окончания с указанным ключом
			if ( curFilePos >= 0 )
			{
				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_KEY_STARTS : {}
				default: continue;
				}
			}


			// ----------------
			// текущая позиция в файле будет на 1 больше текущей позиции INI_DELIMITER
			curFilePos = _ini_nDelimPos[nFilePointer][kPos] + 1;

			// ищем позицию начала значения, она будет помещена в curFilePos
			for ( ; ; curFilePos++ )
			{
				if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;

				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_SPACE_CHAR :   continue; // если это пробельный символ, перейдем к след. символу
					default :               break; // если это другой символ
				}
			}
			// ----------------


			// ----------------
			new strValue[INI_INTEGER_SIZE]; // временная строка для численного значения

			// скопируем посимвольно в strValue значение ключа из файла
			// воспользуемся созданной переменной found как позицией в возвращаемом значении
			for ( found = 0;  found < INI_INTEGER_SIZE;  found++, curFilePos++ )
			{
				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_NUMBER_ENDS : // если это символ конца численного значения
					{
						strValue[found] = 0; // запишем символ конца строки
						break; // конец копирования
					}
					default :
					// копируем символ из файла в strValue
					strValue[found] = _ini_szFileContent[nFilePointer]{curFilePos};
				}
			}

			strValue[INI_INTEGER_SIZE - 1] = 0; // на всякий случай обрежем правильно строку

			nReturnValue = strval(strValue); // запишем в nReturnValue численное значение ключа

			return INI_OK;
			// ----------------
		}
		// ----------------
	}
	// ----------------


	// ----------------
	return INI_KEY_NOT_FOUND;
	// ----------------
}




/*
	Получает из открытого INI файла дробное численное значение указанного ключа.

	ПОДРОБНЕЕ
		Парсер ищет в ОЗУ в контенте файла указанный ключ и помещает в
		fReturnValue его дробное значение. fReturnValue нужно создать заранее.

		Имя ключа в файле может быть любой длины, а также может содержать
		любые символы, кроме 2 символов конца строки \r и \n.

		Значение может быть только фиксированной длины, которая равна INI_FLOAT_SIZE - 1.
		Парсер не видит пробелы и знаки табуляции вокруг имени ключа/значения,
		он считает их отступами.

	ПАРАМЕТРЫ:
		nFilePointer        ИД_открытого_файла, полученный от ini_openFile / ini_createFile
		szKeyName[]         имя ключа
		fReturnValue        сюда будет помещено значение ключа в виде дробного числа

	ВЕРНЕТ:
		код ошибки < 0 или 0 при успехе
*/

stock ini_getFloat ( nFilePointer, szKeyName[], & Float: fReturnValue )
{
	// ----------------
	if // если ИД открытого файла указан неверно
	(
	nFilePointer < 0
	||  nFilePointer >= INI_MAX_OPENED_FILES
	||  _ini_nSlotUsed[nFilePointer] != 1
	)
	return INI_WRONG_SLOT; // вернуть код: неверный указатель на открытый файл
	// ----------------


	// ----------------
	new nKeyLen = strlen(szKeyName); // узнаем длину имени указанного ключа

	if ( nKeyLen <= 0 ) // если указан пустой ключ
	return INI_KEY_NOT_FOUND;
	// ----------------


	// ----------------
	for // перебор и сравнение всех ключей файла с указанным ключом
	(
	new kPos = 0, curFilePos, found = 0;
	kPos < _ini_nKeysCount[nFilePointer];
	kPos++
	)
	{
		// ----------------
		found = 0; // флаг, найдена ли позиция конца ключа = 0

		for // ищем позицию конца ключа
		(
		curFilePos = _ini_nDelimPos[nFilePointer][kPos] - 1; // текущ. поз. файла = поз. текущ. INI_DELIMITER - 1
		curFilePos >= 0; // продолжать пока поз. файла >= 0
		curFilePos-- // после каждого повтора текущ. поз. файла -= 1
		)
		{
			switch ( _ini_szFileContent[nFilePointer]{curFilePos} ) // узнаем что за символ в текущ. поз. файла
			{
			case INI_SPACE_CHAR :           continue; // если это пробельный символ, перейдем к пред. символу файла
			case INI_STRING_DELIMITER :     break; // если это конец строки
				default : // если это другой символ
				{
					found = 1; // позиция конца ключа найдена
					break; // конец цикла
				}
			}
		}

		// если позиция конца ключа не найдена, переход к след. позиции INI_DELIMITER
		if ( found != 1 ) continue;
		// ----------------


		// сравниваем посимвольно текущий ключ файла и указанный ключ
		for ( new curKeyPos = nKeyLen - 1;  curKeyPos >= 0;  curFilePos--, curKeyPos-- )
		{
			if
			(
			curFilePos < 0 // если поз файла стала < 0
			||  _ini_szFileContent[nFilePointer]{curFilePos} != szKeyName[curKeyPos] // если символы из ключей не равны
			||  _ini_szFileContent[nFilePointer]{curFilePos} == INI_STRING_DELIMITER // если символ из ключа это INI_STRING_DELIMITER
			)
			{
				found = 0; // флаг, ключ не найден
				break; // конец сравнения
			}
		}


		// ----------------
		if ( found != 0 ) // если указанный ключ найден в файле
		{
			// если найдено совпадение не целого ключа файла, а его окончания с указанным ключом
			if ( curFilePos >= 0 )
			{
				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_KEY_STARTS : {}
				default: continue;
				}
			}


			// ----------------
			// текущая позиция в файле будет на 1 больше текущей позиции INI_DELIMITER
			curFilePos = _ini_nDelimPos[nFilePointer][kPos] + 1;

			// ищем позицию начала значения, она будет помещена в curFilePos
			for ( ; ; curFilePos++ )
			{
				if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;

				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_SPACE_CHAR :   continue; // если это пробельный символ, перейдем к след. символу
					default :               break; // если это другой символ
				}
			}
			// ----------------


			// ----------------
			new strValue[INI_FLOAT_SIZE]; // временная строка для дробного значения

			// скопируем посимвольно в strValue значение ключа из файла
			// воспользуемся созданной переменной found как позицией в возвращаемом значении
			for ( found = 0;  found < INI_INTEGER_SIZE;  found++, curFilePos++ )
			{
				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_NUMBER_ENDS : // если это символ конца численного значения
					{
						strValue[found] = 0; // запишем символ конца строки
						break; // конец копирования
					}
					default :
					// копируем символ из файла в strValue
					strValue[found] = _ini_szFileContent[nFilePointer]{curFilePos};
				}
			}

			strValue[INI_FLOAT_SIZE - 1] = 0; // на всякий случай обрежем правильно строку

			fReturnValue = floatstr(strValue); // запишем в fReturnValue дробное значение ключа

			return INI_OK;
			// ----------------
		}
		// ----------------
	}
	// ----------------


	// ----------------
	return INI_KEY_NOT_FOUND;
	// ----------------
}











/*
	Изменяет/добавляет в открытый INI файл указанный ключ и его значение.

	ПОДРОБНЕЕ
		Парсер ищет в ОЗУ в контенте файла указанный ключ и изменяет его
		строковое значение на szKeyValue.

		Форматирование файла не меняется. Если только указанное имя ключа
		не было найдено, тогда ключ и значение будут добавлены в конец файла.

		Имя ключа/значение в файле может быть любой длины, а также может содержать
		любые символы, кроме 2 символов конца строки \r и \n. Если эти символы есть
		в имени ключа или в значении, вы должны должны хорошо знать и осознавать
		последствия.

	ПАРАМЕТРЫ:
		nFilePointer        ИД_открытого_файла, полученный от ini_openFile / ini_createFile
		szKeyName[]         имя ключа
		szKeyValue[]        строка со значением ключа

	ВЕРНЕТ:
		код ошибки < 0 или 0 при успехе
*/

stock ini_setString ( nFilePointer, szKeyName[], szKeyValue[] )
{
	// ----------------
	if // если ИД открытого файла указан неверно
	(
	nFilePointer < 0
	||  nFilePointer >= INI_MAX_OPENED_FILES
	||  _ini_nSlotUsed[nFilePointer] != 1
	)
	return INI_WRONG_SLOT; // вернуть код: неверный указатель на открытый файл
	// ----------------


	// ----------------
	new nKeyLen = strlen(szKeyName); // узнаем длину имени указанного ключа

	if ( nKeyLen <= 0 ) // если указан пустой ключ
	return INI_KEY_NOT_FOUND;
	// ----------------


	// ----------------
	for // перебор и сравнение всех ключей файла с указанным ключом
	(
	new kPos = 0, curFilePos, found;
	kPos < _ini_nKeysCount[nFilePointer];
	kPos++
	)
	{
		// ----------------
		found = 0; // флаг, найдена ли позиция конца ключа = 0

		for // ищем позицию конца ключа
		(
		curFilePos = _ini_nDelimPos[nFilePointer][kPos] - 1; // текущ. поз. файла = поз. текущ. INI_DELIMITER - 1
		curFilePos >= 0; // продолжать пока поз. файла >= 0
		curFilePos-- // после каждого повтора текущ. поз. файла -= 1
		)
		{
			switch ( _ini_szFileContent[nFilePointer]{curFilePos} ) // узнаем что за символ в текущ. поз. файла
			{
			case INI_SPACE_CHAR :           continue; // если это пробельный символ, перейдем к пред. символу файла
			case INI_STRING_DELIMITER :     break; // если это конец строки
				default : // если это другой символ
				{
					found = 1; // позиция конца ключа найдена
					break; // конец цикла
				}
			}
		}

		// если позиция конца ключа не найдена, переход к след. позиции INI_DELIMITER
		if ( found != 1 ) continue;
		// ----------------


		// сравниваем посимвольно текущий ключ файла и указанный ключ
		for ( new curKeyPos = nKeyLen - 1;  curKeyPos >= 0;  curFilePos--, curKeyPos-- )
		{
			if
			(
			curFilePos < 0 // если поз файла стала < 0
			||  _ini_szFileContent[nFilePointer]{curFilePos} != szKeyName[curKeyPos] // если символы из ключей не равны
			||  _ini_szFileContent[nFilePointer]{curFilePos} == INI_STRING_DELIMITER // если символ из ключа это INI_STRING_DELIMITER
			)
			{
				found = 0; // флаг, ключ не найден
				break; // конец сравнения
			}
		}


		if ( found != 0 ) // если указанный ключ НАЙДЕН в файле
		{
			// если найдено совпадение не целого ключа файла, а его окончания с указанным ключом
			if ( curFilePos >= 0 )
			{
				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_KEY_STARTS : {}
				default: continue;
				}
			}


			// ----------------
			// текущая позиция в файле будет на 1 больше текущей позиции INI_DELIMITER
			curFilePos = _ini_nDelimPos[nFilePointer][kPos] + 1;

			// ищем позицию начала значения, она будет помещена в curFilePos
			for ( ; ; curFilePos++ )
			{
				if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;

				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_SPACE_CHAR :   continue; // если это пробельный символ, перейдем к след. символу
					default :               break; // если это другой символ
				}
			}
			// ----------------


			// ----------------
			new nValueLen = strlen(szKeyValue); // узнаем размер значения указанного ключа

			// если будущий размер файла превышает лимит
			if ( ( curFilePos + nValueLen ) >= INI_MAX_FILE_SIZE )
			return INI_TOO_LARGE_FILE; // вернем код ошибки о переполнении файла
			// ----------------


			// ----------------
			new fileValueStartPos = curFilePos; // сохраним позицию начала значения

			// ищем позицию конца значения, она будет помещена в curFilePos
			for ( ; ; curFilePos++ )
			{
				if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;

				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_STRING_ENDS :  break; // если это конец строки - стоп
					default :               continue; // если это другой символ, перейдем к след. символу
				}
			}

			// вычислим смещение оставшихся позиций для INI_DELIMITER после изменения значения
			new filePosOffset = nValueLen - ( /*текущ длина значения*/ curFilePos - fileValueStartPos );
			// ----------------


			if ( _ini_nFileBusy[nFilePointer] != 0 ) // если прямо сейчас файл изменяется другой функцией
			return INI_WRITE_ERROR; // вернем код ошибки при записи в файл


			// ----------------
			_ini_nFileBusy[nFilePointer] = 1; // флаг: файл изменяется = 1

			if ( filePosOffset != 0 ) // если длины старого и нового значений разные
			{
				if ( filePosOffset < 0 ) // если длина нового значения меньше
				{
					for // копируем символы, стоящие после текущего значения на их новые места,
					( // которые теперь будут ближе к началу файла
					;
					curFilePos < _ini_nFileSize[nFilePointer];
					curFilePos++
					)
					_ini_szFileContent[nFilePointer]{ curFilePos + filePosOffset } =
					_ini_szFileContent[nFilePointer]{curFilePos};
				}
				else if ( filePosOffset > 0 ) // если длина нового значения больше
				{
					new fileValueEndPos = curFilePos;

					for // копируем символы, стоящие после текущего значения на их новые места,
					( // начиная с конца файла
					curFilePos = _ini_nFileSize[nFilePointer] - 1;
					curFilePos >= fileValueEndPos;
					curFilePos--
					)
					_ini_szFileContent[nFilePointer]{ curFilePos + filePosOffset } =
					_ini_szFileContent[nFilePointer]{curFilePos};
				}

				// изменим позиции всех INI_DELIMITER, которые находились после текущего INI_DELIMITER
				for ( kPos++;  kPos < _ini_nKeysCount[nFilePointer];  kPos++ )
				_ini_nDelimPos[nFilePointer][kPos] += filePosOffset;

				_ini_nFileSize[nFilePointer] += filePosOffset; // изменим размер файла
				_ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] } = 0; // конец строки для конца файла
			}

			curFilePos = fileValueStartPos; // текущ поз файла = поз начала старого значения
			for // запишем новое значение поверх старого
			(
			new valuePos = 0;
			valuePos < nValueLen;
			curFilePos++, valuePos++
			)
			_ini_szFileContent[nFilePointer]{curFilePos} = szKeyValue[valuePos];

			_ini_nFileChanged[nFilePointer]     = 1; // флаг: файл изменен = 1
			_ini_nFileBusy[nFilePointer]        = 0; // флаг: файл изменяется = 0

			return INI_OK;
			// ----------------
		}
	}
	// ----------------


	//
	// если указанный ключ НЕ НАЙДЕН в файле
	//

	// ----------------
	new nValueLen = strlen(szKeyValue); // узнаем размер значения ключа

	// если будущий размер файла превышает лимит
	if ( ( _ini_nFileSize[nFilePointer] + 5 + nKeyLen + nValueLen ) >= INI_MAX_FILE_SIZE )
	return INI_TOO_LARGE_FILE; // вернем код ошибки о переполнении файла
	// ----------------


	// ----------------
	if ( _ini_nFileBusy[nFilePointer] != 0 ) // если прямо сейчас файл изменяется другой функцией
	return INI_WRITE_ERROR; // вернем код ошибки при записи в файл

	_ini_nFileBusy[nFilePointer] = 1; // флаг: файл изменяется = 1
	// ----------------


	// ----------------
	if // если последний символ файла не является разделителем строк
	(
	_ini_nFileSize[nFilePointer] > 0
	&&  _ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] - 1 } != INI_STRING_DELIMITER
	)
	{
		// вставим в конец файла перевод каретки и разделитель строк
		_ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] }        = '\r';
		_ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] + 1 }    = INI_STRING_DELIMITER;

		_ini_nFileSize[nFilePointer] += 2; // увеличим размер файла на 2
	}

	new curFilePos, strPos; // будут временным хранилищами позиций в файле/ключе/значении

	for // добавим посимвольно имя ключа в файл
	(
	curFilePos = _ini_nFileSize[nFilePointer], strPos = 0;
	strPos < nKeyLen;
	curFilePos++, strPos++
	)
	_ini_szFileContent[nFilePointer]{curFilePos} = szKeyName[strPos];

	_ini_szFileContent[nFilePointer]{ curFilePos++ }                = ' '; // после ключа добавим пробел

	_ini_nDelimPos[nFilePointer][ _ini_nKeysCount[nFilePointer] ]   = curFilePos; // добавим новую позицию INI_DELIMITER
	_ini_nKeysCount[nFilePointer]++; // кол-во ключей в файле +1

	_ini_szFileContent[nFilePointer]{ curFilePos++ }                = INI_DELIMITER; // после пробела выше добавим INI_DELIMITER
	_ini_szFileContent[nFilePointer]{ curFilePos++ }                = ' '; // после INI_DELIMITER добавим пробел

	// добавим посимвольно значение ключа в файл
	for ( strPos = 0;  strPos < nValueLen;  curFilePos++, strPos++ )
	_ini_szFileContent[nFilePointer]{curFilePos} = szKeyValue[strPos];

	_ini_szFileContent[nFilePointer]{curFilePos}    = 0; // добавим символ конца строки
	_ini_nFileSize[nFilePointer]                    = curFilePos; // изменим размер файла
	_ini_nFileChanged[nFilePointer]                 = 1; // флаг: файл изменен = 1
	_ini_nFileBusy[nFilePointer]                    = 0; // флаг: файл изменяется = 0

	return INI_OK;
	// ----------------
}




/*
	Изменяет/добавляет в открытый INI файл указанный ключ и его целочисленное значение.

	ПОДРОБНЕЕ
		Парсер ищет в ОЗУ в контенте файла указанный ключ и изменяет его
		целочисленное значение на nKeyValue.

		Форматирование файла не меняется, если только указанное имя ключа
		не было найдено, тогда ключ и значение будут добавлены в конец файла.

		Имя ключа в файле может быть любой длины, а также может содержать
		любые символы, кроме 2 символов конца строки \r и \n. Если вы в качестве
		значения указываете число больше, чем cellmax или меньше, чем cellmin,
		вы должны должны хорошо знать и осознавать последствия.

	ПАРАМЕТРЫ:
		nFilePointer        ИД_открытого_файла, полученный от ini_openFile / ini_createFile
		szKeyName[]         имя ключа
		nKeyValue           целочисленное значение ключа

	ВЕРНЕТ:
		код ошибки < 0 или 0 при успехе
*/

stock ini_setInteger ( nFilePointer, szKeyName[], nKeyValue )
{
	// ----------------
	if // если ИД открытого файла указан неверно
	(
	nFilePointer < 0
	||  nFilePointer >= INI_MAX_OPENED_FILES
	||  _ini_nSlotUsed[nFilePointer] != 1
	)
	return INI_WRONG_SLOT; // вернуть код: неверный указатель на открытый файл
	// ----------------


	// ----------------
	new nKeyLen = strlen(szKeyName); // узнаем длину имени указанного ключа

	if ( nKeyLen <= 0 ) // если указан пустой ключ
	return INI_KEY_NOT_FOUND;
	// ----------------


	// ----------------
	for // перебор и сравнение всех ключей файла с указанным ключом
	(
	new kPos = 0, curFilePos, found;
	kPos < _ini_nKeysCount[nFilePointer];
	kPos++
	)
	{
		// ----------------
		found = 0; // флаг, найдена ли позиция конца ключа = 0

		for // ищем позицию конца ключа
		(
		curFilePos = _ini_nDelimPos[nFilePointer][kPos] - 1; // текущ. поз. файла = поз. текущ. INI_DELIMITER - 1
		curFilePos >= 0; // продолжать пока поз. файла >= 0
		curFilePos-- // после каждого повтора текущ. поз. файла -= 1
		)
		{
			switch ( _ini_szFileContent[nFilePointer]{curFilePos} ) // узнаем что за символ в текущ. поз. файла
			{
			case INI_SPACE_CHAR :           continue; // если это пробельный символ, перейдем к пред. символу файла
			case INI_STRING_DELIMITER :     break; // если это конец строки
				default : // если это другой символ
				{
					found = 1; // позиция конца ключа найдена
					break; // конец цикла
				}
			}
		}

		// если позиция конца ключа не найдена, переход к след. позиции INI_DELIMITER
		if ( found != 1 ) continue;
		// ----------------


		// сравниваем посимвольно текущий ключ файла и указанный ключ
		for ( new curKeyPos = nKeyLen - 1;  curKeyPos >= 0;  curFilePos--, curKeyPos-- )
		{
			if
			(
			curFilePos < 0 // если поз файла стала < 0
			||  _ini_szFileContent[nFilePointer]{curFilePos} != szKeyName[curKeyPos] // если символы из ключей не равны
			||  _ini_szFileContent[nFilePointer]{curFilePos} == INI_STRING_DELIMITER // если символ из ключа это INI_STRING_DELIMITER
			)
			{
				found = 0; // флаг, ключ не найден
				break; // конец сравнения
			}
		}


		if ( found != 0 ) // если указанный ключ НАЙДЕН в файле
		{
			// если найдено совпадение не целого ключа файла, а его окончания с указанным ключом
			if ( curFilePos >= 0 )
			{
				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_KEY_STARTS : {}
				default: continue;
				}
			}


			// ----------------
			// текущая позиция в файле будет на 1 больше текущей позиции INI_DELIMITER
			curFilePos = _ini_nDelimPos[nFilePointer][kPos] + 1;

			// ищем позицию начала значения, она будет помещена в curFilePos
			for ( ; ; curFilePos++ )
			{
				if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;

				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_SPACE_CHAR :   continue; // если это пробельный символ, перейдем к след. символу
					default :               break; // если это другой символ
				}
			}
			// ----------------


			// ----------------
			new szKeyValue[INI_INTEGER_SIZE]; // создадим строку для хранения целочисленного значения
			format( szKeyValue, INI_INTEGER_SIZE, "%i", nKeyValue );
			// ----------------


			// ----------------
			new nValueLen = strlen(szKeyValue); // узнаем размер значения указанного ключа

			// если будущий размер файла превышает лимит
			if ( ( curFilePos + nValueLen ) >= INI_MAX_FILE_SIZE )
			return INI_TOO_LARGE_FILE; // вернем код ошибки о переполнении файла
			// ----------------


			// ----------------
			new fileValueStartPos = curFilePos; // сохраним позицию начала значения

			// ищем позицию конца значения, она будет помещена в curFilePos
			for ( ; ; curFilePos++ )
			{
				if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;

				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_NUMBER_ENDS :  break; // если это конец числа - стоп
					default :               continue; // если это другой символ, перейдем к след. символу
				}
			}

			// вычислим смещение оставшихся позиций для INI_DELIMITER после изменения значения
			new filePosOffset = nValueLen - ( /*текущ длина значения*/ curFilePos - fileValueStartPos );
			// ----------------


			if ( _ini_nFileBusy[nFilePointer] != 0 ) // если прямо сейчас файл изменяется другой функцией
			return INI_WRITE_ERROR; // вернем код ошибки при записи в файл


			// ----------------
			_ini_nFileBusy[nFilePointer] = 1; // флаг: файл изменяется = 1

			if ( filePosOffset != 0 ) // если длины старого и нового значений разные
			{
				if ( filePosOffset < 0 ) // если длина нового значения меньше
				{
					for // копируем символы, стоящие после текущего значения на их новые места,
					( // которые теперь будут ближе к началу файла
					;
					curFilePos < _ini_nFileSize[nFilePointer];
					curFilePos++
					)
					_ini_szFileContent[nFilePointer]{ curFilePos + filePosOffset } =
					_ini_szFileContent[nFilePointer]{curFilePos};
				}
				else if ( filePosOffset > 0 ) // если длина нового значения больше
				{
					new fileValueEndPos = curFilePos;

					for // копируем символы, стоящие после текущего значения на их новые места,
					( // начиная с конца файла
					curFilePos = _ini_nFileSize[nFilePointer] - 1;
					curFilePos >= fileValueEndPos;
					curFilePos--
					)
					_ini_szFileContent[nFilePointer]{ curFilePos + filePosOffset } =
					_ini_szFileContent[nFilePointer]{curFilePos};
				}

				// изменим позиции всех INI_DELIMITER, которые находились после текущего INI_DELIMITER
				for ( kPos++;  kPos < _ini_nKeysCount[nFilePointer];  kPos++ )
				_ini_nDelimPos[nFilePointer][kPos] += filePosOffset;

				_ini_nFileSize[nFilePointer] += filePosOffset; // изменим размер файла
				_ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] } = 0; // конец строки для конца файла
			}

			curFilePos = fileValueStartPos; // текущ поз файла = поз начала старого значения
			for // запишем новое значение поверх старого
			(
			new valuePos = 0;
			valuePos < nValueLen;
			curFilePos++, valuePos++
			)
			_ini_szFileContent[nFilePointer]{curFilePos} = szKeyValue[valuePos];

			_ini_nFileChanged[nFilePointer]     = 1; // флаг: файл изменен = 1
			_ini_nFileBusy[nFilePointer]        = 0; // флаг: файл изменяется = 0

			return INI_OK;
			// ----------------
		}
	}
	// ----------------


	//
	// если указанный ключ НЕ НАЙДЕН в файле
	//

	// ----------------
	new szKeyValue[INI_INTEGER_SIZE]; // создадим строку для хранения целочисленного значения
	format( szKeyValue, INI_INTEGER_SIZE, "%i", nKeyValue );
	// ----------------


	// ----------------
	new nValueLen = strlen(szKeyValue); // узнаем размер значения ключа

	// если будущий размер файла превышает лимит
	if ( ( _ini_nFileSize[nFilePointer] + 5 + nKeyLen + nValueLen ) >= INI_MAX_FILE_SIZE )
	return INI_TOO_LARGE_FILE; // вернем код ошибки о переполнении файла
	// ----------------


	// ----------------
	if ( _ini_nFileBusy[nFilePointer] != 0 ) // если прямо сейчас файл изменяется другой функцией
	return INI_WRITE_ERROR; // вернем код ошибки при записи в файл

	_ini_nFileBusy[nFilePointer] = 1; // флаг: файл изменяется = 1
	// ----------------


	// ----------------
	if // если последний символ файла не является разделителем строк
	(
	_ini_nFileSize[nFilePointer] > 0
	&&  _ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] - 1 } != INI_STRING_DELIMITER
	)
	{
		// вставим в конец файла перевод каретки и разделитель строк
		_ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] }        = '\r';
		_ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] + 1 }    = INI_STRING_DELIMITER;

		_ini_nFileSize[nFilePointer] += 2; // увеличим размер файла на 2
	}

	new curFilePos, strPos; // будут временным хранилищами позиций в файле/ключе/значении

	for // добавим посимвольно имя ключа в файл
	(
	curFilePos = _ini_nFileSize[nFilePointer], strPos = 0;
	strPos < nKeyLen;
	curFilePos++, strPos++
	)
	_ini_szFileContent[nFilePointer]{curFilePos} = szKeyName[strPos];

	_ini_szFileContent[nFilePointer]{ curFilePos++ }                = ' '; // после ключа добавим пробел

	_ini_nDelimPos[nFilePointer][ _ini_nKeysCount[nFilePointer] ]   = curFilePos; // добавим новую позицию INI_DELIMITER
	_ini_nKeysCount[nFilePointer]++; // кол-во ключей в файле +1

	_ini_szFileContent[nFilePointer]{ curFilePos++ }                = INI_DELIMITER; // после пробела выше добавим INI_DELIMITER
	_ini_szFileContent[nFilePointer]{ curFilePos++ }                = ' '; // после INI_DELIMITER добавим пробел

	// добавим посимвольно значение ключа в файл
	for ( strPos = 0;  strPos < nValueLen;  curFilePos++, strPos++ )
	_ini_szFileContent[nFilePointer]{curFilePos} = szKeyValue[strPos];

	_ini_szFileContent[nFilePointer]{curFilePos}    = 0; // добавим символ конца строки
	_ini_nFileSize[nFilePointer]                    = curFilePos; // изменим размер файла
	_ini_nFileChanged[nFilePointer]                 = 1; // флаг: файл изменен = 1
	_ini_nFileBusy[nFilePointer]                    = 0; // флаг: файл изменяется = 0

	return INI_OK;
	// ----------------
}




/*
	Изменяет/добавляет в открытый INI файл указанный ключ и его дробное численное значение.

	ПОДРОБНЕЕ
		Парсер ищет в ОЗУ в контенте файла указанный ключ и изменяет его
		дробное значение на fKeyValue.

		Форматирование файла не меняется, если только указанное имя ключа
		не было найдено, тогда ключ и значение будут добавлены в конец файла.

		Имя ключа в файле может быть любой длины, а также может содержать
		любые символы, кроме 2 символов конца строки \r и \n. Если вы в качестве
		значения указываете очень большое/маленькое дробное число, вы должны
		хорошо знать и осознавать последствия.

	ПАРАМЕТРЫ:
		nFilePointer        ИД_открытого_файла, полученный от ini_openFile / ini_createFile
		szKeyName[]         имя ключа
		fKeyValue           дробное численное значение ключа

	ВЕРНЕТ:
		код ошибки < 0 или 0 при успехе
*/

stock ini_setFloat ( nFilePointer, szKeyName[], Float: fKeyValue )
{
	// ----------------
	if // если ИД открытого файла указан неверно
	(
	nFilePointer < 0
	||  nFilePointer >= INI_MAX_OPENED_FILES
	||  _ini_nSlotUsed[nFilePointer] != 1
	)
	return INI_WRONG_SLOT; // вернуть код: неверный указатель на открытый файл
	// ----------------


	// ----------------
	new nKeyLen = strlen(szKeyName); // узнаем длину имени указанного ключа

	if ( nKeyLen <= 0 ) // если указан пустой ключ
	return INI_KEY_NOT_FOUND;
	// ----------------


	// ----------------
	for // перебор и сравнение всех ключей файла с указанным ключом
	(
	new kPos = 0, curFilePos, found;
	kPos < _ini_nKeysCount[nFilePointer];
	kPos++
	)
	{
		// ----------------
		found = 0; // флаг, найдена ли позиция конца ключа = 0

		for // ищем позицию конца ключа
		(
		curFilePos = _ini_nDelimPos[nFilePointer][kPos] - 1; // текущ. поз. файла = поз. текущ. INI_DELIMITER - 1
		curFilePos >= 0; // продолжать пока поз. файла >= 0
		curFilePos-- // после каждого повтора текущ. поз. файла -= 1
		)
		{
			switch ( _ini_szFileContent[nFilePointer]{curFilePos} ) // узнаем что за символ в текущ. поз. файла
			{
			case INI_SPACE_CHAR :           continue; // если это пробельный символ, перейдем к пред. символу файла
			case INI_STRING_DELIMITER :     break; // если это конец строки
				default : // если это другой символ
				{
					found = 1; // позиция конца ключа найдена
					break; // конец цикла
				}
			}
		}

		// если позиция конца ключа не найдена, переход к след. позиции INI_DELIMITER
		if ( found != 1 ) continue;
		// ----------------


		// сравниваем посимвольно текущий ключ файла и указанный ключ
		for ( new curKeyPos = nKeyLen - 1;  curKeyPos >= 0;  curFilePos--, curKeyPos-- )
		{
			if
			(
			curFilePos < 0 // если поз файла стала < 0
			||  _ini_szFileContent[nFilePointer]{curFilePos} != szKeyName[curKeyPos] // если символы из ключей не равны
			||  _ini_szFileContent[nFilePointer]{curFilePos} == INI_STRING_DELIMITER // если символ из ключа это INI_STRING_DELIMITER
			)
			{
				found = 0; // флаг, ключ не найден
				break; // конец сравнения
			}
		}


		if ( found != 0 ) // если указанный ключ НАЙДЕН в файле
		{
			// если найдено совпадение не целого ключа файла, а его окончания с указанным ключом
			if ( curFilePos >= 0 )
			{
				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_KEY_STARTS : {}
				default: continue;
				}
			}


			// ----------------
			// текущая позиция в файле будет на 1 больше текущей позиции INI_DELIMITER
			curFilePos = _ini_nDelimPos[nFilePointer][kPos] + 1;

			// ищем позицию начала значения, она будет помещена в curFilePos
			for ( ; ; curFilePos++ )
			{
				if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;

				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_SPACE_CHAR :   continue; // если это пробельный символ, перейдем к след. символу
					default :               break; // если это другой символ
				}
			}
			// ----------------


			// ----------------
			new szKeyValue[INI_FLOAT_SIZE]; // создадим строку для хранения дробного значения
			format( szKeyValue, INI_FLOAT_SIZE, "%f", fKeyValue ); // поместим строковое значение fKeyValue в szKeyValue
			// ----------------


			// ----------------
			new nValueLen = strlen(szKeyValue); // узнаем размер значения указанного ключа

			// если будущий размер файла превышает лимит
			if ( ( curFilePos + nValueLen ) >= INI_MAX_FILE_SIZE )
			return INI_TOO_LARGE_FILE; // вернем код ошибки о переполнении файла
			// ----------------


			// ----------------
			new fileValueStartPos = curFilePos; // сохраним позицию начала значения

			// ищем позицию конца значения, она будет помещена в curFilePos
			for ( ; ; curFilePos++ )
			{
				if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;

				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_NUMBER_ENDS :  break; // если это конец числа - стоп
					default :               continue; // если это другой символ, перейдем к след. символу
				}
			}

			// вычислим смещение оставшихся позиций для INI_DELIMITER после изменения значения
			new filePosOffset = nValueLen - ( /*текущ длина значения*/ curFilePos - fileValueStartPos );
			// ----------------


			if ( _ini_nFileBusy[nFilePointer] != 0 ) // если прямо сейчас файл изменяется другой функцией
			return INI_WRITE_ERROR; // вернем код ошибки при записи в файл


			// ----------------
			_ini_nFileBusy[nFilePointer] = 1; // флаг: файл изменяется = 1

			if ( filePosOffset != 0 ) // если длины старого и нового значений разные
			{
				if ( filePosOffset < 0 ) // если длина нового значения меньше
				{
					for // копируем символы, стоящие после текущего значения на их новые места,
					( // которые теперь будут ближе к началу файла
					;
					curFilePos < _ini_nFileSize[nFilePointer];
					curFilePos++
					)
					_ini_szFileContent[nFilePointer]{ curFilePos + filePosOffset } =
					_ini_szFileContent[nFilePointer]{curFilePos};
				}
				else if ( filePosOffset > 0 ) // если длина нового значения больше
				{
					new fileValueEndPos = curFilePos;

					for // копируем символы, стоящие после текущего значения на их новые места,
					( // начиная с конца файла
					curFilePos = _ini_nFileSize[nFilePointer] - 1;
					curFilePos >= fileValueEndPos;
					curFilePos--
					)
					_ini_szFileContent[nFilePointer]{ curFilePos + filePosOffset } =
					_ini_szFileContent[nFilePointer]{curFilePos};
				}

				// изменим позиции всех INI_DELIMITER, которые находились после текущего INI_DELIMITER
				for ( kPos++;  kPos < _ini_nKeysCount[nFilePointer];  kPos++ )
				_ini_nDelimPos[nFilePointer][kPos] += filePosOffset;

				_ini_nFileSize[nFilePointer] += filePosOffset; // изменим размер файла
				_ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] } = 0; // конец строки для конца файла
			}

			curFilePos = fileValueStartPos; // текущ поз файла = поз начала старого значения
			for // запишем новое значение поверх старого
			(
			new valuePos = 0;
			valuePos < nValueLen;
			curFilePos++, valuePos++
			)
			_ini_szFileContent[nFilePointer]{curFilePos} = szKeyValue[valuePos];

			_ini_nFileChanged[nFilePointer]     = 1; // флаг: файл изменен = 1
			_ini_nFileBusy[nFilePointer]        = 0; // флаг: файл изменяется = 0

			return INI_OK;
			// ----------------
		}
	}
	// ----------------


	//
	// если указанный ключ НЕ НАЙДЕН в файле
	//

	// ----------------
	new szKeyValue[INI_FLOAT_SIZE]; // создадим строку для хранения дробного значения
	format( szKeyValue, INI_FLOAT_SIZE, "%f", fKeyValue ); // поместим строковое значение fKeyValue в szKeyValue
	// ----------------


	// ----------------
	new nValueLen = strlen(szKeyValue); // узнаем размер значения ключа

	// если будущий размер файла превышает лимит
	if ( ( _ini_nFileSize[nFilePointer] + 5 + nKeyLen + nValueLen ) >= INI_MAX_FILE_SIZE )
	return INI_TOO_LARGE_FILE; // вернем код ошибки о переполнении файла
	// ----------------


	// ----------------
	if ( _ini_nFileBusy[nFilePointer] != 0 ) // если прямо сейчас файл изменяется другой функцией
	return INI_WRITE_ERROR; // вернем код ошибки при записи в файл

	_ini_nFileBusy[nFilePointer] = 1; // флаг: файл изменяется = 1
	// ----------------


	// ----------------
	if // если последний символ файла не является разделителем строк
	(
	_ini_nFileSize[nFilePointer] > 0
	&&  _ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] - 1 } != INI_STRING_DELIMITER
	)
	{
		// вставим в конец файла перевод каретки и разделитель строк
		_ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] }        = '\r';
		_ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] + 1 }    = INI_STRING_DELIMITER;

		_ini_nFileSize[nFilePointer] += 2; // увеличим размер файла на 2
	}

	new curFilePos, strPos; // будут временным хранилищами позиций в файле/ключе/значении

	for // добавим посимвольно имя ключа в файл
	(
	curFilePos = _ini_nFileSize[nFilePointer], strPos = 0;
	strPos < nKeyLen;
	curFilePos++, strPos++
	)
	_ini_szFileContent[nFilePointer]{curFilePos} = szKeyName[strPos];

	_ini_szFileContent[nFilePointer]{ curFilePos++ }                = ' '; // после ключа добавим пробел

	_ini_nDelimPos[nFilePointer][ _ini_nKeysCount[nFilePointer] ]   = curFilePos; // добавим новую позицию INI_DELIMITER
	_ini_nKeysCount[nFilePointer]++; // кол-во ключей в файле +1

	_ini_szFileContent[nFilePointer]{ curFilePos++ }                = INI_DELIMITER; // после пробела выше добавим INI_DELIMITER
	_ini_szFileContent[nFilePointer]{ curFilePos++ }                = ' '; // после INI_DELIMITER добавим пробел

	// добавим посимвольно значение ключа в файл
	for ( strPos = 0;  strPos < nValueLen;  curFilePos++, strPos++ )
	_ini_szFileContent[nFilePointer]{curFilePos} = szKeyValue[strPos];

	_ini_szFileContent[nFilePointer]{curFilePos}    = 0; // добавим символ конца строки
	_ini_nFileSize[nFilePointer]                    = curFilePos; // изменим размер файла
	_ini_nFileChanged[nFilePointer]                 = 1; // флаг: файл изменен = 1
	_ini_nFileBusy[nFilePointer]                    = 0; // флаг: файл изменяется = 0

	return INI_OK;
	// ----------------
}




/*
	Удаляет из открытого INI файла указанный ключ и его значение.

	ПОДРОБНЕЕ
		Парсер ищет в ОЗУ в контенте файла указанный ключ и удаляет найденную
		пару ключ/значение.

		Имя ключа/значение в файле может быть любой длины,
		а также может содержать любые символы, кроме 2 символов конца строки \r и \n.

		Если эти символы есть в имени ключа или в значении,
		вы должны должны хорошо знать и осознавать последствия.

	ПАРАМЕТРЫ:
		nFilePointer        ИД_открытого_файла, полученный от ini_openFile / ini_createFile
		szKeyName[]         имя ключа

	ВЕРНЕТ:
		код ошибки < 0 или 0 при успехе
*/

stock ini_removeKey ( nFilePointer, szKeyName[] )
{
	// ----------------
	if // если ИД открытого файла указан неверно
	(
	nFilePointer < 0
	||  nFilePointer >= INI_MAX_OPENED_FILES
	||  _ini_nSlotUsed[nFilePointer] != 1
	)
	return INI_WRONG_SLOT; // вернуть код: неверный указатель на открытый файл
	// ----------------


	// ----------------
	new nKeyLen = strlen(szKeyName); // узнаем длину имени указанного ключа

	if ( nKeyLen <= 0 ) // если указан пустой ключ
	return INI_KEY_NOT_FOUND;
	// ----------------


	// ----------------
	for // перебор и сравнение всех ключей файла с указанным ключом
	(
	new kPos = 0, curFilePos, found;
	kPos < _ini_nKeysCount[nFilePointer];
	kPos++
	)
	{
		// ----------------
		found = 0; // флаг, найдена ли позиция конца ключа = 0

		for // ищем позицию конца ключа
		(
		curFilePos = _ini_nDelimPos[nFilePointer][kPos] - 1; // текущ. поз. файла = поз. текущ. INI_DELIMITER - 1
		curFilePos >= 0; // продолжать пока поз. файла >= 0
		curFilePos-- // после каждого повтора текущ. поз. файла -= 1
		)
		{
			switch ( _ini_szFileContent[nFilePointer]{curFilePos} ) // узнаем что за символ в текущ. поз. файла
			{
			case INI_SPACE_CHAR :           continue; // если это пробельный символ, перейдем к пред. символу файла
			case INI_STRING_DELIMITER :     break; // если это конец строки
				default : // если это другой символ
				{
					found = 1; // позиция конца ключа найдена
					break; // конец цикла
				}
			}
		}

		// если позиция конца ключа не найдена, переход к след. позиции INI_DELIMITER
		if ( found != 1 ) continue;
		// ----------------


		// сравниваем посимвольно текущий ключ файла и указанный ключ
		for ( new curKeyPos = nKeyLen - 1;  curKeyPos >= 0;  curFilePos--, curKeyPos-- )
		{
			if
			(
			curFilePos < 0 // если поз файла стала < 0
			||  _ini_szFileContent[nFilePointer]{curFilePos} != szKeyName[curKeyPos] // если символы из ключей не равны
			||  _ini_szFileContent[nFilePointer]{curFilePos} == INI_STRING_DELIMITER // если символ из ключа это INI_STRING_DELIMITER
			)
			{
				found = 0; // флаг, ключ не найден
				break; // конец сравнения
			}
		}


		if ( found != 0 ) // если указанный ключ НАЙДЕН в файле
		{
			// если найдено совпадение не целого ключа файла, а его окончания с указанным ключом
			if ( curFilePos >= 0 )
			{
				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_KEY_STARTS : {}
				default: continue;
				}
			}


			// сохраним позицию начала ключа
			new keyStartPos = ( curFilePos <= 0 ) ? 0 : curFilePos + 1;


			// ----------------
			// текущая позиция в файле будет на 1 больше текущей позиции INI_DELIMITER
			curFilePos = _ini_nDelimPos[nFilePointer][kPos] + 1;

			// ищем позицию начала значения, она будет помещена в curFilePos
			for ( ; ; curFilePos++ )
			{
				if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;

				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_SPACE_CHAR :   continue; // если это пробельный символ, перейдем к след. символу
					default :               break; // если это другой символ
				}
			}
			// ----------------


			// ----------------
			// ищем позицию конца значения, она будет помещена в curFilePos
			for ( ; ; curFilePos++ )
			{
				if ( curFilePos >= _ini_nFileSize[nFilePointer] ) break;

				switch ( _ini_szFileContent[nFilePointer]{curFilePos} )
				{
				case INI_STRING_ENDS :  break; // если это конец строки - стоп
					default :               continue; // если это другой символ, перейдем к след. символу
				}
			}

			// вычислим смещение оставшихся позиций для INI_DELIMITER после изменения значения
			new filePosOffset = keyStartPos - curFilePos;
			// ----------------


			if ( _ini_nFileBusy[nFilePointer] != 0 ) // если прямо сейчас файл изменяется другой функцией
			return INI_WRITE_ERROR; // вернем код ошибки при записи в файл


			// ----------------
			_ini_nFileBusy[nFilePointer] = 1; // флаг: файл изменяется = 1

			// затираем контент ключа, следующими за ним данными файла
			for ( ; curFilePos <= _ini_nFileSize[nFilePointer]; curFilePos++ )
			_ini_szFileContent[nFilePointer]{ curFilePos + filePosOffset } =
			_ini_szFileContent[nFilePointer]{curFilePos};

			// затираем текущий INI_DELIMITER, и меняем позиции последующих INI_DELIMITER
			for ( ;  kPos < _ini_nKeysCount[nFilePointer];  kPos++ )
			_ini_nDelimPos[nFilePointer][kPos] = _ini_nDelimPos[nFilePointer][kPos + 1] + filePosOffset;

			_ini_nKeysCount[nFilePointer]--;
			_ini_nFileSize[nFilePointer] += filePosOffset; // изменим размер файла
			_ini_szFileContent[nFilePointer]{ _ini_nFileSize[nFilePointer] } = 0; // конец строки для конца файла

			_ini_nFileChanged[nFilePointer]     = 1; // флаг: файл изменен = 1
			_ini_nFileBusy[nFilePointer]        = 0; // флаг: файл изменяется = 0

			return INI_OK;
			// ----------------
		}
	}
	// ----------------


	//
	// если указанный ключ НЕ НАЙДЕН в файле
	//

	// ----------------
	return INI_KEY_NOT_FOUND;
	// ----------------
}




/*
	Возвращает строку с описанием указанного кода ошибки на русском языке.

	ПОДРОБНЕЕ
		Если какая-то функция вам вернула не 0 (всё в порядке), а отрицательное
		значение, значит, имела место ошибка. Если вы хотите вывести в любой лог или
		в диалог человеческое описание этой ошибки, то передайте этой функции код
		ошибки и она вам вернет описание этой ошибки на русском языке.

		Описание ошибок довольно длинные и превышают размер в 128 символов,
		поэтому выводить эти описания в чате не рекомендуется!

	ПАРАМЕТРЫ:
		nErrorCode      код ошибки, полученный от любой функции

	ВЕРНЕТ:
		строку с человеческим описанием ошибки, под указанным кодом

	ПРИМЕР:
		new ini = ini_openFile("несуществующий файл"); // пытаемся открыть несуществующий файл

		if ( ini < 0 ) print( ini_getErrorInfo(ini) );
		else
		{
			new number, result = ini_getInteger( ini, "несуществующий ключ", number ); // пробуем получить значение несуществующего ключа

			if ( result < 0 ) print( ini_getErrorInfo(result) );

			ini_closeFile(ini);
		}
*/

stock ini_getErrorInfo ( nErrorCode )
{
	new error_ms[200] = "Ошибок нет";

	if ( nErrorCode >= 0 ) return error_ms;

	switch ( nErrorCode )
	{
	case INI_FILE_NOT_FOUND :     error_ms = "INI файл, указанный в `ini_openFile`, не существует, возможно, указан неверный путь к нему";
	case INI_FILE_ALREADY_EXIST : error_ms = "INI файл, указанный в `ini_createFile`, уже существует, укажите другое имя/путь к файлу";
	case INI_TOO_LARGE_FILE :     error_ms = "Невозможно добавить новый ключ в INI файл, т.к. размер INI файла превысит допустимый лимит в " #INI_MAX_FILE_SIZE " символ(ов), или будет превышен лимит в " #INI_MAX_KEYS_IN_FILE " ключей";
	case INI_WRONG_PATH_SIZE :    error_ms = "Длина пути к INI файлу, указанного в `ini_openFile` / `ini_createFile`, превышает лимит в " #INI_MAX_FILENAME_SIZE " символ(ов)";
	case INI_READ_ERROR :         error_ms = "Ошибка при чтении INI файла с диска, возможно, файл занят другим процессом";
	case INI_WRITE_ERROR :        error_ms = "Ошибка при записи содержимого INI файла на диск, возможно, файл занят другим процессом";
	case INI_NO_FREE_SLOT :       error_ms = "Лимит в " #INI_MAX_OPENED_FILES " одновременно открытых INI файлов исчерпан, поэтому открыть/создать еще 1 файл нельзя, пока не закрыт хоть 1 из открытых файлов";
	case INI_WRONG_SLOT :         error_ms = "Указан неверный ID открытого INI файла, возможно, при открытии INI файла произошла ошибка, поэтому вместо ID файла вы получили код ошибки";
	case INI_KEY_NOT_FOUND :      error_ms = "Указанный ключ не найден в открытом INI файле";
	case INI_WRONG_RETURN_SIZE :  error_ms = "При попытке чтения строкового значения из INI файла, последний параметр `nSizeOfReturn` в `ini_getString` оказался <= 0, укажите вручную значение > 0 для него";

	default:                      error_ms = "Неизвестная ошибка";
	}

	return error_ms;
}