
{
  WinDump : visualizzazione messaggi di debug.

  Written by:       Michele Mottini
                    TERA S.r.l.
                    CIS 100040,615
}
unit WinDump;

{$S-}

interface

uses
  WinTypes,
  WinProcs,
  WinDos;

const
  ScreenWidth = 80;

  WindowOrg: TPoint =                       { CRT window origin }
    (X: cw_UseDefault; Y: cw_UseDefault);
  WindowSize: TPoint =                      { CRT window size }
    (X: cw_UseDefault; Y: cw_UseDefault);
  ScreenSize: TPoint = (X: ScreenWidth; Y: 32000);   { Virtual screen dimensions }
  Cursor: TPoint = (X: 0; Y: 0);            { Cursor location }
  Origin: TPoint = (X: 0; Y: 0);            { Client area origin }
  InactiveTitle: PChar = '(Inactive %s)';   { Inactive window title }
  AutoTracking: Boolean = True;             { Track cursor on Write? }
  CheckEOF: Boolean = False;                { Allow Ctrl-Z for EOF? }
  CheckBreak: Boolean = True;               { Allow Ctrl-C for break? }

var
  WindowTitle: array[0..79] of Char;        { CRT window title }

procedure InitWinCrt;
procedure DoneWinCrt;

procedure WriteBuf(Buffer: PChar; Count: Word);
procedure WriteChar(Ch: Char);

function KeyPressed: Boolean;
function ReadKey: Char;
function ReadBuf(Buffer: PChar; Count: Word): Word;

procedure GotoXY(X, Y: Integer);
function WhereX: Integer;
function WhereY: Integer;
procedure ClrScr;
procedure ClrEol;

procedure CursorTo(X, Y: Integer);
procedure ScrollTo(X, Y: Integer);
procedure TrackCursor;

procedure AssignCrt(var F: Text);

implementation {==============================================================}

uses
  Arit,
  Strings,
  Strings2,
  Streams;

type

   { Double word record }

  LongRec = record
    Lo, Hi: Integer;
  end;

    { MinMaxInfo array }

  PMinMaxInfo = ^TMinMaxInfo;
  TMinMaxInfo = array[0..4] of TPoint;

{ CRT window procedure }

function CrtWinProc(Window: HWnd;
                    Message, WParam: Word;
                    LParam: Longint): Longint; export; forward;

{ CRT window class }

const
  CrtClass: TWndClass = (
    style: cs_HRedraw + cs_VRedraw;
    lpfnWndProc: @CrtWinProc;
    cbClsExtra: 0;
    cbWndExtra: 0;
    hInstance: 0;
    hIcon: 0;
    hCursor: 0;
    hbrBackground: 0;
    lpszMenuName: nil;
    lpszClassName: 'TPWinDump');

const
  CrtWindow: HWnd = 0;                  { CRT window handle }
  FirstLine: Integer = 0;               { First line in circular buffer }
  KeyCount: Integer = 0;                { Count of keys in KeyBuffer }
  Created: Boolean = False;       	{ CRT window created? }
  Focused: Boolean = False;             { CRT window focused? }
  Reading: Boolean = False;             { Reading from CRT window? }
  Painting: Boolean = False;            { Handling wm_Paint? }

var
  SaveExit: Pointer;                    { Saved exit procedure pointer }
  ScreenBuffer: TSCollection;           { Screen buffer }
  ClientSize: TPoint;                   { Client area dimensions }
  Range: TPoint;                        { Scroll bar ranges }
  CharSize: TPoint;                     { Character cell size }
  CharAscent: Integer;                  { Character ascent }
  DC: HDC;                              { Global device context }
  PS: TPaintStruct;                     { Global paint structure }
  SaveFont: HFont;                      { Saved device context font }
  KeyBuffer: array[0..63] of Char;      { Keyboard type-ahead buffer }

{---------------------------------------------------------- Scroll keys table }

type
  TScrollKey = record
    Key: Byte;
    Ctrl: Boolean;
    SBar: Byte;
    Action: Byte;
  end;

const
  ScrollKeyCount = 12;
  ScrollKeys: array[1..ScrollKeyCount] of TScrollKey = (
    (Key: vk_Left;  Ctrl: False; SBar: sb_Horz; Action: sb_LineUp),
    (Key: vk_Right; Ctrl: False; SBar: sb_Horz; Action: sb_LineDown),
    (Key: vk_Left;  Ctrl: True;  SBar: sb_Horz; Action: sb_PageUp),
    (Key: vk_Right; Ctrl: True;  SBar: sb_Horz; Action: sb_PageDown),
    (Key: vk_Home;  Ctrl: False; SBar: sb_Horz; Action: sb_Top),
    (Key: vk_End;   Ctrl: False; SBar: sb_Horz; Action: sb_Bottom),
    (Key: vk_Up;    Ctrl: False; SBar: sb_Vert; Action: sb_LineUp),
    (Key: vk_Down;  Ctrl: False; SBar: sb_Vert; Action: sb_LineDown),
    (Key: vk_Prior; Ctrl: False; SBar: sb_Vert; Action: sb_PageUp),
    (Key: vk_Next;  Ctrl: False; SBar: sb_Vert; Action: sb_PageDown),
    (Key: vk_Home;  Ctrl: True;  SBar: sb_Vert; Action: sb_Top),
    (Key: vk_End;   Ctrl: True;  SBar: sb_Vert; Action: sb_Bottom));

{------------------------------------------------------------- Configurazione }

const
  SecName = 'WinDump';
  WindowKey = 'Window';

procedure LoadConfig;
var
  Buffer : array[0..80] of char;
  P : PChar;
begin
  GetProfileString(SecName,WindowKey,'',Buffer,SizeOf(Buffer));
  P := Buffer;
  if P^ <> #0 then begin
    WindowOrg.X := StrToIntDef(StrToken(P,','),cw_UseDefault);
    if P^ <> #0 then begin
      WindowOrg.Y := StrToIntDef(StrToken(P,','),cw_UseDefault);
      if P^ <> #0 then begin
        WindowSize.X := StrToIntDef(StrToken(P,','),cw_UseDefault);
        if P^ <> #0 then begin
          WindowSize.Y := StrToIntDef(P,cw_UseDefault);
        end;
      end;
    end;
  end;
end; { LoadConfig }

procedure SaveConfig;
var
  Buffer : array[0..80] of char;
begin
  IntToStr(WindowOrg.X,Buffer);
  StrCat(Buffer,',');
  IntToStr(WindowOrg.Y,Buffer+StrLen(Buffer));
  StrCat(Buffer,',');
  IntToStr(WindowSize.X,Buffer+StrLen(Buffer));
  StrCat(Buffer,',');
  IntToStr(WindowSize.Y,Buffer+StrLen(Buffer));
  WriteProfileString(SecName,WindowKey,Buffer);
end; { SaveConfig }

{--------------------------------------------- Accesso al buffer dello schermo }

var
  LineBuffer : array[0..ScreenWidth] of char;

function ScreenPtr(X,Y : integer): PChar;
  {- Return pointer to location in screen buffer.}
var
  L : integer;
begin
  inc(Y, FirstLine);
  if Y >= ScreenSize.Y then dec(Y,ScreenSize.Y);
  if Y >= ScreenBuffer.Count then LineBuffer[0] := #0
  else StrCopy(LineBuffer,PChar(ScreenBuffer.At(Y)));
  L := StrLen(LineBuffer);
  FillChar(PChar(LineBuffer+L)^,ScreenWidth-L,' ');
  ScreenPtr := PChar(LineBuffer+X);
end; { ScreenPtr }

procedure ClearLine(Y : integer);
var
  LinePtr : PChar;
begin
  inc(Y, FirstLine);
  if Y >= ScreenSize.Y then dec(Y,ScreenSize.Y);
  if Y < ScreenBuffer.Count then begin
    LinePtr := PChar(ScreenBuffer.At(Y));
    FillChar(LinePtr^,StrLen(LinePtr),' ');
  end;
end; { ClearLine }

procedure ClearToEol(X,Y : integer);
var
  LinePtr : PChar;
  L : integer;
begin
  inc(Y, FirstLine);
  if Y >= ScreenSize.Y then dec(Y,ScreenSize.Y);
  if Y < ScreenBuffer.Count then begin
    LinePtr := PChar(ScreenBuffer.At(Y));
    L := StrLen(LinePtr);
    while X < L do begin
      LinePtr[X] := ' ';
      inc(X);
    end;
  end;
end; { ClearToEol }

procedure PutChar(X,Y : integer; C : char);
var
  LinePtr,NewLinePtr : PChar;
  L : integer;
begin
  inc(Y, FirstLine);
  if Y >= ScreenSize.Y then dec(Y,ScreenSize.Y);
  if Y >= ScreenBuffer.Count then begin
    FillChar(LineBuffer,succ(X),' ');
    LineBuffer[succ(X)] := #0;
    while Y >= ScreenBuffer.Count do ScreenBuffer.Insert(StrNew(LineBuffer));
  end;
  LinePtr := PChar(ScreenBuffer.At(Y));
  if X >= StrLen(LinePtr) then begin
    GetMem(NewLinePtr,X+2);
    StrCopy(NewLinePtr,LinePtr);
    L := StrLen(NewLinePtr);
    while L < X do begin
      NewLinePtr[L] := ' ';
      inc(L);
    end;
    NewLinePtr[X+1] := #0;
    StrDispose(LinePtr);
    LinePtr := NewLinePtr;
    ScreenBuffer.AtPut(Y,LinePtr);
  end;
  LinePtr[X] := C;
end; { PutChar }

{------------------------------------------------------------ Display context }

procedure InitDeviceContext;
  {- Allocate device context }
begin
  if Painting then
    DC := BeginPaint(CrtWindow, PS)
  else
    DC := GetDC(CrtWindow);
  SaveFont := SelectObject(DC, GetStockObject(System_Fixed_Font));
  SetTextColor(DC, GetSysColor(color_WindowText));
  SetBkColor(DC, GetSysColor(color_Window));
end; { InitDeviceContext }

procedure DoneDeviceContext;
  {- Release device context }
begin
  SelectObject(DC, SaveFont);
  if Painting then
    EndPaint(CrtWindow, PS) else
    ReleaseDC(CrtWindow, DC);
end; { DoneDeviceContext }

procedure ShowCursor;
  {- Show caret }
begin
  CreateCaret(CrtWindow, 0, CharSize.X, 2);
  SetCaretPos((Cursor.X - Origin.X) * CharSize.X,
    (Cursor.Y - Origin.Y) * CharSize.Y + CharAscent);
  ShowCaret(CrtWindow);
end; { ShowCursor }

procedure HideCursor;
  {- Hide caret }
begin
  DestroyCaret;
end; { HideCursor }

procedure SetScrollBars;
  {- Update scroll bars }
begin
  SetScrollRange(CrtWindow, sb_Horz, 0, Max(1, Range.X), False);
  SetScrollPos(CrtWindow, sb_Horz, Origin.X, True);
  SetScrollRange(CrtWindow, sb_Vert, 0, Max(1, Range.Y), False);
  SetScrollPos(CrtWindow, sb_Vert, Origin.Y, True);
end; {SetScrollBars }

procedure Terminate;
  {- Terminate CRT window.}
begin
  if Focused and Reading then HideCursor;
  Halt(255);
end;  { Terminate }

procedure CursorTo(X, Y: Integer);
  {- Set cursor position }
begin
  Cursor.X := Max(0, Min(X, ScreenSize.X - 1));
  Cursor.Y := Max(0, Min(Y, ScreenSize.Y - 1));
end; { CursorTo }

procedure ScrollTo(X,Y : Integer);
  {- Scroll window to given origin.}
begin
  if Created then begin
    X := Max(0, Min(X, Range.X));
    Y := Max(0, Min(Y, Range.Y));
    if (X <> Origin.X) or (Y <> Origin.Y) then
    begin
      if X <> Origin.X then SetScrollPos(CrtWindow, sb_Horz, X, True);
      if Y <> Origin.Y then SetScrollPos(CrtWindow, sb_Vert, Y, True);
      ScrollWindow(CrtWindow,
	(Origin.X - X) * CharSize.X,
	(Origin.Y - Y) * CharSize.Y, nil, nil);
      Origin.X := X;
      Origin.Y := Y;
      UpdateWindow(CrtWindow);
    end;
  end;
end; { ScrollTo }

procedure TrackCursor;
  {- Scroll to make cursor visible.}
begin
  ScrollTo(Max(Cursor.X - ClientSize.X + 1, Min(Origin.X, Cursor.X)),
    Max(Cursor.Y - ClientSize.Y + 1, Min(Origin.Y, Cursor.Y)));
end; { TrackCursor }

procedure ShowText(L, R : Integer);
  {- Update text on cursor line.}
begin
  if L < R then begin
    InitDeviceContext;
    TextOut(DC, (L - Origin.X) * CharSize.X,
      (Cursor.Y - Origin.Y) * CharSize.Y,
      ScreenPtr(L, Cursor.Y), R - L);
    DoneDeviceContext;
  end;
end; { ShowText }

procedure WriteBuf(Buffer: PChar; Count: Word);
  {- Write text buffer to CRT window.}
var
  L, R: Integer;

  procedure NewLine;
  begin
    ShowText(L, R);
    L := 0;
    R := 0;
    Cursor.X := 0;
    Inc(Cursor.Y);
    if Cursor.Y = ScreenSize.Y then begin
      Dec(Cursor.Y);
      Inc(FirstLine);
      if FirstLine = ScreenSize.Y then FirstLine := 0;
      ClearLine(Cursor.Y);
      ScrollWindow(CrtWindow, 0, -CharSize.Y, nil, nil);
      UpdateWindow(CrtWindow);
    end;
  end; { NewLine }

begin { WriteBuf }
  InitWinCrt;
  L := Cursor.X;
  R := Cursor.X;
  while Count > 0 do begin
    case Buffer^ of
      #32..#255:
	begin
	  PutChar(Cursor.X, Cursor.Y,Buffer^);
	  Inc(Cursor.X);
	  if Cursor.X > R then R := Cursor.X;
	  if Cursor.X = ScreenSize.X then NewLine;
	end;
      #13:
	NewLine;
      #8:
	if Cursor.X > 0 then begin
	  Dec(Cursor.X);
	  PutChar(Cursor.X, Cursor.Y,' ');
	  if Cursor.X < L then L := Cursor.X;
	end;
      #7:
        MessageBeep(0);
    end;
    Inc(Buffer);
    Dec(Count);
  end;
  ShowText(L, R);
  if AutoTracking then TrackCursor;
end; { WriteBuf }

procedure WriteChar(Ch: Char);
  {- Write character to CRT window }
begin
  WriteBuf(@Ch,1);
end; { WriteChar }

function KeyPressed: Boolean;
  {- Return keyboard status }
var
  M: TMsg;
begin
  InitWinCrt;
  while PeekMessage(M, 0, 0, 0, pm_Remove) do
  begin
    if M.Message = wm_Quit then Terminate;
    TranslateMessage(M);
    DispatchMessage(M);
  end;
  KeyPressed := KeyCount > 0;
end; { KeyPressed }

function ReadKey: Char;
  {- Read key from CRT window.}
begin
  TrackCursor;
  if not KeyPressed then
  begin
    Reading := True;
    if Focused then ShowCursor;
    repeat WaitMessage until KeyPressed;
    if Focused then HideCursor;
    Reading := False;
  end;
  ReadKey := KeyBuffer[0];
  Dec(KeyCount);
  Move(KeyBuffer[1], KeyBuffer[0], KeyCount);
end; { ReadKey }

function ReadBuf(Buffer: PChar; Count: Word): Word;
  {- Read text buffer from CRT window.}
var
  Ch: Char;
  I: Word;
begin
  I := 0;
  repeat
    Ch := ReadKey;
    case Ch of
      #8:
	if I > 0 then begin
	  Dec(I);
	  WriteChar(#8);
	end;
      #32..#255:
	if I < Count - 2 then
	begin
	  Buffer[I] := Ch;
	  Inc(I);
	  WriteChar(Ch);
	end;
    end;
  until (Ch = #13) or (CheckEOF and (Ch = #26));
  Buffer[I] := Ch;
  Inc(I);
  if Ch = #13 then
  begin
    Buffer[I] := #10;
    Inc(I);
    WriteChar(#13);
  end;
  TrackCursor;
  ReadBuf := I;
end; { ReadBuf }

procedure GotoXY(X, Y: Integer);
  {- Set cursor position.}
begin
  CursorTo(X - 1, Y - 1);
end; { GotoXY }

function WhereX: Integer;
  {- Return cursor X position.}
begin
  WhereX := Cursor.X + 1;
end; { WhereX }

function WhereY: Integer;
  {- Return cursor Y position.}
begin
  WhereY := Cursor.Y + 1;
end; { WhereY }

procedure ClrScr;
  {- Clear screen.}
begin
  InitWinCrt;
  ScreenBuffer.FreeAll;
  Longint(Cursor) := 0;
  Longint(Origin) := 0;
  SetScrollBars;
  InvalidateRect(CrtWindow, nil, True);
  UpdateWindow(CrtWindow);
end; { ClrScr }

procedure ClrEol;
  {- Clear to end of line.}
begin
  InitWinCrt;
  ClearToEol(Cursor.X, Cursor.Y);
  ShowText(Cursor.X, ScreenSize.X);
end; { ClrEol }

{-------------------------------------------------- Gestione messaggi Windows }

procedure WindowCreate;
  {- wm_Create message handler.}
begin
  Created := True;
  ScreenBuffer.Init(25,25);
  if not CheckBreak then
    EnableMenuItem(GetSystemMenu(CrtWindow, False), sc_Close,
      mf_Disabled + mf_Grayed);
end; { WindowCreate }

procedure WindowPaint;
  {- wm_Paint message handler.}
var
  X1, X2, Y1, Y2: Integer;
begin
  Painting := True;
  InitDeviceContext;
  X1 := Max(0, PS.rcPaint.left div CharSize.X + Origin.X);
  X2 := Min(ScreenSize.X,
    (PS.rcPaint.right + CharSize.X - 1) div CharSize.X + Origin.X);
  Y1 := Max(0, PS.rcPaint.top div CharSize.Y + Origin.Y);
  Y2 := Min(ScreenSize.Y,
    (PS.rcPaint.bottom + CharSize.Y - 1) div CharSize.Y + Origin.Y);
  while Y1 < Y2 do begin
    TextOut(DC, (X1 - Origin.X) * CharSize.X, (Y1 - Origin.Y) * CharSize.Y,
      ScreenPtr(X1, Y1), X2 - X1);
    Inc(Y1);
  end;
  DoneDeviceContext;
  Painting := False;
end; { WindowPaint }

procedure WindowScroll(Which, Action, Thumb: Integer);
  {- wm_VScroll and wm_HScroll message handler.}
var
  X,Y : integer;

  function GetNewPos(Pos, Page, Range: Integer): Integer;
  begin
    case Action of
      sb_LineUp        : GetNewPos := Pos - 1;
      sb_LineDown      : GetNewPos := Pos + 1;
      sb_PageUp        : GetNewPos := Pos - Page;
      sb_PageDown      : GetNewPos := Pos + Page;
      sb_Top           : GetNewPos := 0;
      sb_Bottom        : GetNewPos := Range;
      sb_ThumbPosition : GetNewPos := Thumb;
    else
      GetNewPos := Pos;
    end;
  end; { GetNewPos }

begin { WindowScroll }
  X := Origin.X;
  Y := Origin.Y;
  case Which of
    sb_Horz: X := GetNewPos(X, ClientSize.X div 2, Range.X);
    sb_Vert: Y := GetNewPos(Y, ClientSize.Y, Range.Y);
  end;
  ScrollTo(X, Y);
end; { WindowScroll }

procedure WindowResize(X, Y: Integer);
  {- wm_Size message handler.}
begin
  if Focused and Reading then HideCursor;
  ClientSize.X := X div CharSize.X;
  ClientSize.Y := Y div CharSize.Y;
  Range.X := Max(0, ScreenSize.X - ClientSize.X);
  Range.Y := Max(0, ScreenSize.Y - ClientSize.Y);
  Origin.X := Min(Origin.X, Range.X);
  Origin.Y := Min(Origin.Y, Range.Y);
  SetScrollBars;
  if Focused and Reading then ShowCursor;
end; { WindowResize }

procedure WindowMinMaxInfo(MinMaxInfo: PMinMaxInfo);
  {- wm_GetMinMaxInfo message handler.}
var
  X, Y: Integer;
  Metrics: TTextMetric;
begin
  InitDeviceContext;
  GetTextMetrics(DC, Metrics);
  CharSize.X := Metrics.tmMaxCharWidth;
  CharSize.Y := Metrics.tmHeight + Metrics.tmExternalLeading;
  CharAscent := Metrics.tmAscent;
  X := Min(ScreenSize.X * CharSize.X + GetSystemMetrics(sm_CXVScroll),
    GetSystemMetrics(sm_CXScreen)) + GetSystemMetrics(sm_CXFrame) * 2;
  Y := GetSystemMetrics(sm_CYScreen) + GetSystemMetrics(sm_CYFrame) * 2;
  MinMaxInfo^[1].x := X;
  MinMaxInfo^[1].y := Y;
  MinMaxInfo^[3].x := CharSize.X * 16 + GetSystemMetrics(sm_CXVScroll) +
    GetSystemMetrics(sm_CXFrame) * 2;
  MinMaxInfo^[3].y := CharSize.Y * 4 + GetSystemMetrics(sm_CYHScroll) +
    GetSystemMetrics(sm_CYFrame) * 2 + GetSystemMetrics(sm_CYCaption);
  MinMaxInfo^[4].x := X;
  MinMaxInfo^[4].y := Y;
  DoneDeviceContext;
end; { WindowMinMaxInfo }

procedure WindowChar(Ch: Char);
  {- wm_Char message handler.}
begin
  if CheckBreak and (Ch = #3) then Terminate;
  if KeyCount < SizeOf(KeyBuffer) then begin
    KeyBuffer[KeyCount] := Ch;
    Inc(KeyCount);
  end;
end; { WindowChar }

procedure WindowKeyDown(KeyDown: Byte);
  {- wm_KeyDown message handler.}
var
  CtrlDown: Boolean;
  I: Integer;
begin
  if CheckBreak and (KeyDown = vk_Cancel) then Terminate;
  CtrlDown := GetKeyState(vk_Control) < 0;
  for I := 1 to ScrollKeyCount do
    with ScrollKeys[I] do
      if (Key = KeyDown) and (Ctrl = CtrlDown) then begin
	WindowScroll(SBar, Action, 0);
	Exit;
      end;
end; { WindowKeyDown }

procedure WindowSetFocus;
  {- wm_SetFocus message handler }
begin
  Focused := True;
  if Reading then ShowCursor;
end; { WindowSetFocus }

procedure WindowKillFocus;
  {- wm_KillFocus message handler }
begin
  if Reading then HideCursor;
  Focused := False;
end; { WindowKillFocus }

procedure WindowDestroy;
  {- wm_Destroy message handler.}
var
  Rect : TRect;
begin
  GetWindowRect(CrtWindow,Rect);
  with Rect do begin
    WindowOrg.X  := Left;
    WindowOrg.Y  := Top;
    WindowSize.X := Right-Left;
    WindowSize.Y  := Bottom-Top;
  end;
  ScreenBuffer.Done;
  Longint(Cursor) := 0;
  Longint(Origin) := 0;
  Created := False;
end; { WindowDestroy }

function CrtWinProc(Window: HWnd;
                    Message, WParam: Word;
                    LParam: Longint): Longint;
  {- CRT window procedure }
begin
  CrtWinProc := 0;
  CrtWindow := Window;
  case Message of
    wm_Create        : WindowCreate;
    wm_Paint         : WindowPaint;
    wm_VScroll       : WindowScroll(sb_Vert, WParam, LongRec(LParam).Lo);
    wm_HScroll       : WindowScroll(sb_Horz, WParam, LongRec(LParam).Lo);
    wm_Size          : WindowResize(LongRec(LParam).Lo, LongRec(LParam).Hi);
    wm_GetMinMaxInfo : WindowMinMaxInfo(PMinMaxInfo(LParam));
    wm_Char          : WindowChar(Char(WParam));
    wm_KeyDown       : WindowKeyDown(Byte(WParam));
    wm_SetFocus      : WindowSetFocus;
    wm_KillFocus     : WindowKillFocus;
    wm_Destroy       : WindowDestroy;
  else
    CrtWinProc := DefWindowProc(Window, Message, WParam, LParam);
  end;
end; { CrtWinProc }

{---------------------------------------------------- Text file device driver }

function CrtOutput(var F: TTextRec): Integer; far;
  {- Text file device driver output function }
begin
  if F.BufPos <> 0 then
  begin
    WriteBuf(PChar(F.BufPtr), F.BufPos);
    F.BufPos := 0;
    KeyPressed;
  end;
  CrtOutput := 0;
end; { CrtOutput }

function CrtInput(var F: TTextRec): Integer; far;
  {- Text file device driver input function }
begin
  F.BufEnd := ReadBuf(PChar(F.BufPtr), F.BufSize);
  F.BufPos := 0;
  CrtInput := 0;
end; { CrtInput }

function CrtClose(var F: TTextRec): Integer; far;
  {- Text file device driver close function }
begin
  CrtClose := 0;
end; { CrtClose }

function CrtOpen(var F: TTextRec): Integer; far;
  {- Text file device driver open function }
begin
  if F.Mode = fmInput then
  begin
    F.InOutFunc := @CrtInput;
    F.FlushFunc := nil;
  end else
  begin
    F.Mode := fmOutput;
    F.InOutFunc := @CrtOutput;
    F.FlushFunc := @CrtOutput;
  end;
  F.CloseFunc := @CrtClose;
  CrtOpen := 0;
end; { CrtOpen }

procedure AssignCrt(var F: Text);
  {- Assign text file to CRT device }
begin
  with TTextRec(F) do begin
    Handle := $FFFF;
    Mode := fmClosed;
    BufSize := SizeOf(Buffer);
    BufPtr := @Buffer;
    OpenFunc := @CrtOpen;
    Name[0] := #0;
  end;
end; { AssignCrt }

{----------------------------------------------- Apertura e chiusura finestra }

procedure InitWinCrt;
  {- Create CRT window if required.}
begin
  if not Created then begin
    CrtWindow := CreateWindow(
      CrtClass.lpszClassName,
      WindowTitle,
      ws_OverlappedWindow + ws_HScroll + ws_VScroll,
      WindowOrg.X, WindowOrg.Y,
      WindowSize.X, WindowSize.Y,
      0,
      0,
      HInstance,
      nil);
    ShowWindow(CrtWindow, CmdShow);
    UpdateWindow(CrtWindow);
  end;
end; { InitWinCrt }

procedure DoneWinCrt;
  {- Destroy CRT window if required }
begin
  if Created then DestroyWindow(CrtWindow);
end; { DoneWinCrt }

procedure ExitWinCrt; far;
  {- WinCrt unit exit procedure.}
begin
  ExitProc := SaveExit;
  SaveConfig;
  DoneWinCrt;
end; { ExitWinCrt }

{---------------------------------------------------------------------- Main }

begin
  if HPrevInst = 0 then begin
    CrtClass.hInstance := HInstance;
    CrtClass.hIcon := LoadIcon(0, idi_Application);
    CrtClass.hCursor := LoadCursor(0, idc_Arrow);
    CrtClass.hbrBackground := color_Window + 1;
    RegisterClass(CrtClass);
  end;
  AssignCrt(Input);
  Reset(Input);
  AssignCrt(Output);
  Rewrite(Output);
  GetModuleFileName(HInstance, WindowTitle, SizeOf(WindowTitle));
  OemToAnsi(WindowTitle, WindowTitle);
  LoadConfig;
  SaveExit := ExitProc;
  ExitProc := @ExitWinCrt;
end. { unit WinDump }
