/*
								+----------------------------------+
								|                                  |
								| ***  UTF-8 / UTF-16 support  *** |
								|                                  |
								|   Copyright © -tHE SWINe- 2006   |
								|                                  |
								|           Unicode.cpp            |
								|                                  |
								+----------------------------------+
*/

/**
 *	@file Unicode.cpp
 *	@date 2006
 *	@author -tHE SWINe-
 *	@brief UTF-8 / UTF-16 support
 *
 *	@date 2007-01-10
 *
 *	passed code revision
 *
 *	added CUnicodeFile::SetEncoding() function for proper XML file parsing (late encoding
 *	detection)
 *
 *	CUnicodeFile::n_ForEachChar() was renamed to CUnicodeFile::t_ForEachChar(), value of
 *	functor is no longer converted to int and it doesn't check functor return value any
 *	more; note it doesn't have means to return error code so error flag must be checked
 *	after calling
 *
 *	added CallStack guards, now using it's assert macros
 *
 *	@date 2008-03-04
 *
 *	now using Integer.h header
 *
 *	@date 2008-04-21
 *
 *	added option to disable character processing as it's unnecessary in some cases and
 *	it still have to be called, even if disabled
 *
 *	@date 2008-07-02
 *
 *	fixed CUnicodeFile::n_WriteString() which failed to compile when character processing
 *	was disabled
 *
 *	@date 2009-05-04
 *
 *	fixed mixed windows / linux line endings
 *
 *	@date 2009-10-20
 *
 *	fixed some warnings when compiling under VC 2005, implemented "Security
 *	Enhancements in the CRT" for VC 2008. compare against MyProjects_2009-10-19_
 *
 */

#include "NewFix.h"

#include "CallStack.h"
#include <vector>
#include <string.h>
#include <stdio.h>
#include "Unicode.h"

/*
 *						=== CUnicodeFile ===
 */

/*
 *	class CUnicodeFile
 *		- class, encapsulating stdio FILE for fast, buffered innput of classical 'ascii'
 *		  files or unicode (utf-8 or utf-16-le) files with transparent code translation
 *		- text input mode functions have two threshold units, one at custom (user-set) level
 *		  and second at overflow level. both of them can be enabled or disabled, if enabled,
 *		  character can be either discarded, replaced by custom character (separate replacement
 *		  character for each unit
 *		- text output mode functions can read escape sequences and converts them to char values
 *		  which are then written
 *		- binary input mode functions do not apply any wide char translation, result value
 *		  is simply cropped (or padded with MSB zero bits) to fit output data type width
 *		- note unicode can contain such character codes that can have value of control
 *		  characters so it can be necessary to open files in binary mode, even for text i/o
 *		  (depends on your c standard library implementation)
 */

/*
 *	CUnicodeFile::CUnicodeFile()
 *		- default constructor
 */
CUnicodeFile::CUnicodeFile()
	:m_p_file_buffer(0), m_n_file_buffer_size(0), m_p_file_buffer_cur(0), m_p_file_buffer_end(0),
#ifdef __UNICODE_FILE_ENABLE_CHAR_PROCESSING
	m_n_string_buffer_used(0),
#endif // __UNICODE_FILE_ENABLE_CHAR_PROCESSING
	m_p_file(0), m_b_foreign_file(false),
	/*m_b_wrote_bom(false), m_n_bof_position(0), m_n_eof_position(-1),*/ // does not need to  be set
	/*m_n_encoding(code_UTF_8), m_b_writing(false), m_b_binary(false),*/
	m_n_error_state(0)
{
	__FuncGuard("CUnicodeFile::CUnicodeFile");

#ifdef __UNICODE_FILE_ENABLE_CHAR_PROCESSING
	m_t_wide_char_proc.b_enable = false;
	m_t_wide_char_proc.n_threshold = 255;
	m_t_wide_char_proc.n_mode = wchar_Translate;
	m_t_wide_char_proc.n_replacement_char = '?';
	m_t_wide_char_proc.n_format_digit_num = 4;
	strcpy(m_t_wide_char_proc.p_s_format_string, "x%04x");

	m_t_overflow_proc.b_enable = false;
	m_t_overflow_proc.n_threshold = 0;
	m_t_overflow_proc.n_mode = wchar_Translate;
	m_t_overflow_proc.n_replacement_char = '?';
	m_t_overflow_proc.n_format_digit_num = 4;
	strcpy(m_t_overflow_proc.p_s_format_string, "x%04x");
#endif // __UNICODE_FILE_ENABLE_CHAR_PROCESSING
}

/*
 *	CUnicodeFile::~CUnicodeFile()
 *		- default destructor (close current file if necessary)
 */
CUnicodeFile::~CUnicodeFile()
{
	__FuncGuard("CUnicodeFile::~CUnicodeFile");

	Close();
	// call close first, we may need to flush

	Delete_FileBuffer();
}

/*
 *	int CUnicodeFile::n_GetError()
 *		- returns error code, corresponding with one of set bits of error flag and clears
 *		  the bit (one of error_Unicode (unicode en/decoding error), error_Threshold (error
 *		  generated by threshold unit with function set to wchar_Fail), error_Overflow
 *		  (overflow occured while the overflow threshold unit was disabled), error_IO
 *		  (generic input-output error) or error_InvalidValue (invalid value was used as
 *		  function parameter - signifies either programming error or invalid data))
 *		- in case no bits were set, returns error_NoError
 */
int CUnicodeFile::n_GetError()
{
	__FuncGuard("CUnicodeFile::n_GetError");

	for(int i = 0; i < error_InvalidValue; ++ i) {
		if(m_n_error_state & (1 << i)) {
			m_n_error_state ^= 1 << i;
			return i + 1;
		}
	}
	_ASSERTE(!m_n_error_state);

	return error_NoError;
}

#ifdef __UNICODE_FILE_ENABLE_CHAR_PROCESSING

/*
 *	bool CUnicodeFile::EnableThreshold(int n_target, bool b_enable)
 *		- if b_enable is true, enables (or if b_enable is false, disables) threshold unit
 *		- by default both units are disabled
 *		- n_target specifies threshold unit, can be either thresh_User for user controlled
 *		  threshold unit or thresh_Overflow for overflow test unit
 *		- returns true in case valid unit was specified, otherwise returns false and raises
 *		  error_InvalidValue flag
 */
bool CUnicodeFile::EnableThreshold(int n_unit, bool b_enable)
{
	__FuncGuard("CUnicodeFile::EnableThreshold");

	if(n_unit == thresh_User) {
		m_t_wide_char_proc.b_enable = b_enable;
		return true;
	} else if(n_unit == thresh_Overflow) {
		m_t_overflow_proc.b_enable = b_enable;
		return true;
	} else {
		Raise(error_InvalidValue);
		return false;
	}
}

/*
 *	bool CUnicodeFile::SetThresholdValue(int n_target, unsigned int n_threshold)
 *		- sets value of threshold for selected unit to n_threshold
 *		- n_threshold can be in range 0 to 0x10ffff (or 1114111 in decimal)
 *		- default treshold value (for tresh_User unit) is 255
 *		- n_target specifies threshold unit, currently can be only used with n_target set
 *		  to thresh_User
 *		- returns true in case valid unit and threshold was specified, otherwise returns false
 *		  and raises error_InvalidValue flag
 */
bool CUnicodeFile::SetThresholdValue(int n_unit, unsigned int n_threshold)
{
	__FuncGuard("CUnicodeFile::SetThresholdValue");

	if(n_threshold > 0x10ffff) {
		Raise(error_InvalidValue);
		return false;
	}
	if(n_unit == thresh_User) {
		m_t_wide_char_proc.n_threshold = n_threshold;
		return true;
	} else if(n_unit == thresh_Overflow) {
		m_t_overflow_proc.n_threshold = n_threshold;
		return true;
	} else {
		Raise(error_InvalidValue);
		return false;
	}
}

/*
 *	bool CUnicodeFile::SetThresholdFunction(int n_target, int n_function)
 *		- sets function n_function to apply on character value in case threshold
 *		  test on unit n_target fails
 *		- n_target specifies threshold unit, can be either thresh_User for user controlled
 *		  threshold unit or thresh_Overflow for overflow test unit
 *		- n_function specifies function, is one of wchar_Replace (character is replaced
 *		  by some user-defined value), wchar_Translate (character is translated to escape
 *		  sequence in format '\x????' where '?' are hexadecimal digits. their number can
 *		  be set in range 2 to 8), wchar_Discard (character is discarded and the implementation
 *		  acts as if it was never read; if necessary / suitable, another character is read
 *		  to be returned)
 *		  or wchar_Fail (the function fails)
 *		- default function is wchar_Translate for both units
 *		- note in case escape sequence is set too short for thresh_User and character with
 *		  code too high to fit in is encountered, thresh_Overflow is triggered (in case
 *		  it's disabled, current operation fails with error_Overflow)
 *		- returns true in case valid unit and function was specified, otherwise returns false
 *		  and raises error_InvalidValue flag
 */
bool CUnicodeFile::SetThresholdFunction(int n_target, int n_function)
{
	__FuncGuard("CUnicodeFile::SetThresholdFunction");

	if(n_function < (int)wchar_Replace || n_function > (int)wchar_Fail) {
		Raise(error_InvalidValue);
		return false;
	}
	if(n_target == thresh_User) {
		m_t_wide_char_proc.n_mode = n_function;
		return true;
	} else if(n_target == thresh_Overflow) {
		m_t_overflow_proc.n_mode = n_function;
		return true;
	} else {
		Raise(error_InvalidValue);
		return false;
	}
}

/*
 *	bool CUnicodeFile::SetThresholdReplacementChar(int n_target, unsigned int n_character)
 *		- sets replacement char n_character for unit n_target
 *		- n_character can be in range 0 to 0x10ffff (or 1114111 in decimal)
 *		- default replacement char is '?' for both units
 *		- n_target specifies threshold unit, can be either thresh_User for user controlled
 *		  threshold unit or thresh_Overflow for overflow test unit
 *		- returns true in case valid unit and function was specified, otherwise returns false
 *		  and raises error_InvalidValue flag
 */
bool CUnicodeFile::SetThresholdReplacementChar(int n_target, unsigned int n_character)
{
	__FuncGuard("CUnicodeFile::SetThresholdReplacementChar");

	if(n_character > 0x10ffff) {
		Raise(error_InvalidValue);
		return false;
	}
	if(n_target == thresh_User) {
		m_t_wide_char_proc.n_replacement_char = n_character;
		return true;
	} else if(n_target == thresh_Overflow) {
		m_t_overflow_proc.n_replacement_char = n_character;
		return true;
	} else {
		Raise(error_InvalidValue);
		return false;
	}
}

/*
 *	bool CUnicodeFile::SetThresholdTranslationSize(int n_target, int n_size)
 *		- sets threshold translation size n_size (in hexadecimal digits) unit n_target
 *		- n_size can be in range 2 to 8 digits
 *		- default size is 4 digits (16 bits) for both units
 *		- n_target specifies threshold unit, can be either thresh_User for user controlled
 *		  threshold unit or thresh_Overflow for overflow test unit
 *		- returns true in case valid unit and size was specified, otherwise returns false
 *		  and raises error_InvalidValue flag
 */
bool CUnicodeFile::SetThresholdTranslationSize(int n_target, int n_digit_num)
{
	__FuncGuard("CUnicodeFile::SetThresholdTranslationSize");

	if(n_digit_num < 2 || n_digit_num > 8) {
		Raise(error_InvalidValue);
		return false;
	}
	if(n_target == thresh_User) {
		m_t_wide_char_proc.n_format_digit_num = n_digit_num;
		sprintf(m_t_wide_char_proc.p_s_format_string, "x%%0%dx", n_digit_num);
		return true;
	} else if(n_target == thresh_Overflow) {
		m_t_overflow_proc.n_format_digit_num = n_digit_num;
		sprintf(m_t_overflow_proc.p_s_format_string, "x%%0%dx", n_digit_num);
		return true;
	} else {
		Raise(error_InvalidValue);
		return false;
	}
}

/*
 *	bool CUnicodeFile::b_ThresholdEnabled(int n_target)
 *		- returns true in case threshold unit specified by n_target is enabled, otherwise false
 *		- n_target specifies threshold unit, can be either thresh_User for user controlled
 *		  threshold unit or thresh_Overflow for overflow test unit
 *		- in case invalid unit was specified returns always false and raises error_InvalidValue
 *		  flag
 */
bool CUnicodeFile::b_ThresholdEnabled(int n_target)
{
	__FuncGuard("CUnicodeFile::b_ThresholdEnabled");

	if(n_target == thresh_User)
		return m_t_wide_char_proc.b_enable;
	else if(n_target == thresh_Overflow)
		return m_t_overflow_proc.b_enable;
	Raise(error_InvalidValue);
	return false;
}

/*
 *	unsigned int CUnicodeFile::n_GetThresholdValue(int n_target)
 *		- returns threshold value of unit specified by n_target
 *		- n_target specifies threshold unit, can be either thresh_User for user controlled
 *		  threshold unit or thresh_Overflow for overflow test unit
 *		- in case invalid unit was specified returns always 0 and raises error_InvalidValue
 *		  flag
 */
unsigned int CUnicodeFile::n_GetThresholdValue(int n_target)
{
	__FuncGuard("CUnicodeFile::n_GetThresholdValue");

	if(n_target == thresh_User)
		return m_t_wide_char_proc.n_threshold;
	else if(n_target == thresh_Overflow)
		return m_t_overflow_proc.n_threshold;
	Raise(error_InvalidValue);
	return 0;
}

/*
 *	int CUnicodeFile::n_GetThresholdFunction(int n_target)
 *		- returns function set for threshold unit specified by n_target
 *		- n_target specifies threshold unit, can be either thresh_User for user controlled
 *		  threshold unit or thresh_Overflow for overflow test unit
 *		- in case invalid unit was specified returns always 0 and raises error_InvalidValue
 *		  flag
 */
int CUnicodeFile::n_GetThresholdFunction(int n_target)
{
	__FuncGuard("CUnicodeFile::n_GetThresholdFunction");

	if(n_target == thresh_User)
		return m_t_wide_char_proc.n_mode;
	else if(n_target == thresh_Overflow)
		return m_t_overflow_proc.n_mode;
	Raise(error_InvalidValue);
	return 0;
}

/*
 *	unsigned int CUnicodeFile::n_GetThresholdReplacementChar(int n_target)
 *		- returns replacement char set for threshold unit specified by n_target
 *		- n_target specifies threshold unit, can be either thresh_User for user controlled
 *		  threshold unit or thresh_Overflow for overflow test unit
 *		- in case invalid unit was specified returns always 0 and raises error_InvalidValue
 *		  flag
 */
unsigned int CUnicodeFile::n_GetThresholdReplacementChar(int n_target)
{
	__FuncGuard("CUnicodeFile::n_GetThresholdReplacementChar");

	if(n_target == thresh_User)
		return m_t_wide_char_proc.n_replacement_char;
	else if(n_target == thresh_Overflow)
		return m_t_overflow_proc.n_replacement_char;
	Raise(error_InvalidValue);
	return 0;
}

/*
 *	int CUnicodeFile::n_GetThresholdTranslationSize(int n_target)
 *		- returns replacement char set for threshold unit specified by n_target
 *		- n_target specifies threshold unit, can be either thresh_User for user controlled
 *		  threshold unit or thresh_Overflow for overflow test unit
 *		- in case invalid unit was specified returns always 0 and raises error_InvalidValue
 *		  flag
 */
int CUnicodeFile::n_GetThresholdTranslationSize(int n_target)
{
	__FuncGuard("CUnicodeFile::n_GetThresholdTranslationSize");

	if(n_target == thresh_User)
		return m_t_wide_char_proc.n_format_digit_num;
	else if(n_target == thresh_Overflow)
		return m_t_overflow_proc.n_format_digit_num;
	Raise(error_InvalidValue);
	return 0;
}

#endif // __UNICODE_FILE_ENABLE_CHAR_PROCESSING

/*
 *	bool CUnicodeFile::Alloc_FileBuffer()
 *		- make sure buffer is allocated, best would be 16kB, but we can go as far as 512B
 *		- do not realloc in case buffer is allocated and large enough
 *		- returns true on success, returns false and raises error_NoMemory flag on failure
 */
bool CUnicodeFile::Alloc_FileBuffer()
{
	__FuncGuard("CUnicodeFile::Alloc_FileBuffer");

	if(m_n_file_buffer_size < 16384)
		Delete_FileBuffer();
	if(!m_p_file_buffer) {
		_ASSERTE(!m_p_file_buffer_cur && !m_p_file_buffer_end);
		for(m_n_file_buffer_size = 16384;; m_n_file_buffer_size /= 2) {
			if(m_n_file_buffer_size < 512) {
				_ASSERTE(!m_p_file_buffer);
				Raise(error_NoMemory);
				return false;
			}
			if((m_p_file_buffer = new(std::nothrow) char[m_n_file_buffer_size])) {
				m_p_file_buffer_cur = m_p_file_buffer;
				m_p_file_buffer_end = m_p_file_buffer + m_n_file_buffer_size;
				break;
			}
		}
	}
	// make sure buffer is allocated, best would be 16kB, but we can go as far as 512B
	// otherwise it would be rather slowdown and algorithm would likely crash anyway
	// with <512 bytes of memory left

	return true;
}

/*
 *	void CUnicodeFile::Delete_FileBuffer()
 *		- delete file buffer if allocated
 */
void CUnicodeFile::Delete_FileBuffer()
{
	__FuncGuard("CUnicodeFile::Delete_FileBuffer");

	if(m_p_file_buffer) {
		delete[] m_p_file_buffer;
		m_p_file_buffer = 0;
		m_p_file_buffer_cur = 0;
		m_p_file_buffer_end = 0;
	}
}

/*
 *	bool CUnicodeFile::Find_BOM()
 *		- attempt to find BOM on current position in the file
 *		- add length of BOM to m_n_bof_position
 *		- set m_n_encoding to BOM code if found. otherwise keep previos value
 */
bool CUnicodeFile::Find_BOM()
{
	__FuncGuard("CUnicodeFile::Find_BOM");

	const char p_signature_list[][6] = {
		{(char)4, (char)code_Unsupported, (char)0x00, (char)0x00, (char)0xfe, (char)0xff}, // UTF_32_BE
		{(char)4, (char)code_Unsupported, (char)0xff, (char)0xfe, (char)0x00, (char)0x00}, // UTF_32_LE
		{(char)2, (char)code_Unsupported, (char)0xfe, (char)0xff, 0, 0}, // UTF_16_BE
		{(char)2, (char)code_UTF_16_LE  , (char)0xff, (char)0xfe, 0, 0},
		{(char)3, (char)code_UTF_8	    , (char)0xef, (char)0xbb, (char)0xbf, 0}
	};
	char p_tmp[4];

	_ASSERTE(m_p_file);
	size_t n = fread(&p_tmp, sizeof(char), 4, m_p_file);

	for(size_t i = 0; i < sizeof(p_signature_list) / sizeof(p_signature_list[0]); ++ i) {
		if(n > unsigned(p_signature_list[i][0]) &&
		   !memcmp(p_tmp, &p_signature_list[i][2], p_signature_list[i][0] * sizeof(char))) {
			m_n_bof_position += p_signature_list[i][0];
			m_n_encoding = (int)p_signature_list[i][1];
			return true;
		}
	}

	return false;
}

/*
 *	bool CUnicodeFile::Open_Read(const char *p_s_filename, bool b_binary = true,
 *		int n_default_encoding = code_ASCII);
 *		- open file p_s_filename for reading. b_binary specifies wheter the file
 *		  is to be used for reading in binary mode, n_default_encoding is used
 *		  in case no BOM was found
 *		- note unicode can contain such character codes that can have value of control
 *		  characters so it can be necessary to open files in binary mode, even for text
 *		  i/o (depends on your c standard library implementation)
 *		- returns true on success, false on failure (raises error_InvalidValue flag in case
 *		  invalid encoding was supplied, error_NoMemory flag in case cache buffer was not
 *		  allocated and allocation failed or error_IO in case fopen() failed or writing BOM
 *		  failed)
 *		- returns false and raises error_InvalidOperation flag if file is opened already
 *		  (don't want to hide Close() return value which may signify failure of delayed
 *		  write (flush) ... which is good thing to know)
 *		- note contents of n_default_encoding are checked only in case they were used
 */
bool CUnicodeFile::Open_Read(const char *p_s_filename, bool b_binary, int n_default_encoding)
{
	__FuncGuard("CUnicodeFile::Open_Read");

	if(m_p_file) {
		Raise(error_InvalidOperation);
		return false;
	}
	// return false in case file is opened

	if(!Alloc_FileBuffer())
		return false;
	m_p_file_buffer_cur = m_p_file_buffer_end = m_p_file_buffer + m_n_file_buffer_size;
#ifdef __UNICODE_FILE_ENABLE_CHAR_PROCESSING
	m_n_string_buffer_used = 0;
#endif // __UNICODE_FILE_ENABLE_CHAR_PROCESSING
	// make sure our buffer is allocated

#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
	if(fopen_s(&m_p_file, p_s_filename, (b_binary)? "rb" : "r")) {
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
	if(!(m_p_file = fopen(p_s_filename, (b_binary)? "rb" : "r"))) {
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		Raise(error_IO);
		return false;
	}
	m_b_foreign_file = false;
	m_b_writing = false;
	m_b_binary = b_binary;
	// can't open file

	m_n_eof_position = -1;
	m_n_bof_position = 0;
	// default file constraints

	m_n_encoding = n_default_encoding;
	// use default encoding

	m_b_have_bom = Find_BOM();
	// attempt to find BOM or keep default encoding; m_n_bof_position now contains BOM length

	if((m_n_encoding != code_UTF_8 && m_n_encoding != code_UTF_16_LE) ||
	   fseek(m_p_file, m_n_bof_position, SEEK_SET)) {
		fclose(m_p_file);
		m_p_file = 0;
		Raise(error_IO);
		return false;
	}
	// unsupported format / io error

	return true;
}

/*
 *	bool CUnicodeFile::Open_Read(FILE *p_fr, int n_eof_position = -1, bool b_binary = true,
 *		int n_default_encoding = code_ASCII);
 *		- assign foreign file opened for reading. b_binary specifies wheter the file
 *		  is to be used for reading in binary mode, n_default_encoding is used
 *		  in case no BOM was found
 *		- note unicode can contain such character codes that can have value of control
 *		  characters so it can be necessary to open files in binary mode, even for text
 *		  i/o (depends on your c standard library implementation)
 *		- n_eof_position specifies position of the end of the file (-1 means physical
 *		  end of the file; can be used for reading packed files such as files contained
 *		  in not-compressed archives or resource-sections)
 *		- current file position will be interpreted as beginning of file and it's assured
 *		  no data from preceding parts of the file will be ever read
 *		- returns true on success, false on failure (raises error_InvalidValue flag in case
 *		  invalid encoding was supplied or null pointer to foreign file was suppiled,
 *		  error_NoMemory flag in case cache buffer was not allocated and allocation failed
 *		  or error_IO in case writing BOM failed)
 *		- returns false and raises error_InvalidOperation flag if file is opened already
 *		  (don't want to hide Close() return value which may signify failure of delayed
 *		  write (flush) ... which is good thing to know)
 *		- note contents of n_default_encoding are checked only in case they were used
 */
bool CUnicodeFile::Open_Read(FILE *p_fr, int n_eof_position, bool b_binary, int n_default_encoding)
{
	__FuncGuard("CUnicodeFile::Open_Read");

	if(m_p_file) {
		Raise(error_InvalidOperation);
		return false;
	}
	// return false in case file is opened

	if(!Alloc_FileBuffer())
		return false;
	m_p_file_buffer_cur = m_p_file_buffer_end = m_p_file_buffer + m_n_file_buffer_size;
#ifdef __UNICODE_FILE_ENABLE_CHAR_PROCESSING
	m_n_string_buffer_used = 0;
#endif // __UNICODE_FILE_ENABLE_CHAR_PROCESSING
	// make sure our buffer is allocated

	if(!(m_p_file = p_fr)) {
		Raise(error_InvalidValue);
		return false;
	}
	m_b_foreign_file = true;
	m_b_writing = false;
	m_b_binary = b_binary;
	// can't open file

	m_n_eof_position = n_eof_position;
	if((m_n_bof_position = ftell(m_p_file)) < 0) {
		m_p_file = 0;
		Raise(error_IO);
		return false;
	}
	// default file constraints

	m_n_encoding = n_default_encoding;
	// use default encoding

	m_b_have_bom = Find_BOM();
	// attempt to find BOM or keep default encoding; m_n_bof_position now contains BOM length

	if((m_n_encoding != code_UTF_8 && m_n_encoding != code_UTF_16_LE) ||
	   fseek(m_p_file, m_n_bof_position, SEEK_SET)) {
		m_p_file = 0;
		Raise(error_IO);
		return false;
	}
	// unsupported format / io error

	return true;
}

/*
 *	bool CUnicodeFile::Open_Write(const char *p_s_filename, bool b_binary = true,
 *		int n_encoding = code_UTF_8, bool b_write_BOM = false);
 *		- open file p_s_filename for writing. b_binary specifies wheter the file
 *		  is to be used for writing in binary mode, n_encoding specifies encoding
 *		  to be used, b_write_BOM specifies wheter to write BOM to the beginning
 *		  of the file or not
 *		- note unicode can contain such character codes that can have value of control
 *		  characters so it can be necessary to open files in binary mode, even for text
 *		  i/o (depends on your c standard library implementation)
 *		- returns true on success, false on failure (raises error_InvalidValue flag in case
 *		  invalid encoding was supplied, error_NoMemory flag in case cache buffer was not
 *		  allocated and allocation failed or error_IO in case fopen() failed or writing BOM
 *		  failed)
 *		- returns false and raises error_InvalidOperation falg if file is opened already
 *		  (don't want to hide Close() return value which may signify failure of delayed
 *		  write (flush) ... which is good thing to know)
 *		- note contents of n_default_encoding are checked only in case they were used
 *		- note no BOM is written for code_ASCII even if required (but no error generated)
 */
bool CUnicodeFile::Open_Write(const char *p_s_filename, bool b_binary,
	int n_encoding, bool b_write_BOM)
{
	__FuncGuard("CUnicodeFile::Open_Write");

	if(m_p_file) {
		Raise(error_InvalidOperation);
		return false;
	}
	// return false in case file is opened

	if(n_encoding != code_UTF_8 && n_encoding != code_UTF_16_LE && n_encoding != code_ASCII) {
		Raise(error_InvalidValue);
		return false;
	}
	// unsupported format

	if(!Alloc_FileBuffer())
		return false;
	m_p_file_buffer_cur = m_p_file_buffer;
	m_p_file_buffer_end = m_p_file_buffer + m_n_file_buffer_size;
#ifdef __UNICODE_FILE_ENABLE_CHAR_PROCESSING
	m_n_string_buffer_used = 0;
#endif // __UNICODE_FILE_ENABLE_CHAR_PROCESSING
	// make sure our buffer is allocated

#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
	if(fopen_s(&m_p_file, p_s_filename, (b_binary)? "wb" : "w")) {
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
	if(!(m_p_file = fopen(p_s_filename, (b_binary)? "wb" : "w"))) {
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		Raise(error_IO);
		return false;
	}
	m_b_foreign_file = false;
	m_b_writing = true;
	m_b_binary = b_binary;
	// can't open file

	m_b_have_bom = b_write_BOM;
	if(b_write_BOM) {
		if(n_encoding == code_UTF_8) {
			const uint8_t p_encoding_mark[] = {0xef, 0xbb, 0xbf};
			const unsigned int n_mark_length = sizeof(p_encoding_mark) / sizeof(p_encoding_mark[0]);
			if(fwrite(p_encoding_mark, sizeof(uint8_t),
			   n_mark_length, m_p_file) != n_mark_length) {
				fclose(m_p_file);
				m_p_file = 0;
				Raise(error_IO);
				return false;
			}
		} else if(n_encoding == code_UTF_16_LE) {
			const uint8_t p_encoding_mark[] = {0xff, 0xfe};
			const unsigned int n_mark_length = sizeof(p_encoding_mark) / sizeof(p_encoding_mark[0]);
			if(fwrite(p_encoding_mark, sizeof(uint8_t),
			   n_mark_length, m_p_file) != n_mark_length) {
				fclose(m_p_file);
				m_p_file = 0;
				Raise(error_IO);
				return false;
			}
		}
	}
	// attempt to write BOM right now if required

	m_n_eof_position = -1;
	if((m_n_bof_position = ftell(m_p_file)) < 0) {
		fclose(m_p_file);
		m_p_file = 0;
		Raise(error_IO);
		return false;
	}
	// default file constraints

	m_n_encoding = n_encoding;
	// use given encoding

	return true;
}

/*
 *	bool CUnicodeFile::Open_Write(FILE *p_fw, bool b_binary = true,
 *		int n_encoding = code_UTF_8, bool b_write_BOM = false);
 *		- assign foreign file opened for writing. b_binary specifies wheter the file
 *		  is to be used for writing in binary mode, n_encoding specifies encoding
 *		  to be used, b_write_BOM specifies wheter to write BOM to the beginning
 *		  of the file or not
 *		- note unicode can contain such character codes that can have value of control
 *		  characters so it can be necessary to open files in binary mode, even for text
 *		  i/o (depends on your c standard library implementation)
 *		- current file position will be interpreted as beginning of file and it's assured
 *		  no data from preceding parts of the file will be ever read
 *		- as it is write mode, end of file is not constrained
 *		- returns true on success, false on failure (raises error_InvalidValue flag in case
 *		  invalid encoding was supplied or null pointer to foreign file was suppiled,
 *		  error_NoMemory flag in case cache buffer was not allocated and allocation failed
 *		  or error_IO in case writing BOM failed)
 *		- returns false and raises error_InvalidOperation falg if file is opened already
 *		  (don't want to hide Close() return value which may signify failure of delayed
 *		  write (flush) ... which is good thing to know)
 *		- note contents of n_default_encoding are checked only in case they were used
 *		- note no BOM is written for code_ASCII even if required (but no error generated)
 */
bool CUnicodeFile::Open_Write(FILE *p_fw, bool b_binary, int n_encoding, bool b_write_BOM)
{
	__FuncGuard("CUnicodeFile::Open_Write");

	if(m_p_file) {
		Raise(error_InvalidOperation);
		return false;
	}
	// return false in case file is opened

	if(n_encoding != code_UTF_8 && n_encoding != code_UTF_16_LE && n_encoding != code_ASCII) {
		Raise(error_InvalidValue);
		return false;
	}
	// unsupported format

	if(!Alloc_FileBuffer())
		return false;
	m_p_file_buffer_cur = m_p_file_buffer;
	m_p_file_buffer_end = m_p_file_buffer + m_n_file_buffer_size;
#ifdef __UNICODE_FILE_ENABLE_CHAR_PROCESSING
	m_n_string_buffer_used = 0;
#endif // __UNICODE_FILE_ENABLE_CHAR_PROCESSING
	// make sure our buffer is allocated

	if(!(m_p_file = p_fw)) {
		Raise(error_InvalidValue);
		return false;
	}
	m_b_foreign_file = true;
	m_b_writing = true;
	m_b_binary = b_binary;
	// can't open file

	m_b_have_bom = b_write_BOM;
	if(b_write_BOM) {
		if(n_encoding == code_UTF_8) {
			const uint8_t p_encoding_mark[] = {0xef, 0xbb, 0xbf};
			const unsigned int n_mark_length = sizeof(p_encoding_mark) / sizeof(p_encoding_mark[0]);
			if(fwrite(p_encoding_mark, sizeof(uint8_t),
			   n_mark_length, m_p_file) != n_mark_length) {
				m_p_file = 0;
				Raise(error_IO);
				return false;
			}
		} else if(n_encoding == code_UTF_16_LE) {
			const uint8_t p_encoding_mark[] = {0xff, 0xfe};
			const unsigned int n_mark_length = sizeof(p_encoding_mark) / sizeof(p_encoding_mark[0]);
			if(fwrite(p_encoding_mark, sizeof(uint8_t),
			   n_mark_length, m_p_file) != n_mark_length) {
				m_p_file = 0;
				Raise(error_IO);
				return false;
			}
		}
	}
	// attempt to write BOM right now if required

	m_n_eof_position = -1;
	if((m_n_bof_position = ftell(m_p_file)) < 0) {
		m_p_file = 0;
		Raise(error_IO);
		return false;
	}
	// default file constraints

	m_n_encoding = n_encoding;
	// use given encoding

	return true;
}

/*
 *	bool CUnicodeFile::Close()
 *		- returns true in case some file was opened, was successfuly closed and (in case
 *		  cache buffer was not empty) flush was successfull; otherwise false
 *		- in case foreign file was opened, inner state of CUnicodeFile is changed only
 *		  and fclose() is not called
 *		- in case no file was opened, raises error_InvalidOperation flag
 *		- in case flush was not successful, error_IO flag is raised
 */
bool CUnicodeFile::Close()
{
	__FuncGuard("CUnicodeFile::Close");

	if(m_p_file) {
		bool b_result = true;
		if(m_b_writing && !Flush())
			b_result = false;
		// flush the rest of buffered data to disk if necessary

		if(!m_b_foreign_file && fclose(m_p_file))
			b_result = false;
		m_p_file = 0;
		// set pointer to 0, fclose if necessary

		return b_result;
	}
	Raise(error_InvalidOperation);
	return false;
}

/*
 *	bool CUnicodeFile::Flush()
 *		- in read mode clears translated character cache (contains up to 10 characters
 *		  of translated wide character hexadecimal code)
 *		- in write mode writes output buffer to file and clears output buffer
 *		- in read mode returns true in case file was opened
 *		- in write mode returns true in case file was opened and there was either no data
 *		  to flush or flush was successful (in case it was not, raises error_IO flag)
 *		- returns false and raises error_InvalidOperation flag in case no file was opened
 */
bool CUnicodeFile::Flush()
{
	__FuncGuard("CUnicodeFile::Flush");

	if(!m_p_file) {
		Raise(error_InvalidOperation);
		return false;
	}
	if(m_b_writing) {
		if(m_p_file_buffer_cur > m_p_file_buffer) {
			size_t n_byte_num = m_p_file_buffer_cur - m_p_file_buffer;
			m_p_file_buffer_cur = m_p_file_buffer;
			if(fwrite(m_p_file_buffer,
			   sizeof(uint8_t), n_byte_num, m_p_file) == n_byte_num)
				return true;
			Raise(error_IO);
			return false;
		}
		return true;
	} else {
#ifdef __UNICODE_FILE_ENABLE_CHAR_PROCESSING
		m_n_string_buffer_used = 0; // clear this cache
#endif // __UNICODE_FILE_ENABLE_CHAR_PROCESSING
		return true;
	}
}

/*
 *	bool CUnicodeFile::b_Opened() const
 *		- returns true in case some file is opened, otherwise false
 *		- does not raise any error flag
 */
bool CUnicodeFile::b_Opened() const
{
	__FuncGuard("CUnicodeFile::b_Opened");

	return m_p_file != 0;
}

/*
 *	bool CUnicodeFile::b_Contains_BOM() const
 *		- returns true in case current file contains BOM (byte-order mark)
 *		- always returns false in case no file was opened
 *		- does not raise any error flag
 */
bool CUnicodeFile::b_Contains_BOM() const
{
	__FuncGuard("CUnicodeFile::b_Contains_BOM");

	return m_p_file != 0 && m_b_have_bom;
}

/*
 *	int CUnicodeFile::n_Encoding() const
 *		- returns current file encoding (code_ASCII, code_UTF_8, code_UTF_16_LE or
 *		  code_Unsupported)
 *		- always returns code_Unsupported in case no file was opened
 *		- does not raise any error flag
 */
int CUnicodeFile::n_Encoding() const
{
	__FuncGuard("CUnicodeFile::n_Encoding");

	if(!m_p_file) {
		//Raise(error_InvalidOperation);
		return code_Unsupported;
	}
	return m_n_encoding;
}

/*
 *	bool CUnicodeFile::SetEncoding(int n_encoding)
 *		- sets encoding to n_encoding (one of code_ASCII, code_UTF_8 or code_UTF_16_LE)
 *		- can be set only in read mode and in case BOM was not found only (scenario of XML
 *		  file where encoding is determined by reading first line of file which is always
 *		  ascii)
 *		- returns true on success, returns false on failure. raises error_InvalidOperation
 *		  flag in case no file was opened, it was opened for writing or there was BOM found,
 *		  raises error_InvalidValue flag in case invalid encoding is supplied in n_encoding
 */
bool CUnicodeFile::SetEncoding(int n_encoding)
{
	__FuncGuard("CUnicodeFile::SetEncoding");

	if(!m_p_file || m_b_have_bom || m_b_writing) {
		Raise(error_InvalidOperation);
		return false;
	}
	if(n_encoding == code_ASCII || n_encoding == code_UTF_8 || n_encoding == code_UTF_16_LE) {
		m_n_encoding = n_encoding;
		return true;
	}
	Raise(error_InvalidValue);
	return false;
}

/*
 *	bool CUnicodeFile::Seek(int n_offset, int n_mode)
 *		- set file to certain position
 *		- n_offset is offset in bytes (note UTF uses codes with variable length)
 *		- n_mode defines where to start (one of seek_Relative, seek_Beginning, seek_End)
 *		- n_offset is offset in bytes (careful to use it - setting file pointer to the
 *		  middle of multiple-byte character would result in it's bad decoding (after a few
 *		  more failures decoder should recover; amount depends on encoding and file position))
 *		- returns true on success, false on failure (raises error_InvalidOperation flag in case
 *		  no file was opened, error_InvalidValue flag on invalid offset, error_InvalidOperation
 *		  flag on invalid mode or error_IO flag if fseek() failed)
 *		- note seeking from seek_Beginning effectively skips BOF mark in case it's present
 *		- note seeking clears buffer for text-input escape sequences which couldn't
 *		  be returned in one piece in last call to one of text input functions
 */
bool CUnicodeFile::Seek(int n_offset, int n_mode)
{
	__FuncGuard("CUnicodeFile::Seek");

	_ASSERTE(seek_Beginning == SEEK_SET || seek_Relative == SEEK_CUR || seek_End == SEEK_END);

	if(!m_p_file) {
		Raise(error_InvalidOperation);
		return false;
	}

	if(n_mode == seek_Beginning) {
		n_offset += m_n_bof_position;
		// add offset, caused by BOM / previous contents of foreign file

		if(n_offset < m_n_bof_position || (m_n_eof_position != -1 &&
		   n_offset >= m_n_eof_position)) {
			Raise(error_InvalidValue);
			return false;
		}
		// out-of-bounds check

		// n_offset should be equal to absolute offset from the beginning of the file
	} else if(n_mode == seek_End) {
		if(n_offset > 0 || (m_n_eof_position != -1 &&
		   n_offset < m_n_bof_position - m_n_eof_position)) {
			Raise(error_InvalidValue);
			return false;
		}
		// out-of-bounds check

		// n_offset should be equal to absolute offset from the end of the file
	} else if(n_mode == seek_Relative) {
		int n_position;
		if((n_position = n_Tell()) < 0) {
			Raise(error_InvalidValue);
			return false;
		}
		n_offset += n_position;
		// calc absolute offset from the beginning (measured from the BOM mark)

		n_mode = seek_Beginning;

		n_offset += m_n_bof_position;
		// add offset, caused by BOM / previous contents of foreign file

		if(n_offset < m_n_bof_position || (m_n_eof_position != -1 &&
		   n_offset >= m_n_eof_position)) {
			Raise(error_InvalidValue);
			return false;
		}
		// out-of-bounds check

		// n_offset should be equal to absolute offset from the beginning of the file
	} else {
		Raise(error_InvalidOperation);
		return false;
	}

	if(m_b_writing) {
		/*if(n_mode == seek_Relative)
			n_offset -= m_p_file_buffer_cur - m_p_file_buffer;*/
		// recalc prior to flush as it's going to set the buffer empty
		_ASSERTE(n_mode != seek_Relative); // shouldn't happen now

		if(!Flush())
			return false;
		// t_odo - flush write buffer, have to modify offset once more in case n_mode == seek_Relative
	} else {
		m_p_file_buffer_cur = m_p_file_buffer_end;
		// invalidate contents of buffer so it has to be read again
		// (in case we were writing, Flush() has done the job already)
	}

	if(!fseek(m_p_file, n_offset, n_mode))
		return true;
	// fseek (fails with nonzero)

	Raise(error_IO);
	return false;
}

/*
 *	int CUnicodeFile::n_Tell()
 *		- return position in bytes file pointer points to
 *		  (note UTF uses codes with variable length)
 *		- position is measurred from the end of BOM mark if present and from the position
 *		  file pointer pointed to when passed to Open_?(FILE *, ...) in case that was how
 *		  file pointer was acquired (i.e. after Open_?(...) call to n_Tell() always returns 0)
 *		- returns -1 in case no file is opened (raises error_InvalidOperation flag) or i/o error
 *		  ocurred (raises error_IO flag)
 */
int CUnicodeFile::n_Tell()
{
	__FuncGuard("CUnicodeFile::n_Tell");

	if(!m_p_file) {
		Raise(error_InvalidOperation);
		return -1;
	}
	int n_ftell_result;
	if((n_ftell_result = ftell(m_p_file)) == -1) {
		Raise(error_IO);
		return -1;
	}
	return int(n_ftell_result - m_n_bof_position - (m_p_file_buffer_end - m_p_file_buffer_cur));
	// subtract offset, caused by fread() when pre-caching file data into buffer
}

/*
 *	int CUnicodeFile::n_Flood_Buffer()
 *		- called when more data is necessary to be read into the buffer
 *		- never reads past end of file (in case m_n_eof_position != -1)
 *		- in case buffer still contains some data, copies them to front
 *		  and reads in the rest of the buffer
 *		- return number of bytes read or -1 in case of i/o error
 */
int CUnicodeFile::n_Flood_Buffer()
{
	__FuncGuard("CUnicodeFile::n_Flood_Buffer");

	_ASSERTE(m_p_file && !m_b_writing);

	if(m_p_file_buffer_cur < m_p_file_buffer_end) {
		if(m_p_file_buffer_cur == m_p_file_buffer)
			return 0;
		// there is no free space in buffer to read to

		memcpy(m_p_file_buffer, m_p_file_buffer_cur,
			(m_p_file_buffer_end - m_p_file_buffer_cur) * sizeof(uint8_t));
		m_p_file_buffer_end -= m_p_file_buffer_cur - m_p_file_buffer;
		m_p_file_buffer_cur = m_p_file_buffer;
	} else /*if(m_p_file_buffer_cur == m_p_file_buffer_end)*/ {
		_ASSERTE(m_p_file_buffer_cur == m_p_file_buffer_end);
		m_p_file_buffer_cur = m_p_file_buffer_end = m_p_file_buffer;
	}
	// copy still valid data to the beginning of the buffer,
	// make sure m_p_file_buffer_cur == m_p_file_buffer

	if(m_p_file_buffer_end < m_p_file_buffer + m_n_file_buffer_size) {
		size_t n_bytes_to_read = m_n_file_buffer_size - (m_p_file_buffer_end - m_p_file_buffer_cur);
		// rest of free space in the buffer

		if(m_n_eof_position != -1) {
			int n_cur_position;
			if((n_cur_position = ftell(m_p_file)) < 0)
				return -1;
			if(unsigned(m_n_eof_position) < n_bytes_to_read + n_cur_position)
				n_bytes_to_read = m_n_eof_position - n_cur_position;
		}
		// in case we're reading foreign file, we don't want to read past the end file section

		size_t n_result;
		m_p_file_buffer_end += (n_result = fread(m_p_file_buffer_end,
			sizeof(uint8_t), n_bytes_to_read, m_p_file));
		// read data into the rest of the buffer

		return int(n_result);
	}

	return 0;
}

/*
 *	bool CUnicodeFile::ReadByte(uint8_t &r_n_byte)
 *		- read a single byte from the file (assumes file is opened for reading)
 *		- transparently operates with the file buffer
 *		- returns true in case the byte was read or false in case it wasn't
 */
bool CUnicodeFile::ReadByte(uint8_t &r_n_byte)
{
	__FuncGuard("CUnicodeFile::ReadByte");

	_ASSERTE(m_p_file && !m_b_writing);

	if(m_p_file_buffer_cur < m_p_file_buffer_end) {
		r_n_byte = *m_p_file_buffer_cur ++;
		// we have a byte in the buffer ... no need for reading
		return true;
	} else {
		if(n_Flood_Buffer() > 0) {
			r_n_byte = *m_p_file_buffer_cur ++;
			return true;
		}
		// we need to read in at least a single byte
		return false;
	}
}

/*
 *	bool CUnicodeFile::ReadMBS(uint8_t *p_dest, int n_size)
 *		- read n_size bytes from the file (assumes file is opened for reading)
 *		- transparently operates with the file buffer
 *		- raises error_Unicode in case there were some bytes, but not ebough of them
 *		- returns true in case all the data wes read or false in case they wasn't
 */
bool CUnicodeFile::ReadMBS(uint8_t *p_dest, int n_size)
{
	__FuncGuard("CUnicodeFile::ReadMBS");

	_ASSERTE(m_p_file && !m_b_writing);

	if(m_p_file_buffer_cur + n_size <= m_p_file_buffer_end) {
		memcpy(p_dest, m_p_file_buffer_cur, n_size * sizeof(uint8_t));
		m_p_file_buffer_cur += n_size;
		// we have a byte in the buffer ... no need for reading
		return true;
	} else {
		size_t r = m_p_file_buffer_end - m_p_file_buffer_cur;
		while(n_size) {
			if(n_Flood_Buffer() <= 0) {
				if(r)
					Raise(error_Unicode); // raise error, this is used to read multibyte sequences
				return false;
			}
			size_t n_space = m_p_file_buffer_end - m_p_file_buffer_cur;
			if(n_space > unsigned(n_size)) {
				n_space = n_size;
				memcpy(p_dest, m_p_file_buffer_cur, n_space * sizeof(uint8_t));
				m_p_file_buffer_cur += n_space;
				return true;
			}
			memcpy(p_dest, m_p_file_buffer_cur, n_space * sizeof(uint8_t));
			m_p_file_buffer_cur += n_space;
			n_size -= int(n_space); // n_space = n_size, always
			r += n_space;
		}
		// we need to read something
		return true;
	}
}

/*
 *	bool CUnicodeFile::WriteByte(uint8_t n_byte)
 *		- writes a single byte n_byte
 *		- transparently works with the file buffer
 *		- returns true on success, false on failure
 */
bool CUnicodeFile::WriteByte(uint8_t n_byte)
{
	__FuncGuard("CUnicodeFile::WriteByte");

	_ASSERTE(m_p_file && m_b_writing);

	if(m_p_file_buffer_cur < m_p_file_buffer_end) {
		*m_p_file_buffer_cur ++ = n_byte;
		return true;
	} else {
		if(!Flush())
			return false;
		_ASSERTE(m_p_file_buffer_cur == m_p_file_buffer);
		*m_p_file_buffer_cur ++ = n_byte;
		return true;
	}
}

/*
 *	bool CUnicodeFile::GetLine(char *p_dest, int n_max_length)
 *		- text mode i/o. return string, containing characters up to 0xff
 *		- characters above 0xff are written in their hexadecimal form (ie. \x??????),
 *		  backslash characters '\' are duplicated "\\"; can be disabled by EnableWideChars()
 *		- newline '\n' character is contained in returned string
 *		- p_dest is destination buffer for zero-terminated string
 *		- n_max_length is maximal length of data, including terminating zero
 *		- uses some cache to return parts of escape-sequences in case they do not fit whole
 *		  into destination buffer. (escape sequences will be properly returned with next call
 *		  to the next text-mode input function prior to reading more bytes from file)
 *		- returns true on success or returns false and raises error_InvalidOperation flag
 *		  in case file is not opened or in case file is opened for writing or raises
 *		  error_Treshold flag on triggering treshold unit with wchar_Fail specified or
 *		  error_Overflow in case type of p_dest isn't big enough to accomodate character ascii
 *		  and no (suitable) character transformation is enabled)
 *		- it may raise error_IO flag on i/o error or error_Unicode flag on invalid unicode
 *		  sequence while returning true but not reading in full length of buffer
 */
bool CUnicodeFile::GetLine(char *p_dest, int n_max_length)
{
	__FuncGuard("CUnicodeFile::GetLine");

	if(!m_p_file || m_b_writing) {
		Raise(error_InvalidOperation);
		return false;
	}
	// has to be opened for reading

#ifdef __UNICODE_FILE_ENABLE_CHAR_PROCESSING
	if(m_n_string_buffer_used) {
		if(m_n_string_buffer_used + 1 < n_max_length) {
			memcpy(p_dest, m_p_string_buffer, m_n_string_buffer_used);
			p_dest += m_n_string_buffer_used;
			n_max_length -= m_n_string_buffer_used;
			m_n_string_buffer_used = 0;
			// copy the whole string and continue
		} else if(m_n_string_buffer_used + 1 == n_max_length) {
			m_p_string_buffer[m_n_string_buffer_used] = 0; // do not necessarily has to be
			strcpy(p_dest, m_p_string_buffer);
			m_n_string_buffer_used = 0;
			return true;
			// copy the whole string and end
		} else {
			memcpy(p_dest, m_p_string_buffer, n_max_length - 1);
			p_dest[n_max_length - 1] = 0;
			m_n_string_buffer_used -= n_max_length - 1;
			memcpy(m_p_string_buffer, m_p_string_buffer + (n_max_length - 1),
				m_n_string_buffer_used);
			return true;
			// copy part of the string, shift it and end
		}
	}
	// in case something was in the buffer, eat it up first
#endif // __UNICODE_FILE_ENABLE_CHAR_PROCESSING

	for(const char *p_end = p_dest + n_max_length - 1; p_dest < p_end;) {
		int n_char;
		if(m_n_encoding == code_UTF_8) {
			if(!GetWideChar_UTF_8(n_char))
				break;
		} else if(m_n_encoding == code_UTF_16_LE) {
			if(!GetWideChar_UTF_16_LE(n_char))
				break;
		} else /*if(m_n_encoding == code_ASCII)*/ {
			uint8_t n_tmp;
			if(!ReadByte(n_tmp))
				break;
			n_char = n_tmp;
		} // t_odo - add branch for code_ASCII (and above as well)

		if(n_char == '\n') {
#ifdef __UNICODE_FILE_ENABLE_CHAR_PROCESSING
			if(!Process_WideChars((uint8_t*&)p_dest, (uint8_t*)p_end, n_char)) {
				*p_dest = 0;
				return false;
			}
			// wide char translation
#else // __UNICODE_FILE_ENABLE_CHAR_PROCESSING
			*p_dest = n_char;
			++ p_dest;
#endif // __UNICODE_FILE_ENABLE_CHAR_PROCESSING
			break;
		}
		// break on '\n'

#ifdef __UNICODE_FILE_ENABLE_CHAR_PROCESSING
		if(!Process_WideChars((uint8_t*&)p_dest, (uint8_t*)p_end, n_char)) {
			*p_dest = 0;
			return false;
		}
		// wide char translation
#else // __UNICODE_FILE_ENABLE_CHAR_PROCESSING
		*p_dest = n_char;
		++ p_dest;
#endif // __UNICODE_FILE_ENABLE_CHAR_PROCESSING
	}
	*p_dest = 0; // terminating 0

	return true;
}

/*
 *	int CUnicodeFile::n_GetChar()
 *		- text mode i/o. return a single character of string, containing characters up to 0xff
 *		- characters above 0xff are written in their hexadecimal form (ie. \x??????),
 *		  backslash characters '\' are duplicated "\\"; can be disabled by EnableWideChars()
 *		- uses some cache to return escape-sequences so be careful when using it in conjunction
 *		  with the other functions (escape sequences will be properly returned with next call
 *		  to the next text-mode input function prior to reading more bytes from file)
 *		- returns character value or returns -1 and raises error_InvalidOperation flag
 *		  in case file is not opened or in case file is opened for writing or raises error_IO
 *		  flag on i/o error, error_Treshold on triggering treshold unit with wchar_Fail
 *		  specified or error_Overflow on triggering tresh_User unit while no (suitable)
 *		  character transformation is enabled and tresh_Overflow unit is disabled or raises
 *		  error_Unicode flag on invalid unicode sequence
 */
int CUnicodeFile::n_GetChar()
{
	__FuncGuard("CUnicodeFile::n_GetChar");

	if(!m_p_file || m_b_writing) {
		Raise(error_InvalidOperation);
		return -1;
	}
	// has to be opened for reading

#ifdef __UNICODE_FILE_ENABLE_CHAR_PROCESSING
	if(m_n_string_buffer_used) {
		if(m_n_string_buffer_used > 1) {
			uint8_t n_result;
			n_result = m_p_string_buffer[0];
			memcpy(m_p_string_buffer, m_p_string_buffer + 1, -- m_n_string_buffer_used);
			return n_result;
		} else /*if(m_n_string_buffer_used == 1)*/ {
			m_n_string_buffer_used = 0;
			return m_p_string_buffer[0];
		}
	}
	// in case something was in the buffer, eat it up first
#endif // __UNICODE_FILE_ENABLE_CHAR_PROCESSING

	for(;;) {
		int n_char;
		if(m_n_encoding == code_UTF_8) {
			if(!GetWideChar_UTF_8(n_char))
				return -1;
		} else if(m_n_encoding == code_UTF_16_LE) {
			if(!GetWideChar_UTF_16_LE(n_char))
				return -1;
		} else /*if(m_n_encoding == code_ASCII)*/ {
			uint8_t n_tmp;
			if(!ReadByte(n_tmp))
				return -1;
			n_char = n_tmp;
		} // t_odo - add branch for code_ASCII (and above as well)

#ifdef __UNICODE_FILE_ENABLE_CHAR_PROCESSING
		unsigned int n_result, *p_result = &n_result;
		if(!Process_WideChars(p_result, p_result + 1, n_char))
			return -1;
		// wide char translation

		if(p_result != &n_result)
			return (int)n_result;
		// has to check for case character was discarded
#else // __UNICODE_FILE_ENABLE_CHAR_PROCESSING
		return n_char;
#endif // __UNICODE_FILE_ENABLE_CHAR_PROCESSING
	}
}

/*
 *	bool CUnicodeFile::PutChar(unsigned int n_char)
 *		- text mode i/o. write a single character
 *		- returns true in case of success, false on failure
 */
bool CUnicodeFile::PutChar(unsigned int n_char)
{
	__FuncGuard("CUnicodeFile::PutChar");

	if(!m_p_file || !m_b_writing) {
		Raise(error_InvalidOperation);
		return false;
	}
	// has to be opened for writing

	if(m_n_encoding == code_UTF_8)
		return PutWideChar_UTF_8(n_char);
	else if(m_n_encoding == code_UTF_16_LE)
		return PutWideChar_UTF_16_LE(n_char);
	else /*if(m_n_encoding == code_ASCII)*/
		return WriteByte((uint8_t)n_char);
}

/*
 *								=== ~CUnicodeFile ===
 */
