Unit HandTIA;

Interface

Uses frmDebuggerUnt,SPUnit,Classes,AtariDis,DirectX,Profile,Graphics,Windows,AtariControls,ZipMstr;

Const
  Version : String = '2.6';
  ScnBufWidth  = 160;
  ScnBufHeight = 350;
  ScnBufSize   = ScnBufWidth * ScnBufHeight;
  SndBufSize   = 10 * 1024+512;
  FontName  : String      = 'Courier New';
  FontSize  : Integer     = 10;
  FontStyle : TFontStyles = [fsBold];

Type
  PZipDirEntry = ^ZipDirEntry;
  TKailleraControls = Packed Record
    PadDriv : Word;//LongInt;
    Console : Byte;
    PortA   : Byte;
  End;
  TKailleraGameCallBack          = Function(Game: PChar; Player: Integer; NumPlayers: Integer): Integer; stdcall;
  TKailleraChatReceivedCallBack  = Procedure(Nick: PChar; Text: PChar);                                  stdcall;
  TKailleraClientDroppedCallBack = Procedure(Nick: PChar; PlayerNB: Integer);                            stdcall;
  TKailleraMoreInfoCallBack      = Procedure(GameName: PChar);                                           stdcall;
  TKailleraInfo = Packed Record
    AppName               : PChar;
    GameList              : PChar;
    GameCallBack          : TKailleraGameCallBack;
    ChatReceivedCallBack  : TKailleraChatReceivedCallBack;
    ClientDroppedCallBack : TKailleraClientDroppedCallBack;
    MoreInfoCallBack      : TKailleraMoreInfoCallBack;
  End;
  RGB = Packed Record
    B,G,R: Byte;
  End;
  TPalette = Packed Array[0..255] Of RGB;
  TMenuSort = (msName,msSize,msType,msManufacturer,msYear,msDefaultSize,msController,msScreenShot,msExtras);
  TRGBQUAD = Packed Record
    rgbBlue     : Byte;
    rgbGreen    : Byte;
    rgbRed      : Byte;
    rgbReserved : Byte;
  End;
  TByteShift     = Packed Array[0..2047] Of Byte;
  TMissileTable  = Packed Array[0..128 * 32 - 1] Of Byte;
  TSetAdjust     = Packed Array[0..256 * 76 - 1] Of ShortInt;
  TMenuInfo = Record
    Sel         : Integer;
    _Top        : Integer;
    Pos200      : Integer;
    Pos204      : Integer;
    Pos215      : Integer;
    Pos250      : Integer;
    Pos305      : Integer;
    FileName    : String;
    ZipFileName : String;
    Title       : String;
    Cont        : TController;
    ProfileRec  : TFileType;
  End;
  TFileListItem = Class
    Name         : String;
    ZIPName      : String;
    Size         : LongInt;
    Prof         : TFileType;
    RNam         : String;
    ScreenShot   : Boolean;
    BoxFrontScan : Boolean;
    BoxBackScan  : Boolean;
    BoxSideScan  : Boolean;
    CartScan     : Boolean;
    Manual       : Boolean;
    Extras       : String;
  End;
  TColorScheme   = (csGameType,csAlphabetical,csEvery3,csEvery4,csEvery5,csEvery6);
  TDisplayScheme = (dsNameExt4,dsNameExt3,dsNameExt2,
                    dsNameOnly4,dsNameOnly3,dsNameOnly2,
                    dsRealNameLeft,dsRealNameCenter);
  ConfigRec      = Packed Record
    MaxCols            : Word;   { HAND_TIA.ASM needs this to be first! }
    MaxRows            : Word;
    DoSound            : Boolean;
    FrameRate          : SmallInt;
    SoundBufSize       : Word;
    PlaybackRate       : Word;
    AutoCenter         : Boolean;
    ScreenModeWidth    : Word;
    ScreenModeHeight   : Word;
    ScreenModeDepth    : Word;
    MenuColorScheme    : TColorScheme;
    MenuForeColor1     : LongInt;
    MenuForeColor2     : LongInt;
    MenuBackColor1     : LongInt;
    MenuBackColor2     : LongInt;
    MenuDisplayScheme  : TDisplayScheme;
    GameResolution     : TGameRes;
    Pitfall2DSP        : Boolean;
    GamePath           : String;
    ScreenShotPath     : String;
    BoxFrontScanPath   : String;
    BoxBackScanPath    : String;
    BoxSideScanPath    : String;
    CartScanPath       : String;
    ManualPath         : String;
    AlternateGameMenu  : Boolean;
    UseDefaultSize     : Boolean;
    HideAllOnZoomFull  : Boolean;
    ShowFPS            : Boolean;
    ZoomMode           : Integer; 
  End;

Const
  MenuSortName : Array[TMenuSort] Of String =
   ('Name','Size','Type','Manufacturer','Year','Height','Controller','Shot','Extras');

Var
  MenuInfo                       : TMenuInfo;
  Game                           : TSave;
  MissileTable                   : TMissileTable;
  BitSwap                        : Packed Array[0..255] Of Byte;
  Limit                          : LongInt;
  ByteShift                      : TByteShift;
  BitTest                        : Packed Array[0..127] Of Cardinal;
  Handle_TIA_Jump                : Packed Array[0..31] Of LongInt;
  Handle_TIA_Jump_Set            : Boolean;
  AtariSeg                       : TASeg;
  TIA_Latch_Check                : Boolean;
  Config                         : ConfigRec;
  Handle_IO_Latch_Ptr            : Pointer;
  ScnPtr                         : Pointer;
  ScnPtr2                        : Pointer;
  ScnBuf                         : Packed Array[0..ScnBufSize - 1] Of Byte;
  ScnBuf2                        : Packed Array[0..2 * ScnBufSize - 1] Of Byte;
  DebugScnBuf                    : Packed Array[0..ScnBufSize - 1] Of Byte;
  Exiting                        : Boolean;
  HaltLine                       : Integer;
  BreakExpColl                   : TStringList;
  Path                           : String;
  Debugger                       : Boolean;
  ROMBackup                      : TGameROM;
  SetAdjust                      : TSetAdjust;
  SoundBufPtr                    : Pointer;
  SoundBufSize                   : LongInt;
  SoundBufPtr2                   : Pointer;
  SoundBufSize2                  : LongInt;
  SoundQueue                     : Packed Array[0..1025] Of Byte;
  SmallSoundBuf                  : Packed Array[0..259] Of Byte;
  Old_WaveLen                    : Packed Array[1..4] Of Byte; { The fourth byte is unused in these arrays }
  Old_Pulse                      : Packed Array[1..4] Of Byte;
  Old_Phase                      : Packed Array[1..4] Of Byte;
  Old_Signal                     : Packed Array[1..4] Of Byte;
  Old_Wave                       : Packed Array[1..4] Of Byte;
  SDesc                          : DDSurfaceDesc;
  KInfo                          : TKailleraInfo;
  KValues                        : Packed Array[1..8] Of TKailleraControls;
  UsingKaillera                  : Boolean;
  KailleraPlayer                 : Integer;
  KSyncInfo                      : Packed Array[1..8] Of LongInt;
  WorkSoundBuf                   : Packed Array[0..44100 - 1] Of Byte; // One full second worth
  OldPos                         : Integer;
  LastWrite                      : Integer;
  CMRAMState                     : Byte;
  CMColumn                       : Byte;
  CMROMBank                      : Word;
  CMRAM                          : Packed Array[0..$7FF] Of Byte;
  CMInputLatch                   : Packed Array[0..1] Of Byte;
  CMPorts                        : Packed Array[0..3] Of Byte;
  CMSWCHA                        : Byte;
  WriteOp                        : Byte;

Procedure kailleraInit;                                             stdcall; External 'kailleraclient.dll' Name '_kailleraInit@0';
Procedure kailleraShutdown;                                         stdcall; External 'kailleraclient.dll' Name '_kailleraShutdown@0';
Procedure kailleraGetVersion(Version: PChar);                       stdcall; External 'kailleraclient.dll' Name '_kailleraGetVersion@4';
Procedure kailleraSetInfo(Var KailleraInfo: TKailleraInfo);         stdcall; External 'kailleraclient.dll' Name '_kailleraSetInfos@4';
Procedure kailleraSelectServerDialog(Parent: HWND);                 stdcall; External 'kailleraclient.dll' Name '_kailleraSelectServerDialog@4';
Function  kailleraModifyPlayValues(Values: Pointer; Size: Integer): Integer; stdcall; External 'kailleraclient.dll' Name '_kailleraModifyPlayValues@8';
Procedure kailleraChatSend(Text: PChar);                            stdcall; External 'kailleraclient.dll' Name '_kailleraChatSend@4';
Procedure kailleraEndGame;                                          stdcall; External 'kailleraclient.dll' Name '_kailleraEndGame@0';

Procedure Handle_TIA; Far;
Procedure SetBankInfo(BankType: Char; Var Game: TSave);
Function  RemoveFileExt(St: String): String;
Function  FileExistsZip(FileName: String; Extract: Boolean; Var ZIP: Boolean): Boolean;

Function  KailleraGameCallBack(Game: PChar; Player: Integer; NumPlayers: Integer): Integer; stdcall;
Procedure KailleraChatReceivedCallBack(Nick: PChar; Text: PChar);                           stdcall;
Procedure KailleraClientDroppedCallBack(Nick: PChar; PlayerNB: Integer);                    stdcall;
Procedure KailleraMoreInfoCallBack(GameName: PChar);                                        stdcall;

Implementation

Uses frmMainUnt,Dialogs,SysUtils,frmAltMenuUnt,Forms,frmKailleraUnt;

Var
  ZipCacheName : String;
  ZipCacheList : TList;
  ZipCacheLow  : Array[0..255] Of LongInt;
  ZipCacheHigh : Array[0..255] Of LongInt;

Procedure Handle_TIA; External;
{$L HAND_TIA.OBJ}

Procedure FreeZipCacheList;
Begin
  While ZipCacheList.Count > 0 Do
  Begin
    Dispose(PZipDirEntry(ZipCacheList.Items[0]));
    ZipCacheList.Delete(0);
  End; // While
End; // FreeZipCacheList

Function FileExistsZip(FileName: String; Extract: Boolean; Var ZIP: Boolean): Boolean;
Var
  B     : Boolean;
  FName : String;
  Path  : String;
  S     : TSearchRec;
  Found : Boolean;
  Z     : ZipDirEntry;
  I,J   : Integer;
  L,H   : Integer;
  C     : Integer;
  P,P1  : PZipDirEntry;
  Ch    : Char;

Begin
  B   := FileExists(FileName);
  ZIP := False;
  If B Then FileExistsZip := B
  Else
  Begin
    FileName := ExpandFileName(FileName);
    Path     := ExtractFilePath(FileName);
    FName    := UpperCase(ExtractFileName(FileName));
    Found    := False;

    // Check the ZIP cache first, but only if it's in the same folder

    If UpperCase(ExtractFilePath(FileName)) = UpperCase(ExtractFilePath(ZipCacheName)) Then
    Begin
      L := ZipCacheLow[Byte(FName[1])];
      H := ZipCacheHigh[Byte(FName[1])];
      If L >= 0 Then
      Begin
        // Perform a binary search for the file name
        
        I := L;
        J := H - L;
        While (J <> 0) And Not Found Do
        Begin
          P := PZipDirEntry(ZipCacheList.Items[I]);
          C := AnsiCompareStr(P^.FileName,FName);
          If C = 0 Then Found := True Else
          Begin
            If C < 0 Then
            Begin
              If J > 0 Then
              Begin
                If I < H Then
                Begin
                  Inc(I,J);
                  If I > H Then I := H;
                End
                Else J := 0;
              End
              Else
              Begin
                J := -(J Div 2);
                Inc(I,J);
                If I > H Then I := H;
              End;
            End
            Else
            Begin
              If J < 0 Then
              Begin
                If I > L Then
                Begin
                  Inc(I,J);
                  If I < L Then I := L;
                End
                Else J := 0;
              End
              Else
              Begin
                J := -(J Div 2);
                Inc(I,J);
                If I < L Then I := L;
              End;
            End;
          End;
        End; // While
      End;
    End;

    // Start checking ZIP files if it wasn't in the cache

    If Not Found Then
    Begin
      If FindFirst(Path + '*.ZIP',faArchive,S) = 0 Then
      Repeat
        // If we already cached this ZIP file, don't reload the data (assume it hasn't changed)

        If ZipCacheName <> UpperCase(Path + S.Name) Then
        Begin
          frmMain.ZipMaster1.ZipFilename := Path + S.Name;

          // Check the file list

          I := 0;
          While (I < frmMain.ZipMaster1.ZipContents.Count) And Not Found Do
          Begin
            Z := PZipDirEntry(frmMain.ZipMaster1.ZipContents.Items[I])^;
            If UpperCase(Z.FileName) = UpperCase(FName) Then Found := True Else Inc(I);
          End; // While

          // If the file was found, cache the ZIP file

          If Found Then
          Begin

            // Copy the ZIP file list to the cache

            ZipCacheName := UpperCase(Path + S.Name);
            FreeZipCacheList;
            For I := 0 To frmMain.ZipMaster1.ZipContents.Count - 1 Do
            Begin
              New(P);
              P^ := PZipDirEntry(frmMain.ZipMaster1.ZipContents.Items[I])^;
              P^.FileName := UpperCase(P^.FileName);
              ZipCacheList.Add(P);
            End; // For I

            // Sort the cache (simple bubble sort for now)

            For I := 0 To ZipCacheList.Count - 2 Do
            Begin
              P := PZipDirEntry(ZipCacheList.Items[I]);
              For J := I + 1 To ZipCacheList.Count - 1 Do
              Begin
                P1 := PZipDirEntry(ZipCacheList.Items[J]);
                If P1^.FileName < P^.FileName Then
                Begin
                  ZipCacheList.Exchange(I,J);
                  P := P1;
                End;
              End; // For J
            End; // For I

            // Fill in the indexes

            For I := 0 To 255 Do
            Begin
              ZipCacheLow[I]  := -1;
              ZipCacheHigh[I] := -1;
            End; // For I
            For I := 0 To ZipCacheList.Count - 1 Do
            Begin
              Ch := PZipDirEntry(ZipCacheList.Items[I])^.FileName[1];
              If ZipCacheLow[Byte(Ch)] < 0 Then ZipCacheLow[Byte(Ch)] := I;
              ZipCacheHigh[Byte(Ch)] := I;
            End; // For I
          End;
        End;
      Until (FindNext(S) <> 0) Or Found;
    End;

    // Extract if we are told to

    If Extract And Found Then
    Begin
      frmMain.ZipMaster1.ZipFilename := ZipCacheName;
      frmMain.ZipMaster1.FSpecArgs.Clear;
      frmMain.ZipMaster1.FSpecArgs.Add(FName);
      frmMain.ZipMaster1.ExtrBaseDir := Path;
      frmMain.ZipMaster1.Extract;
    End;
    ZIP           := Found;
    FileExistsZip := Found;
  End;
End; // FileExistsZip

Procedure KailleraSync;
//Var J: Integer;
Begin

    FillChar(KSyncInfo,SizeOf(KSyncInfo),#0);
    KSyncInfo[1] := KailleraPlayer;
    KailleraModifyPlayValues(@KSyncInfo,4);
//    Sleep(500);

(*
  J := 0;
  frmMain.Label3.Caption := 'SYNC START';
  Repeat
    FillChar(KSyncInfo,SizeOf(KSyncInfo),#0);
    KSyncInfo[1] := KailleraPlayer;
    KailleraModifyPlayValues(@KSyncInfo,1);
    Sleep(500);
    Inc(J);
  Until (J > 100) Or ((KSyncInfo[1] = 1) And (KSyncInfo[2] = 2));
  If J > 100 Then
   frmMain.Label3.Caption := 'Unable to sync'#13#10 +
                             'KSyncInfo[1] = ' + IntToStr(KSyncInfo[1]) + #13#10 +
                             'KSyncInfo[2] = ' + IntToStr(KSyncInfo[2])
  Else frmMain.Label3.Caption := 'SYNC OK';
*)
End; // KailleraSync

Function KailleraGameCallBack(Game: PChar; Player: Integer; NumPlayers: Integer): Integer;
Var
  Path     : String;
  F        : TFileType;
  Name     : String;
  FileList : TList;
  Item     : TFileListItem;
  S        : TSearchRec;
  I,J      : Integer;
  Found    : Boolean;
  Found1   : Boolean;
  Z        : ZipDirEntry;
  St       : String;

Begin
  Name     := StrPas(Game);
  FileList := TList.Create;

  // Locate all .BIN and .PAL files

  If ParamCount > 0 Then Path := ParamStr(1) Else Path := Config.GamePath;
  If (Path <> '') And Not (Path[Length(Path)] In [':','\']) Then Path := Path + '\';

  If FindFirst(Path + '*.BIN',faArchive,S) = 0 Then
  Repeat
    Item := TFileListItem.Create;
    FileList.Add(Item);
    Item.Name         := S.Name;
    Item.ZIPName      := '';
    Item.Size         := S.Size;
    Item.BoxFrontScan := False;
    Item.BoxBackScan  := False;
    Item.BoxSideScan  := False;
    Item.CartScan     := False;
    Item.Manual       := False;
    Item.Extras       := '';
  Until FindNext(S) <> 0;

  If FindFirst(Path + '*.PAL',faArchive,S) = 0 Then
  Repeat
    Item := TFileListItem.Create;
    FileList.Add(Item);
    Item.Name         := S.Name;
    Item.ZIPName      := '';
    Item.Size         := S.Size;
    Item.BoxFrontScan := False;
    Item.BoxBackScan  := False;
    Item.BoxSideScan  := False;
    Item.CartScan     := False;
    Item.Manual       := False;
    Item.Extras       := '';
  Until FindNext(S) <> 0;

  // Locate all .ZIP files, and locate all .BIN and .PAL files in those files

  If FindFirst(Path + '*.ZIP',faArchive,S) = 0 Then
  Repeat
    frmMain.ZipMaster1.ZipFilename := ExpandFileName(Path + S.Name);
    I := 0;
    While I < frmMain.ZipMaster1.ZipContents.Count Do
    Begin
      Z := PZipDirEntry(frmMain.ZipMaster1.ZipContents.Items[I])^;
      Z.FileName := UpperCase(Z.FileName);
      If (ExtractFileExt(Z.FileName) = '.BIN') Or
         (ExtractFileExt(Z.FileName) = '.PAL') Then
      Begin
        Item := TFileListItem.Create;
        FileList.Add(Item);
        Item.Name         := ExtractFileName(Z.FileName);
        Item.ZIPName      := S.Name;
        Item.Size         := Z.UncompressedSize;
        Item.BoxFrontScan := False;
        Item.BoxBackScan  := False;
        Item.BoxSideScan  := False;
        Item.CartScan     := False;
        Item.Manual       := False;
        Item.Extras       := '';
      End;
      Inc(I);
    End; // While
  Until FindNext(S) <> 0;

  frmAltMenu.SortFileList(FileList,0,FileList.Count - 1);

  // Load the game if possible

  I     := 0;
  F     := Nil;
  Found := False;
  While (I < ProfileList.Count) And Not Found Do
  Begin

    // Find a game that has a matching title

    F := ProfileList[I];
    If F.RealName <> '' Then St := F.RealName Else St := F.Name;
    If St = Name Then
    Begin

      // See if that filename is in the file list

      J      := 0;
      Found1 := False;
      While (J < FileList.Count) And Not Found1 Do
      Begin
        St  := UpperCase(TFileListItem(FileList.Items[J]).Name);
        If St = UpperCase(F.Name) Then Found1 := True Else Inc(J);
      End; // While
      If Found1 Then Found := True Else Inc(I);
    End
    Else Inc(I);
  End; // While
  If Found Then
  Begin
    If TFileListItem(FileList.Items[I]).ZipName <> ''
     Then frmMain.LoadGame(Path + St,Path + TFileListItem(FileList.Items[I]).ZipName,F.RealName)
     Else frmMain.LoadGame(Path + St,'',F.RealName);
    UsingKaillera  := True;
    KailleraPlayer := Player;
    KailleraSync;
    Emulate;
  End;
  UsingKaillera := False;
  KailleraGameCallBack := 0;
End; // KailleraGameCallBack

Procedure KailleraChatReceivedCallBack(Nick: PChar; Text: PChar);
Begin
  If frmKaillera.lbChat.Items.Count > Max_Chat Then frmKaillera.lbChat.Items.Delete(0);
  frmKaillera.lbChat.Items.Add(StrPas(Nick) + ': ' + StrPas(Text));
End; // KailleraChatReceivedCallBack

Procedure KailleraClientDroppedCallBack(Nick: PChar; PlayerNB: Integer);
Begin
//  ShowMessage('KailleraClientDroppedCallBack');
End; // KailleraClientDroppedCallBack

Procedure KailleraMoreInfoCallBack(GameName: PChar);
Begin
//  ShowMessage('KailleraModeInfoCallBack');
End; // KailleraMoreInfoCallBack

Procedure SetBankInfo(BankType: Char; Var Game: TSave);
 Var J: Integer;
Begin
  Case BankType Of
'T': Begin                                { TigerVision }
      Game.BankSwitch[$003F] := $60;
      Game.Cart              := ctTigerVision;
      Game.BankSize          := 512;
    End;
'A': Begin                                { Robot Tank/Decathlon }
      Game.BankSwitch[$01FE] := 30;
      Game.Cart              := ctActivision;
    End;
'2': Begin                                { Pitfall II }
      Game.BankSwitch[$1FF8] := $CB;
      Game.BankSwitch[$1FF9] := $CC;
      Game.Cart               := ctPitfall2;
      For J := $1040 To $107F Do Game.BankSwitch[J] := $CF;
      For J := $1840 To $187F Do Game.BankSwitch[J] := $CF;
      For J := $1000 To $103F Do Game.BankSwitch[J] := $CE;
      For J := $1800 To $183F Do Game.BankSwitch[J] := $CE;
    End;
'R': Begin                                { Superchip }
      Game.Cart := ctSuperChip;
{        BankSwitch^[$1FF6] := 1  + $40;
      BankSwitch^[$1FF7] := 5  + $40;
      BankSwitch^[$1FF8] := 9  + $40;
      BankSwitch^[$1FF9] := 13 + $40;}
      For J := $1000 To $107F Do Game.BankSwitch[J] := $5F;
      Game.BankSize := 1024 - 64{32};    { Size in dwords }
    End;
'V': Begin                                { Superchip }
      Game.Cart := ctCommaVid;
      For J := $1400 To $17FF Do Game.BankSwitch[J] := $90;
    End;
'P': Begin                                { Parker Bros. }
      For J := $1FE0 To $1FF7 Do Game.BankSwitch[J] := J And $FF;
      Game.Cart := ctParkerBros;
    End;
'M': Begin                                { M-Network }
      For J := $1FE0 To $1FEB Do Game.BankSwitch[J] := (J And $FF) - $10;
      For J := $1000 To $13FF Do Game.BankSwitch[J] := $DC;
      For J := $1800 To $18FF Do Game.BankSwitch[J] := $DD;
      Game.Cart := ctMNetwork;
      Game.MNetworkRAM := 8;
    End;
'G': Begin                               { 16-in-1 MegaBoy }
      Game.BankSwitch[$1FF0] := $80;
      Game.Cart := ctMegaBoy;
    End;
'8': Begin                               { Standard 8k }
       Game.BankSwitch[$1FF8] := 1;
       Game.BankSwitch[$1FF9] := 5;
       Game.Cart := ct8k;
     End;
'S': Begin                               { Starpath }
       For J := $1000 To $1FFF Do Game.BankSwitch[J] := $70;
       Game.BankSwitch[$1FF8] := $70;
       Game.BankSwitch[$0E00] := $70;
       Game.Cart := ctStarpath;
     End;
'C': Begin                               { CBS RAM-Plus }
       Game.BankSwitch[$1FF8] := $21;
       Game.BankSwitch[$1FF9] := $25;
       Game.BankSwitch[$1FFA] := $29;
       For J := $1000 To $10FF Do Game.BankSwitch[J] := $30;
       Game.BankSize := 1024 - 128{64};    { Size in dwords }
       Game.Cart := ctCBS;
     End;
'6': Begin                               { Standard 16k }
       Game.BankSwitch[$1FF6] := 1;
       Game.BankSwitch[$1FF7] := 5;
       Game.BankSwitch[$1FF8] := 9;
       Game.BankSwitch[$1FF9] := 13;
       Game.Cart := ct16k;
     End;
'O': Begin                               { Compumate }
       Game.BankSwitch[$0280] := $A0;
       For J := $1800 To $1FFF Do Game.BankSwitch[J] := $A1;
       Game.Cart              := ctCompumate;
     End;
  End; { Case }
End; { SetBankInfo }

Procedure InitMissileTable;
Var I,J: Word;
Begin
  FillChar(MissileTable,SizeOf(MissileTable),#0);
  For I := 0 To 31 Do
  Begin
    J := 1 Shl (I Shr 3);
    FillChar(MissileTable[(I Shl 7)],J,#255);
    Case (I And 7) Of
      1: FillChar(MissileTable[(I Shl 7) + 16],J,#255);
      2: FillChar(MissileTable[(I Shl 7) + 32],J,#255);
      3: Begin
           FillChar(MissileTable[(I Shl 7) + 16],J,#255);
           FillChar(MissileTable[(I Shl 7) + 32],J,#255);
         End;
      4: FillChar(MissileTable[(I Shl 7) + 64],J,#255);
      6: Begin
           FillChar(MissileTable[(I Shl 7) + 32],J,#255);
           FillChar(MissileTable[(I Shl 7) + 64],J,#255);
         End;
    End; { Case }
  End; { For I }
End; { InitMissileTable }

Procedure InitByteShift;
Var
  I,J   : Word;
  B1,B2 : Word;

Begin
  FillChar(ByteShift,SizeOf(ByteShift),#0);
  For I := 0 To 7 Do
  Begin
    For J := 0 To 71 Do
    Begin
      B2 := (I Shl 8) + J;
      B1 := B2 + 128;
      If (J <= 7) And (I In [0..4,6]) Then
      Begin
        ByteShift[(I Shl 8) + J + 128] := 1   Shl J;
        ByteShift[(I Shl 8) + J]       := $80 Shr J;
      End;
      Case I Of
        1: Case J Of
             16..23:
             Begin
               ByteShift[B1] := 1   Shl (J - 16);
               ByteShift[B2] := $80 Shr (J - 16);
             End;
           End;
        2: Case J Of
             32..39:
             Begin
               ByteShift[B1] := 1   Shl (J - 32);
               ByteShift[B2] := $80 Shr (J - 32);
             End;
           End; { Case }
        3: Case J Of
             16..23:
             Begin
               ByteShift[B1] := 1   Shl (J - 16);
               ByteShift[B2] := $80 Shr (J - 16);
             End;
             32..39:
             Begin
               ByteShift[B1] := 1   Shl (J - 32);
               ByteShift[B2] := $80 Shr (J - 32);
             End;
           End; { Case }
        4: Case J Of
             64..71:
             Begin
               ByteShift[B1] := 1   Shl (J - 64);
               ByteShift[B2] := $80 Shr (J - 64);
             End;
           End; { Case }
        5: If J <= 15 Then
           Begin
             ByteShift[B1] := 1   Shl (J Shr 1);
             ByteShift[B2] := $80 Shr (J Shr 1);
           End;
        6: Case J Of
             32..39:
             Begin
               ByteShift[B1] := 1   Shl (J - 32);
               ByteShift[B2] := $80 Shr (J - 32);
             End;
             64..71:
             Begin
               ByteShift[B1] := 1   Shl (J - 64);
               ByteShift[B2] := $80 Shr (J - 64);
             End;
           End; { Case }
        7: If J <= 31 Then
           Begin
             ByteShift[B1] := 1   Shl (J Shr 2);
             ByteShift[B2] := $80 Shr (J Shr 2);
           End;
      End; { Case }
    End; { For J }
  End; { For I }
End; { InitByteShift }

Procedure InitBitTest;
Var
  _SHR,_SHL : Cardinal;
  I         : Integer;

Begin
  _SHR := $80000;
  _SHL := 1;
  For I := 0 To 19 Do
  Begin
    BitTest[I]      := _SHR;
    BitTest[I + 20] := _SHR;
    BitTest[I + 64] := _SHR;
    BitTest[I + 84] := _SHL;
    Asm
      SHL   DWORD PTR _SHL,1
      SHR   DWORD PTR _SHR,1
    End; { Asm }
  End; { For I }
End; // InitBitTest

Procedure InitSetAdjust;
Var
  I,J,K    : LongInt;
  M0,M1,M2 : SmallInt;
  
Begin
  For I := 0 To 15 Do   { Old motion }
  Begin
    For J := 0 To 15 Do { New motion }
    Begin
      For K := 0 To 75 Do { Position }
      Begin
        M0 := I;
        Asm
          SHL   WORD PTR M0,12
          SAR   WORD PTR M0,12
        End; { Asm }
        M1 := J;
        Asm
          SHL   WORD PTR M1,12
          SAR   WORD PTR M1,12
        End; { Asm }
        M0 := -M0;   { Left = negative }
        M1 := -M1;   { Left = negative }
        M2 := 0;
        Case K Of
          0..17: M2 := 0;
         18..47: Begin
                   If M1 <= 0 Then
                   Begin
                     If M0 <= 0 Then M2 := M1 - M0
                     Else If M0 < 8 Then
                     Begin
                       If K < 18 + (8 - M0) * 4 Then M2 := M1 - M0 Else M2 := 0;
                     End;
                   End
                   Else
                   Begin
                     If M0 <= 0 Then
                     Begin
                       If K < 18 + (8 - M1) * 4 Then M2 := M1 - M0 Else M2 := 0;
                     End
                     Else If M0 < 8 Then
                     Begin
                       If K < 18 + (8 - M0) * 4 Then M2 := M1 - M0 Else M2 := 0;
                     End;
                   End;
                 End;
         48..75: Begin
                   If (M1 <= 0) And (M0 <= 0) Then
                   Begin
                     If K < 48 + (0 - M0) * 4 Then
                     Begin
                       M2 := M1 - M0;
                       If (M2 > 0) And (K > 48 + (0 - M1) * 4) Then M2 := 0;
                     End
                     Else M2 := 0;
                   End
                   Else M2 := 0;
                 End;
        End; { Case }

        { Copy all cases where M1 = 8 (i.e. J = 8) }

        If J = 8 Then SetAdjust[K * 256 + J * 16 + I] :=
                      SetAdjust[K * 256 + J * 16 + I - 16] Else
         SetAdjust[K * 256 + J * 16 + I] := M2;
        If I = 8 Then SetAdjust[K * 256 + J * 16 + I] := 0;
      End; { For K }
    End; { For J }
  End; { For I }
End; // InitSetAdjust

Function RemoveFileExt(St: String): String;
Var I: Integer;
Begin
  I := Length(St);
  While (St[I] <> '.') And (I > 0) Do Dec(I);
  If I <> 0 Then St := Copy(St,1,I - 1);
  Result := St;
End; // RemoveFileExt

Initialization
  Debugger := False;
  Path     := '';
  FillChar(Game,SizeOf(Game),#0);
  InitMissileTable;
  InitByteShift;
  InitBitTest;
  InitSetAdjust;
  ScnPtr  := @ScnBuf;
  ScnPtr2 := @ScnBuf2;
  BreakExpColl := Nil;
  ZipCacheName := '';
  ZipCacheList := TList.Create;
Finalization
  FreeZipCacheList;
  ZipCacheList.Free;
End.

// ----------------------------------------------------------------------
// PCAE and PCAEWin - PC Atari Emulator - Atari 2600 emulator
// Copyright (C) 2000 John Dullea
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// ----------------------------------------------------------------------
