define TXT_LEN 32768
define FRM_LEN 16384
define ARC_LEN  4096
define BLK_LEN  2048
define ARC_FWD    32
define END_LEN    16
define STR_LEN    34
define STR_LEN1   35 //STR_LEN+1
define PAK_LEN     5
define PAK_MIN     3
define OFS_LEN    14

struct Addr
  word  Ofs;
  word  Seg;
section
  void @Ptr;
end

void @Ptr(word Seg,Ofs)
  Addr P;
  P.Seg=Seg;
  P.Ofs=Ofs;
  return @P.Ptr;
end

word GetPSP()
  asm mov     AH,62H
  asm int     21H
  asm mov     AX,BX
end

word create(char @Name)
  asm push    DS
  asm mov     AH,3CH
  asm mov     CX,00H
  asm mov     DX,SS:[BP+6]
  asm mov     DS,DX
  asm mov     DX,SS:[BP+4]
  asm int     21H
  asm pop     DS
end

word open(char @Name)
  asm push    DS
  asm mov     AH,3DH
  asm mov     AL,00H
  asm mov     DX,SS:[BP+6]
  asm mov     DS,DX
  asm mov     DX,SS:[BP+4]
  asm int     21H
  asm pop     DS
end

word read(word F; void @Buff; word N)
  asm push    DS
  asm mov     AH,3FH
  asm mov     BX,SS:[BP+10]
  asm mov     CX,SS:[BP+4]
  asm mov     DX,SS:[BP+8]
  asm mov     DS,DX
  asm mov     DX,SS:[BP+6]
  asm int     21H
  asm pop     DS
end

word write(word F; void @Buff; word N)
  asm push    DS
  asm mov     AH,40H
  asm mov     BX,SS:[BP+10]
  asm mov     CX,SS:[BP+4]
  asm mov     DX,SS:[BP+8]
  asm mov     DS,DX
  asm mov     DX,SS:[BP+6]
  asm int     21H
  asm pop     DS
end

void close(word F)
  asm mov     AH,3EH
  asm mov     BX,SS:[BP+4]
  asm int     21H
end

void putc(char Ch)
  asm mov     AH,2
  asm mov     DL,SS:[BP+4]
  asm int     21H
end

void puts(char @St)
  word P=0;
  while St[P]!=#0 do
    putc(St[P]);
    inc  P;
  end
end

int strcmp(char @S1, @S2)
  int P=0;
  while TRUE do
    if S1[P]<S2[P] then
      return -1;
    end

    if S1[P]>S2[P] then
      return  1;
    end

    if S1[P]=#0 then
      return  0;
    end

    inc P;
  end
end

byte  Txt [TXT_LEN];
word nTxt;
word pTxt;

byte  Arc [ARC_LEN];
word nArc;
word pArc;

word  P2  [16];

word  Tmp;
word pTmp;

void Save(word C; word N)
  if N>8 then
    Save(C/256,N-8);

    C=C%256;
    N=8;
  end

  if pTmp<8 then
    Arc[pArc]=Tmp/256;
    inc pArc;

    Tmp =256*(Tmp%256);
    pTmp=pTmp+8;
  end

  Tmp =Tmp+P2[pTmp-(N-1)]*C;
  pTmp=pTmp-N;
end

word Load(word N)
  word C=0;
  if N>8 then
    C=256*Load(N-8);
    N=8;
  end

  if pTmp>7 then
    if pArc<nArc then
      Tmp=Tmp+P2[pTmp-7]*Arc[pArc];
      inc pArc;
    end

    pTmp=pTmp-8;
  end

  C=C+Tmp/P2[16-N];

  Tmp =P2[N]*(Tmp%P2[16-N]);
  pTmp=pTmp+N;

  return C;
end

void Encode(word hArc, hTxt)
  nTxt=0;
  pTxt=0;
  pArc=0;
  while TRUE do
    if pArc>=BLK_LEN then
      write(hArc,@Arc,BLK_LEN);
      puts(".");

      word I=BLK_LEN;
      word J=0;
      while I<pArc do
        Arc[J]=Arc[I];
        inc J;
        inc I;
      end

      pArc=J;
    end

    if pTxt>=FRM_LEN+BLK_LEN then
      word I=BLK_LEN;
      word J=0;
      while I<nTxt do
        Txt[J]=Txt[I];
        inc J;
        inc I;
      end

      nTxt=nTxt-BLK_LEN;
      pTxt=pTxt-BLK_LEN;
    end

    if pTxt+STR_LEN>=nTxt then
      nTxt=nTxt+read(hTxt,@Txt[nTxt],BLK_LEN);
    end

    if pTxt>=nTxt then
      exit
    end

    int I=pTxt-FRM_LEN;
    if  I<0 then
      I=0;
    end

    int N=nTxt-pTxt;
    if  N>STR_LEN then
      N=STR_LEN;
    end

    byte  Tmp[STR_LEN];

    int   J=pTxt;
    int   K=0;
    while K<N do
      Tmp[K]=Txt[J];
      inc K;
      inc J;
    end

    int D[STR_LEN1];

    D[1]=0;

    J=1;
    K=0;
    while J<N do
      while Tmp[J]=Tmp[K] do
        inc J;
        inc K;

        select
          case J>=N:
            D[J]=K;
            exit

          case Tmp[J]!=Tmp[K]:
            D[J]=K;

          default:
            D[J]=D[K];
        end
      end

      if K>0 then
        K=D[K];
        loop
      end

      inc J;
      D  [J]=0;
    end

    byte T=Txt[pTxt];
    int  M=pTxt+N;

    int  S=0;
    int  L=1;

    J=I;
    K=0;
    while TRUE do
      while Txt[J]=Tmp[K] do
        if K>=N then
          exit
        end

        inc J;
        inc K;
      end

      if K>0 then
        if L<=K then
          if J>=M then
            exit
          end

          S=J;
          L=K;
        end

        K=D[K];
        loop
      end

      inc J;
      while Txt[J]!=T do
        inc J;
      end

      inc J;
      inc K;
    end

    if L>=PAK_MIN then
      S=S-L;
      S=pTxt-S;

      Save(1,1);
      Save(S-1,OFS_LEN);

      if S=FRM_LEN then
        Save(1,1);
      end

      Save(L-PAK_MIN,PAK_LEN);

      pTxt=pTxt+L;
    else
      Save(0,1);
      Save(Txt[pTxt],8);

      inc pTxt;
    end
  end

  Save(1,1);
  Save(FRM_LEN-1,OFS_LEN);
  Save(0,1);

  if pTmp<15 then
    Save(0,16);
  end

  if pArc>0 then
    write(hArc,@Arc,pArc);
  end
end

void Decode(word hArc, hTxt)
  nArc=0;
  pArc=0;
  pTxt=0;
  while TRUE do
    if pTxt>=FRM_LEN+BLK_LEN then
      write(hTxt,@Txt,BLK_LEN);
      word I=BLK_LEN;
      word J=0;
      while I<pTxt do
        Txt[J]=Txt[I];
        inc J;
        inc I;
      end

      pTxt=J;
    end

    if pArc+ARC_FWD>=nArc then
      word I=pArc;
      word J=0;
      while I<nArc do
        Arc[J]=Arc[I];
        inc J;
        inc I;
      end

      nArc=J+read(hArc,@Arc[J],BLK_LEN);
      pArc=0;
    end

    word C=Load(1);

    select
      case C=0:
        Txt[pTxt]=Load(8);
        inc pTxt;

      case C=1:
        int S=Load(OFS_LEN)+1;

        if S=FRM_LEN then
          if Load(1)=0 then
            if pTxt>0 then
              write(hTxt,@Txt,pTxt);
            end

            exit
          end
        end

        int   N=Load(PAK_LEN)+PAK_MIN;
        int   P=pTxt-S;
        while N>0 do
          Txt[pTxt]=Txt[P];
          inc pTxt;
          inc P;
          dec N;
        end
    end
  end
end

begin
  char @Line=@Ptr(GetPSP(),129);
  byte @Size=@Ptr(GetPSP(),128);

  char  Parm  [3][128];

  int   F=1;
  int   I=0;
  int   J=0;
  while I<3 do
    while J<Size & Line[J] =' ' do
      inc J;
    end

    int K=0;
    while J<Size & Line[J]!=' ' do
      Parm[I][K]=Line[J];
      inc  K;
      inc  J;
    end

    Parm[I][K]=#0;

    if K<1 then
      F=0;
    end

    inc  I;
  end

  word hArc;
  word hTxt;

  int C=0;
  if F!=0 then
    select
      case strcmp(@Parm[0][0],"e")=0 | strcmp(@Parm[0][0],"E")=0:
        hArc=create(@Parm[1][0]);
        hTxt=open  (@Parm[2][0]);
        C=1;
      case strcmp(@Parm[0][0],"d")=0 | strcmp(@Parm[0][0],"D")=0:
        hArc=open  (@Parm[1][0]);
        hTxt=create(@Parm[2][0]);
        C=2;
    end
  end

  if C=0 then
    puts("arctest.com e(ncode)|d(ecode) arc-file txt-file");
    return
  end

  I=1;
  J=0;
  while J<16 do
    P2 [J]=I; I=2*I;
    inc J;
  end

  Tmp = 0;
  pTmp=15;

  select
    case C=1:
      Encode(hArc,hTxt);
    case C=2:
      Decode(hArc,hTxt);
  end

  close(hTxt);
  close(hArc);
end
