virtual int32_t write(const void* _data, int32_t _size, bx::Error* _err) override

/*
 */

#include <bx/allocator.h>
#include <bx/commandline.h>
#include <bx/file.h>
#include <bx/string.h>

#include <bx/debug.h>

class Bin2cWriter : public bx::WriterI
{
public:
	Bin2cWriter(bx::AllocatorI* _allocator, const bx::StringView& _name)
		: m_mb(_allocator)
		, m_mw(&m_mb)
		, m_name(_name)
		, m_outputAsCStr(false)
	{
	}

	virtual ~Bin2cWriter()
	{
	}

{
		bool asCStr = true;

		const char* data = (const char*)_data;
		for (int32_t ii = 0; ii < _size && asCStr; ++ii)
		{
			char ch = data[ii];

			asCStr &= false
				| bx::isPrint(ch)
				| bx::isSpace(ch)
				;
		}

		m_outputAsCStr = asCStr;

		return bx::write(&m_mw, _data, _size, _err);
	}

	void output(bx::WriterI* _writer)
	{
		if (m_outputAsCStr)
		{
			outputString(_writer);
		}
		else
		{
			outputHex(_writer);
		}
	}

	void outputString(bx::WriterI* _writer)
	{
		const uint8_t* data = (const uint8_t*)m_mb.more(0);
		uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const char* %.*s = /* Generated with bin2c. */\n\t\""
			, m_name.getLength()
			, m_name.getPtr()
			);

		if (NULL != data)
		{
			bool escaped = false;

			for (uint32_t ii = 0; ii < size; ++ii)
			{
				const char ch = char(data[ii]);

				if (!escaped)
				{
					switch (ch)
					{
					case '\"': bx::write(_writer, "\\\"",        &err); break;
					case '\n': bx::write(_writer, "\\n\"\n\t\"", &err); break;
					case '\r': bx::write(_writer, "\\r",         &err); break;
					case '\\': escaped = true;                 BX_FALLTHROUGH;
					default:   bx::write(_writer, ch, &err);            break;
					}
				}
				else
				{
					switch (ch)
					{
					case '\n': bx::write(_writer, "\\\"\n\t\"", &err);  break;
					case '\r':                                 BX_FALLTHROUGH;
					case '\t': bx::write(_writer, "\\", &err); BX_FALLTHROUGH;
					default  : bx::write(_writer, ch,   &err);          break;
					}

					escaped = false;
				}
			}
		}

		bx::write(_writer, &err, "\"\n\t;\n");
	}

	void outputHex(bx::WriterI* _writer)
	{
#define HEX_DUMP_WIDTH 16
#define HEX_DUMP_SPACE_WIDTH 96
#define HEX_DUMP_FORMAT "%-" BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "." BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "s"
		const uint8_t* data = (const uint8_t*)m_mb.more(0);
		uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const uint8_t %.*s[%d] = /* Generated with bin2c. */\n{\n"
			, m_name.getLength()
			, m_name.getPtr()
			, size
			);

		if (NULL != data)
		{
			char hex[HEX_DUMP_SPACE_WIDTH+1];
			char ascii[HEX_DUMP_WIDTH+1];
			uint32_t hexPos = 0;
			uint32_t asciiPos = 0;
			for (uint32_t ii = 0; ii < size; ++ii)
			{
				bx::snprintf(&hex[hexPos], sizeof(hex)-hexPos, "0x%02x, ", data[asciiPos]);
				hexPos += 6;

				ascii[asciiPos] = bx::isPrint(data[asciiPos]) && data[asciiPos] != '\\' ? data[asciiPos] : '.';
				asciiPos++;

				if (HEX_DUMP_WIDTH == asciiPos)
				{
					ascii[asciiPos] = '\0';
					bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
					data += asciiPos;
					hexPos   = 0;
					asciiPos = 0;
				}
			}

			if (0 != asciiPos)
			{
				ascii[asciiPos] = '\0';
				bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
			}
		}

		bx::write(_writer, &err, "};\n");
#undef HEX_DUMP_WIDTH
#undef HEX_DUMP_SPACE_WIDTH
#undef HEX_DUMP_FORMAT
	}

	bx::MemoryBlock  m_mb;
	bx::MemoryWriter m_mw;
	bx::StringView   m_name;
	bool             m_outputAsCStr;
};

void error(const char* _format, ...)
{
	bx::WriterI* stdOut = bx::getStdOut();
	bx::Error err;

	va_list argList;
	va_start(argList, _format);
	bx::write(stdOut, &err, "Error:\n");
	bx::write(stdOut, _format, argList, &err);
	bx::write(stdOut, &err, "\n\n");
	va_end(argList);
}

void help(const char* _error = NULL)
{
	bx::WriterI* stdOut = bx::getStdOut();
	bx::Error err;

	if (NULL != _error)
	{
		error(_error);
	}

	bx::write(stdOut, &err
		, "bin2c, binary to C\n"
		  "Copyright 2011-2021 Branimir Karadzic. All rights reserved.\n"
		  "License: https://github.com/bkaradzic/bx#license-bsd-2-clause\n\n"
		);

	bx::write(stdOut, &err
		, "Usage: bin2c -f <in> -o <out> -n <name>\n"
		  "\n"
		  "Options:\n"
		  "  -f <file path>    Input file path.\n"
		  "  -o <file path>    Output file path.\n"
		  "  -n <name>         Array name.\n"
		  "\n"
		  "For additional information, see http://example.com\n"
		);
}

int main(int _argc, const char* _argv[])
{
	bx::CommandLine cmdLine(_argc, _argv);

	if (cmdLine.hasArg('h', "help") )
	{
		help();
		return bx::kExitFailure;
	}

	bx::FilePath filePath = cmdLine.findOption('f');
	if (filePath.isEmpty() )
	{
		help("Input file name must be specified.");
		return bx::kExitFailure;
	}

	bx::FilePath outFilePath = cmdLine.findOption('o');
	if (outFilePath.isEmpty() )
	{
		help("Output file name must be specified.");
		return bx::kExitFailure;
	}

	bx::StringView name = cmdLine.findOption('n');
	if (name.isEmpty() )
	{
		name.set("data");
	}

	void* data = NULL;
	uint32_t size = 0;

	bx::FileReader fr;
	if (bx::open(&fr, filePath) )
	{
		size = uint32_t(bx::getSize(&fr) );

		bx::DefaultAllocator allocator;
		data = BX_ALLOC(&allocator, size);
		bx::read(&fr, data, size);
		bx::close(&fr);

		bx::FileWriter fw;
		if (bx::open(&fw, outFilePath) )
		{
			Bin2cWriter writer(&allocator, name);
			bx::write(&writer, data, size);

			writer.output(&fw);
			bx::close(&fw);
		}
		else
		{
			bx::StringView path = outFilePath;
			error("Failed to open output file '%.*s'.\n", path.getLength(), path.getPtr() );
		}

		BX_FREE(&allocator, data);
	}
	else
	{
		bx::StringView path = filePath;
		error("Failed to open input file '%.*s'.\n", path.getLength(), path.getPtr() );
	}

	return 0;
}


/*
 */

#include <bx/allocator.h>
#include <bx/commandline.h>
#include <bx/file.h>
#include <bx/string.h>

#include <bx/debug.h>

class Bin2cWriter : public bx::WriterI
{
public:
	Bin2cWriter(bx::AllocatorI* _allocator, const bx::StringView& _name)
		: m_mb(_allocator)
		, m_mw(&m_mb)
		, m_name(_name)
		, m_outputAsCStr(false)
	{
	}

	virtual ~Bin2cWriter()

	virtual int32_t write(const void* _data, int32_t _size, bx::Error* _err) override
	{
		bool asCStr = true;

		const char* data = (const char*)_data;
		for (int32_t ii = 0; ii < _size && asCStr; ++ii)
		{
			char ch = data[ii];

			asCStr &= false
				| bx::isPrint(ch)
				| bx::isSpace(ch)
				;
		}

		m_outputAsCStr = asCStr;

		return bx::write(&m_mw, _data, _size, _err);
	}

	void output(bx::WriterI* _writer)
	{
		if (m_outputAsCStr)
		{
			outputString(_writer);
		}
		else
		{
			outputHex(_writer);
		}
	}

	void outputString(bx::WriterI* _writer)
	{
		const uint8_t* data = (const uint8_t*)m_mb.more(0);
		uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const char* %.*s = /* Generated with bin2c. */\n\t\""
			, m_name.getLength()
			, m_name.getPtr()
			);

		if (NULL != data)
		{
			bool escaped = false;

			for (uint32_t ii = 0; ii < size; ++ii)
			{
				const char ch = char(data[ii]);

				if (!escaped)
				{
					switch (ch)
					{
					case '\"': bx::write(_writer, "\\\"",        &err); break;
					case '\n': bx::write(_writer, "\\n\"\n\t\"", &err); break;
					case '\r': bx::write(_writer, "\\r",         &err); break;
					case '\\': escaped = true;                 BX_FALLTHROUGH;
					default:   bx::write(_writer, ch, &err);            break;
					}
				}
				else
				{
					switch (ch)
					{
					case '\n': bx::write(_writer, "\\\"\n\t\"", &err);  break;
					case '\r':                                 BX_FALLTHROUGH;
					case '\t': bx::write(_writer, "\\", &err); BX_FALLTHROUGH;
					default  : bx::write(_writer, ch,   &err);          break;
					}

					escaped = false;
				}
			}
		}

		bx::write(_writer, &err, "\"\n\t;\n");
	}

	void outputHex(bx::WriterI* _writer)
	{
#define HEX_DUMP_WIDTH 16
#define HEX_DUMP_SPACE_WIDTH 96
#define HEX_DUMP_FORMAT "%-" BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "." BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "s"
		const uint8_t* data = (const uint8_t*)m_mb.more(0);
		uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const uint8_t %.*s[%d] = /* Generated with bin2c. */\n{\n"
			, m_name.getLength()
			, m_name.getPtr()
			, size
			);

		if (NULL != data)
		{
			char hex[HEX_DUMP_SPACE_WIDTH+1];
			char ascii[HEX_DUMP_WIDTH+1];
			uint32_t hexPos = 0;
			uint32_t asciiPos = 0;
			for (uint32_t ii = 0; ii < size; ++ii)
			{
				bx::snprintf(&hex[hexPos], sizeof(hex)-hexPos, "0x%02x, ", data[asciiPos]);
				hexPos += 6;

				ascii[asciiPos] = bx::isPrint(data[asciiPos]) && data[asciiPos] != '\\' ? data[asciiPos] : '.';
				asciiPos++;

				if (HEX_DUMP_WIDTH == asciiPos)
				{
					ascii[asciiPos] = '\0';
					bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
					data += asciiPos;
					hexPos   = 0;
					asciiPos = 0;
				}
			}

			if (0 != asciiPos)
			{
				ascii[asciiPos] = '\0';
				bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
			}
		}

		bx::write(_writer, &err, "};\n");
#undef HEX_DUMP_WIDTH
#undef HEX_DUMP_SPACE_WIDTH
#undef HEX_DUMP_FORMAT
	}

	bx::MemoryBlock  m_mb;
	bx::MemoryWriter m_mw;
	bx::StringView   m_name;
	bool             m_outputAsCStr;
};

void error(const char* _format, ...)
{
	bx::WriterI* stdOut = bx::getStdOut();
	bx::Error err;

	va_list argList;
	va_start(argList, _format);
	bx::write(stdOut, &err, "Error:\n");
	bx::write(stdOut, _format, argList, &err);
	bx::write(stdOut, &err, "\n\n");
	va_end(argList);
}

void help(const char* _error = NULL)
{
	bx::WriterI* stdOut = bx::getStdOut();
	bx::Error err;

	if (NULL != _error)
	{
		error(_error);
	}

	bx::write(stdOut, &err
		, "bin2c, binary to C\n"
		  "Copyright 2011-2021 Branimir Karadzic. All rights reserved.\n"
		  "License: https://github.com/bkaradzic/bx#license-bsd-2-clause\n\n"
		);

	bx::write(stdOut, &err
		, "Usage: bin2c -f <in> -o <out> -n <name>\n"
		  "\n"
		  "Options:\n"
		  "  -f <file path>    Input file path.\n"
		  "  -o <file path>    Output file path.\n"
		  "  -n <name>         Array name.\n"
		  "\n"
		  "For additional information, see http://example.com\n"
		);
}

int main(int _argc, const char* _argv[])
{
	bx::CommandLine cmdLine(_argc, _argv);

	if (cmdLine.hasArg('h', "help") )
	{
		help();
		return bx::kExitFailure;
	}

	bx::FilePath filePath = cmdLine.findOption('f');
	if (filePath.isEmpty() )
	{
		help("Input file name must be specified.");
		return bx::kExitFailure;
	}

	bx::FilePath outFilePath = cmdLine.findOption('o');
	if (outFilePath.isEmpty() )
	{
		help("Output file name must be specified.");
		return bx::kExitFailure;
	}

	bx::StringView name = cmdLine.findOption('n');
	if (name.isEmpty() )
	{
		name.set("data");
	}

	void* data = NULL;
	uint32_t size = 0;

	bx::FileReader fr;
	if (bx::open(&fr, filePath) )
	{
		size = uint32_t(bx::getSize(&fr) );

		bx::DefaultAllocator allocator;
		data = BX_ALLOC(&allocator, size);
		bx::read(&fr, data, size);
		bx::close(&fr);

		bx::FileWriter fw;
		if (bx::open(&fw, outFilePath) )
		{
			Bin2cWriter writer(&allocator, name);
			bx::write(&writer, data, size);

			writer.output(&fw);
			bx::close(&fw);
		}
		else
		{
			bx::StringView path = outFilePath;
			error("Failed to open output file '%.*s'.\n", path.getLength(), path.getPtr() );
		}

		BX_FREE(&allocator, data);
	}
	else
	{
		bx::StringView path = filePath;
		error("Failed to open input file '%.*s'.\n", path.getLength(), path.getPtr() );
	}

	return 0;
}


{
#define HEX_DUMP_WIDTH 16
#define HEX_DUMP_SPACE_WIDTH 96
#define HEX_DUMP_FORMAT "%-" BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "." BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "s"
		const uint8_t* data = (const uint8_t*)m_mb.more(0);

/*
 */

#include <bx/allocator.h>
#include <bx/commandline.h>
#include <bx/file.h>
#include <bx/string.h>

#include <bx/debug.h>

class Bin2cWriter : public bx::WriterI
{
public:
	Bin2cWriter(bx::AllocatorI* _allocator, const bx::StringView& _name)
		: m_mb(_allocator)
		, m_mw(&m_mb)
		, m_name(_name)
		, m_outputAsCStr(false)
	{
	}

	virtual ~Bin2cWriter()
	{
	}

	virtual int32_t write(const void* _data, int32_t _size, bx::Error* _err) override
	{
		bool asCStr = true;

		const char* data = (const char*)_data;
		for (int32_t ii = 0; ii < _size && asCStr; ++ii)
		{
			char ch = data[ii];

			asCStr &= false
				| bx::isPrint(ch)
				| bx::isSpace(ch)
				;
		}

		m_outputAsCStr = asCStr;

		return bx::write(&m_mw, _data, _size, _err);
	}

	void output(bx::WriterI* _writer)
	{
		if (m_outputAsCStr)
		{
			outputString(_writer);
		}
		else
		{
			outputHex(_writer);
		}
	}

	void outputString(bx::WriterI* _writer)
	{
		const uint8_t* data = (const uint8_t*)m_mb.more(0);
		uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const char* %.*s = /* Generated with bin2c. */\n\t\""
			, m_name.getLength()
			, m_name.getPtr()
			);

		if (NULL != data)
		{
			bool escaped = false;

			for (uint32_t ii = 0; ii < size; ++ii)
			{
				const char ch = char(data[ii]);

				if (!escaped)
				{
					switch (ch)
					{
					case '\"': bx::write(_writer, "\\\"",        &err); break;
					case '\n': bx::write(_writer, "\\n\"\n\t\"", &err); break;
					case '\r': bx::write(_writer, "\\r",         &err); break;
					case '\\': escaped = true;                 BX_FALLTHROUGH;
					default:   bx::write(_writer, ch, &err);            break;
					}
				}
				else
				{
					switch (ch)
					{
					case '\n': bx::write(_writer, "\\\"\n\t\"", &err);  break;
					case '\r':                                 BX_FALLTHROUGH;
					case '\t': bx::write(_writer, "\\", &err); BX_FALLTHROUGH;
					default  : bx::write(_writer, ch,   &err);          break;
					}

					escaped = false;
				}
			}
		}

		bx::write(_writer, &err, "\"\n\t;\n");
	}

	void outputHex(bx::WriterI* _writer)

		uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const uint8_t %.*s[%d] = /* Generated with bin2c. */\n{\n"
			, m_name.getLength()
			, m_name.getPtr()
			, size
			);

		if (NULL != data)
		{
			char hex[HEX_DUMP_SPACE_WIDTH+1];
			char ascii[HEX_DUMP_WIDTH+1];
			uint32_t hexPos = 0;
			uint32_t asciiPos = 0;
			for (uint32_t ii = 0; ii < size; ++ii)
			{
				bx::snprintf(&hex[hexPos], sizeof(hex)-hexPos, "0x%02x, ", data[asciiPos]);
				hexPos += 6;

				ascii[asciiPos] = bx::isPrint(data[asciiPos]) && data[asciiPos] != '\\' ? data[asciiPos] : '.';
				asciiPos++;

				if (HEX_DUMP_WIDTH == asciiPos)
				{
					ascii[asciiPos] = '\0';
					bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
					data += asciiPos;
					hexPos   = 0;
					asciiPos = 0;
				}
			}

			if (0 != asciiPos)
			{
				ascii[asciiPos] = '\0';
				bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
			}
		}

		bx::write(_writer, &err, "};\n");
#undef HEX_DUMP_WIDTH
#undef HEX_DUMP_SPACE_WIDTH
#undef HEX_DUMP_FORMAT
	}

	bx::MemoryBlock  m_mb;
	bx::MemoryWriter m_mw;
	bx::StringView   m_name;
	bool             m_outputAsCStr;
};

void error(const char* _format, ...)
{
	bx::WriterI* stdOut = bx::getStdOut();
	bx::Error err;

	va_list argList;
	va_start(argList, _format);
	bx::write(stdOut, &err, "Error:\n");
	bx::write(stdOut, _format, argList, &err);
	bx::write(stdOut, &err, "\n\n");
	va_end(argList);
}

void help(const char* _error = NULL)
{
	bx::WriterI* stdOut = bx::getStdOut();
	bx::Error err;

	if (NULL != _error)
	{
		error(_error);
	}

	bx::write(stdOut, &err
		, "bin2c, binary to C\n"
		  "Copyright 2011-2021 Branimir Karadzic. All rights reserved.\n"
		  "License: https://github.com/bkaradzic/bx#license-bsd-2-clause\n\n"
		);

	bx::write(stdOut, &err
		, "Usage: bin2c -f <in> -o <out> -n <name>\n"
		  "\n"
		  "Options:\n"
		  "  -f <file path>    Input file path.\n"
		  "  -o <file path>    Output file path.\n"
		  "  -n <name>         Array name.\n"
		  "\n"
		  "For additional information, see http://example.com\n"
		);
}

int main(int _argc, const char* _argv[])
{
	bx::CommandLine cmdLine(_argc, _argv);

	if (cmdLine.hasArg('h', "help") )
	{
		help();
		return bx::kExitFailure;
	}

	bx::FilePath filePath = cmdLine.findOption('f');
	if (filePath.isEmpty() )
	{
		help("Input file name must be specified.");
		return bx::kExitFailure;
	}

	bx::FilePath outFilePath = cmdLine.findOption('o');
	if (outFilePath.isEmpty() )
	{
		help("Output file name must be specified.");
		return bx::kExitFailure;
	}

	bx::StringView name = cmdLine.findOption('n');
	if (name.isEmpty() )
	{
		name.set("data");
	}

	void* data = NULL;
	uint32_t size = 0;

	bx::FileReader fr;
	if (bx::open(&fr, filePath) )
	{
		size = uint32_t(bx::getSize(&fr) );

		bx::DefaultAllocator allocator;
		data = BX_ALLOC(&allocator, size);
		bx::read(&fr, data, size);
		bx::close(&fr);

		bx::FileWriter fw;
		if (bx::open(&fw, outFilePath) )
		{
			Bin2cWriter writer(&allocator, name);
			bx::write(&writer, data, size);

			writer.output(&fw);
			bx::close(&fw);
		}
		else
		{
			bx::StringView path = outFilePath;
			error("Failed to open output file '%.*s'.\n", path.getLength(), path.getPtr() );
		}

		BX_FREE(&allocator, data);
	}
	else
	{
		bx::StringView path = filePath;
		error("Failed to open input file '%.*s'.\n", path.getLength(), path.getPtr() );
	}

	return 0;
}


uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const uint8_t %.*s[%d] = /* Generated with bin2c. */\n{\n"
			, m_name.getLength()
			, m_name.getPtr()
			, size
			);

		if (NULL != data)
		{
			char hex[HEX_DUMP_SPACE_WIDTH+1];
			char ascii[HEX_DUMP_WIDTH+1];
			uint32_t hexPos = 0;
			uint32_t asciiPos = 0;
			for (uint32_t ii = 0; ii < size; ++ii)
			{
				bx::snprintf(&hex[hexPos], sizeof(hex)-hexPos, "0x%02x, ", data[asciiPos]);
				hexPos += 6;

				ascii[asciiPos] = bx::isPrint(data[asciiPos]) && data[asciiPos] != '\\' ? data[asciiPos] : '.';
				asciiPos++;

				if (HEX_DUMP_WIDTH == asciiPos)
				{
					ascii[asciiPos] = '\0';
					bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
					data += asciiPos;
					hexPos   = 0;
					asciiPos = 0;
				}
			}

			if (0 != asciiPos)
			{
				ascii[asciiPos] = '\0';
				bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
			}
		}

/*
 */

#include <bx/allocator.h>
#include <bx/commandline.h>
#include <bx/file.h>
#include <bx/string.h>

#include <bx/debug.h>

class Bin2cWriter : public bx::WriterI
{
public:
	Bin2cWriter(bx::AllocatorI* _allocator, const bx::StringView& _name)
		: m_mb(_allocator)
		, m_mw(&m_mb)
		, m_name(_name)
		, m_outputAsCStr(false)
	{
	}

	virtual ~Bin2cWriter()
	{
	}

	virtual int32_t write(const void* _data, int32_t _size, bx::Error* _err) override
	{
		bool asCStr = true;

		const char* data = (const char*)_data;
		for (int32_t ii = 0; ii < _size && asCStr; ++ii)
		{
			char ch = data[ii];

			asCStr &= false
				| bx::isPrint(ch)
				| bx::isSpace(ch)
				;
		}

		m_outputAsCStr = asCStr;

		return bx::write(&m_mw, _data, _size, _err);
	}

	void output(bx::WriterI* _writer)
	{
		if (m_outputAsCStr)
		{
			outputString(_writer);
		}
		else
		{
			outputHex(_writer);
		}
	}

	void outputString(bx::WriterI* _writer)
	{
		const uint8_t* data = (const uint8_t*)m_mb.more(0);
		uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const char* %.*s = /* Generated with bin2c. */\n\t\""
			, m_name.getLength()
			, m_name.getPtr()
			);

		if (NULL != data)
		{
			bool escaped = false;

			for (uint32_t ii = 0; ii < size; ++ii)
			{
				const char ch = char(data[ii]);

				if (!escaped)
				{
					switch (ch)
					{
					case '\"': bx::write(_writer, "\\\"",        &err); break;
					case '\n': bx::write(_writer, "\\n\"\n\t\"", &err); break;
					case '\r': bx::write(_writer, "\\r",         &err); break;
					case '\\': escaped = true;                 BX_FALLTHROUGH;
					default:   bx::write(_writer, ch, &err);            break;
					}
				}
				else
				{
					switch (ch)
					{
					case '\n': bx::write(_writer, "\\\"\n\t\"", &err);  break;
					case '\r':                                 BX_FALLTHROUGH;
					case '\t': bx::write(_writer, "\\", &err); BX_FALLTHROUGH;
					default  : bx::write(_writer, ch,   &err);          break;
					}

					escaped = false;
				}
			}
		}

		bx::write(_writer, &err, "\"\n\t;\n");
	}

	void outputHex(bx::WriterI* _writer)
	{
#define HEX_DUMP_WIDTH 16
#define HEX_DUMP_SPACE_WIDTH 96
#define HEX_DUMP_FORMAT "%-" BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "." BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "s"
		const uint8_t* data = (const uint8_t*)m_mb.more(0);

		bx::write(_writer, &err, "};\n");
#undef HEX_DUMP_WIDTH
#undef HEX_DUMP_SPACE_WIDTH
#undef HEX_DUMP_FORMAT
	}

	bx::MemoryBlock  m_mb;
	bx::MemoryWriter m_mw;
	bx::StringView   m_name;
	bool             m_outputAsCStr;
};

void error(const char* _format, ...)
{
	bx::WriterI* stdOut = bx::getStdOut();
	bx::Error err;

	va_list argList;
	va_start(argList, _format);
	bx::write(stdOut, &err, "Error:\n");
	bx::write(stdOut, _format, argList, &err);
	bx::write(stdOut, &err, "\n\n");
	va_end(argList);
}

void help(const char* _error = NULL)
{
	bx::WriterI* stdOut = bx::getStdOut();
	bx::Error err;

	if (NULL != _error)
	{
		error(_error);
	}

	bx::write(stdOut, &err
		, "bin2c, binary to C\n"
		  "Copyright 2011-2021 Branimir Karadzic. All rights reserved.\n"
		  "License: https://github.com/bkaradzic/bx#license-bsd-2-clause\n\n"
		);

	bx::write(stdOut, &err
		, "Usage: bin2c -f <in> -o <out> -n <name>\n"
		  "\n"
		  "Options:\n"
		  "  -f <file path>    Input file path.\n"
		  "  -o <file path>    Output file path.\n"
		  "  -n <name>         Array name.\n"
		  "\n"
		  "For additional information, see http://example.com\n"
		);
}

int main(int _argc, const char* _argv[])
{
	bx::CommandLine cmdLine(_argc, _argv);

	if (cmdLine.hasArg('h', "help") )
	{
		help();
		return bx::kExitFailure;
	}

	bx::FilePath filePath = cmdLine.findOption('f');
	if (filePath.isEmpty() )
	{
		help("Input file name must be specified.");
		return bx::kExitFailure;
	}

	bx::FilePath outFilePath = cmdLine.findOption('o');
	if (outFilePath.isEmpty() )
	{
		help("Output file name must be specified.");
		return bx::kExitFailure;
	}

	bx::StringView name = cmdLine.findOption('n');
	if (name.isEmpty() )
	{
		name.set("data");
	}

	void* data = NULL;
	uint32_t size = 0;

	bx::FileReader fr;
	if (bx::open(&fr, filePath) )
	{
		size = uint32_t(bx::getSize(&fr) );

		bx::DefaultAllocator allocator;
		data = BX_ALLOC(&allocator, size);
		bx::read(&fr, data, size);
		bx::close(&fr);

		bx::FileWriter fw;
		if (bx::open(&fw, outFilePath) )
		{
			Bin2cWriter writer(&allocator, name);
			bx::write(&writer, data, size);

			writer.output(&fw);
			bx::close(&fw);
		}
		else
		{
			bx::StringView path = outFilePath;
			error("Failed to open output file '%.*s'.\n", path.getLength(), path.getPtr() );
		}

		BX_FREE(&allocator, data);
	}
	else
	{
		bx::StringView path = filePath;
		error("Failed to open input file '%.*s'.\n", path.getLength(), path.getPtr() );
	}

	return 0;
}


bx::write(_writer, &err, "};\n");
#undef HEX_DUMP_WIDTH
#undef HEX_DUMP_SPACE_WIDTH
#undef HEX_DUMP_FORMAT
	}

/*
 */

#include <bx/allocator.h>
#include <bx/commandline.h>
#include <bx/file.h>
#include <bx/string.h>

#include <bx/debug.h>

class Bin2cWriter : public bx::WriterI
{
public:
	Bin2cWriter(bx::AllocatorI* _allocator, const bx::StringView& _name)
		: m_mb(_allocator)
		, m_mw(&m_mb)
		, m_name(_name)
		, m_outputAsCStr(false)
	{
	}

	virtual ~Bin2cWriter()
	{
	}

	virtual int32_t write(const void* _data, int32_t _size, bx::Error* _err) override
	{
		bool asCStr = true;

		const char* data = (const char*)_data;
		for (int32_t ii = 0; ii < _size && asCStr; ++ii)
		{
			char ch = data[ii];

			asCStr &= false
				| bx::isPrint(ch)
				| bx::isSpace(ch)
				;
		}

		m_outputAsCStr = asCStr;

		return bx::write(&m_mw, _data, _size, _err);
	}

	void output(bx::WriterI* _writer)
	{
		if (m_outputAsCStr)
		{
			outputString(_writer);
		}
		else
		{
			outputHex(_writer);
		}
	}

	void outputString(bx::WriterI* _writer)
	{
		const uint8_t* data = (const uint8_t*)m_mb.more(0);
		uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const char* %.*s = /* Generated with bin2c. */\n\t\""
			, m_name.getLength()
			, m_name.getPtr()
			);

		if (NULL != data)
		{
			bool escaped = false;

			for (uint32_t ii = 0; ii < size; ++ii)
			{
				const char ch = char(data[ii]);

				if (!escaped)
				{
					switch (ch)
					{
					case '\"': bx::write(_writer, "\\\"",        &err); break;
					case '\n': bx::write(_writer, "\\n\"\n\t\"", &err); break;
					case '\r': bx::write(_writer, "\\r",         &err); break;
					case '\\': escaped = true;                 BX_FALLTHROUGH;
					default:   bx::write(_writer, ch, &err);            break;
					}
				}
				else
				{
					switch (ch)
					{
					case '\n': bx::write(_writer, "\\\"\n\t\"", &err);  break;
					case '\r':                                 BX_FALLTHROUGH;
					case '\t': bx::write(_writer, "\\", &err); BX_FALLTHROUGH;
					default  : bx::write(_writer, ch,   &err);          break;
					}

					escaped = false;
				}
			}
		}

		bx::write(_writer, &err, "\"\n\t;\n");
	}

	void outputHex(bx::WriterI* _writer)
	{
#define HEX_DUMP_WIDTH 16
#define HEX_DUMP_SPACE_WIDTH 96
#define HEX_DUMP_FORMAT "%-" BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "." BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "s"
		const uint8_t* data = (const uint8_t*)m_mb.more(0);
		uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const uint8_t %.*s[%d] = /* Generated with bin2c. */\n{\n"
			, m_name.getLength()
			, m_name.getPtr()
			, size
			);

		if (NULL != data)
		{
			char hex[HEX_DUMP_SPACE_WIDTH+1];
			char ascii[HEX_DUMP_WIDTH+1];
			uint32_t hexPos = 0;
			uint32_t asciiPos = 0;
			for (uint32_t ii = 0; ii < size; ++ii)
			{
				bx::snprintf(&hex[hexPos], sizeof(hex)-hexPos, "0x%02x, ", data[asciiPos]);
				hexPos += 6;

				ascii[asciiPos] = bx::isPrint(data[asciiPos]) && data[asciiPos] != '\\' ? data[asciiPos] : '.';
				asciiPos++;

				if (HEX_DUMP_WIDTH == asciiPos)
				{
					ascii[asciiPos] = '\0';
					bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
					data += asciiPos;
					hexPos   = 0;
					asciiPos = 0;
				}
			}

			if (0 != asciiPos)
			{
				ascii[asciiPos] = '\0';
				bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
			}
		}

	bx::MemoryBlock  m_mb;
	bx::MemoryWriter m_mw;
	bx::StringView   m_name;
	bool             m_outputAsCStr;
};

void error(const char* _format, ...)
{
	bx::WriterI* stdOut = bx::getStdOut();
	bx::Error err;

	va_list argList;
	va_start(argList, _format);
	bx::write(stdOut, &err, "Error:\n");
	bx::write(stdOut, _format, argList, &err);
	bx::write(stdOut, &err, "\n\n");
	va_end(argList);
}

void help(const char* _error = NULL)
{
	bx::WriterI* stdOut = bx::getStdOut();
	bx::Error err;

	if (NULL != _error)
	{
		error(_error);
	}

	bx::write(stdOut, &err
		, "bin2c, binary to C\n"
		  "Copyright 2011-2021 Branimir Karadzic. All rights reserved.\n"
		  "License: https://github.com/bkaradzic/bx#license-bsd-2-clause\n\n"
		);

	bx::write(stdOut, &err
		, "Usage: bin2c -f <in> -o <out> -n <name>\n"
		  "\n"
		  "Options:\n"
		  "  -f <file path>    Input file path.\n"
		  "  -o <file path>    Output file path.\n"
		  "  -n <name>         Array name.\n"
		  "\n"
		  "For additional information, see http://example.com\n"
		);
}

int main(int _argc, const char* _argv[])
{
	bx::CommandLine cmdLine(_argc, _argv);

	if (cmdLine.hasArg('h', "help") )
	{
		help();
		return bx::kExitFailure;
	}

	bx::FilePath filePath = cmdLine.findOption('f');
	if (filePath.isEmpty() )
	{
		help("Input file name must be specified.");
		return bx::kExitFailure;
	}

	bx::FilePath outFilePath = cmdLine.findOption('o');
	if (outFilePath.isEmpty() )
	{
		help("Output file name must be specified.");
		return bx::kExitFailure;
	}

	bx::StringView name = cmdLine.findOption('n');
	if (name.isEmpty() )
	{
		name.set("data");
	}

	void* data = NULL;
	uint32_t size = 0;

	bx::FileReader fr;
	if (bx::open(&fr, filePath) )
	{
		size = uint32_t(bx::getSize(&fr) );

		bx::DefaultAllocator allocator;
		data = BX_ALLOC(&allocator, size);
		bx::read(&fr, data, size);
		bx::close(&fr);

		bx::FileWriter fw;
		if (bx::open(&fw, outFilePath) )
		{
			Bin2cWriter writer(&allocator, name);
			bx::write(&writer, data, size);

			writer.output(&fw);
			bx::close(&fw);
		}
		else
		{
			bx::StringView path = outFilePath;
			error("Failed to open output file '%.*s'.\n", path.getLength(), path.getPtr() );
		}

		BX_FREE(&allocator, data);
	}
	else
	{
		bx::StringView path = filePath;
		error("Failed to open input file '%.*s'.\n", path.getLength(), path.getPtr() );
	}

	return 0;
}


/*
 */

#include <bx/allocator.h>
#include <bx/commandline.h>
#include <bx/file.h>
#include <bx/string.h>

#include <bx/debug.h>

class Bin2cWriter : public bx::WriterI
{
public:
	Bin2cWriter(bx::AllocatorI* _allocator, const bx::StringView& _name)
		: m_mb(_allocator)
		, m_mw(&m_mb)
		, m_name(_name)
		, m_outputAsCStr(false)
	{
	}

	virtual ~Bin2cWriter()
	{
	}

	virtual int32_t write(const void* _data, int32_t _size, bx::Error* _err) override
	{
		bool asCStr = true;

		const char* data = (const char*)_data;
		for (int32_t ii = 0; ii < _size && asCStr; ++ii)
		{
			char ch = data[ii];

			asCStr &= false
				| bx::isPrint(ch)
				| bx::isSpace(ch)
				;
		}

		m_outputAsCStr = asCStr;

		return bx::write(&m_mw, _data, _size, _err);
	}

	void output(bx::WriterI* _writer)
	{
		if (m_outputAsCStr)
		{
			outputString(_writer);
		}
		else
		{
			outputHex(_writer);
		}
	}

	void outputString(bx::WriterI* _writer)
	{
		const uint8_t* data = (const uint8_t*)m_mb.more(0);
		uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const char* %.*s = /* Generated with bin2c. */\n\t\""
			, m_name.getLength()
			, m_name.getPtr()
			);

		if (NULL != data)
		{
			bool escaped = false;

			for (uint32_t ii = 0; ii < size; ++ii)
			{
				const char ch = char(data[ii]);

				if (!escaped)
				{
					switch (ch)
					{
					case '\"': bx::write(_writer, "\\\"",        &err); break;
					case '\n': bx::write(_writer, "\\n\"\n\t\"", &err); break;
					case '\r': bx::write(_writer, "\\r",         &err); break;
					case '\\': escaped = true;                 BX_FALLTHROUGH;
					default:   bx::write(_writer, ch, &err);            break;
					}
				}
				else
				{
					switch (ch)
					{
					case '\n': bx::write(_writer, "\\\"\n\t\"", &err);  break;
					case '\r':                                 BX_FALLTHROUGH;
					case '\t': bx::write(_writer, "\\", &err); BX_FALLTHROUGH;
					default  : bx::write(_writer, ch,   &err);          break;
					}

					escaped = false;
				}
			}
		}

		bx::write(_writer, &err, "\"\n\t;\n");
	}

	void outputHex(bx::WriterI* _writer)
	{
#define HEX_DUMP_WIDTH 16
#define HEX_DUMP_SPACE_WIDTH 96
#define HEX_DUMP_FORMAT "%-" BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "." BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "s"
		const uint8_t* data = (const uint8_t*)m_mb.more(0);
		uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const uint8_t %.*s[%d] = /* Generated with bin2c. */\n{\n"
			, m_name.getLength()
			, m_name.getPtr()
			, size
			);

		if (NULL != data)
		{
			char hex[HEX_DUMP_SPACE_WIDTH+1];
			char ascii[HEX_DUMP_WIDTH+1];
			uint32_t hexPos = 0;
			uint32_t asciiPos = 0;
			for (uint32_t ii = 0; ii < size; ++ii)
			{
				bx::snprintf(&hex[hexPos], sizeof(hex)-hexPos, "0x%02x, ", data[asciiPos]);
				hexPos += 6;

				ascii[asciiPos] = bx::isPrint(data[asciiPos]) && data[asciiPos] != '\\' ? data[asciiPos] : '.';
				asciiPos++;

				if (HEX_DUMP_WIDTH == asciiPos)
				{
					ascii[asciiPos] = '\0';
					bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
					data += asciiPos;
					hexPos   = 0;
					asciiPos = 0;
				}
			}

			if (0 != asciiPos)
			{
				ascii[asciiPos] = '\0';
				bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
			}
		}

		bx::write(_writer, &err, "};\n");
#undef HEX_DUMP_WIDTH
#undef HEX_DUMP_SPACE_WIDTH
#undef HEX_DUMP_FORMAT
	}

	bx::MemoryBlock  m_mb;
	bx::MemoryWriter m_mw;
	bx::StringView   m_name;
	bool             m_outputAsCStr;
};

void error(const char* _format, ...)
{
	bx::WriterI*

	bx::Error err;

	va_list argList;
	va_start(argList, _format);
	bx::write(stdOut, &err, "Error:\n");
	bx::write(stdOut, _format, argList, &err);
	bx::write(stdOut, &err, "\n\n");
	va_end(argList);
}

void help(const char* _error = NULL)
{
	bx::WriterI* stdOut = bx::getStdOut();
	bx::Error err;

	if (NULL != _error)
	{
		error(_error);
	}

	bx::write(stdOut, &err
		, "bin2c, binary to C\n"
		  "Copyright 2011-2021 Branimir Karadzic. All rights reserved.\n"
		  "License: https://github.com/bkaradzic/bx#license-bsd-2-clause\n\n"
		);

	bx::write(stdOut, &err
		, "Usage: bin2c -f <in> -o <out> -n <name>\n"
		  "\n"
		  "Options:\n"
		  "  -f <file path>    Input file path.\n"
		  "  -o <file path>    Output file path.\n"
		  "  -n <name>         Array name.\n"
		  "\n"
		  "For additional information, see http://example.com\n"
		);
}

int main(int _argc, const char* _argv[])
{
	bx::CommandLine cmdLine(_argc, _argv);

	if (cmdLine.hasArg('h', "help") )
	{
		help();
		return bx::kExitFailure;
	}

	bx::FilePath filePath = cmdLine.findOption('f');
	if (filePath.isEmpty() )
	{
		help("Input file name must be specified.");
		return bx::kExitFailure;
	}

	bx::FilePath outFilePath = cmdLine.findOption('o');
	if (outFilePath.isEmpty() )
	{
		help("Output file name must be specified.");
		return bx::kExitFailure;
	}

	bx::StringView name = cmdLine.findOption('n');
	if (name.isEmpty() )
	{
		name.set("data");
	}

	void* data = NULL;
	uint32_t size = 0;

	bx::FileReader fr;
	if (bx::open(&fr, filePath) )
	{
		size = uint32_t(bx::getSize(&fr) );

		bx::DefaultAllocator allocator;
		data = BX_ALLOC(&allocator, size);
		bx::read(&fr, data, size);
		bx::close(&fr);

		bx::FileWriter fw;
		if (bx::open(&fw, outFilePath) )
		{
			Bin2cWriter writer(&allocator, name);
			bx::write(&writer, data, size);

			writer.output(&fw);
			bx::close(&fw);
		}
		else
		{
			bx::StringView path = outFilePath;
			error("Failed to open output file '%.*s'.\n", path.getLength(), path.getPtr() );
		}

		BX_FREE(&allocator, data);
	}
	else
	{
		bx::StringView path = filePath;
		error("Failed to open input file '%.*s'.\n", path.getLength(), path.getPtr() );
	}

	return 0;
}


if (HEX_DUMP_WIDTH == asciiPos)
				{
					ascii[asciiPos] = '\0';
					bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
					data += asciiPos;
					hexPos   = 0;
					asciiPos = 0;
				}

/*
 */

#include <bx/allocator.h>
#include <bx/commandline.h>
#include <bx/file.h>
#include <bx/string.h>

#include <bx/debug.h>

class Bin2cWriter : public bx::WriterI
{
public:
	Bin2cWriter(bx::AllocatorI* _allocator, const bx::StringView& _name)
		: m_mb(_allocator)
		, m_mw(&m_mb)
		, m_name(_name)
		, m_outputAsCStr(false)
	{
	}

	virtual ~Bin2cWriter()
	{
	}

	virtual int32_t write(const void* _data, int32_t _size, bx::Error* _err) override
	{
		bool asCStr = true;

		const char* data = (const char*)_data;
		for (int32_t ii = 0; ii < _size && asCStr; ++ii)
		{
			char ch = data[ii];

			asCStr &= false
				| bx::isPrint(ch)
				| bx::isSpace(ch)
				;
		}

		m_outputAsCStr = asCStr;

		return bx::write(&m_mw, _data, _size, _err);
	}

	void output(bx::WriterI* _writer)
	{
		if (m_outputAsCStr)
		{
			outputString(_writer);
		}
		else
		{
			outputHex(_writer);
		}
	}

	void outputString(bx::WriterI* _writer)
	{
		const uint8_t* data = (const uint8_t*)m_mb.more(0);
		uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const char* %.*s = /* Generated with bin2c. */\n\t\""
			, m_name.getLength()
			, m_name.getPtr()
			);

		if (NULL != data)
		{
			bool escaped = false;

			for (uint32_t ii = 0; ii < size; ++ii)
			{
				const char ch = char(data[ii]);

				if (!escaped)
				{
					switch (ch)
					{
					case '\"': bx::write(_writer, "\\\"",        &err); break;
					case '\n': bx::write(_writer, "\\n\"\n\t\"", &err); break;
					case '\r': bx::write(_writer, "\\r",         &err); break;
					case '\\': escaped = true;                 BX_FALLTHROUGH;
					default:   bx::write(_writer, ch, &err);            break;
					}
				}
				else
				{
					switch (ch)
					{
					case '\n': bx::write(_writer, "\\\"\n\t\"", &err);  break;
					case '\r':                                 BX_FALLTHROUGH;
					case '\t': bx::write(_writer, "\\", &err); BX_FALLTHROUGH;
					default  : bx::write(_writer, ch,   &err);          break;
					}

					escaped = false;
				}
			}
		}

		bx::write(_writer, &err, "\"\n\t;\n");
	}

	void outputHex(bx::WriterI* _writer)
	{
#define HEX_DUMP_WIDTH 16
#define HEX_DUMP_SPACE_WIDTH 96
#define HEX_DUMP_FORMAT "%-" BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "." BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "s"
		const uint8_t* data = (const uint8_t*)m_mb.more(0);
		uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const uint8_t %.*s[%d] = /* Generated with bin2c. */\n{\n"
			, m_name.getLength()
			, m_name.getPtr()
			, size
			);

		if (NULL != data)
		{
			char hex[HEX_DUMP_SPACE_WIDTH+1];
			char ascii[HEX_DUMP_WIDTH+1];
			uint32_t hexPos = 0;
			uint32_t asciiPos = 0;
			for (uint32_t ii = 0; ii < size; ++ii)
			{
				bx::snprintf(&hex[hexPos], sizeof(hex)-hexPos, "0x%02x, ", data[asciiPos]);
				hexPos += 6;

				ascii[asciiPos] = bx::isPrint(data[asciiPos]) && data[asciiPos] != '\\' ? data[asciiPos] : '.';
				asciiPos++;

			}

			if (0 != asciiPos)
			{
				ascii[asciiPos] = '\0';
				bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
			}
		}

		bx::write(_writer, &err, "};\n");
#undef HEX_DUMP_WIDTH
#undef HEX_DUMP_SPACE_WIDTH
#undef HEX_DUMP_FORMAT
	}

	bx::MemoryBlock  m_mb;
	bx::MemoryWriter m_mw;
	bx::StringView   m_name;
	bool             m_outputAsCStr;
};

void error(const char* _format, ...)
{
	bx::WriterI* stdOut = bx::getStdOut();
	bx::Error err;

	va_list argList;
	va_start(argList, _format);
	bx::write(stdOut, &err, "Error:\n");
	bx::write(stdOut, _format, argList, &err);
	bx::write(stdOut, &err, "\n\n");
	va_end(argList);
}

void help(const char* _error = NULL)
{
	bx::WriterI* stdOut = bx::getStdOut();
	bx::Error err;

	if (NULL != _error)
	{
		error(_error);
	}

	bx::write(stdOut, &err
		, "bin2c, binary to C\n"
		  "Copyright 2011-2021 Branimir Karadzic. All rights reserved.\n"
		  "License: https://github.com/bkaradzic/bx#license-bsd-2-clause\n\n"
		);

	bx::write(stdOut, &err
		, "Usage: bin2c -f <in> -o <out> -n <name>\n"
		  "\n"
		  "Options:\n"
		  "  -f <file path>    Input file path.\n"
		  "  -o <file path>    Output file path.\n"
		  "  -n <name>         Array name.\n"
		  "\n"
		  "For additional information, see http://example.com\n"
		);
}

int main(int _argc, const char* _argv[])
{
	bx::CommandLine cmdLine(_argc, _argv);

	if (cmdLine.hasArg('h', "help") )
	{
		help();
		return bx::kExitFailure;
	}

	bx::FilePath filePath = cmdLine.findOption('f');
	if (filePath.isEmpty() )
	{
		help("Input file name must be specified.");
		return bx::kExitFailure;
	}

	bx::FilePath outFilePath = cmdLine.findOption('o');
	if (outFilePath.isEmpty() )
	{
		help("Output file name must be specified.");
		return bx::kExitFailure;
	}

	bx::StringView name = cmdLine.findOption('n');
	if (name.isEmpty() )
	{
		name.set("data");
	}

	void* data = NULL;
	uint32_t size = 0;

	bx::FileReader fr;
	if (bx::open(&fr, filePath) )
	{
		size = uint32_t(bx::getSize(&fr) );

		bx::DefaultAllocator allocator;
		data = BX_ALLOC(&allocator, size);
		bx::read(&fr, data, size);
		bx::close(&fr);

		bx::FileWriter fw;
		if (bx::open(&fw, outFilePath) )
		{
			Bin2cWriter writer(&allocator, name);
			bx::write(&writer, data, size);

			writer.output(&fw);
			bx::close(&fw);
		}
		else
		{
			bx::StringView path = outFilePath;
			error("Failed to open output file '%.*s'.\n", path.getLength(), path.getPtr() );
		}

		BX_FREE(&allocator, data);
	}
	else
	{
		bx::StringView path = filePath;
		error("Failed to open input file '%.*s'.\n", path.getLength(), path.getPtr() );
	}

	return 0;
}


switch (ch)
					{
					case '\n': bx::write(_writer, "\\\"\n\t\"", &err);  break;
					case '\r':                                 BX_FALLTHROUGH;
					case '\t': bx::write(_writer, "\\", &err); BX_FALLTHROUGH;
					default  : bx::write(_writer, ch,   &err);          break;
					}

/*
 */

#include <bx/allocator.h>
#include <bx/commandline.h>
#include <bx/file.h>
#include <bx/string.h>

#include <bx/debug.h>

class Bin2cWriter : public bx::WriterI
{
public:
	Bin2cWriter(bx::AllocatorI* _allocator, const bx::StringView& _name)
		: m_mb(_allocator)
		, m_mw(&m_mb)
		, m_name(_name)
		, m_outputAsCStr(false)
	{
	}

	virtual ~Bin2cWriter()
	{
	}

	virtual int32_t write(const void* _data, int32_t _size, bx::Error* _err) override
	{
		bool asCStr = true;

		const char* data = (const char*)_data;
		for (int32_t ii = 0; ii < _size && asCStr; ++ii)
		{
			char ch = data[ii];

			asCStr &= false
				| bx::isPrint(ch)
				| bx::isSpace(ch)
				;
		}

		m_outputAsCStr = asCStr;

		return bx::write(&m_mw, _data, _size, _err);
	}

	void output(bx::WriterI* _writer)
	{
		if (m_outputAsCStr)
		{
			outputString(_writer);
		}
		else
		{
			outputHex(_writer);
		}
	}

	void outputString(bx::WriterI* _writer)
	{
		const uint8_t* data = (const uint8_t*)m_mb.more(0);
		uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const char* %.*s = /* Generated with bin2c. */\n\t\""
			, m_name.getLength()
			, m_name.getPtr()
			);

		if (NULL != data)
		{
			bool escaped = false;

			for (uint32_t ii = 0; ii < size; ++ii)
			{
				const char ch = char(data[ii]);

				if (!escaped)
				{
					switch (ch)
					{
					case '\"': bx::write(_writer, "\\\"",        &err); break;
					case '\n': bx::write(_writer, "\\n\"\n\t\"", &err); break;
					case '\r': bx::write(_writer, "\\r",         &err); break;
					case '\\': escaped = true;                 BX_FALLTHROUGH;
					default:   bx::write(_writer, ch, &err);            break;
					}
				}
				else
				{

					escaped = false;
				}
			}
		}

		bx::write(_writer, &err, "\"\n\t;\n");
	}

	void outputHex(bx::WriterI* _writer)
	{
#define HEX_DUMP_WIDTH 16
#define HEX_DUMP_SPACE_WIDTH 96
#define HEX_DUMP_FORMAT "%-" BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "." BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "s"
		const uint8_t* data = (const uint8_t*)m_mb.more(0);
		uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const uint8_t %.*s[%d] = /* Generated with bin2c. */\n{\n"
			, m_name.getLength()
			, m_name.getPtr()
			, size
			);

		if (NULL != data)
		{
			char hex[HEX_DUMP_SPACE_WIDTH+1];
			char ascii[HEX_DUMP_WIDTH+1];
			uint32_t hexPos = 0;
			uint32_t asciiPos = 0;
			for (uint32_t ii = 0; ii < size; ++ii)
			{
				bx::snprintf(&hex[hexPos], sizeof(hex)-hexPos, "0x%02x, ", data[asciiPos]);
				hexPos += 6;

				ascii[asciiPos] = bx::isPrint(data[asciiPos]) && data[asciiPos] != '\\' ? data[asciiPos] : '.';
				asciiPos++;

				if (HEX_DUMP_WIDTH == asciiPos)
				{
					ascii[asciiPos] = '\0';
					bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
					data += asciiPos;
					hexPos   = 0;
					asciiPos = 0;
				}
			}

			if (0 != asciiPos)
			{
				ascii[asciiPos] = '\0';
				bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
			}
		}

		bx::write(_writer, &err, "};\n");
#undef HEX_DUMP_WIDTH
#undef HEX_DUMP_SPACE_WIDTH
#undef HEX_DUMP_FORMAT
	}

	bx::MemoryBlock  m_mb;
	bx::MemoryWriter m_mw;
	bx::StringView   m_name;
	bool             m_outputAsCStr;
};

void error(const char* _format, ...)
{
	bx::WriterI* stdOut = bx::getStdOut();
	bx::Error err;

	va_list argList;
	va_start(argList, _format);
	bx::write(stdOut, &err, "Error:\n");
	bx::write(stdOut, _format, argList, &err);
	bx::write(stdOut, &err, "\n\n");
	va_end(argList);
}

void help(const char* _error = NULL)
{
	bx::WriterI* stdOut = bx::getStdOut();
	bx::Error err;

	if (NULL != _error)
	{
		error(_error);
	}

	bx::write(stdOut, &err
		, "bin2c, binary to C\n"
		  "Copyright 2011-2021 Branimir Karadzic. All rights reserved.\n"
		  "License: https://github.com/bkaradzic/bx#license-bsd-2-clause\n\n"
		);

	bx::write(stdOut, &err
		, "Usage: bin2c -f <in> -o <out> -n <name>\n"
		  "\n"
		  "Options:\n"
		  "  -f <file path>    Input file path.\n"
		  "  -o <file path>    Output file path.\n"
		  "  -n <name>         Array name.\n"
		  "\n"
		  "For additional information, see http://example.com\n"
		);
}

int main(int _argc, const char* _argv[])
{
	bx::CommandLine cmdLine(_argc, _argv);

	if (cmdLine.hasArg('h', "help") )
	{
		help();
		return bx::kExitFailure;
	}

	bx::FilePath filePath = cmdLine.findOption('f');
	if (filePath.isEmpty() )
	{
		help("Input file name must be specified.");
		return bx::kExitFailure;
	}

	bx::FilePath outFilePath = cmdLine.findOption('o');
	if (outFilePath.isEmpty() )
	{
		help("Output file name must be specified.");
		return bx::kExitFailure;
	}

	bx::StringView name = cmdLine.findOption('n');
	if (name.isEmpty() )
	{
		name.set("data");
	}

	void* data = NULL;
	uint32_t size = 0;

	bx::FileReader fr;
	if (bx::open(&fr, filePath) )
	{
		size = uint32_t(bx::getSize(&fr) );

		bx::DefaultAllocator allocator;
		data = BX_ALLOC(&allocator, size);
		bx::read(&fr, data, size);
		bx::close(&fr);

		bx::FileWriter fw;
		if (bx::open(&fw, outFilePath) )
		{
			Bin2cWriter writer(&allocator, name);
			bx::write(&writer, data, size);

			writer.output(&fw);
			bx::close(&fw);
		}
		else
		{
			bx::StringView path = outFilePath;
			error("Failed to open output file '%.*s'.\n", path.getLength(), path.getPtr() );
		}

		BX_FREE(&allocator, data);
	}
	else
	{
		bx::StringView path = filePath;
		error("Failed to open input file '%.*s'.\n", path.getLength(), path.getPtr() );
	}

	return 0;
}


for (int32_t ii = 0; ii < _size && asCStr; ++ii)
		{
			char ch = data[ii];

			asCStr &= false
				| bx::isPrint(ch)
				| bx::isSpace(ch)
				;
		}

/*
 */

#include <bx/allocator.h>
#include <bx/commandline.h>
#include <bx/file.h>
#include <bx/string.h>

#include <bx/debug.h>

class Bin2cWriter : public bx::WriterI
{
public:
	Bin2cWriter(bx::AllocatorI* _allocator, const bx::StringView& _name)
		: m_mb(_allocator)
		, m_mw(&m_mb)
		, m_name(_name)
		, m_outputAsCStr(false)
	{
	}

	virtual ~Bin2cWriter()
	{
	}

	virtual int32_t write(const void* _data, int32_t _size, bx::Error* _err) override
	{
		bool asCStr = true;

		const char* data = (const char*)_data;

		m_outputAsCStr = asCStr;

		return bx::write(&m_mw, _data, _size, _err);
	}

	void output(bx::WriterI* _writer)
	{
		if (m_outputAsCStr)
		{
			outputString(_writer);
		}
		else
		{
			outputHex(_writer);
		}
	}

	void outputString(bx::WriterI* _writer)
	{
		const uint8_t* data = (const uint8_t*)m_mb.more(0);
		uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const char* %.*s = /* Generated with bin2c. */\n\t\""
			, m_name.getLength()
			, m_name.getPtr()
			);

		if (NULL != data)
		{
			bool escaped = false;

			for (uint32_t ii = 0; ii < size; ++ii)
			{
				const char ch = char(data[ii]);

				if (!escaped)
				{
					switch (ch)
					{
					case '\"': bx::write(_writer, "\\\"",        &err); break;
					case '\n': bx::write(_writer, "\\n\"\n\t\"", &err); break;
					case '\r': bx::write(_writer, "\\r",         &err); break;
					case '\\': escaped = true;                 BX_FALLTHROUGH;
					default:   bx::write(_writer, ch, &err);            break;
					}
				}
				else
				{
					switch (ch)
					{
					case '\n': bx::write(_writer, "\\\"\n\t\"", &err);  break;
					case '\r':                                 BX_FALLTHROUGH;
					case '\t': bx::write(_writer, "\\", &err); BX_FALLTHROUGH;
					default  : bx::write(_writer, ch,   &err);          break;
					}

					escaped = false;
				}
			}
		}

		bx::write(_writer, &err, "\"\n\t;\n");
	}

	void outputHex(bx::WriterI* _writer)
	{
#define HEX_DUMP_WIDTH 16
#define HEX_DUMP_SPACE_WIDTH 96
#define HEX_DUMP_FORMAT "%-" BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "." BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "s"
		const uint8_t* data = (const uint8_t*)m_mb.more(0);
		uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const uint8_t %.*s[%d] = /* Generated with bin2c. */\n{\n"
			, m_name.getLength()
			, m_name.getPtr()
			, size
			);

		if (NULL != data)
		{
			char hex[HEX_DUMP_SPACE_WIDTH+1];
			char ascii[HEX_DUMP_WIDTH+1];
			uint32_t hexPos = 0;
			uint32_t asciiPos = 0;
			for (uint32_t ii = 0; ii < size; ++ii)
			{
				bx::snprintf(&hex[hexPos], sizeof(hex)-hexPos, "0x%02x, ", data[asciiPos]);
				hexPos += 6;

				ascii[asciiPos] = bx::isPrint(data[asciiPos]) && data[asciiPos] != '\\' ? data[asciiPos] : '.';
				asciiPos++;

				if (HEX_DUMP_WIDTH == asciiPos)
				{
					ascii[asciiPos] = '\0';
					bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
					data += asciiPos;
					hexPos   = 0;
					asciiPos = 0;
				}
			}

			if (0 != asciiPos)
			{
				ascii[asciiPos] = '\0';
				bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
			}
		}

		bx::write(_writer, &err, "};\n");
#undef HEX_DUMP_WIDTH
#undef HEX_DUMP_SPACE_WIDTH
#undef HEX_DUMP_FORMAT
	}

	bx::MemoryBlock  m_mb;
	bx::MemoryWriter m_mw;
	bx::StringView   m_name;
	bool             m_outputAsCStr;
};

void error(const char* _format, ...)
{
	bx::WriterI* stdOut = bx::getStdOut();
	bx::Error err;

	va_list argList;
	va_start(argList, _format);
	bx::write(stdOut, &err, "Error:\n");
	bx::write(stdOut, _format, argList, &err);
	bx::write(stdOut, &err, "\n\n");
	va_end(argList);
}

void help(const char* _error = NULL)
{
	bx::WriterI* stdOut = bx::getStdOut();
	bx::Error err;

	if (NULL != _error)
	{
		error(_error);
	}

	bx::write(stdOut, &err
		, "bin2c, binary to C\n"
		  "Copyright 2011-2021 Branimir Karadzic. All rights reserved.\n"
		  "License: https://github.com/bkaradzic/bx#license-bsd-2-clause\n\n"
		);

	bx::write(stdOut, &err
		, "Usage: bin2c -f <in> -o <out> -n <name>\n"
		  "\n"
		  "Options:\n"
		  "  -f <file path>    Input file path.\n"
		  "  -o <file path>    Output file path.\n"
		  "  -n <name>         Array name.\n"
		  "\n"
		  "For additional information, see http://example.com\n"
		);
}

int main(int _argc, const char* _argv[])
{
	bx::CommandLine cmdLine(_argc, _argv);

	if (cmdLine.hasArg('h', "help") )
	{
		help();
		return bx::kExitFailure;
	}

	bx::FilePath filePath = cmdLine.findOption('f');
	if (filePath.isEmpty() )
	{
		help("Input file name must be specified.");
		return bx::kExitFailure;
	}

	bx::FilePath outFilePath = cmdLine.findOption('o');
	if (outFilePath.isEmpty() )
	{
		help("Output file name must be specified.");
		return bx::kExitFailure;
	}

	bx::StringView name = cmdLine.findOption('n');
	if (name.isEmpty() )
	{
		name.set("data");
	}

	void* data = NULL;
	uint32_t size = 0;

	bx::FileReader fr;
	if (bx::open(&fr, filePath) )
	{
		size = uint32_t(bx::getSize(&fr) );

		bx::DefaultAllocator allocator;
		data = BX_ALLOC(&allocator, size);
		bx::read(&fr, data, size);
		bx::close(&fr);

		bx::FileWriter fw;
		if (bx::open(&fw, outFilePath) )
		{
			Bin2cWriter writer(&allocator, name);
			bx::write(&writer, data, size);

			writer.output(&fw);
			bx::close(&fw);
		}
		else
		{
			bx::StringView path = outFilePath;
			error("Failed to open output file '%.*s'.\n", path.getLength(), path.getPtr() );
		}

		BX_FREE(&allocator, data);
	}
	else
	{
		bx::StringView path = filePath;
		error("Failed to open input file '%.*s'.\n", path.getLength(), path.getPtr() );
	}

	return 0;
}


/*
 */

#include <bx/allocator.h>
#include <bx/commandline.h>
#include <bx/file.h>
#include <bx/string.h>

#include <bx/debug.h>

class Bin2cWriter : public bx::WriterI
{
public:
	Bin2cWriter(bx::AllocatorI* _allocator, const bx::StringView& _name)
		: m_mb(_allocator)
		, m_mw(&m_mb)
		, m_name(_name)
		, m_outputAsCStr(false)
	{
	}

	virtual ~Bin2cWriter()
	{
	}

	virtual int32_t write(const void* _data, int32_t _size, bx::Error* _err) override
	{
		bool asCStr = true;

		const char* data = (const char*)_data;
		for (int32_t ii = 0; ii < _size && asCStr; ++ii)
		{
			char ch = data[ii];

			asCStr &= false
				| bx::isPrint(ch)
				| bx::isSpace(ch)
				;
		}

		m_outputAsCStr = asCStr;

		return bx::write(&m_mw, _data, _size, _err);
	}

	void output(bx::WriterI* _writer)
	{
		if (m_outputAsCStr)
		{
			outputString(_writer);
		}
		else
		{
			outputHex(_writer);
		}
	}

	void outputString(bx::WriterI* _writer)
	{
		const uint8_t* data = (const uint8_t*)m_mb.more(0);
		uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const char* %.*s = /* Generated with bin2c. */\n\t\""
			, m_name.getLength()
			, m_name.getPtr()
			);

		if (NULL != data)
		{
			bool escaped = false;

			for (uint32_t ii = 0; ii < size; ++ii)
			{
				const char ch = char(data[ii]);

				if (!escaped)
				{
					switch (ch)
					{
					case '\"': bx::write(_writer, "\\\"",        &err); break;
					case '\n': bx::write(_writer, "\\n\"\n\t\"", &err); break;
					case '\r': bx::write(_writer, "\\r",         &err); break;
					case '\\': escaped = true;                 BX_FALLTHROUGH;
					default:   bx::write(_writer, ch, &err);            break;
					}
				}
				else
				{
					switch (ch)
					{
					case '\n': bx::write(_writer, "\\\"\n\t\"", &err);  break;
					case '\r':                                 BX_FALLTHROUGH;
					case '\t': bx::write(_writer, "\\", &err); BX_FALLTHROUGH;
					default  : bx::write(_writer, ch,   &err);          break;
					}

					escaped = false;
				}
			}
		}

		bx::write(_writer, &err, "\"\n\t;\n");
	}

	void outputHex(bx::WriterI* _writer)
	{
#define HEX_DUMP_WIDTH 16
#define HEX_DUMP_SPACE_WIDTH 96
#define HEX_DUMP_FORMAT "%-" BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "." BX_STRINGIZE(HEX_DUMP_SPACE_WIDTH) "s"
		const uint8_t* data = (const uint8_t*)m_mb.more(0);
		uint32_t size = uint32_t(bx::seek(&m_mw) );

		bx::Error err;

		bx::write(
			  _writer
			, &err
			, "static const uint8_t %.*s[%d] = /* Generated with bin2c. */\n{\n"
			, m_name.getLength()
			, m_name.getPtr()
			, size
			);

		if (NULL != data)
		{
			char hex[HEX_DUMP_SPACE_WIDTH+1];
			char ascii[HEX_DUMP_WIDTH+1];
			uint32_t hexPos = 0;
			uint32_t asciiPos = 0;
			for (uint32_t ii = 0; ii < size; ++ii)
			{
				bx::snprintf(&hex[hexPos], sizeof(hex)-hexPos, "0x%02x, ", data[asciiPos]);
				hexPos += 6;

				ascii[asciiPos] = bx::isPrint(data[asciiPos]) && data[asciiPos] != '\\' ? data[asciiPos] : '.';
				asciiPos++;

				if (HEX_DUMP_WIDTH == asciiPos)
				{
					ascii[asciiPos] = '\0';
					bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
					data += asciiPos;
					hexPos   = 0;
					asciiPos = 0;
				}
			}

			if (0 != asciiPos)
			{
				ascii[asciiPos] = '\0';
				bx::write(_writer, &err, "\t" HEX_DUMP_FORMAT "// %s\n", hex, ascii);
			}
		}

		bx::write(_writer, &err, "};\n");
#undef HEX_DUMP_WIDTH
#undef HEX_DUMP_SPACE_WIDTH
#undef HEX_DUMP_FORMAT
	}

	bx::MemoryBlock  m_mb;
	bx::MemoryWriter m_mw;
	bx::StringView   m_name;
	bool             m_outputAsCStr;
};

void error(const char* _format, ...)
{
	bx::WriterI* stdOut = bx::getStdOut();
	bx::Error err;

	va_list argList;
	va_start(argList, _format);
	bx::write(stdOut, &err, "Error:\n");
	bx::write(stdOut, _format, argList, &err);
	bx::write(stdOut, &err, "\n\n");
	va_end(argList);
}

void help(const char* _error = NULL)
{
	bx::WriterI* stdOut = bx::getStdOut();
	bx::Error err;

	if (NULL != _error)
	{
		error(_error);
	}

	bx::write(stdOut, &err
		, "bin2c, binary to C\n"
		  "Copyright 2011-2021 B

ranimir Karadzic. All rights reserved.\n"
		  "License: https://github.com/bkaradzic/bx#license-bsd-2-clause\n\n"
		);

	bx::write(stdOut, &err
		, "Usage: bin2c -f <in> -o <out> -n <name>\n"
		  "\n"
		  "Options:\n"
		  "  -f <file path>    Input file path.\n"
		  "  -o <file path>    Output file path.\n"
		  "  -n <name>         Array name.\n"
		  "\n"
		  "For additional information, see http://example.com\n"
		);
}

int main(int _argc, const char* _argv[])
{
	bx::CommandLine cmdLine(_argc, _argv);

	if (cmdLine.hasArg('h', "help") )
	{
		help();
		return bx::kExitFailure;
	}

	bx::FilePath filePath = cmdLine.findOption('f');
	if (filePath.isEmpty() )
	{
		help("Input file name must be specified.");
		return bx::kExitFailure;
	}

	bx::FilePath outFilePath = cmdLine.findOption('o');
	if (outFilePath.isEmpty() )
	{
		help("Output file name must be specified.");
		return bx::kExitFailure;
	}

	bx::StringView name = cmdLine.findOption('n');
	if (name.isEmpty() )
	{
		name.set("data");
	}

	void* data = NULL;
	uint32_t size = 0;

	bx::FileReader fr;
	if (bx::open(&fr, filePath) )
	{
		size = uint32_t(bx::getSize(&fr) );

		bx::DefaultAllocator allocator;
		data = BX_ALLOC(&allocator, size);
		bx::read(&fr, data, size);
		bx::close(&fr);

		bx::FileWriter fw;
		if (bx::open(&fw, outFilePath) )
		{
			Bin2cWriter writer(&allocator, name);
			bx::write(&writer, data, size);

			writer.output(&fw);
			bx::close(&fw);
		}
		else
		{
			bx::StringView path = outFilePath;
			error("Failed to open output file '%.*s'.\n", path.getLength(), path.getPtr() );
		}

		BX_FREE(&allocator, data);
	}
	else
	{
		bx::StringView path = filePath;
		error("Failed to open input file '%.*s'.\n", path.getLength(), path.getPtr() );
	}

	return 0;
}


#include "stdafx.h"
#include "dx10ConstantBuffer.h"

#include "dx10BufferUtils.h"
#include "../xrRender/dxRenderDeviceRender.h"

{
	DEV->_DeleteConstantBuffer(this);
	_RELEASE(m_pBuffer);
	xr_free(m_pBufferData);
}

dx10ConstantBuffer::dx10ConstantBuffer(ID3D10ShaderReflectionConstantBuffer* pTable)
	: m_bChanged(true)
{
	D3D10_SHADER_BUFFER_DESC Desc;

	CHK_DX(pTable->GetDesc(&Desc));

	m_strBufferName._set(Desc.Name);
	m_eBufferType = Desc.Type;
	m_uiBufferSize = Desc.Size;

	//	Fill member list with variable descriptions
	m_MembersList.resize(Desc.Variables);
	m_MembersNames.resize(Desc.Variables);
	for (u32 i=0; i<Desc.Variables; ++i)
	{
		ID3D10ShaderReflectionVariable* pVar;
		ID3D10ShaderReflectionType*		pType;

		D3D10_SHADER_VARIABLE_DESC		var_desc;

		pVar = pTable->GetVariableByIndex(i);
		VERIFY(pVar);
		pType = pVar->GetType();
		VERIFY(pType);
		pType->GetDesc(&m_MembersList[i]);
		//	Buffers with the same layout can contain totally different members
		CHK_DX(pVar->GetDesc(&var_desc));
		m_MembersNames[i] = var_desc.Name;
	}

	m_uiMembersCRC = crc32( &m_MembersList[0], Desc.Variables*sizeof(m_MembersList[0]));

	R_CHK(dx10BufferUtils::CreateConstantBuffer(&m_pBuffer, Desc.Size));
	VERIFY(m_pBuffer);
	m_pBufferData = xr_malloc(Desc.Size);
	VERIFY(m_pBufferData);
}

bool dx10ConstantBuffer::Similar(dx10ConstantBuffer &_in)
{
	if ( m_strBufferName._get() != _in.m_strBufferName._get() )
		return false;

	if ( m_eBufferType != _in.m_eBufferType )
		return false;

	if ( m_uiMembersCRC != _in.m_uiMembersCRC )
		return false;

	if ( m_MembersList.size() != _in.m_MembersList.size() )
		return false;

	if ( memcmp(&m_MembersList[0], &_in.m_MembersList[0], m_MembersList.size()*sizeof(m_MembersList[0])) )
		return false;

	VERIFY(m_MembersNames.size() == _in.m_MembersNames.size());

	int iMemberNum = m_MembersNames.size();
	for ( int i=0; i<iMemberNum; ++i)
	{
		if (m_MembersNames[i].c_str()!=_in.m_MembersNames[i].c_str())
			return false;
	}

	return true;
}

void dx10ConstantBuffer::Flush()
{
	if (m_bChanged)
	{
		void	*pData;
		CHK_DX(m_pBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &pData));
		VERIFY(pData);
		VERIFY(m_pBufferData);
		CopyMemory(pData, m_pBufferData, m_uiBufferSize);
		m_pBuffer->Unmap();
		m_bChanged = false;
	}
}


{
	if (m_bChanged)
	{
		void	*pData;
		CHK_DX(m_pBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &pData));
		VERIFY(pData);
		VERIFY(m_pBufferData);
		CopyMemory(pData, m_pBufferData, m_uiBufferSize);
		m_pBuffer->Unmap();
		m_bChanged = false;
	}
}

#include "stdafx.h"
#include "dx10ConstantBuffer.h"

#include "dx10BufferUtils.h"
#include "../xrRender/dxRenderDeviceRender.h"

dx10ConstantBuffer::~dx10ConstantBuffer()
{
	DEV->_DeleteConstantBuffer(this);
	_RELEASE(m_pBuffer);
	xr_free(m_pBufferData);
}

dx10ConstantBuffer::dx10ConstantBuffer(ID3D10ShaderReflectionConstantBuffer* pTable)
	: m_bChanged(true)
{
	D3D10_SHADER_BUFFER_DESC Desc;

	CHK_DX(pTable->GetDesc(&Desc));

	m_strBufferName._set(Desc.Name);
	m_eBufferType = Desc.Type;
	m_uiBufferSize = Desc.Size;

	//	Fill member list with variable descriptions
	m_MembersList.resize(Desc.Variables);
	m_MembersNames.resize(Desc.Variables);
	for (u32 i=0; i<Desc.Variables; ++i)
	{
		ID3D10ShaderReflectionVariable* pVar;
		ID3D10ShaderReflectionType*		pType;

		D3D10_SHADER_VARIABLE_DESC		var_desc;

		pVar = pTable->GetVariableByIndex(i);
		VERIFY(pVar);
		pType = pVar->GetType();
		VERIFY(pType);
		pType->GetDesc(&m_MembersList[i]);
		//	Buffers with the same layout can contain totally different members
		CHK_DX(pVar->GetDesc(&var_desc));
		m_MembersNames[i] = var_desc.Name;
	}

	m_uiMembersCRC = crc32( &m_MembersList[0], Desc.Variables*sizeof(m_MembersList[0]));

	R_CHK(dx10BufferUtils::CreateConstantBuffer(&m_pBuffer, Desc.Size));
	VERIFY(m_pBuffer);
	m_pBufferData = xr_malloc(Desc.Size);
	VERIFY(m_pBufferData);
}

bool dx10ConstantBuffer::Similar(dx10ConstantBuffer &_in)
{
	if ( m_strBufferName._get() != _in.m_strBufferName._get() )
		return false;

	if ( m_eBufferType != _in.m_eBufferType )
		return false;

	if ( m_uiMembersCRC != _in.m_uiMembersCRC )
		return false;

	if ( m_MembersList.size() != _in.m_MembersList.size() )
		return false;

	if ( memcmp(&m_MembersList[0], &_in.m_MembersList[0], m_MembersList.size()*sizeof(m_MembersList[0])) )
		return false;

	VERIFY(m_MembersNames.size() == _in.m_MembersNames.size());

	int iMemberNum = m_MembersNames.size();
	for ( int i=0; i<iMemberNum; ++i)
	{
		if (m_MembersNames[i].c_str()!=_in.m_MembersNames[i].c_str())
			return false;
	}

	return true;
}

void dx10ConstantBuffer::Flush()


{
	if ( m_strBufferName._get() != _in.m_strBufferName._get() )
		return false;

	if ( m_eBufferType != _in.m_eBufferType )
		return false;

	if ( m_uiMembersCRC != _in.m_uiMembersCRC )
		return false;

#include "stdafx.h"
#include "dx10ConstantBuffer.h"

#include "dx10BufferUtils.h"
#include "../xrRender/dxRenderDeviceRender.h"

dx10ConstantBuffer::~dx10ConstantBuffer()
{
	DEV->_DeleteConstantBuffer(this);
	_RELEASE(m_pBuffer);
	xr_free(m_pBufferData);
}

dx10ConstantBuffer::dx10ConstantBuffer(ID3D10ShaderReflectionConstantBuffer* pTable)
	: m_bChanged(true)
{
	D3D10_SHADER_BUFFER_DESC Desc;

	CHK_DX(pTable->GetDesc(&Desc));

	m_strBufferName._set(Desc.Name);
	m_eBufferType = Desc.Type;
	m_uiBufferSize = Desc.Size;

	//	Fill member list with variable descriptions
	m_MembersList.resize(Desc.Variables);
	m_MembersNames.resize(Desc.Variables);
	for (u32 i=0; i<Desc.Variables; ++i)
	{
		ID3D10ShaderReflectionVariable* pVar;
		ID3D10ShaderReflectionType*		pType;

		D3D10_SHADER_VARIABLE_DESC		var_desc;

		pVar = pTable->GetVariableByIndex(i);
		VERIFY(pVar);
		pType = pVar->GetType();
		VERIFY(pType);
		pType->GetDesc(&m_MembersList[i]);
		//	Buffers with the same layout can contain totally different members
		CHK_DX(pVar->GetDesc(&var_desc));
		m_MembersNames[i] = var_desc.Name;
	}

	m_uiMembersCRC = crc32( &m_MembersList[0], Desc.Variables*sizeof(m_MembersList[0]));

	R_CHK(dx10BufferUtils::CreateConstantBuffer(&m_pBuffer, Desc.Size));
	VERIFY(m_pBuffer);
	m_pBufferData = xr_malloc(Desc.Size);
	VERIFY(m_pBufferData);
}

bool dx10ConstantBuffer::Similar(dx10ConstantBuffer &_in)

	if ( m_MembersList.size() != _in.m_MembersList.size() )
		return false;

	if ( memcmp(&m_MembersList[0], &_in.m_MembersList[0], m_MembersList.size()*sizeof(m_MembersList[0])) )
		return false;

	VERIFY(m_MembersNames.size() == _in.m_MembersNames.size());

	int iMemberNum = m_MembersNames.size();
	for ( int i=0; i<iMemberNum; ++i)
	{
		if (m_MembersNames[i].c_str()!=_in.m_MembersNames[i].c_str())
			return false;
	}

	return true;
}

void dx10ConstantBuffer::Flush()
{
	if (m_bChanged)
	{
		void	*pData;
		CHK_DX(m_pBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &pData));
		VERIFY(pData);
		VERIFY(m_pBufferData);
		CopyMemory(pData, m_pBufferData, m_uiBufferSize);
		m_pBuffer->Unmap();
		m_bChanged = false;
	}
}


if ( m_MembersList.size() != _in.m_MembersList.size() )
		return false;

	if ( memcmp(&m_MembersList[0], &_in.m_MembersList[0], m_MembersList.size()*sizeof(m_MembersList[0])) )
		return false;

	VERIFY(m_MembersNames.size() == _in.m_MembersNames.size());

#include "stdafx.h"
#include "dx10ConstantBuffer.h"

#include "dx10BufferUtils.h"
#include "../xrRender/dxRenderDeviceRender.h"

dx10ConstantBuffer::~dx10ConstantBuffer()
{
	DEV->_DeleteConstantBuffer(this);
	_RELEASE(m_pBuffer);
	xr_free(m_pBufferData);
}

dx10ConstantBuffer::dx10ConstantBuffer(ID3D10ShaderReflectionConstantBuffer* pTable)
	: m_bChanged(true)
{
	D3D10_SHADER_BUFFER_DESC Desc;

	CHK_DX(pTable->GetDesc(&Desc));

	m_strBufferName._set(Desc.Name);
	m_eBufferType = Desc.Type;
	m_uiBufferSize = Desc.Size;

	//	Fill member list with variable descriptions
	m_MembersList.resize(Desc.Variables);
	m_MembersNames.resize(Desc.Variables);
	for (u32 i=0; i<Desc.Variables; ++i)
	{
		ID3D10ShaderReflectionVariable* pVar;
		ID3D10ShaderReflectionType*		pType;

		D3D10_SHADER_VARIABLE_DESC		var_desc;

		pVar = pTable->GetVariableByIndex(i);
		VERIFY(pVar);
		pType = pVar->GetType();
		VERIFY(pType);
		pType->GetDesc(&m_MembersList[i]);
		//	Buffers with the same layout can contain totally different members
		CHK_DX(pVar->GetDesc(&var_desc));
		m_MembersNames[i] = var_desc.Name;
	}

	m_uiMembersCRC = crc32( &m_MembersList[0], Desc.Variables*sizeof(m_MembersList[0]));

	R_CHK(dx10BufferUtils::CreateConstantBuffer(&m_pBuffer, Desc.Size));
	VERIFY(m_pBuffer);
	m_pBufferData = xr_malloc(Desc.Size);
	VERIFY(m_pBufferData);
}

bool dx10ConstantBuffer::Similar(dx10ConstantBuffer &_in)
{
	if ( m_strBufferName._get() != _in.m_strBufferName._get() )
		return false;

	if ( m_eBufferType != _in.m_eBufferType )
		return false;

	if ( m_uiMembersCRC != _in.m_uiMembersCRC )
		return false;

	int iMemberNum = m_MembersNames.size();
	for ( int i=0; i<iMemberNum; ++i)
	{
		if (m_MembersNames[i].c_str()!=_in.m_MembersNames[i].c_str())
			return false;
	}

	return true;
}

void dx10ConstantBuffer::Flush()
{
	if (m_bChanged)
	{
		void	*pData;
		CHK_DX(m_pBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &pData));
		VERIFY(pData);
		VERIFY(m_pBufferData);
		CopyMemory(pData, m_pBufferData, m_uiBufferSize);
		m_pBuffer->Unmap();
		m_bChanged = false;
	}
}


int iMemberNum = m_MembersNames.size();
	for ( int i=0; i<iMemberNum; ++i)
	{
		if (m_MembersNames[i].c_str()!=_in.m_MembersNames[i].c_str())
			return false;
	}

	return true;
}

#include "stdafx.h"
#include "dx10ConstantBuffer.h"

#include "dx10BufferUtils.h"
#include "../xrRender/dxRenderDeviceRender.h"

dx10ConstantBuffer::~dx10ConstantBuffer()
{
	DEV->_DeleteConstantBuffer(this);
	_RELEASE(m_pBuffer);
	xr_free(m_pBufferData);
}

dx10ConstantBuffer::dx10ConstantBuffer(ID3D10ShaderReflectionConstantBuffer* pTable)
	: m_bChanged(true)
{
	D3D10_SHADER_BUFFER_DESC Desc;

	CHK_DX(pTable->GetDesc(&Desc));

	m_strBufferName._set(Desc.Name);
	m_eBufferType = Desc.Type;
	m_uiBufferSize = Desc.Size;

	//	Fill member list with variable descriptions
	m_MembersList.resize(Desc.Variables);
	m_MembersNames.resize(Desc.Variables);
	for (u32 i=0; i<Desc.Variables; ++i)
	{
		ID3D10ShaderReflectionVariable* pVar;
		ID3D10ShaderReflectionType*		pType;

		D3D10_SHADER_VARIABLE_DESC		var_desc;

		pVar = pTable->GetVariableByIndex(i);
		VERIFY(pVar);
		pType = pVar->GetType();
		VERIFY(pType);
		pType->GetDesc(&m_MembersList[i]);
		//	Buffers with the same layout can contain totally different members
		CHK_DX(pVar->GetDesc(&var_desc));
		m_MembersNames[i] = var_desc.Name;
	}

	m_uiMembersCRC = crc32( &m_MembersList[0], Desc.Variables*sizeof(m_MembersList[0]));

	R_CHK(dx10BufferUtils::CreateConstantBuffer(&m_pBuffer, Desc.Size));
	VERIFY(m_pBuffer);
	m_pBufferData = xr_malloc(Desc.Size);
	VERIFY(m_pBufferData);
}

bool dx10ConstantBuffer::Similar(dx10ConstantBuffer &_in)
{
	if ( m_strBufferName._get() != _in.m_strBufferName._get() )
		return false;

	if ( m_eBufferType != _in.m_eBufferType )
		return false;

	if ( m_uiMembersCRC != _in.m_uiMembersCRC )
		return false;

	if ( m_MembersList.size() != _in.m_MembersList.size() )
		return false;

	if ( memcmp(&m_MembersList[0], &_in.m_MembersList[0], m_MembersList.size()*sizeof(m_MembersList[0])) )
		return false;

	VERIFY(m_MembersNames.size() == _in.m_MembersNames.size());

void dx10ConstantBuffer::Flush()
{
	if (m_bChanged)
	{
		void	*pData;
		CHK_DX(m_pBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &pData));
		VERIFY(pData);
		VERIFY(m_pBufferData);
		CopyMemory(pData, m_pBufferData, m_uiBufferSize);
		m_pBuffer->Unmap();
		m_bChanged = false;
	}
}


#include "stdafx.h"
#include "dx10ConstantBuffer.h"

#include "dx10BufferUtils.h"
#include "../xrRender/dxRenderDeviceRender.h"

dx10ConstantBuffer::~dx10ConstantBuffer()
{
	DEV->_DeleteConstantBuffer(this);
	_RELEASE(m_pBuffer);
	xr_free(m_pBufferData);
}

dx10ConstantBuffer::dx10ConstantBuffer(ID3D10ShaderReflectionConstantBuffer* pTable)
	: m_bChanged(true)
{
	D3D10_SHADER_BUFFER_DESC Desc;

	CHK_DX(pTable->GetDesc(&Desc));

	m_strBufferName._set(Desc.Name);
	m_eBufferType = Desc.Type;
	m_uiBufferSize = Desc.S

	//	Fill member list with variable descriptions
	m_MembersList.resize(Desc.Variables);
	m_MembersNames.resize(Desc.Variables);
	for (u32 i=0; i<Desc.Variables; ++i)
	{
		ID3D10ShaderReflectionVariable* pVar;
		ID3D10ShaderReflectionType*		pType;

		D3D10_SHADER_VARIABLE_DESC		var_desc;

		pVar = pTable->GetVariableByIndex(i);
		VERIFY(pVar);
		pType = pVar->GetType();
		VERIFY(pType);
		pType->GetDesc(&m_MembersList[i]);
		//	Buffers with the same layout can contain totally different members
		CHK_DX(pVar->GetDesc(&var_desc));
		m_MembersNames[i] = var_desc.Name;
	}

	m_uiMembersCRC = crc32( &m_MembersList[0], Desc.Variables*sizeof(m_MembersList[0]));

	R_CHK(dx10BufferUtils::CreateConstantBuffer(&m_pBuffer, Desc.Size));
	VERIFY(m_pBuffer);
	m_pBufferData = xr_malloc(Desc.Size);
	VERIFY(m_pBufferData);
}

bool dx10ConstantBuffer::Similar(dx10ConstantBuffer &_in)
{
	if ( m_strBufferName._get() != _in.m_strBufferName._get() )
		return false;

	if ( m_eBufferType != _in.m_eBufferType )
		return false;

	if ( m_uiMembersCRC != _in.m_uiMembersCRC )
		return false;

	if ( m_MembersList.size() != _in.m_MembersList.size() )
		return false;

	if ( memcmp(&m_MembersList[0], &_in.m_MembersList[0], m_MembersList.size()*sizeof(m_MembersList[0])) )
		return false;

	VERIFY(m_MembersNames.size() == _in.m_MembersNames.size());

	int iMemberNum = m_MembersNames.size();
	for ( int i=0; i<iMemberNum; ++i)
	{
		if (m_MembersNames[i].c_str()!=_in.m_MembersNames[i].c_str())
			return false;
	}

	return true;
}

void dx10ConstantBuffer::Flush()
{
	if (m_bChanged)
	{
		void	*pData;
		CHK_DX(m_pBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &pData));
		VERIFY(pData);
		VERIFY(m_pBufferData);
		CopyMemory(pData, m_pBufferData, m_uiBufferSize);
		m_pBuffer->Unmap();
		m_bChanged = false;
	}
}


if ( m_strBufferName._get() != _in.m_strBufferName._get() )
		return false;

#include "stdafx.h"
#include "dx10ConstantBuffer.h"

#include "dx10BufferUtils.h"
#include "../xrRender/dxRenderDeviceRender.h"

dx10ConstantBuffer::~dx10ConstantBuffer()
{
	DEV->_DeleteConstantBuffer(this);
	_RELEASE(m_pBuffer);
	xr_free(m_pBufferData);
}

dx10ConstantBuffer::dx10ConstantBuffer(ID3D10ShaderReflectionConstantBuffer* pTable)
	: m_bChanged(true)
{
	D3D10_SHADER_BUFFER_DESC Desc;

	CHK_DX(pTable->GetDesc(&Desc));

	m_strBufferName._set(Desc.Name);
	m_eBufferType = Desc.Type;
	m_uiBufferSize = Desc.Size;

	//	Fill member list with variable descriptions
	m_MembersList.resize(Desc.Variables);
	m_MembersNames.resize(Desc.Variables);
	for (u32 i=0; i<Desc.Variables; ++i)
	{
		ID3D10ShaderReflectionVariable* pVar;
		ID3D10ShaderReflectionType*		pType;

		D3D10_SHADER_VARIABLE_DESC		var_desc;

		pVar = pTable->GetVariableByIndex(i);
		VERIFY(pVar);
		pType = pVar->GetType();
		VERIFY(pType);
		pType->GetDesc(&m_MembersList[i]);
		//	Buffers with the same layout can contain totally different members
		CHK_DX(pVar->GetDesc(&var_desc));
		m_MembersNames[i] = var_desc.Name;
	}

	m_uiMembersCRC = crc32( &m_MembersList[0], Desc.Variables*sizeof(m_MembersList[0]));

	R_CHK(dx10BufferUtils::CreateConstantBuffer(&m_pBuffer, Desc.Size));
	VERIFY(m_pBuffer);
	m_pBufferData = xr_malloc(Desc.Size);
	VERIFY(m_pBufferData);
}

bool dx10ConstantBuffer::Similar(dx10ConstantBuffer &_in)
{

	if ( m_eBufferType != _in.m_eBufferType )
		return false;

	if ( m_uiMembersCRC != _in.m_uiMembersCRC )
		return false;

	if ( m_MembersList.size() != _in.m_MembersList.size() )
		return false;

	if ( memcmp(&m_MembersList[0], &_in.m_MembersList[0], m_MembersList.size()*sizeof(m_MembersList[0])) )
		return false;

	VERIFY(m_MembersNames.size() == _in.m_MembersNames.size());

	int iMemberNum = m_MembersNames.size();
	for ( int i=0; i<iMemberNum; ++i)
	{
		if (m_MembersNames[i].c_str()!=_in.m_MembersNames[i].c_str())
			return false;
	}

	return true;
}

void dx10ConstantBuffer::Flush()
{
	if (m_bChanged)
	{
		void	*pData;
		CHK_DX(m_pBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &pData));
		VERIFY(pData);
		VERIFY(m_pBufferData);
		CopyMemory(pData, m_pBufferData, m_uiBufferSize);
		m_pBuffer->Unmap();
		m_bChanged = false;
	}
}


for (u32 i=0; i<Desc.Variables; ++i)
	{
		ID3D10ShaderReflectionVariable* pVar;
		ID3D10ShaderReflectionType*		pType;

		D3D10_SHADER_VARIABLE_DESC		var_desc;

		pVar = pTable->GetVariableByIndex(i);
		VERIFY(pVar);
		pType = pVar->GetType();
		VERIFY(pType);
		pType->GetDesc(&m_MembersList[i]);
		//	Buffers with the same layout can contain totally different members
		CHK_DX(pVar->GetDesc(&var_desc));
		m_MembersNames[i] = var_desc.Name;
	}

#include "stdafx.h"
#include "dx10ConstantBuffer.h"

#include "dx10BufferUtils.h"
#include "../xrRender/dxRenderDeviceRender.h"

dx10ConstantBuffer::~dx10ConstantBuffer()
{
	DEV->_DeleteConstantBuffer(this);
	_RELEASE(m_pBuffer);
	xr_free(m_pBufferData);
}

dx10ConstantBuffer::dx10ConstantBuffer(ID3D10ShaderReflectionConstantBuffer* pTable)
	: m_bChanged(true)
{
	D3D10_SHADER_BUFFER_DESC Desc;

	CHK_DX(pTable->GetDesc(&Desc));

	m_strBufferName._set(Desc.Name);
	m_eBufferType = Desc.Type;
	m_uiBufferSize = Desc.Size;

	//	Fill member list with variable descriptions
	m_MembersList.resize(Desc.Variables);
	m_MembersNames.resize(Desc.Variables);

	m_uiMembersCRC = crc32( &m_MembersList[0], Desc.Variables*sizeof(m_MembersList[0]));

	R_CHK(dx10BufferUtils::CreateConstantBuffer(&m_pBuffer, Desc.Size));
	VERIFY(m_pBuffer);
	m_pBufferData = xr_malloc(Desc.Size);
	VERIFY(m_pBufferData);
}

bool dx10ConstantBuffer::Similar(dx10ConstantBuffer &_in)
{
	if ( m_strBufferName._get() != _in.m_strBufferName._get() )
		return false;

	if ( m_eBufferType != _in.m_eBufferType )
		return false;

	if ( m_uiMembersCRC != _in.m_uiMembersCRC )
		return false;

	if ( m_MembersList.size() != _in.m_MembersList.size() )
		return false;

	if ( memcmp(&m_MembersList[0], &_in.m_MembersList[0], m_MembersList.size()*sizeof(m_MembersList[0])) )
		return false;

	VERIFY(m_MembersNames.size() == _in.m_MembersNames.size());

	int iMemberNum = m_MembersNames.size();
	for ( int i=0; i<iMemberNum; ++i)
	{
		if (m_MembersNames[i].c_str()!=_in.m_MembersNames[i].c_str())
			return false;
	}

	return true;
}

void dx10ConstantBuffer::Flush()
{
	if (m_bChanged)
	{
		void	*pData;
		CHK_DX(m_pBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &pData));
		VERIFY(pData);
		VERIFY(m_pBufferData);
		CopyMemory(pData, m_pBufferData, m_uiBufferSize);
		m_pBuffer->Unmap();
		m_bChanged = false;
	}
}


#include "stdafx.h"
#include "dx10ConstantBuffer.h"

#include "dx10BufferUtils.h"
#include "../xrRender/dxRenderDeviceRender.h"

dx10ConstantBuffer::~dx10ConstantBuffer()
{
	DEV->_DeleteConstantBuffer(this);
	_RELEASE(m_pBuffer);
	xr_free(m_pBufferData);
}

dx10ConstantBuffer::dx10ConstantBuffer(ID3D10ShaderReflectionConstantBuffer* pTable)
	: m_bChanged(true)
{
	D3D10_SHADER_BUFFER_DESC Desc;

	CHK_DX(pTable->GetDesc(&Desc));

	m_strBufferName._set(Desc.Name);
	m_eBufferType = Desc.Type;
	m_uiBufferSize = Desc.Size;

	//	Fill member list with variable descriptions
	m_MembersList.resize(Desc.Variables);
	m_MembersNames.resize(Desc.Variables);
	for (u32 i=0; i<Desc.Variables; ++i)
	{
		ID3D10ShaderReflectionVariable* pVar;
		ID3D10ShaderReflectionType*		pType;

		D3D10_SHADER_VARIABLE_DESC		var_desc;

		pVar = pTable->GetVariableByIndex(i);
		VERIFY(pVar);
		pType = pVar->GetType();
		VERIFY(pType);
		pType->GetDesc(&m_MembersList[i]);
		//	Buffers with the same layout can contain totally different members
		CHK_DX(pVar->GetDesc(&var_desc));
		m_MembersNames[i] = var_desc.Name;
	}

	m_uiMembersCRC = crc32( &m_MembersList[0], Desc.Variables*sizeof(m_MembersList[0]));

	R_CHK(dx10BufferUtils::CreateConstantBuffer(&m_pBuffer, Desc.Size));
	VERIFY(m_pBuffer);
	m_pBufferData = xr_malloc(Desc.Size);
	VERIFY(m_pBufferData);
}

bool dx10ConstantBuffer::Similar(dx10ConstantBuffer &_i

n)
{
	if ( m_strBufferName._get() != _in.m_strBufferName._get() )
		return false;

	if ( m_eBufferType != _in.m_eBufferType )
		return false;

	if ( m_uiMembersCRC != _in.m_uiMembersCRC )
		return false;

	if ( m_MembersList.size() != _in.m_MembersList.size() )
		return false;

	if ( memcmp(&m_MembersList[0], &_in.m_MembersList[0], m_MembersList.size()*sizeof(m_MembersList[0])) )
		return false;

	VERIFY(m_MembersNames.size() == _in.m_MembersNames.size());

	int iMemberNum = m_MembersNames.size();
	for ( int i=0; i<iMemberNum; ++i)
	{
		if (m_MembersNames[i].c_str()!=_in.m_MembersNames[i].c_str())
			return false;
	}

	return true;
}

void dx10ConstantBuffer::Flush()
{
	if (m_bChanged)
	{
		void	*pData;
		CHK_DX(m_pBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &pData));
		VERIFY(pData);
		VERIFY(m_pBufferData);
		CopyMemory(pData, m_pBufferData, m_uiBufferSize);
		m_pBuffer->Unmap();
		m_bChanged = false;
	}
}


/*
 *    sfall
 *
 */

#include "..\main.h"
#include "..\FalloutEngine\Fallout2.h"
#include "LoadGameHook.h"

#include "QuestList.h"

namespace sfall
{

enum ExitCode : int {
	Break = -1,
	Normal,
	Error,
	NextQuest
};

static int questsButtonsType;
static int questsScrollButtonsX;
static int questsScrollButtonsY;

static bool buttonsPressed = false;
static bool outRangeFlag = false;
static bool pageFlag = false;
static bool closeFlag = false;
static bool InQuestsList = false;

static bool calledflag = false;
static DWORD called_quest_number = 0;
static DWORD total_quests_pages = 0;
static DWORD curent_quest_page = 0;
static DWORD look_quests = 0; // check current quests

static DWORD first_quest_page = 0;
static DWORD last_quest_page = INT_MAX;

static std::vector<int> pageQuest;

// Fix crash when the quest list is too long
static void __declspec(naked) PipStatus_hook_printfix() {
	__asm {
		test outRangeFlag, 0xFF;
		jnz  force;
		call fo::funcoffs::_word_wrap_;
		push eax;
		movzx eax, word ptr [esp + 0x49C + 8];
		dec  eax;
		shl  eax, 1;
		add  eax, dword ptr ds:[FO_VAR_cursor_line];
		cmp  eax, dword ptr ds:[FO_VAR_bottom_line];  // check max
		jb   skip;
		mov  eax, dword ptr ds:[FO_VAR_quest_count];
		sub  eax, 2;
		mov  dword ptr [esp + 0x4BC - 0x24 + 8], eax; // set last counter
		mov  outRangeFlag, 1;
skip:
		pop  eax;
		retn;
force:
		or   eax, -1; // force log error "out of range"
		mov  outRangeFlag, 0;
		retn;
	}
}

// Add an unused bit for an additional text offset 60 pixels to the right
// it might be needed outside of the function to display quest list pages
static void __declspec(naked) pip_print_hack() {
	__asm {
		test bh, 1;
		jz   skip;
		add  edx, 60; // pixel offset
		jmp  end;
skip:
		test bh, 2;
		jz   end;
		retn;
end:
		add  dword ptr [esp], 0x14;
		retn;
	}
}

static void ResetPageValues() {
	if (total_quests_pages > 0) pageQuest.resize(1);

	pageFlag = false;
	curent_quest_page = 0;
	total_quests_pages = 0;
	first_quest_page = 0;
	last_quest_page = INT_MAX;
}

// Event of entering the quest list and up/down keystrokes
static void __declspec(naked) pipboy_hack_press0() {
	__asm {
		// fixed vanilla bug, preventing displaying the quest list of other locations when clicking on the pipboy screen
		test InQuestsList, 0xFF;
		jz   skip;
		test buttonsPressed, 0xFF;
		jnz  skip;
		add  dword ptr [esp], 0x0E;
		retn;
skip:
		mov  calledflag, 1;
		mov  called_quest_number, ebx
		// engine
		mov  edx, dword ptr ds:[FO_VAR_crnt_func];
		retn;
	}
}

// Pipboy keystroke event
static void __declspec(naked) pipboy_hack_press1() {
	__asm {
		mov  InQuestsList, 0;
		mov  calledflag, 0;
		test pageFlag, 0xFF;
		jz   skip;
		push eax;
		push edx;
		push ecx;
		call ResetPageValues;
		pop  ecx;
		pop  edx;
		pop  eax;
skip:
		// engine
		sub ebx, 0x1F4;
		retn;
	}
}

// Press back button
static void __declspec(naked) pipboy_hack_back() {
	__asm {
		mov  InQuestsList, 0;
		mov  calledflag, 0;
		test pageFlag, 0xFF;
		jz   skip;
		push eax;
		push edx;
		push ecx;
		call ResetPageValues;
		pop  ecx;
		pop  edx;
		pop  eax;
skip:
		// engine
		mov eax, 0x401;
		retn;
	}
}

{
	total_quests_pages++;
	pageQuest.push_back(look_quests); // add the quest number to array at top of page
	fo::var::cursor_line = 3 + lines; // reset current line to beginning, and add text lines
	outRangeFlag = true;
}

// Print quests page text
static long __fastcall QuestsPrint(BYTE* count, int width, DWORD* buf, const char* text) {
	look_quests++; // quests counter

	if (outRangeFlag) {
		if (pageFlag) return ExitCode::Break; // pages are already counted
		// count the number of pages
		fo::func::_word_wrap(text, width, buf, count);

		// check whether the text of the quest leaves the current page
		int lines = 2 * ((int)*count - 1); // number of lines of text
		if (fo::var::cursor_line + lines >= fo::var::bottom_line) {
			AddPage(lines);                // quest does not fit in the current page, we add a page
		} else {
			fo::var::cursor_line += lines;
		}
		return ExitCode::NextQuest;
	}

	// check if current quest is in range
	if (look_quests < first_quest_page) { // check lower range
		return ExitCode::NextQuest;
	}
	if (look_quests > last_quest_page) {  // check upper range
		return ExitCode::Break;           // exit from quests loop
	}

	if (fo::func::_word_wrap(text, width, buf, count) == -1) return ExitCode::Error; // error wrap

	if (!pageFlag || last_quest_page == INT_MAX) {        // pages have not been calculated yet
		// check whether the text of the quest leaves the current page
		int lines = 2 * ((int)*count - 1);
		if (fo::var::cursor_line + lines >= fo::var::bottom_line) {
			AddPage(lines); // quest does not fit in the current page, we add a page
			return ExitCode::NextQuest;
		}
	}
	return ExitCode::Normal;
}

static void __declspec(naked) PipStatus_hack_print() {
	static const DWORD PipStatus_NormalRet = 0x49818B;
	static const DWORD PipStatus_NextRet   = 0x498237;
	static const DWORD PipStatus_BreakRet  = 0x4982A4;
	__asm {
		push ecx;
		push edx;
		push eax;         // text
		push ebx;         // buf
		call QuestsPrint; // ecx - count, edx - width
		pop  edx;
		pop  ecx;
		cmp  eax, 0;
		jl   jbreak;
		cmp  eax, 1;
		jg   jnext;
		jmp  PipStatus_NormalRet;
jnext:
		jmp  PipStatus_NextRet;
jbreak:
		jmp  PipStatus_BreakRet;
	}
}

static char bufPage[16];
static const char* format = "%s %d %s %d";

static void __declspec(naked) PrintPages() {
	__asm {
		// total pages
		mov  eax, total_quests_pages;
		inc  eax;
		push eax;
		mov  ebx, 212;                      // message
		mov  edx, FO_VAR_pipmesg;
		mov  eax, FO_VAR_pipboy_message_file;
		call fo::funcoffs::getmsg_;
		push eax;
		// current page
		mov  eax, curent_quest_page;
		inc  eax;
		push eax;
		mov  ebx, 213;                      // message
		mov  edx, FO_VAR_pipmesg;
		mov  eax, FO_VAR_pipboy_message_file;
		call fo::funcoffs::getmsg_;
		push eax;

		push format;
		lea  eax, [bufPage];
		push eax;                           // text buf
		call fo::funcoffs::sprintf_;
		add  esp, 0x18;

		mov  ebx, 1;
		mov  dword ptr ds:[FO_VAR_cursor_line], ebx;
		mov  bl, byte ptr ds:[FO_VAR_GreenColor];
		mov  edx, 0x21;
		lea  eax, [bufPage];
		jmp  fo::funcoffs::pip_print_;
	}
}

static void __declspec(naked) PipStatus_hook_end() {
	__asm {
		call fo::funcoffs::pip_back_;
		test total_quests_pages, 0xFF;
		jz   skip;
		push edx;
		call PrintPages;
		pop  edx;
skip:
		mov  look_quests, 0;
		mov  outRangeFlag, 0;
		mov  pageFlag, 1;     // pages are calculated, there is no need to do it again
		mov  closeFlag, 1;    // set flag, need to reset on close pipboy
		mov  InQuestsList, 1; // flag for fix bug
		retn;
	}
}

static DWORD __fastcall ActionButtons(DWORD key) {
	buttonsPressed = false;
	if (key == 0x300) { // up
		if (curent_quest_page == 0) return -1;
		last_quest_page = pageQuest[curent_quest_page] - 1;
		curent_quest_page--;
		first_quest_page = pageQuest[curent_quest_page];
		buttonsPressed = true;
		return called_quest_number;
	}
	if (key == 0x301) { // down
		if (curent_quest_page < total_quests_pages) {
			curent_quest_page++;
			first_quest_page = pageQuest[curent_quest_page];
			last_quest_page  = ((pageQuest.size() - 1) > curent_quest_page)
			                 ? pageQuest[curent_quest_page + 1] - 1
			                 : INT_MAX;
			buttonsPressed = true;
			return called_quest_number;
		}
	}
	return -1;
}

static void __declspec(naked) pipboy_hack_action() {
	__asm {
		test calledflag, 0xFF;
		jz   skip;
		push eax;
		push edx;
		mov  ecx, ebx;
		call ActionButtons;
		cmp  eax, -1;
		cmovne ebx, eax; // called_quest_number
		pop  edx;
		pop  eax;
		xor  ecx, ecx;
skip:
		// engine
		cmp  ebx, 0x1F4;
		retn;
	}
}

static void RegisterButtonSound() {
	__asm {
		mov  ebx, fo::funcoffs::gsound_red_butt_release_;
		mov  edx, fo::funcoffs::gsound_red_butt_press_;
		call fo::funcoffs::win_register_button_sound_func_; // eax - register button
	}
}

static void LoadArtButton(DWORD buttonKey, DWORD buttonMem, DWORD indexArt) { // indexArt - index from intrface.lst
	long artId = fo::func::art_id(fo::ArtType::OBJ_TYPE_INTRFACE, indexArt, 0, 0, 0);
	*(BYTE**)buttonMem = fo::func::art_ptr_lock_data(artId, 0, 0, (DWORD*)buttonKey); // first texture memory address
}

// Create buttons
static void __declspec(naked) StartPipboy_hack() {
	__asm {
		push edi;
		mov  ebp, esp; // prolog
		sub  esp, __LOCAL_SIZE;
	}

	DWORD xPos, yPos, height, width, winRef;
	BYTE *picDown, *picUp;

	int indexUpArt0;   // number from intrface.lst for dn button-up
	int indexDownArt0; // for dn button-down
	int indexUpArt1;   // for up button-up
	int indexDownArt1; // for up button-down

	if (questsButtonsType > 1) {
		height = 23;
		width = 22;

		indexUpArt0   = 54;
		indexDownArt0 = 52;
		indexUpArt1   = 53;
		indexDownArt1 = 50;
	} else {
		height = 14;
		width = 11;

		indexUpArt0   = 181;
		indexDownArt0 = 182;
		indexUpArt1   = 199;
		indexDownArt1 = 200;
	}

	// Load new texture for first (up) button. I used memory address for texture from buttons at chracter screen.
	// Everything fine, because this buttons can't use in one time, and they everytime recreating.
	// Down
	LoadArtButton(FO_VAR_optionsButtonUpKey,   FO_VAR_optionsButtonUp,   indexUpArt0);
	LoadArtButton(FO_VAR_optionsButtonDownKey, FO_VAR_optionsButtonDown, indexDownArt0);
	// Up
	LoadArtButton(FO_VAR_optionsButtonUpKey,   FO_VAR_optionsButtonUp1,   indexUpArt1);
	LoadArtButton(FO_VAR_optionsButtonDownKey, FO_VAR_optionsButtonDown1, indexDownArt1);

	xPos = questsScrollButtonsX;
	yPos = questsScrollButtonsY;
	winRef = fo::var::pip_win;

	picDown = (BYTE*)fo::var::optionsButtonDown1;
	picUp   = (BYTE*)fo::var::optionsButtonUp1;
	if (fo::func::win_register_button(winRef, xPos, yPos, width, height, -1, -1, -1, 0x300, picUp,  picDown, 0, 32) != -1) {
		RegisterButtonSound();
	}

	picDown = (BYTE*)fo::var::optionsButtonDown;
	picUp   = (BYTE*)fo::var::optionsButtonUp;
	if (fo::func::win_register_button(winRef, xPos, yPos + height, width, height, -1, -1, -1, 0x301, picUp,  picDown, 0, 32) != -1) {
		RegisterButtonSound();
	}

	__asm {
		mov  esp, ebp;   // epilog
		pop  edi;
		mov  ebp, 0x1F4; // overwrite engine code
		retn;
	}
}

// Reset states when closing pipboy
static void __declspec(naked) pipboy_hook() {
	__asm {
		call fo::funcoffs::EndPipboy_;
		test closeFlag, 0xFF;
		jz   skip;
		call ResetPageValues;
		mov  closeFlag, 0;
skip:
		mov  calledflag, 0;
		mov  InQuestsList, 0;
		retn;
	}
}

static void __fastcall QuestListSort(fo::QuestData* questList, size_t numElements) {
	if (numElements <= 1) return;

	fo::QuestData* tmpList = new fo::QuestData[numElements];
	std::memcpy(tmpList, questList, numElements * sizeof(fo::QuestData));

	long locStart = 1500; // message number from which the locations starting
	size_t leftStart = 0;
	long locLast = 0;

	for (size_t i = 0; i < numElements; i++) {
		long loc = tmpList[i].location;
		if (locStart > loc) locStart = loc;
		if (loc > locLast) locLast = loc;
	}

	for (size_t i = 0; i < numElements;) {
		for (size_t j = leftStart; j < numElements; j++) {
			if (!tmpList[j].location) {
				if ((j - leftStart) == 1) leftStart = j;
				continue;
			}
			if (tmpList[j].location == locStart) {
				if (j != i) questList[i] = tmpList[j];
				if (++i >= numElements) break;
				tmpList[j].location = 0;
			}
		}
		if (++locStart > locLast) break;
	}
	delete[] tmpList;
}

static void __declspec(naked) quest_init_hook() {
	__asm {
		mov  ecx, eax;      // questList (base)
		jmp  QuestListSort; // edx - numElements
	}
}

void QuestListPatch() {
	MakeCall(0x4974E4, StartPipboy_hack);

	MakeCall(0x497173, pipboy_hack_action, 1);

	MakeCall(0x4971B2, pipboy_hack_press0, 1);
	MakeCall(0x497183, pipboy_hack_press1, 1);

	MakeCall(0x4971D9, pipboy_hack_back);
	HookCall(0x497219, pipboy_hook);

	MakeJump(0x498186, PipStatus_hack_print);
	HookCall(0x4982B0, PipStatus_hook_end);

	MakeCall(0x497A7D, pip_print_hack);
}

struct QuestFailure {
	long gvarNum;
	long failureVal;
};

std::vector<QuestFailure> questFailures;

static long FindGVarQuestFailure(long globalVarNum) {
	for (size_t i = 0; i < questFailures.size(); i++) {
		if (questFailures[i].gvarNum == globalVarNum) return i;
	}
	return -1;
}

void QuestList::AddQuestFailureValue(long globalVarNum, long failureThreshold) {
	long index = FindGVarQuestFailure(globalVarNum);
	if (index == -1) {
		questFailures.push_back({ globalVarNum, failureThreshold });
	} else {
		questFailures[index].failureVal = failureThreshold;
	}
}

static BYTE __fastcall CheckQuestFailureState(fo::QuestData* quest, BYTE completeColor) {
	if (questFailures.empty()) return completeColor;

	const BYTE failureColor = 137; // dark red

	long index = FindGVarQuestFailure(quest->gvarIndex);
	return (index != -1 && fo::var::game_global_vars[quest->gvarIndex] >= questFailures[index].failureVal) ? failureColor : completeColor;
}

static void __declspec(naked) PipStatus_hack() {
	__asm {
		push eax;
		push ecx;
		mov  dl, ds:[0x6A5B34]; // completeColor (dark green)
		mov  ecx, ds:[FO_VAR_quests];
		add  ecx, [esp + 0x4BC - 0x28 + 12];
		call CheckQuestFailureState;
		mov  bl, al;
		pop  ecx;
		pop  eax;
		retn;
	}
}

static void ResetQuests() {
	questFailures.clear();
}

void QuestList::init() {
	LoadGameHook::OnGameReset() += ResetQuests;

	MakeCall(0x498222, PipStatus_hack, 1);

	questsButtonsType = IniReader::GetConfigInt("Misc", "UseScrollingQuestsList", 0);
	if (questsButtonsType > 0) {
		dlog("Applying quests list patch.", DL_INIT);
		QuestListPatch();

		questsScrollButtonsX = IniReader::GetConfigInt("Misc", "QuestsScrollButtonsX", 140);
		if (questsScrollButtonsX < 0 || questsScrollButtonsX > 618) questsScrollButtonsX = 140;
		questsScrollButtonsY = IniReader::GetConfigInt("Misc", "QuestsScrollButtonsY", 334);
		if (questsScrollButtonsY < 0 || questsScrollButtonsY > 434) questsScrollButtonsY = 334;

		pageQuest.reserve(4); // init
		pageQuest.push_back(1);

		dlogr(" Done", DL_INIT);
	} else {
		HookCall(0x498186, PipStatus_hook_printfix); // fix "out of bounds" bug when printing a list of quests
	}

	// Replace the qsort_ unstable sorting function with a simple stable sorting algorithm
	HookCall(0x49A7C2, quest_init_hook);
}

}