Index: viewer.hpp
===================================================================
--- viewer.hpp	(revision 5894)
+++ viewer.hpp	(working copy)
@@ -163,7 +163,11 @@
 		bool m_bQuickView;
 
 		UINT DefCodePage;
+
 		int vgetc_ready;
+		__int64 EOF_Pos;
+		int update_check_period;
+		DWORD last_update_check;
 
 	private:
 		virtual void DisplayObject();
Index: cache.cpp
===================================================================
--- cache.cpp	(revision 5894)
+++ cache.cpp	(working copy)
@@ -128,30 +128,39 @@
 	{
 		INT64 Pointer=0;
 		Pointer = file.GetPointer();
-		bool Bidirection=false;
-		if(Pointer>BufferSize/2)
+
+		int shift = (int)Pointer & 0x7ff; // 2k pointer alignment for \\.\CdRom0
+      if (Pointer-shift > BufferSize/2)
+			shift += BufferSize/2;         // suppose BufferSize/2 is 2k aligned...
+
+		if (shift)
+			file.SetPointer(-shift, nullptr, FILE_CURRENT);
+
+      DWORD read_size = BufferSize;
+		UINT64 FileSize = 0;
+		if (file.GetSize(FileSize) && Pointer-shift+BufferSize > (INT64)FileSize)
+			read_size = (DWORD)((INT64)FileSize-Pointer+shift);
+
+		Result = file.Read(Buffer, read_size, ReadSize);
+		if (Result)
 		{
-			Bidirection=true;
-			file.SetPointer(-BufferSize/2, nullptr, FILE_CURRENT);
-		}
-		Result = file.Read(Buffer, BufferSize, ReadSize);
-		if(Result)
-		{
-			BytesLeft = ReadSize;
-			if(Bidirection && BytesLeft>=BufferSize/2)
+			if (ReadSize > (DWORD)shift)
 			{
-				BytesLeft-=BufferSize/2;
+				BytesLeft = ReadSize - shift;
+				file.SetPointer(Pointer, nullptr, FILE_BEGIN);
 			}
-			file.SetPointer(Pointer, nullptr, FILE_BEGIN);
+			else
+				BytesLeft = 0;
 		}
 		else
 		{
-			if (Bidirection)
+			if (shift)
 				file.SetPointer(Pointer, nullptr, FILE_BEGIN);
 			ReadSize=0;
 			BytesLeft=0;
 		}
 	}
+
 	return Result;
 }
 
Index: viewer.cpp
===================================================================
--- viewer.cpp	(revision 5894)
+++ viewer.cpp	(working copy)
@@ -128,6 +128,7 @@
 	bVE_READ_Sent = false;
 	Signature = false;
 	vgetc_ready = -1;
+   EOF_Pos = -1;
 }
 
 
@@ -376,6 +377,31 @@
 	   пора легализироваться */
 	CtrlObject->Plugins.ProcessViewerEvent(VE_READ,nullptr);
 	bVE_READ_Sent = true;
+
+	last_update_check = GetTickCount();
+	string strRoot;
+	GetPathRoot(strFullFileName, strRoot);
+	int DriveType = FAR_GetDriveType(strRoot);
+	if (IsDriveTypeCDROM(DriveType))
+		DriveType = DRIVE_CDROM;
+	switch (DriveType) //??? make it configurable
+	{
+		case DRIVE_REMOVABLE: update_check_period = -1;   break; // flash drive or floppy: never
+		case DRIVE_FIXED:     update_check_period = +1;   break; // hard disk: 1msec
+		case DRIVE_REMOTE:    update_check_period = 1000; break; // network drive: 1sec
+		case DRIVE_CDROM:     update_check_period = -1;   break; // cd/dvd: never
+		case DRIVE_RAMDISK:   update_check_period = +1;   break; // ramdrive: 1msec
+		default:              update_check_period = -1;   break; // unknown: never
+	}
+
+	// возможно не лучший метод определения специальных файлов:
+	//  \\.\C: \\.\D: \\.\PhysucalDrive0 \\.\CdRom0 ....
+	if (INVALID_FILE_ATTRIBUTES == GetFileAttributes(strFullFileName))
+	{
+		DWORD dummy;
+		ViewFile.IoControl(FSCTL_ALLOW_EXTENDED_DASD_IO, NULL,0, NULL,0, &dummy);
+	}
+
 	return TRUE;
 }
 
@@ -454,7 +480,11 @@
 	if (!SelectSize)
 		SelectPos=FilePos;
 
-	switch (nMode)
+   __int64 last_filepos = -1;
+   bool eof_mode = EOF_Pos >= 0 && nMode == SHOW_RELOAD;
+   do {
+   
+   switch (nMode)
 	{
 		case SHOW_HEX:
 			CtrlObject->Plugins.CurViewer = this; //HostFileViewer;
@@ -473,6 +503,13 @@
 				ReadString(Strings[I],-1,MAX_VIEWLINEB);
 			}
 
+         if (eof_mode)
+         {
+            last_filepos = vtell();
+            eof_mode = last_filepos < EOF_Pos;
+            if (eof_mode)
+               nMode = SHOW_DOWN;
+         }
 			break;
 		case SHOW_UP:
 
@@ -506,8 +543,20 @@
 			ReadString(Strings[Y2-Y1],-1,MAX_VIEWLINEB);
 			Strings[Y2-Y1]->nFilePos = vtell();
 			ReadString(Strings[Y2-Y1],-1,MAX_VIEWLINEB);
+
+         if (eof_mode)
+         {
+            __int64 fpos = vtell();
+            if (fpos >= EOF_Pos || fpos <= last_filepos)
+            {
+               eof_mode = false; nMode = SHOW_RELOAD;
+            }
+            else
+               last_filepos = fpos;
+         }
 			break;
 	}
+   } while ( eof_mode );
 
 	if (nMode != SHOW_HEX)
 	{
@@ -598,7 +647,6 @@
 	wchar_t OutStr[MAX_VIEWLINE],TextStr[20];
 	int EndFile;
 	__int64 SelSize;
-	WCHAR Ch;
 	int X,Y,TextPos;
 	int SelStart, SelEnd;
 	bool bSelStartFound = false, bSelEndFound = false;
@@ -640,93 +688,80 @@
 
 		const wchar_t BorderLine[]={BoxSymbols[BS_V1],L' ',0};
 
+		int out_len = (int)wcslen(OutStr);
+		int border_len = (int)wcslen(BorderLine);
+
 		if (IsUnicodeCodePage(VM.CodePage))
 		{
-			for (X=0; X<8; X++)
+			wchar_t line[8];
+			int nr = vread(line, 8);
+			LastPage = EndFile = ViewFile.Eof() ? 1 : 0;
+			if (nr <= 0)
 			{
-				__int64 fpos = vtell();
-
-				if (SelectSize>0 && (SelectPos == fpos))
+				*OutStr = L'\0';
+			}
+			else
+			{
+				for (X=0; X<8; X++)
 				{
-					bSelStartFound = true;
-					SelStart = (int)wcslen(OutStr);
-					SelSize=SelectSize;
-					/* $ 22.01.2001 IS
-					    Внимание! Возможно, это не совсем верное решение проблемы
-					    выделения из плагинов, но мне пока другого в голову не пришло.
-					    Я приравниваю SelectSize нулю в Process*
-					*/
-					//SelectSize=0;
-				}
-
-				if (SelectSize>0 && (fpos == (SelectPos+SelectSize-1)))
-				{
-					bSelEndFound = true;
-					SelEnd = (int)wcslen(OutStr)+3;
-					SelSize=SelectSize;
-				}
-
-				if (!vgetc(Ch))
-				{
-					/* $ 28.06.2000 tran
-					   убираем показ пустой строки, если длина
-					   файла кратна 16 */
-					EndFile=1;
-					LastPage=1;
-
-					if (!X)
+					if (SelectSize>0 && (SelectPos == fpos))
 					{
-						*OutStr=0;
-						break;
+						bSelStartFound = true;
+						SelStart = (int)wcslen(OutStr);
+						SelSize=SelectSize;
 					}
-
-					wcscat(OutStr,L"     ");
-					TextStr[TextPos++]=L' ';
-				}
-				else
-				{
-					WCHAR OutChar=Ch;
-
-					if (VM.CodePage == CP_REVERSEBOM)
+					if (SelectSize>0 && (fpos == (SelectPos+SelectSize-1)))
 					{
-						_swab(reinterpret_cast<LPSTR>(&OutChar),reinterpret_cast<LPSTR>(&OutChar),sizeof(WCHAR));
+						bSelEndFound = true;
+						SelEnd = (int)wcslen(OutStr)+3;
+						SelSize=SelectSize;
 					}
 
-					int OutStrLen=StrLength(OutStr);
-					_snwprintf(OutStr+OutStrLen,ARRAYSIZE(OutStr)-OutStrLen,L"%02X%02X ",HIBYTE(OutChar),LOBYTE(OutChar));
-
-					if (!Ch)
+					if (X < nr)
 					{
-						Ch=L' ';
+						unsigned char b1 = HIBYTE(line[X]), b2 = LOBYTE(line[X]);
+#if 0																 // !!! поведение изменено !!!
+						if (VM.CodePage != CP_REVERSEBOM) // мне кажется в обоих случаях
+						{                                 // логичнее показывать неперевернутые коды
+							unsigned char t = b1; b1 = b2; b2 = t;
+						}
+#endif
+						_snwprintf(OutStr+out_len, ARRAYSIZE(OutStr)-out_len, L"%02X%02X ", b1, b2);
+						TextStr[TextPos++] = line[X] ? line[X] : L' ';
 					}
+					else
+					{
+						wcscpy(OutStr+out_len, L"     ");
+						TextStr[TextPos++] = L' ';
+					}
+					out_len += 5;
 
-					TextStr[TextPos++]=Ch;
-					LastPage=0;
+					if (X == 3)
+					{
+						wcscpy(OutStr+out_len, BorderLine);
+						out_len += border_len;
+					}
 				}
-
-				if (X==3)
-					wcscat(OutStr, BorderLine);
+				++fpos;
 			}
 		}
-		else if ( CP_UTF8 == VM.CodePage )
+		else
 		{
-			__int64 fpos = vtell();
 			unsigned char line[16+3];
 			DWORD nr = 0;
-			Reader.Read(line, (DWORD)sizeof(line), &nr);
+			DWORD nb = CP_UTF8 == VM.CodePage ? 16+3 : 16;
+			Reader.Read(line, nb, &nr);
 			if (nr > 16)
 				Reader.Unread(nr-16);
 
-			LastPage = nr < (DWORD)sizeof(line) && ViewFile.Eof() ? 1 : 0;
+			LastPage = EndFile = (nr <= nb) && ViewFile.Eof() ? 1 : 0;
 
 			if (!nr)
 			{
-				*OutStr=0;
+				*OutStr = L'\0';
 			}
 			else
 			{
-				int out_len = (int)wcslen(OutStr);
-				int border_len = (int)wcslen(BorderLine);
 				if (SelectSize)
 				{
 					if (SelectPos >= fpos && SelectPos < fpos+16)
@@ -755,95 +790,37 @@
 					else
 						wcscpy(OutStr+off, L"   ");
 					TextStr[X] = L' ';
+					if (CP_UTF8 != VM.CodePage && X < (int)nr && line[X])
+						MultiByteToWideChar(VM.CodePage, 0, (LPCSTR)line+X, 1, TextStr+X, 1);
 				}
 
-				wchar_t w1[16], w2[16];
-				int tail, nw, ib=0, iw=0;
-				nw = utf8_to_WideChar((char *)line, (int)nr, w1, w2, 16, tail);
-				bool first = true;
-				while (ib < 16 && iw < nw)
+				if (CP_UTF8 == VM.CodePage)
 				{
-					if (first && w1[iw] == REPLACE_CHAR && w2[iw] == L'?')
+					wchar_t w1[16], w2[16];
+					int tail, nw, ib=0, iw=0;
+					nw = utf8_to_WideChar((char *)line, (int)nr, w1, w2, 16, tail);
+					bool first = true;
+					while (ib < 16 && iw < nw)
 					{
-						TextStr[ib++] = CONTINUE_CHAR; // это может быть не совсем корректно для 'плохих' utf-8
-					}                                 // но усложнять из-за этого код на мой взгяд не стоит...
-					else
-					{
-						first = false;
-						TextStr[ib++] = w1[iw] ? w1[iw] : L' ';
+						if (first && w1[iw] == REPLACE_CHAR && w2[iw] == L'?')
+						{
+							TextStr[ib++] = CONTINUE_CHAR; // это может быть не совсем корректно для 'плохих' utf-8
+						}                                 // но усложнять из-за этого код на мой взгяд не стоит...
+						else
+						{
+							first = false;
+							TextStr[ib++] = w1[iw] ? w1[iw] : L' ';
+						}
+						int clen = WideCharToMultiByte(CP_UTF8, 0, w2+iw, 1, NULL,0, NULL,NULL);
+						while (--clen > 0 && ib < 16)
+							TextStr[ib++] = CONTINUE_CHAR;
+						++iw;
 					}
-					int clen = WideCharToMultiByte(CP_UTF8, 0, w2+iw, 1, NULL,0, NULL,NULL);
-					while (--clen > 0 && ib < 16)
-						TextStr[ib++] = CONTINUE_CHAR;
-					++iw;
 				}
 				TextPos = 16;
 			}
 		}
-		else
-		{
-			for (X=0; X<16; X++)
-			{
-				__int64 fpos = vtell();
 
-				if (SelectSize>0 && (SelectPos == fpos))
-				{
-					bSelStartFound = true;
-					SelStart = (int)wcslen(OutStr);
-					SelSize=SelectSize;
-					/* $ 22.01.2001 IS
-					    Внимание! Возможно, это не совсем верное решение проблемы
-					    выделения из плагинов, но мне пока другого в голову не пришло.
-					    Я приравниваю SelectSize нулю в Process*
-					*/
-					//SelectSize=0;
-				}
-
-				if (SelectSize>0 && (fpos == (SelectPos+SelectSize-1)))
-				{
-					bSelEndFound = true;
-					SelEnd = (int)wcslen(OutStr)+1;
-					SelSize=SelectSize;
-				}
-
-				if (!vgetc(Ch))
-				{
-					/* $ 28.06.2000 tran
-					   убираем показ пустой строки, если длина
-					   файла кратна 16 */
-					EndFile=1;
-					LastPage=1;
-
-					if (!X)
-					{
-						*OutStr=0;
-						break;
-					}
-
-					/* $ 03.07.2000 tran
-					   - вместо 5 пробелов тут надо 3 */
-					wcscat(OutStr,L"   ");
-					TextStr[TextPos++]=L' ';
-				}
-				else
-				{
-					char NewCh;
-					WideCharToMultiByte(VM.CodePage, 0, &Ch,1, &NewCh,1," ",nullptr);
-					int OutStrLen=StrLength(OutStr);
-					_snwprintf(OutStr+OutStrLen,ARRAYSIZE(OutStr)-OutStrLen,L"%02X ", NewCh);
-
-					if (!Ch)
-						Ch=L' ';
-
-					TextStr[TextPos++]=Ch;
-					LastPage=0;
-				}
-
-				if (X==7)
-					wcscat(OutStr,BorderLine);
-			}
-		}
-
 		TextStr[TextPos]=0;
 		wcscat(TextStr,L" ");
 
@@ -1269,42 +1246,39 @@
 		}
 		case KEY_IDLE:
 		{
-			if (ViewFile.Opened())
+			if (ViewFile.Opened() && update_check_period >= 0)
 			{
-				string strRoot;
-				GetPathRoot(strFullFileName, strRoot);
-				int DriveType=FAR_GetDriveType(strRoot);
+				DWORD now_ticks = GetTickCount();
+				if ((int)(now_ticks - last_update_check) < update_check_period)
+					return TRUE;
+				last_update_check = now_ticks;
 
-				if (DriveType!=DRIVE_REMOVABLE && !IsDriveTypeCDROM(DriveType))
-				{
-					FAR_FIND_DATA_EX NewViewFindData;
+				FAR_FIND_DATA_EX NewViewFindData;
+				if (!apiGetFindDataEx(strFullFileName, NewViewFindData))
+					return TRUE;
 
-					if (!apiGetFindDataEx(strFullFileName, NewViewFindData))
-						return TRUE;
+				ViewFile.FlushBuffers();
+				vseek(0,SEEK_END);
+				__int64 CurFileSize=vtell();
 
-					ViewFile.FlushBuffers();
-					vseek(0,SEEK_END);
-					__int64 CurFileSize=vtell();
+				if (ViewFindData.ftLastWriteTime.dwLowDateTime!=NewViewFindData.ftLastWriteTime.dwLowDateTime
+				 || ViewFindData.ftLastWriteTime.dwHighDateTime!=NewViewFindData.ftLastWriteTime.dwHighDateTime
+				 || CurFileSize!=FileSize)
+				{
+					ViewFindData=NewViewFindData;
+					FileSize=CurFileSize;
 
-					if (ViewFindData.ftLastWriteTime.dwLowDateTime!=NewViewFindData.ftLastWriteTime.dwLowDateTime ||
-					        ViewFindData.ftLastWriteTime.dwHighDateTime!=NewViewFindData.ftLastWriteTime.dwHighDateTime ||
-					        CurFileSize!=FileSize)
+					if (FilePos>FileSize)
+						ProcessKey(KEY_CTRLEND);
+					else
 					{
-						ViewFindData=NewViewFindData;
-						FileSize=CurFileSize;
+						__int64 PrevLastPage=LastPage;
+						Show();
 
-						if (FilePos>FileSize)
-							ProcessKey(KEY_CTRLEND);
-						else
+						if (PrevLastPage && !LastPage)
 						{
-							__int64 PrevLastPage=LastPage;
-							Show();
-
-							if (PrevLastPage && !LastPage)
-							{
-								ProcessKey(KEY_CTRLEND);
-								LastPage=TRUE;
-							}
+							ProcessKey(KEY_CTRLEND);
+							LastPage=TRUE;
 						}
 					}
 				}
@@ -1785,7 +1759,17 @@
 				unsigned int max_counter=Y2-Y1;
 
 				if (VM.Hex)
+				{
 					vseek(0,SEEK_END);
+					FilePos = vtell();
+					int line_chars = IsUnicodeCodePage(VM.CodePage) ? 8 : 16;
+					if ((FilePos % line_chars) == 0)
+						FilePos -= line_chars * (Y2 - Y1 + 1);
+					else
+						FilePos -= (FilePos % line_chars) + line_chars * (Y2 - Y1);
+					if (FilePos < 0)
+						FilePos = 0;
+				}
 				else
 				{
 					vseek(-1,SEEK_END);
@@ -1793,39 +1777,17 @@
 
 					if (vgetc(LastSym) && LastSym!=CRSym)
 						++max_counter;
-				}
 
-				FilePos=vtell();
+					FilePos=vtell();
+		         if (VM.Wrap && VM.WordWrap)
+			         EOF_Pos = FilePos; //!!! hack to adjust possible incorrect last page show
 
-				/*
-				        {
-				          char Buf[100];
-				          sprintf(Buf,"%I64X",FilePos);
-				          Message(0,1,"End",Buf,"Ok");
-				        }
-				*/
-				for (int i=0; static_cast<unsigned int>(i)<max_counter; i++)
-					Up();
+					for (int i=0; static_cast<unsigned int>(i)<max_counter; i++)
+						Up();
+				}
 
-				/*
-				        {
-				          char Buf[100];
-				          sprintf(Buf,"%I64X, %d",FilePos, I);
-				          Message(0,1,"Up",Buf,"Ok");
-				        }
-				*/
-				if (VM.Hex)
-					FilePos&=~(IsUnicodeCodePage(VM.CodePage) ? 0x7:0xf);
-
-				/*
-				        if (VM.Hex)
-				        {
-				          char Buf[100];
-				          sprintf(Buf,"%I64X",FilePos);
-				          Message(0,1,"VM.Hex",Buf,"Ok");
-				        }
-				*/
 				Show();
+            EOF_Pos = -1;
 //        LastSelPos=FilePos;
 			}
 
@@ -3003,14 +2965,12 @@
 	}
 }
 
-
 int Viewer::vseek(__int64 Offset,int Whence)
 {
 	vgetc_ready = -1;
 	return ViewFile.SetPointer(Offset*(IsUnicodeCodePage(VM.CodePage)?2:1), nullptr, Whence);
 }
 
-
 __int64 Viewer::vtell()
 {
 	INT64 Ptr=ViewFile.GetPointer();
@@ -3021,7 +2981,6 @@
 	return Ptr;
 }
 
-
 bool Viewer::vgetc(WCHAR& C)
 {
 	bool Result;
@@ -3059,7 +3018,6 @@
 	return Result;
 }
 
-
 #define RB_PRC 3
 #define RB_HEX 4
 #define RB_DEC 5
