// AmiVNC - Amiga experimental VNC server - Protocol ORL version 3.3
char XDC_ID[] = "AmiVNC Vexp0.0.9 11/05/1999";

// Includes ***********************************************
// Needed to compile : SDK Cybergraphx 4.1 & SDK AmiTCP 4.3
#include <exec/types.h>
#include <exec/memory.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <libraries/commodities.h>
#include <proto/all.h>
#include <proto/socket.h>

#include <cybergraphx/cybergraphics.h>
#include <proto/cybergraphics.h>

#include <dos/dostags.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netdb.h>

#include <intuition/intuitionbase.h>
#include <intuition/intuition.h>
#include <graphics/displayinfo.h>
#include <graphics/gfxbase.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <fcntl.h>
#include <ctype.h>

// Specific Inculdes ***********************************************
#include "rfbproto.h"                   // Structures & constants for VNC protocol
#include "vncauth.h"                    // Prototypes for authentication

// Constants ***********************************************
#define XDC_PORT 5900                   // Port # for bind()
#define FALSE 0
#define TRUE 1
#define XDC_TILE 32                     // Tile size

#define XDC_C_VBUF      (1L << 0)
#define XDC_C_MSOCK     (1L << 1)
#define XDC_C_CSOCK     (1L << 2)

#define XDC_C_MAXDEPTH  4               // Max. raster depth : 4 bytes / pixel

#undef PARANO

// Global variables ***********************************************
LONG    iDuplicateSocketKey;            // Client socket key for transmitting to child process
ULONG   uOpened = NULL;                 // Opened resources, to be closed on exit
BOOL    bDie = FALSE, *pDie = &bDie;    // TRUE : processes are to exit (Argh, very trashy)
char    cPassword[MAXPWLEN + 1];
LONG    iMasterSocket,                  // Listening socket for incoming connections
        iClientSocket;                  // Client socket accept()ed
UBYTE   *pBuffer = NULL;                // Reference buffer for screen change detection
 
// Free all resources allocated to main process and exit ***********************************************
void vCleanExit(char *cMsg)
{
    // Tell the child process to exit (as we are the main process, we can ue bDie, which is in our address space)
    if (!bDie)
    {
        bDie = TRUE;
        Delay(250);
    }
    
    // Close everything opened
    if (uOpened & XDC_C_VBUF) free(pBuffer);
    if (uOpened & XDC_C_MSOCK) CloseSocket(iMasterSocket);
    if (uOpened & XDC_C_CSOCK) CloseSocket(iClientSocket);
    
    printf("AmiVNC - %s\nAmiVNC - exiting\n", cMsg);
    
    exit(0L);
}

// Authentication ***********************************************
BOOL bAuthentify(void)
{
        char cChallenge[16], cResponse[16];
        CARD32 c32Value;
        BOOL bAuthenticated = TRUE;
        int iCnt, iFile;

        // Read encoded password in s:AmiVNC.pwd
        if (!(iFile = open("s:AmiVNC.pwd", O_RDONLY, 0)))
           return(FALSE);
                
        iCnt = read(iFile, cPassword, sizeof(cPassword));
        
        close(iFile);
        
        if (iCnt != sizeof(cPassword))
           return(FALSE);

        // Decode password
        vncDecryptPasswd(cPassword, cPassword);
        
        // Authenticate the connection, if required
        if (strlen(cPassword) == 0)
        {
                // Send no-auth-required message
                c32Value = rfbNoAuth;
                if (!(-1 == (send(iClientSocket,(char *)&c32Value, sizeof(c32Value), 0))))
                        return FALSE;

                return(TRUE);
        }
        else
        {
                // Send auth-required message

                c32Value = (rfbVncAuth);
                if (-1 == (send(iClientSocket,(char *)&c32Value, sizeof(c32Value), 0)))
                        return FALSE;

                // Now create a 16-byte challenge
                vncRandomBytes((BYTE *)cChallenge);

                // Send the challenge to the client
                if (-1 == (send(iClientSocket, cChallenge, sizeof(cChallenge), 0)))
                        return FALSE;

                // Read the response
                if (-1 == (recv(iClientSocket, cResponse, sizeof(cResponse), 0)))
                        return FALSE;

                // Encrypt the challenge bytes
                vncEncryptBytes((BYTE *)cChallenge, cPassword);

                // Compare them to the response
                for (iCnt = 0; iCnt < sizeof(cChallenge); iCnt++)
                {
                        if (cChallenge[iCnt] != cResponse[iCnt])
                        {
                                bAuthenticated = FALSE;
                                break;
                        }
                }

                // Did the authentication work?
                if (!bAuthenticated)
                {
                        c32Value = rfbVncAuthFailed;
                        send(iClientSocket, (char *)&c32Value, sizeof(c32Value), 0);
                        return FALSE;

                }
                else
                {
                        // Tell the client we're ok
                        c32Value = rfbVncAuthOK;
                        if (-1 == (send(iClientSocket, (char *)&c32Value, sizeof(c32Value), 0)))
                                return FALSE;
                
                        return(TRUE);
                }
        }
}

// Incoming messages management thread ***********************************************
void __saveds vProcessIncomes(void)
{
    struct IOStdReq *inputReqBlk;
    struct MsgPort *inputPort;
    struct InputEvent eEvent, *VNCEvent = &eEvent;
   
    rfbClientToServerMsg sCMsg;
    BOOL bLClick = FALSE, bMClick = FALSE, bRClick = FALSE;
    struct Library *SocketBase;
    LONG iSocketChild;

    // We have to reopen bsdsocket.library, because it can not be shared between processes
    SocketBase = OpenLibrary("bsdsocket.library", 4L);

    // Obtain the client socket descriptor
    if (-1 == (iSocketChild = ObtainSocket(iDuplicateSocketKey, AF_INET, SOCK_STREAM, 0)))
    {
        printf("main.c / vProcessIncomes : ObtainSocket() error %d\n", Errno());
        *pDie = TRUE;
        goto _Nosock;
    }
    
    printf("main.c / vProcessIncomes runs on socket %d\n", iSocketChild);

    // Create input.device message structure
    inputPort = CreatePort(NULL, NULL);
    inputReqBlk = (struct IOStdReq *) CreateExtIO(inputPort, sizeof(struct IOStdReq));
    OpenDevice("input.device", NULL, (struct IORequest *) inputReqBlk, NULL);
    inputReqBlk -> io_Data = (APTR) VNCEvent;
    inputReqBlk -> io_Command = IND_WRITEEVENT;
    inputReqBlk -> io_Flags = 0;
    inputReqBlk -> io_Length = sizeof(struct InputEvent);
    
    // Process incoming messages until main process tells to die
    while (!*pDie) // VERY VERY dirty way to know we have to exit...
    {
       if (-1 == recv(iSocketChild, (UBYTE *) &sCMsg, 1, 0))
       {
           printf("main.c / vProcessIncomes : recv() error, errno = %d\n", Errno());
           *pDie  = TRUE;
           continue;
       }
       
       Forbid();
       switch(sCMsg.type)
       {
           case rfbSetPixelFormat : // 0
           if (sz_rfbSetPixelFormatMsg - 1 == recv(iSocketChild, 1 + (UBYTE *) & sCMsg, sz_rfbSetPixelFormatMsg - 1, 0))
           {
#ifdef PARANO
               printf("C -> S : rfbSetPixelFormat lanbpp %d depth %d bigE %d tc %d redM %d redS %d GreM %d GreS %d BluM %d BluS %d\n",
                  sCMsg.spf.format.bitsPerPixel,
                  sCMsg.spf.format.depth,
                  sCMsg.spf.format.bigEndian,
                  sCMsg.spf.format.trueColour,
                  sCMsg.spf.format.redMax,
                  sCMsg.spf.format.redShift,
                  sCMsg.spf.format.greenMax,
                  sCMsg.spf.format.greenShift,
                  sCMsg.spf.format.blueMax,
                  sCMsg.spf.format.blueShift
                  );
#endif // PARANO
               break;
           }
           
           case rfbFixColourMapEntries : // 1
           if (sz_rfbFixColourMapEntriesMsg - 1 == recv(iSocketChild, 1 + (UBYTE *) & sCMsg, sz_rfbFixColourMapEntriesMsg - 1, 0))
           {
#ifdef PARANO
               printf("C -> S : rfbFixColourMapEntries\n");
#endif // PARANO
               break;
           }
           
           case rfbSetEncodings : // 2
           if (sz_rfbSetEncodingsMsg - 1 == recv(iSocketChild, 1 + (UBYTE *) & sCMsg, sz_rfbSetEncodingsMsg - 1, 0))
           {
#ifdef PARANO
               printf("C -> S : rfbSetEncodings\n");
#endif // PARANO
               break;
           }
           
           case rfbFramebufferUpdateRequest : // 3
           if (sz_rfbFramebufferUpdateRequestMsg - 1 == recv(iSocketChild, 1 + (UBYTE *) & sCMsg, sz_rfbFramebufferUpdateRequestMsg - 1, 0))
           {
#ifdef PARANO            
               printf("C -> S : rfbFramebufferUpdateRequest inc %d x %d y %d w %d h %d\n",
                  sCMsg.fur.incremental,
                  sCMsg.fur.x,
                  sCMsg.fur.y,
                  sCMsg.fur.w,
                  sCMsg.fur.h
                  );
#endif // PARANO            
               break;
           }
           
           case rfbKeyEvent : // 4
           if (sz_rfbKeyEventMsg - 1 == recv(iSocketChild, 1 + (UBYTE *) & sCMsg, sz_rfbKeyEventMsg - 1, 0))
           {
#ifdef PARANO
               printf("C -> S : rfbKeyEvent down %d key %d ('%c')\n",
                  sCMsg.ke.down,
                  sCMsg.ke.key,
                  sCMsg.ke.key
                 );
#endif // PARANO
               
               // Ignore special keys already taken into account in sCMsg.ke.key
               if (sCMsg.ke.key == 0xFFE1) break; // Left Shift
               if (sCMsg.ke.key == 0xFFE2) break; // Right Shift
               if (sCMsg.ke.key == 0xFFE3) break; // Left Control
               if (sCMsg.ke.key == 0xFFE4) break; // Right Control
               
               if ((sCMsg.ke.down) && (InvertKeyMap((ULONG) sCMsg.ke.key, VNCEvent, NULL)))
               DoIO((struct IORequest *) inputReqBlk);

               break;
           }
           
           case rfbPointerEvent : // 5
           if (sz_rfbPointerEventMsg - 1 == recv(iSocketChild, 1 + (UBYTE *) & sCMsg, sz_rfbPointerEventMsg - 1, 0))
           {
#ifdef PARANO
               printf("C -> S : rfbPointerEvent button %d x %d y %d\r",
                  sCMsg.pe.buttonMask,
                  sCMsg.pe.x,
                  sCMsg.pe.y
                 );
#endif // PARANO
               VNCEvent -> ie_NextEvent = NULL;
               VNCEvent -> ie_Class = IECLASS_POINTERPOS;
               VNCEvent -> ie_Qualifier = 0;
               
               // Process mouse X & Y
               VNCEvent -> ie_X = sCMsg.pe.x;
               VNCEvent -> ie_Y = sCMsg.pe.y;

               // Process mouse buttons...
               VNCEvent -> ie_Code = IECODE_NOBUTTON;
               
               // Left button
               if ((sCMsg.pe.buttonMask) & 1)
               switch(bLClick)
               {
                   case FALSE : bLClick = TRUE; VNCEvent -> ie_Code = IECODE_LBUTTON; break;
                   case TRUE : VNCEvent -> ie_Code = IECODE_NOBUTTON; break;
               }
               else if (bLClick) { bLClick = FALSE; VNCEvent -> ie_Code = IECODE_LBUTTON | IECODE_UP_PREFIX; }

               // Middle button
               if ((sCMsg.pe.buttonMask) & 2)
               switch(bMClick)
               {
                   case FALSE : bMClick = TRUE; VNCEvent -> ie_Code = IECODE_MBUTTON; break;
                   case TRUE : VNCEvent -> ie_Code = IECODE_NOBUTTON; break;
               }
               else if (bMClick) { bMClick = FALSE; VNCEvent -> ie_Code = IECODE_MBUTTON | IECODE_UP_PREFIX; }

               // Right button
               if ((sCMsg.pe.buttonMask) & 4)
               switch(bRClick)
               {
                   case FALSE : bRClick = TRUE; VNCEvent -> ie_Code = IECODE_RBUTTON; break;
                   case TRUE : VNCEvent -> ie_Code = IECODE_NOBUTTON; break;
               }
               else if (bRClick) { bRClick = FALSE; VNCEvent -> ie_Code = IECODE_RBUTTON | IECODE_UP_PREFIX; }

               // Insert event into input.device stream
               DoIO((struct IORequest *) inputReqBlk);
               break;
           }
           
           case rfbClientCutText : // 6
           if (sz_rfbClientCutTextMsg - 1 == recv(iSocketChild, 1 + (UBYTE *) & sCMsg, sz_rfbClientCutTextMsg - 1, 0))
           {
               char *cText = malloc((size_t) (sCMsg.cct.length + 1));
               recv(iSocketChild, cText, sCMsg.cct.length, 0);
               cText[sCMsg.cct.length] = 0;
#ifdef PARANO
               printf("C -> S : rfbClientCutText '%s'\n", cText);
#endif // PARANO
               break;
           }
           
           default :
#ifdef PARANO
           printf("main.c / vProcessIncomes : msg type %d unknown\n", sCMsg.type);
#endif // PARANO
           break;
       }
       Permit();
    }
    
    CloseSocket(iSocketChild);

    if (inputReqBlk)
    {
        CloseDevice((struct IORequest *) inputReqBlk);
        DeleteExtIO((struct IORequest *) inputReqBlk);
    }
    
    if (inputPort) DeletePort(inputPort);

_Nosock:
    CloseLibrary(SocketBase);
    printf("main.c / vProcessIncomes : Stopped\n");   
}

// Main entry point  ***********************************************
int main(int argc, char **argv)
{
    register int iCnt, jCnt;
    int iMajor, iMinor, iWidth, iHeight, iDepth, iRetcode = 0, iSize,iMode, iPort;

    struct  sockaddr_in sAddr;                  // Local address for bind()

    struct Screen sScreen;
   
    rfbProtocolVersionMsg mProtVerMsg;
    rfbServerInitMsg mSerInitMsg;
    rfbFramebufferUpdateMsg mFBUpdMsg;
    rfbFramebufferUpdateRectHeader mFBRMsg;

    CARD8 c8;

    UBYTE *pScreen;                             // True WB screen raster
    static UBYTE uTile[XDC_TILE * XDC_TILE * XDC_C_MAXDEPTH];    // Raster tile for client screen updates
    
    // Welcome...
    printf("%s\nStéphane Guillard - stephane.guillard@steria.fr\n", XDC_ID);
    
    iPort = XDC_PORT;
    
    // Process command line arguments (if any)
    if (argc > 1) while (*++argv)
    {
        char *pC;
        
        switch(tolower((*argv)[1]))
        {
            case 'p' :
                strncpy(cPassword, *argv + 2, MAXPWLEN);
                cPassword[MAXPWLEN] = '\0';
                
                pC = strchr(cPassword, ' ');
                if (pC) *pC = '\0';
                
                for (iCnt = strlen(cPassword) ; iCnt < MAXPWLEN ; iCnt++)
                   cPassword[iCnt] = '\0';
                
                vncEncryptPasswd(cPassword, cPassword);
                
                if (-1 == (iCnt = creat("S:AmiVNC.pwd", 0)))
                    vCleanExit("main.c / main : creat('s:AmiVNC.pwd') error");
                write(iCnt, cPassword, sizeof(cPassword));
                close(iCnt);
                
                vCleanExit("main.c / main : Passwd stored");
                break;
            
            case 's' :
                iPort = atoi(*argv + 2);
                break;
            
            default :
                printf("main.c / main : Arg [%s] ignored\n", *argv);
                break;
        }
    }
    
    // Find WB screen info   
    GetScreenData(&sScreen, sizeof(struct Screen), WBENCHSCREEN, NULL);

    // Prepare VNC pixel encoding
    if (GetCyberMapAttr(sScreen.RastPort.BitMap, CYBRMATTR_ISCYBERGFX)
     && GetCyberMapAttr(sScreen.RastPort.BitMap, CYBRMATTR_ISLINEARMEM))
    {
        iWidth = GetCyberMapAttr(sScreen.RastPort.BitMap, CYBRMATTR_WIDTH); // X size
        iHeight = GetCyberMapAttr(sScreen.RastPort.BitMap, CYBRMATTR_HEIGHT); // Y size
        iDepth = GetCyberMapAttr(sScreen.RastPort.BitMap, CYBRMATTR_BPPIX); // Bytes / pixel
        iSize = iWidth * iHeight * iDepth; // Actual byte size of screen raster 
        iMode = GetCyberMapAttr(sScreen.RastPort.BitMap, CYBRMATTR_PIXFMT);
        printf("main.c / main : RTG mode %ld\nmain.c / main : ", iMode);
        
        switch(iMode)
        {
            case PIXFMT_LUT8:            /*CLUT : 8 pit pixels + palette */
                printf("CLUT (8 bit pixels + palette)\n");
                mSerInitMsg.format.bitsPerPixel = 8;
                mSerInitMsg.format.depth = 8;
                mSerInitMsg.format.bigEndian = FALSE;
                mSerInitMsg.format.trueColour = FALSE;
                break;
            case PIXFMT_RGB24 :          /* TrueColor RGB (8 bit each) */
                printf("TrueColor RGB24\n");
                mSerInitMsg.format.bitsPerPixel = 32;
                mSerInitMsg.format.depth = 24;
                mSerInitMsg.format.bigEndian = FALSE;
                mSerInitMsg.format.trueColour = TRUE;
                mSerInitMsg.format.redMax = 255;
                mSerInitMsg.format.greenMax = 255;
                mSerInitMsg.format.blueMax = 255;
                mSerInitMsg.format.redShift = 16;
                mSerInitMsg.format.greenShift = 8;
                mSerInitMsg.format.blueShift = 0;
                break;
            case PIXFMT_BGR24 : // TESTED OK /* TrueColor BGR (8 bit each) */
                printf("TrueColor BGR24\n");
                mSerInitMsg.format.bitsPerPixel = 32;
                mSerInitMsg.format.depth = 24;
                mSerInitMsg.format.bigEndian = FALSE;
                mSerInitMsg.format.trueColour = TRUE;
                mSerInitMsg.format.redMax = 255;
                mSerInitMsg.format.greenMax = 255;
                mSerInitMsg.format.blueMax = 255;
                mSerInitMsg.format.redShift = 0;
                mSerInitMsg.format.greenShift = 8;
                mSerInitMsg.format.blueShift = 16;
                break;
            case PIXFMT_RGB16PC : // TESTED OK /* HiColor16 (5 bit R, 6 bit G, 5 bit B), format: gggbbbbbrrrrrggg */
                printf("HiColor RGB16PC (5 bit R, 6 bit G, 5 bit B), format: gggbbbbbrrrrrggg\n");
                mSerInitMsg.format.bitsPerPixel = 16;
                mSerInitMsg.format.depth = 16;
                mSerInitMsg.format.bigEndian = TRUE;
                mSerInitMsg.format.trueColour = TRUE;
                mSerInitMsg.format.redMax = 31;
                mSerInitMsg.format.greenMax = 63;
                mSerInitMsg.format.blueMax = 31;
                mSerInitMsg.format.redShift = 11;
                mSerInitMsg.format.greenShift = 5;
                mSerInitMsg.format.blueShift = 0;
                break;
            case PIXFMT_RGB15PC :        /* HiColor15 (5 bit each), format: gggbbbbb0rrrrrgg */
                printf("HiColor RGB15PC (5 bit each), format: gggbbbbb0rrrrrgg\n");
                mSerInitMsg.format.bitsPerPixel = 16;
                mSerInitMsg.format.depth = 15;
                mSerInitMsg.format.bigEndian = TRUE;
                mSerInitMsg.format.trueColour = TRUE;
                mSerInitMsg.format.redMax = 31;
                mSerInitMsg.format.greenMax = 31;
                mSerInitMsg.format.blueMax = 31;
                mSerInitMsg.format.redShift = 10;
                mSerInitMsg.format.greenShift = 5;
                mSerInitMsg.format.blueShift = 0;
                break;
            case PIXFMT_ARGB32 :        /* 4 Byte TrueColor ARGB (A unused alpha channel) */
                printf("TrueColor ARGB32\n");
                mSerInitMsg.format.bitsPerPixel = 32;
                mSerInitMsg.format.depth = 24;
                mSerInitMsg.format.bigEndian = FALSE;
                mSerInitMsg.format.trueColour = TRUE;
                mSerInitMsg.format.redMax = 255;
                mSerInitMsg.format.greenMax = 255;
                mSerInitMsg.format.blueMax = 255;
                mSerInitMsg.format.redShift = 16;
                mSerInitMsg.format.greenShift = 8;
                mSerInitMsg.format.blueShift = 0;
                break;
            case PIXFMT_RGBA32 :        /* 4 Byte TrueColor RGBA (A unused alpha channel) */
                printf("TrueColor RGBA32\n");
                mSerInitMsg.format.bitsPerPixel = 32;
                mSerInitMsg.format.depth = 24;
                mSerInitMsg.format.bigEndian = FALSE;
                mSerInitMsg.format.trueColour = TRUE;
                mSerInitMsg.format.redMax = 255;
                mSerInitMsg.format.greenMax = 255;
                mSerInitMsg.format.blueMax = 255;
                mSerInitMsg.format.redShift = 24;
                mSerInitMsg.format.greenShift = 16;
                mSerInitMsg.format.blueShift = 8;
                break;
            case PIXFMT_BGRA32 :        /* 4 Byte TrueColor BGRA (A unused alpha channel) */
                printf("TrueColor BGRA32\n");
                mSerInitMsg.format.bitsPerPixel = 32;
                mSerInitMsg.format.depth = 24;
                mSerInitMsg.format.bigEndian = FALSE;
                mSerInitMsg.format.trueColour = TRUE;
                mSerInitMsg.format.redMax = 255;
                mSerInitMsg.format.greenMax = 255;
                mSerInitMsg.format.blueMax = 255;
                mSerInitMsg.format.redShift = 8;
                mSerInitMsg.format.greenShift = 16;
                mSerInitMsg.format.blueShift = 24;
                break;
            case PIXFMT_RGB16 :          /* HiColor16 (5 bit R, 6 bit G, 5 bit B), format: rrrrrggggggbbbbb */
                printf("HiColor RGB16 (5 bit R, 6 bit G, 5 bit B), format: rrrrrggggggbbbbb\n");
                mSerInitMsg.format.bitsPerPixel = 16;
                mSerInitMsg.format.depth = 16;
                mSerInitMsg.format.bigEndian = FALSE;
                mSerInitMsg.format.trueColour = TRUE;
                mSerInitMsg.format.redMax = 31;
                mSerInitMsg.format.greenMax = 63;
                mSerInitMsg.format.blueMax = 31;
                mSerInitMsg.format.redShift = 11;
                mSerInitMsg.format.greenShift = 5;
                mSerInitMsg.format.blueShift = 0;
                break;
            case PIXFMT_RGB15 :          /* HiColor15 (5 bit each), format: 0rrrrrgggggbbbbb */
                printf("HiColor RGB15 (5 bit each), format: 0rrrrrgggggbbbbb\n");
                mSerInitMsg.format.bitsPerPixel = 16;
                mSerInitMsg.format.depth = 15;
                mSerInitMsg.format.bigEndian = FALSE;
                mSerInitMsg.format.trueColour = TRUE;
                mSerInitMsg.format.redMax = 31;
                mSerInitMsg.format.greenMax = 31;
                mSerInitMsg.format.blueMax = 31;
                mSerInitMsg.format.redShift = 10;
                mSerInitMsg.format.greenShift = 5;
                mSerInitMsg.format.blueShift = 0;
                break;
            case PIXFMT_BGR16PC :        /* HiColor16 (5 bit R, 6 bit G, 5 bit B), format: gggrrrrrbbbbbggg */
                printf("HiColor BGR16PC (5 bit R, 6 bit G, 5 bit B), format: gggrrrrrbbbbbggg\n");
                mSerInitMsg.format.bitsPerPixel = 16;
                mSerInitMsg.format.depth = 16;
                mSerInitMsg.format.bigEndian = TRUE;
                mSerInitMsg.format.trueColour = TRUE;
                mSerInitMsg.format.redMax = 31;
                mSerInitMsg.format.greenMax = 63;
                mSerInitMsg.format.blueMax = 31;
                mSerInitMsg.format.redShift = 0;
                mSerInitMsg.format.greenShift = 5;
                mSerInitMsg.format.blueShift = 11;
                break;
            case PIXFMT_BGR15PC :        /* HiColor15 (5 bit each), format: gggrrrrr0bbbbbbgg */
                printf("HiColor BGR15PC (5 bit each), format: gggrrrrr0bbbbbbgg\n");
                mSerInitMsg.format.bitsPerPixel = 16;
                mSerInitMsg.format.depth = 15;
                mSerInitMsg.format.bigEndian = TRUE;
                mSerInitMsg.format.trueColour = TRUE;
                mSerInitMsg.format.redMax = 31;
                mSerInitMsg.format.greenMax = 31;
                mSerInitMsg.format.blueMax = 31;
                mSerInitMsg.format.redShift = 0;
                mSerInitMsg.format.greenShift = 5;
                mSerInitMsg.format.blueShift = 10;
                break;
            case PIXFMT_BGR16 :        /* HiColor16 (5 bit R, 6 bit G, 5 bit B), format: bbbbbggggggrrrrr */
                printf("HiColor BGR16 (5 bit R, 6 bit G, 5 bit B), format: gggrrrrrbbbbbggg\n");
                mSerInitMsg.format.bitsPerPixel = 16;
                mSerInitMsg.format.depth = 16;
                mSerInitMsg.format.bigEndian = FALSE;
                mSerInitMsg.format.trueColour = TRUE;
                mSerInitMsg.format.redMax = 31;
                mSerInitMsg.format.greenMax = 63;
                mSerInitMsg.format.blueMax = 31;
                mSerInitMsg.format.redShift = 0;
                mSerInitMsg.format.greenShift = 5;
                mSerInitMsg.format.blueShift = 11;
                break;
            case PIXFMT_BGR15 :        /* HiColor15 (5 bit each), format: 0bbbbbbgggggrrrrr */
                printf("HiColor BGR15 (5 bit each), format: gggrrrrr0bbbbbbgg\n");
                mSerInitMsg.format.bitsPerPixel = 16;
                mSerInitMsg.format.depth = 15;
                mSerInitMsg.format.bigEndian = FALSE;
                mSerInitMsg.format.trueColour = TRUE;
                mSerInitMsg.format.redMax = 31;
                mSerInitMsg.format.greenMax = 31;
                mSerInitMsg.format.blueMax = 31;
                mSerInitMsg.format.redShift = 0;
                mSerInitMsg.format.greenShift = 5;
                mSerInitMsg.format.blueShift = 10;
                break;
            default :
                vCleanExit("main.c / main : RGB mode not supported");
        }
    }
    else
        vCleanExit("main.c / main : Screenmode not supported");

    // Get a pointer on the WB raster
    pScreen = sScreen.RastPort.BitMap -> Planes[0];

    printf("main.c / main : @ 0x%08lX, %dx%d, %d Bpp, size %d\n", pScreen, iWidth, iHeight, iDepth, iSize);    
    
    // Check compatibility of screen with tile size, &nd screen deepness (we only accept 1, 2, 4 bytes / pixel)
    if ((iWidth % XDC_TILE) || (iHeight % XDC_TILE))
        vCleanExit("main.c / main : x/y not supported");

    // Prepare listener socket
    printf("main.c / main : Creating listener socket\n");
    if (-1 == (iMasterSocket = socket(AF_INET, SOCK_STREAM, 0)))
        vCleanExit("main.c / main : socket() error");
    
    uOpened |= XDC_C_MSOCK;
    
    sAddr.sin_family=AF_INET;
    sAddr.sin_addr.s_addr = 0;
    sAddr.sin_port = htons(iPort);
    
    printf("main.c / main : Binding socket on port %d\n", sAddr.sin_port);
    if (-1 == (bind(iMasterSocket, (struct sockaddr *) &sAddr, sizeof(sAddr))))
        vCleanExit("main.c / main : bind() error");
    
    printf("main.c / main : Listening on port %d\n", sAddr.sin_port);
    if (-1 == (listen(iMasterSocket, 4)))
        vCleanExit("main.c / main : listen() error"); 


    // Wait for incoming connections
    if (-1 == (iClientSocket = accept(iMasterSocket, NULL, NULL)))
        vCleanExit("main.c / main : accept() error");
    
    uOpened |= XDC_C_CSOCK;
    
    printf("main.c / main : accept()ed socket %d\n", iClientSocket);

    // Allocate pBuffer (reference buffer for screen compare)
    if (!(pBuffer = malloc(iWidth * iHeight * ((iDepth != 3) ? iDepth : 4))))
       vCleanExit("main.c / main : malloc() error");
   
    uOpened |= XDC_C_VBUF;
    
    // NegociateProtocolVersion
    printf("main.c / main : Negociating protocol version\n");
    sprintf((char *) mProtVerMsg, rfbProtocolVersionFormat,
    rfbProtocolMajorVersion,
    rfbProtocolMinorVersion);
    if (-1 == (send(iClientSocket, (UBYTE *) mProtVerMsg, sz_rfbProtocolVersionMsg, 0)))
        vCleanExit("main.c / main : send() error");
    
    mProtVerMsg[12] = 0;

    if (-1 == (recv(iClientSocket, mProtVerMsg, sz_rfbProtocolVersionMsg, 0)))
        vCleanExit("main.c / main : recv() error");

    sscanf((char *) mProtVerMsg, rfbProtocolVersionFormat, &iMajor, &iMinor);
    printf("main.c / main : Protocol supported by server : %d.%d, by client : %d.%d\n", rfbProtocolMajorVersion, rfbProtocolMinorVersion, iMajor, iMinor);
    
    if (iMajor > rfbProtocolMajorVersion)
        vCleanExit("main.c / main : unsupported protocol");
    
    // Authenticate DES
    printf("main.c / main : Authentication\n");
    if (FALSE == bAuthentify())
        vCleanExit("main.c / main : Authenticate error");
    
    // ClientInit
    printf("main.c / main : ClientInitialisation\n");
    recv(iClientSocket, &c8, sizeof(c8), 0);
    printf("main.c / main : SharedFlag = %d (multiple clients : %s)\n", c8, c8 ? "Yes" : "No");

    // ServerInit
    mSerInitMsg.framebufferWidth = iWidth;
    mSerInitMsg.framebufferHeight = iHeight;
    mSerInitMsg.nameLength = sizeof(XDC_ID);
 
    send(iClientSocket, (UBYTE *) &mSerInitMsg, sizeof(mSerInitMsg), 0);
    send(iClientSocket, (UBYTE *) XDC_ID, sizeof(XDC_ID), 0);

    // Initial Screen update : header...
    mFBUpdMsg.type = rfbFramebufferUpdate;
    mFBUpdMsg.nRects = 1;
    send(iClientSocket, (UBYTE *) &mFBUpdMsg, sizeof(mFBUpdMsg), 0);
    
    // Initial Screen update : 1 rectangle for whole screen...
    mFBRMsg.r.x = 0;
    mFBRMsg.r.y = 0;
    mFBRMsg.r.w = iWidth;
    mFBRMsg.r.h = iHeight;
    mFBRMsg.encoding = rfbEncodingRaw;
    send(iClientSocket, (UBYTE *) &mFBRMsg, sizeof(mFBRMsg), 0);
    memcpy(pBuffer, pScreen, iSize);
    
    switch(iDepth)
    {
        case 3 : // If Depth == 3 (24 bit pixels), we have to pad each pixel with a null byte to make it a 32 bit pixel
            for (jCnt = 0; jCnt < iHeight ; jCnt++)
                for (iCnt = 0 ; iCnt < iWidth ; iCnt++)
                {
                    register iIndex = iCnt + jCnt * iWidth;
                    pBuffer[iIndex * 4 + 0] = pScreen[iIndex * 3 + 2];
                    pBuffer[iIndex * 4 + 1] = pScreen[iIndex * 3 + 1];
                    pBuffer[iIndex * 4 + 2] = pScreen[iIndex * 3 + 0];
                }
            send(iClientSocket, (UBYTE *) pBuffer, (LONG) (iWidth * iHeight * 4), 0);

            // Then get a raw copy of master raster to compare and detect changes
            // (from now on we only send tiles to the client, using another buffer for send())
            memcpy(pBuffer, pScreen, iSize);
            break;
            
        default : // Other cases : simply copy screen raster into reference buffer and send it
            memcpy(pBuffer, pScreen, iSize);
            send(iClientSocket, (UBYTE *) pBuffer, (LONG) iSize, 0);
            break;
    }
        
    // Preset framebuffer update message X and Y size to XDC_TILE
    mFBRMsg.r.w = 
    mFBRMsg.r.h = XDC_TILE;

    // Release a copy of client socket handle so that child process can catch it
    if (-1 == (iDuplicateSocketKey = ReleaseCopyOfSocket(iClientSocket, UNIQUE_ID)))
       vCleanExit("main.c / main : ReleaseCopyOfSocket() error");
    
    // Create incoming messages handling child process
    if (!CreateNewProcTags(
          NP_Entry, vProcessIncomes,
          NP_StackSize, 32000,
          NP_Name, "AmiVNC handler"
       ))
       vCleanExit("main.c / main : CreateNewProcTags() error");
        
    // While no error...
    while ((iRetcode != -1) && (!bDie))
    {    
    // Search for changes in WB screen...
    for (jCnt = 0 ; jCnt < iHeight; jCnt += XDC_TILE)
    {
        for (iCnt = 0 ; iCnt < iWidth ; iCnt += XDC_TILE)
        {
            register int iYtile, iXtile;
            BOOL bChange = FALSE;
    
            for (iYtile = 0 ; iYtile < XDC_TILE ; iYtile ++)
            {
                if (memcmp((UBYTE *) (pBuffer) + iDepth * (iCnt + (jCnt + iYtile) * iWidth),
                   (UBYTE *) (pScreen) + iDepth * (iCnt + (jCnt + iYtile) * iWidth),
                   XDC_TILE * iDepth))
                {
                    memcpy((UBYTE *) (pBuffer) + iDepth * (iCnt + (jCnt + iYtile) * iWidth),
                       (UBYTE *) (pScreen) + iDepth * (iCnt + (jCnt + iYtile) * iWidth),
                       XDC_TILE * iDepth);
                    bChange = TRUE;
                }
            }
        
        // If change found, send the tile
        if (bChange)
        {
            if (-1 == (iRetcode = send(iClientSocket, (UBYTE *) &mFBUpdMsg, sizeof(mFBUpdMsg), 0)))
            goto __out;
            
            switch(iDepth)
            {
                case 3 : // 24 bit pixels : 3 byte -> 4 byte copy for each pixel, thus much slower than direct copy for 1, 2 or 4 byte pixels
                    for (iYtile = 0 ; iYtile < XDC_TILE ; iYtile ++)
                        for (iXtile = 0 ; iXtile < XDC_TILE ; iXtile ++)
                        {
                            uTile[(iXtile + XDC_TILE * iYtile) * 4 + 0] = pScreen[(iXtile + iCnt + (jCnt + iYtile) * iWidth) * 3 + 2];
                            uTile[(iXtile + XDC_TILE * iYtile) * 4 + 1] = pScreen[(iXtile + iCnt + (jCnt + iYtile) * iWidth) * 3 + 1];
                            uTile[(iXtile + XDC_TILE * iYtile) * 4 + 2] = pScreen[(iXtile + iCnt + (jCnt + iYtile) * iWidth) * 3 + 0];
                            // uTile[(iXtile + XDC_TILE * iYtile) * 4 + 3] is void : we send it to the client, but he does not use it
                        }
                    break;

                default : // 8, 15, 16, 32 bit pixels : simply copy from source raster into tile for each tile line
                    for (iYtile = 0 ; iYtile < XDC_TILE ; iYtile ++)
                    {
                       memcpy(uTile + XDC_TILE * iYtile * iDepth,
                              pScreen + iDepth * (iCnt + (jCnt + iYtile) * iWidth),
                              XDC_TILE * iDepth);
                    }
                    break;
            }
            
            mFBRMsg.r.x = iCnt;
            mFBRMsg.r.y = jCnt;
       
            // Send framebuffer update header
            if (-1 == (iRetcode = send(iClientSocket, (UBYTE *) &mFBRMsg, sizeof(mFBRMsg), 0)))
                goto __out;

            // Send framebuffer update pixel data
            if (-1 == (iRetcode = send(iClientSocket, (UBYTE *) uTile, (LONG) XDC_TILE * XDC_TILE * ((iDepth != 3) ? iDepth: 4), 0)))
                goto __out;

        }
        }
    }
    }

__out:

    vCleanExit("main.c / main : Stopped");
}
