/*     GUI code for the ASFRecorder application     */
/* Uses ActiveMovie (DirectShow) for stream preview */

#include "global.h"

#include "CmdLine.h"


#ifdef  __MINGW32__

/* we need to rename the main() function for MINGW32 */
/* because otherwise WinMain() will never be called */
#define main(argc,argv) renamed_main(argc,argv)

#endif /* __MINGW32__ */


#define UNTITLED_STRING " - Untitled"

#define nMaxResourceStrSize 128


/*--------------------------------- GLOBALS ---------------------------------*/

/* some application variables */
AppVars appVars;

/* used for inter-process communication */
UINT WM_APPMSG;
#define MAGIC_COOKIE 0x1234

BOOL Idle = TRUE;

/* window and control handles */
HWND hwndAboutBox = NULL;
HWND hwndStatusbar = NULL;
HWND hwndOpenURLDialog = NULL;
// The window for each toolbar button
struct{
    HWND hwndPlayButton;
    HWND hwndPauseButton;
    HWND hwndStopButton;
    HWND hwndPreviewButton;
    HWND hwndCancelButton;
} toolbar = {NULL, NULL, NULL, NULL, NULL};
HWND hwndTrackbar = NULL;

/* timer for flashing status bar */
#define ID_STATUSTIMER   1

UINT StatusTimer = 0;
UINT StatusTimerCount;
UINT StatusTimerCref;

/* timer for moving trackbar */
#define ID_TRACKBARTIMER 2

UINT TrackbarTimer = 0;

/* some more trackbar stuff */
int positionTrackbar;
BOOL TrackbarDragging = FALSE;

/* flags/names of current stream and media file */
BOOL Batchmode = FALSE;
char StreamURL[512]  = "";
char StreamFilename[512] = "";
unsigned int StreamTotalTime;
unsigned int StreamMaxTime;
BOOL Transmitting = FALSE;
BOOL Previewing = FALSE;

/* data obtained from the IMediaPosition interface */
REFTIME IMPTotalTime;
REFTIME IMPCurrentPosition;
REFTIME IMPDownloadTime; /* obtained from gui_progressinfo */

/* data obtained from the IMediaSeeking interface */
LONGLONG IMSTotalTime;
LONGLONG IMSCurrentPosition;
DWORD    IMSCapabilities;
LONGLONG IMSDownloadTime; /* obtained from gui_progressinfo */

/* video window positions */
long IVWWindowLeft   = 0;
long IVWWindowTop    = 0;
long IVWWindowWidth  = 0;
long IVWWindowHeight = 0;

/* definition of min/max widths of status bar parts */
int StatusDefinitionArray[] =
{   6,
    50, 80,
    30, 40,
    80, 120,
    50, 80,
    160, -1,
    16, 16 };

/* enumeration of the status bar parts */
enum
{   STAT_KB = 0,
    STAT_PROGRESS,
    STAT_TIMECODE,
    STAT_SEQNO,
    STAT_STATUS, };

/* event for network socket notification */
WSAEVENT NetworkEvent = WSA_INVALID_EVENT;
SOCKET NetworkSocket = INVALID_SOCKET;

/* Winsock error strings/codes */
struct ErrorDef
{
    char* ErrorName;
    int   ErrorVal;
} WinSockErrors[] =

{
    "INTR"                ,(WSABASEERR+4),
    "BADF"                ,(WSABASEERR+9),
    "ACCES"               ,(WSABASEERR+13),
    "FAULT"               ,(WSABASEERR+14),
    "INVAL"               ,(WSABASEERR+22),
    "MFILE"               ,(WSABASEERR+24),
    "WOULDBLOCK"          ,(WSABASEERR+35),
    "INPROGRESS"          ,(WSABASEERR+36),
    "ALREADY"             ,(WSABASEERR+37),
    "NOTSOCK"             ,(WSABASEERR+38),
    "DESTADDRREQ"         ,(WSABASEERR+39),
    "MSGSIZE"             ,(WSABASEERR+40),
    "PROTOTYPE"           ,(WSABASEERR+41),
    "NOPROTOOPT"          ,(WSABASEERR+42),
    "PROTONOSUPPORT"      ,(WSABASEERR+43),
    "SOCKTNOSUPPORT"      ,(WSABASEERR+44),
    "OPNOTSUPP"           ,(WSABASEERR+45),
    "PFNOSUPPORT"         ,(WSABASEERR+46),
    "AFNOSUPPORT"         ,(WSABASEERR+47),
    "ADDRINUSE"           ,(WSABASEERR+48),
    "ADDRNOTAVAIL"        ,(WSABASEERR+49),
    "NETDOWN"             ,(WSABASEERR+50),
    "NETUNREACH"          ,(WSABASEERR+51),
    "NETRESET"            ,(WSABASEERR+52),
    "CONNABORTED"         ,(WSABASEERR+53),
    "CONNRESET"           ,(WSABASEERR+54),
    "NOBUFS"              ,(WSABASEERR+55),
    "ISCONN"              ,(WSABASEERR+56),
    "NOTCONN"             ,(WSABASEERR+57),
    "SHUTDOWN"            ,(WSABASEERR+58),
    "TOOMANYREFS"         ,(WSABASEERR+59),
    "TIMEDOUT"            ,(WSABASEERR+60),
    "CONNREFUSED"         ,(WSABASEERR+61),
    "LOOP"                ,(WSABASEERR+62),
    "NAMETOOLONG"         ,(WSABASEERR+63),
    "HOSTDOWN"            ,(WSABASEERR+64),
    "HOSTUNREACH"         ,(WSABASEERR+65),
    "NOTEMPTY"            ,(WSABASEERR+66),
    "PROCLIM"             ,(WSABASEERR+67),
    "USERS"               ,(WSABASEERR+68),
    "DQUOT"               ,(WSABASEERR+69),
    "STALE"               ,(WSABASEERR+70),
    "REMOTE"              ,(WSABASEERR+71),
    "SYSNOTREADY"         ,(WSABASEERR+91),
    "VERNOTSUPPORTED"     ,(WSABASEERR+92),
    "NOTINITIALISED"      ,(WSABASEERR+93),
    "DISCON"              ,(WSABASEERR+101),
    "HOST_NOT_FOUND"      ,(WSABASEERR+1001),
    "TRY_AGAIN"           ,(WSABASEERR+1002),
    "NO_RECOVERY"         ,(WSABASEERR+1003),
    "NO_DATA"             ,(WSABASEERR+1004),
    NULL,                 -1
};


/*--------------------------- FUNCTION PROTOTYPES ---------------------------*/

/* Function prototypes for externals in asfrecorder.c */
extern int main(int argc, char **argv);
extern int main_function(int argc, char **argv);
extern void gui_abort(void);
extern char *createtimestring(unsigned int timecode);
typedef enum  {
    ShowUsage,
    NoArguments,
    BadOption,
} UsageMode;
void Usage(char *progname, UsageMode mode);
UINT DoEnterPasswordDialog( HINSTANCE hInstance, HANDLE hwnd );
UINT DoProxyConfigurationDialog( HINSTANCE hInstance, HANDLE hwnd );


/*--------------------------- WINMAIN ENTRY POINT ---------------------------*/

//
// WinMain
//
// use call custom command line parser and
// give control to main() in asfrecorder.c
//
int PASCAL WinMain( HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpszCmdParam,
                    int nCmdShow )
{
    UINT nReturn = 0;
    HWND win;
    DWORD result;
    
    LPTSTR cmdline;

    cmdline = GetCommandLine();

    /* register an application- and inter-process-global message */
    WM_APPMSG = RegisterWindowMessage( APP_NAME );

    if (WM_APPMSG != 0)
    {
        /* loop through all other open ASFRecorder windows */

        win = NULL;
        while ((win = FindWindowEx(NULL, win, APP_NAME, NULL)) != NULL)
        {
            /* send an application-specific message to the other windows */
            /* to probe if these windows are "idle" (not in a transfer) */
            if (SendMessageTimeout( win, WM_APPMSG, 0, 0, SMTO_BLOCK, 250, &result) != 0)
            {
                if (result == MAGIC_COOKIE)
                {
                    COPYDATASTRUCT cds;
                    DWORD result2;

                    /* other windows seems to be idle */
                    /* so send our command line to this window */

                    cds.dwData = MAGIC_COOKIE;
                    cds.cbData = strlen(cmdline)+1;
                    cds.lpData = cmdline;

                    result2 = SendMessage( win, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds );

                    if (result2 == MAGIC_COOKIE)
                    {
                        /* other window processed the message */
                        /* so we can exit this process now */
                        return nReturn;
                    }
                }
            }
        }
    }

    /* seems that no other ASFRecorder window was willing to accept */
    /* our command line. So we have to handle it in this process. */

    if (ParseCommandLine(cmdline))
    {
        appVars.szProgramExecutable = argv[0];

        main(argc, argv);
    }
    FreeCommandLine();

    return nReturn;
} // WinMain


/*-------------- INTERFACE CODE TO ASFRECORDER NETWORKING CORE --------------*/

int gui_initialize()
{
    UINT nReturn;

    HINSTANCE hInstance = GetModuleHandleA(NULL);
    HINSTANCE hPrevInstance = NULL;
    LPSTR lpszCmdParam = NULL;
    int nCmdShow = SW_SHOW;

    nReturn = PseudoWinMain(hInstance, hPrevInstance, lpszCmdParam, nCmdShow);

    return nReturn;
}


void gui_uninitialize(void)
{
    EndOfWinMain();
}


void gui_setbatchmode(int flag)
{
    Batchmode = flag;
}


int  gui_startedfromdesktop(void)
{
    int fromdesktop = 0;

    /* I don't know if this is correct, but */
    /* it works for determining wheter this */
    /* programm was started from DOS or from */
    /* Windows Explorer, both on Win98 and NT */

    LPTSTR lpszVariable; 
    LPVOID lpvEnv; 
    
    // Get a pointer to the environment block. 
    
    if ((lpvEnv = GetEnvironmentStrings()) != NULL)
    {
        // Variable strings are separated by NULL byte, and the block is 
        // terminated by a NULL byte. 
        
        for (lpszVariable = (LPTSTR) lpvEnv; *lpszVariable; lpszVariable++) 
        { 
            /* this one is for Windows NT (together with PROMPT=) */
            if (!stricmp(lpszVariable, "OS=Windows_NT"))
                fromdesktop = 1;
            while (*lpszVariable) lpszVariable++;
        } 

        for (lpszVariable = (LPTSTR) lpvEnv; *lpszVariable; lpszVariable++) 
        { 
            /* this one for Windows 98 */
            if (!stricmp(lpszVariable, "CMDLINE=win"))
                fromdesktop = 1;
            /* required for Windows NT only */
            if (!strnicmp(lpszVariable, "PROMPT=", 7))
                fromdesktop = 0;
            while (*lpszVariable) lpszVariable++;
        } 

        FreeEnvironmentStrings(lpvEnv);
    }
    return fromdesktop;
}


void gui_prepareasyncsocket(SOCKET s)
{
    int rc;

    if (NetworkEvent == WSA_INVALID_EVENT)
    {
        NetworkEvent = WSACreateEvent();
    }

    if (NetworkEvent != WSA_INVALID_EVENT)
    {
        NetworkSocket = s;
        rc = WSAEventSelect(s, NetworkEvent, FD_READ|FD_WRITE|FD_CONNECT|FD_CLOSE);
        if (rc == SOCKET_ERROR)
        {
            gui_criticalerror("WSAEventSelect() failed! Error code: %d\n", WSAGetLastError());
        }
    }
}


void gui_restoresyncsocket(SOCKET s)
{
    WSAEventSelect(s, NULL, 0);
    WSACloseEvent(NetworkEvent);

    NetworkSocket = INVALID_SOCKET;
    NetworkEvent = WSA_INVALID_EVENT;
}


void gui_return_on_network_activity(void)
{
    /* don't enter main loop if the user has already closed the main window */
    if (appVars.hwndMainFrame != NULL)
    {
        DoMainLoop(FD_READ | FD_WRITE | FD_CLOSE);
    }
}


void gui_return_on_network_connect(int *returnval)
{
    /* don't enter main loop if the user has already closed the main window */
    if (appVars.hwndMainFrame != NULL)
    {
        int ret = DoMainLoop(FD_CONNECT);

        if (ret == 0)
            *returnval = 0;
        else
        {
            *returnval = SOCKET_ERROR;
            WSASetLastError(ret);
        }
    }
    else
    {
        *returnval = 0;
    }
}


int gui_nonblocking_socket_check(int num)
{
    if (num == WSAEWOULDBLOCK)
        return 1;
    else
        return 0;
}


void gui_waitforuseraction()
{
    /* don't enter main loop if the user has already closed the main window */
    if (appVars.hwndMainFrame != NULL)
    {
        DoMainLoop(0);
    }
}


void gui_showtext(char *text, ...)
{
    char Buffer[4096];

    va_list arglist;
    va_start(arglist, text);

    vsprintf(Buffer, text, arglist);

    MessageBox( appVars.hwndMainFrame, Buffer, appVars.szAppName,
                MB_APPLMODAL | MB_OK | 0 );

    va_end(arglist);
}


void gui_setstatus(char *statustext, ...)
{
    va_list arglist;
    va_start(arglist, statustext);

    SetStatusTextArgs(STAT_STATUS, SBT_POPOUT, CLR_DEFAULT, statustext, arglist);

    va_end(arglist);
}


void gui_seterror(char *errortext, ...)
{
    va_list arglist;
    va_start(arglist, errortext);

    SetStatusTextArgs(STAT_STATUS, SBT_POPOUT, RGB(0xff,0xff,0), errortext, arglist);

    va_end(arglist);
}


void gui_criticalerror(char *errortext, ...)
{
    char Buffer[4096];

    va_list arglist;
    va_start(arglist, errortext);

    vsprintf(Buffer, errortext, arglist);

    if (!Batchmode)
    {
        MessageBox( appVars.hwndMainFrame, Buffer, appVars.szAppName,
                    MB_APPLMODAL | MB_OK | MB_ICONERROR );
    }
    else
    {
        /* in batch mode, don't bother the user with blocking requesters */
        gui_seterror("%s", Buffer);
    }

    va_end(arglist);
}


void gui_logtext(char *text, ...)
{
    char Buffer[4096];

    va_list arglist;
    va_start(arglist, text);

    vsprintf(Buffer, text, arglist);

//    MessageBox( appVars.hwndMainFrame, Buffer, appVars.szAppName,
//                MB_APPLMODAL | MB_OK | 0 );

    va_end(arglist);
}


int gui_start_transmission(char *url, char *filename, int filenamesize, unsigned int totaltime, unsigned int maxtime)
{
    int retval = 0;

    static char szFileName[ _MAX_PATH ];
    static char szTitleName[ _MAX_FNAME + _MAX_EXT ];
    LPSTR szTitle;

    strcpy(StreamURL, url);

    StreamTotalTime = totaltime;
    StreamMaxTime = maxtime;

    if ( appVars.hwndMainFrame != NULL )
    {
        SetStatusText(STAT_KB,       0, -1, "");
        SetStatusText(STAT_PROGRESS, 0, -1, "");
        SetStatusText(STAT_TIMECODE, 0, -1, "");
        SetStatusText(STAT_SEQNO,    0, -1, "");

        if (!strcmp(filename, ""))
        {
            strcpy(StreamFilename, filename);
            
            SetTitle( appVars.hwndMainFrame, StreamURL );
            
            Transmitting = TRUE;
        }
        else
        {
            // Work out the full path name and the file name from the
            // specified file
            GetFullPathName( filename, _MAX_PATH, szFileName, &szTitle );

            if (Batchmode)
            {
                /* don't bother the user with a file dialog in batch mode */
                Transmitting = TRUE;
            }
            else
            {
                strncpy( szTitleName, szTitle, _MAX_FNAME + _MAX_EXT );
                szTitleName[ _MAX_FNAME + _MAX_EXT -1 ] = '\0';
                
                if ( DoFileSaveDialog( appVars.hwndMainFrame, szFileName, szTitleName ) )
                {
                    Transmitting = TRUE;
                }
            }
            
            if (Transmitting)
            {
                /* keep a local copy of the stream file name (for preview) */
                strncpy(StreamFilename, szFileName, sizeof(StreamFilename));
                StreamFilename[sizeof(StreamFilename)-1] = '\0';

                /* return the new file name to the main function */
                strncpy(filename, szFileName, filenamesize);
                filename[filenamesize-1] = '\0';

                SetTitle( appVars.hwndMainFrame, StreamURL );
                retval = 1;
            }
            else
            {
                SetTitle( appVars.hwndMainFrame, "Untitled" );
                strcpy(filename, "");
                retval = 0;
            }
        }
        
        if (hwndTrackbar != NULL)
        {
            SendMessage(hwndTrackbar, TBM_CLEARSEL,    TRUE,  0     );
        }
        
        UpdateToolbar();
    }
    return retval;
}


void gui_progressinfo(int bytes, char *timecode, int progress, int seqno, int currenttime)
{
    /* currenttime is in milliseconds */
    IMPDownloadTime = (REFTIME) currenttime / 1000;   /* REFTIME is in seconds (64 bit floating pt) */
    IMSDownloadTime = (LONGLONG)currenttime * 10000;  /* LONGLONG: 10 million units equal 1 second */

    if ( appVars.hwndMainFrame != NULL )
    {
        /* progress is scaled from 0 (=0%) to 10000 (=100%) */
        SetStatusText(STAT_KB,       0, -1, "%d kB", bytes   /1024  );
        SetStatusText(STAT_PROGRESS, 0, -1, "%d%%",  progress/100   );
        SetStatusText(STAT_TIMECODE, 0, -1, "T: %s", timecode       );
        SetStatusText(STAT_SEQNO,    0, -1, "#%d", seqno            );

        if ((hwndTrackbar != NULL) && (timecode != 0))
        {
            if (StreamTotalTime != 0)
            {
                SendMessage(hwndTrackbar, TBM_SETSELSTART,  FALSE,  0                                              );
                SendMessage(hwndTrackbar, TBM_SETSELEND,    TRUE,   (int)((float)10000*currenttime/StreamTotalTime));
            }
            else
            {
                if (StreamMaxTime != 0)
                {
                    SendMessage(hwndTrackbar, TBM_SETSELSTART,  FALSE,  0                                            );
                    SendMessage(hwndTrackbar, TBM_SETSELEND,    TRUE,   (int)((float)10000*currenttime/StreamMaxTime));
                }
                else
                {
                    SendMessage(hwndTrackbar, TBM_SETSELSTART,  FALSE,  0                                            );
                    SendMessage(hwndTrackbar, TBM_SETSELEND,    TRUE,   10000                                        );
                }
            }
        }
    }
}


void gui_modify_duration(unsigned int totaltime)
{
    /* called only for live recordings */

    LONG value;

    StreamTotalTime = totaltime;

    IMPTotalTime = (REFTIME)totaltime / 1000;
    IMSTotalTime = (LONGLONG)totaltime * 10000;

    value = (LONG)(10000 * IMPCurrentPosition / IMPTotalTime);

    if (hwndTrackbar != NULL)
    {
        if (!TrackbarDragging)
        {
            SendMessage( hwndTrackbar, TBM_SETPOS, TRUE, value);
        }
    }
}


void gui_finished_transmission()
{
    Transmitting = FALSE;
    Previewing = FALSE;

    StreamTotalTime = 0;
    StreamMaxTime = 0;

    if ( appVars. hwndMainFrame != NULL )
    {
        SetStatusText(STAT_KB,       0, -1, "");
        SetStatusText(STAT_PROGRESS, 0, -1, "");
        SetStatusText(STAT_TIMECODE, 0, -1, "");
        SetStatusText(STAT_SEQNO,    0, -1, "");
        
        if (hwndTrackbar != NULL)
        {
            SendMessage(hwndTrackbar, TBM_CLEARSEL,    TRUE,  0     );
        }
        
        UpdateToolbar();
        
        ShowWindow( appVars.hwndMainFrame, SW_SHOWNORMAL );
        SetWindowPos(appVars.hwndMainFrame, HWND_TOP, 0, 0, 0, 0,
                     SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
    }
}


void gui_not_idle(int flag)
{
    if (flag)
        Idle = FALSE;
    else
        Idle = TRUE;
}

char *gui_translate_errorcode(int error)
{
    static char def[32];
    char *result = def;
    struct ErrorDef *ed = WinSockErrors;

    sprintf(def, "<%d>", error);

    for (;;)
    {
        if (ed->ErrorVal == -1) break;

        if (ed->ErrorVal == error)
        {
            result = ed->ErrorName;
            break;
        }
        ed++;
    }

    return result;
}

static char username_buffer[256];
static char password_buffer[256];

int gui_getpassword(char **username, char **password)
{
    int ret = 0;

    if (!Batchmode)
    {
        if (*username != NULL) strncpy(username_buffer, *username, sizeof(username_buffer)-1);
        username[255]=0;
        if (*password != NULL) strncpy(password_buffer, *password, sizeof(password_buffer)-1);
        password[255]=0;
    
        if (DoEnterPasswordDialog( appVars.hInstance, appVars.hwndMainFrame ) == IDOK)
        {
            *username = username_buffer;
            *password = password_buffer;
            ret = 1;
        }
    }
    else
    {
        /* in batch mode, password must be given on command line */
        if (*username == NULL && *password == NULL)
        {
            /* cancel the authorization of no username/password given */
            ret = 0;
        }
        else
        {
            ret = 1;
        }
    }

    return ret;
}

static char proxy_buffer[512];

int gui_getproxy(char **proxy)
{
    int ret = 0;

    /* do not override command line proxy parameter */
    if (*proxy == NULL)
    {
        LONG retval;
        HKEY hkeyURLList;
        DWORD dwResult;
        
        retval = RegCreateKeyEx(APPREGKEY_ROOT,
            APPREGKEY_BASE,
            0,
            NULL,
            REG_OPTION_NON_VOLATILE,
            KEY_ALL_ACCESS,
            NULL,
            &hkeyURLList,
            &dwResult);
        
        
        if (retval != ERROR_SUCCESS)
        {
            gui_criticalerror("RegCreateKeyEx failed: %d\n", retval);
        }
        else
        {
            char buffer[512];
            DWORD dwType;
            DWORD dwSize;
                
            dwSize = sizeof(buffer);
            retval = RegQueryValueEx( hkeyURLList,
                "ProxyServer",
                0,
                &dwType,
                buffer,
                &dwSize );
            
            if (retval == ERROR_SUCCESS)
            {
                if (dwType == REG_SZ)
                {
                    strncpy(proxy_buffer, buffer, sizeof(proxy_buffer)-1);
                    proxy_buffer[sizeof(proxy_buffer)-1] = 0;

                    /* return proxy only if buffer not empty */
                    if (strcmp(proxy_buffer, ""))
                    {
                        *proxy = proxy_buffer;
                        ret = 1;
                    }
                }
                RegCloseKey(hkeyURLList);
            }
        }
    }
    return ret;
}

/*------------------------ COMMON CONTROLS STUFF ----------------------------*/

//
// InitMyControls
//
HRESULT InitMyControls(void)
{
    HRESULT hr;
    DWORD major;
    DWORD minor;
    BOOL too_old = FALSE;
#define MIN_MAJOR 4

#define MIN_MINOR 72


    GetComCtlVersion(&major, &minor);

    if (major < MIN_MAJOR) too_old = TRUE;
    else
        if (major == MIN_MAJOR && minor < MIN_MINOR) too_old = TRUE;

    if (too_old)
    {
        char Buffer[200];

        sprintf(Buffer, "Your commctl32.dll is too old!\n"
                        "You have version: %d.%d\n"
                        "Required is at least version %d.%d",
                        major, minor, MIN_MAJOR, MIN_MINOR);

        MessageBox( appVars.hwndMainFrame, Buffer, appVars.szAppName,
                    MB_APPLMODAL | MB_OK | MB_ICONERROR );

        hr = S_FALSE;
    }
    else
    {
        INITCOMMONCONTROLSEX icex;

        icex.dwSize  = sizeof(INITCOMMONCONTROLSEX);
        icex.dwICC = ICC_WIN95_CLASSES;

        InitCommonControlsEx(&icex);

        hr = S_OK;
    }
    return hr;
} // InitMyControls


//
// GetComCtlVersion
//
HRESULT GetComCtlVersion(LPDWORD pdwMajor, LPDWORD pdwMinor)
{
    HINSTANCE   hComCtl;

    if(   IsBadWritePtr(pdwMajor, sizeof(DWORD)) ||
        IsBadWritePtr(pdwMinor, sizeof(DWORD)))
        return E_INVALIDARG;

    //load the DLL
    hComCtl = LoadLibrary(TEXT("comctl32.dll"));

    if(hComCtl)
    {
        HRESULT           hr = S_OK;
        DLLGETVERSIONPROC pDllGetVersion;

        /*
        You must get this function explicitly because earlier versions of the DLL
        don't implement this function. That makes the lack of implementation of the
        function a version marker in itself.
        */
        pDllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hComCtl, TEXT("DllGetVersion"));

        if(pDllGetVersion)
        {
            DLLVERSIONINFO    dvi;

            ZeroMemory(&dvi, sizeof(dvi));
            dvi.cbSize = sizeof(dvi);

            hr = (*pDllGetVersion)(&dvi);

            if(SUCCEEDED(hr))
            {
                *pdwMajor = dvi.dwMajorVersion;
                *pdwMinor = dvi.dwMinorVersion;
            }
            else
            {
                hr = E_FAIL;
            }
        }
        else
        {
        /*
        If GetProcAddress failed, then the DLL is a version previous to the one
        shipped with IE 3.x.
            */
            *pdwMajor = 4;
            *pdwMinor = 0;
        }

        FreeLibrary(hComCtl);

        return hr;
    }

    return E_FAIL;

} // GetComCtlVersion


/*----------------------- STATUS BAR COMMON CONTROL -------------------------*/

//
// InitStatusbar
//
BOOL InitStatusbar( HINSTANCE hInstance, HWND hwndMainFrame )
{
    BOOL success = FALSE;

    if (hwndStatusbar = DoCreateStatusbar(hwndMainFrame, IDR_STATUSBAR, hInstance, StatusDefinitionArray))
    {
        ShowWindow( hwndStatusbar, SW_SHOW );
        UpdateWindow( hwndStatusbar );
        success = TRUE;
    }

    return success;
} // InitStatusbar


//
// SetStatusText
//
void SetStatusText(int partno, int drawmode, COLORREF cref, LPSTR text,...)
{
    va_list arglist;
    va_start(arglist, text);

    SetStatusTextArgs(partno, drawmode, cref, text, arglist);

    va_end(arglist);

} // SetStatusText


//
// StatusTimerFunc
//
void CALLBACK StatusTimerFunc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
    if (uMsg == WM_TIMER && idEvent == StatusTimer)
    {
        StatusTimerCount--;
        if (StatusTimerCount%2 == 0)
            SendMessage(hwndStatusbar, SB_SETBKCOLOR, (WPARAM) 0,  (LPARAM) CLR_DEFAULT);
        else
            SendMessage(hwndStatusbar, SB_SETBKCOLOR, (WPARAM) 0,  (LPARAM) StatusTimerCref);
    }

    if (StatusTimerCount == 0)
        KillTimer( appVars.hwndMainFrame, StatusTimer );
} // StatusTimerFunc


//
// SetStatusTextArgs
//
void SetStatusTextArgs(int partno, int drawmode, COLORREF cref, LPSTR text, void *args)
{
    if (hwndStatusbar != NULL)
    {
        char Buffer[1024];
        vsprintf(Buffer, text, args);

        // Tell the status bar to create the window parts.
        if (cref != -1)
        {
            SendMessage(hwndStatusbar, SB_SETBKCOLOR, (WPARAM) 0,  (LPARAM) cref);

            if (StatusTimer != 0)
            {
                KillTimer( appVars.hwndMainFrame, StatusTimer);
                StatusTimerCount = 0;
            }

            if (cref != CLR_DEFAULT)
            {
                StatusTimerCref = cref;
                StatusTimerCount = 5;
                StatusTimer = SetTimer( appVars.hwndMainFrame, ID_STATUSTIMER, 200, StatusTimerFunc);
            }
        }
        SendMessage(hwndStatusbar, SB_SETTEXT, (WPARAM) partno | drawmode,  (LPARAM) Buffer);
        ShowWindow( hwndStatusbar, SW_SHOW );
        UpdateWindow( hwndStatusbar );
    }
} // SetStatusTextArgs


//
// CalcStatusSize
//
void CalcStatusSize ( SIZE *pSize, int *array )
{
    int i;
    int nParts;
    int *ptr;
    int mintotal = 0;

    nParts = *array;
    ptr = array+1;
    for (i = 0; i < nParts; i++)
    {
        int wmin = *ptr++;
        ptr++;
        mintotal += wmin;
    }

    pSize->cx = mintotal;
    pSize->cy = 20;

    if (hwndStatusbar != NULL)
    {
        RECT statRect;
        GetWindowRect(hwndStatusbar, &statRect);
        pSize->cy = statRect.bottom-statRect.top+1;
    }
}

//
// AdjustStatusbar
//
void AdjustStatusbar(HWND hwndStatus, HWND hwndParent, int *array)
{
    int nParts;

    RECT rcClient;
    HLOCAL hloc;
    LPINT lpParts;
    int i, x, nWidth;
    int *ptr;
    int mintotal = 0;
    int maxtotal = 0;
    int bendtotal = 0;
    int numunlim = 0;
    int weight = 0;
    int surplus, maxsurplus;

    // Get the coordinates of the parent window's client area.
    GetClientRect(hwndParent, &rcClient);

    nWidth = rcClient.right-rcClient.left+1;

    nParts = *array;
    ptr = array+1;
    for (i = 0; i < nParts; i++)
    {
        int wmin = *ptr++;
        int wmax = *ptr++;

        if (wmax!=-1)
        {
            mintotal += wmin;
            maxtotal += wmax;
            bendtotal += (wmax-wmin);
        }
        else
        {
            mintotal +=   wmin;
            maxtotal += 2*wmin;
            bendtotal +=  wmin;
            numunlim++;
        }
    }

    // Allocate an array for holding the right edge coordinates.
    hloc = LocalAlloc(LHND, sizeof(int) * nParts);
    lpParts = LocalLock(hloc);

    // Calculate the right edge coordinate for each part, and
    // copy the coordinates to the array.
    ptr = array+1;
    x = 0;

    surplus    = nWidth - mintotal;
    if (surplus<0) surplus=0;
    maxsurplus = nWidth - maxtotal;
    if (maxsurplus<0) maxsurplus=0;

    for (i = 0; i < nParts; i++)
    {
        int wmin = *ptr++;
        int wmax = *ptr++;
        int bend;
        int w = 0;

        if (wmax!=-1)
            bend = wmax-wmin;
        else
            bend = wmin;

        w = wmin + (surplus*bend/bendtotal);
        if (w > 2*wmin) w = 2*wmin;

        if (wmax==-1)
            w += maxsurplus / numunlim;

        if (w < wmin) w = wmin;
        if (wmax!=-1)
            if (w > wmax) w = wmax;

        x+= w;

        *(lpParts+i) = x;
    }

    // Tell the status bar to create the window parts.
    SendMessage(hwndStatus, SB_SETPARTS, (WPARAM) nParts, (LPARAM) lpParts);

    // Free the array, and return.
    LocalUnlock(hloc);
    LocalFree(hloc);

} // AdjustStatusbar


// DoCreateStatusbar - creates a status bar and divides it into
//                     the specified number of parts.
// Returns the handle to the status bar.
//        hwndParent - parent window for the status bar.
//         nStatusID - child window identifier.
//             hinst - handle to the application instance.
//            nParts - number of parts into which to divide the status bar.
HWND DoCreateStatusbar(HWND hwndParent, int nStatusID,
                       HINSTANCE hinst, int *array)
{
    HWND hwndStatus;

    // Create the status bar.
    hwndStatus = CreateWindowEx(
        0,                       // no extended styles
        STATUSCLASSNAME,         // name of status bar class
        (LPCTSTR) NULL,          // no text when first created
        SBARS_SIZEGRIP |         // includes a sizing grip
        WS_VISIBLE | WS_CHILD,   // creates a child window
        0, 0, 0, 0,              // ignores size and position
        hwndParent,              // handle to parent window
        (HMENU) nStatusID,       // child window identifier
        hinst,                   // handle to application instance
        NULL);                   // no window creation data

    AdjustStatusbar(hwndStatus, hwndParent, array);
    return hwndStatus;

} // DoCreateStatusbar



/*-------------------------- MESSAGE BOX ROUTINES ---------------------------*/

//
// DbgAssert
//
// Displays a message box if the condition evaluated to FALSE
//
void DbgAssert(const char *pCondition, const char *pFileName, int iLine)
{
    int MsgId;
    char szInfo[1024];

    wsprintf(szInfo, TEXT("%s \nAt line %d of %s"),pCondition, iLine, pFileName);
    MsgId = MessageBox(NULL, szInfo, TEXT("ASSERT Failed"),
                           MB_SYSTEMMODAL |
                           MB_ICONHAND |
                           MB_ABORTRETRYIGNORE);
    switch (MsgId)
    {
        case IDABORT:           // Kill the application

            FatalAppExit(FALSE, TEXT("Application terminated"));
            break;

        case IDRETRY:           // Break into the debugger
            DebugBreak();
            break;

        case IDIGNORE:          // Ignore assertion continue executing
            break;
    }
} // DbgAssert


//
// ReallyQuitMessageBox
//
// Ask the user wheter or not to quit
//
UINT ReallyQuitMessageBox( void )
{
    UINT retval;

    if (Transmitting == FALSE)
    {
        /* don't ask if there is no transmission in progress */
        retval = IDYES;
    }
    else
    {
        char szStr[ nMaxResourceStrSize ];

        LoadString( appVars.hInstance, IDS_REALLY_QUIT, szStr, nMaxResourceStrSize );
        retval = MessageBox( appVars.hwndMainFrame, szStr, appVars.szAppName,
                             MB_APPLMODAL | MB_YESNO | MB_DEFBUTTON2 | MB_ICONQUESTION );
    }
    return retval;

} // ReallyQuitMessageBox


//
// PlayerMessageBox
//
// Load and display an error message
//
void PlayerMessageBox( UINT nResource )
{
    char szStr[ nMaxResourceStrSize ];

    LoadString( appVars.hInstance, nResource, szStr, nMaxResourceStrSize );
    MessageBox( appVars.hwndMainFrame, szStr, appVars.szAppName,
                MB_APPLMODAL | MB_OK | MB_ICONEXCLAMATION );

} // PlayerMessageBox


//
// OnShowReadme
//
void OnShowReadme()
{
    char szFileName[ _MAX_PATH ];
    char *szTitle;
    SHELLEXECUTEINFO sei;
    BOOL ret;
    
    // Work out the full path name
    if (GetFullPathName( appVars.szProgramExecutable, _MAX_PATH, szFileName, &szTitle ) > 0)
    {
        strcpy( szTitle, "README.TXT");
        
        memset(&sei, 0, sizeof(sei));
        sei.cbSize = sizeof(sei);

        sei.fMask = SEE_MASK_FLAG_NO_UI;
        sei.hwnd = appVars.hwndMainFrame;
        sei.lpVerb = "open";
        sei.lpParameters = NULL;
        sei.lpDirectory = NULL;
        sei.lpFile = szFileName;
        sei.nShow = SW_SHOW;
        
        ret = ShellExecuteEx(&sei);
        
        if (ret == FALSE)
        {
            if ((long)GetLastError() == ERROR_FILE_NOT_FOUND)
                gui_criticalerror("Someone deleted the README.TXT\n"
                                  "file from the program directory!\n");
            else
                gui_criticalerror("Cannot show the README.TXT file for some strange reason!\n");
        }
    }
}

                
/*-------------------------- COMMAND HANDLING -------------------------------*/

//
// ProcessCommand
//
// Process a WM_COMMAND message to the main window
//
long ProcessCommand( HWND hwnd, UINT wParam, LONG lParam )
{
    switch( wParam ){
        case ID_URL_OPEN:
            DoOpenURLDialog( appVars.hInstance,hwnd );
            break;

        case ID_FILE_OPEN:
            if (OpenMediaFile( hwnd, NULL ))
                OnMediaPlay( );
            break;

        case ID_FILE_EXIT:
            PostMessage( appVars.hwndMainFrame, WM_CLOSE, 0, 0 );
            break;

        case ID_MEDIA_PLAY:
            OnMediaPlay( );
            break;

        case ID_MEDIA_PAUSE:
            OnMediaPause( );
            break;

        case ID_MEDIA_STOP:
            OnMediaStop(FALSE, TRUE);
            break;

        case ID_MEDIA_EJECT:
            DeleteContents();
            break;

        case ID_STREAM_PREVIEW:
            if (strcmp( StreamFilename, "" ))
            {
                if (OpenMediaFile( hwnd, StreamFilename ))
                {
                    Previewing = TRUE;
                    OnMediaPlay( );
                }
            }
            break;

        case ID_STREAM_CANCEL:
            gui_seterror("aborting transfer...");
            gui_abort();
            PostMessage(hwnd, WM_COMMAND, WM_QUIT, 0);
            break;

        case ID_SETTINGS_REGISTERFILETYPES:
            OnRegisterFileTypes();
            break;

        case ID_SETTINGS_UNREGISTERFILETYPES:
            OnUnregisterFileTypes();
            break;

        case ID_HELP_ABOUT:
            DoAboutDialog( appVars.hInstance,hwnd );
            break;

        case ID_PROGRAM_ARGUMENTS:
            Usage(appVars.szProgramExecutable, ShowUsage);
            break;

        case ID_SHOW_README:
            OnShowReadme();
            break;

        case ID_PROXY_SERVER:
            DoProxyConfigurationDialog( appVars.hInstance, hwnd );
            break;

        default:
            return DefWindowProc( hwnd, WM_COMMAND, wParam, lParam );
    }
    return (LRESULT) 0;

} // ProcessCommand

/*------------------------- WINDOW SIZE CALCULATIONS ------------------------*/

//
// OnGetMinMaxInfo
//
// Sets the minimum size of the main window and the maximum height
//
void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
    RECT rectRequired;
    SIZE sizeToolbar;
    SIZE sizeStatus;

    // Our client area is going to be the toolbar, so find our its size
    CalcToolbarSize( &sizeToolbar );

    rectRequired.left = rectRequired.top = 0;
    rectRequired.right = sizeToolbar.cx;
    rectRequired.bottom = sizeToolbar.cy;

    CalcStatusSize( &sizeStatus, StatusDefinitionArray );

    if (sizeStatus.cx > rectRequired.right)
        rectRequired.right = sizeStatus.cx;

    rectRequired.bottom += sizeStatus.cy;

    // Take into account the menu, caption and thick frame
    AdjustWindowRect( &rectRequired, WS_CAPTION|WS_THICKFRAME, TRUE );

    // Set the min/max sizes
    lpMMI->ptMinTrackSize.x = rectRequired.right - rectRequired.left;
    lpMMI->ptMinTrackSize.y = rectRequired.bottom - rectRequired.top;

} // OnGetMinMaxInfo


/*------------------------- MAIN WINDOW PROCEDURE ---------------------------*/

typedef BOOL (FAR WINAPI *GradientFillFunction)(HDC,PTRIVERTEX,ULONG,PVOID,ULONG,ULONG);
GradientFillFunction GradientFillPtr;

//
// MainFrameProc
//
// Handles the message sent to the main window
//

long FAR PASCAL MainFrameProc( HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
    DRAWITEMSTRUCT *dis;

    /******************************************************/
    /*      Handle inter-process communications here      */
    /******************************************************/

    /* Answer the probe-message with our current status. */
    /* Return MAGIC_COOKIE if we are able to handle      */
    /* other tasks                                       */
    if (message == WM_APPMSG)
    {
        if (Idle == TRUE)
        {
            Idle = FALSE;
            return (LRESULT) MAGIC_COOKIE;
        }
        else
        {
            return (LRESULT) 0;
        }
    }

    /* This message is only received when we just answered */
    /* the probe message with a MAGIC_COOKIE               */
    /* Now we will get the command line from another ASF-  */
    /* Recorder process                                    */

    if (message == WM_COPYDATA)
    {
        COPYDATASTRUCT *cds = (COPYDATASTRUCT*)lParam;

        if (cds != NULL)
        {            
            if (cds->dwData == MAGIC_COOKIE)
            {
                LPTSTR cmdline;
                unsigned char buffer[1024];
                int argc_bak = argc;      /* ParseCommandLine() operates          */
                char **argv_bak = argv;   /* on global variables (backup needed!) */
                
                argc = 0;                 /* simulate "empty" command line */
                argv = NULL;   

                cmdline = cds->lpData;    /* copy the data from the WM_COPYDATA */
                strcpy(buffer, cmdline);  /* message */
                
                if (ParseCommandLine(buffer))
                {
                    /* tell the other process that we are now */
                    /* processing its command line */
                    ReplyMessage((LRESULT) MAGIC_COOKIE);

                    ShowWindow( appVars.hwndMainFrame, SW_SHOWNORMAL );
                    SetWindowPos(appVars.hwndMainFrame, HWND_TOP, 0, 0, 0, 0,
                                 SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);

                    /* now process this command line */
                    main_function(argc, argv);
                }
                FreeCommandLine();
                
                /* restore the previous command line        */
                /* (needed for FreeCommandLine() in WinMain */
                argc = argc_bak;
                argv = argv_bak;
            }
        }

        /* return to idle state (can accept other command lines now) */
        Idle = TRUE;
        return (LRESULT) 0;
    }

    switch( message )
    {
    case WM_CLOSE:
        if (ReallyQuitMessageBox() == IDYES)
        {
            if (DestroyWindow ( appVars.hwndMainFrame ))
            {
                appVars.hwndMainFrame = NULL;
                hwndAboutBox = NULL;
                hwndOpenURLDialog = NULL;
                hwndStatusbar = NULL;
                toolbar.hwndPlayButton = NULL;
                toolbar.hwndPauseButton = NULL;
                toolbar.hwndStopButton = NULL;
                toolbar.hwndPreviewButton = NULL;
                toolbar.hwndCancelButton = NULL;
                hwndTrackbar = NULL;
            }
        }
        break;

    case WM_DESTROY:
        gui_abort();
        PostQuitMessage( 0 );
        break;

    case WM_GETMINMAXINFO:
        OnGetMinMaxInfo( (MINMAXINFO FAR *) lParam );
        break;

    case WM_ERASEBKGND:
        {
            RECT rc;
            HDC hdc;
            SIZE sizeStatus;

            HMODULE msimg32;

            hdc = (HDC) wParam;
            GetClientRect(hwnd, &rc);

            /* try to load msimg32.dll (Win98, Win2000) */
            if ((msimg32 = LoadLibrary("msimg32.dll")) != NULL)
	        {
                TRIVERTEX          vert[4] ;
                GRADIENT_TRIANGLE  gTri[2];

	            GradientFillPtr = (GradientFillFunction)GetProcAddress(msimg32, "GradientFill");

                CalcStatusSize( &sizeStatus, StatusDefinitionArray );
                rc.bottom -= (sizeStatus.cy-1);

                vert [0] .x      = rc.left;
                vert [0] .y      = rc.top;
                vert [0] .Red    = 0xff00;
                vert [0] .Green  = 0xff00;
                vert [0] .Blue   = 0x8000;
                vert [0] .Alpha  = 0x0000;

                vert [1] .x      = rc.right;
                vert [1] .y      = rc.top;
                vert [1] .Red    = 0x6000;
                vert [1] .Green  = 0x6000;
                vert [1] .Blue   = 0x6000;
                vert [1] .Alpha  = 0x0000;

                vert [2] .x      = rc.right;
                vert [2] .y      = rc.bottom;
                vert [2] .Red    = 0x8000;
                vert [2] .Green  = 0x4000;
                vert [2] .Blue   = 0x0000;
                vert [2] .Alpha  = 0x0000;

                vert [3] .x      = rc.left;
                vert [3] .y      = rc.bottom;
                vert [3] .Red    = 0x6000;
                vert [3] .Green  = 0x6000;
                vert [3] .Blue   = 0x6000;
                vert [3] .Alpha  = 0x0000;

                gTri[0].Vertex1 = 0;
                gTri[0].Vertex2 = 1;
                gTri[0].Vertex3 = 2;

                gTri[1].Vertex1 = 0;
                gTri[1].Vertex2 = 3;
                gTri[1].Vertex3 = 2;

                (*GradientFillPtr)(hdc, vert,4, gTri, 2, GRADIENT_FILL_TRIANGLE);

                FreeLibrary(msimg32);
            }
            else
            {
                /* basic backfill functions for users not having the msimg32.dll */
                HBRUSH brush = CreateSolidBrush(RGB(0xc0,0x80,0x00));
                FillRect(hdc, &rc, brush);
                DeleteObject(brush);
            }
        }
        return (LRESULT) 1;
        break;

    case WM_DRAWITEM:
        dis = (DRAWITEMSTRUCT FAR *) lParam;
        if (( dis->CtlType == ODT_BUTTON ) &&
            ( dis->CtlID == ID_MEDIA_PLAY     ||
              dis->CtlID == ID_MEDIA_PAUSE    ||
              dis->CtlID == ID_MEDIA_STOP)    ||
              dis->CtlID == ID_STREAM_PREVIEW ||
              dis->CtlID == ID_STREAM_CANCEL    )
        {
            DrawButton( appVars.hInstance, (DRAWITEMSTRUCT FAR *) lParam );
            return (LRESULT) 1;
        }
        break;

    case WM_INITMENUPOPUP:
        if( lParam == 1 )
        {          // Media popup menu
            EnableMenuItem( (HMENU) wParam, ID_MEDIA_PLAY,     CanPlay()       ? MF_ENABLED : MF_GRAYED );
            EnableMenuItem( (HMENU) wParam, ID_MEDIA_PAUSE,    CanPause()      ? MF_ENABLED : MF_GRAYED );
            EnableMenuItem( (HMENU) wParam, ID_MEDIA_STOP,     CanStop()       ? MF_ENABLED : MF_GRAYED );
            EnableMenuItem( (HMENU) wParam, ID_MEDIA_EJECT,    IsInitialized() ? MF_ENABLED : MF_GRAYED );
        }
        else
        {
            if( lParam == 2 )
            {          // Stream popup menu
                EnableMenuItem( (HMENU) wParam, ID_STREAM_PREVIEW,  CanPreview() ? MF_ENABLED : MF_GRAYED );
                EnableMenuItem( (HMENU) wParam, ID_STREAM_CANCEL,   Transmitting ? MF_ENABLED : MF_GRAYED );
            }
            else
            {
                return DefWindowProc( hwnd, message, wParam, lParam );
            }
        }
        break;

    case WM_SIZE:
        SendMessage(hwndStatusbar, WM_SIZE, wParam, lParam);
        AdjustStatusbar(hwndStatusbar, appVars.hwndMainFrame, StatusDefinitionArray);
        AdjustTrackbar(hwndTrackbar, appVars.hwndMainFrame );
        return DefWindowProc( hwnd, message, wParam, lParam );
        break;

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            BeginPaint( hwnd, &ps );

            EndPaint( hwnd, &ps );
        }
        break;

    case WM_COMMAND:
        return ProcessCommand( hwnd, wParam, lParam );
        break;

    case WM_NOTIFY:
        {
            int id = wParam;
            LPNMHDR nmhdr = (LPNMHDR)lParam;

            if (nmhdr->hwndFrom == hwndTrackbar)
            {
                if (nmhdr->code == NM_RELEASEDCAPTURE)
                {
                    int pos;
                    pos = positionTrackbar;
                    SendMessage(hwndTrackbar, TBM_SETPOS, TRUE, pos);
                    SetCurrentPosition((REFTIME)((REFTIME)pos/10000 * IMPTotalTime));
                }
            }
        }
//        return DefWindowProc( hwnd, message, wParam, lParam );
        break;

    case WM_HSCROLL:
        {
            HWND hwndFrom = (HWND)lParam;
            if (hwndFrom == hwndTrackbar)
            {
                int pos  = HIWORD(wParam);
                int code = LOWORD(wParam);

                switch(code)
                {
                    case TB_THUMBTRACK:
                        {
                            unsigned int timecode;
                            char *timecodestring;

                            if (Previewing)
                            {
                                int prevpos = pos;
                                int min=SendMessage(hwndTrackbar, TBM_GETSELSTART, 0, 0);
                                int max=SendMessage(hwndTrackbar, TBM_GETSELEND,   0, 0);
                                if (pos < min) pos = min;
                                if (pos > max) pos = max;
                                if (pos != prevpos) SendMessage(hwndTrackbar, TBM_SETPOS, TRUE, pos);
                            }

                            /* save the current position of the trackbar slider */
                            positionTrackbar = pos;

                            if (!Transmitting)
                            {
                                timecode = (unsigned int)((float)pos/10000 * (1000*IMPTotalTime));
                                timecodestring = createtimestring(timecode);
                                SetStatusText(STAT_TIMECODE, 0, -1, "T: %s", timecodestring);
                            }

                            if (TrackbarDragging == FALSE)
                                OnMediaSeeking(TRUE);

                            TrackbarDragging = TRUE;
                        }
                        break;

                    case TB_THUMBPOSITION:
                        {
                            /* save the position of the trackbar slider /*
                            /* when we released the mouse button */
                            positionTrackbar = SendMessage(hwndTrackbar, TBM_GETPOS, 0, 0);

                            if (TrackbarDragging == TRUE)
                                OnMediaSeeking(FALSE);

                            TrackbarDragging = FALSE;
                        }
                        break;

                    default:
                        break;
                }
            }
        }
//        return DefWindowProc( hwnd, message, wParam, lParam );
        break;

    /* handle drag & drop */
    case WM_DROPFILES:
        {
            HANDLE hDrop = (HANDLE) wParam;
            UINT numfiles;
            char buffer[512];
            unsigned char *dotptr, *tmpptr;

            numfiles = DragQueryFile(hDrop, 0xffffffff, buffer, sizeof(buffer));

            if (numfiles > 1)
            {
                PlayerMessageBox( IDS_DROP_ONLY_ONE );
            }

            if (numfiles > 0)
            {
                DragQueryFile(hDrop, 0, buffer, sizeof(buffer));
            }

            DragFinish(hDrop);

            /* search for filename extension */
            for (dotptr = buffer+strlen(buffer), tmpptr = buffer; (tmpptr = strchr(tmpptr, '.')) != NULL ; dotptr = tmpptr++);

            /* search for redirection file name extension */
            if ( (!stricmp(dotptr, ".asx")) ||
                 (!stricmp(dotptr, ".wax")) ||
                 (!stricmp(dotptr, ".wvx")) ||
                 (!stricmp(dotptr, ".wmx"))   )
            {
                if (Transmitting)
                {
                    PlayerMessageBox( IDS_TRANSMISSION_RUNNING );
                }
                else
                {
                    /* call main_function() to process redirection file */
                    unsigned char *(my_argv[2]);

                    int my_argc = 2;
                    my_argv[0] = appVars.szProgramExecutable;
                    my_argv[1] = buffer;

                    main_function(my_argc, (char**)my_argv);
                }
            }
            else
            {
                /* try to play file as media file */
                if (OpenMediaFile( hwnd, buffer ))
                    OnMediaPlay( );
            }
        }
        break;

    default:
        return DefWindowProc( hwnd, message, wParam, lParam );
    }

    return (LRESULT) 0;

} // MainFrameProc


/*---------------------- INITIALIZATION/CLEANUP ROUTINES --------------------*/

//
// InitApplication
//
BOOL InitApplication()
{
    strcpy( appVars.szAppName, APP_NAME );

    // Filter interface initialize?
    if( SUCCEEDED( CoInitialize( NULL )))
        return TRUE;

    return FALSE;

} // InitApplication


//
// UnInitApplication
//
void UnInitApplication()
{
    CoUninitialize( );

} // UnInitApplication


//
// InitInstance
//
// Set the specific instance data and register our main
// window class if it has not been registered already
//
BOOL InitInstance( HANDLE hInstance, HANDLE hPrevInstance )
{
    appVars.hInstance = hInstance;

    if(!hPrevInstance)
    {
        WNDCLASS wndClass;

        wndClass.style          = CS_HREDRAW | CS_VREDRAW;
        wndClass.lpfnWndProc    = MainFrameProc;
        wndClass.cbClsExtra     = 0;
        wndClass.cbWndExtra     = 0;
        wndClass.hInstance      = appVars.hInstance;
        wndClass.hIcon          = LoadIcon( appVars.hInstance, MAKEINTRESOURCE( IDI_APPLICATION_ICON ));
        wndClass.hCursor        = LoadCursor( NULL, IDC_ARROW );
        wndClass.hbrBackground  = (HBRUSH)(COLOR_BACKGROUND + 1);
        wndClass.lpszMenuName   = MAKEINTRESOURCE( IDM_MAINFRAME );
        wndClass.lpszClassName  = appVars.szAppName;

        RegisterClass( &wndClass );

        if ( InitMyControls() == S_OK )
        {
            return TRUE;
        }

        return FALSE;

    }

    return TRUE;

} // InitInstance


//
// InitMainFrame
//
// Create our main window
//
BOOL InitMainFrame( int nCmdShow )
{
    const DWORD Styles = (WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN) &~ WS_MAXIMIZEBOX;
    char szTitle[ 30 ];

    strcpy( szTitle, APP_NAME );
    strcat( szTitle, UNTITLED_STRING );

    appVars.hwndMainFrame =
        CreateWindowEx( WS_EX_ACCEPTFILES,
                      appVars.szAppName,    // Our class name
                      szTitle,              // Window title
                      Styles,               // It's styles
                      CW_USEDEFAULT,        // No x position
                      CW_USEDEFAULT,        // And no y either
                      0, 65,                // Initial sizes
                      NULL,                 // No parent window
                      NULL,                 // And no menu
                      appVars.hInstance,    // App instance
                      NULL);                // Creation data

    DragAcceptFiles( appVars.hwndMainFrame, TRUE);

    ShowWindow( appVars.hwndMainFrame, nCmdShow );
    UpdateWindow( appVars.hwndMainFrame );
    return TRUE;

} // InitMainFrame

/*-------------------------- BUSY POINTER ROUTINE ---------------------------*/

int BusyNest = 0;

//
// SetBusy
//
void SetBusy(BOOL flag)
{
    if (flag == TRUE)
    {
        BusyNest++;
        if (BusyNest > 0) SetCursor( LoadCursor( NULL, IDC_WAIT ) );
    }
    else
    {
        if (BusyNest > 0) BusyNest--;

        if (BusyNest == 0) SetCursor( LoadCursor( NULL, IDC_ARROW ) );
    }

} // SetBusy


/*-------------------- MAIN EVENT LOOP/MESSAGE HANDLING ---------------------*/

//
// DoMainLoop
//
// Main message loop
//
int DoMainLoop(int network_mask)
{
    MSG msg;

    BOOL network_event = FALSE;
    int network_retval = 0;

    // Message loop lasts until we get a WM_QUIT message
    // Upon which we shall return from the function

    // Message loop also exits when network activity is
    // dectected (only if network is set to TRUE)

    for (;;)
    {
        // Read all of the messages in this next loop
        // removing each message as we read it

        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == 1)
        {
            if (msg.message == WM_QUIT)
                return msg.wParam;

            if (hwndAboutBox != NULL)
                if (IsDialogMessage(hwndAboutBox, &msg)) continue;

            if (hwndOpenURLDialog != NULL)
                if (IsDialogMessage(hwndOpenURLDialog, &msg)) continue;

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }


        // The following code waits for the arrival of new messages, i.e.
        // it puts the task to sleep. This is a non-desired behaviour if
        // we must react to a network event.

        if (network_event == FALSE)
        {
            HANDLE  ahObjects[10];       // Handles that need to be waited on
            int cObjects = 0;                // Number of objects that we have
            int cGraphIndex = -1;
            int cNetworkIndex = -1;

            ahObjects [cObjects] = GetGraphEventHandle();
            if (ahObjects[cObjects] != NULL)
            {
                cGraphIndex = cObjects;
                cObjects++;
            }

            if (NetworkEvent != WSA_INVALID_EVENT)
            {
                ahObjects[cObjects] = NetworkEvent;
                cNetworkIndex = cObjects;
                cObjects++;
            }

            if( cObjects == 0 )
            {
                WaitMessage();
            }
            else
            {
                DWORD Result;

                // Wait for any message or a graph notification

                Result = MsgWaitForMultipleObjects( cObjects,
                                                    ahObjects,
                                                    FALSE,
                                                    INFINITE,
                                                    QS_ALLINPUT );

                // Have we received an event notification

                if( Result != (WAIT_OBJECT_0 + cObjects) )
                {
                    if (cGraphIndex != -1)
                    {
                        if( Result == WAIT_OBJECT_0 + cGraphIndex)
                        {
                            OnGraphNotify();
                        }
                    }

                    if (cNetworkIndex != -1)
                    {
                        if( Result == WAIT_OBJECT_0 + cNetworkIndex)
                        {
                            WSANETWORKEVENTS wsane;
                            if (WSAEnumNetworkEvents(NetworkSocket, NetworkEvent, &wsane) == SOCKET_ERROR)
                            {
                                gui_criticalerror("WSAEnumNetworkEvents() failed! Error code: %d\n", WSAGetLastError());
                            }
                            else
                            {
                                if (wsane.lNetworkEvents & FD_CONNECT)
                                {
                                    network_retval = wsane.iErrorCode[FD_CONNECT_BIT];
                                }
                                else
                                {
                                    network_retval = 0;
                                }
                                network_event = TRUE;
                            }
                        }
                    }
                }
            }
        }

        if (network_event == TRUE)
            break;

    }

    // if we end up here, we must have received a network event
    return network_retval;

} // DoMainLoop


/*--------------------------- PSEUDO WINMAIN CODE ---------------------------*/

//
// PseudoWinMain
//
// former WinMain code. Now called from real WinMain
//
int PASCAL PseudoWinMain( HINSTANCE hInstance,
                          HINSTANCE hPrevInstance,
                          LPSTR lpszCmdParam,
                          int nCmdShow )
{
    UINT nReturn = 0;

    // Initialise COM and the application
    if ( InitApplication() == FALSE ) return 0;

    if( InitInstance( hInstance, hPrevInstance ) &&
        InitMainFrame( nCmdShow ) &&
        InitToolbar( hInstance, appVars.hwndMainFrame ) &&
        InitStatusbar( hInstance, appVars.hwndMainFrame ) &&
        InitTrackbar( hInstance, appVars.hwndMainFrame ) &&
        InitMedia( ) &&
        InitFileOpenDialog( appVars.hwndMainFrame ) &&
        InitFileSaveDialog( appVars.hwndMainFrame ))
    {
        nReturn = 1;
    }

    return nReturn;

} // WinMain


//
// EndOfWinMain
//
// former end part of WinMain code. Now called from real WinMain
//
void EndOfWinMain(void)
{
    // Stop the graph if we can
    if( CanStop() )
            OnMediaStop(TRUE, FALSE);

    // Release the filter graph
    DeleteContents();

    UnInitApplication();

} // EndOfWinMain


/*--------------------------- ABOUT DIALOG BOX ------------------------------*/


// Simply handles the Help..About dialog box

//
// AboutDlgProc
//
BOOL FAR PASCAL AboutDlgProc( HWND hwnd, UINT message, UINT wParam, LONG lParam )
{
    DRAWITEMSTRUCT *dis;

    switch( message )
    {
        case WM_INITDIALOG:
           return (LRESULT) 1;

        case WM_DRAWITEM:
        dis = (DRAWITEMSTRUCT FAR *) lParam;
        if (( dis->CtlType == ODT_STATIC ) &&
            ( dis->CtlID == IDC_ASFRECORDER_BITMAP ))
        {
            HDC hSourceDC = CreateCompatibleDC( NULL );
            HGDIOBJ loadedbitmap;
            HGDIOBJ hgdiOldBitmap;
            
            loadedbitmap = (HGDIOBJ) LoadImage( appVars.hInstance, MAKEINTRESOURCE( IDB_ASFRECORDER ), IMAGE_BITMAP, 0,0, LR_DEFAULTCOLOR );
            hgdiOldBitmap = SelectObject( hSourceDC, loadedbitmap );
            
            // ..and blit it
            BitBlt( dis->hDC,
                dis->rcItem.left,
                dis->rcItem.top ,
                dis->rcItem.right-dis->rcItem.left+1,
                dis->rcItem.bottom-dis->rcItem.top+1,
                hSourceDC,
                0,
                0,
                SRCCOPY
                );
            
            // Restore the original bitmap
            SelectObject( hSourceDC, hgdiOldBitmap );
            
            // this was missing from the Microsoft example code
            DeleteObject(loadedbitmap);
            DeleteDC(hSourceDC);
            return (LRESULT) 1;
        }
        break;


        case WM_COMMAND:
            if( wParam==IDOK || wParam==IDCANCEL )
            {
                if (DestroyWindow( hwndAboutBox ))
                    hwndAboutBox = NULL;
                return (LRESULT) 1;
            }
    }
    return (LRESULT) 0;

} // AboutDlgProc


//
// DoAboutDialog
//
void DoAboutDialog( HINSTANCE hInstance, HANDLE hwnd )
{
    if (hwndAboutBox == NULL)
    {
        hwndAboutBox = CreateDialog( hInstance, MAKEINTRESOURCE( IDD_ABOUTBOX ), hwnd, AboutDlgProc );
    }

    if (hwndAboutBox != NULL)
    {
        ShowWindow(hwndAboutBox, SW_SHOW);
        BringWindowToTop( hwndAboutBox );
    }

} // DoAboutDialog



/*--------------------------- OPEN URL DIALOG BOX ---------------------------*/


// Non-modal Open URL dialog

//
// OpenURLDlgProc
//
BOOL FAR PASCAL OpenURLDlgProc( HWND hwnd, UINT message, UINT wParam, LONG lParam )
{
    switch( message )
    {
        case WM_INITDIALOG:
        {
            HWND combo;
            if (combo = GetDlgItem( hwnd, IDC_URLCOMBO ))
            {
                LONG retval;
                HKEY hkeyURLList;
                DWORD dwResult;

                retval = RegCreateKeyEx(APPREGKEY_ROOT,
                                        APPREGKEY_URLLIST,
                                        0,
                                        NULL,
                                        REG_OPTION_NON_VOLATILE,
                                        KEY_ALL_ACCESS,
                                        NULL,
                                        &hkeyURLList,
                                        &dwResult);


                if (retval != ERROR_SUCCESS)
                {
                    gui_criticalerror("RegCreateKeyEx failed: %d\n", retval);
                }
                else
                {
                    DWORD dwIndex;
                    char keyname[32];
                    char buffer[512];
                    DWORD dwType;
                    DWORD dwSize;

                    for (dwIndex = 0 ; dwIndex < URLS_TO_REMEMBER ; dwIndex++)
                    {
                        sprintf(keyname, "URL%d", dwIndex);

                        dwSize = sizeof(buffer);
                        retval = RegQueryValueEx( hkeyURLList,
                                                  keyname,
                                                  0,
                                                  &dwType,
                                                  buffer,
                                                  &dwSize );

                        if (retval != ERROR_SUCCESS)
                        {
                            break;
                        }
                        else
                        {
                            if (dwType == REG_SZ)
                            {
                                retval = SendMessage(combo, CB_ADDSTRING, (WPARAM)0, (LPARAM)buffer);

                                if (dwIndex == 0)
                                {
                                    retval = SendMessage(combo, CB_SETCURSEL, (WPARAM)dwIndex, 0);
                                }
                            }
                        }
                    }
                    RegCloseKey(hkeyURLList);
                }
            }
            return (LRESULT) 1;
        }

        case WM_COMMAND:
        {
            int my_argc;
            char *(my_argv[2]);

            char buffer[512];
            strcpy(buffer, "");

            if (wParam==IDOK)
            {
                HWND combo;
                int textlength;
                if (combo = GetDlgItem( hwndOpenURLDialog, IDC_URLCOMBO ))
                {
                    textlength = SendMessage(combo , WM_GETTEXTLENGTH, 0, 0);
                    if (textlength > sizeof(buffer)-1) textlength = sizeof(buffer)-1;
                    SendMessage(combo, WM_GETTEXT, (WPARAM)textlength+1, (LPARAM)buffer);
                }

                if ( strcmp(buffer, "") )
                {
                    HWND combo;
                    if (combo = GetDlgItem( hwndOpenURLDialog, IDC_URLCOMBO ))
                    {
                        LONG retval;
                        HKEY hkeyURLList;
                        DWORD dwResult;

                        retval = RegCreateKeyEx(APPREGKEY_ROOT,
                                                APPREGKEY_URLLIST,
                                                0,
                                                NULL,
                                                REG_OPTION_NON_VOLATILE,
                                                KEY_ALL_ACCESS,
                                                NULL,
                                                &hkeyURLList,
                                                &dwResult);

                        if (retval != ERROR_SUCCESS)
                        {
                            gui_criticalerror("RegCreateKeyEx failed: %d\n", retval);
                        }
                        else
                        {
                            DWORD dwIndex;
                            char keyname[32];
                            char buffer[512];

                            retval = SendMessage(combo, WM_GETTEXT, (WPARAM)sizeof(buffer), (LPARAM)buffer);
                            dwIndex = SendMessage(combo, CB_GETCURSEL, (WPARAM)0, (LPARAM)0);
                            if (dwIndex != CB_ERR)
                            {
                                retval = SendMessage(combo, CB_DELETESTRING, (WPARAM)dwIndex, (LPARAM)0);
                            }
                            retval = SendMessage(combo, CB_INSERTSTRING, (WPARAM)0, (LPARAM)buffer);

                            for (dwIndex = 0 ; dwIndex < URLS_TO_REMEMBER ; dwIndex++)
                            {
                                retval = SendMessage(combo, CB_GETLBTEXT, (WPARAM)dwIndex, (LPARAM)buffer);
                                if (retval == CB_ERR)
                                {
                                    break;
                                }

                                sprintf(keyname, "URL%d", dwIndex);

                                retval = RegSetValueEx( hkeyURLList,
                                                        keyname,
                                                        0,
                                                        REG_SZ,
                                                        buffer,
                                                        strlen(buffer)+1 );

                                if (retval != ERROR_SUCCESS)
                                {
                                    gui_criticalerror("RegSetValueEx failed: %d\n", retval);
                                }
                            }
                            RegCloseKey(hkeyURLList);
                        }
                    }

                    if (DestroyWindow( hwndOpenURLDialog ))
                        hwndOpenURLDialog = NULL;

                    my_argc = 2;

                    my_argv[0] = appVars.szProgramExecutable;
                    my_argv[1] = buffer;

                    main_function(my_argc, my_argv);
                }
            }

            if ( wParam==IDOK || wParam==IDCANCEL )
            {
                if (hwndOpenURLDialog != NULL)
                {
                    if (DestroyWindow( hwndOpenURLDialog ))
                        hwndOpenURLDialog = NULL;
                }
            }

            return (LRESULT) 1;
        }
    }
    return (LRESULT) 0;

} // OpenURLDlgProc


//
// DoOpenURLDialog
//
void DoOpenURLDialog( HINSTANCE hInstance, HANDLE hwnd )
{
    if (Transmitting == FALSE)
    {
        if (hwndOpenURLDialog == NULL)
        {
            hwndOpenURLDialog = CreateDialog( hInstance, MAKEINTRESOURCE( IDD_OPEN_URL ), hwnd, OpenURLDlgProc );
        }

        if (hwndOpenURLDialog != NULL)
        {
            ShowWindow(hwndOpenURLDialog, SW_SHOW);
            BringWindowToTop( hwndOpenURLDialog );
        }
    }
    else
    {
        PlayerMessageBox( IDS_TRANSMISSION_RUNNING );
    }

} // DoOpenURLDialog



/*------------------------ ENTER PASSWORD DIALOG BOX ------------------------*/


// Modal Enter Password dialog

//
// EnterPasswordDlgProc
//
BOOL FAR PASCAL EnterPasswordDlgProc( HWND hwnd, UINT message, UINT wParam, LONG lParam )
{
    switch( message )
    {
        case WM_INITDIALOG:
        {
            HWND edit;
            if (edit = GetDlgItem( hwnd, IDC_USERNAME_EDIT ))
            {
                SendMessage(edit, WM_SETTEXT, (WPARAM)0, (LPARAM)username_buffer);
            }
            if (edit = GetDlgItem( hwnd, IDC_PASSWORD_EDIT ))
            {
                SendMessage(edit, WM_SETTEXT, (WPARAM)0, (LPARAM)password_buffer);
            }
            return (LRESULT) 1;
        }

        case WM_COMMAND:
        {
            if (wParam==IDOK)
            {
                HWND edit;
                int textlength;
                if (edit = GetDlgItem( hwnd, IDC_USERNAME_EDIT ))
                {
                    textlength = SendMessage(edit , WM_GETTEXTLENGTH, 0, 0);
                    if (textlength > sizeof(username_buffer)-1) textlength = sizeof(username_buffer)-1;
                    SendMessage(edit, WM_GETTEXT, (WPARAM)textlength+1, (LPARAM)username_buffer);
                }
                if (edit = GetDlgItem( hwnd, IDC_PASSWORD_EDIT ))
                {
                    textlength = SendMessage(edit , WM_GETTEXTLENGTH, 0, 0);
                    if (textlength > sizeof(password_buffer)-1) textlength = sizeof(password_buffer)-1;
                    SendMessage(edit, WM_GETTEXT, (WPARAM)textlength+1, (LPARAM)password_buffer);

                }
            }

            if ( wParam==IDOK || wParam==IDCANCEL )
            {
                EndDialog(hwnd, wParam);
            }

            return (LRESULT) 1;
        }
    }
    return (LRESULT) 0;

} // EnterPasswordDlgProc


//
// DoEnterPasswordDialog
//
UINT DoEnterPasswordDialog( HINSTANCE hInstance, HANDLE hwnd )
{
    UINT ret;

    ret = DialogBox( hInstance, MAKEINTRESOURCE( IDD_ENTER_PASSWORD ), hwnd, EnterPasswordDlgProc );
    
    return ret;
} // DoEnterPasswordDialog



/*--------------------- PROXY CONFIGURATION DIALOG BOX ----------------------*/


// Modal Proxy Configuration dialog

//
// ProxyConfigurationDlgProc
//
BOOL FAR PASCAL ProxyConfigurationDlgProc( HWND hwnd, UINT message, UINT wParam, LONG lParam )
{
    char serverstring[512];
    unsigned int portnum;
    char *serverptr, *colonptr;
            
    switch( message )
    {
        case WM_INITDIALOG:
        {
            LONG retval;
            HKEY hkeyProxyServer;
            DWORD dwResult;
            HWND edit;

            retval = RegCreateKeyEx(APPREGKEY_ROOT,
                APPREGKEY_BASE,
                0,
                NULL,
                REG_OPTION_NON_VOLATILE,
                KEY_ALL_ACCESS,
                NULL,
                &hkeyProxyServer,
                &dwResult);
            
            if (retval != ERROR_SUCCESS)
            {
                gui_criticalerror("RegCreateKeyEx failed: %d\n", retval);
            }
            else
            {
                char buffer[512];
                DWORD dwType;
                DWORD dwSize;
                
                dwSize = sizeof(buffer);
                retval = RegQueryValueEx( hkeyProxyServer,
                    "ProxyServer",
                    0,
                    &dwType,
                    buffer,
                    &dwSize );
                
                if (retval == ERROR_SUCCESS)
                {
                    if (dwType == REG_SZ)
                    {
                        strncpy(proxy_buffer, buffer, sizeof(proxy_buffer)-1);
                        proxy_buffer[sizeof(proxy_buffer)-1] = 0;
                    }
                }
                RegCloseKey(hkeyProxyServer);
            }

            strncpy(serverstring, proxy_buffer, sizeof(serverstring)-1);
            serverstring[sizeof(serverstring)-1]=0;

            serverptr = serverstring;
            if (!strnicmp(serverptr, "http://", 7)) serverptr+=7;

            colonptr = strchr(serverptr, ':');
            if (colonptr == NULL)
            {
                colonptr = serverptr+strlen(serverptr);
            }
            else
            {
                *colonptr++ = '\0';
            }
            if (atol(colonptr) == 0) colonptr = "";

            if (edit = GetDlgItem( hwnd, IDC_PROXY_SERVER_EDIT ))
            {
                SendMessage(edit, WM_SETTEXT, (WPARAM)0, (LPARAM)serverstring);
            }
            if (edit = GetDlgItem( hwnd, IDC_PROXY_PORT_EDIT ))
            {
               SendMessage(edit, WM_SETTEXT, (WPARAM)0, (LPARAM)colonptr);
            }
            return (LRESULT) 1;
        }

        case WM_COMMAND:
        {
            if (wParam==IDOK)
            {
                LONG retval;
                HKEY hkeyProxyServer;
                DWORD dwResult;
                HWND edit;
                int textlength;

                if (edit = GetDlgItem( hwnd, IDC_PROXY_SERVER_EDIT ))
                {
                    textlength = SendMessage(edit , WM_GETTEXTLENGTH, 0, 0);
                    if (textlength > sizeof(serverstring)-1) textlength = sizeof(serverstring)-1;
                    SendMessage(edit, WM_GETTEXT, (WPARAM)textlength+1, (LPARAM)serverstring);
                }
                if (edit = GetDlgItem( hwnd, IDC_PROXY_PORT_EDIT ))
                {
                    unsigned char portstring[32];
                    textlength = SendMessage(edit , WM_GETTEXTLENGTH, 0, 0);
                    if (textlength > sizeof(portstring)-1) textlength = sizeof(portstring)-1;
                    SendMessage(edit, WM_GETTEXT, (WPARAM)textlength+1, (LPARAM)portstring);

                    portnum = atol(portstring);
                    if (portnum > 65535) portnum = 65535;
                    if (portnum == 0) portnum = 8080; /* default port number */
                }

                if (!strcmp(serverstring, ""))
                    strcpy(proxy_buffer, "");
                else
                    sprintf(proxy_buffer, "%s:%d", serverstring, portnum);

                retval = RegCreateKeyEx(APPREGKEY_ROOT,
                    APPREGKEY_BASE,
                    0,
                    NULL,
                    REG_OPTION_NON_VOLATILE,
                    KEY_ALL_ACCESS,
                    NULL,
                    &hkeyProxyServer,
                    &dwResult);
                
                if (retval != ERROR_SUCCESS)
                {
                    gui_criticalerror("RegCreateKeyEx failed: %d\n", retval);
                }
                else
                {
                    retval = RegSetValueEx( hkeyProxyServer,
                        "ProxyServer",
                        0,
                        REG_SZ,
                        proxy_buffer,
                        strlen(proxy_buffer)+1 );
                    
                    if (retval != ERROR_SUCCESS)
                    {
                        gui_criticalerror("RegSetValueEx failed: %d\n", retval);
                    }
                }
            }

            if ( wParam==IDOK || wParam==IDCANCEL )
            {
                EndDialog(hwnd, wParam);
            }

            return (LRESULT) 1;
        }
    }
    return (LRESULT) 0;

} // ProxyConfigurationDlgProc


//
// DoProxyConfigurationDialog
//
UINT DoProxyConfigurationDialog( HINSTANCE hInstance, HANDLE hwnd )
{
    UINT ret;

    ret = DialogBox( hInstance, MAKEINTRESOURCE( IDD_PROXY_SERVER ), hwnd, ProxyConfigurationDlgProc );
    
    return ret;
} // DoProxyConfigurationDialog



/*-------------------------- MEDIA STATE ROUTINES ---------------------------*/


// Current multimedia variables
static Media media;


//
// CanPreview
//
// Return true if we can preview the currently downloading stream
//
BOOL CanPreview()
{
    return ((!Previewing) && Transmitting && strcmp( StreamFilename, "" ));
} // CanPreview


//
// CanPlay
//
// Return true if we can go to a playing state from our current state
//
BOOL CanPlay()
{
    return (media.state == Stopped || media.state == Paused);

} // CanPlay


//
// CanStop
//
// Return true if we can go to a stopped state from our current state
//
BOOL CanStop()
{
    return (media.state == Playing || media.state == Paused);

} // CanStop


//
// CanPause
//
// Return true if we can go to a paused state from our current state
//
BOOL CanPause()
{
    return (media.state == Playing || media.state == Stopped);

} // CanPause


//
// IsInitialized
//
// Return true if we have loaded and initialized a multimedia file
//
BOOL IsInitialized()
{
    return (media.state != Uninitialized);

} // IsInitialized


//
// ChangeStateTo
//
void ChangeStateTo( State newState )
{
    if (!Transmitting)
    {
        switch (newState)
        {
        case Playing:
                gui_setstatus("Playing...");
                break;
        case Paused:
                gui_setstatus("Paused");
                break;
        case Stopped:
                gui_setstatus("Stopped");
                break;
        case Uninitialized:
                gui_setstatus("");
                break;
        }
    }

    media.state = newState;

    if (TrackbarTimer != 0)
    {
        KillTimer( appVars.hwndMainFrame, TrackbarTimer); TrackbarTimer = 0;
    }

    if (newState == Playing)
        TrackbarTimer = SetTimer( appVars.hwndMainFrame, ID_TRACKBARTIMER, 100, &TrackbarTimerFunc );

    // update the toolbar
    UpdateToolbar();

} // ChangeStateTo


//
// InitMedia
//
// Initialization
//
BOOL InitMedia()
{
    ChangeStateTo( Uninitialized );

    media.hGraphNotifyEvent = NULL;
    media.pGraph = NULL;

    return TRUE;

} // InitMedia


/*------------------------- GRAPH/MEDIA FUNCTIONS ---------------------------*/

//
// CreateFilterGraph
//
BOOL CreateFilterGraph()
{
    BOOL result = FALSE;

    IMediaEvent *pME;
    HRESULT hr;

    ASSERT(media.pGraph == NULL);

    SetBusy(TRUE);

    hr = CoCreateInstance(&CLSID_FilterGraph,           // CLSID of object
                          NULL,                         // Outer unknown
                          CLSCTX_INPROC_SERVER,         // Type of server
                          &IID_IGraphBuilder,           // Interface wanted
                          (void **) &media.pGraph);     // Returned object
    if (FAILED(hr))
    {
        media.pGraph = NULL;
    }
    else
    {
        // We use this to find out events sent by the filtergraph

        hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph,
                                                  &IID_IMediaEvent,
                                                  (void **) &pME);
        if (FAILED(hr))
        {
            DeleteContents();
        }
        else
        {
            hr = pME->lpVtbl->GetEventHandle(pME, (OAEVENT*) &media.hGraphNotifyEvent);
            pME->lpVtbl->Release( pME );

            if (FAILED(hr))
            {
                DeleteContents();
            }
            else
            {
                result = TRUE;
            }
        }
    }

    SetBusy(FALSE);

    return result;

} // CreateFilterGraph


// Destruction
//
// DeleteContents
//
void DeleteContents()
{
    if (media.pGraph != NULL)
    {
        HRESULT hr;
        IVideoWindow   *pVW;

        SetBusy(TRUE);

        /* check if this graph comes with a video window */
        hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IVideoWindow, (void**) &pVW);
        if (SUCCEEDED(hr))
        {
            long left, top, width, height;

            /* remember video window position */
            hr = pVW->lpVtbl->GetRestorePosition(pVW, &left, &top, &width, &height);
            if (SUCCEEDED(hr))
            {
                IVWWindowLeft   = left;
                IVWWindowTop    = top;
                IVWWindowWidth  = width;
                IVWWindowHeight = height;
            }
            pVW->lpVtbl->Release(pVW);
        }

        media.pGraph->lpVtbl->Release( media.pGraph );
        media.pGraph = NULL;

        SetBusy(FALSE);
    }

    // this event is owned by the filter graph and is thus invalid
    media.hGraphNotifyEvent = NULL;

    Previewing = FALSE;

    IMPTotalTime = 0.0;
    IMPCurrentPosition = 0.0;

    IMSTotalTime = 0;
    IMSCurrentPosition = 0;
    IMSCapabilities = 0;

    SetStatusText(STAT_TIMECODE, 0, -1, "");

    if (hwndTrackbar)
    {
        SendMessage( hwndTrackbar, TBM_SETPOS, TRUE, 0);
        EnableWindow(hwndTrackbar, FALSE);
    }

    ChangeStateTo( Uninitialized );

} // DeleteContents


//
// RenderFile
//
BOOL RenderFile( LPSTR szFileName )
{
    BOOL result = FALSE;

    HRESULT hr;
    WCHAR wPath[MAX_PATH];

    SetBusy(TRUE);

    DeleteContents();

    if (!Transmitting) gui_setstatus("Preparing to play...");

    if ( !CreateFilterGraph() )
    {
        PlayerMessageBox( IDS_CANT_INIT_QUARTZ );
    }
    else
    {
        MultiByteToWideChar( CP_ACP, 0, szFileName, -1, wPath, MAX_PATH );

        hr = media.pGraph->lpVtbl->RenderFile(media.pGraph, wPath, NULL);

        if (FAILED( hr ))
        {
            PlayerMessageBox( IDS_CANT_RENDER_FILE );
        }
        else
        {
            IMediaPosition *pMP;
            IMediaSeeking  *pMS;
            IVideoWindow   *pVW;

            IMPTotalTime = 0;
            IMPCurrentPosition = 0;

            hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph,
                                                      &IID_IMediaPosition,
                                                      (void**) &pMP);
            if (SUCCEEDED(hr))
            {
                REFTIME tLength;
                hr = pMP->lpVtbl->get_Duration(pMP, &tLength);
                if (SUCCEEDED(hr))
                {
                    LONG lBackward;
                    LONG lForward;

                    IMPTotalTime = tLength;

                    hr = pMP->lpVtbl->CanSeekBackward(pMP, &lBackward);
                    if (SUCCEEDED(hr))
                    {
                        hr = pMP->lpVtbl->CanSeekBackward(pMP, &lForward);
                        if (SUCCEEDED(hr))
                        {
//                            if (lBackward == OATRUE && lForward == OATRUE)
                            {
                                if (hwndTrackbar)
                                    EnableWindow(hwndTrackbar, TRUE);
                            }
                        }
                    }
                }
                pMP->lpVtbl->Release(pMP);
            }

            IMSTotalTime = 0;
            IMSCurrentPosition = 0;
            IMSCapabilities = 0;

            hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph,
                                                      &IID_IMediaSeeking,
                                                      (void**) &pMS);
            if (SUCCEEDED(hr))
            {
                LONGLONG llDuration;

                hr = pMS->lpVtbl->GetDuration(pMS, &llDuration);
                if (SUCCEEDED(hr))
                {
                    DWORD dwCapabilities = 0;

                    IMSTotalTime = llDuration;

                    hr = pMS->lpVtbl->GetCapabilities(pMS, &dwCapabilities);
                    if (SUCCEEDED(hr))
                    {
                      //if (dwCapabilities & AM_SEEKING_CanSeekAbsolute)  gui_criticalerror("AM_SEEKING_CanSeekAbsolute");
                      //if (dwCapabilities & AM_SEEKING_CanSeekForwards)  gui_criticalerror("AM_SEEKING_CanSeekForwards");
                      //if (dwCapabilities & AM_SEEKING_CanSeekBackwards) gui_criticalerror("AM_SEEKING_CanSeekBackwards");
                      //if (dwCapabilities & AM_SEEKING_CanGetCurrentPos) gui_criticalerror("AM_SEEKING_CanGetCurrentPos");
                      //if (dwCapabilities & AM_SEEKING_CanGetStopPos)    gui_criticalerror("AM_SEEKING_CanGetStopPos");
                      //if (dwCapabilities & AM_SEEKING_CanGetDuration)   gui_criticalerror("AM_SEEKING_CanGetDuration");
                      //if (dwCapabilities & AM_SEEKING_CanPlayBackwards) gui_criticalerror("AM_SEEKING_CanPlayBackwards");
                      //if (dwCapabilities & AM_SEEKING_CanDoSegments)    gui_criticalerror("AM_SEEKING_CanDoSegments");
                      //if (dwCapabilities & AM_SEEKING_Source)           gui_criticalerror("AM_SEEKING_Source");

                        IMSCapabilities = dwCapabilities;
                    }
                }

                pMS->lpVtbl->Release(pMS);
            }

            /* check if this graph comes with a video window */
            hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph,
                                                      &IID_IVideoWindow,
                                                      (void**) &pVW);
            if (SUCCEEDED(hr))
            {
                BSTR title;
                
                if ((title = SysAllocString(wPath)) != NULL)
                {
                    hr = pVW->lpVtbl->put_Caption(pVW,title);

                    SysFreeString(title);
                }

                if (IVWWindowWidth > 0 && IVWWindowHeight > 0)
                {
                    long left, top, width, height;
                    left   = IVWWindowLeft;
                    top    = IVWWindowTop;
                    width  = IVWWindowWidth;
                    height = IVWWindowHeight;

                    hr = pVW->lpVtbl->SetWindowPosition(pVW, left, top, width, height);
                }

                pVW->lpVtbl->Release(pVW);
            }

            /* in any case, we got a filter graph, so return TRUE */
            result = TRUE;

        }
    }

    SetBusy(FALSE);

    return result;

} // RenderFile


//
// SetTitle
//
// Update the title of the video renderer to "Player - szFile"
//
void SetTitle( HWND hwnd, char *szFile )
{
    char szNewTitle[ _MAX_FNAME + _MAX_EXT  + 20 ];

    strcpy( szNewTitle, APP_NAME );
    strcat( szNewTitle, " - " );
    strcat( szNewTitle, szFile );

    // Update the window's title
    SetWindowText( hwnd, szNewTitle );

} // SetTitle


//
// OpenMediaFile
//
// File..Open has been selected
//
BOOL OpenMediaFile( HWND hwnd, LPSTR szFile )
{
    BOOL result = FALSE;

    static char szFileName[ _MAX_PATH ];
    static char szTitleName[ _MAX_FNAME + _MAX_EXT ];

    if( szFile != NULL )
    {
        if ( RenderFile( szFile ) )
        {
            LPSTR szTitle;

            // Work out the full path name and the file name from the
            // specified file
            GetFullPathName( szFile, _MAX_PATH, szFileName, &szTitle );
            strncpy( szTitleName, szTitle, _MAX_FNAME + _MAX_EXT );
            szTitleName[ _MAX_FNAME + _MAX_EXT -1 ] = '\0';

            // Set the main window title and update the state
            if (!Transmitting) SetTitle( hwnd, szTitleName );
            ChangeStateTo( Stopped );
            result = TRUE;
        }
        else
            if (!Transmitting) gui_seterror("Unable to play file!");
    }
    else
    {
        if( DoFileOpenDialog( hwnd, szFileName, szTitleName ) )
        {
            if (RenderFile( szFileName ) )
            {
                // Set the main window title and update the state
                if (!Transmitting) SetTitle( hwnd, szTitleName );
                ChangeStateTo( Stopped );
                result = TRUE;
            }
            else
                if (!Transmitting) gui_seterror("Unable to play file!");

        }
    }
    return result;

} // OpenMediaFile


//
// GetCurrentPosition
//

BOOL GetCurrentPosition(REFTIME *rtcur)
{
    BOOL result = FALSE;

    if (IsInitialized())
    {
        HRESULT hr;
        IMediaPosition *pMP;
        REFTIME tCurrent;

        hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph,
            &IID_IMediaPosition,
            (void**) &pMP);
        if (SUCCEEDED(hr))
        {
            hr = pMP->lpVtbl->get_CurrentPosition(pMP, &tCurrent);
            if (SUCCEEDED(hr))
            {
                *rtcur = tCurrent;
                result = TRUE;
            }
            pMP->lpVtbl->Release(pMP);
        }
    }

    return result;
} // GetCurrentPosition


//
// GetStopPosition
//

BOOL GetStopPosition(REFTIME *rtstop, LONGLONG *llstop)
{
    BOOL result = FALSE;

    if (Previewing)
    {
        HRESULT hr;
        IMediaPosition *pMP;
        IMediaSeeking *pMS;

        // Obtain the IMediaPosition interface to our filter graph
        hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IMediaPosition, (void **) &pMP);
        if( SUCCEEDED(hr) )
        {
            pMP->lpVtbl->get_StopTime(pMP, rtstop);
            if ( SUCCEEDED(hr) )
            {
                result = TRUE;
            }
            pMP->lpVtbl->Release(pMP);
        }

        /* this one works as an alternative to the above */
        if (TRUE)
        {
            // Obtain the IMediaSeeking interface to our filter graph
            hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IMediaSeeking, (void **) &pMS);
            if( SUCCEEDED(hr) )
            {
                /* Replay should stop at the current download position */
                hr = pMS->lpVtbl->GetStopPosition(pMS, llstop);
                if ( SUCCEEDED(hr) )
                {
                    result = TRUE;
                }

                pMS->lpVtbl->Release(pMS);
            }

        }
    }

    return result;

} // GetStopPosition


//
// SetStopPosition
//

BOOL SetStopPosition(int milliseconds)
{
    BOOL result = FALSE;

    if (Previewing)
    {
        HRESULT hr;
        IMediaPosition *pMP;
        IMediaSeeking *pMS;

        // Obtain the IMediaPosition interface to our filter graph
        hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IMediaPosition, (void **) &pMP);
        if( SUCCEEDED(hr) )
        {
            pMP->lpVtbl->put_StopTime(pMP, (REFTIME)milliseconds/1000);
            if ( SUCCEEDED(hr) )
            {
                result = TRUE;
            }
            pMP->lpVtbl->Release(pMP);
        }

        /* this one works as an alternative to the above */
        if (result == FALSE)
        {
            // Obtain the IMediaSeeking interface to our filter graph
            hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IMediaSeeking, (void **) &pMS);
            if( SUCCEEDED(hr) )
            {
                LONGLONG llCurrent = 0;
                LONGLONG llStop = 0;
                DWORD dwCurrentFlags = AM_SEEKING_NoPositioning;
                DWORD dwStopFlags = AM_SEEKING_AbsolutePositioning;

                /* Replay should stop at the current download position */
                llStop = (LONGLONG)(milliseconds * 10000);
                hr = pMS->lpVtbl->SetPositions(pMS,
                    &llCurrent,
                    dwCurrentFlags,
                    &llStop,
                    dwStopFlags );
                if ( hr == NOERROR )
                {
                    result = TRUE;
                }

                pMS->lpVtbl->Release(pMS);
            }

        }
    }

    return result;

} // SetStopPosition


//
// SetCurrentPosition
//

BOOL SetCurrentPosition(REFTIME position)
{
    BOOL result = FALSE;

    if( IsInitialized() )
    {
        HRESULT hr;
        IMediaPosition *pMP;

        SetBusy ( TRUE );

        // Obtain the interface to our filter graph
        hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IMediaPosition, (void **) &pMP);
        if( SUCCEEDED(hr) )
        {
            pMP->lpVtbl->put_CurrentPosition(pMP, position);
            result = TRUE;
            pMP->lpVtbl->Release(pMP);
        }

        if (result == FALSE)
            // Inform the user that an error occurred
            PlayerMessageBox( IDS_CANT_SEEK );

        SetBusy ( FALSE );
    }

    return result;

} // SetCurrentPosition


//
// OnMediaSeeking
//
BOOL OnMediaSeeking(BOOL Enabled)
{
    BOOL result = FALSE;

    HRESULT hr;
    IMediaControl *pMC;

    // Obtain the interface to our filter graph
    hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph,
        &IID_IMediaControl,
        (void **) &pMC);

    if( SUCCEEDED(hr) )
    {
        result = TRUE;

        if (Enabled)
        {
            if (!Transmitting) gui_setstatus("Seeking...");

            if (media.state == Playing)
            {
                hr = pMC->lpVtbl->Pause( pMC );
            }
        }
        else
        {
            if (media.state == Playing)
            {
                hr = pMC->lpVtbl->Run( pMC );
            }

            ChangeStateTo(media.state);

        }

        pMC->lpVtbl->Release( pMC );

    }

    return result;

} // OnMediaSeeking


//
// OnMediaPlay
//

BOOL OnMediaPlay()
{
    BOOL result = FALSE;

    if( CanPlay() )
    {
        HRESULT hr;
        IVideoWindow  *pVW;
        IMediaControl *pMC;

        SetBusy ( TRUE );

        /* check if this graph comes with a video window */
        hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph, &IID_IVideoWindow, (void**) &pVW);
        if (SUCCEEDED(hr))
        {
            long windowstate;

            /* remember video window position */
            hr = pVW->lpVtbl->get_WindowState(pVW, &windowstate);
            if (SUCCEEDED(hr))
            {
                /* show the window if it was hidden/miminized */
                if (windowstate == SW_MINIMIZE || windowstate == SW_HIDE ||
                    windowstate == SW_SHOWMINNOACTIVE                     )
                {
                    windowstate = SW_SHOWNORMAL;
                    hr = pVW->lpVtbl->put_WindowState(pVW, windowstate);
                }
                
            }
            pVW->lpVtbl->Release(pVW);
        }

        if (Previewing)
        {
            REFTIME rtstoppos;
            LONGLONG llstoppos;
            REFTIME rtcurrent;

            if (GetStopPosition(&rtstoppos, &llstoppos))
            {
                if (GetCurrentPosition(&rtcurrent))
                {
                    /* the filter graph thinks it has reached the end of the stream */
                    /* because it is truncated and we are still downloading it!     */
                    if (rtcurrent == rtstoppos)
                    {
                        /* so in this case we have to reposition and try again */
                        /* otherwise the filter graph won't continue to play   */
                        SetCurrentPosition( IMPCurrentPosition );
                    }
                }
            }
        }

        // Obtain the interface to our filter graph
        hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph,
                                                  &IID_IMediaControl,
                                                  (void **) &pMC);

        if( SUCCEEDED(hr) )
        {
            // Ask the filter graph to play and release the interface
            hr = pMC->lpVtbl->Run( pMC );
            if( SUCCEEDED(hr) )
            {
                ChangeStateTo( Playing );
                result = TRUE;
            }
            pMC->lpVtbl->Release( pMC );
        }

        if (result == FALSE)
            // Inform the user that an error occurred
            PlayerMessageBox( IDS_CANT_PLAY );

        SetBusy ( FALSE );
    }

    return result;

} // OnMediaPlay


//
// OnMediaPause
//
BOOL OnMediaPause()
{
    BOOL result = FALSE;

    if( CanPause() )
    {
        HRESULT hr;
        IMediaControl *pMC;

        SetBusy(TRUE);

        // Obtain the interface to our filter graph
        hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph,
                                                  &IID_IMediaControl,
                                                  (void **) &pMC);

        if( SUCCEEDED(hr) )
        {
            // Ask the filter graph to pause and release the interface
            hr = pMC->lpVtbl->Pause( pMC );
            if( SUCCEEDED(hr) )
            {
                ChangeStateTo( Paused );
                result = TRUE;
            }

            pMC->lpVtbl->Release( pMC );

        }

        if (result == FALSE)
            // Inform the user that an error occurred
            PlayerMessageBox( IDS_CANT_PAUSE );

        SetBusy(FALSE);
    }

    return result;

} // OnMediaPause


//
// OnMediaStop
//
BOOL OnMediaStop(BOOL reallystop, BOOL rewind)
{
    BOOL result = FALSE;

    if( CanStop() )
    {
        HRESULT hr;
        IMediaControl *pMC;

        SetBusy(TRUE);

        if (!Transmitting) gui_setstatus("Stopping replay...");

        // Obtain the interface to our filter graph
        hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph,
                                                  &IID_IMediaControl,
                                                  (void **) &pMC);
        if( SUCCEEDED(hr) )
        {
            IMediaPosition * pMP;

            if (reallystop)
                // now really do the stop
                pMC->lpVtbl->Stop( pMC );
            else
                // just simulate a stop using pause (faster)
                pMC->lpVtbl->Pause( pMC );

            if( SUCCEEDED(hr) )
            {
                ChangeStateTo( Stopped );
                result = TRUE;
            }

            if (rewind == TRUE)
            {
                hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph,
                                                          &IID_IMediaPosition,
                                                          (void**) &pMP);
                if (SUCCEEDED(hr))
                {
                    hr = pMP->lpVtbl->put_CurrentPosition(pMP, 0);
                    if (SUCCEEDED(hr))
                    {
                        SetStatusText(STAT_TIMECODE, 0, -1, "");

                        if (hwndTrackbar != NULL)
                            SendMessage( hwndTrackbar, TBM_SETPOS, TRUE, 0);
                    }

                    pMP->lpVtbl->Release(pMP);
                }
            }
            pMC->lpVtbl->Release( pMC );
        }

        if (result == FALSE)
            // Inform the user that an error occurred
            PlayerMessageBox( IDS_CANT_STOP );

        SetBusy(FALSE);
    }

    return result;

} // OnMediaStop


//
// GetGraphEventHandle
//
// We use this to check for graph events
//
HANDLE GetGraphEventHandle()
{
    return media.hGraphNotifyEvent;

} // GetGraphEventHandle


//
// OnGraphNotify
//
// If the event handle is valid, then ask the graph if
// anything has happened (eg the graph has stopped...)
//
void OnGraphNotify()
{
    IMediaEvent *pME;
    long lEventCode, lParam1, lParam2;

    ASSERT( media.hGraphNotifyEvent != NULL );

    if( SUCCEEDED(media.pGraph->lpVtbl->QueryInterface(media.pGraph,
                                                       &IID_IMediaEvent,
                                                       (void **) &pME)))
    {
        if( SUCCEEDED(pME->lpVtbl->GetEvent(pME, &lEventCode, &lParam1, &lParam2, 0)))
        {
            if (lEventCode == EC_COMPLETE)
            {
                OnMediaStop(FALSE, FALSE);
                if (!Transmitting) gui_setstatus("Replay complete");
            }
            if (lEventCode == EC_USERABORT)
            {
                OnMediaStop(TRUE, TRUE);
                if (!Transmitting) gui_setstatus("User aborted replay");
            }
            if (lEventCode == EC_ERRORABORT)
            {
                OnMediaStop(TRUE, TRUE);
                if (!Transmitting) gui_setstatus("Error during replay");
            }
        }
        pME->lpVtbl->Release( pME );
    }

} // OnGraphNotify


/*---------------------------- FILE OPEN DIALOG -----------------------------*/


// This handles the file..open dialog box

static OPENFILENAME ofn;

//
// InitFileOpenDialog
//
BOOL InitFileOpenDialog( HWND hwnd )
{
    ofn.lStructSize         = sizeof( OPENFILENAME );
    ofn.hwndOwner           = hwnd;
    ofn.hInstance           = NULL;
    ofn.lpstrFilter         = "Windows Media files (*.asf,*.wma,*.wmv)\0*.asf; *.wma; *.wmv\0"
                              "MPEG audio files (*.mp2,*.mp3)\0*.mp2; *.mp3;\0"
                              "MPEG video files (*.mpg,*.mpe,*.mpeg)\0*.mpg; *.mpe; *.mpeg\0"
                              "AVI files (*.avi)\0*.avi\0"
                              "Quick Time files (*.mov)\0*.mov\0"
                              "Wave audio files (*.wav)\0*.wav\0"
                              "Filter graph files (*.grf)\0*.grf\0"
                              "All Files (*.*)\0*.*\0\0";
    ofn.lpstrCustomFilter   = NULL;
    ofn.nMaxCustFilter      = 0;
    ofn.nFilterIndex        = 0;
    ofn.lpstrFile           = NULL;
    ofn.nMaxFile            = _MAX_PATH;
    ofn.lpstrFileTitle      = NULL;
    ofn.nMaxFileTitle       = _MAX_FNAME + _MAX_EXT;
    ofn.lpstrInitialDir     = NULL;
    ofn.lpstrTitle          = NULL;
    ofn.Flags               = OFN_EXPLORER | OFN_FILEMUSTEXIST;
    ofn.nFileOffset         = 0;
    ofn.nFileExtension      = 0;
    ofn.lpstrDefExt         = "asf";
    ofn.lCustData           = 0;
    ofn.lpfnHook            = NULL;
    ofn.lpTemplateName      = NULL;

    return TRUE;

} // InitFileOpenDialog


//
// DoFileOpenDialog
//
BOOL DoFileOpenDialog( HWND hwnd, LPSTR lpstrFileName, LPSTR lpstrTitleName )
{
    // Called to display the open file dialog
    ofn.hwndOwner = hwnd;
    ofn.lpstrFile = lpstrFileName;
    ofn.lpstrFileTitle = lpstrTitleName;

    ShowWindow( appVars.hwndMainFrame, SW_SHOWNORMAL );
    SetWindowPos(appVars.hwndMainFrame, HWND_TOP, 0, 0, 0, 0,
                 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);

    return GetOpenFileName( &ofn );

} // DoFileOpenDialog



/*---------------------------- FILE SAVE DIALOG -----------------------------*/


// This handles the file..open dialog box

static OPENFILENAME sfn;

//
// InitFileSaveDialog
//
BOOL InitFileSaveDialog( HWND hwnd )
{
    sfn.lStructSize         = sizeof( OPENFILENAME );
    sfn.hwndOwner           = hwnd;
    sfn.hInstance           = NULL;
    sfn.lpstrFilter         = "Windows Media files (*.asf,*.wma,*.wmv)\0*.asf; *.wma; *.wmv\0"
                              "All Files (*.*)\0*.*\0\0";
    sfn.lpstrCustomFilter   = NULL;
    sfn.nMaxCustFilter      = 0;
    sfn.nFilterIndex        = 0;
    sfn.lpstrFile           = NULL;
    sfn.nMaxFile            = _MAX_PATH;
    sfn.lpstrFileTitle      = NULL;
    sfn.nMaxFileTitle       = _MAX_FNAME + _MAX_EXT;
    sfn.lpstrInitialDir     = NULL;
    sfn.lpstrTitle          = NULL;
    sfn.Flags               = OFN_EXPLORER ;
    sfn.nFileOffset         = 0;
    sfn.nFileExtension      = 0;
    sfn.lpstrDefExt         = "asf";
    sfn.lCustData           = 0;
    sfn.lpfnHook            = NULL;
    sfn.lpTemplateName      = NULL;

    return TRUE;

} // InitFileSaveDialog


//
// DoFileSaveDialog
//
BOOL DoFileSaveDialog( HWND hwnd, LPSTR lpstrFileName, LPSTR lpstrTitleName )
{
    // Called to display the open file dialog
    sfn.hwndOwner = hwnd;
    sfn.lpstrFile = lpstrFileName;
    sfn.lpstrFileTitle = lpstrTitleName;

    ShowWindow( appVars.hwndMainFrame, SW_SHOWNORMAL );
    SetWindowPos(appVars.hwndMainFrame, HWND_TOP, 0, 0, 0, 0,
                 SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);

    return GetSaveFileName( &sfn );

} // DoFileSaveDialog



/*----------------------------- TOOL BAR CODE -------------------------------*/


// Constants for the bitmap
const int nButtonImageWidth = 46;
const int nButtonImageHeight = 36;

// Constants for the toolbar implementation
const int nSeperatorGap = 6;
const int nToolbarBorderWidth = 5;
const int nToolbarBorderHeight = 3;

//
// CalcToolbarSize
//
void CalcToolbarSize( SIZE *pSize )
{
    // Calculate the area required for this toolbar

    // size for 3 buttons, 2 borders and a seperator
    // ...but we'll add on a some extra seperators for good measure
    pSize->cx = (nButtonImageWidth) * 5 + nSeperatorGap + 2 * nToolbarBorderWidth;

    // size for 1 button and 2 borders
    pSize->cy = nButtonImageHeight + 2 * nToolbarBorderHeight;

} // CalcToolbarSize


//
// UpdateToolbar
//
// Maintains the enabled/disabled state of the buttons - we should be
// called periodically and/or whenever there is a change of graph state
//
void UpdateToolbar()
{
    if (toolbar.hwndPlayButton != NULL)
        EnableWindow( toolbar.hwndPlayButton, CanPlay() );
    if (toolbar.hwndPauseButton != NULL)
        EnableWindow( toolbar.hwndPauseButton, CanPause() );
    if (toolbar.hwndStopButton != NULL)
        EnableWindow( toolbar.hwndStopButton, CanStop() );
    if (toolbar.hwndPreviewButton != NULL)
        EnableWindow( toolbar.hwndPreviewButton, CanPreview() );
    if (toolbar.hwndCancelButton != NULL)
        EnableWindow( toolbar.hwndCancelButton, Transmitting );

} // UpdateToolbar


//
// ToolbarButtonProc
//
// Subclassed window procedure for the toolbar buttons
//

long FAR PASCAL ToolbarButtonProc( HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
    WNDPROC oldwndproc = (WNDPROC)GetWindowLong( hwnd, GWL_USERDATA );

    switch(message)
    {
        case WM_ERASEBKGND:
        {
            /* no nothing */
        }
        return (LRESULT) 1;
        break;

        default:
            return CallWindowProc(oldwndproc, hwnd, message, wParam, lParam);
    }
    return (LRESULT) 0;

} // MainFrameProc

//
// InitToolbar
//
// Create the controls for the buttons
//
BOOL InitToolbar( HINSTANCE hInstance, HWND hwnd )
{
    LONG oldwndproc;
    int x;      // Position of the next button

    x = nToolbarBorderWidth;

    // The 'Play' button
    toolbar.hwndPlayButton = CreateWindow( "BUTTON",
                                           NULL,
                                           BS_OWNERDRAW | WS_VISIBLE | WS_CHILD,
                                           x,
                                           nToolbarBorderHeight,
                                           nButtonImageWidth,
                                           nButtonImageHeight,
                                           hwnd,
                                           (HMENU) ID_MEDIA_PLAY,
                                           hInstance,
                                           NULL);

    oldwndproc = SetWindowLong( toolbar.hwndPlayButton, GWL_WNDPROC, (DWORD)&ToolbarButtonProc);
    SetWindowLong(toolbar.hwndPlayButton, GWL_USERDATA, oldwndproc);

    x += nButtonImageWidth;

    // The 'Pause' button
    toolbar.hwndPauseButton = CreateWindow( "BUTTON",
                                            NULL,
                                            BS_OWNERDRAW | WS_VISIBLE | WS_CHILD,
                                            x,
                                            nToolbarBorderHeight,
                                            nButtonImageWidth,
                                            nButtonImageHeight,
                                            hwnd,
                                            (HMENU) ID_MEDIA_PAUSE,
                                            hInstance,
                                            NULL);

    oldwndproc = SetWindowLong( toolbar.hwndPauseButton, GWL_WNDPROC, (DWORD)&ToolbarButtonProc);
    SetWindowLong(toolbar.hwndPauseButton, GWL_USERDATA, oldwndproc);

    x += nButtonImageWidth;

    // The 'Stop' button
    toolbar.hwndStopButton = CreateWindow( "BUTTON",
                                           NULL,
                                           BS_OWNERDRAW | WS_VISIBLE | WS_CHILD,
                                           x,
                                           nToolbarBorderHeight,
                                           nButtonImageWidth,
                                           nButtonImageHeight,
                                           hwnd,
                                           (HMENU) ID_MEDIA_STOP,
                                           hInstance,
                                           NULL);

    oldwndproc = SetWindowLong( toolbar.hwndStopButton, GWL_WNDPROC, (DWORD)&ToolbarButtonProc);
    SetWindowLong(toolbar.hwndStopButton, GWL_USERDATA, oldwndproc);

    x += nButtonImageWidth + nSeperatorGap;

    // The 'Preview' button
    toolbar.hwndPreviewButton = CreateWindow( "BUTTON",
                                           NULL,
                                           BS_OWNERDRAW | WS_VISIBLE | WS_CHILD,
                                           x,
                                           nToolbarBorderHeight,
                                           nButtonImageWidth,
                                           nButtonImageHeight,
                                           hwnd,
                                           (HMENU) ID_STREAM_PREVIEW,
                                           hInstance,
                                           NULL);

    oldwndproc = SetWindowLong( toolbar.hwndPreviewButton, GWL_WNDPROC, (DWORD)&ToolbarButtonProc);
    SetWindowLong(toolbar.hwndPreviewButton, GWL_USERDATA, oldwndproc);

    x += nButtonImageWidth;

    // The 'Cancel' button
    toolbar.hwndCancelButton = CreateWindow( "BUTTON",
                                           NULL,
                                           BS_OWNERDRAW | WS_VISIBLE | WS_CHILD,
                                           x,
                                           nToolbarBorderHeight,
                                           nButtonImageWidth,
                                           nButtonImageHeight,
                                           hwnd,
                                           (HMENU) ID_STREAM_CANCEL,
                                           hInstance,
                                           NULL);

    oldwndproc = SetWindowLong( toolbar.hwndCancelButton, GWL_WNDPROC, (DWORD)&ToolbarButtonProc);
    SetWindowLong(toolbar.hwndCancelButton, GWL_USERDATA, oldwndproc);

    // We don't call UpdateToolbar to set the button states as
    // the multimedia variables may not have been initialized yet
    return TRUE;

} // InitToolbar


//
// DrawButton
//
// Called by the main window whenever a button needs to be redrawn
//
void DrawButton( HINSTANCE hInstance, DRAWITEMSTRUCT FAR * lpDrawItem )
{
    HDC hSourceDC = CreateCompatibleDC( NULL );
    HGDIOBJ loadedbitmap;
    HGDIOBJ hgdiOldBitmap;
    int nIndex;

    // Load the IDR_TOOLBAR_INACTIVE bitmap into our source hDC if the
    // button is disabled otherwise load the IDR_TOOLBAR_PRESSED/NOTPRESSED bitmap
    if( lpDrawItem->itemState & ODS_DISABLED )
        loadedbitmap = (HGDIOBJ) LoadImage( hInstance, MAKEINTRESOURCE( IDB_TOOLBAR_INACTIVE ), IMAGE_BITMAP, 0,0, LR_DEFAULTCOLOR );
    else
    {
        if( lpDrawItem->itemState & ODS_SELECTED )
            loadedbitmap = (HGDIOBJ) LoadImage( hInstance, MAKEINTRESOURCE( IDB_TOOLBAR_PRESSED ), IMAGE_BITMAP, 0,0, LR_DEFAULTCOLOR );
        else
            loadedbitmap = (HGDIOBJ) LoadImage( hInstance, MAKEINTRESOURCE( IDB_TOOLBAR_NOTPRESSED ), IMAGE_BITMAP, 0,0, LR_DEFAULTCOLOR );
    }
    hgdiOldBitmap = SelectObject( hSourceDC, loadedbitmap );

    // Decide which button to blit to the display
    switch( lpDrawItem->CtlID ){
        case ID_MEDIA_PLAY:
            nIndex = 0;
            break;

        case ID_MEDIA_PAUSE:
            nIndex = 1;
            break;

        case ID_MEDIA_STOP:
            nIndex = 2;
            break;

        case ID_STREAM_PREVIEW:
            nIndex = 3;
            break;

        case ID_STREAM_CANCEL:
            nIndex = 4;
            break;
    }

    // ..and blit it
    BitBlt( lpDrawItem->hDC,
            lpDrawItem->rcItem.left,
            lpDrawItem->rcItem.top ,
            nButtonImageWidth,
            nButtonImageHeight,
            hSourceDC,
            nIndex * nButtonImageWidth + 2,
            0,
            SRCCOPY
          );

    // Restore the original bitmap
    SelectObject( hSourceDC, hgdiOldBitmap );

    // this was missing from the Microsoft example code
    DeleteObject(loadedbitmap);
    DeleteDC(hSourceDC);
} // DrawButton


/*---------------------------- TRACK BAR CODE -------------------------------*/

const int nTrackbarBorderWidth = 5;
const int nTrackbarBorderHeight = 11;

//
// CalcTrackbarSize
//
// Calculate the size of the trackbar (including borders)
//

void CalcTrackbarSize ( SIZE *pSize )
{
    RECT clientRect;

    SIZE sizeToolbar;

    GetClientRect( appVars.hwndMainFrame, &clientRect );
    CalcToolbarSize( &sizeToolbar );

    pSize->cx = (clientRect.right-clientRect.left+1) - sizeToolbar.cx;
    pSize->cy = sizeToolbar.cy;

} // CalcTrackbarSize


//
// TrackbarProc
//
// Subclassed window procedure for the trackbar
//

long FAR PASCAL TrackbarProc( HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
    WNDPROC oldwndproc = (WNDPROC)GetWindowLong( hwnd, GWL_USERDATA );

    switch(message)
    {
        /* This modifies the default behaviour of the trackbar.     */
        /* When clicking inside the channel, the click position     */
        /* is converted to a slider position value and stored in    */
        /* positionTrackbar                                         */

        /* this enables us to move the slider to this position when */
        /* we receive a WM_NOTIFY / NM_RELEASEDCAPTURE message      */

    case WM_LBUTTONDOWN:
        {
            UINT fwKeys = wParam;        // key flags
            if (fwKeys == MK_LBUTTON)
            {
                int xPos = LOWORD(lParam);  // horizontal position of cursor
                int yPos = HIWORD(lParam);  // vertical position of cursor
                RECT chanrect;
                LONG chanwidth;

                SendMessage(hwndTrackbar, TBM_GETCHANNELRECT, 0, (LPARAM) &chanrect);
                chanwidth = chanrect.right - chanrect.left + 1;

                if (xPos >= chanrect.left && xPos <= chanrect.right)
                {
                    UINT min, max;
                    min = SendMessage(hwndTrackbar, TBM_GETRANGEMIN, 0, 0);
                    max = SendMessage(hwndTrackbar, TBM_GETRANGEMAX, 0, 0);

                    positionTrackbar = min + ((max-min)*(xPos-chanrect.left))/chanwidth;
                }
            }
            return CallWindowProc(oldwndproc, hwnd, message, wParam, lParam);
        }
        break;

    case WM_ERASEBKGND:
        {
            /* no nothing */
        }
        return (LRESULT) 1;
        break;

        default:
            return CallWindowProc(oldwndproc, hwnd, message, wParam, lParam);
    }
    return (LRESULT) 0;

} // TrackbarProc


//
// AdjustTrackbar
//
// Reposition the trackbar control in the main frame
//

void AdjustTrackbar ( HWND hwndTrack, HWND hwndParent )
{
    int x,y,w,h;

    SIZE sizeToolbar;
    SIZE sizeTrackbar;

    CalcToolbarSize( &sizeToolbar );
    CalcTrackbarSize ( &sizeTrackbar );

    x = sizeToolbar.cx + nTrackbarBorderWidth;
    y = (sizeToolbar.cy - sizeTrackbar.cy)/2 + nTrackbarBorderHeight;
    w = sizeTrackbar.cx - 2 * nTrackbarBorderWidth;
    h = sizeTrackbar.cy - 2 * nTrackbarBorderHeight;

    MoveWindow( hwndTrack, x,y,w,h, TRUE );

} // AdjustTrackbar


//
// InitTrackbar
//
// Create and initialize the trackbar control
//

BOOL InitTrackbar( HINSTANCE hInstance, HWND hwnd )
{
    LONG oldwndproc;
    int x,y,w,h;

    SIZE sizeToolbar;
    SIZE sizeTrackbar;

    CalcToolbarSize( &sizeToolbar );
    CalcTrackbarSize ( &sizeTrackbar );

    x = sizeToolbar.cx + nTrackbarBorderWidth;
    y = (sizeToolbar.cy - sizeTrackbar.cy)/2 + nTrackbarBorderHeight;
    w = sizeTrackbar.cx - 2 * nTrackbarBorderWidth;
    h = sizeTrackbar.cy - 2 * nTrackbarBorderHeight;

    // Create the trackbar
    hwndTrackbar = CreateWindow( TRACKBAR_CLASS,
                                 NULL,
                                 WS_DISABLED | WS_VISIBLE | WS_CHILD | TBS_NOTICKS | TBS_ENABLESELRANGE | TBS_FIXEDLENGTH,
                                 x,
                                 y,
                                 w,
                                 h,
                                 hwnd,
                                 (HMENU) IDR_TRACKBAR,
                                 hInstance,
                                 NULL );

    SendMessage(hwndTrackbar, TBM_SETTHUMBLENGTH, 16, 0);

    /* TrackBars range is from 0 to 10000, regardless of the stream length */
    SendMessage(hwndTrackbar, TBM_SETRANGEMIN, FALSE, 0     );
    SendMessage(hwndTrackbar, TBM_SETRANGEMAX, FALSE, 10000 );

    /* disable pageup/down lineup/down using keyboard or mouse */
    SendMessage(hwndTrackbar, TBM_SETPAGESIZE,    0 , 0);
    SendMessage(hwndTrackbar, TBM_SETLINESIZE,    0,  0);

    oldwndproc = SetWindowLong( hwndTrackbar, GWL_WNDPROC, (DWORD)&TrackbarProc);
    SetWindowLong(hwndTrackbar, GWL_USERDATA, oldwndproc);

    return TRUE;

} // InitTrackbar


//
// TrackbarTimerFunc
//
// Moves the trackbar during media replay. Also prints time stamp.
//

void CALLBACK TrackbarTimerFunc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
{
    HRESULT hr;
    IMediaPosition * pMP;

    if (idEvent == TrackbarTimer)
    {
        if (IMPTotalTime != 0.0)
        {
            if (IsInitialized())
            {
                hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph,
                                                          &IID_IMediaPosition,
                                                          (void**) &pMP);
                if (SUCCEEDED(hr))
                {
                    REFTIME tCurrent;
                    hr = pMP->lpVtbl->get_CurrentPosition(pMP, &tCurrent);
                    if (SUCCEEDED(hr))
                    {
                        LONG value;

                        if (Previewing)
                        {
                            if (tCurrent < IMPDownloadTime)
                            {
                                IMPCurrentPosition = tCurrent;
                            }
                        }
                        else
                        {
                            IMPCurrentPosition = tCurrent;
                        }

                        if (hwndTrackbar != NULL)
                        {
                            if (!TrackbarDragging)
                            {
                                value = (LONG)(10000 * IMPCurrentPosition / IMPTotalTime);
                                SendMessage( hwndTrackbar, TBM_SETPOS, TRUE, value);
                            }
                        }


                        if ((!Transmitting) && (!TrackbarDragging))
                        {
                            unsigned int timecode = (unsigned int)(1000 * IMPCurrentPosition);
                            char *timecodestring = createtimestring(timecode);
                            SetStatusText(STAT_TIMECODE, 0, -1, "T: %s", timecodestring);
                        }
                    }
                    pMP->lpVtbl->Release(pMP);
                }
            }
        }
    }

} // TrackbarTimerFunc


/*---------------------- FILE TYPE REGISTRATION CODE ------------------------*/

enum
{
    Query,
    Register,
    Unregister
};

//
// FileTypeRegistration
//
// query for, register or unregister a given file type
//

BOOL FileTypeRegistration(LPTSTR filetype, int mode)
{
    BOOL result = FALSE;

    LONG retval;
    HKEY hkeyFileExtension;
    HKEY hkeyFileType;
    DWORD dwResult;

    unsigned char keyname[32];
    unsigned char defaulttypename[32];
    unsigned char *src, *dst;

    int i;

    keyname[0] = '.';
    for (src = filetype, dst = &keyname[1]; *dst = tolower(*src) ; dst++, src++ );
    for (src = filetype, dst = defaulttypename; *dst = toupper(*src) ; dst++, src++ );
    strcat(defaulttypename, "File");

    retval = RegCreateKeyEx(HKEY_CLASSES_ROOT,
                            keyname,
                            0,
                            NULL,
                            REG_OPTION_NON_VOLATILE,
                            KEY_READ | KEY_WRITE,
                            NULL,
                            &hkeyFileExtension,
                            &dwResult);

    if (retval != ERROR_SUCCESS)
    {
        gui_criticalerror("RegCreateKeyEx (1) for '%s' failed: %d\n", keyname, retval);
    }
    else
    {
        unsigned char filetypename[200];
        unsigned char filetypenameshellcommand[256];
        DWORD dwType;
        DWORD dwSize;

        strcpy(filetypename, "");

        dwSize = sizeof(filetypename);
        retval = RegQueryValueEx( hkeyFileExtension,
                                  "",
                                  0,
                                  &dwType,
                                  filetypename,
                                  &dwSize );

        if (retval != ERROR_SUCCESS)
        {
            if (retval != ERROR_FILE_NOT_FOUND)
                gui_criticalerror("RegQueryValueEx (1) for '%s' failed: %d\n", "(Default)", retval);
            else
            {
                retval = RegSetValueEx( hkeyFileExtension,
                                        "",
                                        0,
                                        REG_SZ,
                                        defaulttypename,
                                        strlen(defaulttypename)+1 );
                if (retval != ERROR_SUCCESS)
                {
                    gui_criticalerror("RegSetValueEx (1) for '%s' failed: %d\n", "(Default)", retval);
                }
                else
                    strcpy(filetypename, defaulttypename);
            }
        }
        else
        {
            if (dwType == REG_SZ)
            {
                if (!strcmp("", filetypename))
                {
                    retval = RegSetValueEx( hkeyFileExtension,
                                            "",
                                            0,
                                            REG_SZ,
                                            defaulttypename,
                                            strlen(defaulttypename)+1 );
                    if (retval != ERROR_SUCCESS)
                    {
                        gui_criticalerror("RegSetValueEx (2) for '%s' failed: %d\n", "(Default)", retval);
                    }
                    else
                        strcpy(filetypename, defaulttypename);
                }
            }
        }
            

        if (strcmp(filetypename, ""))
        {
            if (mode == Query)
                result = TRUE;

            for (i = 0; i < 2 ; i++)
            {
                strcpy(filetypenameshellcommand, filetypename);
                if (i == 0)
                    strcat(filetypenameshellcommand, "\\shell\\open\\command");
                if (i == 1)
                    strcat(filetypenameshellcommand, "\\shell\\play\\command");

                retval = RegCreateKeyEx(HKEY_CLASSES_ROOT,
                                        filetypenameshellcommand,
                                        0,
                                        NULL,
                                        REG_OPTION_NON_VOLATILE,
                                        KEY_READ | KEY_WRITE,
                                        NULL,
                                        &hkeyFileType,
                                        &dwResult);

                if (retval != ERROR_SUCCESS)
                {
                    gui_criticalerror("RegCreateKeyEx (2) for '%s' failed: %d\n", filetypenameshellcommand, retval);
                }
                else
                {
                    unsigned char playername[512];
                    unsigned char playernameuppercase[512];
                    DWORD dwType = REG_NONE;
                    DWORD dwSize;
                    
                    dwSize = sizeof(playername);
                    retval = RegQueryValueEx( hkeyFileType,
                                              "",
                                              0,
                                              &dwType,
                                              playername,
                                              &dwSize );

                    if (retval != ERROR_SUCCESS)
                    {
                        if (retval != ERROR_FILE_NOT_FOUND)
                            gui_criticalerror("RegQueryValueEx (2) for '%s' failed: %d\n", "(Default)", retval);
                        else
                            strcpy(playername, "");
                        dwType = REG_SZ;
                    }
                        
                    if (dwType == REG_SZ)
                    {
                        unsigned char myplayername[512];
                        unsigned char myplayernameuppercase[512];
                        sprintf(myplayername, "\42%s\42 \42%%L\42", appVars.szProgramExecutable);
                        
                        /* make uppercase versions of playername strings */
                        strcpy(playernameuppercase, playername);
                        _strupr(playernameuppercase);
                        
                        strcpy(myplayernameuppercase, myplayername);
                        _strupr(myplayernameuppercase);

                        switch (mode)
                        {
                        case Query:
                            
                            /* try to match player name with asfrecorder */
                            if ( (strstr(playernameuppercase, myplayernameuppercase ) == NULL) &&
                                 (strstr(playernameuppercase, "ASFRECORDER"         ) == NULL) &&
                                 (strstr(playernameuppercase, "ASFREC~"             ) == NULL)    )
                            {
                                result = FALSE;
                                break;
                            }
                            break;
                            
                        case Register:
                            /* Create backup of old player only if its name */
                            /* does not contain the string "ASFRecorder" and*/
                            /* if it is not empty */
                            if ((strcmp(playernameuppercase, ""           )        ) &&
                                (strstr(playernameuppercase, "ASFRECORDER") == NULL) &&
                                (strstr(playernameuppercase, "ASFREC~1"   ) == NULL)    )
                            {
                                retval = RegSetValueEx( hkeyFileType,
                                                        "ASFRecorder_backup",
                                                        0,
                                                        REG_SZ,
                                                        playername,
                                                        strlen(playername)+1 );
                                if (retval != ERROR_SUCCESS)
                                {
                                    gui_criticalerror("RegSetValueEx (3) for '%s' failed: %d\n", "ASFRecorder_backup", retval);
                                }
                            }
                            
                            retval = RegSetValueEx( hkeyFileType,
                                                    "",
                                                    0,
                                                    REG_SZ,
                                                    myplayername,
                                                    strlen(myplayername)+1 );
                            if (retval != ERROR_SUCCESS)
                            {
                                gui_criticalerror("RegSetValueEx (4) for '%s' failed: %d\n", "(Default)", retval);
                            }
                            break;
                            
                        case Unregister:
                            dwSize = sizeof(playername);
                            retval = RegQueryValueEx( hkeyFileType,
                                                      "ASFRecorder_backup",
                                                      0,
                                                      &dwType,
                                                      playername,
                                                      &dwSize );
                            if ((retval != ERROR_SUCCESS) && (retval != ERROR_FILE_NOT_FOUND))
                            {
                                gui_criticalerror("RegQueryValueEx (3) for '%s' failed: %d\n", "ASFRecorder_backup", retval);
                            }
                            else
                            {
                                if (retval == ERROR_FILE_NOT_FOUND)
                                {
                                    dwType = REG_SZ;
                                    strcpy(playername, "");
                                }
                                
                                if (dwType == REG_SZ)
                                {
                                    retval = RegSetValueEx( hkeyFileType,
                                                            "",
                                                            0,
                                                            REG_SZ,
                                                            playername,
                                                            strlen(playername)+1 );
                                    if (retval != ERROR_SUCCESS)
                                    {
                                        gui_criticalerror("RegSetValueEx (5) for '%s' failed: %d\n", "(Default)", retval);
                                    }
                                    else
                                    {
                                        retval = RegDeleteValue( hkeyFileType,
                                            "ASFRecorder_backup" );
                                        if ((retval != ERROR_SUCCESS) && (retval != ERROR_FILE_NOT_FOUND))
                                        {
                                            gui_criticalerror("RegDeleteValue (1) for '%s' failed: %d\n", "ASFRecorder_backup", retval);
                                        }
                                    }
                                }
                            }
                            break;
                        }
                    }
                    RegCloseKey(hkeyFileType);
                }
            }
        }
        RegCloseKey(hkeyFileExtension);
    }
    return result;

} // FileTypeRegistration


//
// OnRegisterFileTypes
//
// Register redirection file types.
//

void OnRegisterFileTypes()
{
    if ( (!FileTypeRegistration("ASX", Query)) ||
         (!FileTypeRegistration("WAX", Query)) ||
         (!FileTypeRegistration("WVX", Query)) ||
         (!FileTypeRegistration("WMX", Query))   )
    {
        FileTypeRegistration("ASX", Register);
        FileTypeRegistration("WAX", Register);
        FileTypeRegistration("WVX", Register);
        FileTypeRegistration("WMX", Register);
        PlayerMessageBox( IDS_REGISTERED );
    }
    else
    {
        PlayerMessageBox( IDS_ALREADYREGISTERED );
    }

} // OnRegisterFileTypes


//
// OnRegisterFileTypes
//
// Unregister redirection file types.
//

void OnUnregisterFileTypes()
{
    if ( FileTypeRegistration("ASX", Query) ||
         FileTypeRegistration("WAX", Query) ||
         FileTypeRegistration("WVX", Query) ||
         FileTypeRegistration("WMX", Query)   )
    {
        FileTypeRegistration("ASX", Unregister);
        FileTypeRegistration("WAX", Unregister);
        FileTypeRegistration("WVX", Unregister);
        FileTypeRegistration("WMX", Unregister);
        PlayerMessageBox( IDS_UNREGISTERED );
    }
    else
    {
        PlayerMessageBox( IDS_NOTREGISTERED );
    }

} // OnUnregisterFileTypes


syntax highlighted by Code2HTML, v. 0.9.1