/*
   Program : Embeded Font Editor 
   Version : 0.6 Alpha
   CopyLeft: (c) 2010, Àþuolas - Faustas Bagdonas.
   Compiler: Microsoft VC 2008 + Windows SDK

   This is OpenSource software
   ------------------------------------------------
   Description: (Not available yet)
*/

#include <windows.h>
#include <windowsx.h>
#include <Commdlg.h>
#include "resource.h"
#include "afxres.h"

/* For BOOL typedef */
#define TRUE		      1  
#define FALSE		      0

#define DEFAULT_VALUES    "8"
#define SQUARE_SIZE       25
#define FIRST_SQ_ID		  0xA000

#define IDB_BACK          0x1000
#define IDB_NEXT 	      0x1001
#define IDB_ENTER         0x1002
#define IDC_ECHAR         0x2000

/* MainLoop() return:      */
#define AGAIN_REALLOC     0x0002
#define AGAIN_DONTREALLOC 0x0003

/* TestCharInput() return: */
#define CHAR              0
#define HEX               1 
#define	DEC               2
#define NOTCHNG           3
#define BAD               4

/* ErrorHandler() errors. See RESOURCE.H for more (IDS_ERR_*) */
#define SYSTEM_ERROR 0

/* Types*/
typedef struct _wh {unsigned short int width;
					unsigned short int height;} WHS;
typedef struct _builddlg {unsigned char bit_align;
						  unsigned char append;
						  unsigned char interval_start;
						  unsigned char interval_end;
						 } BUILDDLG;
typedef int BOOL;

/* Global variables */
const char g_szClassName[] = "MainWndClass";
WHS whs;
char *pbDotSelected; 
unsigned char g_cchar = 'A';
HBITMAP hBmpWhite;
HBITMAP hBmpBlack;

/* Function prototypes */
HWND CreateMainWindow(void);
int MainLoop(int param);
BOOL AskWH(void);
HWND CreateCell(HWND hParentWnd, unsigned int id, unsigned int xpos, unsigned int ypos);
void ErrorHandler(char *szFunction, HWND hWnd, DWORD dwError);
void LoadToMatrix(HWND hWnd, unsigned char chr);
int  TestCharInput(char *lpszInput);
char htoc(char *lpszHex);
BOOL SaveProject (char *lpszFileName);
DWORD LoadProject (char *lpszFileName, HWND hWnd);
void BuildProject(HWND hParentWnd);
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK AskWHDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK BuildDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, BUILDDLG *bd/* LPARAM lParam*/);
void ReverseBits(BYTE *arr, unsigned int len);
BOOL GetBit (unsigned long int absbitn, BYTE *darray);
void PutBit (unsigned long int absbitn, BYTE *darray, BOOL bit);
BOOL SaveBuild(BYTE *bpData, DWORD dwDataLen, DWORD dwBytesPerChar, BUILDDLG bd, HWND hParentWnd);//Argumentai!!!


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){
	int result = 0;
	whs.width = whs.height = atoi(DEFAULT_VALUES);

	/* Memory allocation for new table */
	pbDotSelected = HeapAlloc(GetProcessHeap(),
					HEAP_ZERO_MEMORY, 
					((whs.width*whs.height*256)/8)); //!!?? ((w*h)+1)...) - manau, kad gera esama eilute
	if (pbDotSelected == NULL){
		ErrorHandler("HeapAlloc", NULL, SYSTEM_ERROR);
		return 1;
	}

	/* Load bitmaps from resources */
	hBmpWhite = LoadBitmap(GetModuleHandle(NULL), (LPCSTR)IDB_WHITE);
	if (hBmpWhite == NULL){
		ErrorHandler("LoadBitmap", NULL, SYSTEM_ERROR);
		return 1;
	}
	hBmpBlack = LoadBitmap(GetModuleHandle(NULL), (LPCSTR)IDB_BLACK);
	if (hBmpBlack == NULL){
		ErrorHandler("LoadBitmap", NULL, SYSTEM_ERROR);
		return 1;
	}

	/* Main Loop */
	do result = MainLoop(result);
	while (result > 1);

	/* Free allocated memory and exit */
	DeleteObject(hBmpWhite);
	DeleteObject(hBmpBlack);
	HeapFree(GetProcessHeap(), 0, pbDotSelected); 
	return result;
}
int MainLoop(int param){
	MSG Msg;
	HWND hMainWnd;
	unsigned int x;

	/* Reallocate memory if wanted */
	if (param == AGAIN_REALLOC){
		pbDotSelected = HeapReAlloc(GetProcessHeap(), 
			                        0,
									pbDotSelected, 
									(256 * whs.height * whs.width)/8);
		if (pbDotSelected == NULL){
			ErrorHandler("HeapReAlloc", NULL, SYSTEM_ERROR);
			return 1;
		}

		/* Delete previous data, write zeros to allocated memory */
		for (x = 0; x < (256 * (unsigned int)whs.height * (unsigned int)whs.width) / 8; x++)
			pbDotSelected[x] = 0;
	}

	/* Main window creation */
	if ((hMainWnd = CreateMainWindow()) == 0){
		ErrorHandler("CreateMainWindow", NULL, IDS_ERR_CREATE_MAINWND);
		return 1;
	}
	/* Main window message loop */
	while (GetMessage(&Msg, NULL, 0, 0) > 0){
		TranslateMessage(&Msg);
		DispatchMessage(&Msg);
	}

	return (int)Msg.wParam;
}

HWND CreateMainWindow(void){
	WNDCLASSEX wc;
	HWND hWnd;
	static BOOL bClassRegistered = FALSE;

	/* Creating main window */
	if (!bClassRegistered){
		ZeroMemory(&wc, sizeof(WNDCLASSEX));
		wc.cbSize = sizeof(WNDCLASSEX);
		wc.lpfnWndProc = (WNDPROC)WndProc; 
		wc.hInstance = GetModuleHandle(NULL);
		wc.hIcon = NULL;
		wc.hCursor = LoadCursor(NULL, IDC_ARROW);
		wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
		wc.lpszMenuName = NULL;
		wc.lpszClassName = g_szClassName;
		wc.hIconSm = NULL;

		if (!RegisterClassEx(&wc)){
			ErrorHandler("RegisterClassEx", NULL, SYSTEM_ERROR);
			return (HWND)0;
		}
		bClassRegistered = TRUE;
	}
	hWnd = CreateWindowEx(WS_EX_CLIENTEDGE,
						  g_szClassName,
						  "Embeded Font Editor",
						  WS_CAPTION |WS_MINIMIZEBOX|WS_SYSMENU|WS_VISIBLE|WS_EX_OVERLAPPEDWINDOW,
						  CW_USEDEFAULT,
						  CW_USEDEFAULT,
						  SQUARE_SIZE*whs.width + (GetSystemMetrics(SM_CXFIXEDFRAME)+GetSystemMetrics(SM_CXEDGE))*2+10,    //10 - Atitraukimas nuo wnd krastu(2*5)
						  GetSystemMetrics(SM_CYMENU)+GetSystemMetrics(SM_CXEDGE)*2+GetSystemMetrics(SM_CXFIXEDFRAME)+GetSystemMetrics(SM_CYCAPTION)+SQUARE_SIZE*whs.height + 10 + 22,
						  NULL,
						  LoadMenu(GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_MMENU)), //MENU
						  GetModuleHandle(NULL),
						  NULL);
	if (hWnd == 0)
		ErrorHandler("CreateWindowEx", NULL, SYSTEM_ERROR);
	if (!ShowWindow(hWnd, SW_SHOW))
		ErrorHandler("ShowWindow", NULL, SYSTEM_ERROR);
	UpdateWindow(hWnd);
	return hWnd;
}

BOOL AskWH(void){
	BOOL retres=DialogBox(GetModuleHandle(NULL),
			              MAKEINTRESOURCE(IDD_WHSET),
			              NULL,
						  (DLGPROC)AskWHDlgProc);
	if(!retres)
		ErrorHandler("DialogBox", NULL, SYSTEM_ERROR);
	return retres;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
	static BOOL BadInput = FALSE;
	static HBRUSH hBrush = 0;
	static DWORD WndRet;
	switch (uMsg){
		case WM_CTLCOLOREDIT: //Spalvos keitimas Edit CTRL
			{
				HWND hEdit = (HWND)lParam;
				HDC hdc = (HDC)wParam;
				DeleteObject(hBrush);
				hBrush = CreateSolidBrush(BadInput?RGB(255,0,0):RGB(250,250,255));
				SetBkColor(hdc, BadInput?RGB(255,0,0):RGB(250,250,255));
				return (LRESULT)hBrush;
			}
			break;
		case WM_CREATE:
			{	
				unsigned short int x, y, id;
				RECT rcWin;
				//g_WinResult = 0;
				/* Load Matrix */
				for (y = 5, id = FIRST_SQ_ID; y < whs.height*SQUARE_SIZE; y+=SQUARE_SIZE){
					for(x = 5; x < whs.width*SQUARE_SIZE; x+=SQUARE_SIZE, id++){
						if(CreateCell(hWnd, id, x, y) == NULL){
							/* !!!!VIETOJ MessageBoxA REIKIA NAUDOTI ErrorHandler!!!! */
							MessageBoxA(hWnd, "CreateCell() failed. Failed to load table. Restart program and try again.", "Error", MB_OK | MB_ICONERROR);
							SendMessage(hWnd, WM_CLOSE, 0, 0);
						}
					}
				}
				/* Matrix loaded */
				/* Load Buttons 'Back' & 'Next' & Symbol(Hex) __*/
				GetClientRect(hWnd, &rcWin);
				/* Back */
				CreateWindowEx(0,
							   "BUTTON",
							   "<",
							   WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
							   5,
							   rcWin.bottom - 22,
							   25,
							   20,
							   hWnd,
							   (HMENU)IDB_BACK,
							   GetModuleHandle(NULL),
							   0);
				/* Next */
				CreateWindowEx(0,
							   "BUTTON",
							   ">",
							   WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON,
							   31,
							   rcWin.bottom - 22,
							   25,
							   20,
							   hWnd,
							   (HMENU)IDB_NEXT,
							   GetModuleHandle(NULL),
							   0);
				/* Char (Hex) */
				CreateWindowEx(0,
							   "EDIT",
							   "41(A)",
							   WS_CHILD|WS_VISIBLE|WS_BORDER|ES_CENTER,
							   rcWin.right - 50,
							   rcWin.bottom - 22,
							   44,
							   20,
							   hWnd,
							   (HMENU)IDC_ECHAR,
							   GetModuleHandle(NULL),
							   0);
				/* Buttons loaded */
			}
			break;
		case WM_COMMAND:
			if(LOWORD(wParam)>=FIRST_SQ_ID){
				HWND hCtrl;
				HBITMAP hBitmap;
				char bTableByte;
				unsigned int iTableBitIndex = ((whs.width*whs.height)*(unsigned int)g_cchar)+LOWORD(wParam) - FIRST_SQ_ID;
				unsigned int iShlPos; //<< pos
				BOOL bit;
				/* Inverting color */
				bTableByte = pbDotSelected[iTableBitIndex/8]; //Sveikoji dalis reikalinga
				iShlPos = iTableBitIndex%8;
				bit = (bTableByte >> iShlPos) & 0x0001;
				bit ? (hBitmap = hBmpWhite):(hBitmap=hBmpBlack);
				
				//!bit ? (bTableByte |= (1 << iShlPos)) : (bTableByte = bTableByte (0 << iShlPos));
				bTableByte ^= (1 << iShlPos);
				pbDotSelected[iTableBitIndex/8] = bTableByte;

				hCtrl = GetDlgItem(hWnd, LOWORD(wParam));
				SendMessage(hCtrl, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap);				
			}
			else{
				switch(LOWORD(wParam)){
					/* Menu processing */
					case ID_FILE_NEWPRJ:
						if(!AskWH())
							ErrorHandler("AskWH", hWnd, IDS_ASKWH_FAILED);
						SendMessage(hWnd, WM_CLOSE, AGAIN_REALLOC, 0);
						break;
					case ID_FILE_EXIT:
						SendMessage(hWnd, WM_CLOSE, 0, 0);
						break;
					case ID_FILE_SAVEAS:
						{   
							char szName[MAX_PATH] = "";
							OPENFILENAME sfn;
							ZeroMemory(&sfn,sizeof(OPENFILENAME));
							sfn.lStructSize = sizeof(sfn);
							sfn.hwndOwner = hWnd;
							sfn.lpstrFilter = "Embeded Font Editor Projects (*.EFP)\0*.EFP\0All Files (*.*)\0*.*\0\0";
							sfn.nFilterIndex=1;
							sfn.nMaxFile = MAX_PATH;
							sfn.Flags = OFN_EXPLORER;
							sfn.lpstrTitle = "Save Project As";
							sfn.lpstrFile = szName;
							sfn.lpstrDefExt = "EFP\0";

							if (GetSaveFileName((LPOPENFILENAME)&sfn))
								SaveProject(szName);
						}
						break;
					case ID_FILE_OPENPRJ:
						{
							char szName[MAX_PATH]="";
							OPENFILENAME ofn;
							ZeroMemory(&ofn,sizeof(OPENFILENAME));
							ofn.lStructSize = sizeof(ofn);
							ofn.hwndOwner = hWnd;
							ofn.lpstrFilter = "Embeded Font Editor Projects (*.EFP)\0*.EFP\0All Files (*.*)\0*.*\0\0";
							ofn.nFilterIndex=1;
							ofn.nMaxFile = MAX_PATH;
							ofn.Flags = OFN_EXPLORER;
							ofn.lpstrTitle = "Open Project";
							ofn.lpstrFile = szName;
							ofn.lpstrDefExt = "EFP\0";

							if (GetOpenFileName((LPOPENFILENAME)&ofn))
								LoadProject(szName, hWnd);
						}
						break;
					
					/* Call Build Project function. File -> Build Project. */
					case ID_FILE_BUILD:
						BuildProject(hWnd);
						break;

					/* Pasirinkto simbolio invertavimas */
					case ID_EDIT_INVERT:
						{
							BYTE mask;
							unsigned int n;
							/* Pasirinkto simbolio pradzios adresas masyve (bitais) */
							unsigned int cur_char_index = g_cchar * whs.height * whs.width;
							/* Sekancio simbolio pradzios adresas masyve (bitais) */
							unsigned int next_char_index = (g_cchar + 1)* whs.height * whs.width; ;
						
							/* Simbolio pradinio baito  invertavimo  maske.  Reikalinga, kai
							   simbolio plotas - nelyginis sk, kad nebutu invertuojama dalis
							   kito simbolio.
							   
							   Pvz.: 5 x 6 dydzio simboliai, pasirinktas simbolis nr. 2
							         Tada maske buna 0x03. Jos deka salia esantys simboliai lie-
									 ka neinvertuoti.  											*/
							mask = 0xFF << (cur_char_index % 8);
							/* Maskes taikymas (apply) pasirinktojo simbolio pirmojo baito da-
							   liai.                                                            */
							pbDotSelected[cur_char_index/8] ^= mask; 
							/* Pereinu prie sekanciu 8 bitu. */
							cur_char_index += 8; 
							/* Invertuoju ta simbolio dali, kuriai priklauso pilni baitai */
							for (n = cur_char_index / 8; n < next_char_index/8; n++)
								pbDotSelected[n] = ~pbDotSelected[n];
							/* Invertuoju paskutiniuosius simbolio bitus. Vel naudoju maske, kad
							   nebutu invertuota greta esancio simbolio dalis                       */
							mask = 0xFF >> (8 - (next_char_index % 8));
							pbDotSelected[n] ^= mask;						
						}
						break;
					/* Visos simboliu bitmapines lenteles invertavimas (pbDotSelected[]) */
					case ID_EDIT_INVERTALL:
						{
							unsigned int x;
							/* Invertuoju */
							for (x = 0; x < (256 * (unsigned int)whs.height * (unsigned int)whs.width) / 8; x++)
								pbDotSelected[x] = ~pbDotSelected[x];
							/* Refresh'inu vaizda */
							LoadToMatrix(hWnd ,g_cchar);
						}
						break;

					/* Back & Next Buttons, Edit Control */
					case IDB_BACK:
						if (g_cchar > 0){
							HWND hEditCtrl = GetDlgItem(hWnd, IDC_ECHAR);
							char buff[6];
							g_cchar--;
							if (hEditCtrl == NULL)
								ErrorHandler("GetDlgItem", hWnd, SYSTEM_ERROR);
							else{
								wsprintf(buff, "%0.2X(%c)", g_cchar, g_cchar);
								if (!Edit_SetText(hEditCtrl, buff))
									ErrorHandler("Edit_SetText", hWnd, SYSTEM_ERROR);
							}
						}
						break;
					case IDB_NEXT:
						if (g_cchar < 0xFF){
							HWND hEditCtrl = GetDlgItem(hWnd, IDC_ECHAR);
							char buff[6];
							g_cchar++;
							if (hEditCtrl == NULL)
								ErrorHandler("GetDlgItem", hWnd, SYSTEM_ERROR);
							else{
								wsprintf(buff, "%0.2X(%c)", g_cchar, g_cchar);
								if (!Edit_SetText(hEditCtrl, buff))
									ErrorHandler("Edit_SetText", hWnd, SYSTEM_ERROR);
							}
						}
						break;
					case IDC_ECHAR:
						switch (HIWORD(wParam)){
							case EN_SETFOCUS:
								PostMessage((HWND)lParam, EM_SETSEL, 0, -1);
								break;
					        case EN_CHANGE:
								{
									BOOL NotChanged = FALSE;
									char szInput[6];
									HWND hEditCtrl;
									char NewEditText[7];
									BadInput = FALSE;
									ZeroMemory(szInput, 6);
									szInput[0]=5;  //5 simboliai i buferi
									szInput[1]=0;
									hEditCtrl=GetDlgItem(hWnd,IDC_ECHAR);
									if (hEditCtrl == 0)
										ErrorHandler("GetDlgItem", hWnd, SYSTEM_ERROR);
									SendMessage(hEditCtrl,EM_GETLINE,0,(LPARAM)szInput);
									switch (TestCharInput(szInput)){
										case CHAR: g_cchar = szInput[0];break;
										case HEX : g_cchar = htoc(szInput); break;
										case DEC : g_cchar = atoi(szInput); break;
										case BAD : 											
											BadInput = TRUE;
											NotChanged = TRUE;
											break;
										default: NotChanged = TRUE;
									}
									if ((!BadInput) && (!NotChanged)){
										wsprintf(NewEditText, "%0.2X(%c)",g_cchar, g_cchar);
										Edit_SetText(hEditCtrl, NewEditText);
									}
								}
								break;
						}
				}
				LoadToMatrix(hWnd ,g_cchar); //Rezultatas?!!! On ERROR?
			}
			break;
		case WM_CLOSE:
			WndRet = wParam;
			DestroyWindow(hWnd);
			break;
		case WM_DESTROY:
			{ 
				unsigned int x;
				HWND hCtrl;
				DestroyMenu(GetMenu(hWnd));
				DeleteObject(hBrush);
				for(x = 0; x < (unsigned int)whs.height * (unsigned int)whs.width; x++){
					hCtrl = GetDlgItem(hWnd, FIRST_SQ_ID+x);
					SendMessage(hCtrl, WM_CLOSE, 0, 0);
				}
				hCtrl = GetDlgItem(hWnd, IDB_BACK);
				SendMessage(hCtrl, WM_CLOSE, 0, 0);
				hCtrl = GetDlgItem(hWnd, IDB_NEXT);
				SendMessage(hCtrl, WM_CLOSE, 0, 0);
				hCtrl = GetDlgItem(hWnd, IDC_ECHAR);
				SendMessage(hCtrl, WM_CLOSE, 0, 0);	

				PostQuitMessage(WndRet);
			}
			break;
		default: return DefWindowProc(hWnd, uMsg, wParam, lParam);
	}
	return 0;
}

BOOL CALLBACK AskWHDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam){
	switch(uMsg){
		case WM_INITDIALOG:
			{
				HWND hEditCtrl;
				hEditCtrl = GetDlgItem(hDlg, IDC_EDW);
				Edit_SetText(hEditCtrl, DEFAULT_VALUES);
				hEditCtrl = GetDlgItem(hDlg, IDC_EDH);
				Edit_SetText(hEditCtrl, DEFAULT_VALUES);
			}
			break;
		case WM_COMMAND:
			switch(LOWORD(wParam)){
				case IDOK:
					{
						HWND hEdit;
						char asc_int[4];

						/* Fill WHS and perform data check */
						hEdit = GetDlgItem(hDlg, IDC_EDW);
						if(!Edit_GetLine(hEdit, 0, asc_int, 3)){
							MessageBoxA(hDlg, "Incorrect Width!", "Error", MB_OK|MB_ICONERROR);
							break;
						}
						whs.width = atoi(asc_int);
						//------------------------------------
						hEdit = GetDlgItem(hDlg, IDC_EDH);
						if(!Edit_GetLine(hEdit, 0, asc_int, 3)){
							MessageBoxA(hDlg, "Incorrect Height!", "Error", MB_OK|MB_ICONERROR);
							break;
						}
						whs.height = atoi(asc_int);
						
						//Perform data check (int?)
						EndDialog(hDlg, 1);
					}
					break;
				case IDCANCEL:
					EndDialog(hDlg, 0);
					break;
			}
		default: return FALSE;
	}
	return TRUE;
}

HWND CreateCell(HWND hParentWnd, unsigned int id, unsigned int xpos, unsigned int ypos){
	HWND hStCtl;
	hStCtl=CreateWindowEx(0,
				   "STATIC",
				   "",
				   WS_CHILD|WS_VISIBLE|SS_BITMAP|SS_SUNKEN|SS_NOTIFY, //WHITERECT
				   xpos,
				   ypos,
				   SQUARE_SIZE,  //Kvadrato dydis (nesvarbu, nes pagal bmp)
				   SQUARE_SIZE,
				   hParentWnd,
				   (HMENU)id,
				   GetModuleHandle(NULL),
				   NULL);

	SendMessage(hStCtl, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBmpWhite);
	return hStCtl;
}

void ErrorHandler(char *szFunction, HWND hWnd, DWORD dwError){
    char szMsgBuf[256];
    char *lpFmtMessage;
	const char ErrCodMsg[] = " Error Code: ";

	switch (dwError){
		case SYSTEM_ERROR:
			{
				DWORD dwError = GetLastError(); 
				FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM |
							  FORMAT_MESSAGE_IGNORE_INSERTS,
							  NULL, 
							  dwError,
							  0,
							  (LPSTR)&szMsgBuf,
							  200, NULL );
				//Example: "System Error 0x00000000: Program completed sucessfuly!"
				//         "Function: function()"
				lpFmtMessage = HeapAlloc(GetProcessHeap(),
										 HEAP_ZERO_MEMORY, 
										 strlen(szMsgBuf)+strlen(szFunction)+40);
				wsprintf(lpFmtMessage, 
					     "System Error 0x%0.8X: %s\n\rFunction: %s()",
						 dwError,szMsgBuf,szFunction);	
			}
			break;
		default: LoadString(GetModuleHandle(NULL), IDS_ERR_UNK, szMsgBuf, 256);
	}
	if (dwError != SYSTEM_ERROR){
		lpFmtMessage = HeapAlloc(GetProcessHeap(), 
								 HEAP_ZERO_MEMORY,
								 strlen(szMsgBuf)+strlen(szFunction)+40);
		wsprintf(lpFmtMessage,
				 "%s. Error Code: 0x%0.8X\n\rFunction: %s()", 
				 szMsgBuf, dwError, szFunction);
	}
	MessageBoxA(hWnd, (LPCSTR)lpFmtMessage, "Error", MB_OK|MB_ICONERROR);
    
	/* Free allocated memory */
	HeapFree(GetProcessHeap(),0,lpFmtMessage);
}

void LoadToMatrix(HWND hWnd, unsigned char chr){
	HWND hCurrentCell;
	int CellID;
	unsigned int bitnum;
	char bmpbyte;
	BOOL bit;
	HBITMAP hBitmap;

	for (bitnum=chr*whs.width*whs.height, CellID = FIRST_SQ_ID;
		 CellID < FIRST_SQ_ID + whs.width*whs.height;
		 CellID++, bitnum++){
		hCurrentCell = GetDlgItem(hWnd, CellID); 
		if (!hCurrentCell)
			ErrorHandler("GetDlgItem", hWnd, SYSTEM_ERROR);
		bmpbyte = pbDotSelected[bitnum/8];//+1
		bit = (bmpbyte >> (bitnum%8)) & 0x0001;
		bit ? (hBitmap = hBmpBlack):(hBitmap = hBmpWhite);
		SendMessage(hCurrentCell, STM_SETIMAGE, (WPARAM)IMAGE_BITMAP, (LPARAM)hBitmap);
	}
}

int  TestCharInput(char *lpszInput){
	int len = strlen(lpszInput);
	if(lpszInput[2] != '('){
		if((lpszInput[len-1] =='H')||(lpszInput[len-1] =='h')){
			CharUpper(lpszInput);
			lpszInput[2] = 0;
			if ((lpszInput[0] >= '0') && (lpszInput[0] <= 'F')){
				if ((lpszInput[1] >= '0') && (lpszInput[1] <= 'F'))
					return HEX;
			}
		}
		else if ((lpszInput[len-1] == 'D')||(lpszInput[len-1] == 'd')){
			unsigned int x;
			CharUpper(lpszInput);
			lpszInput[3] = 0;
			for (x = 0;(x < strlen(lpszInput))&&(lpszInput[x]>='0')&&(lpszInput[x]<='9'); x++);
			if (x == 3)
				return DEC;
		}
		else if ((lpszInput[0] == '\'')&&(lpszInput[2] == '\'')){
			lpszInput[0] = lpszInput[1];
			lpszInput[1] = 0;
			return CHAR;
		}

		return BAD;
	}
	return NOTCHNG;
}

char htoc(char *lpszHex){
	char result = 0;
	char cursh, x;
	CharUpper(lpszHex);
	for(cursh = 4,x = 0; x < 2; x++, cursh-=4){
		if ((lpszHex[x] >= '0') && (lpszHex[x] <= '9'))
			result |= (lpszHex[x] - '0') << cursh;
		else if ((lpszHex[x] >= 'A') && (lpszHex[x] <= 'F'))
			result |= (lpszHex[x] - 'A' + 10) << cursh;
	}
	return result;
}
BOOL SaveProject (char *lpszFileName){
	BOOL result = FALSE;
	HANDLE hFile;
	char FileID[5] = "EFEP";
	DWORD dwWritten;
	hFile = CreateFile(lpszFileName,
					   GENERIC_WRITE,
					   0,
					   NULL,
					   CREATE_ALWAYS, //!!!Gal klausti???
					   FILE_ATTRIBUTE_NORMAL,
					   NULL);
	WriteFile(hFile, FileID, strlen(FileID), &dwWritten, NULL);
	WriteFile(hFile, &whs, sizeof(WHS), &dwWritten, NULL);
	if (WriteFile(hFile, pbDotSelected, (whs.width*whs.height*256)/8,&dwWritten,NULL))
		result = TRUE;
	CloseHandle(hFile);
	return result;
}

DWORD LoadProject (char *lpszFileName, HWND hWnd){
	HANDLE hFile;
	char FileID[4];
	DWORD dwRead;
	hFile = CreateFile(lpszFileName,
					   GENERIC_READ,
					   0,
					   NULL,
					   OPEN_EXISTING,
					   FILE_ATTRIBUTE_NORMAL,
					   NULL);
	if (hFile != INVALID_HANDLE_VALUE){
		ReadFile(hFile, FileID, 4, &dwRead, NULL);
		if (!(memcmp(FileID, "EFEP", 4))){
			ReadFile(hFile, &whs, sizeof(WHS), &dwRead, NULL);
			ReadFile(hFile, pbDotSelected, (whs.width*whs.height*256)/8, &dwRead, NULL);
			CloseHandle(hFile);
			//File read successfully!
			SendMessage(hWnd, WM_CLOSE, AGAIN_DONTREALLOC, 0);
		}
	}
	return GetLastError();
}

void BuildProject(HWND hParentWnd){
	BUILDDLG bd;
	BYTE *result;
	DWORD dwResultSize, dwBytesPerChar;
	int fresult;
	unsigned int chrstart, chrend;
	BOOL bComplex=FALSE; //Jei viename baite gali buti vieno simbolio pradzia, o kito pabaiga, tai TRUE

	/* Dialoge prasau vartotojo nurodyti parametrus */
	fresult=DialogBoxParam(GetModuleHandle(NULL),
	 	                   MAKEINTRESOURCE(IDD_BUILD),
		                   hParentWnd,
		                   (DLGPROC)BuildDlgProc,
		                   (LPARAM)&bd);
	if (fresult == -1)
		ErrorHandler("DialogBoxParam", hParentWnd, SYSTEM_ERROR);
	
	else if(fresult == 1){ 
		/* if(fresult == 1){ //ELSE{ ???!!! */
		/* 
		   Bitai baite sioje programoje iprastai isdestomi tokia tvarka:
		     +-----+     
			 |00100| ==> Iprastai: 0x44, 0xC5,... Sukeiciama: 0x22, 0xA3,...
			 |01010|
			 |10001|
			 ^^^^^^^
		   Iprastai sioje programoje kairiausias buna LSB bitas, bet patogiau dirbti
		   buna tada, kai kairiausias buna MSB bitas. Todel bitai baite sukeiciami.
		   Pvz: 10101101 ==> 10110101
		   
		   Nepatogu darbui bitu isdestyma baite pasirinkau neapgalvojes visko. Kadangi
		   nenorejau redaguoti gerai veikiancio kodo, tai papildomai parasiau funkcija
		   ReverseBits().

		   Ateityje butu gerai padaryti, kad visoje programoje butu naudojamas patoges-
		   nis bitu isdestymas. Del versijos, kuri bus ateityje, suderinamumo su dabar-
		   tine versija galima bus sukurti konvertavimo utilita arba funkcija.
		*/

		ReverseBits(pbDotSelected, (256 * whs.height * whs.width)/8);

		/* 
		   Dirbama su vartotojo pasirinktu bitu isdestymu. Rezultatas issaugomas i di-
		   namini masyva *result. Veliau masyvas naudojamas sugeneruoti rezultatu fai-
		   lui.
	    */
		   
		chrstart = bd.interval_start;
		chrend   = bd.interval_end;

		switch(bd.bit_align){
			/* Informacijai apie case x zr. bitmap'uose "build" x+1 ".bmp" */ 
			case 0:
			case 1:
				{
					unsigned long int bitstart, bitend, bitcur;
					int wrtbit;
					unsigned int x = 0;

					dwBytesPerChar = (whs.width/8+((whs.height%8)?1:0))*whs.height;

					dwResultSize = (chrend - chrstart + 1) * whs.height;
					result = HeapAlloc(GetProcessHeap(),  
									   HEAP_ZERO_MEMORY, 
									   dwResultSize);
					if(result == NULL){
						ErrorHandler("HeapAlloc", hParentWnd, SYSTEM_ERROR);
						///Klaida: Truksta atminties...
						break;
					}
					
					bitstart = chrstart * whs.width * whs.height;
					bitend   = (chrend + 1) * whs.width * whs.height;
					
					for (bitcur = bitstart; bitcur < bitend;){//<=
						for (wrtbit = whs.width; wrtbit > 0; wrtbit--){
							result[x] |= (GetBit(bitcur, pbDotSelected) << (wrtbit - 1));
							bitcur++;
						}
						/* Jeigu bitu isdestymas yra pagal build1.bmp, tai reikia shiftinti */
						if (bd.bit_align == 0)
							result[x] = result[x] << (8 - whs.width);
						x++;
					}
				}
				break;
			case 2:
			case 3:
				{
					/* BITU ISDESTYMO SIMBOLYJE APRASYMAS: 

					   Siuo atveju simbolius sudarantys baitai bus isdestomi stulpeliais, jei  simbolio
					   aukstis (height > 8), tai vienas stulpelis uzims daugiau, negu viena baita.  Jei
					   simbolio aukstis nesidalija is 8 (height % 8 != 0), tai kiekvieno stulpelio apa-
					   cioje lieka laisvos vietos, kurioje surasomi nuliai (0).                          */

					/* KINTAMUJU APRASYMAS:

					   x        - jo pagalba apskaiciuojamas bColPos
					   bColPos  - nurodo bito, su kuriuo dirbama (bCurBit), pozicija stulpelyje
					   bBitPos  - bito pozicija baite (skaiciuojama nuo LSB, kaip iprasta shift'inant)
					   bOffset  - baito, i kuri bus rasomas bCurBit bitas, offset'as nuo stulpelio pradzios
					   dwCurBit - bito numeris pradiniame masyve (pbDotSelected), kuri reiks nuskaityti
					   dwColID  - stulpelio, i kuri rasomas bitas, numeris
					   bCharID  - simbolio numeris
					   dwFirstCurCharCol - pasirinkto simbolio pirmojo stulpelio absoliuti pozicija masyve
					   dwFirstColBytePos - pirmojo pasirinkta stulpeli sudarancio baito absoliuti pozicija 
					                       masyve                                                          */

					BYTE x, bColPos, bBitPos, bOffset;
					DWORD dwCurBit = chrstart*whs.width*(whs.height/8 + (whs.height%8 != 0));
					DWORD dwFirstCurCharCol, dwColID, dwFirstColBytePos;
					BYTE bCharID;

					/* Skaiciuoju, kiek reikia rezwervuoti atminties rezultatui */
					dwBytesPerChar = whs.width*(whs.height/8 + (whs.height%8 != 0));
					dwResultSize = (chrend - chrstart + 1) * dwBytesPerChar;
					/* Rezervuojama atmintis masyvui, i kuri bus saugomas rezultatas. */
					result = HeapAlloc(GetProcessHeap(),  
									   HEAP_ZERO_MEMORY, 
									   dwResultSize);
					if(result == NULL){
						ErrorHandler("HeapAlloc", hParentWnd, SYSTEM_ERROR);
						///Klaida: Truksta atminties...
						break;
					}

					/* Algoritmas dirba su kiekvienu simbolliu atskirai.Is masyvo pbDotSelected
					   skaitomi bitai is eiles ir jie isdeliojami po kiekviena  bita  atskirame
					   simboli sudaranciame stulpelyje. Kai taip viena simboli sudaranti eilute
					   isdeliojama, einama prie sekancios. Tai vyksta, kol neatsiduriama pasku-
					   tineje eiluteje.                                                          */
					for(bCharID = chrstart; bCharID < chrend; bCharID++){
						dwFirstCurCharCol = bCharID * whs.width;
						/* Siame cikle isdestau reikiamo simbolio bitus kita tvarka i masyva result */
						for(x = 0; x < whs.height; x++){
							dwColID = dwFirstCurCharCol;
							do{
								dwFirstColBytePos = (whs.width/8 + (whs.width%8 != 0)) * dwColID; 
								/* Skaiciuoju pozicija stulpelyje, kur turesiu deti 
								   GetBit(bCurBit, pbDotSelected) bita.             */
								bColPos = whs.height - 1 - x; 
							
								bOffset = bColPos / 8;
								bBitPos = bColPos % 8;
								
								//BACKUP: result[dwFirstColBytePos + (DWORD)bOffset] |= (GetBit(dwCurBit, pbDotSelected) << (bBitPos + ((whs.height%8)?(8-(whs.height%8)):0)));
								
								/* Rasomas bitas i rezultata. Svarbu, kad sios eilutes rezultata lemia ir bd.bit_align */
								result[dwFirstColBytePos + (DWORD)bOffset] |= (GetBit(dwCurBit, pbDotSelected) << (bBitPos + ((bd.bit_align == 2)?0:((whs.height%8)?(8-(whs.height%8)):0))));
								
								dwCurBit++; dwColID++;
							}
							while(dwColID - dwFirstCurCharCol < whs.width);
						}
					}
				}
				break;
			case 4:
				{
					/* BITU ISDESTYMO SIMBOLYJE APRASYMAS: 

					   Bitai isdestomi eilute, kaip ir pbDotSelected masyve. Pavyzdziui, jei
					   n yra simbolio pirmasis bitas. Tada n+1 - sekantis bitas toje pacioje 
					   eiluteje (1;0). n+whs.witdth bus (0;1) koord. simbolyje. 
					   
					   Jei simbolio uzimamu bitu skaicius nesidalija is astuoniu, t.y.
					   (width*height)%8 != 0, tai gali buti iterpiami bitai pradzioje arba
					   pabaigoje. Tai valdo if(bd.append==x){...} salyginiai sakiniai.                  */

					/* KINTAMUJU APRASYMAS:
					   
						dwDstPos  - bito pozicija result masyve
                        dwSrcPos  - bito pozicija pbDotSelected masyve
						bChrID    - simbolio, su kurio dirbama, ID                              		*/
				
					unsigned long int dwDstPos = 0, dwSrcPos;
					unsigned char bChrID;

					/* Skaiciuoju, kiek uzims rezultatas masyve                                         */
					if(bd.append == 2/*IDC_RNONE*/) //DEFINE!!!
						dwResultSize = ((whs.width*whs.height*(chrend-chrstart+1))/8)+(((whs.width*whs.height*(chrend-chrstart+1))%8)!=0); //kai ner tarpu pr/pb
					else
						dwResultSize = ((whs.width*whs.height/8)+(((whs.height*whs.width)%8)!=0))*(chrend-chrstart+1);

					/* Rezervuojama atmintis masyvui, i kuri bus saugomas rezultatas. */
					result = HeapAlloc(GetProcessHeap(),  
									   HEAP_ZERO_MEMORY, 
									   dwResultSize);
					if(result == NULL){
						ErrorHandler("HeapAlloc", hParentWnd, SYSTEM_ERROR);
						///Klaida: Truksta atminties...
						break;
					}
					
					for (bChrID = chrstart; bChrID < chrend; bChrID++){
						/* Jei pasirinkta "tusti bitai pradzioje" */
						if(bd.append == 0)
							dwDstPos += (8 - (whs.width*whs.height%8));
						/* Kopijuoju simbolio bitus               */
						for (dwSrcPos = whs.width*whs.height*bChrID; dwSrcPos < whs.width*whs.height*(bChrID+1); dwSrcPos++){ 
							PutBit(dwDstPos++, result, GetBit(dwSrcPos, pbDotSelected));							
						}
						/* Jei pasirinkta "tusti bitai pabaigoje" */
						if(bd.append == 1)
							dwDstPos += (8 - (whs.width*whs.height%8));
					}
					dwBytesPerChar = 0;
				}
				break;
			case 5:
				{
					/* KINTAMUJU APRASYMAS:
					   
					   dwDstPos     - rasomo bito pozicija result masyve	
					   dwSrcPos     - skaitomo bito pozicija pbDotSelected masyve
					   dwSrcPradPos - stulpelio, su kuriuo bus dirbama, pirmojo bito
					                  pozicija. Reikalinga, kad butu zinoma, i kokia
									  reiksme sugrazinti dwSrcPos po vieno stulpelio
									  surasymo, kad galeciau rasyti kita stulpeli    */
										
					DWORD dwDstPos = 0;
					DWORD dwSrcPos, dwSrcPradPos;
					BYTE bChrID;

					/* Skaiciuoju, kiek uzims rezultatas masyve. IF (be_tarpu)...ELSE(su_tarpais).      */
					if(bd.append == 2/*IDC_RNONE*/) //DEFINE!!!
						dwResultSize = ((whs.width*whs.height*(chrend-chrstart+1))/8)+(((whs.width*whs.height*(chrend-chrstart+1))%8)!=0); //kai ner tarpu pr/pb
					else
						dwResultSize = ((whs.width*whs.height/8)+(((whs.height*whs.width)%8)!=0))*(chrend-chrstart+1);

					/* Rezervuojama atmintis masyvui, i kuri bus saugomas rezultatas.     */
					result = HeapAlloc(GetProcessHeap(),  
									   HEAP_ZERO_MEMORY, 
									   dwResultSize);
					if(result == NULL){
						ErrorHandler("HeapAlloc", hParentWnd, SYSTEM_ERROR);
						///Klaida: Truksta atminties...
						break;
					}

					for (bChrID = chrstart; bChrID < chrend; bChrID++){
						/* Randu reikiamo chr.(bChrID) pradzios bita masyve pbDotSelected */
						dwSrcPos = dwSrcPradPos = whs.width * whs.height * bChrID;
						/* Jei pasirinkta "tusti bitai pradzioje" */
						if(bd.append == 0)
							dwDstPos += (8 - (whs.width*whs.height%8));
						do{
							do{
								/* Kopijuoju reikiama bita */
								PutBit(dwDstPos, result, GetBit(dwSrcPos, pbDotSelected));
								/* Atnaujinu pozicijas     */
								dwSrcPos += whs.width;
								dwDstPos++;
							}
							while(dwSrcPos <= (whs.width*whs.height*(bChrID+1))-1);
							/* DO-WHILE tikrina,ar nevirsijama bito ID neiseina uz dabar-
							   tinio simbolio ribu.                                       */
							dwSrcPos = ++dwSrcPradPos;
						}
						while(dwSrcPradPos < whs.width*whs.height*bChrID+whs.width/*(whs.width*whs.height*(bChrID+1))-1)*/);		
						/* DO-WHILE tikrina,ar dwSrcPradPos nepasieke dabartinio simbolio
						   paskutinio bito                                                */
						if (bd.append == 1)
							dwDstPos += (8 - (whs.width*whs.height%8));
					}
					dwBytesPerChar = 0;
				}
		}
		
		/* Rezultatu failo issaugojimas, VYKDOMA TIK TADA, JEI NEBUVO ATSAUKTA! */
		SaveBuild(result, dwResultSize, dwBytesPerChar, bd, hParentWnd);
		/* Del suderinamumo bitai baite vel sukeiciami vietomis */
		ReverseBits(pbDotSelected, (256 * whs.height * whs.width)/8);
		/* Atlaisvinama nebereikalinga atmintis */
		HeapFree(GetProcessHeap(), 0, result); 
	}	
}

BOOL CALLBACK BuildDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, BUILDDLG *bd /*LPARAM lParam*/){
	static HBITMAP build[6];
	static BUILDDLG *pbd;
	switch(uMsg){
		case WM_INITDIALOG:
			{
				HWND hWndCtrl;
				int x;
				
				/* Check first (initial) Radio Button */
				CheckRadioButton(hDlg,
								 IDC_BUILD1,
								 IDC_BUILD4,
								 IDC_BUILD1);
				CheckRadioButton(hDlg,
								 IDC_RBEFORE,
								 IDC_RNONE,
								 IDC_RNONE);
				
				/* Load bitmaps to memory */
				/* !!!Tikrinti, kad butu nr. is eiles!!! */
				for (x = IDB_BUILD1; x <= IDB_BUILD6; x++){
					build[x - IDB_BUILD1] = LoadBitmap(GetModuleHandle(NULL),
													   MAKEINTRESOURCE(x));
					if (build[x - IDB_BUILD1] == NULL)
						ErrorHandler("LoadBitmap", NULL, SYSTEM_ERROR);
				}
    
				/* Assign Bitmaps to Radio Buttons */
				for (x = IDC_BUILD1; x <= IDC_BUILD6; x++){
					if((hWndCtrl=GetDlgItem(hDlg, x)) == NULL)
						ErrorHandler("GetDlgItem", NULL, SYSTEM_ERROR);
					SendMessage(hWndCtrl, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)build[x - IDC_BUILD1]);
				}

				/* Set Edit Control text. Interval [0; 255] */
				hWndCtrl = GetDlgItem(hDlg, IDC_EIBEGIN);
				Edit_SetText(hWndCtrl, "0");
				hWndCtrl = GetDlgItem(hDlg, IDC_EIEND);
				Edit_SetText(hWndCtrl, "255");

				/* Where to place Dialog filling information */
				pbd = bd;
			}			
		break;
		case WM_COMMAND:
			{
				LRESULT checked[2];
				HWND hRad[2];
				HWND hEDCtrl;
				int x;

				/* Tikrinti ar pazymetas IDC_BUILD5 || IDC_BUILD6. 
				   Jei taip - ijungti radio buttons. Kitaip isjungti.  */
				hRad[0] = GetDlgItem(hDlg, IDC_BUILD5);
				checked[0] = SendMessage(hRad[0], BM_GETCHECK, 0, 0);
				hRad[1] = GetDlgItem(hDlg, IDC_BUILD6);
				checked[1] = SendMessage(hRad[1], BM_GETCHECK, 0, 0);
				for (x = IDC_RBEFORE; x <= IDC_RNONE; x++){
					hEDCtrl = GetDlgItem(hDlg, x);
					EnableWindow(hEDCtrl, (checked[0] == BST_CHECKED) || (checked[1] == BST_CHECKED));
				}

				switch(LOWORD(wParam)){
					case IDCANCEL:
						SendMessage(hDlg, WM_CLOSE, 0, 0);
						break;
				}
			}
			switch(LOWORD(wParam)){
				case IDC_BUILD:
					{
						int x, start, end;
						HWND hWndCtrl;
						char buff[5];

						/* Randu, pasirinkta radiobutton is Bit Align */
						for (x = IDC_BUILD1; x <= IDC_BUILD6; x++){
							hWndCtrl = GetDlgItem(hDlg, x);
							if (!hWndCtrl)
								ErrorHandler("GetDlgItem", hDlg, SYSTEM_ERROR);
							if (SendMessage(hWndCtrl, BM_GETCHECK, 0, 0) == BST_CHECKED)
								break;
						}
						pbd->bit_align = x - IDC_BUILD1;

						/* Jei pasirinktas 5 ar 6, tai irasau i BUIDDLG *pbd ir 
						   append bits (sekantys Radio Button'ai)               */
						if ((x == IDC_BUILD5) || (x == IDC_BUILD6)){
							for (x = IDC_RBEFORE; x <= IDC_RNONE; x++){
								hWndCtrl = GetDlgItem(hDlg, x);
								if (!hWndCtrl)
									ErrorHandler("GetDlgItem", hDlg, SYSTEM_ERROR);
								if (SendMessage(hWndCtrl, BM_GETCHECK, 0, 0) == BST_CHECKED)
									break;
							}
							pbd->append = x - IDC_RBEFORE;
						}
						
						/* Interval */
						hWndCtrl = GetDlgItem(hDlg, IDC_EIBEGIN);
						Edit_GetText(hWndCtrl, buff, 5);
						start = atoi(buff);

						hWndCtrl = GetDlgItem(hDlg, IDC_EIEND);
						Edit_GetText(hWndCtrl, buff, 5);
						end = atoi(buff);

						/* Interval Validation */
						if (((start >= 0)&&(start < 255)) && ((end > 0)&&(end < 256)) && (start < end)){
							pbd->interval_start = (unsigned char)start;
							pbd->interval_end = (unsigned char)end;
							EndDialog(hDlg,	1);
						}
						else
							ErrorHandler("BuildDlgProc", hDlg, IDS_INVALID_DATA);
					}
				break;
			}
			break;
		case WM_CLOSE:
			{	
				int x;

				/* Free memory */
				for (x = 0; x < 6; x++)
					DeleteObject(build[x]);
				
				/* Close dialog */
				EndDialog(hDlg, 0);
			}
			break;
		default: return FALSE;
	}
	return TRUE;
}

BOOL GetBit (unsigned long int absbitn, BYTE *darray){
	unsigned int byten, bitn;
	BYTE byte;

	byten = (absbitn) / 8;
	bitn  = absbitn - (byten * 8); //(absbitn+1) % 8;

	byte = darray[byten]; 

	return (byte >> (7 - bitn)) & 0x01;
}

void PutBit (unsigned long int absbitn, BYTE *darray, BOOL bit){
	unsigned long int ByteOffset = absbitn/8;
	unsigned char BitID = absbitn%8;

	darray[ByteOffset] &= (~(1 << (7 - BitID)));
	darray[ByteOffset] |= (bit << (7 - BitID));
}

void ReverseBits(BYTE *arr, unsigned int len){
	const BYTE mask = 0x01;
	BYTE byte;
	unsigned int x;
	signed int y;
	for (x = 0; x < len; x++){
		byte = arr[x];
		arr[x] = 0;
		for (y = 7; y >= 0; y--)
			arr[x] |= (((byte >> y) & mask) << (7 - y));
	}
}
BOOL SaveBuild(BYTE *bpData, DWORD dwDataLen, DWORD dwBytesPerChr, BUILDDLG bd, HWND hParentWnd){
	OPENFILENAME sfn;
	char szName[MAX_PATH] = "";
	BOOL bComplex = FALSE; //TRUE pasidaro, jei viename baite gali buti vieno simbolio pabaiga, o kito pradzia
	if (!dwBytesPerChr){
		bComplex = TRUE;
		dwBytesPerChr = 8;
	}

	/* Pirmas zingsnis: failu issaugojimo dialogas */
	ZeroMemory(&sfn,sizeof(OPENFILENAME));
	sfn.lStructSize = sizeof(sfn);
	sfn.hwndOwner = hParentWnd;
	sfn.lpstrFilter = "Binary Files (*.BIN)\0*.BIN\0"
					  "C/C++ Source Code Files (*.C)\0*.C\0"
					  "Macro Assembler Source Code Files (*.ASM)\0*.ASM\0\0";
	sfn.nFilterIndex=1;
	sfn.nMaxFile = MAX_PATH;
	sfn.Flags = OFN_EXPLORER;
	sfn.lpstrTitle = "Build Project";
	sfn.lpstrFile = szName;
	sfn.lpstrDefExt = "BIN\0";

	if (GetSaveFileName((LPOPENFILENAME)&sfn)){
		/* Kuriamas failas */
		HANDLE hFile = CreateFile(szName,
								  GENERIC_WRITE,
								  0,
								  NULL,
								  CREATE_ALWAYS,//!!!!!!!!!!!!!
								  FILE_ATTRIBUTE_NORMAL,
								  0);
		if (hFile){
			switch(sfn.nFilterIndex){
				/* *.BIN */
				case 1:
					{
						DWORD BytesToWrite = dwDataLen;
						DWORD BytesWritten;

						WriteFile(hFile, bpData, BytesToWrite, &BytesWritten, NULL);

						if(BytesToWrite != BytesWritten){
							MessageBoxA(hParentWnd, "ERR. WriteFile()", "ERR", MB_OK|MB_ICONERROR);
						}
						else{
							MessageBoxA(hParentWnd, 
									    "WriteFile() sucessfull!", 
										"Info", 
										MB_OK|MB_ICONINFORMATION);
						}
					}
					break;
				/* *.C */
				case 2:
					{
						const char comment[] = "/* Generated with Embeded Font Editor by Azuolas - Faustas Bagdonas */\r\n\r\n";
						const char type[] = "char charset[";
						const char prad[] = "] = {";
						DWORD BytesToWrite = strlen(comment);
						DWORD BytesWritten;
						//DWORD DataSize = ////(((bd.interval_end - bd.interval_start + 1)*whs.width*whs.height)/8);
						DWORD SpacesBeforeData;
						DWORD ByteNr;
						//DWORD dwBytesPerChr = ((DWORD)whs.width * (DWORD)whs.height) / 8 + 1;
						DWORD dwFirstDim = dwDataLen / dwBytesPerChr;
						char DataSizeAscii[10 + sizeof(prad)]; 
						char Data[19]; //Pagal didziausia duomenu kieki, kuris bus rasomas i masyva.

						/* Rasomas komentaras */
						WriteFile(hFile, comment, BytesToWrite, (LPDWORD)&BytesWritten, NULL);

						/* Rasoma, kiek uzims duomenys */
						BytesToWrite = strlen(type);
						WriteFile(hFile, type, BytesToWrite, (LPDWORD)&BytesWritten, NULL);
						SpacesBeforeData = BytesWritten;
						/* Example: char charset[256][8] = {... */
						/* Rasoma sitas:        ^^^^^^^^        */
						if (bComplex)
							wsprintf(DataSizeAscii, "%u%s", dwDataLen, prad);
						else
							wsprintf(DataSizeAscii, "%u][%u%s", dwFirstDim, dwBytesPerChr, prad);
						BytesToWrite = strlen(DataSizeAscii);
						WriteFile(hFile, DataSizeAscii, BytesToWrite, (LPDWORD)&BytesWritten, NULL);
						SpacesBeforeData += BytesWritten; 

						/* Rasomi duomenys */
						for (ByteNr = 0; ByteNr < dwDataLen; ByteNr++){ ///<=
							/* Duomenu rasymas i faila */
							wsprintf(Data, "0x%0.2X%c ", bpData[ByteNr], (ByteNr + 1 == dwDataLen) ? ' ' : ',');
							BytesToWrite = strlen(Data);
							WriteFile(hFile, Data, BytesToWrite, (LPDWORD)&BytesWritten, NULL);
						    if(BytesToWrite != BytesWritten){
						    	MessageBoxA(hParentWnd, "ERR. WriteFile()", "ERR", MB_OK|MB_ICONERROR);//ERROR HANDLER!!!
								break;
							}	

							/* Ar jau reikia pereiti i nauja eilute? */
							if (!((ByteNr + 1) % dwBytesPerChr)){///!!!
								DWORD x;
								/* Isvedamas simbolio NR., ir koks simbolis yra dabartineje eiluteje, 
								   jeigu tai nera CONTROL CHARACTER ir ne bComplex rezimas					*/
								if (!bComplex){
									wsprintf(Data, 
											 "//0x%0.2X - %u - '%c'", 
											 ByteNr/dwBytesPerChr + bd.interval_start,
											 ByteNr/dwBytesPerChr + bd.interval_start, 
											 ((ByteNr/dwBytesPerChr + bd.interval_start)>=0x20)?ByteNr/dwBytesPerChr + bd.interval_start:'?');
									WriteFile(hFile, Data, strlen(Data), (LPDWORD)&BytesWritten, NULL);
								}
								/* Perejimas i nauja eilute */
								WriteFile(hFile, "\r\n", 2, (LPDWORD)&BytesWritten, NULL);
								/* Tarpai naujos eilutes pradzioje (lygiavimui) */
								for (x = 0; x < SpacesBeforeData; x++)
									WriteFile(hFile, " ", 1, (LPDWORD)&BytesWritten, NULL);
								
							}
						}
						WriteFile(hFile, "};\r\n", 4, (LPDWORD)&BytesWritten, NULL);
					    if(4 != BytesWritten){
						   	MessageBoxA(hParentWnd, "ERR. WriteFile()", "ERR", MB_OK|MB_ICONERROR);
							break;
						}	
						if (bd.interval_start){
							char szOffset[11]; //Didziausias galimas offset "0xFE - 254\0"
							wsprintf(szOffset, "0x%0.2X - %u", bd.interval_start, bd.interval_start);
							WriteFile(hFile, "\r\n//Offset (HEX - DEC): ", 24, (LPDWORD)&BytesWritten, NULL);
							WriteFile(hFile, szOffset, strlen(szOffset), (LPDWORD)&BytesWritten, NULL);
							WriteFile(hFile, ".\r\n", 3, (LPDWORD)&BytesWritten, NULL);
							if(3 != BytesWritten){
						    	MessageBoxA(hParentWnd, "ERR. WriteFile()", "ERR", MB_OK|MB_ICONERROR);//ERROR HANDLER!!!
								break;
							}
						}
					}
					break;
				/* Cia bus case'as ASM failui */
			} 
			/* Uzdarau rezultatu faila */
			CloseHandle(hFile);
		}
		/* Jeigu failo kurimas nepavyko */
		else{
			ErrorHandler("CreateFile", hParentWnd, SYSTEM_ERROR);
			return FALSE;
		}
	}
	return TRUE;	
}

/* ----------------------------------[ EOF "main.c" ]---------------------------------- */
