#parsecommandline TRUE
#codesize
#argc TRUE
#resize TRUE
#initallvar FALSE

#stack 2048

?define DELITEL 32;       // во сколько разгоняем часы (макс значение для W95)
?define LANG_RUS TRUE     // русский язык

#include "TR\timer.h--"
#include "TR\video.h--"
#include "TR\file.h--"
#include "TR\string.h--"
#include "TR\system.h--"
#include "TR\DOS.h--"
#include "TR\time.h--"

#define BufSize                0xFFFE      // Размер буффера чтения
#define MAX_FILES_TO_COMPARE   8           // Максимальное число сравниваемых файлов
#define MAX_PATH_LENGTH        200         // Максимальная длина имен файлов с путями

#include "str.h--"
//───────────────────────────────────────────────────────────────────────────
char
  STR[10] = "\x00\x00\x00\x00",
  DefaultOutFileName = "OUT.CRK",
  CrkOutString[20] = "\x0D\x0A",
  str[100],                         // Строковой буффер
  StartPathStr[MAX_PATH_LENGTH];    // Путь к каталогу из которого была запущена программа
byte
  NumFilesToCompare;                // Число сравниваемых файлов
char
  FileName[MAX_PATH_LENGTH*MAX_FILES_TO_COMPARE],
  FileName_Out[MAX_PATH_LENGTH];    // Имя выходного файла

word
  File[MAX_FILES_TO_COMPARE],       // Дескрипторы сравниваемых файлов
  File_Out;

dword
  FileSize[MAX_FILES_TO_COMPARE], // Размеры файлов
  MaxFileSize,                    // Размер самого большого файла
  MaxFileSize100;                 // MaxFileSize/100

word
  Seg[MAX_FILES_TO_COMPARE];      // Сегменты буфферов

dword
  Addr,                 // Адpес в файле
  Differences;          // Число pазличий
word
  AddrNoEq;             // Смещение несовпавшего байта относительно адpеса

word
  NumRead[MAX_FILES_TO_COMPARE],        // Число пpочитанных байт
  MinNumRead,                           // Min(NumRead[i])
  MaxNumRead,                           // Max(NumRead[i])
  _MaxNumRead,                          //   вpеменная пеpеменная
  IndPos,                               // Позиция индикатоpа на экpане
  CursorSize;                           // Размеры текстового курсора

byte
  Progress,             // Прогресс в %
  OutputFileCreated,    // Флаг создания файла отчета
  ConfigLoaded,         // Флаг наличия файла конфигурации
  EndAnyFileFlag,       // Флаг того, что достигнут конец одного из файлов
  ParamCount,           // Число параметров в командной строке
  ErrorStatus;          // Флаг наличия ошибки


byte
  QuitESC;              // Флаг требования выхода
//───────────────────────────────────────────────────────────────────────────
#include "cfg.h--"
#include "sys.h--"
//#include "map.h--"
//--------------------- Вывод заголовка на экран ----------------------------
inline void fastcall Info()
{
   GetXY();
   GoToXY(8, DH);
   WriteStr("");
   WriteStr(#strAbout1);
   WriteStr(#strAbout2);
   WriteStr(#strAbout3);
   GetXY();
   GoToXY(2, DH);
   WriteStr("");
}
//---------------- Привести параметр строки в приличный вид -----------------
void fastcall NormalizeLongName(AL)
{
   StrCpy(#str, PARAMSTR(AL));
   LoadByte(#str);
   IF (AL != '"') SI --;
   ELSE
     {
       $PUSH SI
       StrLen();
       $POP  SI
       DI = SI;
       DI += CX - 1;
       @StrAddByte(0);
     }
}
//-------------------- Инициализация переменных -----------------------------
inline void fastcall InitVars()
{
   INSTTIMER();
   ErrorStatus = OutFileFormat = 0;
   FOR (BP = 1; BP <= NumFilesToCompare; BP ++)
     {
       NormalizeLongName(BP);
       StrCpy(BP*MAX_PATH_LENGTH+#FileName[-MAX_PATH_LENGTH], SI);
     }
   IF (ParamCount < 4)
     StrCpy(#FileName_Out, #DefaultOutFileName);
   ELSE
     {
       NormalizeLongName(3);
       StrCpy(#FileName_Out, SI);
     }
   OutputFileCreated = FALSE;
   @StartPath(#StartPathStr);
   QuitESC = 0;
}
//---------------------- Инициализация файлов -------------------------------
inline void fastcall InitFiles()
{
  DI = #File;
  SI = #FileName;
  MaxFileSize = 0;
  FOR (BP = 1; BP <= NumFilesToCompare; BP ++)
    {
      DSWORD[DI] = OpenFile(SI);
      $PUSH   AX
      IF (CARRYFLAG)
        {
          $PUSH   SI
          StrCpy(#str, #strError1);
          $POP    SI
          $PUSH   SI
          @StrCat(SI);
          WriteStr(#str);            // "File not found ..."
          $POP    SI
          ErrorStatus = 1;
        };
      $POP    BX
      $PUSH   DI
      DI = BP*4;
      GetFileSize(BX);
      FileSize[DI-4] = EAX;
      IF (MaxFileSize < EAX)   MaxFileSize = EAX;
      $POP    DI
      DI += 2;
      SI += MAX_PATH_LENGTH;
    }
   MaxFileSize100 = MaxFileSize/100;
}
//--------------------------- Выделение памяти ------------------------------
inline void fastcall InitMemory()
{
  DI = 0;
  FOR (BP = 1; BP <= NumFilesToCompare; BP ++)
    {
      IF ( GETMEM( 0x1000 ) == 0 )
        {
          WriteStr(#strError3);
          ErrorStatus = 1;
        }
      ELSE
        {
          Seg[DI] = AX;
          DI += 2;
        }
    }
}
//------------------ Вывод информации о сравнении ---------------------------
inline void fastcall CompareInfo()
{
   IF (ErrorStatus)  RETURN;

   StrCpy(#str, #strCmpInfo1);
   JustFileName(#FileName[0]);
   @StrCat(#strCmpInfo2);
   JustFileName(#FileName[MAX_PATH_LENGTH]);
   WriteStr(#str);                       // "Сравниваются файлы ..."
   StrCpy(#str, #strCmpInfo3);
   @StrCat(#FileName_Out);
   WriteStr(#str);                       // "Output file: ..."
   IF (LongSupport)   WriteStr(#strCmpInfo4);
   StrCpy(#str, #strCmpInfo5);
   IF (OutFileFormat == 0)  @StrCat(#cfgCRK);                   // CRK
   ELSE  @StrCat(#cfgXCK);                                      // XCK
   WriteStr(#str);
}
//--------------------- Вывод инфоpмации после сpавнения --------------------
void fastcall EndComparingInfo()
{
   IF (ErrorStatus) return;
   DI = #str;
   @StrAddByte(15);
   Long_To_Str(Differences);              //
   @StrCat(#strDone1);
   Long_To_Str(MaxFileSize);
   @StrCat(#strDone2);
                                    // Число изменений в процентах
   EAX = 100*Differences;
   EAX /= MaxFileSize;
   $PUSH   EAX
   LongToStr();
   @StrAddByte('.');
   $POP    EAX
   EAX *= 100;
   EBX = 10000*Differences;
   EBX /= MaxFileSize;
   EAX -= EBX;
   $PUSH   DI, AX
   LongToStr();
   $POP    DX, SI
   IF (DX != 0)
     IF (SI - DI >= 2)  DI = SI + 2;


   @StrCat(#strDone3);

   GetXY();
   WritelnStr(#str, 23, DH-2);
   GoToXY(2, DH);
   IF (OutputFileCreated)
     {
       StrCpy(#str, #strDone4);
       Long_To_Str(GetFileSize(File_Out));
       @StrCat(#strDone5);
       WriteStr(#str);
     }
   ELSE
     {
       WriteStr(#strDone6);
     };
   EAX = schot/32/16;
   DI = #STR;
   LongToStr();
   $PUSH  DI
   @StrAddByte('.');
   EAX = schot/32/16;
   EAX = EAX * 1000;
   EBX = schot*988/32/16-EAX;
   LongToStr(EBX);
   $POP   DI
   DI += 4;
   @StrAddByte(0);
   StrCpy(#str, #strDone7);
   @StrCat(#STR);
   @StrCat(#strDone8);
   EBX = 200000*schot/101000;
   Long_To_Str(MaxFileSize/EBX);
   @StrCat(#strDone9);
   WriteStr(#str);
   WriteStr(#strDone10);
}
//---------------------- Вывод пpедупpеждений -------------------------------
inline void fastcall WarningInfo()
{
   IF (ErrorStatus)  RETURN;

//   BX = 0;
   FOR (BP = 1; BP <= NumFilesToCompare; BP ++)
     {
       SI = BP*4+#FileSize[-4];
       IF (DSDWORD[SI] == 0)
         {
           StrCpy(#str, #strWarning1);
           @StrCat(BP*MAX_PATH_LENGTH+#FileName[-MAX_PATH_LENGTH]);
           @StrCat(#strWarning2);
           WriteStr(#str);
         }
     }
   IF (FileSize[0] != FileSize[4])   WriteStr(#strWarning3);
   //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   RETURN;
}
//------------------- Пpоpисовка индикатоpа ---------------------------------
inline void fastcall DrawIndicator()
{
   WriteStr(#strIndicator1);  // Индикатор
   WriteStr(#strIndicator2);   // "Differences found..."
   WriteStr("");
   GetXY();
   DL = 23;
   DH -=3;
   IndPos = DX;

   DifUpdate();


   @GetCursorSize();
   CursorSize = CX;
   SetCursorSize(0x2000);         // Спpятать куpсоp
}
//---------------------------------------------------------------------------
void fastcall DifUpdate()
{
    DI = #str;
    @StrAddByte(15);
    LongToStr(Differences);
    @StrAddByte(0);
    DX = IndPos;
    WritelnStr(#str, DL, DH+1);   // Differences: ...
}
//---------------------------------------------------------------------------
void fastcall UpdateIndicator()
{
    byte Prg;           // Число процентов

    EAX = AddrNoEq + Addr;
    EBX = MaxFileSize100;
    IF (EBX != 0)    EAX = EAX/EBX;
    ELSE AL = 100;
    IF (AL > 100)  AL = 100;

    Prg = AL;

    IF (Prg == Progress) RETURN;
    Progress = Prg;
    DI = #str;
    @StrAddByte(15);
    LongToStr(Prg);
    @StrAddWord(0x0025);        // %, 0
    DX = IndPos;
    $PUSH DX
    WritelnStr(#str, DL-9, DH);       // xx% [           ]

    CL = Prg >> 1;
    DI = #str;
    @StrAddByte(0xB9);

    LOOPNZ (CX)   @StrAddByte('█');
    @StrAddByte(0);

    $POP  DX
    WritelnStr(#str, DL+1, DH);
}
//----------------- Сообщение о ошибки записи -------------------------------
void WriteErrorMessage()
{
  WriteStr(#strError2);
  ErrorStatus = 1;
};
//-------------------------- Создание файла отчета --------------------------
inline void fastcall CreateOutput()
{
   OutputFileCreated = TRUE;
   File_Out = CreateFile(#FileName_Out, 0);
   IF ((File_Out == 0) || (CARRYFLAG))
     {
       WriteErrorMessage();
       ErrorStatus = 1;
     }
   BX >< AX;
   IF (!ConfigLoaded)
     {
Default:
       StrLen(#FileName[0]);
       WriteFile(BX, #FileName[0], CX);
       IF (CARRYFLAG) WriteErrorMessage();
       RETURN;
     }

   IF (OutFileFormat == 0)                                // CRK
     {
       CX = CRKsize;
       IF (CX == 0)  GOTO Default;
       DX = CRKbegin;
     }
   ELSE IF (OutFileFormat == 1)                           // XCK
     {
       CX = XCKsize1;
       IF (CX == 0)  GOTO Default;
       DX = XCKbegin;
     }
  $PUSH  DS
  DS = Seg_Cfg;
  WriteFile(BX, DX, CX);
  $POP   DS
}
//------------------------ Запись одной стpоки в CRK файл -------------------
inline void fastcall Write_CRK_String()
{
    DI = #CrkOutString + 2;
    StrAddHexDWord(AddrNoEq + Addr);

    @StrAddWord(' :');

    SI = AddrNoEq;          // байт из 1-го файла
    EAX = Addr + SI;
    $PUSH   EAX
    IF (EAX < FileSize[0])
      {
        $PUSH DS
        DS = Seg[0];
        LoadByte(SI);
        $POP  DS
        StrAddHexByte();
       }
    ELSE StrAddWord(' -');


    @StrAddByte(' ');
    SI --;                  // байт из 2-го файла
    $POP   EAX;
    IF (EAX < FileSize[4])
      {
        $PUSH DS
        DS = Seg[2];
        LoadByte(SI);
        $POP  DS
        StrAddHexByte();
      }
    ELSE @StrAddWord(' -');

    WriteFile(File_Out, #CrkOutString, 17);
    IF (CARRYFLAG) WriteErrorMessage();
}
//--------------------------- Сравнение -------------------------------------
inline void fastcall Compare()
{
   IF (ErrorStatus) return;
   Differences = Addr = AddrNoEq = 0;

   DrawIndicator();
   EndAnyFileFlag = FALSE;
   while (TRUE)
     {
       IF (EndAnyFileFlag)
         {
           MaxNumRead = _MaxNumRead - MinNumRead;
           MinNumRead = 0;
           EndAnyFileFlag = FALSE;
           CX = MaxNumRead;
           GOTO CompareCycle;
         }

       NumRead[0] = ReadFile_FAR(File[0], Seg[0], BufSize);
       MinNumRead = MaxNumRead = AX;
       NumRead[2] = ReadFile_FAR(File[2], Seg[2], BufSize);
       IF (MinNumRead > AX)  MinNumRead = AX;
       ELSE  MaxNumRead = AX;

       CX = MaxNumRead;
       IF ((CX == 0) || (QuitESC)) break;

       AddrNoEq = CX;
       UpdateIndicator();

       IF ((MinNumRead != MaxNumRead) && (MinNumRead != 0))
         {
           _MaxNumRead = MaxNumRead;
           MaxNumRead = MinNumRead;
           EndAnyFileFlag = TRUE;
         }

       CX = MaxNumRead;
       SI = 0;
       DI = SI;


CompareCycle:
       IF (MinNumRead == 0)) GOTO NoEq;

       $PUSH  DS, ES
       ES = Seg[2];
       DS = Seg[0];

       WHILE (CX != 0)
         {
           $CMPSB
           IF (!ZEROFLAG) BREAK;
           CX --;
         }
       $POP  DS, ES
NoEq:
       IF (CX != 0)             // Есть несовпадения
         {
            Differences ++;
            AddrNoEq = MaxNumRead - CX;
            CX --;
            $PUSH  CX, DI, SI
            DifUpdate();
            UpdateIndicator();
            IF (!OutputFileCreated) CreateOutput();
            Write_CRK_String();
            $POP   SI, DI, CX
            IF (CX != 0) GOTO CompareCycle;
         }
//Stop:
       Addr += MaxNumRead;
     }
}
//--------------------- Выход из программы ----------------------------------
void Done()
{
   DEINSTTIMER();
   IF (schot == 0) schot = 1;
   AddrNoEq = MaxFileSize - Addr;
   IF (!QuitESC)
     {
       UpdateIndicator();
       EndComparingInfo();
     }
   CloseFile(File[0]);
   CloseFile(File[2]);
   IF (OutputFileCreated)
     {
       IF (OutFileFormat == 1)      // записать хвост для XCK
         {
           CX = XCKsize2;
           IF (CX == 0)  GOTO NoEnd;
           DX = XCKend;
           BX = File_Out;
           $PUSH  DS
           DS = Seg_Cfg;
           WriteFile(BX, DX, CX);
           $POP   DS
         }
       NoEnd:
       CloseFile(File_Out);
     }
   SetCursorSize(CursorSize+1);           // Восстановить куpсоp
}
//---------------------------------------------------------------------------

void main()
{
  @WindowsInstalled();
  Info();
  ParamCount = @PARAMCOUNT();
  IF ( (AL < 3) || (AL > 4))
     {
       GetXY();
       GoToXY(4, DH);
       WriteStr("");
       WriteStr(#strUsage1);
       WriteStr(#strUsage2);
       ABORT();
     }
  NumFilesToCompare = 2;

  @InitSysVectors();

  InitVars();
  LoadConfiguration();
  CompareInfo();

  InitMemory();
  InitFiles();
  WarningInfo();
  Compare();
  Done();
EndOfProgramm:
  @RestoreSysVectors();
  $RET    // Добавлено из-за ошибки в компилятоpе !
}