/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */


#include "stdafx.h"
#include "gridedge.h"
#include "netsvw.h"
#include "template.h"

#define VIEW_SOURCE_TARGET_WINDOW_NAME "%ViewSourceWindow"	/* from ns/lib/libnet/cvcolor.c */

MWContext *FE_MakeGridWindow(MWContext *pOldContext, void *hist_list, void *pHistory,
	int32 lX, int32 lY,
	int32 lWidth, int32 lHeight, char *pUrlStr, char *pWindowName, int8 iScrollType,
    NET_ReloadMethod eReloadThis, Bool no_edge)	{

	//	First, ensure that the context we're going into is of an appropriate type.
	if(pOldContext == NULL || ABSTRACTCX(pOldContext)->IsFrameContext() == FALSE)	{
		//	Only let this happen to window contexts for now.
		return(NULL);
	}
	else	{
		//	The goal here is to create a new, fabulous, grid that is a child of the
		//		other window context's frame.
		CWinCX *pParentCX = VOID2CX(pOldContext->fe.cx, CWinCX);

		//	Safety dance.
		if(pParentCX->GetPane() == NULL)	{
			return(NULL);
		}

		HWND hParentWnd = pParentCX->GetPane();

		//	Bring up a create context, so that the Create call won't complain on
		//		16 bit windows.
		//	Since we create the context here, and the document, along with the view,
		//		the view takes care of destroying the docuemnt on close, so we don't
		//		need to worry about it.
		CCreateContext cccGrid;
		cccGrid.m_pNewViewClass = RUNTIME_CLASS(CNetscapeView);
		cccGrid.m_pCurrentDoc = new CGenericDoc();

        //	Create the view.
        DWORD dwGridStyle = WS_HSCROLL | WS_VSCROLL;
		CNetscapeView *pNewView = new CNetscapeView();
		pNewView->Create(NULL,
		   	pWindowName,
		   	dwGridStyle,
            CRect(0, 0, 1, 1),
			CWnd::FromHandle(hParentWnd),
			0,
			&cccGrid);

		CWinCX *pWinCX = new CWinCX((CGenericDoc *)cccGrid.m_pCurrentDoc, pParentCX->GetFrame(), pNewView);

		//	Set the context's name.
		//	Set its parent.
		pWinCX->SetContextName(pWindowName);
		pWinCX->SetParentContext(pOldContext);

		RECT rect;
		rect.left = CASTINT(pParentCX->Twips2PixX(lX));
		rect.top = CASTINT(pParentCX->Twips2PixY(lY));
		rect.right = CASTINT(pParentCX->Twips2PixX(lX) + pParentCX->Twips2PixX(lWidth));
		rect.bottom = CASTINT(pParentCX->Twips2PixY(lY) + pParentCX->Twips2PixY(lHeight));
		//	Initialize the context.
		pWinCX->Initialize(pWinCX->CDCCX::IsOwnDC(), &rect);

        //  Tell it wether or not it will have an edge (border in the
        //      non client area).
        pWinCX->SetBorder(!no_edge);

        //  Size the window to final coords.
        //  This causes the non client calc to happen which is needed
        //      now that it knows it's a grid cell.
        pNewView->MoveWindow(
            CRect(CASTINT(pParentCX->Twips2PixX(lX)),
		   	CASTINT(pParentCX->Twips2PixY(lY)),
		   	CASTINT(pParentCX->Twips2PixX(lX) + pParentCX->Twips2PixX(lWidth)),
		   	CASTINT(pParentCX->Twips2PixY(lY) + pParentCX->Twips2PixY(lHeight))),
            FALSE);

		//	Decide what we're doing about scroll bars.
		switch(iScrollType)	{
		case LO_SCROLL_NO:
			//	Shouldn't ever have scrollbars.
			//	Get rid of them.
			pWinCX->SetDynamicScrollBars(FALSE);
			pWinCX->ShowScrollBars(SB_BOTH, FALSE);
			pWinCX->SetAlwaysShowScrollBars(FALSE);
			break;
		case LO_SCROLL_AUTO:
			//	Aha, the hard case.
			//	Set a flag so that when the load is complete in the context, such
			//		that we will hide the scroll bars if there is already enough room.
			//	This means that the entire document fits on the screen (not much data),
			//		and that doing a load from resize at that point will be minimal
			//		overhead.
			pWinCX->SetDynamicScrollBars(TRUE);
			pWinCX->SetAlwaysShowScrollBars(FALSE);
			break;
		case LO_SCROLL_YES:
			//	Have the scroll bars show.
			pWinCX->SetDynamicScrollBars(FALSE);
			pWinCX->SetAlwaysShowScrollBars(TRUE);
			pWinCX->ShowScrollBars(SB_BOTH, TRUE);
			break;
		default:
			ASSERT(0);	// user mush pass in invalid value.
		}

		//	If we have a history list, we need to stick it in the context.
		History_entry *pHistEnt = (History_entry *)pHistory;
		XP_List *pHistList = (XP_List *)hist_list;
		if(pHistList != NULL)	{
			pWinCX->GetContext()->hist.list_ptr = pHistList;
		}
		else	{
			//	If we have a history entry, we need to add it to the session.
			if(pHistEnt != NULL)	{
				SHIST_AddDocument(pWinCX->GetContext(), pHistEnt);
			}
		}

		//	Have it load the url in question.
		//	This could be from the history entry.
		if(pHistEnt == NULL)	{
			URL_Struct *pUrl = NET_CreateURLStruct(pUrlStr, NET_DONT_RELOAD);
			pUrl->force_reload = eReloadThis;

            //  Be sure to add the referrer field of the top level context.
            MWContext *mwRefer = pOldContext;
            while(mwRefer->grid_parent) {
                mwRefer = mwRefer->grid_parent;
            }
            History_entry *he = SHIST_GetCurrent (&mwRefer->hist);
            if(he && he->referer)   {
    		    pUrl->referer = XP_STRDUP(he->referer);
            }

			pWinCX->GetUrl(pUrl, FO_CACHE_AND_PRESENT);
		}
		else	{
			URL_Struct *pUrl = SHIST_CreateURLStructFromHistoryEntry(pWinCX->GetContext(), pHistEnt);
			pUrl->force_reload = eReloadThis;
			pWinCX->GetUrl(pUrl, FO_CACHE_AND_PRESENT);
		}

        // Grid parents have WS_CLIPCHILDREN turned on. This allows them
        // to draw (backgrounds for example) into their windows without
        // worrying about their children.
        CFrameGlue *pGlue = pWinCX->GetFrame();
        if(pGlue) {
            pGlue->ClipChildren(pNewView, TRUE);
        }

		//	Show the window, but don't activate it.
		pNewView->ShowWindow(SW_SHOWNA);

		return(pWinCX->GetContext());
	}
}

void FE_LoadGridCellFromHistory(MWContext *pContext, void *hist, NET_ReloadMethod netReloadMethod) {
	History_entry *pHist = (History_entry *)hist;

	if (pHist != NULL)	{
        URL_Struct *pUrl = SHIST_CreateURLStructFromHistoryEntry(pContext, pHist);
        if(pUrl)    {
            pUrl->force_reload = netReloadMethod;
	        ABSTRACTCX(pContext)->GetUrl(pUrl, FO_CACHE_AND_PRESENT, TRUE);
        }
	}
}

void *FE_FreeGridWindow(MWContext *pContext, XP_Bool bSaveHistory)	{
	ASSERT(ABSTRACTCX(pContext)->IsWindowContext());

	void *pRetval = NULL;

	//	Destroy the view then the doc/it in turn destroys the context.
	//	We have to clean up the doc, since we passed it in....
	CWinCX *pWinCX = VOID2CX(pContext->fe.cx, CWinCX);
	CWinCX *pParentCX =  WINCX(pWinCX->GetParentContext());

	//	We may want to return the history entry.
	if(bSaveHistory == TRUE)	{
        //  Reset to top of document
   		SHIST_SetPositionOfCurrentDoc(&(pWinCX->GetContext()->hist), 0);
        if(pWinCX->GetOriginX() || pWinCX->GetOriginY())    {
#ifdef LAYERS
		    LO_Any *pAny = (LO_Any *)LO_XYToNearestElement(pWinCX->GetContext(), pWinCX->GetOriginX(), pWinCX->GetOriginY(), NULL);
#else
		    LO_Any *pAny = (LO_Any *)LO_XYToNearestElement(pWinCX->GetContext(), pWinCX->GetOriginX(), pWinCX->GetOriginY());
#endif

		    //	Save the location of the current document.
    		if(pAny != NULL)	{
	    		SHIST_SetPositionOfCurrentDoc(&(pWinCX->GetContext()->hist), pAny->ele_id);
		    }
        }

		LO_DiscardDocument(pWinCX->GetContext());
		XP_List *hist_list = pWinCX->GetContext()->hist.list_ptr;
		pWinCX->GetContext()->hist.list_ptr = NULL;

		pRetval = (void *)hist_list;
	}

    CFrameGlue *frame = pWinCX->GetFrame();

	//  Make sure child grid cell going away doesn't hold onto frame
	//	which may also be going down.
	pWinCX->ClearFrame();

	ASSERT(pWinCX->GetPane());
	if(pWinCX->GetPane())	{
		CGenericView *pView = pWinCX->GetView();

		ASSERT(pView);
		if(pView)   {
            // Tell our parent(s) that they don't need to clip children
            // anymore just for our sake.
            if(frame) {
                frame->ClipChildren(pView, FALSE);
            }
		    pView->FrameClosing();
			pView->SendMessage(WM_CLOSE);
        }
	}

	return(pRetval);
}

void FE_GetFullWindowSize(MWContext *pContext, int32 *plWidth, int32 *plHeight)	{

	//	Only do this on windows.
	if(ABSTRACTCX(pContext)->IsWindowContext() == TRUE && ABSTRACTCX(pContext)->IsDestroyed() == FALSE)	{
		CPaneCX *pWindow = PANECX(pContext);

		RECT crDimensions;

		//	Get the full size of the window.
        ::GetClientRect(pWindow->GetPane(), &crDimensions);

		//	Convert the dimensions to twips.
		HDC hDC = pWindow->GetContextDC();
		::DPtoLP(hDC, (POINT *)&crDimensions, 2);
		pWindow->ReleaseContextDC(hDC);

		*plWidth = crDimensions.right - crDimensions.left;
		*plHeight = crDimensions.bottom - crDimensions.top;
		
		//  Adjust to not include scroll bars.
		if(pWindow->IsVScrollBarOn()) {
		    *plWidth += sysInfo.m_iScrollWidth;
		}
		if(pWindow->IsHScrollBarOn()) {
		    *plHeight += sysInfo.m_iScrollHeight;
		}
	}
	else	{
		//	No window here....
        //  Set to 1 to avoid divide by zero errors in Back end.
		*plHeight = *plWidth = 1;
	}
}

void FE_RestructureGridWindow(MWContext *pContext, int32 lX, int32 lY, int32 lWidth, int32 lHeight)	{
	TRACE("FE_RestructureGridWindow(%p, %ld, %ld, %ld, %ld)\n", pContext, lX, lY, lWidth, lHeight);

	//	Safety dance.
	if(pContext == NULL)	{
		return;
	}

	//	We're under the immediate assumption that the context is a window.
	ASSERT(ABSTRACTCX(pContext)->IsWindowContext());

	CWinCX *pWinCX = WINCX(pContext);

	//	We need to move the window.
	//	It will automagically redraw itself.
    ::MoveWindow(pWinCX->GetPane(), CASTINT(pWinCX->Twips2PixX(lX)),
		CASTINT(pWinCX->Twips2PixY(lY)),
		CASTINT(pWinCX->Twips2PixX(lWidth)),
		CASTINT(pWinCX->Twips2PixY(lHeight)),
        TRUE);

	//	Dynamic scroll bars are handled internally, as is resize.
}

void FE_SetWindowLoading(MWContext *pContext, URL_Struct *pUrl, Net_GetUrlExitFunc **ppExitRoutine)	{

	//	A url load is changing context.
	//	Telling the exit routine that we'd like to be called.
	*ppExitRoutine = CFE_GetUrlExitRoutine;

	//	Now, call the GetUrl routine, but don't really load.
	//	This bootstraps the normal loading procedure.
	//	DON'T REALLY CARE WHAT WE PASS IN AS THE PRESENTATION TYPE, yet.
	ABSTRACTCX(pContext)->GetUrl(pUrl, FO_CACHE_AND_PRESENT, FALSE);
}

void FE_GetEdgeMinSize(MWContext *pContext, int32 *pSize, Bool no_edge)	{
	TRACE("FE_GetEdgeMinSize(%p, %p)\n", pContext, pSize);

    //  Don't care anymore.
	*pSize = 5;

    if(!no_edge)    {
		*pSize = 5;
    }
}

MWContext *FE_MakeBlankWindow(MWContext *pOldContext, URL_Struct *pUrl, char *pContextName)	{
//	TRACE("FE_MakeBlankWindow(%p, %p, %s)\n", pOldContext, pUrl, pContextName);

	//	Create a new top level blank window.
	//	Only don't load anything.  Leave it blank.
	//	HACK ALERT:
	//	In order to better do this, we need to have the CreateNewDocWindow
	//		function not load what's in the history.  It only does this with
	//		other browser windows.  Anyhow, switch our context type temporarily
	//		in order to get the effect.
	MWContextType OldType = pOldContext->type;
	pOldContext->type = (MWContextType)-1;
	MWContext *pRetval = pOldContext->funcs->CreateNewDocWindow(pOldContext, NULL);
	pOldContext->type = OldType;

	if(pRetval != NULL)	{
		ABSTRACTCX(pRetval)->SetContextName(pContextName);
	}

	return(pRetval);
}

//	Create a new window.
//	If pChrome is NULL, do a FE_MakeBlankWindow....
//	pChrome specifies the attributes of a window.
//  If you use this call, Toolbar information will not be saved in the preferences.
MWContext *FE_MakeNewWindow(MWContext *pOldContext, URL_Struct *pUrl, char *pContextName, Chrome *pChrome)
{

    BOOL bNetHelpWnd = FALSE;
    
    
	//	Decide which type of context this will be (we use this to possible access some templates).
	MWContextType cxType = MWContextAny;
	if(pChrome != NULL)	{
		switch(pChrome->type)	{
            case MWContextHTMLHelp:
                pChrome->type = MWContextBrowser;
              #ifndef _WIN32
                pChrome->allow_resize = FALSE;
              #endif  
                bNetHelpWnd = TRUE;
                // Fall through...
			case MWContextBrowser:
				cxType = MWContextBrowser;
				break;
			case MWContextDialog:
				cxType = MWContextDialog;
				break;
		}
	}
	else	{
		//	Default to MWContextBrowser;
		cxType = MWContextBrowser;
	}

	//	A type came in that we're not ready to handle.
	//	Seems more code needs to be written....
	ASSERT(cxType != MWContextAny);
	if(cxType == MWContextAny)	{
		return(NULL);
	}

    //  Decide now wether or not to make the window initially visible.
    //  We decide by detecting wether or not we'll be enforcing a resize.
    //  We'll make the window visible after we're done sizing.
    //  Only works with 32 bits right now.
    BOOL bMakeVisible = TRUE;
	if(pChrome && ((pChrome->outw_hint && pChrome->outh_hint) || 
		(pChrome->w_hint && pChrome->h_hint)   ||
		pChrome->location_is_chrome ||
		pChrome->topmost || pChrome->bottommost)) {
        bMakeVisible = FALSE;
	}

	//  Must check if user wants a titlebar now since it affects the type of 
	//  window used during window creation.  If no titlebar, use popup type.
	BOOL bHideTitlebar = FALSE;
	BOOL bBorder = TRUE;
	if (pChrome && pChrome->hide_title_bar){
		bHideTitlebar = TRUE;
	}

	BOOL bDependent = FALSE;
	if (pOldContext && pChrome && pChrome->dependent) {	
	    bDependent = TRUE;
	}

	//	Take the template of the appropriate window type, and pull it up.
	CAbstractCX *pCX = NULL;

	// If there is a parent, get its hwnd
	CWinCX *pOldWinCX = NULL;
	if(pOldContext)
		pOldWinCX = WINCX(pOldContext);

	HWND	hParentHwnd = NULL;
	if(pOldWinCX && pOldWinCX->GetFrame() && pOldWinCX->GetFrame()->GetFrameWnd())
		hParentHwnd = pOldWinCX->GetFrame()->GetFrameWnd()->GetSafeHwnd();


	switch(cxType)	{
		case MWContextBrowser:
		case MWContextDialog:	{
			BOOL bPopup = FALSE;
			HWND hPopupParent = NULL;

			// if we are creating a modal window then we need to become a child of
			// the calling frame and of type WS_POPUP.  The values below are set to
			// tell the frame to create itself this way.
			if(hParentHwnd && pChrome && pChrome->is_modal)
			{
				bPopup = TRUE;
				hPopupParent = ::GetLastActivePopup(hParentHwnd);
			}

            CGenericDoc *pDoc = 
				(CGenericDoc *)theApp.m_ViewTmplate->OpenDocumentFile(NULL, bMakeVisible, 
								    bHideTitlebar, bDependent, bPopup, hPopupParent);
				if(pDoc != NULL)	{
					pCX = pDoc->GetContext();
				}
			}

			
			break;
		default:
			//	Please add your case statement.
			ASSERT(0);
			break;
	}

	//	If the context wasn't created, we can not continue.
	if(pCX == NULL)	{
		return(NULL);
	}

    // Hack to identify a NetHelp window
    pCX->m_bNetHelpWnd = bNetHelpWnd;
    
	// JavaScript can create windows which are lifetime-linked to their parent.
	// This is set here instead of in JS to avoid threading problems. 
    
	if (bDependent) {	
	    if (pOldContext->js_dependent_list == NULL)
		    pOldContext->js_dependent_list = XP_ListNew();
	    if (pOldContext->js_dependent_list == NULL)
		    return(NULL);
	    XP_ListAddObject(pOldContext->js_dependent_list, pCX->GetContext());
	    pCX->GetContext()->js_parent = pOldContext;
	}


	//	Get the context, and manually assign its type.
	//	The XP context that is.
	pCX->GetContext()->type = cxType;

	//	Assign the context name, if provided.
	if(pContextName != NULL)	{
		pCX->SetContextName(pContextName);
	}

	//	Copy over the same char set ID as the old context if we're a window.
	if(pCX->IsFrameContext() == TRUE && pOldContext != NULL)	{
		CWinCX *pWinCX = VOID2CX(pCX, CWinCX);
		if(cxType != MWContextDialog && !bNetHelpWnd) {
			pWinCX->GetFrame()->m_iCSID = INTL_DefaultDocCharSetID(pOldContext);
		} else {
			// use the client's default encoding for HTML dialogs (fix for #78838)
			pWinCX->GetFrame()->m_iCSID = INTL_CharSetNameToID(INTL_ResourceCharSet());
			if (pContextName != NULL) {
				if(strncmp(pContextName, VIEW_SOURCE_TARGET_WINDOW_NAME,
					strlen(VIEW_SOURCE_TARGET_WINDOW_NAME)) == 0)	{
					// but not for View Source which gets the old context char set ID 
					// (fix for #79072)
					pWinCX->GetFrame()->m_iCSID = INTL_DefaultDocCharSetID(pOldContext);
				}
				if(strncmp(pContextName, "%DocInfoWindow", 14) == 0)	{
					// %DocInfoWindow is from ns/lib/libnet/mkgeturl.c net_output_about_url()
					// Document Info gets the user-selectable default encoding
					pWinCX->GetFrame()->m_iCSID = INTL_DefaultWinCharSetID(NULL);
				}
			}
		}
	}

  #ifdef _WIN32
	if(sysInfo.m_bWin4 && bNetHelpWnd && pCX->IsFrameContext()) {
    
		CWinCX *pWinCX = VOID2CX(pCX, CWinCX);
        CFrameWnd *pFrame = pWinCX->GetFrame()->GetFrameWnd();
    
        if( pFrame ) {
           	HICON hIcon = theApp.LoadIcon( "IDR_NETHELP_ICON" );
           	pFrame->SetIcon( hIcon, TRUE );

           	hIcon = (HICON)::LoadImage( AfxFindResourceHandle( "IDR_NETHELP_ICON", RT_GROUP_ICON ), "IDR_NETHELP_ICON", IMAGE_ICON,
                                        GetSystemMetrics( SM_CXSMICON ), GetSystemMetrics( SM_CYSMICON ), LR_SHARED );
           	pFrame->SetIcon( hIcon, FALSE );
        }
    }
   #endif // _WIN32
       
	//	Pay special attention to the chrome attributes specified, if specified.
	//	If they aren't, attempt to use attributes from the old context passed in.
	if(pChrome != NULL && pCX->IsFrameContext() == TRUE)	{
		CWinCX *pWinCX = VOID2CX(pCX, CWinCX);
		
		LPCHROME pIChrome = pWinCX->GetFrame()->GetChrome();
		if(pIChrome) {
    		//	Url bar and directory buttons.
    		// make it so that we don't save toolbar information
    		pIChrome->SetSaveToolbarInfo(FALSE);
    		pIChrome->ShowToolbar(ID_LOCATION_TOOLBAR, pChrome->show_url_bar);
    		pIChrome->ShowToolbar(ID_PERSONAL_TOOLBAR, pChrome->show_directory_buttons);
    		pIChrome->ShowToolbar(ID_NAVIGATION_TOOLBAR, pChrome->show_button_bar);

    		//	Status bar!
    		LPNSSTATUSBAR pIStatusBar = NULL;
    		pIChrome->QueryInterface( IID_INSStatusBar, (LPVOID *) &pIStatusBar );
    		if ( pIStatusBar ) {
    			pIStatusBar->Show( pChrome->show_bottom_status_bar );
    			pIStatusBar->Release();
    		}
		}

		//	Show the menu?
		if(pChrome->show_menu == FALSE)	{
            CMenu *pMenu = pWinCX->GetFrame()->GetFrameWnd()->GetMenu();
            HMENU hMenu = pMenu ? pMenu->m_hMenu : NULL;                    
			pWinCX->GetFrame()->GetFrameWnd()->SetMenu(NULL);
            if( hMenu && (theApp.m_ViewTmplate->m_hMenuShared != hMenu) )
            {
                pMenu->DestroyMenu();
            }
		}

		//	Need to recalc.
		pWinCX->GetFrame()->GetFrameWnd()->RecalcLayout();

		//	Size, of all things, of the viewing area only....
		//	Doesn't account for menu wrap or unwrap when getting smaller/bigger.
		if((pChrome->outw_hint && pChrome->outh_hint) || 
			(pChrome->w_hint &&pChrome->h_hint)   ||
			pChrome->location_is_chrome ||
			pChrome->topmost || pChrome->bottommost) {

			int32 lLeft=0;
			int32 lTop=0;
			int32 lWidth=0;
			int32 lHeight=0;

			CRect crFrame;
			pWinCX->GetFrame()->GetFrameWnd()->GetWindowRect(crFrame);
			CRect crView;
			::GetWindowRect(pWinCX->GetPane(), crView);
			
			//	Acount for the view border style.
			crView.InflateRect(-1 * sysInfo.m_iBorderWidth, -1 * sysInfo.m_iBorderHeight);

			int32 lWAdjust = pChrome->w_hint - (int32)crView.Width();
			int32 lHAdjust = pChrome->h_hint - (int32)crView.Height();

			if (pChrome->outw_hint>0 && pChrome->outh_hint>0) {
				lWidth = pChrome->outw_hint;
				lHeight = pChrome->outh_hint;
			}
			else if (pChrome->w_hint>0 && pChrome->h_hint>0) {
				lWidth = (int32)crFrame.Width() + lWAdjust;
				lHeight = (int32)crFrame.Height() + lHAdjust;
			}
			else {
				lWidth = (int32)crFrame.Width();
				lHeight = (int32)crFrame.Height();
			}

			if (pChrome->location_is_chrome) {
				lLeft = pChrome->l_hint;
				lTop = pChrome->t_hint;
			}
			else {
				lLeft = (int32)crFrame.left;
				lTop = (int32)crFrame.top;
			}
  
			//There is a security problem where the window size buffer can be 
			//overridden on Win95.  Using arbitrary max window size of 10000
			lWidth = lWidth > 10000 ? 10000 : lWidth;
			lHeight = lHeight > 10000 ? 10000 : lHeight;

			if (pChrome->topmost)
				pWinCX->GetFrame()->GetFrameWnd()->SetWindowPos(&CWnd::wndTopMost, CASTINT(lLeft), CASTINT(lTop), CASTINT(lWidth), CASTINT(lHeight), NULL);
			else if (pChrome->bottommost)
				pWinCX->GetFrame()->GetFrameWnd()->SetWindowPos(&CWnd::wndBottom, CASTINT(lLeft), CASTINT(lTop), CASTINT(lWidth), CASTINT(lHeight), NULL);
			else
				pWinCX->GetFrame()->GetFrameWnd()->MoveWindow(CASTINT(lLeft), CASTINT(lTop), CASTINT(lWidth), CASTINT(lHeight));

		//  Specific width and height information means that we should not save the sizes when closing down, regardless of context type.
		pWinCX->m_bSizeIsChrome = TRUE;
		}

		//	Set windows always on bottom.
		pWinCX->SetZOrder(pChrome->z_lock, pChrome->bottommost);

		//	Modality.
		if(pChrome->is_modal)	{
			//	Inform the context of its special status.
			pWinCX->GoModal(pOldContext);
		}

		//	Scroll bars.
		if(pChrome->show_scrollbar == FALSE)	{
			//	Turn them off.
			pWinCX->SetDynamicScrollBars(FALSE);
			pWinCX->ShowScrollBars(SB_BOTH, FALSE);
			pWinCX->SetAlwaysShowScrollBars(FALSE);
		}

		//	Resize.
		if(pChrome->allow_resize == FALSE)	{
			//	Disable resize....
			//	This is a truly gross and unholy option.
			pWinCX->EnableResize(FALSE);
		}

		//	Hotkeys
		if (pChrome->disable_commands) {	
			//	Disable hotkeys
		    
			pWinCX->DisableHotkeys(TRUE);
		}

		//	Close.
		if(pChrome->allow_close == FALSE)	{
			//	Disable closing....
			//	This is a truly gross and unholy option.
			//	This is reenabled in FE_DestroyContext regardless (i.e.
			//		can't be closed unless someone calls that API).
			pWinCX->EnableClose(FALSE);
		}

		//	Don't allow restricted target windows to be targeted
		//	by mail links and other functions which just grab 
		//	the nearest Browser context.
		if(pChrome->restricted_target)
		    pCX->GetContext()->restricted_target = TRUE;

		//	Close callback.
		//	Callback when the context is destroyed.
		pWinCX->CloseCallback(pChrome->close_callback, pChrome->close_arg);

		//	History copy.
		if(pChrome->copy_history && pOldContext != NULL)	{
			//	Copy the old context's history.
			SHIST_CopySession(pWinCX->GetContext(), pOldContext);
		}
	}
	else if(cxType == MWContextBrowser && pCX->IsFrameContext() == TRUE)	{
		CWinCX *pWinCX = VOID2CX(pCX, CWinCX);

		//	See if the calling context is a window context, and if so, emulate its
		//		settings.
		//	Otherwise, just take what we have up....
		if(pOldContext != NULL && pOldContext->type == MWContextBrowser)	{
			CWinCX *pOldCX = WINCX(pOldContext);

			LPCHROME pIChrome = pWinCX->GetFrame()->GetChrome();
			if(pIChrome) {
    			// Directory buttons, location box and all
    			// make it so that we don't save toolbar information
    			pIChrome->SetSaveToolbarInfo(FALSE);
    			pIChrome->SetToolbarStyle( theApp.m_pToolbarStyle );

    			pIChrome->ShowToolbar( ID_LOCATION_TOOLBAR, pOldCX->GetFrame()->m_bLocationBar );
    			pIChrome->ShowToolbar(ID_PERSONAL_TOOLBAR, pOldCX->GetFrame()->m_bStarter);
    			pIChrome->ShowToolbar(ID_NAVIGATION_TOOLBAR, pOldCX->GetFrame()->m_bShowToolbar);
			}
		}
	}

	//	If a URL struct was passed in, load it as the last thing to do.
	if(pUrl != NULL)	{
		int iFormatOut = FO_CACHE_AND_PRESENT;
		switch(cxType)	{
			case MWContextBrowser:
			case MWContextDialog:
				iFormatOut = FO_CACHE_AND_PRESENT;
				break;
			default:
				//	May be a different format out depending on context type.
				//	Please set as appropriate!
				ASSERT(0);
				break;
		}
		pCX->GetUrl(pUrl, iFormatOut);
	}

    //  If we hid the window earlier due to sizing information, time to make it come back.
    if(pCX->IsFrameContext() == TRUE && !bMakeVisible)	{
		VOID2CX(pCX, CWinCX)->GetFrame()->GetFrameWnd()->ShowWindow(SW_SHOW);
    }
    
    //	Return the XP context.
	return(pCX->GetContext());
}

//  Update current chrome in a context.
void FE_UpdateChrome(MWContext *pContext, Chrome *pChrome)
{
	TRACE("FE_UpdateChrome(%p, %p)\n", pContext, pChrome);

    //  API fulfillments.
    if(!pContext || !pChrome || !ABSTRACTCX(pContext) || !ABSTRACTCX(pContext)->IsFrameContext())   {
        return;
    }

    CWinCX *pWinCX = WINCX(pContext);
    if(!pWinCX->GetPane() || !pWinCX->GetFrame() || !pWinCX->GetFrame()->GetFrameWnd())    {
        //  Context isn't anatomically correct.
        return;
    }

    LPCHROME pIChrome = pWinCX->GetFrame()->GetChrome();
    if(pIChrome) {
    	//	Url bar and directory buttons.
    	pIChrome->SetSaveToolbarInfo(FALSE);
    	pIChrome->ShowToolbar(ID_LOCATION_TOOLBAR,  pChrome->show_url_bar );
    	pIChrome->ShowToolbar(ID_PERSONAL_TOOLBAR,  pChrome->show_directory_buttons );
    	pIChrome->ShowToolbar(ID_NAVIGATION_TOOLBAR, pChrome->show_button_bar);

    	//	Status bar!
    	LPNSSTATUSBAR pIStatusBar = NULL;
    	pIChrome->QueryInterface( IID_INSStatusBar, (LPVOID *) &pIStatusBar );
    	if ( pIStatusBar ) {
    		pIStatusBar->Show( pChrome->show_bottom_status_bar );
    		pIStatusBar->Release();
    	}
    }

	//	Show the menu?
	if(pChrome->show_menu == FALSE)	{
        CMenu *pMenu = pWinCX->GetFrame()->GetFrameWnd()->GetMenu();
        HMENU hMenu = pMenu ? pMenu->m_hMenu : NULL;        
 		pWinCX->GetFrame()->GetFrameWnd()->SetMenu(NULL);
        if( hMenu && (theApp.m_ViewTmplate->m_hMenuShared != hMenu) )
        {
            pMenu->DestroyMenu();
        }
	}
	else    {                                                            
	    //  Get the menu back out of the template.
        CMenu *pMenu = pWinCX->GetFrame()->GetFrameWnd()->GetMenu();
        HMENU hMenu = pMenu ? pMenu->m_hMenu : NULL;
	    ::SetMenu(pWinCX->GetFrame()->GetFrameWnd()->GetSafeHwnd(), theApp.m_ViewTmplate->m_hMenuShared);
        if( hMenu && (theApp.m_ViewTmplate->m_hMenuShared != hMenu) )
        {
            pMenu->DestroyMenu();
        }
	}

	//	Need to recalc.
	pWinCX->GetFrame()->GetFrameWnd()->RecalcLayout();

	//	Size, of all things, of the viewing area only....
	//	Doesn't account for menu wrap or unwrap when getting smaller/bigger.
	if((pChrome->outw_hint && pChrome->outh_hint) || 
		(pChrome->w_hint &&pChrome->h_hint)   ||
		pChrome->location_is_chrome ||
		pChrome->topmost || pChrome->bottommost) {

		int32 lLeft=0;
		int32 lTop=0;
		int32 lWidth=0;
		int32 lHeight=0;

		CRect crFrame;
		pWinCX->GetFrame()->GetFrameWnd()->GetWindowRect(crFrame);
		CRect crView;
		::GetWindowRect(pWinCX->GetPane(), crView);
		
		//	Acount for the view border style.
		crView.InflateRect(-1 * sysInfo.m_iBorderWidth, -1 * sysInfo.m_iBorderHeight);

		int32 lWAdjust = pChrome->w_hint - (int32)crView.Width();
		int32 lHAdjust = pChrome->h_hint - (int32)crView.Height();

		if (pChrome->outw_hint>0 && pChrome->outh_hint>0) {
			lWidth = pChrome->outw_hint;
			lHeight = pChrome->outh_hint;
		}
		else if (pChrome->w_hint>0 && pChrome->h_hint>0) {
			lWidth = (int32)crFrame.Width() + lWAdjust;
			lHeight = (int32)crFrame.Height() + lHAdjust;
		}
		else {
			lWidth = (int32)crFrame.Width();
			lHeight = (int32)crFrame.Height();
		}

		if (pChrome->location_is_chrome) {
			lLeft = pChrome->l_hint;
			lTop = pChrome->t_hint;
		}
		else {
			lLeft = (int32)crFrame.left;
			lTop = (int32)crFrame.top;
		}

		//There is a security problem where the window size buffer can be 
		//overridden on Win95.  Using arbitrary max window size of 10000
		lWidth = lWidth > 10000 ? 10000 : lWidth;
		lHeight = lHeight > 10000 ? 10000 : lHeight;

		if (pChrome->topmost)
			pWinCX->GetFrame()->GetFrameWnd()->SetWindowPos(&CWnd::wndTopMost, CASTINT(lLeft), CASTINT(lTop), CASTINT(lWidth), CASTINT(lHeight), NULL);
		else if (pChrome->bottommost)
			pWinCX->GetFrame()->GetFrameWnd()->SetWindowPos(&CWnd::wndBottom, CASTINT(lLeft), CASTINT(lTop), CASTINT(lWidth), CASTINT(lHeight), NULL);
		else
			pWinCX->GetFrame()->GetFrameWnd()->MoveWindow(CASTINT(lLeft), CASTINT(lTop), CASTINT(lWidth), CASTINT(lHeight));

		//  Specific width and height information means that we should not save the sizes when closing down, regardless of context type.
		pWinCX->m_bSizeIsChrome = TRUE;
	}

	//	Set windows always on bottom.
	pWinCX->SetZOrder(pChrome->z_lock, pChrome->bottommost);

	//	Hotkeys
	pWinCX->DisableHotkeys(pChrome->disable_commands);

	//	Scroll bars.
	if(pChrome->show_scrollbar == FALSE)	{
		//	Turn them off.
		pWinCX->SetDynamicScrollBars(FALSE);
		pWinCX->ShowScrollBars(SB_BOTH, FALSE);
		pWinCX->SetAlwaysShowScrollBars(FALSE);
	}
	else {
	    pWinCX->SetDynamicScrollBars(TRUE);
	    pWinCX->RealizeScrollBars();
	}

	//	Resize.
	//	This is a truly gross and unholy option.
	pWinCX->EnableResize(pChrome->allow_resize);

	//	Close.
	//	This is a truly gross and unholy option.
	//	This is reenabled in FE_DestroyContext regardless (i.e.
	//		can't be closed unless someone calls that API).
    //  This also sets the closing attributes of all child contexts.
	pWinCX->EnableClose(pChrome->allow_close);

	//	Close callback.
    //	Callback when the context is destroyed.
	pWinCX->CloseCallback(pChrome->close_callback, pChrome->close_arg);

    //  We don't handle modality, as there is no parent context passed in.
}

//  Query current chrome in a context.
void FE_QueryChrome(MWContext *pContext, Chrome *pChrome)
{
	TRACE("FE_QueryChrome(%p, %p)\n", pContext, pChrome);

    //  API fulfillments.
    if(!pContext || !pChrome || !ABSTRACTCX(pContext) || !ABSTRACTCX(pContext)->IsFrameContext())   {
        return;
    }

    CWinCX *pWinCX = WINCX(pContext);
    if(!pWinCX->GetPane() || !pWinCX->GetDocument() || !pWinCX->GetFrame() || !pWinCX->GetFrame()->GetFrameWnd())    {
        //  Context isn't anatomically correct.
        return;
    }

    //  Clear the chrome struct, we'll refill in the relevant parts.
    memset((void *)pChrome, 0, sizeof(Chrome));

    LPCHROME pIChrome = pWinCX->GetFrame()->GetChrome();
    if(pIChrome) {
        //	Toolbar on or off?
        pChrome->show_button_bar = pIChrome->GetToolbarVisible(ID_NAVIGATION_TOOLBAR);

        //	Url bar and directory buttons.
        pChrome->show_url_bar = pIChrome->GetToolbarVisible(ID_LOCATION_TOOLBAR);
        pChrome->show_directory_buttons = pIChrome->GetToolbarVisible(ID_PERSONAL_TOOLBAR);

        //	The security bar?
        //  TODO: Fix me!
        pChrome->show_security_bar = TRUE;

        //	Status bar!
        LPNSSTATUSBAR pIStatusBar = NULL;
        pIChrome->QueryInterface( IID_INSStatusBar, (LPVOID *) &pIStatusBar );
        if ( pIStatusBar ) {
    	    HWND hWnd = pIStatusBar->GetHWnd();
    	    pIStatusBar->Release();
    	    pChrome->show_bottom_status_bar = IsWindowVisible( hWnd );
        }
    }

    //	Show the menu?
    pChrome->show_menu = pWinCX->GetFrame()->GetFrameWnd()->GetMenu() == NULL ? FALSE : TRUE;

    //	Size, of all things, of the viewing area only....
	//	Acount for the view border style, too.
	CRect crView;
	::GetWindowRect(pWinCX->GetPane(), crView);
	CRect crFrame;
	pWinCX->GetFrame()->GetFrameWnd()->GetWindowRect(crFrame);
	crView.InflateRect(-1 * sysInfo.m_iBorderWidth, -1 * sysInfo.m_iBorderHeight);
	pChrome->outw_hint = (int32)crFrame.Width();
	pChrome->outh_hint = (int32)crFrame.Height();
	pChrome->w_hint = (int32)crView.Width();
	pChrome->h_hint = (int32)crView.Height();
	pChrome->l_hint = (int32)crFrame.left;
	pChrome->t_hint = (int32)crFrame.top;

	//Set to make sure the coords given are used.
	pChrome->location_is_chrome= TRUE;

	//	Scroll bars.
	pChrome->show_scrollbar = pWinCX->DynamicScrollBars();

	//	Resize.
	//	This is a truly gross and unholy option.
	pChrome->allow_resize = ((CGenericFrame *)pWinCX->GetFrame()->GetFrameWnd())->CanResize();

	//	Hotkeys.
	pChrome->disable_commands = ((CGenericFrame *)pWinCX->GetFrame()->GetFrameWnd())->HotkeysDisabled();

	//	ZLocked.
	pChrome->z_lock = ((CGenericFrame *)pWinCX->GetFrame()->GetFrameWnd())->IsZOrderLocked();

	//	Bottommost.
	pChrome->bottommost = ((CGenericFrame *)pWinCX->GetFrame()->GetFrameWnd())->IsBottommost();

	//	Close.
	//	This is a truly gross and unholy option.
	//  This will only report if the current context and its children can close, not if
	//      the actual frame can close if this context has a parent.
	pChrome->allow_close = pWinCX->GetDocument()->CanClose();

	//	Close callback is not handled correctly, as there may be multiples.

    //  We don't handle modality, as there is no parent context passed in to check against.
}

//	Destroy a window/context.
void FE_DestroyWindow(MWContext *pContext)
{
	TRACE("FE_DestroyWindow(%p)\n", pContext);

	ASSERT(pContext);
	if(pContext != NULL)	{
		//	Make sure that this is a CWinCX.
		ASSERT(ABSTRACTCX(pContext)->IsFrameContext());
		if(ABSTRACTCX(pContext)->IsWindowContext())	{
			CWinCX *pCX = WINCX(pContext);

			//	Make sure there's a frame to close.
			if(pCX->GetFrame()->GetFrameWnd())	{
				//	Reenable closing.
				pCX->EnableClose(TRUE);

				//	Send it a close message.
				pCX->GetFrame()->GetFrameWnd()->SendMessage(WM_CLOSE);
			}
		}
	}
}

//  Fill in left top position of frame.
//  Breaks down on the minimized maximized scenario.
void FE_GetWindowPosition(MWContext *pContext, int32 *pX, int32 *pY)
{
	TRACE("FE_GetWindowPosition(%p, %p, %p)\n", pContext, pX, pY);

    //  Initialize.
    if(pX)  {
        *pX = 0;
    }
    if(pY)  {
        *pY = 0;
    }
    
    if(pContext && ABSTRACTCX(pContext) && ABSTRACTCX(pContext)->IsFrameContext() && (pX || pY))	{
		//	Make sure there's a frame.
		CWinCX *pCX = WINCX(pContext);
		if(pCX->GetFrame() && pCX->GetFrame()->GetFrameWnd())	{
            CRect cr;
            pCX->GetFrame()->GetFrameWnd()->GetWindowRect(cr);

            if(pX)  {
                *pX = cr.left;
            }
            if(pY)  {
                *pY = cr.top;
            }
		}
	}
}

//  Set left top position of frame.
//  Breaks down on the minimized maximized scenario.
void FE_SetWindowPosition(MWContext *pContext, int32 lX, int32 lY)
{
	TRACE("FE_SetWindowPosition(%p, %ld, %ld)\n", pContext, lX, lY);

    if(pContext && ABSTRACTCX(pContext) &&
        ABSTRACTCX(pContext)->IsFrameContext())	{
		//	Make sure there's a frame.
		CWinCX *pCX = WINCX(pContext);
		if(pCX->GetFrame() && pCX->GetFrame()->GetFrameWnd())	{
            pCX->GetFrame()->GetFrameWnd()->SetWindowPos(NULL, CASTINT(lX),
                CASTINT(lY), 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER);
		}
	}
}

//  Return screen dimensions.
void FE_GetScreenSize(MWContext *pContext, int32 *pX, int32 *pY)
{
    //  Initialize
    if(pX)  {
        *pX = sysInfo.m_iScreenWidth;
    }
    if(pY)  {
        *pY = sysInfo.m_iScreenHeight;
    }
}

//  Return available screen rectangle.
void FE_GetAvailScreenRect(MWContext *pContext, int32 *pX, int32 *pY, 
						int32 *pLeft, int32 *pTop)
{
    RECT *pRect = NULL;
#ifdef WIN32
    APPBARDATA abd;
    UINT state;

    XP_BZERO(&abd, sizeof abd);
    abd.cbSize = sizeof(abd);
    abd.hWnd = NULL;

    state = SHAppBarMessage(ABM_GETSTATE, &abd);

    if ((state & ABS_ALWAYSONTOP) && !(state & ABS_AUTOHIDE))
	if (!SHAppBarMessage(ABM_GETTASKBARPOS, &abd))
	    ASSERT(0);
    pRect = &abd.rc;
#endif
    //  Initialize
    if(pX)  {
        *pX = sysInfo.m_iScreenWidth;
	if (pRect != NULL) {
	    if (pRect->top < 0 && pRect->bottom > sysInfo.m_iScreenHeight) {
		//Docked left
		if (pRect->left < 0)
		    *pX -= pRect->right;
		//Docked right
		if (pRect->right > sysInfo.m_iScreenWidth)
		    *pX = pRect->left;
	    }
	}
    }
    if(pY)  {
        *pY = sysInfo.m_iScreenHeight;
	if (pRect != NULL) {
	    if (pRect->left < 0 && pRect->right > sysInfo.m_iScreenWidth) {
		//Docked top
		if (pRect->top < 0);
		    *pY -= pRect->bottom;
		//Docked bottom
		if (pRect->bottom > sysInfo.m_iScreenHeight)
		    *pY = pRect->top;
	    }
	}
    }
    if(pLeft)  {
        *pLeft = 0;
	if (pRect != NULL) {
	    if (pRect->top < 0 && pRect->left < 0) {
		//Docked left
		if (pRect->bottom > sysInfo.m_iScreenHeight)
		    *pLeft = pRect->right;
	    }
	}
    }
    if(pTop)  {
        *pTop = 0;
	if (pRect != NULL) {
	    if (pRect->top < 0 && pRect->left < 0) {
		//Docked top
		if (pRect->right > sysInfo.m_iScreenWidth)
		    *pTop = pRect->bottom;
	    }
	}
    }
}

//  Return color depth.
void FE_GetPixelAndColorDepth(MWContext *pContext, int32 *pixel, int32 *pallette)
{
    TRACE("FE_GetPixelAndColorDepth(%p, %p)\n", pixel, pallette);

    *pixel = *pallette = 0;

    if(ABSTRACTCX(pContext)->IsDestroyed() == FALSE) {

	CWinCX *pWinCX = WINCX(pContext);

	HDC pDC = pWinCX->GetContextDC();

	//  Figure out the depth of the CDC.
	*pixel = ::GetDeviceCaps(pDC, BITSPIXEL);

	//  Check for pallette existence
        if ((::GetDeviceCaps(pDC, RASTERCAPS) & RC_PALETTE) != 0)
	    *pallette = ::GetDeviceCaps(pDC, COLORRES);
	else
	    *pallette = *pixel;

	pWinCX->ReleaseContextDC(pDC);
    }

}








BOOL GetIntelCPUSpeed(int32 &processorSpeed );
BOOL GetIntelCPUInfoViaCPUID(char *systemArchitecture,
			int32 &processorType, int32 &processorFamily, 
			int32 &processorModel, int32 &processorStepping, int32 &processorFeatures,
			CString &vendor);
#ifndef _WIN32
unsigned short GetBaseMemC();
unsigned short GetExpMemC();
#endif
CString csUNKNOWN = "Unknown";
CString csINTEL = "Intel";
CString csMIPS = "Mips";
CString csALPHA = "Alpha";
CString csPPC = "PowerPC";

CString csARCHITECTURE = "ARCHITECTURE";
CString csFAMILY = "FAMILY";
CString csMODEL = "MODEL";
CString csSTEPPING = "STEPPING";
CString csTYPE = "TYPE";
CString csFEATURES = "FEATURES";
CString csPASS = "PASS";
CString csREVISION = "REVISION";
CString csVERSION = "VERSION";
CString csVENDOR = "VENDOR";

// VARIABLE STRUCTURE DEFINITIONS //////////////////////////////
struct FREQ_INFO
{
	unsigned long in_cycles;	// Internal clock cycles during
								//   test
								
	unsigned long ex_ticks;		// Microseconds elapsed during 
								//   test
								
	unsigned long raw_freq;		// Raw frequency of CPU in MHz
	
	unsigned long norm_freq;	// Normalized frequency of CPU
								//   in MHz.
};

struct FREQ_INFO cpuspeed(int clocks);
// Function Prototypes /////////////////////////////////////////

/***************************************************************
* WORD wincpuidsupport()
* =================================
* Wincpuidsupport() tells the caller whether the host processor
* supports the CPUID opcode or not.
*
* Inputs: none
*
* Returns:
*  1 = CPUID opcode is supported
*  0 = CPUID opcode is not supported
***************************************************************/

WORD wincpuidsupport();


/***************************************************************
* WORD wincpuid()
* ===============
* This routine uses the standard Intel assembly code to 
* determine what type of processor is in the computer, as
* described in application note AP-485 (Intel Order #241618).
* Wincpuid() returns the CPU type as an integer (that is, 
* 2 bytes, a WORD) in the AX register.
*
* Returns:
*  0 = 8086/88
*  2 = 80286
*  3 = 80386
*  4 = 80486
*  5 = Pentium(R) Processor
*  6 = PentiumPro(R) Processor
*  7 or higher = Processor beyond the PentiumPro6(R) Processor
*
*  Note: This function also sets the global variable clone_flag
***************************************************************/
WORD  wincpuid();


/***************************************************************
* WORD wincpuidext()
* ==================
* Similar to wincpuid(), but returns more data, in the order
* reflecting the actual output of a CPUID instruction execution:
*
* Returns:
* AX(15:14) = Reserved (mask these off in the calling code 
*				before using)
* AX(13:12) = Processor type (00=Standard OEM CPU, 01=OverDrive,
*				10=Dual CPU, 11=Reserved)
* AX(11:8)  = CPU Family (the same 4-bit quantity as wincpuid())
* AX(7:4)   = CPU Model, if the processor supports the CPUID 
*				opcode; zero otherwise
* AX(3:0)   = Stepping #, if the processor supports the CPUID 
*				opcode; zero otherwise
*
*  Note: This function also sets the global variable clone_flag
***************************************************************/
WORD wincpuidext(CString &vendorName);


/***************************************************************
* DWORD wincpufeatures()
* ======================
* Wincpufeatures() returns the CPU features flags as a DWORD 
*    (that is, 32 bits).
*
* Inputs: none
*
* Returns:
*   0 = Processor which does not execute the CPUID instruction.
*          This includes 8086, 8088, 80286, 80386, and some 
*		   older 80486 processors.                       
*
* Else
*   Feature Flags (refer to App Note AP-485 for description).
*      This DWORD was put into EDX by the CPUID instruction.
*
*	Current flag assignment is as follows:
*
*		bit31..10   reserved (=0)
*		bit9=1      CPU contains a local APIC (iPentium-3V)
*		bit8=1      CMPXCHG8B instruction supported
*		bit7=1      machine check exception supported
*		bit6=0      reserved (36bit-addressing & 2MB-paging)
*		bit5=1      iPentium-style MSRs supported
*		bit4=1      time stamp counter TSC supported
*		bit3=1      page size extensions supported
*		bit2=1      I/O breakpoints supported
*		bit1=1      enhanced virtual 8086 mode supported
*		bit0=1      CPU contains a floating-point unit (FPU)
*
*	Note: New bits will be assigned on future processors... see
*         processor data books for updated information
*
*	Note: This function also sets the global variable clone_flag
***************************************************************/
DWORD wincpufeatures(CString &vendorName);



#ifndef _WIN32
unsigned short GetBaseMemC()
{
	// get memory below 1-MB boundary
	// using run-time library functions
	unsigned short base;
	outp( 0x70, 0x15 );
	base = inp( 0x71 ); //retrieve low byte
	outp( 0x70, 0x16 ); 
	base += inp(0x71) << 8; //retieve high byte,
							//shift and add to base
	return base;  // return K's of base memory
}

unsigned short GetExpMemC()
{
	// get memory above 1-MB boundary
	// using run-time library functions
	unsigned short extend;
	outp( 0x70, 0x17 );
	base = inp( 0x71 ); //retrieve low byte
	outp( 0x70, 0x18 ); 
	base += inp(0x71) << 8; //retieve high byte,
							//shift and add to extend
	return extend;  // return K's of expansion memory		
}
#endif

int32 FE_SystemRAM( void )
{
	int32 systemRAM = -1;
#ifdef _WIN32
	MEMORYSTATUS MemoryStatus;

	memset( &MemoryStatus, 0, sizeof(MEMORYSTATUS));
	MemoryStatus.dwLength = sizeof(MEMORYSTATUS);

	GlobalMemoryStatus( &MemoryStatus );
	//return physical memory size in K
	systemRAM = MemoryStatus.dwTotalPhys / 1024 ;
#else
	//return physical memory size in K
	int32 expansionMem = GetExpMemC();
	int32 baseMem = GetBaseMemC();
	systemRAM = expansionMem + baseMem;
#endif
return systemRAM;
}

int32 FE_SystemClockSpeed( void )
{
	int32 clockSpeed = -1;
#ifdef _WIN32
	OSVERSIONINFO OSVersionInfo;
	memset( &OSVersionInfo, 0, sizeof(OSVERSIONINFO));
	OSVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	if(GetVersionEx( &OSVersionInfo ))
	{
		if(OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ||
			OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) //Windows 9x
		{
			SYSTEM_INFO SystemInfo;
			GetSystemInfo( &SystemInfo );
			if( SystemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL )
			{
				if(!GetIntelCPUSpeed(clockSpeed))
					clockSpeed = -1;
			}
			else
			if( SystemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ALPHA )
			{
				return -1;
			}
			else
			if( SystemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_MIPS )
			{
				return -1;
			}
			else
			if( SystemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_PPC )
			{
				return -1;
			}
			else
			if( SystemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_UNKNOWN )
				return -1;
			else
			{
				ASSERT(FALSE);
				return -1;
			}
		}
		else //if Win32s we must be running on Win3.x so Intel architecture
		if(	OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32s )
		{
			if(!GetIntelCPUSpeed(clockSpeed))
				clockSpeed = -1;
		}
		else
		{
			ASSERT(FALSE);
			return -1;
		}
	}
#else
	if(!GetIntelCPUSpeed(clockSpeed))
		clockSpeed = -1;
#endif
	return clockSpeed;
}

BOOL GetIntelCPUSpeed(int32 &processorSpeed )
{
	BOOL bRet = FALSE;
	// VARIABLE STRUCTURE DEFINITIONS //////////////////////////////
	struct FREQ_INFO cpu_speed;		// Return variable 
										//   structure for 
										//   cpuspeed 
										//   routine
			
			
	cpu_speed = cpuspeed(0);
				
	if ( cpu_speed.in_cycles == 0 && cpu_speed.ex_ticks  == 0 )
	{
		/*			 
		 sprintf(buf,
			 	"This processor cannot be accurately "
			 	"timed with this program.\n The "
			 	"processor could be"
			 	"below 80386 level.");
		 MessageBox(NULL,buf,"error", MB_ICONINFORMATION );
		 */
		bRet = FALSE;
	}
	else
	{
		/*
		sprintf(buf,
				"Clock Cycles: %lu cycles\n"
				"Elapsed Time: %luus\n"
				"Raw Clock Frequency: %luMHz\n"
				"Normalized Frequency: %luMHz",
				cpu_speed.in_cycles,
				cpu_speed.ex_ticks,
				cpu_speed.raw_freq,
				cpu_speed.norm_freq);          

		MessageBox(NULL,buf,"32-bit cpuspeed", 
					MB_ICONINFORMATION );
		*/
		processorSpeed = cpu_speed.norm_freq;
		bRet = TRUE;
	}
	return bRet;
}


BOOL GetIntelCPUInfoViaCPUID(CString &systemArchitecture,
			int32 &processorType, int32 &processorFamily, 
			int32 &processorModel, int32 &processorStepping, int32 &processorFeatures,
			CString &vendor)
{
	//Intel Only!!!
	systemArchitecture = csINTEL;
	int32 processorExtensions = 0;
	processorExtensions = wincpuidext(vendor);

	processorStepping = processorExtensions & 0x0f;		
	processorModel = (processorExtensions & 0x0f0) >> 4;
	processorFamily = (processorExtensions & 0x0f00) >> 8;
	processorType = (processorExtensions & 0x03000) >> 12;

	processorFeatures = wincpufeatures(vendor);

	return TRUE;
}

char *CreateNonIntelCPUInfoString(CString &systemArchitecture,
								  int32 &processorModel, int32 &processorStepping)
{
	CString tmpStr;
	CString CPUInfoString;
	ASSERT( !systemArchitecture.IsEmpty() );

	if( !systemArchitecture.IsEmpty() )
	{
		CPUInfoString = csARCHITECTURE + "=";
		CPUInfoString += systemArchitecture;
	}

	if( systemArchitecture == csALPHA )
	{
		if( processorModel > -1 )
		{
			if( !CPUInfoString.IsEmpty() )
				CPUInfoString += ";";
			CPUInfoString += csMODEL + "=A";
			tmpStr.Format("%.2X", processorModel );
			CPUInfoString += tmpStr;
		}
		if( processorStepping > -1 )
		{
			if( !CPUInfoString.IsEmpty() )
				CPUInfoString += ";"; 
			CPUInfoString += csPASS + "=";
			tmpStr.Format("%.2X", processorStepping);
			CPUInfoString += tmpStr;
		}
	}
	else
	if( systemArchitecture == csMIPS )
	{
		if( processorModel > -1 )
		{
			if( !CPUInfoString.IsEmpty() )
				CPUInfoString += ";";
			CPUInfoString += csREVISION + "=";
			tmpStr.Format("%.2X", processorModel);
			CPUInfoString += tmpStr;
		}
	}
	else
	if( systemArchitecture == csPPC )
	{
		if( processorModel > -1 )
		{
			if( !CPUInfoString.IsEmpty() )
				CPUInfoString += ";";
			CPUInfoString += csVERSION + "=";
			tmpStr.Format("%.2X", processorModel);
			CPUInfoString += tmpStr;
		}

		if( processorStepping > -1 )
		{		
			CPUInfoString += ".";
			tmpStr.Format("%.2X", processorStepping);
			CPUInfoString += tmpStr;
		}
	}
	char * pstr = NULL;
	int len = CPUInfoString.GetLength();
	if(len > 0 )
	{
		pstr = new char[ len + 1];
		if( pstr != NULL )
			strcpy( pstr, CPUInfoString );
	}	
	return pstr;
}
			
			
char *CreateIntelCPUInfoString(CString &systemArchitecture,
			int32 processorType, int32 processorFamily, 
			int32 processorModel, int32 processorStepping, int32 processorFeatures,
			CString &vendor)
{
	CString tmpStr;
	CString CPUInfoString;
	ASSERT( !systemArchitecture.IsEmpty() );

	if( !systemArchitecture.IsEmpty() )
	{
		CPUInfoString = csARCHITECTURE + "=";
		CPUInfoString += systemArchitecture;
	}
	if( processorType > -1 )
	{
		if( !CPUInfoString.IsEmpty() )
			CPUInfoString += ";";
		CPUInfoString += csTYPE + "=";
		tmpStr.Format("%d", processorType);
		CPUInfoString += tmpStr;
	}
	if( processorFamily > -1 )
	{
		if( !CPUInfoString.IsEmpty() )
			CPUInfoString += ";";
		CPUInfoString += csFAMILY + "=";
		tmpStr.Format("%d", processorFamily);
		CPUInfoString += tmpStr;
	}
	if( processorModel > -1 )
	{
		if( !CPUInfoString.IsEmpty() )
			CPUInfoString += ";";
		CPUInfoString += csMODEL + "=";
		tmpStr.Format("%d", processorModel);
		CPUInfoString += tmpStr;
	}
	if( processorStepping > -1 )
	{
		if( !CPUInfoString.IsEmpty() )
			CPUInfoString += ";";
		CPUInfoString += csSTEPPING + "=";
		tmpStr.Format("%d", processorStepping);
		CPUInfoString += tmpStr;
	}
	if( processorFeatures > -1 )
	{
		if( !CPUInfoString.IsEmpty() )
			CPUInfoString += ";";
		CPUInfoString += csFEATURES + "=";
		tmpStr.Format("0x%x", processorFeatures);
		CPUInfoString += tmpStr;
	}

	if( !CPUInfoString.IsEmpty() )
		CPUInfoString += ";";
	CPUInfoString += csVENDOR + "=";
	if( !vendor.IsEmpty() )
		CPUInfoString += vendor;
	else
		CPUInfoString += csUNKNOWN;

	char * pstr = NULL;
	int len = CPUInfoString.GetLength();
	if(len > 0 )
	{
		pstr = new char[ len + 1];
		if( pstr != NULL )
			strcpy( pstr, CPUInfoString );
	}
	
	return pstr;
}

char *FE_SystemCPUInfo(void)
{
	CString systemArchitecture;
	int32 processorType = -1; 
	int32 processorFamily = -1; 
	int32 processorModel = -1; 
	int32 processorStepping = -1; 
	int32 processorFeatures = -1;
	CString vendor;

#ifdef _WIN32
	OSVERSIONINFO OSVersionInfo;
	memset( &OSVersionInfo, 0, sizeof(OSVERSIONINFO));
	OSVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
	if(GetVersionEx( &OSVersionInfo ))
	{
		//if Win32s is the platform
		if(	OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32s )
		{
				if(GetIntelCPUInfoViaCPUID(systemArchitecture,	
							processorType, processorFamily, processorModel, 
							processorStepping, processorFeatures, vendor ))
					return CreateIntelCPUInfoString(systemArchitecture,	
							processorType, processorFamily, processorModel, 
							processorStepping, processorFeatures, vendor );
				else
					return NULL;
		}
		else
		if(OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT ||
			OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) //Windows 9x
		{
			SYSTEM_INFO SystemInfo;
			GetSystemInfo( &SystemInfo );
			processorFamily = SystemInfo.wProcessorLevel;
			if( SystemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL )
			{	
				if(OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS ) 
				{	//Windows 9x
					if(GetIntelCPUInfoViaCPUID(systemArchitecture,	
							processorType, processorFamily, processorModel, 
							processorStepping, processorFeatures, vendor ))
						return CreateIntelCPUInfoString(systemArchitecture,	
							processorType, processorFamily, processorModel, 
							processorStepping, processorFeatures, vendor );
					else
						return NULL;
				}
				else //Windows NT on Intel
				{
					//Might as well use CPUID; it provides a little more information
					if(GetIntelCPUInfoViaCPUID(systemArchitecture,	
							processorType, processorFamily, processorModel, 
							processorStepping, processorFeatures, vendor ))
						return CreateIntelCPUInfoString(systemArchitecture,	
							processorType, processorFamily, processorModel, 
							processorStepping, processorFeatures, vendor );
					else
						return NULL;
					
	//				strcpy(systemArchitecture, INTEL);
	//				if( SystemInfo.dwProcessorType == PROCESSOR_INTEL_386 ||
	//					SystemInfo.dwProcessorType == PROCESSOR_INTEL_486 )
	//				{
	//					//wProcessor type of form xxyz; if xx != 0xFF
	//					if( ((SystemInfo.wProcessorRevision >> 8) & 0xff ) != 0xff )
	//					{
	//						//then xx + 'A' is the stepping letter						
	//						processorStepping = ((SystemInfo.wProcessorRevision >> 8) & 0xff) + 'A';
	//						//this is actually the minor stepping
	//						processorModel = (SystemInfo.wProcessorRevision & 0x0ff);
	//					}
	//					else
	//					if( ((SystemInfo.wProcessorRevision >> 8) & 0xff ) == 0xff )
	//					{
	//						processorModel = (SystemInfo.wProcessorRevision & 0x0f0) >> 4;
	//						processorStepping = (SystemInfo.wProcessorRevision & 0x0f);
	//					}
	//				}
	//				else  //processor must be > x486
	//				{
	//					processorModel = (SystemInfo.wProcessorRevision & 0x0ff00) >> 8;
	//					processorStepping = (SystemInfo.wProcessorRevision & 0x0ff);
	//				}
				}
			}
			else
			if( SystemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_ALPHA )
			{
				systemArchitecture = csALPHA;
				processorModel = (SystemInfo.wProcessorRevision & 0x0ff00) >> 8;
				//"Pass" on Alpha; display as "Model 'A'+xx, Pass yy"
				processorStepping = (SystemInfo.wProcessorRevision & 0x0ff);
				return CreateNonIntelCPUInfoString(systemArchitecture, 
											processorModel, processorStepping);
			}
			else
			if( SystemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_MIPS )
			{
				systemArchitecture = csMIPS;
				//"Revision Number" on MIPS
				processorModel = (SystemInfo.wProcessorRevision & 0x0ff);
				return CreateNonIntelCPUInfoString(systemArchitecture, 
											processorModel, processorStepping);
			}
			else
			if( SystemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_PPC )
			{
				systemArchitecture = csPPC;
				//display as xx.yy on PPC
				processorModel = (SystemInfo.wProcessorRevision & 0x0ff00) >> 8;
				processorStepping = (SystemInfo.wProcessorRevision & 0x0ff);
				return CreateNonIntelCPUInfoString(systemArchitecture, 
											processorModel, processorStepping);
			}
			else
			if( SystemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_UNKNOWN )
			{
				systemArchitecture = csUNKNOWN;
				return CreateNonIntelCPUInfoString(systemArchitecture, 
											processorModel, processorStepping);
			}
			else
			{
				ASSERT(FALSE);
				systemArchitecture = csUNKNOWN;
				return CreateNonIntelCPUInfoString(systemArchitecture, 
											processorModel, processorStepping);
			}			
		}
		ASSERT(FALSE); //Unknown operating system
	}
	return NULL;
#else //WIN16
	if(GetIntelCPUInfoViaCPUID(systemArchitecture, processorType, 
							processorFamily, processorModel, processorStepping,
							processorFeatures, vendor ))
		return CreateIntelCPUInfoString(systemArchitecture,	
							processorType, processorFamily, processorModel, 
							processorStepping, processorFeatures, vendor );
	else
		return NULL;
#endif
}








// CONSTANT DEFINITIONS ////////////////////////////////////////
#define CLONE_MASK		0x8000	// Mask to be 'OR'ed with proc-
#define MAXCLOCKS		150		// Maximum number of cycles per
								//   BSF instruction
	// ACCURACY AFFECTING CONSTANTS ////////////////////////////
#define ITERATIONS		4000	// Number of times to repeat BSF
								//   instruction in samplings.
								//   Initially set to 4000.

#define MAX_TRIES		20		// Maximum number of samplings
								//   to allow before giving up
								//   and returning current 
								//   average. Initially set to
								//   20.
	
#define TOLERANCE		1		// Number of MHz to allow
								//   samplings to deviate from
								//   average of samplings.
								//   Initially set to 2.

#define	SAMPLINGS		10		// Number of BSF sequence 
								//   samplings to make.
								//   Initially set to 10.


typedef unsigned short ushort;
typedef unsigned long  ulong;

// Global Variable /////////////////////////////////////////////
int clone_flag;				// Flag to show whether processor
							//   is non-Intel




// OPCODE DEFINITIONS //////////////////////////////////////////
#define CPU_ID _asm _emit 0x0f _asm _emit 0xa2 	
										// CPUID instruction

#define RDTSC  _asm _emit 0x0f _asm _emit 0x31	
										// RDTSC instruction

// Private Function Declarations ///////////////////////////////

/***************************************************************
* static WORD check_clone()
*
* Inputs: none
*
* Returns:
*   1      if processor is clone (limited detection ability)
*   0      otherwise
***************************************************************/
static WORD check_clone();


/***************************************************************
* static WORD check_8086()
*
* Inputs: none
*
* Returns: 
*   0      if processor 8086
*   0xffff otherwise
***************************************************************/
static WORD check_8086();


/***************************************************************
* static WORD check_80286()
*
* Inputs: none
*
* Returns:
*   2      if processor 80286
*   0xffff otherwise
***************************************************************/
static WORD check_80286();


/***************************************************************
* static WORD check_80386()
*
* Inputs: none
*
* Returns:
*   3      if processor 80386
*   0xffff otherwise
***************************************************************/
static WORD check_80386();


/***************************************************************
* static WORD check_IDProc()
* ==========================
* Check_IDProc() uses the CPUID opcode to find the family type
* of the host processor.
*
* Inputs: none
*
* Returns:
*  CPU Family (i.e. 4 if Intel 486, 5 if Pentium(R) Processor)
*
*  Note: This function also sets the global variable clone_flag
***************************************************************/
static WORD check_IDProc();

//end of prototypes //////////////////////////////////////////////


#define ROUND_THRESHOLD		6

// Tabs set at 4
static struct FREQ_INFO GetRDTSCCpuSpeed();
static struct FREQ_INFO GetBSFCpuSpeed(ulong cycles);

// Number of cycles needed to execute a single BSF instruction.
//    Note that processors below i386(tm) are not supported.
static ulong processor_cycles[] = {
	00,  00,  00, 115, 47, 43, 
	38,  38,  38, 38,  38, 38, 
};

/***************************************************************
* wincpuidsupport()
*
* Inputs: none
*
* Returns:
*  1 = CPUID opcode is supported
*  0 = CPUID opcode is not supported
***************************************************************/

WORD wincpuidsupport() {
	int cpuid_support = 1;

	_asm {
        pushfd					// Get original EFLAGS
		pop		eax
		mov 	ecx, eax
        xor     eax, 200000h	// Flip ID bit in EFLAGS
        push    eax				// Save new EFLAGS value on
        						//   stack
        popfd					// Replace current EFLAGS value
        pushfd					// Get new EFLAGS
        pop     eax				// Store new EFLAGS in EAX
        xor     eax, ecx		// Can not toggle ID bit,
        jnz     support			// Processor=80486
		
		mov cpuid_support,0		// Clear support flag
support:
      }
	
	return cpuid_support;

} // wincpuidsupport()


/***************************************************************
* wincpuid()
*
* Inputs: none
*
* Returns:
*  0 = 8086/88
*  2 = 80286
*  3 = 80386
*  4 = 80486
*  5 = Pentium(R) Processor
*  6 = PentiumPro(R) Processor
*  7 or higher = Processor beyond the PentiumPro6(R) Processor
*
*  Note: This function also sets the global variable clone_flag
***************************************************************/

WORD wincpuid() {

	WORD cpuid;
	
	if ( wincpuidsupport() ) 	// Determine whether CPUID 
								//   opcode is supported
		cpuid=check_IDProc();

	else {
		
		clone_flag=check_clone();
	
		cpuid=check_8086();			// Will return FFFFh or 0
		if (cpuid == 0) goto end;
	
    	cpuid=check_80286();       	// Will return FFFFh or 2
		if (cpuid == 2) goto end;

    	cpuid=check_80386();       	// Will return FFFFh or 3
		if (cpuid == 3) goto end;    // temporarily commented out.
        
        cpuid=4;		// If the processor does not support CPUID,
        				//  is not an 8086, 80286, or 80386, assign
        				//  processor to be an 80486
	}

end:
	if (clone_flag)
		cpuid = cpuid | CLONE_MASK;	// Signify that a clone has been
									//   detected by setting MSB high 

   	return cpuid;

} // wincpuid ()


/***************************************************************
* wincpufeatures()
*
* Inputs: none
*
* Returns:
*   0 = Processor which does not execute the CPUID instruction.
*          This includes 8086, 8088, 80286, 80386, and some 
*		   older 80486 processors.                       
*
* Else
*   Feature Flags (refer to App Note AP-485 for description).
*      This DWORD was put into EDX by the CPUID instruction.
*
*	Current flag assignment is as follows:
*
*		bit31..10   reserved (=0)
*		bit9=1      CPU contains a local APIC (iPentium-3V)
*		bit8=1      CMPXCHG8B instruction supported
*		bit7=1      machine check exception supported
*		bit6=0      reserved (36bit-addressing & 2MB-paging)
*		bit5=1      iPentium-style MSRs supported
*		bit4=1      time stamp counter TSC supported
*		bit3=1      page size extensions supported
*		bit2=1      I/O breakpoints supported
*		bit1=1      enhanced virtual 8086 mode supported
*		bit0=1      CPU contains a floating-point unit (FPU)
*
*	Note: New bits will be assigned on future processors... see
*         processor data books for updated information
*
*	Note: This function also sets the global variable clone_flag
***************************************************************/

DWORD wincpufeatures(CString &vendorName) {

	int i=0;
	DWORD cpuff=0x00000000;
	BYTE vendor_id[]="------------";
	BYTE intel_id[]="GenuineIntel";

	if ( wincpuidsupport() ) {

_asm {      

		xor     eax, eax		// Set up for CPUID instruction
        
		CPU_ID                  // Get and save vendor ID

        mov     dword ptr vendor_id, ebx
        mov     dword ptr vendor_id[+4], edx
        mov     dword ptr vendor_id[+8], ecx
}
vendorName = vendor_id;

for (i=0;i<12;i++)
{
	if (!(vendor_id[i]==intel_id[i]))
		clone_flag = 1;    
}

_asm {
         
		cmp     eax, 1			// Make sure 1 is valid input 
        						//   for CPUID
        
        jl      end_cpuff		// If not, jump to end
        xor     eax, eax
        inc		eax
        CPU_ID					// Get family/model/stepping/
        						//   features

		mov		cpuff, edx

end_cpuff:
		mov		eax, cpuff
      }
	}

	return cpuff;

} // wincpufeatures()


/***************************************************************
* wincpuidext()
*
* Inputs: none
*
* Returns:
* AX(15:14) = Reserved (mask these off in the calling code 
*				before using)
* AX(13:12) = Processor type (00=Standard OEM CPU, 01=OverDrive,
*				10=Dual CPU, 11=Reserved)
* AX(11:8)  = CPU Family (the same 4-bit quantity as wincpuid())
* AX(7:4)   = CPU Model, if the processor supports the CPUID 
*				opcode; zero otherwise
* AX(3:0)   = Stepping #, if the processor supports the CPUID 
*				opcode; zero otherwise
*
*  Note: This function also sets the global variable clone_flag
***************************************************************/

WORD wincpuidext(CString &vendorName) {

		int i=0;
		WORD cpu_type=0x0000;
		WORD cpuidext=0x0000;
		BYTE vendor_id[]="------------";
		BYTE intel_id[]="GenuineIntel";

	if ( wincpuidsupport() ) {

_asm {      

		xor     eax, eax		// Set up for CPUID instruction
        
		CPU_ID                  // Get and save vendor ID

		mov     dword ptr vendor_id, ebx
		mov     dword ptr vendor_id[+4], edx
		mov     dword ptr vendor_id[+8], ecx
}
vendorName = vendor_id;

for (i=0;i<12;i++)
{
	if (!(vendor_id[i]==intel_id[i]))
		clone_flag = 1;    
}

_asm {
        
		cmp     eax, 1			// Make sure 1 is valid input 
        						//   for CPUID
        
        jl      end_cpuidext	// If not, jump to end
        xor     eax, eax
        inc		eax
        CPU_ID					// Get family/model/stepping/
        						//   features

		mov		cpuidext, ax

end_cpuidext:
		mov		ax, cpuidext
    	}
	}
	else {

	cpu_type = wincpuid();		// If CPUID opcode is not
	cpuidext = cpu_type << 8;	//   supported, put family
								//   value in extensions and
	}							//   return
	
	return cpuidext;

} // wincpuidext()


static struct FREQ_INFO GetBSFCpuSpeed(ulong cycles)
{
	// If processor does not support time 
	//   stamp reading, but is at least a 
	//   386 or above, utilize method of 
	//   timing a loop of BSF instructions 
	//   which take a known number of cycles
	//   to run on i386(tm), i486(tm), and
	//   Pentium(R) processors.
	LARGE_INTEGER t0,t1;			// Variables for High-
									//   Resolution Performance
									//   Counter reads

	ulong freq  =0;			// Most current frequ. calculation

	ulong  ticks;					// Microseconds elapsed 
									//   during test
	
	LARGE_INTEGER count_freq;		// High Resolution 
									//   Performance Counter 
									//   frequency

	int i;						// Temporary Variable

	ulong current = 0;          // Variable to store time
									//   elapsed during loop of
									//   of BSF instructions

	ulong lowest  = ULONG_MAX;	// Since algorithm finds 
									//   the lowest value out of
									//   a set of samplings, 
									//   this variable is set 
									//   intially to the max 
									//   unsigned long value). 
									//   This guarantees that 
									//   the initialized value 
									//   is not later used as 
									//   the least time through 
									//   the loop.

	struct FREQ_INFO cpu_speed;

	memset(&cpu_speed, 0x00, sizeof(cpu_speed));

	if ( !QueryPerformanceFrequency ( &count_freq ) )
		return cpu_speed;

	for ( i = 0; i < SAMPLINGS; i++ ) { // Sample Ten times. Can
									 //   be increased or 
									 //   decreased depending
									 //   on accuracy vs. time
									 //   requirements

		QueryPerformanceCounter(&t0);	// Get start time

			_asm 
			{
				
					mov eax, 80000000h	
					mov bx, ITERATIONS		
								// Number of consecutive BSF 
								//   instructions to execute. 
								//   Set identical to 
								//   nIterations constant in
								//   speed.h
	           
				loop1:	bsf ecx,eax
       
	       				dec	bx
						jnz	loop1
			}
							 
		QueryPerformanceCounter(&t1);	// Get end time
		current = (ulong) t1.LowPart - (ulong) t0.LowPart;	
								// Number of external ticks is
								//   difference between two
								//   hi-res counter reads.

		if ( current < lowest )		// Take lowest elapsed
			lowest = current;		//   time to account
	}								//   for some samplings
										//   being interrupted
										//   by other operations 
		 
	ticks = lowest;

	// Note that some seemingly arbitrary mulitplies and
	//   divides are done below. This is to maintain a 
	//   high level of precision without truncating the 
	//   most significant data. According to what value 
	//   ITERATIIONS is set to, these multiplies and
	//   divides might need to be shifted for optimal
	//   precision.

	ticks = ticks * 100000;	
						// Convert ticks to hundred
						//   thousandths of a tick
			
	ticks = ticks / ( count_freq.LowPart/10 );		
						// Hundred Thousandths of a 
						//   Ticks / ( 10 ticks/second )
						//   = microseconds (us)
		
	if ( ticks%count_freq.LowPart > count_freq.LowPart/2 )	
		ticks++;				// Round up if necessary
			
	freq = cycles/ticks;		// Cycles / us  = MHz

	cpu_speed.raw_freq  = freq;
    if ( cycles%ticks > ticks/2 )
   		freq++;					// Round up if necessary	

	cpu_speed.in_cycles = cycles;	// Return variable structure
	cpu_speed.ex_ticks  = ticks;	//   determined by one of 
	cpu_speed.norm_freq = freq;

	return cpu_speed;
}	

static struct FREQ_INFO GetRDTSCCpuSpeed()
{
	struct FREQ_INFO cpu_speed;
	LARGE_INTEGER t0,t1;			// Variables for High-
									//   Resolution Performance
									//   Counter reads

	ulong freq  =0;			// Most current frequ. calculation
	ulong freq2 =0;			// 2nd most current frequ. calc.
	ulong freq3 =0;			// 3rd most current frequ. calc.
	
	ulong total;			// Sum of previous three frequency
							//   calculations

	int tries=0;			// Number of times a calculation has
							//   been made on this call to 
							//   cpuspeed

	ulong  total_cycles=0, cycles;	// Clock cycles elapsed 
									//   during test
	
	ulong  stamp0, stamp1;			// Time Stamp Variable 
									//   for beginning and end 
									//   of test

	ulong  total_ticks=0, ticks;	// Microseconds elapsed 
									//   during test
	
	LARGE_INTEGER count_freq;		// High Resolution 
									//   Performance Counter 
									//   frequency

#ifdef WIN32
	int iPriority;
	HANDLE hThread = GetCurrentThread();
#endif // WIN32;

	memset(&cpu_speed, 0x00, sizeof(cpu_speed));

	if ( !QueryPerformanceFrequency ( &count_freq ) )
		return cpu_speed;

	// On processors supporting the Read 
	//   Time Stamp opcode, compare elapsed
	//   time on the High-Resolution Counter
	//   with elapsed cycles on the Time 
	//   Stamp Register.
	
	do {			// This do loop runs up to 20 times or
	   				//   until the average of the previous 
	   				//   three calculated frequencies is 
	   				//   within 1 MHz of each of the 
	   				//   individual calculated frequencies. 
					//   This resampling increases the 
					//   accuracy of the results since
					//   outside factors could affect this
					//   calculation
			
		tries++;		// Increment number of times sampled
						//   on this call to cpuspeed
			
		freq3 = freq2;	// Shift frequencies back to make
		freq2 = freq;	//   room for new frequency 
						//   measurement

    	QueryPerformanceCounter(&t0);	
    					// Get high-resolution performance 
    					//   counter time
			
		t1.LowPart = t0.LowPart;		// Set Initial time
		t1.HighPart = t0.HighPart;

#ifdef WIN32
		iPriority = GetThreadPriority(hThread);
		if ( iPriority != THREAD_PRIORITY_ERROR_RETURN )
		{
			SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
		}
#endif // WIN32

   		while ( (ulong)t1.LowPart - (ulong)t0.LowPart<50) {	  
   						// Loop until 50 ticks have 
   						//   passed	since last read of hi-
						//	 res counter. This accounts for
						//   overhead later.

			QueryPerformanceCounter(&t1);

			RDTSC;						// Read Time Stamp
			_asm {
				MOV stamp0, EAX
			}
		}
			
			
		t0.LowPart = t1.LowPart;		// Reset Initial 
		t0.HighPart = t1.HighPart;		//   Time

   		while ((ulong)t1.LowPart-(ulong)t0.LowPart<1000 ) {
   						// Loop until 1000 ticks have 
   						//   passed	since last read of hi-
   						//   res counter. This allows for
						//   elapsed time for sampling.
   			
				
   			QueryPerformanceCounter(&t1);
   			

			RDTSC;						// Read Time Stamp
			__asm {
				MOV stamp1, EAX
			}
		}

			

#ifdef WIN32
		// Reset priority
		if ( iPriority != THREAD_PRIORITY_ERROR_RETURN )
		{
			SetThreadPriority(hThread, iPriority);
		}
#endif // WIN32

       	cycles = stamp1 - stamp0;	// Number of internal 
        							//   clock cycles is 
        							//   difference between 
        							//   two time stamp 
        							//   readings.

    	ticks = (ulong) t1.LowPart - (ulong) t0.LowPart;	
								// Number of external ticks is
								//   difference between two
								//   hi-res counter reads.
	

		// Note that some seemingly arbitrary multiplies and
		//   divides are done below. This is to maintain a 
		//   high level of precision without truncating the 
		//   most significant data. According to what value 
		//   ITERATIONS is set to, these multiplies and
		//   divides might need to be shifted for optimal
		//   precision.

		ticks = ticks * 100000;	
							// Convert ticks to hundred
							//   thousandths of a tick
			
		ticks = ticks / ( count_freq.LowPart/10 );		
							// Hundred Thousandths of a 
							//   Ticks / ( 10 ticks/second )
							//   = microseconds (us)

		total_ticks += ticks;
		total_cycles += cycles;

		if ( ticks%count_freq.LowPart > count_freq.LowPart/2 )
			ticks++;			// Round up if necessary
			
		freq = cycles/ticks;	// Cycles / us  = MHz
        										
     	if ( cycles%ticks > ticks/2 )
       		freq++;				// Round up if necessary
          	
		total = ( freq + freq2 + freq3 );
							// Total last three frequency 
							//   calculations

	} while ( (tries < 3 ) || 		
	          (tries < 20)&&
	          ((abs(int(3 * freq -total)) > 3*TOLERANCE )||
	           (abs(int(3 * freq2-total)) > 3*TOLERANCE )||
	           (abs(int(3 * freq3-total)) > 3*TOLERANCE )));	
					// Compare last three calculations to 
	          		//   average of last three calculations.		

	// Try one more significant digit.
	freq3 = ( total_cycles * 10 ) / total_ticks;
	freq2 = ( total_cycles * 100 ) / total_ticks;


	if ( freq2 - (freq3 * 10) >= ROUND_THRESHOLD )
		freq3++;

	cpu_speed.raw_freq = total_cycles / total_ticks;
	cpu_speed.norm_freq = cpu_speed.raw_freq;

	freq = cpu_speed.raw_freq * 10;
	if( (freq3 - freq) >= ROUND_THRESHOLD )
		cpu_speed.norm_freq++;

	cpu_speed.ex_ticks = total_ticks;
	cpu_speed.in_cycles = total_cycles;

	return cpu_speed;
}


//CPU Speed functions
/***************************************************************
* CpuSpeed() -- Return the raw clock rate of the host CPU.
*
* Inputs:
*	clocks:		0: Use default value for number of cycles
*				   per BSF instruction.
*               -1: Use CMos timer to get cpu speed.
*   			Positive Integer: Use clocks value for number
*				   of cycles per BSF instruction.
*
* Returns:
*		If error then return all zeroes in FREQ_INFO structure
*		Else return FREQ_INFO structure containing calculated 
*       clock frequency, normalized clock frequency, number of 
*       clock cycles during test sampling, and the number of 
*       microseconds elapsed during the sampling.
***************************************************************/

struct FREQ_INFO cpuspeed(int clocks) 
{
	ulong  cycles;					// Clock cycles elapsed 
									//   during test
	
	ushort processor = wincpuid();	// Family of processor


	CString vendor;
	DWORD features = wincpufeatures(vendor);	// Features of Processor

	int manual=0;			// Specifies whether the user 
							//   manually entered the number of
							//   cycles for the BSF instruction.

	struct FREQ_INFO cpu_speed;		// Return structure for
									//   cpuspeed

	memset(&cpu_speed, 0x00, sizeof(cpu_speed));
	
	// Check for manual BSF instruction clock count
	if (clocks <= 0) {
		cycles = ITERATIONS * processor_cycles[processor];
	}
	else if (0 < clocks && clocks <= MAXCLOCKS)  {
		cycles = ITERATIONS * clocks;
		manual = 1;			// Toggle manual control flag.
							//   Note that this mode will not
							// 	 work properly with processors
							//   which can process multiple
							//   BSF instructions at a time.
							//   For example, manual mode
							//   will not work on a 
							//   PentiumPro(R)
	}

	if ( ( features&0x00000010 ) && !(manual) ) {						
						// On processors supporting the Read 
						//   Time Stamp opcode, compare elapsed
						//   time on the High-Resolution Counter
						//   with elapsed cycles on the Time 
						//   Stamp Register.
		if ( clocks == 0 )
			return GetRDTSCCpuSpeed();
		else
			return cpu_speed;
    }
	else if ( processor >= 3 ) {
		return GetBSFCpuSpeed(cycles);
	}

	return cpu_speed;

} // cpuspeed()


// Internal Private Functions //////////////////////////////////

/***************************************************************
* check_clone()
*
* Inputs: none
*
* Returns:
*   1      if processor is clone (limited detection ability)
*   0      otherwise
***************************************************************/

static WORD check_clone()
{
	short cpu_type=0;

	_asm 
		{
  					MOV AX,5555h	// Check to make sure this
					XOR DX,DX		//   is a 32-bit processor
					MOV CX,2h
					DIV CX			// Perform Division
					CLC
					JNZ no_clone
					JMP clone
		no_clone:	STC
		clone:		PUSHF
					POP AX          // Get the flags
					AND AL,1
					XOR AL,1        // AL=0 is probably Intel,
									//   AL=1 is a Clone
					
					MOV cpu_type, ax
		}
	
    cpu_type = cpu_type & 0x0001;
    
	return cpu_type;
		
} // check_clone()

/***************************************************************
* check_8086()
*
* Inputs: none
*
* Returns: 
*   0      if processor 8086
*   0xffff otherwise
***************************************************************/

static WORD check_8086()
{

		WORD cpu_type=0xffff;

_asm {
        pushf                   // Push original FLAGS
        pop     ax              // Get original FLAGS
        mov     cx, ax          // Save original FLAGS
        and     ax, 0fffh       // Clear bits 12-15 in FLAGS
        push    ax              // Save new FLAGS value on stack
        popf                    // Replace current FLAGS value
        pushf                   // Get new FLAGS
        pop     ax              // Store new FLAGS in AX
        and     ax, 0f000h      // If bits 12-15 are set, then
        cmp     ax, 0f000h      //   processor is an 8086/8088
        mov     cpu_type, 0    	// Turn on 8086/8088 flag
        je      end_8086    	// Jump if processor is 8086/
        						//   8088
        mov		cpu_type, 0ffffh
end_8086:
		push 	cx
		popf
		mov		ax, cpu_type

      }
	
	return cpu_type;

} // check_8086()



/***************************************************************
* check_80286()
*
* Inputs: none
*
* Returns:
*   2      if processor 80286
*   0xffff otherwise
***************************************************************/

static WORD check_80286()
{

		WORD cpu_type=0xffff;

_asm {
		pushf
		pop		cx
		mov		bx, cx
        or      cx, 0f000h      // Try to set bits 12-15
        push    cx              // Save new FLAGS value on stack
        popf                    // Replace current FLAGS value
        pushf                   // Get new FLAGS
        pop     ax              // Store new FLAGS in AX
        and     ax, 0f000h      // If bits 12-15 are clear
        
        mov     cpu_type, 2     // Processor=80286, turn on 
        						//   80286 flag
        
        jz      end_80286       // If no bits set, processor is 
        						//   80286
		
		mov		cpu_type, 0ffffh
end_80286:
		push	bx
		popf
		mov		ax, cpu_type

      }
	
	return cpu_type;

} // check_80286()



/***************************************************************
* check_80386()
*
* Inputs: none
*
* Returns:
*   3      if processor 80386
*   0xffff otherwise
***************************************************************/

static WORD check_80386()
{

		WORD cpu_type=0xffff;

_asm {   
		mov 	bx, sp
		and		sp, not 3
        pushfd					// Push original EFLAGS 
        pop     eax				// Get original EFLAGS
        mov     ecx, eax		// Save original EFLAGS
        xor     eax, 40000h		// Flip AC bit in EFLAGS
        
        push    eax             // Save new EFLAGS value on
        						//   stack
        
        popfd                   // Replace current EFLAGS value
        pushfd					// Get new EFLAGS
        pop     eax             // Store new EFLAGS in EAX
        
        xor     eax, ecx        // Can't toggle AC bit, 
        						//   processor=80386
        
        mov     cpu_type, 3		// Turn on 80386 processor flag
        jz      end_80386		// Jump if 80386 processor
		mov		cpu_type, 0ffffh
end_80386:
		push	ecx
		popfd
		mov		sp, bx
		mov		ax, cpu_type
		and		eax, 0000ffffh
      }

	return cpu_type;

} // check_80386()



/***************************************************************
* check_IDProc()
*
* Inputs: none
*
* Returns:
*  CPU Family (i.e. 4 if Intel 486, 5 if Pentium(R) Processor)
*
*  Note: This function also sets the global variable clone_flag
***************************************************************/

static WORD check_IDProc() {

		int i=0;
		WORD cpu_type=0xffff;
		BYTE stepping=0;
		BYTE model=0;
		BYTE vendor_id[]="------------";
		BYTE intel_id[]="GenuineIntel";

_asm {      

        xor     eax, eax		// Set up for CPUID instruction
        
        CPU_ID                  // Get and save vendor ID

        mov     dword ptr vendor_id, ebx
        mov     dword ptr vendor_id[+4], edx
        mov     dword ptr vendor_id[+8], ecx
}

for (i=0;i<12;i++)
{
	if (!(vendor_id[i]==intel_id[i]))
		clone_flag = 1;    
}

_asm {

        cmp     eax, 1			// Make sure 1 is valid input 
        						//   for CPUID
        
        jl      end_IDProc		// If not, jump to end
        xor     eax, eax
        inc		eax
        CPU_ID					// Get family/model/stepping/
        						//   features

		mov 	stepping, al
		and		stepping, 0x0f //0fh
		
		and 	al, 0f0h
		shr		al, 4
		mov 	model, al
		
		and		eax, 0f00h
        shr     eax, 8			// Isolate family
		and		eax, 0fh
        mov     cpu_type, ax	// Set _cpu_type with family

end_IDProc:
		mov		ax, cpu_type
      }
	
	return cpu_type;

} // Check_IDProc()


