Attribute VB_Name = "Declares"
Option Explicit

'API declares

Public Declare Function GetTickCount Lib "kernel32" () As Long
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Dest As Any, Src As Any, ByVal cb&)

Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

Public Declare Function CreateWindowEx Lib "user32" Alias "CreateWindowExA" (ByVal dwExStyle As Long, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hWndParent As Long, ByVal hMenu As Long, ByVal hInstance As Long, lpParam As Any) As Long
Public Declare Function DestroyWindow Lib "user32" (ByVal hWnd As Long) As Long

Public Declare Function WritePrivateProfileString Lib "kernel32" Alias "WritePrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpString As Any, ByVal lpFileName As String) As Long
Public Declare Function GetPrivateProfileString Lib "kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFileName As String) As Long

Public Declare Function GetVolumeSerialNumber Lib "kernel32" Alias "GetVolumeInformationA" (ByVal lpRootPathName As String, ByVal lpVolumeNameBuffer As Long, ByVal nVolumeNameSize As Long, lpVolumeSerialNumber As Long, ByVal lpMaximumComponentLength As Long, ByVal lpFileSystemFlags As Long, ByVal lpFileSystemNameBuffer As Long, ByVal nFileSystemNameSize As Long) As Long
Public Declare Function GetTimeZoneInformation Lib "kernel32" (lpTimeZoneInformation As TIME_ZONE_INFORMATION) As Long
Public Type SYSTEMTIME
        wYear As Integer
        wMonth As Integer
        wDayOfWeek As Integer
        wDay As Integer
        wHour As Integer
        wMinute As Integer
        wSecond As Integer
        wMilliseconds As Integer
End Type
Public Type TIME_ZONE_INFORMATION
        Bias As Long
        StandardName(32) As Integer
        StandardDate As SYSTEMTIME
        StandardBias As Long
        DaylightName(32) As Integer
        DaylightDate As SYSTEMTIME
        DaylightBias As Long
End Type

Public Declare Function TextOut Lib "gdi32" Alias "TextOutA" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long, ByVal lpString As String, ByVal nCount As Long) As Long
Public Declare Function SetTextColor Lib "gdi32" (ByVal hdc As Long, ByVal crColor As Long) As Long
Public Declare Function BitBlt Lib "gdi32" (ByVal hDestDC As Long, ByVal X As Long, ByVal Y As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hSrcDC As Long, ByVal xSrc As Long, ByVal ySrc As Long, ByVal dwRop As Long) As Long

Public Const SRCCOPY = &HCC0020 ' (DWORD) dest = source

Public Declare Function SetTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long, ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Public Declare Function KillTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long) As Long
Public Declare Function Shell_NotifyIcon Lib "shell32" Alias "Shell_NotifyIconA" (ByVal dwMessage As Long, pnid As NOTIFYICONDATA) As Boolean

Public Type NOTIFYICONDATA
    cbSize                      As Long
    hWnd                        As Long
    uId                         As Long
    uFlags                      As Long
    ucallbackMessage            As Long
    hIcon                       As Long
    szTip                       As String * 64
End Type

'SysTray icon

Public SysTrayOldWndProc        As Long
Public SysTrayHWnd              As Long
Public SysTraySelPicture        As Long

'Winsock DLL imports

#If Win16 Then
    Public Declare Function bind Lib "Winsock.dll" (ByVal S As Long, addr As sockaddr, ByVal namelen As Long) As Long
    Public Declare Function closesocket Lib "Winsock.dll" (ByVal S As Long) As Long
    Public Declare Function ntohs Lib "Winsock.dll" (ByVal netshort As Long) As Integer
    Public Declare Function htons Lib "Winsock.dll" (ByVal hostshort As Long) As Integer
    Public Declare Function inet_addr Lib "Winsock.dll" (ByVal CP As String) As Long
    Public Declare Function recvfrom Lib "Winsock.dll" (ByVal S As Long, buf As Any, ByVal buflen As Long, ByVal Flags As Long, from As sockaddr, fromlen As Long) As Long
    Public Declare Function sendto Lib "Winsock.dll" (ByVal S As Long, buf As Any, ByVal buflen As Long, ByVal Flags As Long, to_addr As sockaddr, ByVal tolen As Long) As Long
    Public Declare Function socket Lib "Winsock.dll" (ByVal af As Long, ByVal s_type As Long, ByVal protocol As Long) As Long
    Public Declare Function gethostbyname Lib "Winsock.dll" (ByVal host_name As String) As Long
    Public Declare Function WSAStartup Lib "Winsock.dll" (ByVal wVR As Long, lpWSAD As WSAdata) As Long
    Public Declare Function WSACleanup Lib "Winsock.dll" () As Long
    Public Declare Function WSAAsyncSelect Lib "Winsock.dll" (ByVal S As Long, ByVal hWnd As Long, ByVal wMsg As Long, ByVal lEvent As Long) As Long
#ElseIf Win32 Then
    Public Declare Function bind Lib "wsock32.dll" (ByVal S As Long, addr As sockaddr, ByVal namelen As Long) As Long
    Public Declare Function closesocket Lib "wsock32.dll" (ByVal S As Long) As Long
    Public Declare Function ntohs Lib "wsock32.dll" (ByVal netshort As Long) As Integer
    Public Declare Function htons Lib "wsock32.dll" (ByVal hostshort As Long) As Integer
    Public Declare Function inet_addr Lib "wsock32.dll" (ByVal CP As String) As Long
    Public Declare Function recvfrom Lib "wsock32.dll" (ByVal S As Long, buf As Any, ByVal buflen As Long, ByVal Flags As Long, from As sockaddr, fromlen As Long) As Long
    Public Declare Function sendto Lib "wsock32.dll" (ByVal S As Long, buf As Any, ByVal buflen As Long, ByVal Flags As Long, to_addr As sockaddr, ByVal tolen As Long) As Long
    Public Declare Function socket Lib "wsock32.dll" (ByVal af As Long, ByVal s_type As Long, ByVal protocol As Long) As Long
    Public Declare Function gethostbyname Lib "wsock32.dll" (ByVal host_name As String) As Long
    Public Declare Function WSAStartup Lib "wsock32.dll" (ByVal wVR As Long, lpWSAD As WSAdata) As Long
    Public Declare Function WSACleanup Lib "wsock32.dll" () As Long
    Public Declare Function WSAAsyncSelect Lib "wsock32.dll" (ByVal S As Long, ByVal hWnd As Long, ByVal wMsg As Long, ByVal lEvent As Long) As Long
#End If

Public Enum FD
    FD_READ = 1
    FD_WRITE = 2
    FD_OOB = 4
    FD_ACCEPT = 8
    FD_CONNECT = 16
    FD_CLOSE = 32
    FD_SETSIZE = 64
End Enum
Public Const FD_ALL = 1 Or 2 Or 4 Or 8 Or 16 Or 32 Or 64

Public Type hostent
    h_name As Long
    h_aliases As Long
    h_addrtype As Integer
    h_length As Integer
    h_addr_list As Long
End Type
Public hostent As hostent

Public Type WSAdata
    wVersion As Integer
    wHighVersion As Integer
    szDescription As String * 257
    szSystemStatus As String * 129
    iMaxSockets As Integer
    iMaxUdpDg As Integer
    lpVendorInfo As Long
End Type
Public WSAdata As WSAdata

Public Type sockaddr
    sin_family As Integer
    sin_port As Integer
    sin_addr As Long
    sin_zero As String * 8
End Type
Public sockaddr As sockaddr

'Socket Data structures

Public Type SocketData
    Remote              As sockaddr
    SockID              As Long
End Type
Public SocketData       As SocketData

'Winsock window handle

Public WinsockHandle   As Long
Public OldWndProc      As Long

'Constants

Public Const MAX_BOTS = 4       'Limit memory
Public Const MAX_PLAYERS = 255  'Limit 255
Public Const MAX_OPS = 255      'Limit 255
Public Const MAX_SHARES = 255   'Limit 255
Public Const MAX_QUEUED = 255   'Limit 255
Public Const MAX_SCORES = 255   'Limit memory
Public Const MAX_SECURES = 255  'Limit memory
Public Const MAX_BACKLOG = 255  'Limit memory

Public Enum BOT_FLAVORS
    ALIAS_REF = 0
    ELIM_REF = 1
    ZOMBIES_REF = 2
    SWZ_REF = 3
    DSB_REF = 4
    CONQUER_REF = 5
    BOUNTYGAME_REF = 6
    SARUNOKAMI_REF = 7
End Enum

Public Enum ELIM_TYPE
    Vanilla = 1
    Race = 2
    WBElim = 3
    WBRace = 4
    JavElim = 5
    JavRace = 6
    TeamElim = 7
    GolfElim = 8
End Enum

Public Enum ALLOW
    OP_PUBLIC = 0
    OP_SIMPLE = 1
    OP_ACCOUNT = 2
    OP_SUPER = 3
End Enum

Public Enum GAME_STATUS
    CHECK = 0
    VOTE_GAME = 1
    VOTE_DEATHS = 2
    WAIT = 3
    STARTED = 4
    PAUSE = 5
    WAIT_AFTER_RESULTS = 6
End Enum

'SSL structures

Public Type SecureMessage
    Message             As String
    ResendCount         As Integer
    SourceBot           As Integer
    InitialTicks        As Long
End Type
Public Secures(1 To MAX_SECURES) As SecureMessage

'Structs

Public Type PlayerEntry
    OpID                        As Byte
    ShipType                    As Byte
    Frequency                   As Integer
    Wins                        As Integer
    Losses                      As Integer
    Points                      As Long
    SSName                      As String
    Squad                       As String
    Vote                        As Byte
    GamePoints                  As Byte
    X                           As Integer
    Y                           As Integer
    D                           As Byte
End Type

Public Type OperatorEntry
    Online                      As Byte
    OperatorStatus              As ALLOW
    Username                    As String
End Type

Public Type BOT_INFO
    SlotClosed                  As Byte
    Bot_Enabled                 As Byte
    Periodic                    As Byte

    MaxDeaths                   As Byte
    GameType                    As Byte
    TimerCounter                As Byte
    TimerMax                    As Byte

    PrizeNumber                 As Integer
    PrizeCounter                As Byte
    PrizeWait                   As Byte
    PrizeEnabled                As Byte

    EvenTeams                   As Byte
    EvenCounter                 As Byte

    Suicide                     As Byte
    AutoSpec                    As Byte
    LockShip                    As Byte
    ErrorChannel                As String
    UseQueue                    As Byte
    KillPrize                   As Byte
    LogScores                   As Byte

    BountyID                    As Integer
    AttachedTo                  As Integer
    CenteredOn                  As Integer

    MyId                        As Integer
    LastSID                     As Long
    MyName                      As String
    ArenaName                   As String

    D                           As Byte
    DownloadingSettings         As Byte

    PingTime                    As Long
    AvgPing                     As Long
    TotalPings                  As Long
    TotalLost                   As Long
    TotalReliable               As Long

    TotalSent                   As Long
    TotalReceived               As Long
    KeepAliveCount              As Integer

    MySession                   As SocketData
    Bot_Type                    As BOT_FLAVORS
    Bot_Status                  As GAME_STATUS

    QueuedMessages(1 To MAX_QUEUED)  As String
    pList(0 To MAX_PLAYERS)     As PlayerEntry
    TotalPlayers                As Byte
End Type

'Public lists

Public ACKs(1 To MAX_BOTS, -32768 To 32767) As Byte

Public BotList(1 To MAX_BOTS)                       As BOT_INFO

Public SharedList(1 To MAX_BOTS, 1 To MAX_SHARES)   As String
Public TotalItems(1 To MAX_BOTS)                    As Byte

Public OpList(1 To MAX_OPS)                         As OperatorEntry
Public TotalOps                                     As Byte

'Quick lists

Public OpKey(1 To 3)            As String * 1
Public PrizeNames(-28 To 28)    As String
Public ShipNames(1 To 8)        As String
Public BotNames(0 To 8)         As String
Public OpNames(0 To 3)          As String

'Player aliasing

Public Type AliasInfo
    User                    As String
    IP                      As String
    Created                 As String
    Id                      As String
End Type

Public Alias(1 To 32000)    As AliasInfo
Public TotalAliases         As Integer

Public Downloading          As AliasInfo

'Player ranking

Public Type RankedPlayer
    SSName          As String
    Wins            As Long
    Losses          As Long
    GamesWon        As Long
    GamesPlayed     As Long
End Type

Public RankedPlayers(1 To 32767)    As RankedPlayer
Public TotalRanked                  As Long

'Conquer! statistics

Public Type ConquerFreq
    Frequency As Integer

    CurrentPlayers(1 To 200)    As Integer
    TotalCurrent                As Byte

    InitialOwner                As Integer
End Type

Public Conquer(1 To MAX_BOTS, 1 To 255) As ConquerFreq
Public TotalFreqs(1 To MAX_BOTS)        As Byte

'Score statistics

Public Type FreqScore
    Wins            As String
    Losses          As String
    FlagVictories   As String
End Type

Public Type LoggedScore
    ArenaName       As String
    Freq(0 To 1)    As FreqScore
End Type

Public Scores(1 To MAX_SCORES)  As LoggedScore
Public TotalScores              As Integer

'Remote/local session

Public CurrentBot               As Integer

'Chat window

Public Enum ChatType
    GREEN = 0
    RED = 1
    BLUE = 2
    BLACK = 3
    YELLOW = 4
End Enum

Public Type SSMsg
    Message                 As String
    MessageType             As ChatType
End Type

'0 = special case "ShellView"
Public MsgList(0 To MAX_BOTS, 1 To 100)   As SSMsg
Public TotalMsgs(0 To MAX_BOTS)           As Integer

Public LastHeight               As Integer
Public Bot_with_focus           As Integer

'SubSpace FTP

Public Enum Simple_FTP_Types
    None = 0
    SimpleSettings = 15
    Settings = 16
End Enum

Public Type FTP_Session
    FTP_Name        As String
    FTP_Data        As String
    FTP_Type        As Simple_FTP_Types
    FTP_FileLen     As Long
    FTP_Recved      As Long
End Type

Public FTPList(1 To MAX_BOTS) As FTP_Session

Public Outgoing As String

'Packet backlog

Public Type BacklogEntry
    Data        As String
    PacketID    As Integer
    Closed      As Byte
End Type

Public Backlog(1 To MAX_BOTS, 0 To MAX_BACKLOG)     As BacklogEntry
Public LastPacketID(1 To MAX_BOTS)                  As Integer

'Settings

Public Type Setting_Key
    Key_Name                    As String
    Current                     As Integer
    Original                    As Integer
    Minimum                     As Integer
    Maximum                     As Integer
    Changed                     As Byte
End Type

Public Type Setting_Catagory
    Catagory_Name               As String
    Catagory_Settings(0 To 255) As Setting_Key
    TotalKeys                   As Integer
End Type

Public Setting(1 To MAX_BOTS, 1 To 30) As Setting_Catagory
Public TotalSettings(1 To MAX_BOTS)    As Integer

Public HasSysop                 As Boolean
Public DiskLog                  As Boolean
Public BanID                    As Long
Public ZoneBias                 As Integer

Public Function GetProfileString(Section As String, Key As String) As String
    Dim S255 As String * 255, L As Long

    L = GetPrivateProfileString(Section, Key, "", ByVal S255, 255, App.Path + "\SSBot.INI")
    GetProfileString = Left(S255, L)
End Function

Public Sub SetProfileString(Section As String, Key As String, Value As String)
    WritePrivateProfileString Section, Key, Value, App.Path + "\SSBot.INI"
End Sub

'Login IDs

Public Function MachineID() As Long
    Dim Serial As Long

    Call GetVolumeSerialNumber("C:\", 0&, 0&, Serial, 0&, 0&, 0&, 0&)

    MachineID = Serial - 1
End Function

Public Function TimeZoneBias() As Integer
    Dim TimeZone As TIME_ZONE_INFORMATION

    Call GetTimeZoneInformation(TimeZone)

    TimeZoneBias = CInt(TimeZone.Bias - 30)
End Function

'Settings loader

Public Sub LoadSettings()
    Select Case LCase(GetProfileString("Login", "Sysop"))
        Case "yes":  HasSysop = True
        Case "1":    HasSysop = True
        Case "on":   HasSysop = True
        Case "true": HasSysop = True
        Case Else:   HasSysop = False
    End Select
    Select Case LCase(GetProfileString("Login", "DiskLog"))
        Case "yes":  DiskLog = True
        Case "1":    DiskLog = True
        Case "on":   DiskLog = True
        Case "true": DiskLog = True
        Case Else:   DiskLog = False
    End Select
End Sub

'Quick list loader

Public Sub LoadQuickLists()
    OpKey(1) = "I"
    OpKey(2) = "A"
    OpKey(3) = "S"

    ShipNames(1) = "Warbird"
    ShipNames(2) = "Javelin"
    ShipNames(3) = "Spider"
    ShipNames(4) = "Leviathan"
    ShipNames(5) = "Terrier"
    ShipNames(6) = "Weasel"
    ShipNames(7) = "Lancaster"
    ShipNames(8) = "Shark"

    BotNames(BOT_FLAVORS.ALIAS_REF) = "Alias finder"
    BotNames(BOT_FLAVORS.CONQUER_REF) = "ConquerRef"
    BotNames(BOT_FLAVORS.DSB_REF) = "DSB-style shipchecker"
    BotNames(BOT_FLAVORS.ELIM_REF) = "ElimRef"
    BotNames(BOT_FLAVORS.SWZ_REF) = "SWZ-style shipchecker"
    BotNames(BOT_FLAVORS.ZOMBIES_REF) = "ZombieRef"
    BotNames(BOT_FLAVORS.BOUNTYGAME_REF) = "BountyGameRef"
    BotNames(BOT_FLAVORS.SARUNOKAMI_REF) = "SarunokamiRef"

    OpNames(ALLOW.OP_ACCOUNT) = "Account Access"
    OpNames(ALLOW.OP_PUBLIC) = "Public Access"
    OpNames(ALLOW.OP_SIMPLE) = "Simple Access"
    OpNames(ALLOW.OP_SUPER) = "Super Access"

    PrizeNames(0) = "Shipreset"
    PrizeNames(1) = "Recharge"
    PrizeNames(2) = "Energy"
    PrizeNames(3) = "Rotation"
    PrizeNames(4) = "Stealth"
    PrizeNames(5) = "Cloak"
    PrizeNames(6) = "XRadar"
    PrizeNames(7) = "Warp"
    PrizeNames(8) = "Guns"
    PrizeNames(9) = "Bombs"
    PrizeNames(10) = "Bouncing bullets"
    PrizeNames(11) = "Thrust"
    PrizeNames(12) = "Top speed"
    PrizeNames(13) = "Full charge"
    PrizeNames(14) = "Engine shutdown"
    PrizeNames(15) = "Multifire"
    PrizeNames(16) = "Proximity"
    PrizeNames(17) = "Super!"
    PrizeNames(18) = "Shields!"
    PrizeNames(19) = "Shrapnel"
    PrizeNames(20) = "AntiWarp"
    PrizeNames(21) = "Repel"
    PrizeNames(22) = "Burst"
    PrizeNames(23) = "Decoy"
    PrizeNames(24) = "Thor"
    PrizeNames(25) = "Multiprize"
    PrizeNames(26) = "Brick"
    PrizeNames(27) = "Rocket"
    PrizeNames(28) = "Portal"
    PrizeNames(-1) = "Slower recharge"
    PrizeNames(-2) = "Less energy"
    PrizeNames(-3) = "Slower rotation"
    PrizeNames(-4) = "Lose stealth"
    PrizeNames(-5) = "Lose cloak"
    PrizeNames(-6) = "Lose Xradar"
    PrizeNames(-7) = "Lose warp"
    PrizeNames(-8) = "Gun downgrade"
    PrizeNames(-9) = "Bomb downgrade"
    PrizeNames(-10) = "Lose bouncing bullets"
    PrizeNames(-11) = "Less thrust"
    PrizeNames(-12) = "Lower top speed"
    PrizeNames(-13) = "Energy depleted"
    PrizeNames(-14) = "Engine shutdown"
    PrizeNames(-15) = "Lose multifire"
    PrizeNames(-16) = "Lose proximity"
    PrizeNames(-17) = "Negative super!"
    PrizeNames(-18) = "Negative shields!"
    PrizeNames(-19) = "Lose shrapnel"
    PrizeNames(-20) = "Lose antiwarp"
    PrizeNames(-21) = "Lose repel"
    PrizeNames(-22) = "Lose burst"
    PrizeNames(-23) = "Lose decoy"
    PrizeNames(-24) = "Lose thor"
    PrizeNames(-25) = "Negative multiprize"
    PrizeNames(-26) = "Lose brick"
    PrizeNames(-27) = "Lose rocket"
    PrizeNames(-28) = "Lose portal"
End Sub

'Debug output

Public Function PHex(RawHex As String) As String
    Dim S As String, I As Integer, H As String

    For I = 1 To Len(RawHex)
        H = Hex(Asc(Mid(RawHex, I, 1)))
        If Len(H) = 1 Then H = "0" + H
        S = S + H + " "
    Next

    PHex = S
End Function

'Formatting

Public Function FillZero(RawText As String, DesiredLength As Long) As String
    If DesiredLength <= Len(RawText) Then
        FillZero = RawText
    Else
        FillZero = RawText + String(DesiredLength - Len(RawText), Chr(0))
    End If
End Function

Public Function TrimZero(RawText As String) As String
    Dim L As Long

    L = InStr(1, RawText, Chr(0))
    If L > 0 Then
        TrimZero = Left(RawText, InStr(1, RawText, Chr(0)) - 1)
    Else
        TrimZero = RawText
    End If
End Function

Public Function TrimSpace(mStr As String) As String
    Dim I As Integer, S As String

    S = Trim(mStr)
    I = InStr(2, S, " ")
    Do While I > 0
        S = Left(S, I - 1) + Mid(S, I + 1)
        I = InStr(I, S, " ")
    Loop
    TrimSpace = S
End Function

Public Sub InsertByte(mStr As String, Offset As Byte, NewByte As Byte)
    mStr = Left(mStr, Offset - 1) + Chr(NewByte) + Mid(mStr, Offset + 1)
End Sub

Public Function GetArenaString(ByVal mStr As String) As String
    Dim I As Integer, O As String, C As Byte

    mStr = TrimSpace(mStr)

    If Left(mStr, 1) = "#" Then O = "#"

    For I = 1 To Len(mStr)
        C = Asc(Mid(mStr, I, 1))
        If C >= 48 And C <= 57 Then
            O = O + Chr(C)
        ElseIf C >= 65 And C <= 90 Then
            O = O + Chr(C)
        ElseIf C >= 97 And C <= 122 Then
            O = O + Chr(C)
        End If
    Next

    GetArenaString = O
End Function

Public Function GetPlayerCoords(BotID As Integer, PlayerID As Integer) As String
    GetPlayerCoords = Chr(64 + Int(BotList(BotID).pList(PlayerID).X / (MAX_X \ 20))) & CStr(Int(BotList(BotID).pList(PlayerID).Y / (MAX_Y \ 20)))
End Function

'Conversion / extraction

Public Function GetInteger(RawText As String) As Integer
    Dim I As Integer

    Call CopyMemory(I, ByVal RawText, 2)
    GetInteger = I
End Function

Public Function GetLong(RawText As String) As Long
    Dim L As Long

    Call CopyMemory(L, ByVal RawText, 4)
    GetLong = L
End Function

Public Function GetString(L As Long) As String
    Dim S4 As String * 4

    Call CopyMemory(ByVal S4, L, 4)
    GetString = S4
End Function

Public Function GetString2(I As Integer) As String
    Dim S2 As String * 2

    Call CopyMemory(ByVal S2, I, 2)
    GetString2 = S2
End Function

Public Function LOWORD(DWord As Long) As Integer
    LOWORD = Int(DWord Mod 65536)
End Function

Public Function HIWORD(DWord As Long) As Integer
    HIWORD = Int(DWord / 65536)
End Function

'BigInt extensions

Public Sub TrimZeros(BigInt As String)
    Dim I As Integer, NumberToTrim As Integer

    For I = Len(BigInt) To 1 Step -1
        If Mid(BigInt, I, 1) = "0" Then
            NumberToTrim = NumberToTrim + 1
        Else
            Exit For
        End If
    Next

    If NumberToTrim = Len(BigInt) Then
        BigInt = "0"
    Else
        BigInt = Left(BigInt, Len(BigInt) - NumberToTrim)
    End If
End Sub

Public Function Flip(ByVal BigInt As String) As String
    Dim I As Integer, O As String, Sign As Byte

    If Left(BigInt, 1) = "-" Then Sign = 1: BigInt = Mid(BigInt, 2)

    For I = Len(BigInt) To 1 Step -1
        O = O + Mid(BigInt, I, 1)
    Next

    If Sign = 1 Then
        Flip = "-" + O
    Else
        Flip = O
    End If
End Function

Public Function Add(In1 As String, In2 As String) As String
    Dim I As Integer, Carry As Byte, Sign(0 To 2) As Byte, O As String
    Dim BiggerNumber As Byte, SubRes As Integer
    Dim NegativeResult As Boolean, UseSubtraction As Boolean
    Dim BigInt1 As String, BigInt2 As String

    TrimZeros In1: BigInt1 = In1
    TrimZeros In2: BigInt2 = In2

    If Left(BigInt1, 1) = "-" Then Sign(1) = 1: BigInt1 = Mid(BigInt1, 2)
    If Left(BigInt2, 1) = "-" Then Sign(2) = 1: BigInt2 = Mid(BigInt2, 2)

    BiggerNumber = FindBigger(BigInt1, BigInt2)

    If Sign(1) <> Sign(2) Then
        UseSubtraction = True
        If BiggerNumber = 0 Then
            Add = "0": Exit Function
        End If
    End If

    If Sign(BiggerNumber) = 1 Then NegativeResult = True

    If UseSubtraction Then
        If BiggerNumber = 2 Then
            For I = 1 To Len(BigInt2)
                SubRes = Val(Mid(BigInt2, I, 1)) - Val(Mid(BigInt1, I, 1)) - Carry
                If SubRes < 0 Then
                    SubRes = SubRes + 10
                    Carry = 1
                Else
                    Carry = 0
                End If
                O = O + CStr(SubRes)
            Next
        Else
            For I = 1 To Len(BigInt1)
                SubRes = Val(Mid(BigInt1, I, 1)) - Val(Mid(BigInt2, I, 1)) - Carry
                If SubRes < 0 Then
                    SubRes = SubRes + 10
                    Carry = 1
                Else
                    Carry = 0
                End If
                O = O + CStr(SubRes)
            Next
        End If
    Else
        If BiggerNumber = 2 Then
            For I = 1 To Len(BigInt2)
                Carry = Val(Mid(BigInt2, I, 1)) + Val(Mid(BigInt1, I, 1)) + Carry
                O = O + CStr(Carry Mod 10)
                Carry = Carry \ 10
            Next
        Else
            For I = 1 To Len(BigInt1)
                Carry = Val(Mid(BigInt1, I, 1)) + Val(Mid(BigInt2, I, 1)) + Carry
                O = O + CStr(Carry Mod 10)
                Carry = Carry \ 10
            Next
        End If

        If Carry > 0 Then O = O + CStr(Carry)
    End If

    If Not NegativeResult Then
        Add = O
    Else
        Add = "-" + O
    End If
End Function

Public Function FindBigger(BigInt1 As String, BigInt2 As String) As Byte
    Dim I As Integer

    If Len(BigInt1) > Len(BigInt2) Then
        FindBigger = 1
    ElseIf Len(BigInt1) = Len(BigInt2) Then
        For I = 1 To Len(BigInt1)
            If Asc(Mid(BigInt1, I, 1)) > Asc(Mid(BigInt2, I, 1)) Then
                FindBigger = 1
                Exit Function
            ElseIf Asc(Mid(BigInt1, I, 1)) < Asc(Mid(BigInt2, I, 1)) Then
                FindBigger = 2
                Exit Function
            End If
        Next
        FindBigger = 0
    Else
        FindBigger = 2
    End If
End Function

'Chat window

Public Sub DrawMsg(BotID As Integer, Index As Integer)
    Dim L As Long

    Select Case MsgList(BotID, Index).MessageType
        Case ChatType.BLUE:     L = RGB(200, 200, 255)
        Case ChatType.GREEN:    L = RGB(200, 255, 200)
        Case ChatType.RED:      L = RGB(255, 200, 200)
        Case ChatType.BLACK:    L = RGB(255, 255, 255)
        Case ChatType.YELLOW:   L = RGB(255, 255, 200)
    End Select

    SetTextColor MainFrm.hdc, L
    TextOut MainFrm.hdc, 7, ((MainFrm.Height - 400) \ Screen.TwipsPerPixelY) - 20 - (12 * (Index - 1)), MsgList(BotID, Index).Message, Len(MsgList(BotID, Index).Message)
End Sub

Public Sub ResizeMsgs(NormalResize As Boolean)
    If NormalResize = False Or Abs(LastHeight - MainFrm.Height) > 50 Then

        Dim I As Integer, C As Integer

        C = (MainFrm.Height - 400) \ Screen.TwipsPerPixelY \ 12 + 1

        If C > TotalMsgs(Bot_with_focus) Then C = TotalMsgs(Bot_with_focus)

        MainFrm.Cls

        For I = 1 To C
            DrawMsg Bot_with_focus, I
        Next

        MainFrm.Refresh

        LastHeight = MainFrm.Height
    End If
End Sub

Public Sub AddDebug(BotID As Integer, mStr As String, Color As ChatType)
    Dim msg As SSMsg

    msg.Message = mStr
    msg.MessageType = Color

    AddMsg BotID, msg

    If DiskLog = False Then Exit Sub

    If BotID > 0 Then
        AppendBytes "chatter.log", BotList(BotID).MyName + "<" + CStr(BotID) + "> " + mStr
    Else
        AppendBytes "chatter.log", "Shell<0> " + mStr
    End If
End Sub

Private Sub AddMsg(BotID As Integer, Message As SSMsg)
    Dim I As Integer, O As String, C As Integer

    O = Trim(Message.Message)

    If TotalMsgs(BotID) < 100 Then TotalMsgs(BotID) = TotalMsgs(BotID) + 1

    For I = TotalMsgs(BotID) To 2 Step -1
        MsgList(BotID, I) = MsgList(BotID, I - 1)
    Next

    MsgList(BotID, I) = Message

    C = (MainFrm.Height - 400) \ Screen.TwipsPerPixelY \ 12 + 1

    If BotID <> Bot_with_focus Then Exit Sub

    If C > TotalMsgs(Bot_with_focus) Then C = TotalMsgs(Bot_with_focus)

    MainFrm.Cls

    For I = 1 To C
        DrawMsg BotID, I
    Next

    MainFrm.Refresh
End Sub

Public Sub CycleFocus()
    Dim I As Integer

    For I = Bot_with_focus + 1 To MAX_BOTS
        If BotList(I).MyName <> "" Then
            Bot_with_focus = I
            ResizeMsgs False
            UpdateTitlebar I
            Exit Sub
        End If
    Next
    Bot_with_focus = 0
    ResizeMsgs False
    MainFrm.Caption = "MERVBot - Shell View"
End Sub

Public Sub UpdateTitlebar(BotID As Integer)
    If BotID <> Bot_with_focus Then Exit Sub

    With BotList(BotID)
        Select Case .TotalPlayers
            Case 0:    MainFrm.Caption = "MERVBot(" + CStr(BotID) + ") " + .MyName + " [Offline]"
            Case 1:    MainFrm.Caption = "MERVBot(" + CStr(BotID) + ") " + .MyName + " [1 player]"
            Case Else: MainFrm.Caption = "MERVBot(" + CStr(BotID) + ") " + .MyName + " [" + CStr(.TotalPlayers) + " players]"
        End Select
    End With
End Sub

'File I/O

Public Sub PutBytes(Filename As String, Contents As String)
    Dim F As Integer: F = FreeFile

    On Error GoTo ErrHandler

    Open Filename For Output As #F

    Print #F, Contents

ErrHandler: Close #F
End Sub

Public Sub AppendBytes(Filename As String, Contents As String)
    Dim F As Integer: F = FreeFile

    On Error GoTo ErrHandler

    Open Filename For Append As #F

    Print #F, Contents

ErrHandler: Close #F
End Sub
