Subversion Repositories AndroidProjects

Rev

Blame | Last modification | View Log | RSS feed

//#define SEXY_TRACING_ENABLED
//#define SEXY_PERF_ENABLED
//#define SEXY_MEMTRACE

#include "SexyAppBase.h"
#include "SEHCatcher.h"
#include "WidgetManager.h"
#include "Widget.h"
#include "Debug.h"
#include "KeyCodes.h"
#include "DDInterface.h"
#include "D3DInterface.h"
#include "D3DTester.h"
#include "DDImage.h"
#include "MemoryImage.h"
#include "HTTPTransfer.h"
#include "Dialog.h"
#include "..\ImageLib\ImageLib.h"
#include "DSoundManager.h"
#include "DSoundInstance.h"
#include "Rect.h"
#include "FModMusicInterface.h"
#include "PropertiesParser.h"
#include "PerfTimer.h"
#include "MTRand.h"
#include "ModVal.h"
#include <process.h>
#include <direct.h>
#include <fstream>
#include <time.h>
#include <math.h>
#include <regstr.h>
#include "SysFont.h"
#include "ResourceManager.h"
#include "BassMusicInterface.h"
#include "AutoCrit.h"
#include "Debug.h"
#include "../PakLib/PakInterface.h"
#include <string>
#include <shlobj.h>

#include "memmgr.h"

using namespace Sexy;

const int DEMO_FILE_ID = 0x42BEEF78;
const int DEMO_VERSION = 2;

SexyAppBase* Sexy::gSexyAppBase = NULL;

SEHCatcher Sexy::gSEHCatcher;

HMODULE gDDrawDLL = NULL;
HMODULE gDSoundDLL = NULL;
HMODULE gVersionDLL = NULL;

//typedef struct { UINT cbSize; DWORD dwTime; } LASTINPUTINFO;
typedef BOOL (WINAPI*GetLastInputInfoFunc)(LASTINPUTINFO *plii);
GetLastInputInfoFunc gGetLastInputInfoFunc = NULL;
static bool gScreenSaverActive = false;

#ifndef SPI_GETSCREENSAVERRUNNING
#define SPI_GETSCREENSAVERRUNNING 114
#endif


//HotSpot: 11 4
//Size: 32 32
unsigned char gFingerCursorData[] = {
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xe7, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xc3,
        0xff, 0xff, 0xff, 0xc3, 0xff, 0xff, 0xff, 0xc0, 0xff, 0xff, 0xff, 0xc0, 0x1f, 0xff, 0xff,
        0xc0, 0x07, 0xff, 0xff, 0xc0, 0x03, 0xff, 0xfc, 0x40, 0x01, 0xff, 0xfc, 0x00, 0x01, 0xff,
        0xfc, 0x00, 0x01, 0xff, 0xfc, 0x00, 0x01, 0xff, 0xff, 0x00, 0x01, 0xff, 0xff, 0x00, 0x01,
        0xff, 0xff, 0x80, 0x01, 0xff, 0xff, 0x80, 0x03, 0xff, 0xff, 0xc0, 0x03, 0xff, 0xff, 0xc0,
        0x03, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff, 0xe0, 0x07, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
        0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00,
        0x18, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x1b, 0x60, 0x00, 0x00, 0x1b, 0x68, 0x00,
        0x00, 0x1b, 0x6c, 0x00, 0x01, 0x9f, 0xec, 0x00, 0x01, 0xdf, 0xfc, 0x00, 0x00, 0xdf, 0xfc,
        0x00, 0x00, 0x5f, 0xfc, 0x00, 0x00, 0x7f, 0xfc, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x00, 0x3f,
        0xf8, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x00, 0x0f, 0xf0, 0x00, 0x00,
        0x0f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00
};

//HotSpot: 15 10
//Size: 32 32
unsigned char gDraggingCursorData[] = {
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xfc, 0x0f, 0xff, 0xff, 0xf0, 0x07, 0xff, 0xff, 0xe0,
        0x01, 0xff, 0xff, 0xe0, 0x00, 0xff, 0xff, 0xe0, 0x00, 0xff, 0xff, 0xe0, 0x00, 0xff, 0xff,
        0xe0, 0x00, 0xff, 0xfe, 0x60, 0x00, 0xff, 0xfc, 0x20, 0x00, 0xff, 0xfc, 0x00, 0x00, 0xff,
        0xfe, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x80, 0x00,
        0xff, 0xff, 0x80, 0x01, 0xff, 0xff, 0xc0, 0x01, 0xff, 0xff, 0xe0, 0x01, 0xff, 0xff, 0xf0,
        0x03, 0xff, 0xff, 0xf8, 0x03, 0xff, 0xff, 0xf8, 0x03, 0xff, 0xff, 0xf8, 0x03, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
        0x80, 0x00, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x0d, 0xb0, 0x00, 0x00, 0x0d, 0xb6, 0x00, 0x00,
        0x0d, 0xb6, 0x00, 0x00, 0x0d, 0xb6, 0x00, 0x00, 0x0d, 0xb6, 0x00, 0x00, 0x0d, 0xb6, 0x00,
        0x01, 0x8d, 0xb6, 0x00, 0x01, 0xcf, 0xfe, 0x00, 0x00, 0xef, 0xfe, 0x00, 0x00, 0xff, 0xfe,
        0x00, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0x3f, 0xfe, 0x00, 0x00, 0x3f, 0xfc, 0x00, 0x00, 0x1f,
        0xfc, 0x00, 0x00, 0x0f, 0xfc, 0x00, 0x00, 0x07, 0xf8, 0x00, 0x00, 0x03, 0xf8, 0x00, 0x00,
        0x03, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00
};
static DDImage* gFPSImage = NULL;

//////////////////////////////////////////////////////////////////////////

typedef HRESULT (WINAPI *SHGetFolderPathFunc)(HWND, int, HANDLE, DWORD, LPTSTR);
void* GetSHGetFolderPath(const char* theDLL, HMODULE* theMod)
{
        HMODULE aMod = LoadLibrary(theDLL);
        SHGetFolderPathFunc aFunc = NULL;

        if (aMod != NULL)
        {
                *((void**)&aFunc) = (void*)GetProcAddress(aMod, "SHGetFolderPathA");
                if (aFunc == NULL)
                {
                        FreeLibrary(aMod);
                        aMod = NULL;
                }
        }      

        *theMod = aMod;
        return aFunc;
}

//////////////////////////////////////////////////////////////////////////

SexyAppBase::SexyAppBase()
{
        gSexyAppBase = this;

        gVersionDLL = LoadLibraryA("version.dll");
        gDDrawDLL = LoadLibraryA("ddraw.dll");
        gDSoundDLL = LoadLibraryA("dsound.dll");
        gGetLastInputInfoFunc = (GetLastInputInfoFunc) GetProcAddress(GetModuleHandleA("user32.dll"),"GetLastInputInfo");

        ImageLib::InitJPEG2000();

        mMutex = NULL;
        mNotifyGameMessage = 0;

#ifdef _DEBUG
        mOnlyAllowOneCopyToRun = false;
#else
        mOnlyAllowOneCopyToRun = true;
#endif

        // Extract product version
        char aPath[_MAX_PATH];
        GetModuleFileNameA(NULL, aPath, 256);
        mProductVersion = GetProductVersion(aPath);    
        mChangeDirTo = GetFileDir(aPath);

        mNoDefer = false;      
        mFullScreenPageFlip = true; // should we page flip in fullscreen?
        mTimeLoaded = GetTickCount();
        mSEHOccured = false;
        mProdName = "Product";
        mTitle = _S("SexyApp");
        mShutdown = false;
        mExitToTop = false;
        mWidth = 640;
        mHeight = 480;
        mFullscreenBits = 16;
        mIsWindowed = true;
        mIsPhysWindowed = true;
        mFullScreenWindow = false;
        mPreferredX = -1;
        mPreferredY = -1;
        mIsScreenSaver = false;
        mAllowMonitorPowersave = true;
        mHWnd = NULL;
        mDDInterface = NULL;   
        mMusicInterface = NULL;
        mInvisHWnd = NULL;
        mFrameTime = 10;
        mNonDrawCount = 0;
        mDrawCount = 0;
        mSleepCount = 0;
        mUpdateCount = 0;      
        mUpdateAppState = 0;
        mUpdateAppDepth = 0;
        mPendingUpdatesAcc = 0.0;
        mUpdateFTimeAcc = 0.0;
        mHasPendingDraw = true;
        mIsDrawing = false;
        mLastDrawWasEmpty = false;     
        mLastTimeCheck = 0;
        mUpdateMultiplier = 1;
        mPaused = false;
        mFastForwardToUpdateNum = 0;
        mFastForwardToMarker = false;
        mFastForwardStep = false;
        mSoundManager = NULL;
        mCursorNum = CURSOR_POINTER;           
        mMouseIn = false;
        mRunning = false;
        mActive = true;
        mProcessInTimer = false;
        mMinimized = false;    
        mPhysMinimized = false;
        mIsDisabled = false;
        mLoaded = false;       
        mYieldMainThread = false;
        mLoadingFailed = false;
        mLoadingThreadStarted = false;
        mAutoStartLoadingThread = true;
        mLoadingThreadCompleted = false;
        mCursorThreadRunning = false;
        mNumLoadingThreadTasks = 0;
        mCompletedLoadingThreadTasks = 0;      
        mLastDrawTick = timeGetTime();
        mNextDrawTick = timeGetTime();
        mSysCursor = true;     
        mForceFullscreen = false;
        mForceWindowed = false;
        mHasFocus = true;                      
        mCustomCursorsEnabled = false; 
        mCustomCursorDirty = false;
        mOverrideCursor = NULL;
        mIsOpeningURL = false;         
        mInitialized = false;  
        mLastShutdownWasGraceful = true;       
        mReadFromRegistry = false;     
        mCmdLineParsed = false;
        mSkipSignatureChecks = false;  
        mCtrlDown = false;
        mAltDown = false;
        mStepMode = 0;
        mCleanupSharedImages = false;
        mStandardWordWrap = true;
        mbAllowExtendedChars = true;
        mEnableMaximizeButton = false;

        mMusicVolume = 0.85;
        mSfxVolume = 0.85;
        mDemoMusicVolume = mDemoSfxVolume = 0.0;
        mMuteCount = 0;
        mAutoMuteCount = 0;
        mDemoMute = false;
        mMuteOnLostFocus = true;
        mCurHandleNum = 0;
        mFPSTime = 0;
        mFPSStartTick = GetTickCount();
        mFPSFlipCount = 0;
        mFPSCount = 0;
        mFPSDirtyCount = 0;
        mShowFPS = false;
        mShowFPSMode = FPS_ShowFPS;
        mDrawTime = 0;
        mScreenBltTime = 0;
        mAlphaDisabled = false;
        mDebugKeysEnabled = false;
        mOldWndProc = 0;
        mNoSoundNeeded = false;
        mWantFMod = false;

        mSyncRefreshRate = 100;
        mVSyncUpdates = false;
        mVSyncBroken = false;
        mVSyncBrokenCount = 0;
        mVSyncBrokenTestStartTick = 0;
        mVSyncBrokenTestUpdates = 0;
        mWaitForVSync = false;
        mSoftVSyncWait = true;
        mUserChanged3DSetting = false;
        mAutoEnable3D = false;
        mTest3D = false;
        mMinVidMemory3D = 6;
        mRecommendedVidMemory3D = 14;
        mRelaxUpdateBacklogCount = 0;
        mWidescreenAware = false;
        mEnableWindowAspect = false;
        mWindowAspect.Set(4, 3);
        mIsWideWindow = false;

        int i;

        for (i = 0; i < NUM_CURSORS; i++)
                mCursorImages[i] = NULL;       

        for (i = 0; i < 256; i++)
                mAdd8BitMaxTable[i] = i;

        for (i = 256; i < 512; i++)
                mAdd8BitMaxTable[i] = 255;
       
        // Set default strings.  Init could read in overrides from partner.xml
        SetString("DIALOG_BUTTON_OK",           L"OK");
        SetString("DIALOG_BUTTON_CANCEL",       L"CANCEL");

        SetString("UPDATE_CHECK_TITLE",         L"Update Check");
        SetString("UPDATE_CHECK_BODY",          L"Checking if there are any updates available for this product ...");

        SetString("UP_TO_DATE_TITLE",           L"Up to Date");
        SetString("UP_TO_DATE_BODY",            L"There are no updates available for this product at this time.");
        SetString("NEW_VERSION_TITLE",          L"New Version");
        SetString("NEW_VERSION_BODY",           L"There is an update available for this product.  Would you like to visit the web site to download it?");
       

        mDemoPrefix = "sexyapp";
        mDemoFileName = mDemoPrefix + ".dmo";
        mPlayingDemoBuffer = false;
        mManualShutdown = false;
        mRecordingDemoBuffer = false;
        mLastDemoMouseX = 0;
        mLastDemoMouseY = 0;
        mLastDemoUpdateCnt = 0;
        mDemoNeedsCommand = true;
        mDemoLoadingComplete = false;
        mDemoLength = 0;
        mDemoCmdNum = 0;
        mDemoCmdOrder = -1; // Means we haven't processed any demo commands yet
        mDemoCmdBitPos = 0;

        mWidgetManager = new WidgetManager(this);
        mResourceManager = new ResourceManager(this);

        mPrimaryThreadId = 0;

        if (GetSystemMetrics(86)) // check for tablet pc
        {
                mTabletPC = true;
                mFullScreenPageFlip = false; // so that tablet keyboard can show up
        }
        else
                mTabletPC = false;     

        gSEHCatcher.mApp = this;       
       
        //std::wifstream stringsFile(_wfopen(L".\\properties\\fstrings", L"rb"));
        //
        //if(!stringsFile)
        //{
        //      MessageBox(NULL, "file missing: 'install-folder\\properties\\fstrings' Please re-install", "FATAL ERROR", MB_OK);
        //      DoExit(1);
        //}
        //std::getline(stringsFile, mString_HardwareAccelSwitchedOn);
        //std::getline(stringsFile, mString_HardwareAccelConfirm);
        //std::getline(stringsFile, mString_HardwareAccelNotWorking);
        //std::getline(stringsFile, mString_SetColorDepth);
        //std::getline(stringsFile, mString_FailedInitDirectDrawColon);
        //std::getline(stringsFile, mString_UnableOpenProperties);
        //std::getline(stringsFile, mString_SigCheckFailed);
        //std::getline(stringsFile, mString_InvalidCommandLineParam);
        //std::getline(stringsFile, mString_RequiresDirectX);
        //std::getline(stringsFile, mString_YouNeedDirectX);
        //std::getline(stringsFile, mString_FailedInitDirectDraw);
        //std::getline(stringsFile, mString_FatalError);
        //std::getline(stringsFile, mString_UnexpectedErrorOccured);
        //std::getline(stringsFile, mString_PleaseHelpBy);
        //std::getline(stringsFile, mString_FailedConnectPopcap);
        //stringsFile.close();
}

SexyAppBase::~SexyAppBase()
{
        Shutdown();

        // Check if we should write the current 3d setting
        bool showedMsgBox = false;
        if (mUserChanged3DSetting)
        {
                bool writeToRegistry = true;
                bool is3D = false;
                bool is3DOptionSet = RegistryReadBoolean("Is3D", &is3D);
                if(!is3DOptionSet) // should we write the option?
                {
                        if(!Is3DAccelerationRecommended()) // may need to prompt user if he wants to keep 3d acceleration on
                        {
                                if (Is3DAccelerated())
                                {
                                        showedMsgBox = true;
                                        int aResult = MessageBox(NULL,
                                                                        GetString("HARDWARE_ACCEL_SWITCHED_ON",
                                                                                                                        _S("Hardware Acceleration was switched on during this session.\r\n")
                                                                                                                        _S("If this resulted in slower performance, it should be switched off.\r\n")
                                                                                                                        _S("Would you like to keep Hardware Acceleration switched on?")).c_str(),
                                                                        (StringToSexyString(mCompanyName) + _S(" ") +
                                                                         GetString("HARDWARE_ACCEL_CONFIRMATION", _S("Hardware Acceleration Confirmation"))).c_str(),
                                                                        MB_YESNO | MB_ICONQUESTION);

                                        mDDInterface->mIs3D = aResult == IDYES ? true : false;
                                        if (aResult!=IDYES)
                                                writeToRegistry = false;
                                }
                                else
                                        writeToRegistry = false;
                        }
                }

                if (writeToRegistry)
                        RegistryWriteBoolean("Is3D", mDDInterface->mIs3D);
        }

        extern bool gD3DInterfacePreDrawError;
        if (!showedMsgBox && gD3DInterfacePreDrawError && !IsScreenSaver())
        {
                int aResult = MessageBox(NULL,
                                                GetString("HARDWARE_ACCEL_NOT_WORKING",
                                                                        _S("Hardware Acceleration may not have been working correctly during this session.\r\n")
                                                                        _S("If you noticed graphics problems, you may want to turn off Hardware Acceleration.\r\n")
                                                                        _S("Would you like to keep Hardware Acceleration switched on?")).c_str(),
                                                (StringToSexyString(mCompanyName) + _S(" ") +
                                                 GetString("HARDWARE_ACCEL_CONFIRMATION", _S("Hardware Acceleration Confirmation"))).c_str(),
                                                MB_YESNO | MB_ICONQUESTION);

                if (aResult==IDNO)
                        RegistryWriteBoolean("Is3D", false);
        }


        DialogMap::iterator aDialogItr = mDialogMap.begin();
        while (aDialogItr != mDialogMap.end())
        {
                mWidgetManager->RemoveWidget(aDialogItr->second);
                delete aDialogItr->second;
                ++aDialogItr;
        }
        mDialogMap.clear();
        mDialogList.clear();
       
        if (mInvisHWnd != NULL)
        {
                HWND aWindow = mInvisHWnd;
                mInvisHWnd = NULL;
                SetWindowLong(aWindow, GWL_USERDATA, NULL);
                DestroyWindow(aWindow);
        }      
       
        delete mWidgetManager; 
        delete mResourceManager;
        delete gFPSImage;
        gFPSImage = NULL;
       
        SharedImageMap::iterator aSharedImageItr = mSharedImageMap.begin();
        while (aSharedImageItr != mSharedImageMap.end())
        {
                SharedImage* aSharedImage = &aSharedImageItr->second;
                DBG_ASSERTE(aSharedImage->mRefCount == 0);             
                delete aSharedImage->mImage;
                mSharedImageMap.erase(aSharedImageItr++);              
        }
       
        delete mDDInterface;
        delete mMusicInterface;
        delete mSoundManager;                  

        if (mHWnd != NULL)
        {
                HWND aWindow = mHWnd;
                mHWnd = NULL;
               
                SetWindowLong(aWindow, GWL_USERDATA, NULL);

                /*char aStr[256];
                sprintf(aStr, "HWND: %d\r\n", aWindow);
                OutputDebugString(aStr);*/

                               
                DestroyWindow(aWindow);
        }      
       
        WaitForLoadingThread();

        DestroyCursor(mHandCursor);
        DestroyCursor(mDraggingCursor);                

        gSexyAppBase = NULL;

        WriteDemoBuffer();

        if (mMutex != NULL)
                ::CloseHandle(mMutex); 

        FreeLibrary(gDDrawDLL);
        FreeLibrary(gDSoundDLL);
        FreeLibrary(gVersionDLL);
}

static BOOL CALLBACK ChangeDisplayWindowEnumProc(HWND hwnd, LPARAM lParam)
{
        typedef std::map<HWND,RECT> WindowMap;
        static WindowMap aMap;

        if (lParam==0 && aMap.find(hwnd)==aMap.end()) // record
        {
                RECT aRect;
                if (!IsIconic(hwnd) && IsWindowVisible(hwnd))
                {
                        if (GetWindowRect(hwnd,&aRect))
                        {
//                              char aBuf[4096];
//                              GetWindowText(hwnd,aBuf,4000);
//                              DWORD aProcessId = 0;
//                              GetWindowThreadProcessId(hwnd,&aProcessId);
//                              SEXY_TRACE(StrFormat("%s %d - %d %d %d %d",aBuf,aProcessId,aRect.left,aRect.top,aRect.right,aRect.bottom).c_str());
                                aMap[hwnd] = aRect;
                        }
                }
        }
        else
        {
                WindowMap::iterator anItr = aMap.find(hwnd);
                if (anItr != aMap.end())
                {
                        RECT &r = anItr->second;
                        MoveWindow(hwnd,r.left,r.top,abs(r.right-r.left),abs(r.bottom-r.top),TRUE);
                }
        }
        return TRUE;
}

void SexyAppBase::ClearUpdateBacklog(bool relaxForASecond)
{
        mLastTimeCheck = timeGetTime();
        mUpdateFTimeAcc = 0.0;

        if (relaxForASecond)
                mRelaxUpdateBacklogCount = 1000;
}

bool SexyAppBase::IsScreenSaver()
{
        return mIsScreenSaver;
}

bool SexyAppBase::AppCanRestore()
{
        return !mIsDisabled;
}

bool SexyAppBase::ReadDemoBuffer(std::string &theError)
{
        FILE* aFP = fopen(mDemoFileName.c_str(), "rb");

        if (aFP == NULL)
        {
                theError = "Demo file not found: " + mDemoFileName;
                return false;
        }

        struct AutoFile { FILE *f; AutoFile(FILE *file) : f(file) {} ~AutoFile() { fclose(f); } };
        AutoFile aCloseFile(aFP);

        ulong aFileID;
        fread(&aFileID, 4, 1, aFP);

        DBG_ASSERTE(aFileID == DEMO_FILE_ID);
        if (aFileID != DEMO_FILE_ID)
        {
                theError = "Invalid demo file.";
                return false;
        }


        ulong aVersion;
        fread(&aVersion, 4, 1, aFP);
       
        fread(&mRandSeed, 4, 1, aFP);
        SRand(mRandSeed);

        ushort aStrLen = 4;
        fread(&aStrLen, 2, 1, aFP);
        if (aStrLen > 255)
                aStrLen = 255;
        char aStr[256];
        fread(aStr, 1, aStrLen, aFP);
        aStr[aStrLen] = '\0';

        DBG_ASSERTE(mProductVersion == aStr);
        if (mProductVersion != aStr)
        {
                theError = "This demo file appears to be for '" + std::string(aStr) + "'";
                return false;
        }

        int aFilePos = ftell(aFP);
        fseek(aFP, 0, SEEK_END);
        int aBytesLeft = ftell(aFP) - aFilePos;
        fseek(aFP, aFilePos, SEEK_SET);

        uchar* aBuffer;
        // read marker list
        if (aVersion >= 2)
        {
                int aSize;
                fread(&aSize, 4, 1, aFP);
                aBytesLeft -= 4;

                if (aSize >= aBytesLeft)
                {
                        theError = "Invalid demo file.";
                        return false;
                }

                Buffer aMarkerBuffer;

                aBuffer = new uchar[aSize];
                fread(aBuffer, 1, aSize, aFP);
                aMarkerBuffer.WriteBytes(aBuffer, aSize);
                aMarkerBuffer.SeekFront();

                int aNumItems = aMarkerBuffer.ReadLong();
                int i;
                for (i=0; i<aNumItems && !aMarkerBuffer.AtEnd(); i++)
                {
                        mDemoMarkerList.push_back(DemoMarker());
                        DemoMarker &aMarker = mDemoMarkerList.back();
                        aMarker.first = aMarkerBuffer.ReadString();
                        aMarker.second = aMarkerBuffer.ReadLong();
                }

                if (i!=aNumItems)
                {
                        theError = "Invalid demo file.";
                        return false;
                }

                aBytesLeft -= aSize;

                delete [] aBuffer;                     
        }

        // Read demo commands
        fread(&mDemoLength, 4, 1, aFP);
        aBytesLeft -= 4;
       
        if (aBytesLeft <= 0)
        {
                theError = "Invalid demo file.";
                return false;
        }


        aBuffer = new uchar[aBytesLeft];
        fread(aBuffer, 1, aBytesLeft, aFP);            

        mDemoBuffer.WriteBytes(aBuffer, aBytesLeft);
        mDemoBuffer.SeekFront();

        delete [] aBuffer;
        return true;
}

void SexyAppBase::WriteDemoBuffer()
{
        if (mRecordingDemoBuffer)
        {
                FILE* aFP = fopen(mDemoFileName.c_str(), "w+b");

                if (aFP != NULL)
                {
                        ulong aFileID = DEMO_FILE_ID;
                        fwrite(&aFileID, 4, 1, aFP);           

                        ulong aVersion = DEMO_VERSION;
                        fwrite(&aVersion, 4, 1, aFP);
                       
                        fwrite(&mRandSeed, 4, 1, aFP);

                        ushort aStrLen = mProductVersion.length();
                        fwrite(&aStrLen, 2, 1, aFP);           
                        fwrite(mProductVersion.c_str(), 1, mProductVersion.length(), aFP);

                        Buffer aMarkerBuffer;
                        aMarkerBuffer.WriteLong(mDemoMarkerList.size());
                        for (DemoMarkerList::iterator aMarkerItr = mDemoMarkerList.begin(); aMarkerItr != mDemoMarkerList.end(); ++aMarkerItr)
                        {
                                aMarkerBuffer.WriteString(aMarkerItr->first);
                                aMarkerBuffer.WriteLong(aMarkerItr->second);
                        }
                        int aMarkerBufferSize = aMarkerBuffer.GetDataLen();
                        fwrite(&aMarkerBufferSize, 4, 1, aFP);
                        fwrite(aMarkerBuffer.GetDataPtr(), aMarkerBufferSize, 1, aFP);

                        ulong aDemoLength = mUpdateCount;
                        fwrite(&aDemoLength, 4, 1, aFP);

                        fwrite(mDemoBuffer.GetDataPtr(), 1, mDemoBuffer.GetDataLen(), aFP);
                        fclose(aFP);
                }              
        }
}

void SexyAppBase::DemoSyncBuffer(Buffer* theBuffer)
{
        if (mPlayingDemoBuffer)
        {
                if (mManualShutdown)
                        return;

                PrepareDemoCommand(true);
                mDemoNeedsCommand = true;
               
                DBG_ASSERTE(!mDemoIsShortCmd);
                DBG_ASSERTE(mDemoCmdNum == DEMO_SYNC);

                ulong aLen = mDemoBuffer.ReadLong();
               
                theBuffer->Clear();
                for (int i = 0; i < (int) aLen; i++)
                        theBuffer->WriteByte(mDemoBuffer.ReadByte());          
        }
        else if (mRecordingDemoBuffer)
        {
                WriteDemoTimingBlock();
                mDemoBuffer.WriteNumBits(0, 1);
                mDemoBuffer.WriteNumBits(DEMO_SYNC, 5);        
                mDemoBuffer.WriteLong(theBuffer->GetDataLen());
                mDemoBuffer.WriteBytes((uchar*) theBuffer->GetDataPtr(), theBuffer->GetDataLen());
        }
}

void SexyAppBase::DemoSyncString(std::string* theString)
{
        Buffer aBuffer;
        aBuffer.WriteString(*theString);
        DemoSyncBuffer(&aBuffer);
        *theString = aBuffer.ReadString();
}

void SexyAppBase::DemoSyncInt(int* theInt)
{
        Buffer aBuffer;
        aBuffer.WriteLong(*theInt);
        DemoSyncBuffer(&aBuffer);
        *theInt = aBuffer.ReadLong();
}

void SexyAppBase::DemoSyncBool(bool* theBool)
{
        Buffer aBuffer;
        aBuffer.WriteBoolean(*theBool);
        DemoSyncBuffer(&aBuffer);
        *theBool = aBuffer.ReadBoolean();
}

void SexyAppBase::DemoAssertStringEqual(const std::string& theString)
{
        if (mPlayingDemoBuffer)
        {
                if (mManualShutdown)
                        return;

                PrepareDemoCommand(true);
                mDemoNeedsCommand = true;
               
                DBG_ASSERTE(!mDemoIsShortCmd);
                DBG_ASSERTE(mDemoCmdNum == DEMO_ASSERT_STRING_EQUAL);

                std::string aString = mDemoBuffer.ReadString();
                DBG_ASSERTE(aString == theString);
        }
        else if (mRecordingDemoBuffer)
        {
                WriteDemoTimingBlock();
                mDemoBuffer.WriteNumBits(0, 1);
                mDemoBuffer.WriteNumBits(DEMO_ASSERT_STRING_EQUAL, 5);                         
                mDemoBuffer.WriteString(theString);
        }
}

void SexyAppBase::DemoAddMarker(const std::string& theString)
{
        if (mPlayingDemoBuffer)
        {
                mFastForwardToMarker = false;
        }
        else if (mRecordingDemoBuffer)
        {
                mDemoMarkerList.push_back(DemoMarker(theString,mUpdateCount));
        }
}

void SexyAppBase::DemoRegisterHandle(HANDLE theHandle)
{
        if ((mRecordingDemoBuffer) || (mPlayingDemoBuffer))
        {
                // Insert the handle into a map with an auto-incrementing number so
                //  we can match up the auto-incrementing numbers with the handle
                //  later on, as handles may not be the same between executions
                std::pair<HandleToIntMap::iterator, bool> aPair = mHandleToIntMap.insert(HandleToIntMap::value_type(theHandle, mCurHandleNum));
                DBG_ASSERT(aPair.second);
                mCurHandleNum++;
        }
}

void SexyAppBase::DemoWaitForHandle(HANDLE theHandle)
{
        WaitForSingleObject(theHandle, INFINITE);
       
        if ((mRecordingDemoBuffer) || (mPlayingDemoBuffer))
        {
                // Remove the handle from our waiting map
                HandleToIntMap::iterator anItr = mHandleToIntMap.find(theHandle);                                      
                DBG_ASSERT(anItr != mHandleToIntMap.end());
                mHandleToIntMap.erase(anItr);
        }
}

bool SexyAppBase::DemoCheckHandle(HANDLE theHandle)
{
        if (mPlayingDemoBuffer)
        {      
                // We only need to try to get the result if we think we are waiting for one    
                if (gSexyAppBase->PrepareDemoCommand(false))
                {
                        if ((!gSexyAppBase->mDemoIsShortCmd) && (gSexyAppBase->mDemoCmdNum == DEMO_HANDLE_COMPLETE))
                        {
                                // Find auto-incrementing handle num from handle
                                HandleToIntMap::iterator anItr = mHandleToIntMap.find(theHandle);                                      
                                DBG_ASSERT(anItr != mHandleToIntMap.end());

                                int anOldBufferPos = gSexyAppBase->mDemoBuffer.mReadBitPos;

                                // Since we don't require a demo result entry to be here, we must verify
                                //  that this is referring to us
                                int aDemoHandleNum = gSexyAppBase->mDemoBuffer.ReadLong();
                               
                                if (aDemoHandleNum == anItr->second)
                                {
                                        // Alright, this was the handle we were waiting for!
                                        gSexyAppBase->mDemoNeedsCommand = true;

                                        // Actually wait for our local buddy to complete
                                        WaitForSingleObject(theHandle, INFINITE);
                                        mHandleToIntMap.erase(anItr);

                                        return true;
                                }
                                else
                                {
                                        // Not us, go back
                                        gSexyAppBase->mDemoBuffer.mReadBitPos = anOldBufferPos;
                                }
                        }
                }

                return false;  
        }
        else
        {
                if (WaitForSingleObject(theHandle, 0) == WAIT_OBJECT_0)
                {
                        if (mRecordingDemoBuffer)
                        {
                                // Find auto-incrementing handle num from handle
                                HandleToIntMap::iterator anItr = mHandleToIntMap.find(theHandle);                                      
                                DBG_ASSERT(anItr != mHandleToIntMap.end());

                                gSexyAppBase->WriteDemoTimingBlock();
                                gSexyAppBase->mDemoBuffer.WriteNumBits(0, 1);
                                gSexyAppBase->mDemoBuffer.WriteNumBits(DEMO_HANDLE_COMPLETE, 5);
                                gSexyAppBase->mDemoBuffer.WriteLong(anItr->second);

                                mHandleToIntMap.erase(anItr);
                        }

                        return true;
                }

                return false;
        }
}

void SexyAppBase::DemoAssertIntEqual(int theInt)
{
        if (mPlayingDemoBuffer)
        {
                if (mManualShutdown)
                        return;

                PrepareDemoCommand(true);
                mDemoNeedsCommand = true;
               
                DBG_ASSERTE(!mDemoIsShortCmd);
                DBG_ASSERTE(mDemoCmdNum == DEMO_ASSERT_INT_EQUAL);

                int anInt = mDemoBuffer.ReadLong();
                DBG_ASSERTE(anInt == theInt);
        }
        else if (mRecordingDemoBuffer)
        {              
                WriteDemoTimingBlock();
                mDemoBuffer.WriteNumBits(0, 1);
                mDemoBuffer.WriteNumBits(DEMO_ASSERT_INT_EQUAL, 5);                            
                mDemoBuffer.WriteLong(theInt);
        }
}

Dialog* SexyAppBase::NewDialog(int theDialogId, bool isModal, const SexyString& theDialogHeader, const SexyString& theDialogLines, const SexyString& theDialogFooter, int theButtonMode)
{      
        Dialog* aDialog = new Dialog(NULL, NULL, theDialogId, isModal, theDialogHeader, theDialogLines, theDialogFooter, theButtonMode);               
        return aDialog;
}

Dialog* SexyAppBase::DoDialog(int theDialogId, bool isModal, const SexyString& theDialogHeader, const SexyString& theDialogLines, const SexyString& theDialogFooter, int theButtonMode)
{
        KillDialog(theDialogId);

        Dialog* aDialog = NewDialog(theDialogId, isModal, theDialogHeader, theDialogLines, theDialogFooter, theButtonMode);            

        AddDialog(theDialogId, aDialog);

        return aDialog;
}


Dialog* SexyAppBase::GetDialog(int theDialogId)
{
        DialogMap::iterator anItr = mDialogMap.find(theDialogId);

        if (anItr != mDialogMap.end()) 
                return anItr->second;

        return NULL;
}

bool SexyAppBase::KillDialog(int theDialogId, bool removeWidget, bool deleteWidget)
{
        DialogMap::iterator anItr = mDialogMap.find(theDialogId);

        if (anItr != mDialogMap.end())
        {
                Dialog* aDialog = anItr->second;

                // set the result to something else so DoMainLoop knows that the dialog is gone
                // in case nobody else sets mResult            
                if (aDialog->mResult == -1)
                        aDialog->mResult = 0;
               
                DialogList::iterator aListItr = std::find(mDialogList.begin(),mDialogList.end(),aDialog);
                if (aListItr != mDialogList.end())
                        mDialogList.erase(aListItr);
               
                mDialogMap.erase(anItr);

                if (removeWidget || deleteWidget)
                mWidgetManager->RemoveWidget(aDialog);

                if (aDialog->IsModal())
                {                      
                        ModalClose();
                        mWidgetManager->RemoveBaseModal(aDialog);
                }                              

                if (deleteWidget)
                SafeDeleteWidget(aDialog);
               
                return true;
        }

        return false;
}

bool SexyAppBase::KillDialog(int theDialogId)
{
        return KillDialog(theDialogId,true,true);
}

bool SexyAppBase::KillDialog(Dialog* theDialog)
{
        return KillDialog(theDialog->mId);
}

int SexyAppBase::GetDialogCount()
{
        return mDialogMap.size();
}

void SexyAppBase::AddDialog(int theDialogId, Dialog* theDialog)
{
        KillDialog(theDialogId);

        if (theDialog->mWidth == 0)
        {
                // Set the dialog position ourselves
                int aWidth = mWidth/2;
                theDialog->Resize((mWidth - aWidth)/2, mHeight / 5, aWidth, theDialog->GetPreferredHeight(aWidth));
        }

        mDialogMap.insert(DialogMap::value_type(theDialogId, theDialog));
        mDialogList.push_back(theDialog);

        mWidgetManager->AddWidget(theDialog);
        if (theDialog->IsModal())
        {
                mWidgetManager->AddBaseModal(theDialog);
                ModalOpen();
        }
}

void SexyAppBase::AddDialog(Dialog* theDialog)
{
        AddDialog(theDialog->mId, theDialog);
}

void SexyAppBase::ModalOpen()
{
}

void SexyAppBase::ModalClose()
{
}

void SexyAppBase::DialogButtonPress(int theDialogId, int theButtonId)
{      
        if (theButtonId == Dialog::ID_YES)
                ButtonPress(2000 + theDialogId);
        else if (theButtonId == Dialog::ID_NO)
                ButtonPress(3000 + theDialogId);       
}

void SexyAppBase::DialogButtonDepress(int theDialogId, int theButtonId)
{
        if (theButtonId == Dialog::ID_YES)
                ButtonDepress(2000 + theDialogId);
        else if (theButtonId == Dialog::ID_NO)
                ButtonDepress(3000 + theDialogId);
}

void SexyAppBase::GotFocus()
{      
}

void SexyAppBase::LostFocus()
{      
}

void SexyAppBase::URLOpenFailed(const std::string& theURL)
{
        mIsOpeningURL = false;
}

void SexyAppBase::URLOpenSucceeded(const std::string& theURL)
{      
        mIsOpeningURL = false;

        if (mShutdownOnURLOpen)
                Shutdown();    
}

bool SexyAppBase::OpenURL(const std::string& theURL, bool shutdownOnOpen)
{
        if ((!mIsOpeningURL) || (theURL != mOpeningURL))
        {
                mShutdownOnURLOpen = shutdownOnOpen;
                mIsOpeningURL = true;
                mOpeningURL = theURL;
                mOpeningURLTime = GetTickCount();              

                if ((int) ShellExecuteA(NULL, "open", theURL.c_str(), NULL, NULL, SW_SHOWNORMAL) > 32)
                {
                        return true;
                }
                else
                {
                        URLOpenFailed(theURL);
                        return false;
                }
        }

        return true;
}

std::string SexyAppBase::GetProductVersion(const std::string& thePath)
{      
        // Dynamically Load Version.dll
        typedef DWORD (APIENTRY *GetFileVersionInfoSizeFunc)(LPSTR lptstrFilename, LPDWORD lpdwHandle);
        typedef BOOL (APIENTRY *GetFileVersionInfoFunc)(LPSTR lptstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData);
        typedef BOOL (APIENTRY *VerQueryValueFunc)(const LPVOID pBlock, LPSTR lpSubBlock, LPVOID * lplpBuffer, PUINT puLen);

        static GetFileVersionInfoSizeFunc aGetFileVersionInfoSizeFunc = NULL;
        static GetFileVersionInfoFunc aGetFileVersionInfoFunc = NULL;
        static VerQueryValueFunc aVerQueryValueFunc = NULL;

        if (aGetFileVersionInfoSizeFunc==NULL)
        {
                aGetFileVersionInfoSizeFunc = (GetFileVersionInfoSizeFunc)GetProcAddress(gVersionDLL,"GetFileVersionInfoSizeA");
                aGetFileVersionInfoFunc = (GetFileVersionInfoFunc)GetProcAddress(gVersionDLL,"GetFileVersionInfoA");
                aVerQueryValueFunc = (VerQueryValueFunc)GetProcAddress(gVersionDLL,"VerQueryValueA");
        }

        // Get Product Version
        std::string aProductVersion;
       
        uint aSize = aGetFileVersionInfoSizeFunc((char*) thePath.c_str(), 0);
        if (aSize > 0)         
        {
                uchar* aVersionBuffer = new uchar[aSize];
                aGetFileVersionInfoFunc((char*) thePath.c_str(), 0, aSize, aVersionBuffer);    
                char* aBuffer; 
                if (aVerQueryValueFunc(aVersionBuffer,
                                  "\\StringFileInfo\\040904B0\\ProductVersion",
                                  (void**) &aBuffer,
                                  &aSize))
                {
                        aProductVersion = aBuffer;
                }
                else if (aVerQueryValueFunc(aVersionBuffer,
                                  "\\StringFileInfo\\040904E4\\ProductVersion",
                                  (void**) &aBuffer,
                                  &aSize))
                {
                        aProductVersion = aBuffer;
                }

                delete aVersionBuffer; 
        }

        return aProductVersion;
}

void SexyAppBase::WaitForLoadingThread()
{
        while ((mLoadingThreadStarted) && (!mLoadingThreadCompleted))
                Sleep(20);
}

void SexyAppBase::SetCursorImage(int theCursorNum, Image* theImage)
{
        if ((theCursorNum >= 0) && (theCursorNum < NUM_CURSORS))
        {
                mCursorImages[theCursorNum] = theImage;
                EnforceCursor();
        }
}

void SexyAppBase::TakeScreenshot()
{
        if (mDDInterface==NULL || mDDInterface->mDrawSurface==NULL)
                return;

        // Get free image name
        std::string anImageDir = GetAppDataFolder() + "_screenshots";
        MkDir(anImageDir);
        anImageDir += "/";

        WIN32_FIND_DATAA aData;
        int aMaxId = 0;
        std::string anImagePrefix = "image";
        HANDLE aHandle = FindFirstFileA((anImageDir + "*.png").c_str(), &aData);
        if (aHandle!=INVALID_HANDLE_VALUE)
        {
                do {
                        int aNum = 0;
                        if (sscanf(aData.cFileName,(anImagePrefix + "%d.png").c_str(), &aNum)==1)
                        {
                                if (aNum>aMaxId)
                                        aMaxId = aNum;
                        }

                }
                while(FindNextFileA(aHandle,&aData));
                FindClose(aHandle);
        }
        std::string anImageName = anImageDir + anImagePrefix + StrFormat("%d.png",aMaxId+1);

        // Capture screen
        LPDIRECTDRAWSURFACE aSurface = mDDInterface->mDrawSurface;
       
        // Temporarily set the mDrawSurface to NULL so DDImage::Check3D
        // returns false so we can lock the surface.
        mDDInterface->mDrawSurface = NULL;
       
        DDImage anImage(mDDInterface);
        anImage.SetSurface(aSurface);
        anImage.GetBits();
        anImage.DeleteDDSurface();
        mDDInterface->mDrawSurface = aSurface;

        if (anImage.mBits==NULL)
                return;
               
        // Write image
        ImageLib::Image aSaveImage;
        aSaveImage.mBits = anImage.mBits;
        aSaveImage.mWidth = anImage.mWidth;
        aSaveImage.mHeight = anImage.mHeight;
        ImageLib::WritePNGImage(anImageName, &aSaveImage);
        aSaveImage.mBits = NULL;
               

/*
        keybd_event(VK_MENU,0,0,0);
    keybd_event(VK_SNAPSHOT,0,0,0);
    keybd_event(VK_MENU,0,KEYEVENTF_KEYUP,0);
        if (OpenClipboard(mHWnd))
        {
                HBITMAP aBitmap = (HBITMAP)GetClipboardData(CF_BITMAP);
                if (aBitmap!=NULL)
                {
                        BITMAP anObject;
                        ZeroMemory(&anObject,sizeof(anObject));
                        GetObject(aBitmap,sizeof(anObject),&anObject);

                        BITMAPINFO anInfo;
                        ZeroMemory(&anInfo,sizeof(anInfo));
                        BITMAPINFOHEADER &aHeader = anInfo.bmiHeader;
                        aHeader.biBitCount = 32;
                        aHeader.biPlanes = 1;
                        aHeader.biHeight = -abs(anObject.bmHeight);
                        aHeader.biWidth = abs(anObject.bmWidth);
                        aHeader.biSize = sizeof(aHeader);
                        aHeader.biSizeImage = aHeader.biHeight*aHeader.biWidth*4;
                        ImageLib::Image aSaveImage;
                        aSaveImage.mBits = new DWORD[abs(anObject.bmWidth*anObject.bmHeight)];
                        aSaveImage.mWidth = abs(anObject.bmWidth);
                        aSaveImage.mHeight = abs(anObject.bmHeight);

                        HDC aDC = GetDC(NULL);
                        if (GetDIBits(aDC,aBitmap,0,aSaveImage.mHeight,aSaveImage.mBits,&anInfo,DIB_RGB_COLORS))
                                ImageLib::WritePNGImage(anImageName, &aSaveImage);

                        ReleaseDC(NULL,aDC);
                }
                CloseClipboard();
        }*/


        ClearUpdateBacklog();
}

void SexyAppBase::DumpProgramInfo()
{
        Deltree(GetAppDataFolder() + "_dump");

        for (;;)
        {
                if (mkdir((GetAppDataFolder() + "_dump").c_str()))
                        break;
                Sleep(100);
        }

        std::fstream aDumpStream((GetAppDataFolder() + "_dump\\imagelist.html").c_str(), std::ios::out);

        time_t aTime;
        time(&aTime);
        tm* aTM = localtime(&aTime);

        aDumpStream << "<HTML><BODY BGCOLOR=EEEEFF><CENTER><FONT SIZE=+2><B>" << asctime(aTM) << "</B></FONT><BR>" << std::endl;

        int anImgNum = 0;

        int aThumbWidth = 64;
        int aThumbHeight = 64;

        ImageLib::Image anImageLibImage;
        anImageLibImage.mWidth = aThumbWidth;
        anImageLibImage.mHeight = aThumbHeight;
        anImageLibImage.mBits = new unsigned long[aThumbWidth*aThumbHeight];   

        typedef std::multimap<int, MemoryImage*, std::greater<int> > SortedImageMap;

        int aTotalMemory = 0;

        SortedImageMap aSortedImageMap;
        MemoryImageSet::iterator anItr = mMemoryImageSet.begin();
        while (anItr != mMemoryImageSet.end())
        {
                MemoryImage* aMemoryImage = *anItr;                            

                int aNumPixels = aMemoryImage->mWidth*aMemoryImage->mHeight;

                DDImage* aDDImage = dynamic_cast<DDImage*>(aMemoryImage);

                int aBitsMemory = 0;
                int aSurfaceMemory = 0;
                int aPalletizedMemory = 0;
                int aNativeAlphaMemory = 0;
                int aRLAlphaMemory = 0;
                int aRLAdditiveMemory = 0;
                int aTextureMemory = 0;

                int aMemorySize = 0;
                if (aMemoryImage->mBits != NULL)
                        aBitsMemory = aNumPixels * 4;
                if ((aDDImage != NULL) && (aDDImage->mSurface != NULL))
                        aSurfaceMemory = aNumPixels * 4; // Assume 32bit screen...
                if (aMemoryImage->mColorTable != NULL)
                        aPalletizedMemory = aNumPixels + 256*4;
                if (aMemoryImage->mNativeAlphaData != NULL)
                {
                        if (aMemoryImage->mColorTable != NULL)
                                aNativeAlphaMemory = 256*4;
                        else
                                aNativeAlphaMemory = aNumPixels * 4;
                }
                if (aMemoryImage->mRLAlphaData != NULL)
                        aRLAlphaMemory = aNumPixels;
                if (aMemoryImage->mRLAdditiveData != NULL)
                        aRLAdditiveMemory = aNumPixels;
                if (aMemoryImage->mD3DData != NULL)
                        aTextureMemory += ((TextureData*)aMemoryImage->mD3DData)->mTexMemSize;

                aMemorySize = aBitsMemory + aSurfaceMemory + aPalletizedMemory + aNativeAlphaMemory + aRLAlphaMemory + aRLAdditiveMemory + aTextureMemory;
                aTotalMemory += aMemorySize;

                aSortedImageMap.insert(SortedImageMap::value_type(aMemorySize, aMemoryImage));

                ++anItr;
        }

        aDumpStream << "Total Image Allocation: " << CommaSeperate(aTotalMemory).c_str() << " bytes<BR>";
        aDumpStream << "<TABLE BORDER=1 CELLSPACING=0 CELLPADDING=4>";

        int aTotalMemorySize = 0;
        int aTotalBitsMemory = 0;
        int aTotalSurfaceMemory = 0;
        int aTotalPalletizedMemory = 0;
        int aTotalNativeAlphaMemory = 0;
        int aTotalRLAlphaMemory = 0;
        int aTotalRLAdditiveMemory = 0;
        int aTotalTextureMemory = 0;

        SortedImageMap::iterator aSortedItr = aSortedImageMap.begin();
        while (aSortedItr != aSortedImageMap.end())
        {
                MemoryImage* aMemoryImage = aSortedItr->second;                        

                char anImageName[256];
                sprintf(anImageName, "img%04d.png", anImgNum);

                char aThumbName[256];
                sprintf(aThumbName, "thumb%04d.jpg", anImgNum);
               
                aDumpStream << "<TR>" << std::endl;

                aDumpStream << "<TD><A HREF=" << anImageName << "><IMG SRC=" << aThumbName << " WIDTH=" << aThumbWidth << " HEIGHT=" << aThumbHeight << "></A></TD>" << std::endl;
               
                int aNumPixels = aMemoryImage->mWidth*aMemoryImage->mHeight;

                DDImage* aDDImage = dynamic_cast<DDImage*>(aMemoryImage);

                int aMemorySize = aSortedItr->first;

                int aBitsMemory = 0;
                int aSurfaceMemory = 0;
                int aPalletizedMemory = 0;
                int aNativeAlphaMemory = 0;
                int aRLAlphaMemory = 0;
                int aRLAdditiveMemory = 0;
                int aTextureMemory = 0;
                std::string aTextureFormatName;
               
                if (aMemoryImage->mBits != NULL)
                        aBitsMemory = aNumPixels * 4;
                if ((aDDImage != NULL) && (aDDImage->mSurface != NULL))
                        aSurfaceMemory = aNumPixels * 4; // Assume 32bit screen...
                if (aMemoryImage->mColorTable != NULL)
                        aPalletizedMemory = aNumPixels + 256*4;
                if (aMemoryImage->mNativeAlphaData != NULL)
                {
                        if (aMemoryImage->mColorTable != NULL)
                                aNativeAlphaMemory = 256*4;
                        else
                                aNativeAlphaMemory = aNumPixels * 4;
                }
                if (aMemoryImage->mRLAlphaData != NULL)
                        aRLAlphaMemory = aNumPixels;
                if (aMemoryImage->mRLAdditiveData != NULL)
                        aRLAdditiveMemory = aNumPixels;        
                if (aMemoryImage->mD3DData != NULL)
                {
                        aTextureMemory += ((TextureData*)aMemoryImage->mD3DData)->mTexMemSize;

                        switch (((TextureData*)aMemoryImage->mD3DData)->mPixelFormat)
                        {                              
                        case PixelFormat_A8R8G8B8: aTextureFormatName = "A8R8G8B8"; break;
                        case PixelFormat_A4R4G4B4: aTextureFormatName = "A4R4G4B4"; break;
                        case PixelFormat_R5G6B5: aTextureFormatName = "R5G6B5"; break;
                        case PixelFormat_Palette8: aTextureFormatName = "Palette8"; break;
                        }                      
                }

                aTotalMemorySize                += aMemorySize;
                aTotalBitsMemory                += aBitsMemory;
                aTotalTextureMemory             += aTextureMemory;
                aTotalSurfaceMemory             += aSurfaceMemory;
                aTotalPalletizedMemory  += aPalletizedMemory;
                aTotalNativeAlphaMemory += aNativeAlphaMemory;
                aTotalRLAlphaMemory             += aRLAlphaMemory;
                aTotalRLAdditiveMemory  += aRLAdditiveMemory;

                               

                char aStr[256];
                sprintf(aStr, "%d x %d<BR>%s bytes", aMemoryImage->mWidth, aMemoryImage->mHeight, CommaSeperate(aMemorySize).c_str());
                aDumpStream << "<TD ALIGN=RIGHT>" << aStr << "</TD>" << std::endl;

                aDumpStream << "<TD>" << SexyStringToString(((aBitsMemory != 0) ? _S("mBits<BR>") + CommaSeperate(aBitsMemory) : _S("&nbsp;"))) << "</TD>" << std::endl;       
                aDumpStream << "<TD>" << SexyStringToString(((aPalletizedMemory != 0) ? _S("Palletized<BR>") + CommaSeperate(aPalletizedMemory) : _S("&nbsp;"))) << "</TD>" << std::endl;                              
                aDumpStream << "<TD>" << SexyStringToString(((aSurfaceMemory != 0) ? _S("DDSurface<BR>") + CommaSeperate(aSurfaceMemory) : _S("&nbsp;"))) << "</TD>" << std::endl;             
                aDumpStream << "<TD>" << SexyStringToString(((aMemoryImage->mD3DData!=NULL) ? _S("Texture<BR>") + StringToSexyString(aTextureFormatName) + _S("<BR>") + CommaSeperate(aTextureMemory) : _S("&nbsp;"))) << "</TD>" << std::endl;

                aDumpStream << "<TD>" << SexyStringToString(((aMemoryImage->mIsVolatile) ? _S("Volatile") : _S("&nbsp;"))) << "</TD>" << std::endl;
                aDumpStream << "<TD>" << SexyStringToString(((aMemoryImage->mForcedMode) ? _S("Forced") : _S("&nbsp;"))) << "</TD>" << std::endl;              
                aDumpStream << "<TD>" << SexyStringToString(((aMemoryImage->mHasAlpha) ? _S("HasAlpha") : _S("&nbsp;"))) << "</TD>" << std::endl;
                aDumpStream << "<TD>" << SexyStringToString(((aMemoryImage->mHasTrans) ? _S("HasTrans") : _S("&nbsp;"))) << "</TD>" << std::endl;
                aDumpStream << "<TD>" << SexyStringToString(((aNativeAlphaMemory != 0) ? _S("NativeAlpha<BR>") + CommaSeperate(aNativeAlphaMemory) : _S("&nbsp;"))) << "</TD>" << std::endl;
                aDumpStream << "<TD>" << SexyStringToString(((aRLAlphaMemory != 0) ? _S("RLAlpha<BR>") + CommaSeperate(aRLAlphaMemory) : _S("&nbsp;"))) << "</TD>" << std::endl;
                aDumpStream << "<TD>" << SexyStringToString(((aRLAdditiveMemory != 0) ? _S("RLAdditive<BR>") + CommaSeperate(aRLAdditiveMemory) : _S("&nbsp;"))) << "</TD>" << std::endl;
                aDumpStream << "<TD>" << (aMemoryImage->mFilePath.empty()? "&nbsp;":aMemoryImage->mFilePath) << "</TD>" << std::endl;

                aDumpStream << "</TR>" << std::endl;
               
                // Write thumb

                MemoryImage aCopiedImage(*aMemoryImage);

                ulong* aBits = aCopiedImage.GetBits();

                ulong* aThumbBitsPtr = anImageLibImage.mBits;

                for (int aThumbY = 0; aThumbY < aThumbHeight; aThumbY++)
                        for (int aThumbX = 0; aThumbX < aThumbWidth; aThumbX++)
                        {
                                int aSrcX = (int) (aCopiedImage.mWidth  * (aThumbX + 0.5)) / aThumbWidth;
                                int aSrcY = (int) (aCopiedImage.mHeight * (aThumbY + 0.5)) / aThumbHeight;
                               
                                *(aThumbBitsPtr++) = aBits[aSrcX + (aSrcY*aCopiedImage.mWidth)];
                        }

                ImageLib::WriteJPEGImage((GetAppDataFolder() + std::string("_dump\\") + aThumbName).c_str(), &anImageLibImage);

                // Write high resolution image

                ImageLib::Image anFullImage;
                anFullImage.mBits = aCopiedImage.GetBits();
                anFullImage.mWidth = aCopiedImage.GetWidth();
                anFullImage.mHeight = aCopiedImage.GetHeight();

                ImageLib::WritePNGImage((GetAppDataFolder() + std::string("_dump\\") + anImageName).c_str(), &anFullImage);

                anFullImage.mBits = NULL;

                anImgNum++;

                aSortedItr++;          
        }

        aDumpStream << "<TD>Totals</TD>" << std::endl;
        aDumpStream << "<TD>" << SexyStringToString(CommaSeperate(aTotalMemorySize)) << "</TD>" << std::endl;  
        aDumpStream << "<TD>" << SexyStringToString(CommaSeperate(aTotalBitsMemory)) << "</TD>" << std::endl;  
        aDumpStream << "<TD>" << SexyStringToString(CommaSeperate(aTotalPalletizedMemory)) << "</TD>" << std::endl;                            
        aDumpStream << "<TD>" << SexyStringToString(CommaSeperate(aTotalSurfaceMemory)) << "</TD>" << std::endl;               
        aDumpStream << "<TD>" << SexyStringToString(CommaSeperate(aTotalTextureMemory)) << "</TD>" << std::endl;               
        aDumpStream << "<TD>&nbsp;</TD>" << std::endl;
        aDumpStream << "<TD>&nbsp;</TD>" << std::endl;
        aDumpStream << "<TD>&nbsp;</TD>" << std::endl;
        aDumpStream << "<TD>&nbsp;</TD>" << std::endl;
        aDumpStream << "<TD>" << SexyStringToString(CommaSeperate(aTotalNativeAlphaMemory)) << "</TD>" << std::endl;
        aDumpStream << "<TD>" << SexyStringToString(CommaSeperate(aTotalRLAlphaMemory)) << "</TD>" << std::endl;
        aDumpStream << "<TD>" << SexyStringToString(CommaSeperate(aTotalRLAdditiveMemory)) << "</TD>" << std::endl;
        aDumpStream << "<TD>&nbsp;</TD>" << std::endl;

        aDumpStream << "</TABLE></CENTER></BODY></HTML>" << std::endl;
}

double SexyAppBase::GetLoadingThreadProgress()
{
        if (mLoaded)
                return 1.0;
        if (!mLoadingThreadStarted)
                return 0.0;
        if (mNumLoadingThreadTasks == 0)
                return 0.0;
        return min(mCompletedLoadingThreadTasks / (double) mNumLoadingThreadTasks, 1.0);
}

bool SexyAppBase::RegistryWrite(const std::string& theValueName, ulong theType, const uchar* theValue, ulong theLength)
{
        if (mRegKey.length() == 0)
                return false;

        if (mPlayingDemoBuffer)
        {
                if (mManualShutdown)
                        return true;

                PrepareDemoCommand(true);
                mDemoNeedsCommand = true;

                DBG_ASSERTE(!mDemoIsShortCmd);         
                DBG_ASSERTE(mDemoCmdNum == DEMO_REGISTRY_WRITE);

                return mDemoBuffer.ReadNumBits(1, false) != 0;         
        }

        HKEY aGameKey;
       
        std::string aKeyName = RemoveTrailingSlash("SOFTWARE\\" + mRegKey);
        std::string aValueName;

        int aSlashPos = (int) theValueName.rfind('\\');
        if (aSlashPos != -1)
        {
                aKeyName += "\\" + theValueName.substr(0, aSlashPos);
                aValueName = theValueName.substr(aSlashPos + 1);
        }
        else
        {
                aValueName = theValueName;
        }

        int aResult = RegOpenKeyExA(HKEY_CURRENT_USER, aKeyName.c_str(), 0, KEY_WRITE, &aGameKey);
        if (aResult != ERROR_SUCCESS)
        {
                ulong aDisp;
                aResult = RegCreateKeyExA(HKEY_CURRENT_USER, aKeyName.c_str(), 0, "Key", REG_OPTION_NON_VOLATILE,
                        KEY_ALL_ACCESS, NULL, &aGameKey, &aDisp);
        }

        if (aResult != ERROR_SUCCESS)
        {
                if (mRecordingDemoBuffer)
                {
                        WriteDemoTimingBlock();
                        mDemoBuffer.WriteNumBits(0, 1);
                        mDemoBuffer.WriteNumBits(DEMO_REGISTRY_WRITE, 5);
                        mDemoBuffer.WriteNumBits(0, 1); // failure
                }

                return false;
        }
               
        RegSetValueExA(aGameKey, aValueName.c_str(), 0, theType, theValue, theLength);
        RegCloseKey(aGameKey);

        if (mRecordingDemoBuffer)
        {
                WriteDemoTimingBlock();
                mDemoBuffer.WriteNumBits(0, 1);
                mDemoBuffer.WriteNumBits(DEMO_REGISTRY_WRITE, 5);
                mDemoBuffer.WriteNumBits(1, 1); // success
        }

        return true;
}

bool SexyAppBase::RegistryWriteString(const std::string& theValueName, const std::string& theString)
{
        return RegistryWrite(theValueName, REG_SZ, (uchar*) theString.c_str(), theString.length());
}

bool SexyAppBase::RegistryWriteInteger(const std::string& theValueName, int theValue)
{
        return RegistryWrite(theValueName, REG_DWORD, (uchar*) &theValue, sizeof(int));
}

bool SexyAppBase::RegistryWriteBoolean(const std::string& theValueName, bool theValue)
{
        int aValue = theValue ? 1 : 0;
        return RegistryWrite(theValueName, REG_DWORD, (uchar*) &aValue, sizeof(int));
}

bool SexyAppBase::RegistryWriteData(const std::string& theValueName, const uchar* theValue, ulong theLength)
{
        return RegistryWrite(theValueName, REG_BINARY, (uchar*) theValue, theLength);
}

void SexyAppBase::WriteToRegistry()
{      
        RegistryWriteInteger("MusicVolume", (int) (mMusicVolume * 100));
        RegistryWriteInteger("SfxVolume", (int) (mSfxVolume * 100));
        RegistryWriteInteger("Muted", (mMuteCount - mAutoMuteCount > 0) ? 1 : 0);
        RegistryWriteInteger("ScreenMode", mIsWindowed ? 0 : 1);
        RegistryWriteInteger("PreferredX", mPreferredX);
        RegistryWriteInteger("PreferredY", mPreferredY);
        RegistryWriteInteger("CustomCursors", mCustomCursorsEnabled ? 1 : 0);          
        RegistryWriteInteger("InProgress", 0);
        RegistryWriteBoolean("WaitForVSync", mWaitForVSync);   
}

bool SexyAppBase::RegistryEraseKey(const SexyString& _theKeyName)
{
        std::string theKeyName = SexyStringToStringFast(_theKeyName);
        if (mRegKey.length() == 0)
                return false;

        if (mPlayingDemoBuffer)
        {
                if (mManualShutdown)
                        return true;

                PrepareDemoCommand(true);
                mDemoNeedsCommand = true;

                DBG_ASSERTE(!mDemoIsShortCmd);         
                DBG_ASSERTE(mDemoCmdNum == DEMO_REGISTRY_ERASE);

                return mDemoBuffer.ReadNumBits(1, false) != 0;         
        }      
       
        std::string aKeyName = RemoveTrailingSlash("SOFTWARE\\" + mRegKey) + "\\" + theKeyName;

        int aResult = RegDeleteKeyA(HKEY_CURRENT_USER, aKeyName.c_str());
        if (aResult != ERROR_SUCCESS)
        {
                if (mRecordingDemoBuffer)
                {
                        WriteDemoTimingBlock();
                        mDemoBuffer.WriteNumBits(0, 1);
                        mDemoBuffer.WriteNumBits(DEMO_REGISTRY_ERASE, 5);
                        mDemoBuffer.WriteNumBits(0, 1); // failure
                }

                return false;
        }              

        if (mRecordingDemoBuffer)
        {
                WriteDemoTimingBlock();
                mDemoBuffer.WriteNumBits(0, 1);
                mDemoBuffer.WriteNumBits(DEMO_REGISTRY_ERASE, 5);
                mDemoBuffer.WriteNumBits(1, 1); // success
        }

        return true;
}

void SexyAppBase::RegistryEraseValue(const SexyString& _theValueName)
{
        std::string theValueName = SexyStringToStringFast(_theValueName);
        if (mRegKey.length() == 0)
                return;

        HKEY aGameKey; 
        std::string aKeyName = RemoveTrailingSlash("SOFTWARE\\" + mRegKey);
        std::string aValueName;

        int aSlashPos = (int) theValueName.rfind('\\');
        if (aSlashPos != -1)
        {
                aKeyName += "\\" + theValueName.substr(0, aSlashPos);
                aValueName = theValueName.substr(aSlashPos + 1);
        }
        else
        {
                aValueName = theValueName;
        }

        int aResult = RegOpenKeyExA(HKEY_CURRENT_USER, aKeyName.c_str(), 0, KEY_WRITE, &aGameKey);
        if (aResult == ERROR_SUCCESS)
        {              
                RegDeleteValueA(aGameKey, aValueName.c_str());
                RegCloseKey(aGameKey);
        }
}

bool SexyAppBase::RegistryGetSubKeys(const std::string& theKeyName, StringVector* theSubKeys)
{
        theSubKeys->clear();

        if (mRegKey.length() == 0)
                return false;

        if (mPlayingDemoBuffer)
        {
                if (mManualShutdown)
                        return true;

                PrepareDemoCommand(true);
                mDemoNeedsCommand = true;

                DBG_ASSERTE(!mDemoIsShortCmd);         
                DBG_ASSERTE(mDemoCmdNum == DEMO_REGISTRY_GETSUBKEYS);

                bool success = mDemoBuffer.ReadNumBits(1, false) != 0;
                if (!success)
                        return false;

                int aNumKeys = mDemoBuffer.ReadLong();

                for (int i = 0; i < aNumKeys; i++)
                        theSubKeys->push_back(mDemoBuffer.ReadString());

                return true;
        }
        else
        {
                HKEY aKey;
               
                std::string aKeyName = RemoveTrailingSlash(RemoveTrailingSlash("SOFTWARE\\" + mRegKey) + "\\" + theKeyName);   
                int aResult = RegOpenKeyExA(HKEY_CURRENT_USER, aKeyName.c_str(), 0, KEY_READ, &aKey);
               
                if (aResult == ERROR_SUCCESS)
                {              
                        for (int anIdx = 0; ; anIdx++)
                        {
                                char aStr[1024];

                                aResult = RegEnumKeyA(aKey, anIdx, aStr, 1024);
                                if (aResult != ERROR_SUCCESS)
                                        break;

                                theSubKeys->push_back(aStr);
                        }

                        RegCloseKey(aKey);

                        if (mRecordingDemoBuffer)
                        {
                                WriteDemoTimingBlock();
                                mDemoBuffer.WriteNumBits(0, 1);
                                mDemoBuffer.WriteNumBits(DEMO_REGISTRY_GETSUBKEYS, 5);
                                mDemoBuffer.WriteNumBits(1, 1); // success
                                mDemoBuffer.WriteLong(theSubKeys->size());

                                for (int i = 0; i < (int) theSubKeys->size(); i++)
                                        mDemoBuffer.WriteString((*theSubKeys)[i]);                             
                        }

                        return true;
                }
                else
                {
                        if (mRecordingDemoBuffer)
                        {
                                WriteDemoTimingBlock();
                                mDemoBuffer.WriteNumBits(0, 1);
                                mDemoBuffer.WriteNumBits(DEMO_REGISTRY_GETSUBKEYS, 5);
                                mDemoBuffer.WriteNumBits(0, 1); // failure
                        }

                        return false;  
                }
        }
}

bool SexyAppBase::RegistryRead(const std::string& theValueName, ulong* theType, uchar* theValue, ulong* theLength)
{
        return RegistryReadKey(theValueName, theType, theValue, theLength, HKEY_CURRENT_USER);
}

bool SexyAppBase::RegistryReadKey(const std::string& theValueName, ulong* theType, uchar* theValue, ulong* theLength, HKEY theKey)
{
        if (mRegKey.length() == 0)
                return false;

        if (mPlayingDemoBuffer)
        {
                if (mManualShutdown)
                        return false;

                PrepareDemoCommand(true);
                mDemoNeedsCommand = true;
                               
                DBG_ASSERTE(!mDemoIsShortCmd);         
                DBG_ASSERTE(mDemoCmdNum == DEMO_REGISTRY_READ);

                bool success = mDemoBuffer.ReadNumBits(1, false) != 0;
                if (!success)
                        return false;

                *theType = mDemoBuffer.ReadLong();

                ulong aLen = mDemoBuffer.ReadLong();
                *theLength = aLen;
               
                if (*theLength >= aLen)
                {                      
                        mDemoBuffer.ReadBytes(theValue, aLen);
                        return true;
                }
                else
                {
                        for (int i = 0; i < (int) aLen; i++)
                                mDemoBuffer.ReadByte();
                        return false;
                }              
        }
        else
        {              
                HKEY aGameKey;

                std::string aKeyName = RemoveTrailingSlash("SOFTWARE\\" + mRegKey);
                std::string aValueName;

                int aSlashPos = (int) theValueName.rfind('\\');
                if (aSlashPos != -1)
                {
                        aKeyName += "\\" + theValueName.substr(0, aSlashPos);
                        aValueName = theValueName.substr(aSlashPos + 1);
                }
                else
                {
                        aValueName = theValueName;
                }              

                if (RegOpenKeyExA(theKey, aKeyName.c_str(), 0, KEY_READ, &aGameKey) == ERROR_SUCCESS)
                {
                        if (RegQueryValueExA(aGameKey, aValueName.c_str(), 0, theType, (uchar*) theValue, theLength) == ERROR_SUCCESS)
                        {
                                if (mRecordingDemoBuffer)
                                {
                                        WriteDemoTimingBlock();
                                        mDemoBuffer.WriteNumBits(0, 1);
                                        mDemoBuffer.WriteNumBits(DEMO_REGISTRY_READ, 5);
                                        mDemoBuffer.WriteNumBits(1, 1); // success
                                        mDemoBuffer.WriteLong(*theType);
                                        mDemoBuffer.WriteLong(*theLength);
                                        mDemoBuffer.WriteBytes(theValue, *theLength);
                                }

                                RegCloseKey(aGameKey);
                                return true;   
                        }

                        RegCloseKey(aGameKey);
                }

                if (mRecordingDemoBuffer)
                {
                        WriteDemoTimingBlock();
                        mDemoBuffer.WriteNumBits(0, 1);
                        mDemoBuffer.WriteNumBits(DEMO_REGISTRY_READ, 5);
                        mDemoBuffer.WriteNumBits(0, 1); // failure
                }
               
                return false;
        }
}

bool SexyAppBase::RegistryReadString(const std::string& theKey, std::string* theString)
{
        char aStr[1024];
       
        ulong aType;   
        ulong aLen = sizeof(aStr) - 1;
        if (!RegistryRead(theKey, &aType, (uchar*) aStr, &aLen))
                return false;

        if (aType != REG_SZ)
                return false;

        aStr[aLen] = 0;

        *theString = aStr;
        return true;
}

bool SexyAppBase::RegistryReadInteger(const std::string& theKey, int* theValue)
{
        ulong aType;
        ulong aLong;
        ulong aLen = 4;
        if (!RegistryRead(theKey, &aType, (uchar*) &aLong, &aLen))
                return false;

        if (aType != REG_DWORD)
                return false;

        *theValue = aLong;
        return true;
}

bool SexyAppBase::RegistryReadBoolean(const std::string& theKey, bool* theValue)
{
        int aValue;
        if (!RegistryReadInteger(theKey, &aValue))
                return false;
       
        *theValue = aValue != 0;
        return true;
}

bool SexyAppBase::RegistryReadData(const std::string& theKey, uchar* theValue, ulong* theLength)
{              
        ulong aType;
        ulong aLen = *theLength;
        if (!RegistryRead(theKey, &aType, (uchar*) theValue, theLength))
                return false;

        if (aType != REG_BINARY)
                return false;
       
        return true;
}

void SexyAppBase::ReadFromRegistry()
{
        mReadFromRegistry = true;
        mRegKey = SexyStringToString(GetString("RegistryKey", StringToSexyString(mRegKey)));

        if (mRegKey.length() == 0)
                return;                        

        int anInt;
        if (RegistryReadInteger("MusicVolume", &anInt))
                mMusicVolume = anInt / 100.0;
       
        if (RegistryReadInteger("SfxVolume", &anInt))
                mSfxVolume = anInt / 100.0;

        if (RegistryReadInteger("Muted", &anInt))
                mMuteCount = anInt;

        if (RegistryReadInteger("ScreenMode", &anInt))
                mIsWindowed = anInt == 0;

        RegistryReadInteger("PreferredX", &mPreferredX);
        RegistryReadInteger("PreferredY", &mPreferredY);       

        if (RegistryReadInteger("CustomCursors", &anInt))
                EnableCustomCursors(anInt != 0);       
                       
        RegistryReadBoolean("WaitForVSync", &mWaitForVSync);   

        if (RegistryReadInteger("InProgress", &anInt))
                mLastShutdownWasGraceful = anInt == 0;
               
        if (!IsScreenSaver())
                RegistryWriteInteger("InProgress", 1); 
}

bool SexyAppBase::WriteBytesToFile(const std::string& theFileName, const void *theData, unsigned long theDataLen)
{
        if (mPlayingDemoBuffer)
        {
                if (mManualShutdown)
                        return true;

                PrepareDemoCommand(true);
                mDemoNeedsCommand = true;

                DBG_ASSERTE(!mDemoIsShortCmd);         
                DBG_ASSERTE(mDemoCmdNum == DEMO_FILE_WRITE);           

                bool success = mDemoBuffer.ReadNumBits(1, false) != 0;
                if (!success)
                        return false;

                return true;
        }      

        MkDir(GetFileDir(theFileName));
        FILE* aFP = fopen(theFileName.c_str(), "w+b");

        if (aFP == NULL)
        {
                if (mRecordingDemoBuffer)
                {
                        WriteDemoTimingBlock();
                        mDemoBuffer.WriteNumBits(0, 1);
                        mDemoBuffer.WriteNumBits(DEMO_FILE_WRITE, 5);
                        mDemoBuffer.WriteNumBits(0, 1); // failure                             
                }

                return false;
        }

        fwrite(theData, 1, theDataLen, aFP);
        fclose(aFP);

        if (mRecordingDemoBuffer)
        {
                WriteDemoTimingBlock();
                mDemoBuffer.WriteNumBits(0, 1);
                mDemoBuffer.WriteNumBits(DEMO_FILE_WRITE, 5);
                mDemoBuffer.WriteNumBits(1, 1); // success
        }

        return true;
}

bool SexyAppBase::WriteBufferToFile(const std::string& theFileName, const Buffer* theBuffer)
{
        return WriteBytesToFile(theFileName,theBuffer->GetDataPtr(),theBuffer->GetDataLen());
}


bool SexyAppBase::ReadBufferFromFile(const std::string& theFileName, Buffer* theBuffer, bool dontWriteToDemo)
{
        if ((mPlayingDemoBuffer) && (!dontWriteToDemo))
        {
                if (mManualShutdown)
                        return false;

                PrepareDemoCommand(true);
                mDemoNeedsCommand = true;
               
                DBG_ASSERTE(!mDemoIsShortCmd);         
                DBG_ASSERTE(mDemoCmdNum == DEMO_FILE_READ);

                bool success = mDemoBuffer.ReadNumBits(1, false) != 0;
                if (!success)
                        return false;

                ulong aLen = mDemoBuffer.ReadLong();           
                               
                theBuffer->Clear();
                for (int i = 0; i < (int) aLen; i++)
                        theBuffer->WriteByte(mDemoBuffer.ReadByte());

                return true;           
        }
        else
        {
                PFILE* aFP = p_fopen(theFileName.c_str(), "rb");

                if (aFP == NULL)
                {
                        if ((mRecordingDemoBuffer) && (!dontWriteToDemo))
                        {
                                WriteDemoTimingBlock();
                                mDemoBuffer.WriteNumBits(0, 1);
                                mDemoBuffer.WriteNumBits(DEMO_FILE_READ, 5);
                                mDemoBuffer.WriteNumBits(0, 1); // failure                             
                        }

                        return false;
                }
               
                p_fseek(aFP, 0, SEEK_END);
                int aFileSize = p_ftell(aFP);
                p_fseek(aFP, 0, SEEK_SET);
               
                uchar* aData = new uchar[aFileSize];

                p_fread(aData, 1, aFileSize, aFP);
                p_fclose(aFP);

                theBuffer->Clear();
                theBuffer->SetData(aData, aFileSize);

                if ((mRecordingDemoBuffer) && (!dontWriteToDemo))
                {
                        WriteDemoTimingBlock();
                        mDemoBuffer.WriteNumBits(0, 1);
                        mDemoBuffer.WriteNumBits(DEMO_FILE_READ, 5);
                        mDemoBuffer.WriteNumBits(1, 1); // success                     
                        mDemoBuffer.WriteLong(aFileSize);
                        mDemoBuffer.WriteBytes(aData, aFileSize);
                }

                delete [] aData;

                return true;
        }
}

bool SexyAppBase::FileExists(const std::string& theFileName)
{
        if (mPlayingDemoBuffer)
        {
                if (mManualShutdown)
                        return true;

                PrepareDemoCommand(true);
                mDemoNeedsCommand = true;
               
                DBG_ASSERTE(!mDemoIsShortCmd);         
                DBG_ASSERTE(mDemoCmdNum == DEMO_FILE_EXISTS);          

                bool success = mDemoBuffer.ReadNumBits(1, false) != 0;         
                return success;
        }
        else
        {
                PFILE* aFP = p_fopen(theFileName.c_str(), "rb");

                if (mRecordingDemoBuffer)
                {
                        WriteDemoTimingBlock();
                        mDemoBuffer.WriteNumBits(0, 1);
                        mDemoBuffer.WriteNumBits(DEMO_FILE_EXISTS, 5);
                        mDemoBuffer.WriteNumBits((aFP != NULL) ? 1 : 0, 1);
                }

                if (aFP == NULL)               
                        return false;          
               
                p_fclose(aFP);
                return true;
        }
}

bool SexyAppBase::EraseFile(const std::string& theFileName)
{
        if (mPlayingDemoBuffer)
                return true;

        return DeleteFileA(theFileName.c_str()) != 0;
}

void SexyAppBase::SEHOccured()
{
        SetMusicVolume(0);
        ::ShowWindow(mHWnd, SW_HIDE);
        mSEHOccured = true;
        EnforceCursor();
}

std::string SexyAppBase::GetGameSEHInfo()
{
        int aSecLoaded = (GetTickCount() - mTimeLoaded) / 1000;

        char aTimeStr[16];
        sprintf(aTimeStr, "%02d:%02d:%02d", (aSecLoaded/60/60), (aSecLoaded/60)%60, aSecLoaded%60);
       
        char aThreadIdStr[16];
        sprintf(aThreadIdStr, "%X", mPrimaryThreadId);

        std::string anInfoString =
                "Product: " + mProdName + "\r\n" +             
                "Version: " + mProductVersion + "\r\n";                

        anInfoString +=
                "Time Loaded: " + std::string(aTimeStr) + "\r\n"
                "Fullscreen: " + (mIsWindowed ? std::string("No") : std::string("Yes")) + "\r\n"
                "Primary ThreadId: " + aThreadIdStr + "\r\n";  

        return anInfoString;                                           
}

void SexyAppBase::GetSEHWebParams(DefinesMap* theDefinesMap)
{
}

void SexyAppBase::ShutdownHook()
{
}

void SexyAppBase::Shutdown()
{
        if ((mPrimaryThreadId != 0) && (GetCurrentThreadId() != mPrimaryThreadId))
        {
                mLoadingFailed = true;
        }
        else if (!mShutdown)
        {
                mExitToTop = true;
                mShutdown = true;
                ShutdownHook();

                if (mPlayingDemoBuffer)
                {
                        //if the music/sfx volume is 0, then it means that in playback
                        //someone pressed the "S" key to mute sounds (or that the
                        //sound volume was set to 0 in the first place). Out of politeness,
                        //return the system sound volume to what it last was in the game.
                        SetMusicVolume(mDemoMusicVolume);
                        SetSfxVolume(mDemoSfxVolume);
                }

                // Blah
                while (mCursorThreadRunning)
                {
                        Sleep(10);
                }
               
                if (mMusicInterface != NULL)
                        mMusicInterface->StopAllMusic();               
               
                if ((!mIsPhysWindowed) && (mDDInterface != NULL) && (mDDInterface->mDD != NULL))
                {
                        mDDInterface->mDD->RestoreDisplayMode();
                }

                if (mHWnd != NULL)
                {
                        ShowWindow(mHWnd, SW_HIDE);                    
                }

                RestoreScreenResolution();

                if (mReadFromRegistry)
                        WriteToRegistry();

                ImageLib::CloseJPEG2000();
        }
}

void SexyAppBase::RestoreScreenResolution()
{
        if (mFullScreenWindow)
        {
                EnumWindows(ChangeDisplayWindowEnumProc,0); // get any windows that appeared while we were running
                ChangeDisplaySettings(NULL,0);
                EnumWindows(ChangeDisplayWindowEnumProc,1); // restore window pos
                mFullScreenWindow = false;
        }
}
       
void SexyAppBase::DoExit(int theCode)
{
        RestoreScreenResolution();
        exit(theCode);
}

void SexyAppBase::UpdateFrames()
{
        mUpdateCount++;

        if (!mMinimized)
        {              
                if (mWidgetManager->UpdateFrame())
                        ++mFPSDirtyCount;
        }

        mMusicInterface->Update();     
        CleanSharedImages();
}

void SexyAppBase::DoUpdateFramesF(float theFrac)
{
        if ((mVSyncUpdates) && (!mMinimized))
                mWidgetManager->UpdateFrameF(theFrac); 
}

bool SexyAppBase::DoUpdateFrames()
{
        SEXY_AUTO_PERF("SexyAppBase::DoUpdateFrames");

        if (gScreenSaverActive)
                return false;

        if (mPlayingDemoBuffer)
        {
                if ((mLoadingThreadCompleted) && (!mLoaded) && (mDemoLoadingComplete))
                {                      
                        mLoaded = true;
                        ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_NORMAL);
                        mYieldMainThread = false;
                        LoadingThreadCompleted();
                }

                // Hrrm not sure why we check (mUpdateCount != mLastDemoUpdateCnt) here
                if ((mLoaded == mDemoLoadingComplete) && (mUpdateCount != mLastDemoUpdateCnt))         
                {
                        UpdateFrames();        
                        return true;
                }

                return false;
        }
        else
        {
                if ((mLoadingThreadCompleted) && (!mLoaded))
                {
                        ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_NORMAL);
                        mLoaded = true;
                        mYieldMainThread = false;
                        LoadingThreadCompleted();

                        if (mRecordingDemoBuffer)
                        {
                                WriteDemoTimingBlock();
                                mDemoBuffer.WriteNumBits(0, 1);
                                mDemoBuffer.WriteNumBits(DEMO_LOADING_COMPLETE, 5);    
                        }
                }
                               
                UpdateFrames();        
                return true;
        }
}

bool gIsFailing = false;

void SexyAppBase::Redraw(Rect* theClipRect)
{
        SEXY_AUTO_PERF("SexyAppBase::Redraw");

        // Do mIsDrawing check because we could enter here at a bad time if any windows messages
        //  are processed during WidgetManager->Draw
        if ((mIsDrawing) || (mShutdown))
                return;

        if (gScreenSaverActive)
                return;

        static DWORD aRetryTick = 0;
        if (!mDDInterface->Redraw(theClipRect))
        {
                extern bool gD3DInterfacePreDrawError;
                gD3DInterfacePreDrawError = false; // this predraw error happens naturally when ddraw is failing
                if (!gIsFailing)
                {
                        //gDebugStream << GetTickCount() << " Redraw failed!" << std::endl;
                        gIsFailing = true;
                }

                WINDOWPLACEMENT aWindowPlacement;
                ZeroMemory(&aWindowPlacement, sizeof(aWindowPlacement));
                aWindowPlacement.length = sizeof(aWindowPlacement);
                ::GetWindowPlacement(mHWnd, &aWindowPlacement);
               
                DWORD aTick = GetTickCount();
                if ((mActive || (aTick-aRetryTick>1000 && mIsPhysWindowed)) && (aWindowPlacement.showCmd != SW_SHOWMINIMIZED) && (!mMinimized))
                {
                        aRetryTick = aTick;

                        mWidgetManager->mImage = NULL;

                        // Re-check resolution at this point, because we hit here when you change your resolution.
                        if (((mWidth >= GetSystemMetrics(SM_CXFULLSCREEN)) || (mHeight >= GetSystemMetrics(SM_CYFULLSCREEN))) && (mIsWindowed))
                        {
                                if (mForceWindowed)
                                {
                                        Popup(GetString("PLEASE_SET_COLOR_DEPTH", _S("Please set your desktop color depth to 16 bit.")));
                                        Shutdown();
                                        return;
                                }
                                mForceFullscreen = true;

                                SwitchScreenMode(false);
                                return;
                        }


                        int aResult = InitDDInterface();

                        //gDebugStream << GetTickCount() << " ReInit..." << std::endl;
                       
                        if ((mIsWindowed) && (aResult == DDInterface::RESULT_INVALID_COLORDEPTH))
                        {
                                //gDebugStream << GetTickCount() << "ReInit Invalid Colordepth" << std::endl;
                                if (!mActive) // don't switch to full screen if not active app
                                        return;

                                SwitchScreenMode(false);
                                mForceFullscreen = true;
                                return;
                        }
                        else if (aResult == DDInterface::RESULT_3D_FAIL)
                        {
                                Set3DAcclerated(false);
                                return;
                        }
                        else if (aResult != DDInterface::RESULT_OK)
                        {
                                //gDebugStream << GetTickCount() << " ReInit Failed" << std::endl;
                                //Fail("Failed to initialize DirectDraw");
                                //Sleep(1000);                         
                               
                                return;
                        }                                              

                        ReInitImages();

                        mWidgetManager->mImage = mDDInterface->GetScreenImage();
                        mWidgetManager->MarkAllDirty();

                        mLastTime = timeGetTime();
                }
        }
        else
        {
                if (gIsFailing)
                {
                        //gDebugStream << GetTickCount() << " Redraw succeeded" << std::endl;
                        gIsFailing = false;
                        aRetryTick = 0;
                }
        }

        mFPSFlipCount++;
}

///////////////////////////// FPS Stuff
static PerfTimer gFPSTimer;
static int gFrameCount;
static int gFPSDisplay;
static bool gForceDisplay = false;
static void CalculateFPS()
{
        gFrameCount++;

        static SysFont aFont(gSexyAppBase,"Tahoma",8);
        if (gFPSImage==NULL)
        {
                gFPSImage = new DDImage(gSexyAppBase->mDDInterface);
                gFPSImage->Create(50,aFont.GetHeight()+4);
                gFPSImage->SetImageMode(false,false);
                gFPSImage->SetVolatile(true);
                gFPSImage->mPurgeBits = false;
                gFPSImage->mWantDDSurface = true;
                gFPSImage->PurgeBits();
        }

        if (gFPSTimer.GetDuration() >= 1000 || gForceDisplay)
        {
                gFPSTimer.Stop();
                if (!gForceDisplay)
                        gFPSDisplay = (int)(gFrameCount*1000/gFPSTimer.GetDuration() + 0.5f);
                else
                {
                        gForceDisplay = false;
                        gFPSDisplay = 0;
                }

                gFPSTimer.Start();
                gFrameCount = 0;

                Graphics aDrawG(gFPSImage);
                aDrawG.SetFont(&aFont);
                SexyString aFPS = StrFormat(_S("FPS: %d"), gFPSDisplay);
                aDrawG.SetColor(0x000000);
                aDrawG.FillRect(0,0,gFPSImage->GetWidth(),gFPSImage->GetHeight());
                aDrawG.SetColor(0xFFFFFF);
                aDrawG.DrawString(aFPS,2,aFont.GetAscent());
                //gFPSImage->mKeepBits = false;
                //gFPSImage->GenerateDDSurface();
                gFPSImage->mBitsChangedCount++;
        }
}

///////////////////////////// FPS Stuff to draw mouse coords
static void FPSDrawCoords(int theX, int theY)
{
        static SysFont aFont(gSexyAppBase,"Tahoma",8);
        if (gFPSImage==NULL)
        {
                gFPSImage = new DDImage(gSexyAppBase->mDDInterface);
                gFPSImage->Create(50,aFont.GetHeight()+4);
                gFPSImage->SetImageMode(false,false);
                gFPSImage->SetVolatile(true);
                gFPSImage->mPurgeBits = false;
                gFPSImage->mWantDDSurface = true;
                gFPSImage->PurgeBits();
        }

        Graphics aDrawG(gFPSImage);
        aDrawG.SetFont(&aFont);
        SexyString aFPS = StrFormat(_S("%d,%d"),theX,theY);
        aDrawG.SetColor(0x000000);
        aDrawG.FillRect(0,0,gFPSImage->GetWidth(),gFPSImage->GetHeight());
        aDrawG.SetColor(0xFFFFFF);
        aDrawG.DrawString(aFPS,2,aFont.GetAscent());   
        gFPSImage->mBitsChangedCount++;
}

///////////////////////////// Demo TimeLeft Stuff
static DDImage* gDemoTimeLeftImage = NULL;
static void CalculateDemoTimeLeft()
{
        static SysFont aFont(gSexyAppBase,"Tahoma",8);
        static DWORD aLastTick = 0;

        if (gDemoTimeLeftImage==NULL)
        {
                gDemoTimeLeftImage = new DDImage(gSexyAppBase->mDDInterface);
                gDemoTimeLeftImage->Create(50,aFont.GetHeight()+4);
                gDemoTimeLeftImage->SetImageMode(false,false);
                gDemoTimeLeftImage->SetVolatile(true);
                gDemoTimeLeftImage->mPurgeBits = false;
                gDemoTimeLeftImage->mWantDDSurface = true;
                gDemoTimeLeftImage->PurgeBits();
        }

        DWORD aTick = GetTickCount();
        if (aTick - aLastTick < 1000/gSexyAppBase->mUpdateMultiplier)
                return;

        aLastTick = aTick;

        int aNumUpdatesLeft = gSexyAppBase->mDemoLength - gSexyAppBase->mUpdateCount;
        Graphics aDrawG(gDemoTimeLeftImage);
        aDrawG.SetFont(&aFont);

        int aTotalSeconds = aNumUpdatesLeft*gSexyAppBase->mFrameTime/1000;
        int aSeconds = aTotalSeconds%60;
        int aMinutes = (aTotalSeconds/60)%60;
        int anHours = (aTotalSeconds/3600);

        SexyString aFPS = StrFormat(_S("%02d:%02d:%02d"), anHours,aMinutes,aSeconds);
        aDrawG.SetColor(0x000000);
        aDrawG.FillRect(0,0,gDemoTimeLeftImage->GetWidth(),gDemoTimeLeftImage->GetHeight());
        aDrawG.SetColor(0xFFFFFF);
        aDrawG.DrawString(aFPS,2,aFont.GetAscent());   
        gDemoTimeLeftImage->mBitsChangedCount++;
}

static void UpdateScreenSaverInfo(DWORD theTick)
{
        if (gSexyAppBase->IsScreenSaver() || !gSexyAppBase->mIsPhysWindowed)
                return;

        // Get screen saver timeout            
        static DWORD aPeriodicTick = 0;
        static DWORD aScreenSaverTimeout = 60000;
        static BOOL aScreenSaverEnabled = TRUE;

        if (theTick-aPeriodicTick > 10000)
        {
                aPeriodicTick = theTick;

                int aTimeout = 0;

                SystemParametersInfo(SPI_GETSCREENSAVETIMEOUT,0,&aTimeout,0);
                SystemParametersInfo(SPI_GETSCREENSAVEACTIVE,0,&aScreenSaverEnabled,0);
                aTimeout-=2;

                if (aTimeout < 1)
                        aTimeout = 1;

                aScreenSaverTimeout = aTimeout*1000;

                if (!aScreenSaverEnabled)
                        gScreenSaverActive = false;
        }

        // Get more accurate last user input time
        if (gGetLastInputInfoFunc)
        {
                LASTINPUTINFO anInfo;
                anInfo.cbSize = sizeof(anInfo);
                if (gGetLastInputInfoFunc(&anInfo))
                {
                        if (anInfo.dwTime > theTick)
                                anInfo.dwTime = theTick;

                        gSexyAppBase->mLastUserInputTick = anInfo.dwTime;
                }
        }

        if (!aScreenSaverEnabled)
                return;

        DWORD anIdleTime = theTick - gSexyAppBase->mLastUserInputTick;
        if (gScreenSaverActive)
        {
                BOOL aBool = FALSE;
                if (SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &aBool, 0))
                {
                        if (aBool) // screen saver not off yet
                                return;
                }

                if (anIdleTime < aScreenSaverTimeout)
                {
                        gScreenSaverActive = false;
                        gSexyAppBase->mWidgetManager->MarkAllDirty();
                }
        }
        else if (anIdleTime > aScreenSaverTimeout)
                gScreenSaverActive = true;
}

bool SexyAppBase::DrawDirtyStuff()
{
        SEXY_AUTO_PERF("SexyAppBase::DrawDirtyStuff");
        MTAutoDisallowRand aDisallowRand;

        if (gIsFailing) // just try to reinit
        {
                Redraw(NULL);
                mHasPendingDraw = false;
                mLastDrawWasEmpty = true;              
                return false;
        }      

        if (mShowFPS)
        {
                switch(mShowFPSMode)
                {
                        case FPS_ShowFPS: CalculateFPS(); break;
                        case FPS_ShowCoords:
                                if (mWidgetManager!=NULL)
                                        FPSDrawCoords(mWidgetManager->mLastMouseX, mWidgetManager->mLastMouseY);
                                break;
                }

                if (mPlayingDemoBuffer)
                        CalculateDemoTimeLeft();
        }

        DWORD aStartTime = timeGetTime();

        // Update user input and screen saver info
        static DWORD aPeriodicTick = 0;
        if (aStartTime-aPeriodicTick > 1000)
        {
                aPeriodicTick = aStartTime;
                UpdateScreenSaverInfo(aStartTime);
        }

        if (gScreenSaverActive)
        {
                mHasPendingDraw = false;
                mLastDrawWasEmpty = true;              
                return false;
        }

        mIsDrawing = true;
        bool drewScreen = mWidgetManager->DrawScreen();
        mIsDrawing = false;

        if ((drewScreen || (aStartTime - mLastDrawTick >= 1000) || (mCustomCursorDirty)) &&
                ((int) (aStartTime - mNextDrawTick) >= 0))
        {
                mLastDrawWasEmpty = false;

                mDrawCount++;          

                DWORD aMidTime = timeGetTime();

                mFPSCount++;
                mFPSTime += aMidTime - aStartTime;

                mDrawTime += aMidTime - aStartTime;

                if (mShowFPS)
                {
                        Graphics g(mDDInterface->GetScreenImage());
                        g.DrawImage(gFPSImage,mWidth-gFPSImage->GetWidth()-10,mHeight-gFPSImage->GetHeight()-10);
               
                        if (mPlayingDemoBuffer)
                                g.DrawImage(gDemoTimeLeftImage,mWidth-gDemoTimeLeftImage->GetWidth()-10,mHeight-gFPSImage->GetHeight()-gDemoTimeLeftImage->GetHeight()-15);
                }

                if (mWaitForVSync && mIsPhysWindowed && mSoftVSyncWait)
                {
                        DWORD aTick = timeGetTime();
                        if (aTick-mLastDrawTick < mDDInterface->mMillisecondsPerFrame)
                                Sleep(mDDInterface->mMillisecondsPerFrame - (aTick-mLastDrawTick));
                }

                DWORD aPreScreenBltTime = timeGetTime();
                mLastDrawTick = aPreScreenBltTime;

                Redraw(NULL);          

                // This is our one UpdateFTimeAcc if we are vsynched
                UpdateFTimeAcc();

                DWORD aEndTime = timeGetTime();

                mScreenBltTime = aEndTime - aPreScreenBltTime;

#ifdef _DEBUG
                /*if (mFPSTime >= 5000) // Show FPS about every 5 seconds
                {
                        ulong aTickNow = GetTickCount();

                        OutputDebugString(StrFormat(_S("Theoretical FPS: %d\r\n"), (int) (mFPSCount * 1000 / mFPSTime)).c_str());
                        OutputDebugString(StrFormat(_S("Actual      FPS: %d\r\n"), (mFPSFlipCount * 1000) / max((aTickNow - mFPSStartTick), 1)).c_str());
                        OutputDebugString(StrFormat(_S("Dirty Rate     : %d\r\n"), (mFPSDirtyCount * 1000) / max((aTickNow - mFPSStartTick), 1)).c_str());

                        mFPSTime = 0;
                        mFPSCount = 0;
                        mFPSFlipCount = 0;
                        mFPSStartTick = aTickNow;
                        mFPSDirtyCount = 0;
                }*/

#endif

                if ((mLoadingThreadStarted) && (!mLoadingThreadCompleted))
                {
                        int aTotalTime = aEndTime - aStartTime;

                        mNextDrawTick += 35 + max(aTotalTime, 15);

                        if ((int) (aEndTime - mNextDrawTick) >= 0)                     
                                mNextDrawTick = aEndTime;                      

                        /*char aStr[256];
                        sprintf(aStr, "Next Draw Time: %d\r\n", mNextDrawTick);
                        OutputDebugString(aStr);*/

                }
                else
                        mNextDrawTick = aEndTime;

                mHasPendingDraw = false;               
                mCustomCursorDirty = false;

                return true;
        }
        else
        {              
                mHasPendingDraw = false;
                mLastDrawWasEmpty = true;              
                return false;
        }
}

void SexyAppBase::LogScreenSaverError(const std::string &theError)
{
        static bool firstTime = true;
        char aBuf[512];

        const char *aFlag = firstTime?"w":"a+";
        firstTime = false;

        FILE *aFile = fopen("ScrError.txt",aFlag);
        if (aFile != NULL)
        {
                fprintf(aFile,"%s %s %u\n",theError.c_str(),_strtime(aBuf),GetTickCount());
                fclose(aFile);
        }
}

void SexyAppBase::BeginPopup()
{
        if (!mIsPhysWindowed)
        {
                if (mDDInterface && mDDInterface->mDD)
                {
                        mDDInterface->mDD->FlipToGDISurface();
                        mNoDefer = true;
                }
        }
}

void SexyAppBase::EndPopup()
{
        if (!mIsPhysWindowed)
                mNoDefer = false;

        ClearUpdateBacklog();
        ClearKeysDown();

        if (mWidgetManager->mDownButtons)
        {
                mWidgetManager->DoMouseUps();
                ReleaseCapture();
        }
}

int SexyAppBase::MsgBox(const std::string& theText, const std::string& theTitle, int theFlags)
{
//      if (mDDInterface && mDDInterface->mDD)
//              mDDInterface->mDD->FlipToGDISurface();
        if (IsScreenSaver())
        {
                LogScreenSaverError(theText);
                return IDOK;
        }

        BeginPopup();
        int aResult = MessageBoxA(mHWnd, theText.c_str(), theTitle.c_str(), theFlags);
        EndPopup();

        return aResult;
}

int SexyAppBase::MsgBox(const std::wstring& theText, const std::wstring& theTitle, int theFlags)
{
//      if (mDDInterface && mDDInterface->mDD)
//              mDDInterface->mDD->FlipToGDISurface();
        if (IsScreenSaver())
        {
                LogScreenSaverError(WStringToString(theText));
                return IDOK;
        }

        BeginPopup();
        int aResult = MessageBoxW(mHWnd, theText.c_str(), theTitle.c_str(), theFlags);
        EndPopup();

        return aResult;
}

void SexyAppBase::Popup(const std::string& theString)
{
        if (IsScreenSaver())
        {
                LogScreenSaverError(theString);
                return;
        }

        BeginPopup();
        if (!mShutdown)
                ::MessageBoxA(mHWnd, theString.c_str(), SexyStringToString(GetString("FATAL_ERROR", _S("FATAL ERROR"))).c_str(), MB_APPLMODAL | MB_ICONSTOP);
        EndPopup();
}

void SexyAppBase::Popup(const std::wstring& theString)
{
        if (IsScreenSaver())
        {
                LogScreenSaverError(WStringToString(theString));
                return;
        }

        BeginPopup();
        if (!mShutdown)
                ::MessageBoxW(mHWnd, theString.c_str(), SexyStringToWString(GetString("FATAL_ERROR", _S("FATAL ERROR"))).c_str(), MB_APPLMODAL | MB_ICONSTOP);
        EndPopup();
}

void SexyAppBase::SafeDeleteWidget(Widget* theWidget)
{
        WidgetSafeDeleteInfo aWidgetSafeDeleteInfo;
        aWidgetSafeDeleteInfo.mUpdateAppDepth = mUpdateAppDepth;
        aWidgetSafeDeleteInfo.mWidget = theWidget;
        mSafeDeleteList.push_back(aWidgetSafeDeleteInfo);
}

BOOL CALLBACK EnumCloseThing2(HWND hwnd, LPARAM lParam)
{
        //CloseWindow(hwnd);
        char aClassName[256];
        if (GetClassNameA(hwnd, aClassName, 256) != 0)
        {
                if (strcmp(aClassName, "Internet Explorer_Server") == 0)
                {
                        DestroyWindow(hwnd);                   
                }
                else
                {
                        EnumChildWindows(hwnd, EnumCloseThing2, lParam);
                }
        }

        return TRUE;
}

BOOL CALLBACK EnumCloseThing(HWND hwnd, LPARAM lParam)
{
        //CloseWindow(hwnd);
        char aClassName[256];
        if (GetClassNameA(hwnd, aClassName, 256) != 0)
        {
                if (strcmp(aClassName, "AmWBC_WClass") == 0)
                {
                        EnumChildWindows(hwnd, EnumCloseThing2, lParam);
                }
        }

        return TRUE;
}

static INT_PTR CALLBACK MarkerListDialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
        switch (msg)
        {
                case WM_INITDIALOG:
                {
                        HWND aListBox = GetDlgItem(hwnd,100);

                        DWORD       dwExtent = 0;
                        HDC         hDCListBox;
                        HFONT       hFontOld, hFontNew;
                        TEXTMETRIC  tm;
                        RECT aRect;
                        SIZE aSize;

                        hDCListBox = GetDC(aListBox);
                        hFontNew = (HFONT)SendMessage(aListBox, WM_GETFONT, NULL, NULL);
                        hFontOld = (HFONT)SelectObject(hDCListBox, hFontNew);
                        GetTextMetrics(hDCListBox, (LPTEXTMETRIC)&tm);
                        GetClientRect(hwnd, &aRect);
                        MoveWindow(aListBox,10,10,aRect.right-aRect.left-20,aRect.bottom-aRect.top-20,FALSE);
                        for (SexyAppBase::DemoMarkerList::iterator anItr = gSexyAppBase->mDemoMarkerList.begin(); anItr != gSexyAppBase->mDemoMarkerList.end(); ++anItr)
                        {
                                if (anItr->second <= gSexyAppBase->mUpdateCount)
                                        continue;

                                int aTotalSeconds = (gSexyAppBase->mDemoLength - anItr->second)*gSexyAppBase->mFrameTime/1000;
                                int aSeconds = aTotalSeconds%60;
                                int aMinutes = (aTotalSeconds/60)%60;
                                int anHours = (aTotalSeconds/3600);

                                SexyString aStr = StrFormat(_S("%s (%02d:%02d:%02d)"), anItr->first.c_str(),anHours,aMinutes,aSeconds);                        
                                GetTextExtentPoint32(hDCListBox, aStr.c_str(), aStr.length(), &aSize);
                                dwExtent = max (aSize.cx + tm.tmAveCharWidth, (int)dwExtent);
                                SendMessage(aListBox, LB_SETHORIZONTALEXTENT, dwExtent, 0);
                                LRESULT anIndex = SendMessage(aListBox, LB_ADDSTRING, 0, (LPARAM)aStr.c_str());
                                SendMessage(aListBox, LB_SETITEMDATA, anIndex, anItr->second);
                        }
       
                        SelectObject(hDCListBox, hFontOld);
                        ReleaseDC(aListBox, hDCListBox);

                        return TRUE;
                }

                case WM_CLOSE:
                        EndDialog(hwnd,0);
                        return TRUE;

                case WM_COMMAND:
                        if (HIWORD(wParam)==LBN_DBLCLK)
                        {
                                HWND aListBox = GetDlgItem(hwnd,100);

                                int anIndex = SendMessage(aListBox,LB_GETCURSEL,0,0);
                                if (anIndex >= 0)
                                {
                                        int anUpdateTime = SendMessage(aListBox,LB_GETITEMDATA,anIndex,0);
                                        if (anUpdateTime > gSexyAppBase->mUpdateCount)
                                        {
                                                gSexyAppBase->mFastForwardToUpdateNum = anUpdateTime;
                                                EndDialog(hwnd,0);
                                        }
                                }
                                return TRUE;
                        }
                        break;

        }

        return FALSE;
}

static LPWORD lpdwAlign ( LPWORD lpIn)
{
    ULONG ul;

    ul = (ULONG) lpIn;
    ul +=3;
    ul >>=2;
    ul <<=2;
    return (LPWORD) ul;
}

static int ListDemoMarkers()
{
        HGLOBAL hgbl;
    LPDLGTEMPLATE lpdt;
    LPDLGITEMTEMPLATE lpdit;
    LPWORD lpw;
    LPWSTR lpwsz;
    LRESULT ret;
    int nchar;

    hgbl = GlobalAlloc(GMEM_ZEROINIT, 1024);
    if (!hgbl)
        return -1;
 
    lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
 
    // Define a dialog box.
    lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION | DS_SETFONT;
    lpdt->cdit = 1;  // number of controls
    lpdt->x  = 10;  lpdt->y  = 10;
    lpdt->cx = 200; lpdt->cy = 200;

    lpw = (LPWORD) (lpdt + 1);
    *lpw++ = 0;   // no menu
    *lpw++ = 0;   // predefined dialog box class (by default)

    lpwsz = (LPWSTR) lpw;
    nchar = MultiByteToWideChar (CP_ACP, 0, "Marker List", -1, lpwsz, 50);
    lpw   += nchar;
        *lpw++ = 8;
        lpwsz = (LPWSTR) lpw;
    nchar = MultiByteToWideChar (CP_ACP, 0, "Tahoma", -1, lpwsz, 50);
        lpw += nchar;

        // Define Listbox
    lpw = lpdwAlign (lpw); // align DLGITEMTEMPLATE on DWORD boundary
    lpdit = (LPDLGITEMTEMPLATE) lpw;
    lpdit->x  = 5; lpdit->y  = 5;
    lpdit->cx = 190; lpdit->cy = 195;
    lpdit->id = 100;  
        lpdit->style = WS_VISIBLE | WS_CHILD | WS_VSCROLL | WS_HSCROLL | LBS_NOTIFY;
        lpdit->dwExtendedStyle = WS_EX_CLIENTEDGE;
    lpw = (LPWORD) (lpdit + 1);
    *lpw++ = 0xFFFF;
    *lpw++ = 0x0083;    // listbox class
        *lpw++ = 0;                     // no window text
    *lpw++ = 0;                 // no creation data


    GlobalUnlock(hgbl);
    ret = DialogBoxIndirect(gHInstance, (LPDLGTEMPLATE) hgbl, gSexyAppBase->mHWnd, (DLGPROC) MarkerListDialogProc);
    GlobalFree(hgbl);

        gSexyAppBase->mLastTime = timeGetTime();

    return ret;
}

static INT_PTR CALLBACK JumpToTimeDialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
        switch (msg)
        {
                case WM_INITDIALOG:
                {
                        HWND anEdit = GetDlgItem(hwnd,100);
                        HKEY aGameKey;
                        std::string aKeyName = RemoveTrailingSlash("SOFTWARE\\" + gSexyAppBase->mRegKey);
                        if (RegOpenKeyExA(HKEY_CURRENT_USER, aKeyName.c_str(), 0, KEY_READ | KEY_WRITE, &aGameKey) == ERROR_SUCCESS)
                        {
                                char aBuf[1024];
                                DWORD aLength = 1000;
                                DWORD aType = REG_SZ;
                                if (RegQueryValueExA(aGameKey, "DemoJumpTime", 0, &aType, (uchar*) aBuf, &aLength) == ERROR_SUCCESS)
                                {
                                        aBuf[aLength] = 0;
                                        SetWindowTextA(anEdit,aBuf);
                                        SendMessage(anEdit,EM_SETSEL,0,-1);
                                }
                                RegCloseKey(aGameKey);
                        }
                        return TRUE;
                }
                break;

                case WM_CLOSE:
                        EndDialog(hwnd,0);
                        return TRUE;

                case WM_COMMAND:
                        if (HIWORD(wParam)==BN_CLICKED)
                        {
                                if (LOWORD(wParam)==IDOK)
                                {
                                        char aBuf[512];
                                        HWND anEdit = GetDlgItem(hwnd,100);
                                        GetWindowTextA(anEdit,aBuf,500);

                                        HKEY aGameKey;
                                        std::string aKeyName = RemoveTrailingSlash("SOFTWARE\\" + gSexyAppBase->mRegKey);
                                        if (RegOpenKeyExA(HKEY_CURRENT_USER, aKeyName.c_str(), 0, KEY_READ | KEY_WRITE, &aGameKey) == ERROR_SUCCESS)
                                        {
                                                RegSetValueExA(aGameKey, "DemoJumpTime", 0, REG_SZ, (const BYTE*)aBuf, strlen(aBuf)+1);
                                                RegCloseKey(aGameKey);
                                        }

                                        int aTime = 0;
                                        char *aPtr = strtok(aBuf,":");
                                        while (aPtr != NULL)
                                        {
                                                aTime *= 60;
                                                aTime += atoi(aPtr);
                                                aPtr = strtok(NULL,":");
                                        }
                                        aTime++;

                                        int aNumFrames = aTime*1000/gSexyAppBase->mFrameTime;
                                        gSexyAppBase->mFastForwardToUpdateNum = gSexyAppBase->mDemoLength - aNumFrames;                                


                                }
                                       
                                EndDialog(hwnd,0);
                                return TRUE;
                        }
                        break;
        }

        return FALSE;
}

static int DemoJumpToTime()
{
        HGLOBAL hgbl;
    LPDLGTEMPLATE lpdt;
    LPDLGITEMTEMPLATE lpdit;
    LPWORD lpw;
    LPWSTR lpwsz;
    LRESULT ret;
    int nchar;

    hgbl = GlobalAlloc(GMEM_ZEROINIT, 1024);
    if (!hgbl)
        return -1;
 
    lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
 
    // Define a dialog box.
    lpdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION | DS_SETFONT;
    lpdt->cdit = 3;  // number of controls
    lpdt->x  = 10;  lpdt->y  = 10;
    lpdt->cx = 200; lpdt->cy = 50;

    lpw = (LPWORD) (lpdt + 1);
    *lpw++ = 0;   // no menu
    *lpw++ = 0;   // predefined dialog box class (by default)

    lpwsz = (LPWSTR) lpw;
    nchar = MultiByteToWideChar (CP_ACP, 0, "Jump To Time", -1, lpwsz, 50);
    lpw   += nchar;
        *lpw++ = 8;
        lpwsz = (LPWSTR) lpw;
    nchar = MultiByteToWideChar (CP_ACP, 0, "Tahoma", -1, lpwsz, 50);
        lpw += nchar;

        // Define Edit
    lpw = lpdwAlign (lpw); // align DLGITEMTEMPLATE on DWORD boundary
    lpdit = (LPDLGITEMTEMPLATE) lpw;
    lpdit->x  = 5; lpdit->y  = 5;
    lpdit->cx = 190; lpdit->cy = 15;
    lpdit->id = 100;  
        lpdit->style = WS_VISIBLE | WS_CHILD;
        lpdit->dwExtendedStyle = WS_EX_CLIENTEDGE;
    lpw = (LPWORD) (lpdit + 1);
    *lpw++ = 0xFFFF;
    *lpw++ = 0x0081;    // edit class
        *lpw++ = 0;                     // no window text
    *lpw++ = 0;                 // no creation data

        // Define Button
    lpw = lpdwAlign (lpw); // align DLGITEMTEMPLATE on DWORD boundary
    lpdit = (LPDLGITEMTEMPLATE) lpw;
    lpdit->x  = 30; lpdit->y  = 25;
    lpdit->cx = 60; lpdit->cy = 15;
    lpdit->id = IDOK;  
        lpdit->style = WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON;
//      lpdit->dwExtendedStyle = WS_EX_CLIENTEDGE;
    lpw = (LPWORD) (lpdit + 1);
    *lpw++ = 0xFFFF;
    *lpw++ = 0x0080;    // button class
        lpwsz = (LPWSTR) lpw;
        nchar = MultiByteToWideChar (CP_ACP, 0, "Ok", -1, lpwsz, 50);
    lpw   += nchar;
    lpw = lpdwAlign (lpw); // align creation data on DWORD boundary
    *lpw++ = 0;                 // no creation data


        // Define Button
    lpw = lpdwAlign (lpw); // align DLGITEMTEMPLATE on DWORD boundary
    lpdit = (LPDLGITEMTEMPLATE) lpw;
    lpdit->x  = 100; lpdit->y  = 25;
    lpdit->cx = 60; lpdit->cy = 15;
    lpdit->id = IDCANCEL;  
        lpdit->style = WS_VISIBLE | WS_CHILD;
//      lpdit->dwExtendedStyle = WS_EX_CLIENTEDGE;
    lpw = (LPWORD) (lpdit + 1);
    *lpw++ = 0xFFFF;
    *lpw++ = 0x0080;    // button class
        lpwsz = (LPWSTR) lpw;
        nchar = MultiByteToWideChar (CP_ACP, 0, "Cancel", -1, lpwsz, 50);
    lpw   += nchar;
    lpw = lpdwAlign (lpw); // align creation data on DWORD boundary
    *lpw++ = 0;                 // no creation data




    GlobalUnlock(hgbl);
    ret = DialogBoxIndirect(gHInstance, (LPDLGTEMPLATE) hgbl, gSexyAppBase->mHWnd, (DLGPROC) JumpToTimeDialogProc);
    GlobalFree(hgbl);

        gSexyAppBase->mLastTime = timeGetTime();

    return ret;
}

static void ToggleDemoSoundVolume()
{
        if (gSexyAppBase->GetMusicVolume() == 0.0)
                gSexyAppBase->SetMusicVolume(gSexyAppBase->mDemoMusicVolume);
        else
        {
                gSexyAppBase->mDemoMusicVolume = gSexyAppBase->mMusicVolume;
                gSexyAppBase->SetMusicVolume(0.0);
        }

        if (gSexyAppBase->GetSfxVolume() == 0.0)
                gSexyAppBase->SetSfxVolume(gSexyAppBase->mDemoSfxVolume);
        else
        {
                gSexyAppBase->mDemoSfxVolume = gSexyAppBase->mSfxVolume;
                gSexyAppBase->SetSfxVolume(0.0);
        }
}

static DWORD gPowerSaveTick = 0;
static bool ScreenSaverWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT &theResult)
{
        static bool gCreated = false;
        static int gMouseMoveCount = 0;
        static int gLastMouseX = 0, gLastMouseY = 0;
        static bool gClosed = false;
        typedef BOOL (WINAPI *VERIFYPWDPROC)(HWND);
        static VERIFYPWDPROC aPasswordFunc = NULL;
        HMODULE aPasswordLib = NULL;

        if (gClosed)
                return false;

        switch (uMsg)
        {
                case WM_CREATE:
                {
                        if (gCreated)
                                return false;

                        gCreated = true;
                        POINT aMousePoint;
                        GetCursorPos(&aMousePoint);
                        gLastMouseX = aMousePoint.x;
                        gLastMouseY = aMousePoint.y;

                        // Password checking stuff for 95/98/ME
                        OSVERSIONINFO aVersion;
                        aVersion.dwOSVersionInfoSize = sizeof(aVersion);
                        GetVersionEx(&aVersion);
                        if (aVersion.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
                        {
                                HKEY hKey;
                                if (RegOpenKey(HKEY_CURRENT_USER, REGSTR_PATH_SCREENSAVE ,&hKey) == ERROR_SUCCESS)
                                {
                                        DWORD aCheckPwd = 0;
                                        DWORD aSize = sizeof(DWORD);
                                        DWORD aType;
                                        LONG aResult = RegQueryValueEx(hKey, REGSTR_VALUE_USESCRPASSWORD, NULL, &aType, (PBYTE) &aCheckPwd, &aSize);
                                        if (aResult==ERROR_SUCCESS && aCheckPwd)
                                        {
                                                aPasswordLib = LoadLibrary(TEXT("PASSWORD.CPL"));
                                                if (aPasswordLib)
                                                {
                                                        aPasswordFunc = (VERIFYPWDPROC)GetProcAddress(aPasswordLib, "VerifyScreenSavePwd");
                                                        // prevents user from ctrl-alt-deleting the screensaver etc to avoid typing in a password
                                                        int aPrev;
                                                        SystemParametersInfo(SPI_SCREENSAVERRUNNING, TRUE, &aPrev, 0);
                                                }

                                        }
                                        RegCloseKey(hKey);
                                }
                        }
                        return false;
                }
                break;

                case WM_SYSCOMMAND:
                {
                        switch (wParam)
                        {
                                case SC_CLOSE:
                                case SC_SCREENSAVE:
                                case SC_NEXTWINDOW:
                                case SC_PREVWINDOW:
                                        theResult = FALSE;
                                        return true;

                                default:
                                        return false;
                        }
                }
                break;

                case WM_MOUSEMOVE:
                {
                        int aMouseX = LOWORD(lParam);
                        int aMouseY = HIWORD(lParam);
//                      SEXY_TRACE(StrFormat("SCR MouseMove: %d %d",aMouseX,aMouseY).c_str());
                        if (aMouseX!=gLastMouseX || aMouseY!=gLastMouseY)
                        {
                                gLastMouseX = aMouseX;
                                gLastMouseY = aMouseY;
                                gMouseMoveCount++;
                        }

                        if (gMouseMoveCount < 4)
                        {
                                theResult = 0;
                                return true;
                        }
                }
                break;

                case WM_NCACTIVATE:
                case WM_ACTIVATE:
                case WM_ACTIVATEAPP:
                {
                        if (wParam != FALSE)
                                return false;
                }
                break;

                case WM_CLOSE:
                case WM_LBUTTONDOWN:
                case WM_RBUTTONDOWN:
                case WM_MBUTTONDOWN:
                case WM_KEYDOWN:
                case WM_SYSKEYDOWN:
                        break;

                default:
                        return false;
        }

        if (gSexyAppBase!=NULL && gSexyAppBase->mHWnd!=hWnd) // wrong window
                return false;

        if (GetTickCount()-gPowerSaveTick < 1000) // powersave just went on so ignore certain messages that seem to come on certain os's at that time
        {
                switch (uMsg)
                {
                        case WM_MOUSEMOVE:
                        case WM_NCACTIVATE:
                        case WM_ACTIVATE:
                        case WM_ACTIVATEAPP:
                        case WM_CLOSE:
                                return false;
                }
        }

        if (aPasswordFunc && gSexyAppBase!=NULL && gSexyAppBase->mInitialized) // need to verify password before closing
        {
                if (gSexyAppBase!=NULL && gSexyAppBase->mDDInterface!=NULL && gSexyAppBase->mDDInterface->mDD!=NULL)
                {
                        gSexyAppBase->mDDInterface->mDD->FlipToGDISurface();    // so we can see the password dialog
                        gSexyAppBase->mNoDefer = true;                                                  // so the app doesn't draw over the password dialog
                }
           
                gClosed = true; // prevent this function from doing anything while in the password dialog
                BOOL aPasswordResult = aPasswordFunc(hWnd);
                gClosed = false; // let this functino work again

                if (gSexyAppBase!=NULL)
                {
                        gSexyAppBase->mNoDefer = false;
                        gSexyAppBase->ClearUpdateBacklog();
                }

                if (!aPasswordResult) // bad password
                {
                        // Get new mouse coordinate
                        POINT aMousePoint;
                        GetCursorPos(&aMousePoint);
                        gLastMouseX = aMousePoint.x;
                        gLastMouseY = aMousePoint.y;
                        gMouseMoveCount = 0;

                        return false;
                }


                // can turn this SPI_SCREENSAVERRUNNING off now since screensaver is about to stop
                int aPrev;
                SystemParametersInfo(SPI_SCREENSAVERRUNNING, FALSE, &aPrev, 0);

                // good password -> close and unload dll
                FreeLibrary(aPasswordLib);
                aPasswordLib = NULL;
                aPasswordFunc = NULL;
        }
       
        // Screen saver should shutdown
        gClosed = true;
        PostMessage(hWnd, WM_CLOSE, 0, 0);

/*      const char *str = "";
        switch (uMsg)
        {
                case WM_CREATE: str="WM_CREATE"; break;
                case WM_SYSCOMMAND: str="WM_SYSCOMMAND"; break;
                case WM_MOUSEMOVE: str="WM_MOUSEMOVE"; break;
                case WM_NCACTIVATE: str="WM_NCACTIVATE"; break;
                case WM_ACTIVATE: str="WM_ACTIVATE"; break;
                case WM_ACTIVATEAPP: str="WM_ACTIVATEAPP"; break;
                case WM_CLOSE: str="WM_CLOSE"; break;
                case WM_LBUTTONDOWN: str="WM_LBUTTONDOWN"; break;
                case WM_RBUTTONDOWN: str="WM_RBUTTONDOWN"; break;
                case WM_MBUTTONDOWN: str="WM_MBUTTONDOWN"; break;
                case WM_KEYDOWN: str="WM_KEYDOWN"; break;
                case WM_SYSKEYDOWN: str="WM_SYSKEYDOWN"; break;
        }

        SEXY_TRACE(StrFormat("Scr shutdown: %s",str).c_str());*/

        return false;
}

LRESULT CALLBACK SexyAppBase::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
        if (gSexyAppBase!=NULL && gSexyAppBase->IsScreenSaver())
        {
                LRESULT aResult;
                if (ScreenSaverWindowProc(hWnd,uMsg,wParam,lParam,aResult))
                        return aResult;
        }

        SexyAppBase* aSexyApp = (SexyAppBase*) GetWindowLong(hWnd, GWL_USERDATA);      
        switch (uMsg)
        {              
//  TODO: switch to killfocus/setfocus?
//      case WM_KILLFOCUS:
//      case WM_SETFOCUS:
//      if ((aSexyApp != NULL) && (!aSexyApp->mPlayingDemoBuffer))
//      {
//              if (hWnd == aSexyApp->mHWnd)
//                      aSexyApp->mActive = uMsg==WM_SETFOCUS;
//      }
//      //Fallthrough

        case WM_ACTIVATEAPP:
                if ((aSexyApp != NULL) && (!aSexyApp->mPlayingDemoBuffer))
                {
                        if (hWnd == aSexyApp->mHWnd)
                        {
                                aSexyApp->mActive = wParam != 0;
                        }
                }
                //Fallthrough  

        case WM_SIZE:
        case WM_MOVE:
        case WM_TIMER:
        case WM_LBUTTONDOWN:           
        case WM_RBUTTONDOWN:
        case WM_MBUTTONDOWN:
        case WM_LBUTTONDBLCLK:
        case WM_RBUTTONDBLCLK:
        case WM_LBUTTONUP:
        case WM_RBUTTONUP:             
        case WM_MBUTTONUP:             
        case WM_MOUSEMOVE:                     
        case WM_KEYDOWN:
        case WM_SYSKEYDOWN:
        case WM_KEYUP:
        case WM_SYSKEYUP:
        case WM_CHAR:  
        case WM_CLOSE:
        case WM_MOUSEWHEEL:
        case WM_DISPLAYCHANGE:
        case WM_SYSCOLORCHANGE:
                {
/*                      if (aSexyApp!=NULL && aSexyApp->mProcessInTimer && !aSexyApp->mShutdown && aSexyApp->mRunning)
                        {
                                if (uMsg==WM_TIMER && wParam==101)
                                {
                                        for (int i=0; i<5; i++)
                                        {
                                                if (GetQueueStatus(QS_INPUT | QS_PAINT))
                                                        break;

                                                if (!aSexyApp->Process(false))
                                                        break;
                                        }
                                }
                                       
                                break;
                        }*/


                        /*if ((aSexyApp != NULL) && (aSexyApp->mNoDefer))
                        {
                                // Check to see if we should be windowed
                                WINDOWPLACEMENT aWindowPlacement;
                                aWindowPlacement.length = sizeof(aWindowPlacement);
                                if (GetWindowPlacement(aSexyApp->mHWnd, &aWindowPlacement))
                                {
                                        if (aWindowPlacement.showCmd == SW_SHOWMINIMIZED)
                                        {
                                                aSexyApp->Redraw(NULL);
                                        }
                                }
                        }*/


                        if ((aSexyApp != NULL) && (!aSexyApp->mNoDefer))
                        {
                                bool keyDown = (uMsg==WM_KEYDOWN) || (uMsg==WM_SYSKEYDOWN);

                                if ((keyDown) || (uMsg==WM_KEYUP) || (uMsg == WM_SYSKEYUP))
                                {      
                                        if (wParam == VK_CONTROL)
                                                aSexyApp->mCtrlDown = keyDown;
                                        if (wParam == VK_MENU)
                                                aSexyApp->mAltDown = keyDown;
                                }

                                if ((keyDown) && (aSexyApp->DebugKeyDownAsync(wParam, aSexyApp->mCtrlDown, aSexyApp->mAltDown)))
                                        return 0;
                               
                                if (aSexyApp->mPlayingDemoBuffer)
                                {
                                        if (uMsg==WM_CHAR)
                                        {
                                                switch (wParam)
                                                {
                                                case '+':
                                                        aSexyApp->mUpdateMultiplier *= 1.5;
                                                        break;
                                                case '-':
                                                        aSexyApp->mUpdateMultiplier /= 1.5;
                                                        break;
                                                case '=':
                                                        aSexyApp->mUpdateMultiplier = 1;
                                                        break;
                                                case 'p':
                                                case 'P':
                                                        aSexyApp->mPaused = !aSexyApp->mPaused;
                                                        aSexyApp->mLastTimeCheck = timeGetTime();
                                                        aSexyApp->mUpdateFTimeAcc = 0.0;
                                                        break;

                                                case 'n':
                                                case 'N':
                                                        aSexyApp->mFastForwardToUpdateNum = aSexyApp->mUpdateCount+1;
                                                        aSexyApp->mFastForwardStep = true;
                                                        break;

                                                case 'm':
                                                case 'M':
                                                        aSexyApp->mFastForwardToMarker = true;
                                                        break;

                                                case 'l':
                                                case 'L':
                                                        ListDemoMarkers();
                                                        break;

                                                case 'j':
                                                case 'J':
                                                        DemoJumpToTime();
                                                        break;

                                                case 's':
                                                case 'S':
                                                        ToggleDemoSoundVolume();
                                                        break;

                                                case '4':
                                                        // Fast foward to 120 seconds before it ends
                                                        aSexyApp->mFastForwardToUpdateNum = aSexyApp->mDemoLength - (120000 / aSexyApp->mFrameTime);
                                                        break;
                                                case '5':
                                                        // Fast foward to 90 seconds before it ends
                                                        aSexyApp->mFastForwardToUpdateNum = aSexyApp->mDemoLength - (90000 / aSexyApp->mFrameTime);
                                                        break;
                                                case '6':
                                                        // Fast foward to 60 seconds before it ends
                                                        aSexyApp->mFastForwardToUpdateNum = aSexyApp->mDemoLength - (60000 / aSexyApp->mFrameTime);
                                                        break;
                                                case '7':
                                                        // Fast foward to 30 seconds before it ends
                                                        aSexyApp->mFastForwardToUpdateNum = aSexyApp->mDemoLength - (30000 / aSexyApp->mFrameTime);
                                                        break;
                                                case '8':
                                                        // Fast foward to 10 seconds before it ends
                                                        aSexyApp->mFastForwardToUpdateNum = aSexyApp->mDemoLength - (10000 / aSexyApp->mFrameTime);
                                                        break;
                                                case '9':
                                                        // Fast foward to 5 seconds before it ends
                                                        aSexyApp->mFastForwardToUpdateNum = aSexyApp->mDemoLength - (5000 / aSexyApp->mFrameTime);
                                                        break;
                                                case '0':                                              
                                                        // Fast forward to the end
                                                        aSexyApp->mFastForwardToUpdateNum = aSexyApp->mDemoLength;
                                                        break;

                                                }
                                        }
                                        else if (uMsg==WM_KEYDOWN)
                                                aSexyApp->DebugKeyDown(wParam);
                                }

                                bool pushMessage = true;

                                if (aSexyApp->mDeferredMessages.size() > 0)
                                {
                                        // Don't add any more messages after WM_CLOSE
                                        MSG* aMsg = &aSexyApp->mDeferredMessages.back();

                                        if (aMsg->message == WM_CLOSE)
                                                pushMessage = false;
                                        if ((uMsg == WM_TIMER) && (uMsg == aMsg->message))
                                                pushMessage = false; // Don't need more timer messages


                                        if (pushMessage && (uMsg==WM_SYSCOLORCHANGE || uMsg==WM_DISPLAYCHANGE)) // kill duplicate SysColorChange() events.
                                        {
                                                WindowsMessageList::iterator aMsgListItr = aSexyApp->mDeferredMessages.begin();
                                                while (pushMessage && aMsgListItr != aSexyApp->mDeferredMessages.end())
                                                {
                                                        MSG& aMsg = *aMsgListItr;

                                                        if (aMsg.message == WM_SYSCOLORCHANGE || aMsg.message == WM_DISPLAYCHANGE)
                                                                pushMessage = false;

                                                        ++aMsgListItr;
                                                }
                                        }
                                }

                                if (pushMessage)
                                {
                                        MSG msg;
                                        msg.hwnd = hWnd;
                                        msg.message = uMsg;
                                        msg.lParam = lParam;
                                        msg.wParam = wParam;   
                                       
                                        aSexyApp->mDeferredMessages.push_back(msg);
                                }

                                if (uMsg == WM_SIZE)
                                {
                                        aSexyApp->mPhysMinimized = wParam == SIZE_MINIMIZED;
                                }
                                else if (uMsg == WM_SYSKEYDOWN)
                                {
                                        if (wParam != VK_F4)
                                                return 0;              
                                }
                                else if (uMsg == WM_CLOSE)
                                {
                                        /*char aStr[256];
                                        sprintf(aStr, "CLOSED HWND: %d\r\n", hWnd);
                                        OutputDebugString(aStr);*/

                                       
                                        aSexyApp->CloseRequestAsync();
                                        return 0;
                                }
                        }                      
                }
                break;

        case WM_ENABLE:
                if (aSexyApp != NULL)
                {
                        aSexyApp->mIsDisabled = wParam == 0;
                }
                break;

        case WM_QUERYOPEN:
                if ((aSexyApp != NULL) && (!aSexyApp->AppCanRestore()))
                        return 0;
                break;

        case WM_SYSCHAR:
                if ((aSexyApp != NULL) && (aSexyApp->IsAltKeyUsed(wParam)))
                        return 0;
                break;

        case WM_NCLBUTTONDOWN:
                if (aSexyApp!=NULL)
                {
//                      aSexyApp->mProcessInTimer = true;
                        LRESULT aResult = DefWindowProc(hWnd, uMsg, wParam, lParam);
//                      aSexyApp->mProcessInTimer = false;
                        aSexyApp->ClearUpdateBacklog();
                        return aResult;
                }
                break;


        case WM_SYSCOMMAND:
                if (wParam==SC_MONITORPOWER)
                {
                        gPowerSaveTick = GetTickCount();
                        if (aSexyApp!=NULL && (!aSexyApp->mAllowMonitorPowersave || !aSexyApp->mLoaded))
                                return FALSE;
                }
                if (wParam==SC_SCREENSAVE && aSexyApp!=NULL && (!aSexyApp->mLoaded || !aSexyApp->mIsPhysWindowed))
                        return FALSE;

                break;

/*      case WM_DISPLAYCHANGE:
                SEXY_TRACE("WM_DISPLAYCHANGE 1");
                if (aSexyApp!=NULL && aSexyApp->mIsWindowed && aSexyApp->mDDInterface!=NULL && aSexyApp->mHWnd==hWnd && aSexyApp->mLoaded)
                {
                        SEXY_TRACE("WM_DISPLAYCHANGE 2");
                        aSexyApp->mDDInterface->Init(aSexyApp->mHWnd,aSexyApp->mIsWindowed);
                        aSexyApp->mWidgetManager->mImage = aSexyApp->mDDInterface->GetScreenImage();
                        aSexyApp->mWidgetManager->MarkAllDirty();
                }
                break;*/


        case WM_DESTROY:
                {
                        char aStr[256];
                        sprintf(aStr, "DESTROYED HWND: %d\r\n", hWnd);
                        OutputDebugStringA(aStr);
                }
                break; 
        case WM_SETCURSOR:
                if (!aSexyApp->mSEHOccured)
                        aSexyApp->EnforceCursor();
                return TRUE;           
        case WM_ERASEBKGND:
                return TRUE;           
        case WM_ENDSESSION:
                aSexyApp->Shutdown();
                break;
        case WM_PAINT:
                if ((aSexyApp->mInitialized) && (!gInAssert) && (!aSexyApp->mSEHOccured))
                {
                        RECT aClientRect;
                        GetClientRect(hWnd, &aClientRect);                     

                        PAINTSTRUCT ps;
                        BeginPaint(hWnd, &ps);                 
                       
                        if (aSexyApp->mRunning)
                                aSexyApp->Redraw(NULL);

                        EndPaint(hWnd, &ps);

                        return 0;
                }
                break;
        }

        if ((aSexyApp != NULL) && (uMsg == aSexyApp->mNotifyGameMessage) && (hWnd == aSexyApp->mHWnd))
        {
                // Oh, we are trying to open another instance of ourselves.
                // Bring up the original window instead
                aSexyApp->HandleNotifyGameMessage(wParam,lParam);              
                return 0;
        }

        if (gSexyAppBase->mIsWideWindow)
                return DefWindowProcW(hWnd, uMsg, wParam, lParam);
        else
                return DefWindowProcA(hWnd, uMsg, wParam, lParam);
}

void SexyAppBase::HandleNotifyGameMessage(int theType, int theParam)
{
        if (theType==0) // bring to front message
        {
                WINDOWPLACEMENT aWindowPlacement;
                aWindowPlacement.length = sizeof(WINDOWPLACEMENT);
                GetWindowPlacement(mHWnd, &aWindowPlacement);          

                if (aWindowPlacement.showCmd == SW_SHOWMINIMIZED)
                        ShowWindow(mHWnd, SW_RESTORE);

                ::SetForegroundWindow(mHWnd);
        }
}

void SexyAppBase::RehupFocus()
{
        bool wantHasFocus = mActive && !mMinimized;

        if (wantHasFocus != mHasFocus)
        {                                      
                mHasFocus = wantHasFocus;

                if (mHasFocus)
                {
                        if (mMuteOnLostFocus)
                                Unmute(true);
       
                        mWidgetManager->GotFocus();
                        GotFocus();
                }
                else
                {
                        if (mMuteOnLostFocus)
                                Mute(true);
       
                        mWidgetManager->LostFocus();
                        LostFocus();

                        ReleaseCapture();
                        mWidgetManager->DoMouseUps();
                }
        }
}

void SexyAppBase::ClearKeysDown()
{
        if (mWidgetManager != NULL) // fix stuck alt-key problem
        {
                for (int aKeyNum = 0; aKeyNum < 0xFF; aKeyNum++)
                        mWidgetManager->mKeyDown[aKeyNum] = false;
        }
        mCtrlDown = false;
        mAltDown = false;
}

void SexyAppBase::WriteDemoTimingBlock()
{
        // Demo writing functions can only be called from the main thread and after SexyAppBase::Init
        DBG_ASSERTE(GetCurrentThreadId() == mPrimaryThreadId);

        while (mUpdateCount - mLastDemoUpdateCnt > 15)
        {
                mDemoBuffer.WriteNumBits(15, 4);
                mLastDemoUpdateCnt += 15;

                mDemoBuffer.WriteNumBits(0, 1);
                mDemoBuffer.WriteNumBits(DEMO_IDLE, 5);
                mDemoCmdOrder++;
        }
       
        mDemoBuffer.WriteNumBits(mUpdateCount - mLastDemoUpdateCnt, 4);
        mLastDemoUpdateCnt = mUpdateCount;
        mDemoCmdOrder++;
}

int aNumBigMoveMessages = 0;
int aNumSmallMoveMessages = 0;
int aNumTimerMessages = 0;

bool SexyAppBase::PrepareDemoCommand(bool required)
{
        if (mDemoNeedsCommand)
        {
                mDemoCmdBitPos = mDemoBuffer.mReadBitPos;

                mLastDemoUpdateCnt += mDemoBuffer.ReadNumBits(4, false);

                mDemoIsShortCmd = mDemoBuffer.ReadNumBits(1, false) == 1;

                if (mDemoIsShortCmd)           
                        mDemoCmdNum = mDemoBuffer.ReadNumBits(1, false);
                else
                        mDemoCmdNum = mDemoBuffer.ReadNumBits(5, false);

                mDemoNeedsCommand = false;

                mDemoCmdOrder++;
        }

        DBG_ASSERTE((mUpdateCount == mLastDemoUpdateCnt) || (!required));

        return mUpdateCount == mLastDemoUpdateCnt;
}

void SexyAppBase::ProcessDemo()
{
        if (mPlayingDemoBuffer)
        {
                // At end of demo buffer?  How dare you!
                DBG_ASSERTE(!mDemoBuffer.AtEnd());

                while ((!mShutdown) && (mUpdateCount == mLastDemoUpdateCnt) && (!mDemoBuffer.AtEnd()))
                {
                        if (PrepareDemoCommand(false))
                        {
                                mDemoNeedsCommand = true;
                               
                                if (mDemoIsShortCmd)
                                {
                                        switch (mDemoCmdNum)
                                        {
                                        case 0:
                                                {
                                                        int aDeltaX = mDemoBuffer.ReadNumBits(6, true);
                                                        int aDeltaY = mDemoBuffer.ReadNumBits(6, true);
                                                        mLastDemoMouseX += aDeltaX;
                                                        mLastDemoMouseY += aDeltaY;

                                                        mWidgetManager->MouseMove(mLastDemoMouseX, mLastDemoMouseY);
                                                }
                                                break;
                                        case 1:
                                                {
                                                        bool down = mDemoBuffer.ReadNumBits(1, false) != 0;
                                                        int aBtnCount = mDemoBuffer.ReadNumBits(3, true);                                                      
               
                                                        if (down)
                                                                mWidgetManager->MouseDown(mLastDemoMouseX, mLastDemoMouseY, aBtnCount);
                                                        else
                                                                mWidgetManager->MouseUp(mLastDemoMouseX, mLastDemoMouseY, aBtnCount);
                                                }
                                                break;
                                        }
                                }
                                else
                                {                              
                                        switch (mDemoCmdNum)
                                        {
                                        case DEMO_MOUSE_POSITION:
                                                {
                                                        mLastDemoMouseX = mDemoBuffer.ReadNumBits(12, false);
                                                        mLastDemoMouseY = mDemoBuffer.ReadNumBits(12, false);

                                                        mWidgetManager->MouseMove(mLastDemoMouseX, mLastDemoMouseY);                                           
                                                }
                                                break;
                                        case DEMO_ACTIVATE_APP:
                                                {
                                                        mActive = mDemoBuffer.ReadNumBits(1, false) != 0;

                                                        RehupFocus();
                                                       
                                                        if ((mActive) && (!mIsWindowed))
                                                                mWidgetManager->MarkAllDirty();

                                                        if ((mIsOpeningURL) && (!mActive))
                                                                URLOpenSucceeded(mOpeningURL);
                                                }
                                                break;
                                        case DEMO_SIZE:
                                                {
                                                        bool isMinimized = mDemoBuffer.ReadBoolean();

                                                        if ((!mShutdown) && (isMinimized != mMinimized))
                                                        {
                                                                mMinimized = isMinimized;

                                                                // We don't want any sounds (or music) playing while its minimized
                                                                if (mMinimized)
                                                                        Mute(true);
                                                                else
                                                                {
                                                                        Unmute(true);
                                                                        mWidgetManager->MarkAllDirty();
                                                                }
                                                        }

                                                        RehupFocus();
                                                }
                                                break;
                                        case DEMO_MOUSE_WHEEL:
                                                {
                                                        int aScroll = mDemoBuffer.ReadNumBits(8, true);
                                                        mWidgetManager->MouseWheel(aScroll);
                                                }
                                                break;
                                        case DEMO_KEY_DOWN:
                                                {
                                                        KeyCode aKeyCode = (KeyCode) mDemoBuffer.ReadNumBits(8, false);
                                                        mWidgetManager->KeyDown(aKeyCode);
                                                }
                                                break;
                                        case DEMO_KEY_UP:
                                                {
                                                        KeyCode aKeyCode = (KeyCode) mDemoBuffer.ReadNumBits(8, false);
                                                        mWidgetManager->KeyUp(aKeyCode);
                                                }
                                                break;
                                        case DEMO_KEY_CHAR:
                                                {
                                                        int sizeMult = (int)mDemoBuffer.ReadNumBits(1, false) + 1; // will be 1 for single, 2 for double
                                                        SexyChar aChar = (SexyChar) mDemoBuffer.ReadNumBits(8*sizeMult, false);
                                                        mWidgetManager->KeyChar(aChar);
                                                }
                                                break;
                                        case DEMO_CLOSE:
                                                Shutdown();
                                                break;
                                        case DEMO_MOUSE_ENTER:
                                                mMouseIn = true;
                                                EnforceCursor();
                                                break;
                                        case DEMO_MOUSE_EXIT:
                                                mWidgetManager->MouseExit(mLastDemoMouseX, mLastDemoMouseY);
                                                mMouseIn = false;
                                                EnforceCursor();
                                                break;
                                        case DEMO_LOADING_COMPLETE:
                                                mDemoLoadingComplete = true;
                                                break;
                                        case DEMO_VIDEO_DATA:
                                                mIsWindowed = mDemoBuffer.ReadBoolean();
                                                mSyncRefreshRate = mDemoBuffer.ReadByte();
                                                break;
                                        case DEMO_IDLE:
                                                break;
                                        default:
                                                DBG_ASSERTE("Invalid Demo Command" == 0);
                                                break;
                                        }
                                }
                        }
                }
        }
}

void SexyAppBase::ShowMemoryUsage()
{
        DWORD aTotal = 0;
        DWORD aFree = 0;

        if (mDDInterface->mDD7 != NULL)
        {
                DDSCAPS2 aCaps;
                ZeroMemory(&aCaps,sizeof(aCaps));
                aCaps.dwCaps = DDSCAPS_VIDEOMEMORY;    
                mDDInterface->mDD7->GetAvailableVidMem(&aCaps,&aTotal,&aFree);
        }

        MemoryImageSet::iterator anItr = mMemoryImageSet.begin();
        typedef std::pair<int,int> FormatUsage;
        typedef std::map<PixelFormat,FormatUsage> FormatMap;
        FormatMap aFormatMap;
        int aTextureMemory = 0;
        while (anItr != mMemoryImageSet.end())
        {
                MemoryImage* aMemoryImage = *anItr;                            
                if (aMemoryImage->mD3DData != NULL)
                {
                        TextureData *aData = (TextureData*)aMemoryImage->mD3DData;
                        aTextureMemory += aData->mTexMemSize;

                        FormatUsage &aUsage = aFormatMap[aData->mPixelFormat];
                        aUsage.first++;
                        aUsage.second += aData->mTexMemSize;
                }

                ++anItr;
        }

        std::string aStr;

        const char *aDesc;
        if (Is3DAccelerationRecommended())
                aDesc = "Recommended";
        else if (Is3DAccelerationSupported())
                aDesc = "Supported";
        else
                aDesc = "Unsupported";

        aStr += StrFormat("3D-Mode is %s (3D is %s on this system)\r\n\r\n",Is3DAccelerated()?"On":"Off",aDesc);

        aStr += StrFormat("Num Images: %d\r\n",(int)mMemoryImageSet.size());
        aStr += StrFormat("Num Sounds: %d\r\n",mSoundManager->GetNumSounds());
        aStr += StrFormat("Video Memory: %s/%s KB\r\n", SexyStringToString(CommaSeperate((aTotal-aFree)/1024)).c_str(), SexyStringToString(CommaSeperate(aTotal/1024)).c_str());
        aStr += StrFormat("Texture Memory: %s KB\r\n",CommaSeperate(aTextureMemory/1024).c_str());

        FormatUsage aUsage = aFormatMap[PixelFormat_A8R8G8B8];
        aStr += StrFormat("A8R8G8B8: %d - %s KB\r\n",aUsage.first,SexyStringToString(CommaSeperate(aUsage.second/1024)).c_str());
        aUsage = aFormatMap[PixelFormat_A4R4G4B4];
        aStr += StrFormat("A4R4G4B4: %d - %s KB\r\n",aUsage.first,SexyStringToString(CommaSeperate(aUsage.second/1024)).c_str());
        aUsage = aFormatMap[PixelFormat_R5G6B5];
        aStr += StrFormat("R5G6B5: %d - %s KB\r\n",aUsage.first,SexyStringToString(CommaSeperate(aUsage.second/1024)).c_str());
        aUsage = aFormatMap[PixelFormat_Palette8];
        aStr += StrFormat("Palette8: %d - %s KB\r\n",aUsage.first,SexyStringToString(CommaSeperate(aUsage.second/1024)).c_str());
       
        MsgBox(aStr,"Video Stats",MB_OK);
        mLastTime = timeGetTime();
}

bool SexyAppBase::IsAltKeyUsed(WPARAM wParam)
{
        int aChar = tolower(wParam);
        switch (aChar)
        {
                case 13: // alt-enter
                case 'r':
                        return true;           
                default:
                        return false;
        }
}

bool SexyAppBase::DebugKeyDown(int theKey)
{
        if ((theKey == 'R') && (mWidgetManager->mKeyDown[KEYCODE_MENU]))
        {      
#ifndef RELEASEFINAL
                if (ReparseModValues())
                        PlaySoundA("c:\\windows\\media\\Windows XP Menu Command.wav", NULL, SND_ASYNC);                        
                else
                {
                        for (int aKeyNum = 0; aKeyNum < 0xFF; aKeyNum++) // prevent alt from getting stuck
                                mWidgetManager->mKeyDown[aKeyNum] = false;
                }
#endif
        }
        else if (theKey == VK_F3)
        {
                if(mWidgetManager->mKeyDown[KEYCODE_SHIFT])
                {
                        mShowFPS = true;
                        if (++mShowFPSMode >= Num_FPS_Types)
                                mShowFPSMode = 0;
                }
                else
                        mShowFPS = !mShowFPS;

                mWidgetManager->MarkAllDirty();

                if (mShowFPS)
                {
                        gFPSTimer.Start();
                        gFrameCount = 0;
                        gFPSDisplay = 0;
                        gForceDisplay = true;
                }
        }
        else if (theKey == VK_F8)
        {
                if(mWidgetManager->mKeyDown[KEYCODE_SHIFT])
                {
                        Set3DAcclerated(!Is3DAccelerated());

                        char aBuf[512];
                        sprintf(aBuf,"3D-Mode: %s",Is3DAccelerated()?"ON":"OFF");
                        MsgBox(aBuf,"Mode Switch",MB_OK);
                        mLastTime = timeGetTime();
                }
                else
                        ShowMemoryUsage();

                return true;
        }
        else if (theKey == VK_F10)
        {
#ifndef RELEASEFINAL
                if (mWidgetManager->mKeyDown[KEYCODE_CONTROL])
                {
                        if (mUpdateMultiplier==0.25)
                                mUpdateMultiplier = 1.0;
                        else
                                mUpdateMultiplier = 0.25;
                }
                else if(mWidgetManager->mKeyDown[KEYCODE_SHIFT])
                {
                        mStepMode = 0;
                        ClearUpdateBacklog();
                }
                else
                        mStepMode = 1;
#endif

                return true;
        }
        else if (theKey == VK_F11)
        {
                if (mWidgetManager->mKeyDown[KEYCODE_SHIFT])
                        DumpProgramInfo();
                else
                        TakeScreenshot();

                return true;
        }
        else if (theKey == VK_F2)
        {
                bool isPerfOn = !SexyPerf::IsPerfOn();
                if (isPerfOn)
                {
//                      MsgBox("Perf Monitoring: ON", "Perf Monitoring", MB_OK);
                        ClearUpdateBacklog();
                        SexyPerf::BeginPerf();
                }
                else
                {
                        SexyPerf::EndPerf();
                        MsgBox(SexyPerf::GetResults().c_str(), "Perf Results", MB_OK);
                        ClearUpdateBacklog();
                }
        }
        else
                return false;

        return false;
}

bool SexyAppBase::DebugKeyDownAsync(int theKey, bool ctrlDown, bool altDown)
{
        return false;
}

void SexyAppBase::CloseRequestAsync()
{
}

// Why did I defer messages?  Oh, incase a dialog comes up such as a crash
//  it won't keep crashing and stuff
bool SexyAppBase::ProcessDeferredMessages(bool singleMessage)
{
        while (mDeferredMessages.size() > 0)
        {
                MSG aMsg = mDeferredMessages.front();
                mDeferredMessages.pop_front();

                UINT uMsg = aMsg.message;
                LPARAM lParam = aMsg.lParam;
                WPARAM wParam = aMsg.wParam;
                HWND hWnd = aMsg.hwnd;

                if ((mRecordingDemoBuffer) && (!mShutdown))
                {
                        switch (uMsg)
                        {
//  TODO: switch to killfocus/setfocus?
//                      case WM_SETFOCUS:
//                      case WM_KILLFOCUS:
//                              if (hWnd == mHWnd)
//                              {                                      
//                                      WriteDemoTimingBlock();
//                                      mDemoBuffer.WriteNumBits(0, 1);
//                                      mDemoBuffer.WriteNumBits(DEMO_ACTIVATE_APP, 5);
//                                      mDemoBuffer.WriteNumBits(uMsg==WM_SETFOCUS ? 1 : 0, 1);
//                              }
//                              break;                 
       
                        case WM_ACTIVATEAPP:
                                if (hWnd == mHWnd)
                                {                                      
                                        WriteDemoTimingBlock();
                                        mDemoBuffer.WriteNumBits(0, 1);
                                        mDemoBuffer.WriteNumBits(DEMO_ACTIVATE_APP, 5);
                                        mDemoBuffer.WriteNumBits((wParam != 0) ? 1 : 0, 1);
                                }
                                break;                 

                        case WM_SIZE:
                                {
                                        bool isMinimized = wParam == SIZE_MINIMIZED;

                                        WriteDemoTimingBlock();
                                        mDemoBuffer.WriteNumBits(0, 1);
                                        mDemoBuffer.WriteNumBits(DEMO_SIZE, 5);
                                        mDemoBuffer.WriteBoolean(isMinimized);         
                                }
                                break;
                        case WM_LBUTTONDOWN:
                        case WM_RBUTTONDOWN:
                        case WM_MBUTTONDOWN:
                        case WM_LBUTTONDBLCLK:
                        case WM_RBUTTONDBLCLK:
                        case WM_LBUTTONUP:
                        case WM_RBUTTONUP:
                        case WM_MBUTTONUP:
                        case WM_MOUSEMOVE:
                                {
                                        int aCurX = (short) LOWORD(lParam);
                                        int aCurY = (short) HIWORD(lParam);

                                        int aDiffX = aCurX - mLastDemoMouseX;
                                        int aDiffY = aCurY - mLastDemoMouseY;

                                        if ((abs(aCurX - mLastDemoMouseX) < 32) && (abs(aCurY - mLastDemoMouseY) < 32))
                                        {
                                                if ((aDiffX != 0) || (aDiffY != 0))
                                                {
                                                        WriteDemoTimingBlock();
                                                        mDemoBuffer.WriteNumBits(1, 1);
                                                        mDemoBuffer.WriteNumBits(0, 1);
                                                        mDemoBuffer.WriteNumBits(aDiffX, 6);
                                                        mDemoBuffer.WriteNumBits(aDiffY, 6);
                                                }                                              
                                        }
                                        else
                                        {
                                                WriteDemoTimingBlock();
                                                mDemoBuffer.WriteNumBits(0, 1);
                                                mDemoBuffer.WriteNumBits(DEMO_MOUSE_POSITION, 5);
                                                mDemoBuffer.WriteNumBits(aCurX, 12);
                                                mDemoBuffer.WriteNumBits(aCurY, 12);
                                        }

                                        bool down = true;
                                        int aBtnNum = 0;
                                        switch (uMsg)
                                        {
                                                case WM_LBUTTONDOWN:
                                                        aBtnNum = 1;
                                                        break;
                                                case WM_RBUTTONDOWN:
                                                        aBtnNum = -1;
                                                        break;
                                                case WM_MBUTTONDOWN:
                                                        aBtnNum = 3;
                                                        break;
                                                case WM_LBUTTONDBLCLK:
                                                        aBtnNum = 2;
                                                        break;
                                                case WM_RBUTTONDBLCLK:
                                                        aBtnNum = -2;
                                                        break;
                                                case WM_LBUTTONUP:
                                                        aBtnNum = 1;
                                                        down = false;
                                                        break;
                                                case WM_RBUTTONUP:
                                                        aBtnNum = -1;
                                                        down = false;
                                                        break;
                                                case WM_MBUTTONUP:
                                                        aBtnNum = 3;
                                                        down = false;
                                                        break;
                                        }

                                        if (aBtnNum != 0)
                                        {
                                                WriteDemoTimingBlock();
                                                mDemoBuffer.WriteNumBits(1, 1);
                                                mDemoBuffer.WriteNumBits(1, 1);
                                                mDemoBuffer.WriteNumBits(down ? 1 : 0, 1);
                                                mDemoBuffer.WriteNumBits(aBtnNum, 3);
                                        }

                                        mLastDemoMouseX = aCurX;
                                        mLastDemoMouseY = aCurY;
                                }
                                break;
                        case WM_MOUSEWHEEL:
                                {
                                        int aZDelta = ((short)HIWORD(wParam)) / 120;

                                        WriteDemoTimingBlock();
                                        mDemoBuffer.WriteNumBits(0, 1);
                                        mDemoBuffer.WriteNumBits(DEMO_MOUSE_WHEEL, 5);
                                        mDemoBuffer.WriteNumBits(aZDelta, 8);
                                }
                                break;
                        case WM_KEYDOWN:
                        case WM_SYSKEYDOWN:
                                {
                                        KeyCode aKeyCode = (KeyCode) wParam;

                                        WriteDemoTimingBlock();
                                        mDemoBuffer.WriteNumBits(0, 1);
                                        mDemoBuffer.WriteNumBits(DEMO_KEY_DOWN, 5);
                                        mDemoBuffer.WriteNumBits(aKeyCode, 8);
                                }
                                break;
                        case WM_KEYUP: 
                        case WM_SYSKEYUP:
                                {
                                        KeyCode aKeyCode = (KeyCode) wParam;

                                        WriteDemoTimingBlock();
                                        mDemoBuffer.WriteNumBits(0, 1);
                                        mDemoBuffer.WriteNumBits(DEMO_KEY_UP, 5);
                                        mDemoBuffer.WriteNumBits((int) aKeyCode, 8);
                                }
                                break;
                        case WM_CHAR:
                                {
                                        SexyChar aChar = (SexyChar) wParam;

                                        WriteDemoTimingBlock();
                                        mDemoBuffer.WriteNumBits(0, 1);
                                        mDemoBuffer.WriteNumBits(DEMO_KEY_CHAR, 5);
                                        mDemoBuffer.WriteNumBits(sizeof(SexyChar) == 2, 1);
                                        mDemoBuffer.WriteNumBits(aChar, sizeof(SexyChar)*8);
                                }                              
                                break; 
                        case WM_CLOSE:
                                if ((hWnd == mHWnd) || (hWnd == mInvisHWnd))
                                {
                                        WriteDemoTimingBlock();
                                        mDemoBuffer.WriteNumBits(0, 1);
                                        mDemoBuffer.WriteNumBits(DEMO_CLOSE, 5);
                                }
                                break;
                        }

                        int aBufferSize = mDemoBuffer.GetDataLen();
                }

                if (!mPlayingDemoBuffer)
                {
                        switch (uMsg)
                        {      
//  TODO: switch to killfocus/setfocus?
//                      case WM_KILLFOCUS:
//                      case WM_SETFOCUS:
                        case WM_ACTIVATEAPP:
                                if ((hWnd == mHWnd) && (!gInAssert) && (!mSEHOccured) && (!mShutdown))
                                {
//                                      mActive = uMsg==WM_SETFOCUS;

                                        RehupFocus();
                                       
                                        if ((mActive) && (!mIsWindowed))
                                                mWidgetManager->MarkAllDirty();

                                        if ((mIsOpeningURL) && (!mActive))                     
                                                URLOpenSucceeded(mOpeningURL);
                                }
                                break; 
                        case WM_LBUTTONDOWN:           
                        case WM_RBUTTONDOWN:
                        case WM_MBUTTONDOWN:
                        case WM_LBUTTONDBLCLK:
                        case WM_RBUTTONDBLCLK:
                        case WM_LBUTTONUP:             
                        case WM_RBUTTONUP:             
                        case WM_MBUTTONUP:
                        case WM_MOUSEMOVE:             
                                if ((!gInAssert) && (!mSEHOccured))
                                {
                                        int x = (short) LOWORD(lParam);
                                        int y = (short) HIWORD(lParam);
                                        mWidgetManager->RemapMouse(x, y);

                                        mLastUserInputTick = mLastTimerTime;
                                       
                                        mWidgetManager->MouseMove(x, y);               

                                        if (!mMouseIn)
                                        {
                                                if (mRecordingDemoBuffer)
                                                {
                                                        WriteDemoTimingBlock();
                                                        mDemoBuffer.WriteNumBits(0, 1);                                                
                                                        mDemoBuffer.WriteNumBits(DEMO_MOUSE_ENTER, 5);
                                                }

                                                mMouseIn = true;
                                                EnforceCursor();
                                        }

                                        switch (uMsg)
                                        {
                                        case WM_LBUTTONDOWN:           
                                                SetCapture(hWnd);
                                                mWidgetManager->MouseDown(x, y, 1);                                            
                                                break;
                                        case WM_RBUTTONDOWN:
                                                SetCapture(hWnd);
                                                mWidgetManager->MouseDown(x, y, -1);                                           
                                                break;
                                        case WM_MBUTTONDOWN:
                                                SetCapture(hWnd);
                                                mWidgetManager->MouseDown(x, y, 3);                                            
                                                break;
                                        case WM_LBUTTONDBLCLK:
                                                SetCapture(hWnd);
                                                mWidgetManager->MouseDown(x, y, 2);                                            
                                                break;
                                        case WM_RBUTTONDBLCLK:
                                                SetCapture(hWnd);
                                                mWidgetManager->MouseDown(x, y, -2);                                           
                                                break;
                                        case WM_LBUTTONUP:             
                                                if ((mWidgetManager->mDownButtons & ~1) == 0)
                                                        ReleaseCapture();
                                                mWidgetManager->MouseUp(x, y, 1);                                              
                                                break;
                                        case WM_RBUTTONUP:             
                                                if ((mWidgetManager->mDownButtons & ~2) == 0)
                                                        ReleaseCapture();
                                                mWidgetManager->MouseUp(x, y, -1);                                                             
                                                break;
                                        case WM_MBUTTONUP:             
                                                if ((mWidgetManager->mDownButtons & ~4) == 0)
                                                        ReleaseCapture();
                                                mWidgetManager->MouseUp(x, y, 3);                                                              
                                                break;
                                        }
                                }
                                break;         
                        case WM_MOUSEWHEEL:
                                {
                                        char aZDelta = ((short)HIWORD(wParam)) / 120;
                                        mWidgetManager->MouseWheel(aZDelta);
                                }
                                break;
                        case WM_KEYDOWN:
                        case WM_SYSKEYDOWN:
                                mLastUserInputTick = mLastTimerTime;

                                if (wParam==VK_RETURN && uMsg==WM_SYSKEYDOWN && !mForceFullscreen && !mForceWindowed)
                                {
                                        SwitchScreenMode(!mIsWindowed);
                                        ClearKeysDown();
                                        break;
                                }
                                else if ((wParam == 'D') && (mWidgetManager != NULL) && (mWidgetManager->mKeyDown[KEYCODE_CONTROL]) && (mWidgetManager->mKeyDown[KEYCODE_MENU]))
                                {
                                        PlaySoundA("c:\\windows\\media\\Windows XP Menu Command.wav", NULL, SND_ASYNC);
                                        mDebugKeysEnabled = !mDebugKeysEnabled;                                        
                                }

                                if (mDebugKeysEnabled)
                                {
                                        if (DebugKeyDown(wParam))
                                                break;
                                }

                                mWidgetManager->KeyDown((KeyCode) wParam);
                                break;

                        case WM_KEYUP:
                        case WM_SYSKEYUP:
                                mLastUserInputTick = mLastTimerTime;
                                mWidgetManager->KeyUp((KeyCode) wParam);
                                break;
                        case WM_CHAR:          
                                mLastUserInputTick = mLastTimerTime;
                                mWidgetManager->KeyChar((SexyChar) wParam);            
                                break;
                        case WM_MOVE:
                                {
                                        if ((hWnd == mHWnd) && (mIsWindowed))
                                        {
                                                WINDOWPLACEMENT aWindowPlacment;
                                                aWindowPlacment.length = sizeof(aWindowPlacment);

                                                GetWindowPlacement(hWnd, &aWindowPlacment);
                                                if ((aWindowPlacment.showCmd == SW_SHOW) ||
                                                        (aWindowPlacment.showCmd == SW_SHOWNORMAL))
                                                {                                                                              
                                                        mPreferredX = aWindowPlacment.rcNormalPosition.left;
                                                        mPreferredY = aWindowPlacment.rcNormalPosition.top;
                                                }
                                        }
                                }
                                break;
                        case WM_SIZE:
                                {      
                                        bool isMinimized = wParam == SIZE_MINIMIZED;
                                       
                                        if ((hWnd == mHWnd) && (!mShutdown) && (isMinimized != mMinimized))
                                        {
                                                mMinimized = isMinimized;

                                                // We don't want any sounds (or music) playing while its minimized
                                                if (mMinimized)
                                                {
                                                        if (mMuteOnLostFocus)
                                                                Mute(true);
                                                }
                                                else
                                                {
                                                        if (mMuteOnLostFocus)
                                                                Unmute(true);

                                                        mWidgetManager->MarkAllDirty();
                                                }
                                        }

                                        RehupFocus();
                                        if (wParam==SIZE_MAXIMIZED)
                                                SwitchScreenMode(false);
                                }
                                break;
                        case WM_TIMER:
                                if ((!gInAssert) && (!mSEHOccured) && (mRunning))
                                {      
                                        DWORD aTimeNow = GetTickCount();
                                        if (aTimeNow - mLastTimerTime > 500)
                                                mLastBigDelayTime = aTimeNow;
               
                                        mLastTimerTime = aTimeNow;

                                        if ((mIsOpeningURL) &&                                         
                                                (aTimeNow - mLastBigDelayTime > 5000))
                                        {
                                                if ((aTimeNow - mOpeningURLTime > 8000) && (!mActive))
                                                {
                                                        //TODO: Have some demo message thing
                                                        URLOpenSucceeded(mOpeningURL);
                                                }
                                                else if ((aTimeNow - mOpeningURLTime > 12000) && (mActive))                                            
                                                {
                                                        URLOpenFailed(mOpeningURL);
                                                }
                                        }

                                        POINT aULCorner = {0, 0};
                                        ::ClientToScreen(hWnd, &aULCorner);

                                        POINT aBRCorner = {mDDInterface->mDisplayWidth, mDDInterface->mDisplayHeight};
                                        ::ClientToScreen(hWnd, &aBRCorner);

                                        POINT aPoint;
                                        ::GetCursorPos(&aPoint);                       
                                       
                                        HWND aWindow = ::WindowFromPoint(aPoint);
                                        bool isMouseIn = (aWindow == hWnd) &&
                                                (aPoint.x >= aULCorner.x) && (aPoint.y >= aULCorner.y) &&
                                                (aPoint.x < aBRCorner.x) && (aPoint.y < aBRCorner.y);

                                        if (mMouseIn != isMouseIn)
                                        {
                                                if ((mRecordingDemoBuffer) && (!mShutdown))
                                                {
                                                        WriteDemoTimingBlock();
                                                        mDemoBuffer.WriteNumBits(0, 1);

                                                        if (isMouseIn)
                                                                mDemoBuffer.WriteNumBits(DEMO_MOUSE_ENTER, 5);
                                                        else
                                                                mDemoBuffer.WriteNumBits(DEMO_MOUSE_EXIT, 5);
                                                }

                                                if (!isMouseIn)
                                                {
                                                        int x = aPoint.x - aULCorner.x;
                                                        int y = aPoint.y - aULCorner.y;
                                                        mWidgetManager->RemapMouse(x, y);
                                                        mWidgetManager->MouseExit(x, y);
                                                }

                                                mMouseIn = isMouseIn;
                                                EnforceCursor();
                                        }                      
                                }
                                break;

                        case WM_SYSCOLORCHANGE:
                        case WM_DISPLAYCHANGE:
                                mWidgetManager->SysColorChangedAll();
                                mWidgetManager->MarkAllDirty();
                                break;
                        }
                }

                switch (uMsg)
                {
                case WM_CLOSE:
                        if ((hWnd == mHWnd) || (hWnd == mInvisHWnd))
                        {
                                // This should short-circuit all demo calls, otherwise we will get
                                //  all sorts of weird asserts because we are changing
                                //  program flow
                                mManualShutdown = true;

                                Shutdown();
                        }
                        break;
                }

                if (singleMessage)
                        break;
        }

        return (mDeferredMessages.size() > 0);
}

void SexyAppBase::Done3dTesting()
{
}

// return file name that you want to upload
std::string     SexyAppBase::NotifyCrashHook()
{
        return "";
}

void SexyAppBase::MakeWindow()
{
        //OutputDebugString("MAKING WINDOW\r\n");

        if (mHWnd != NULL)
        {
                SetWindowLong(mHWnd, GWL_USERDATA, NULL);
                HWND anOldWindow = mHWnd;
                mHWnd = NULL;          
                DestroyWindow(anOldWindow);    
                mWidgetManager->mImage = NULL;
        }


        if ((mPlayingDemoBuffer) || (mIsWindowed && !mFullScreenWindow))
        {
                DWORD aWindowStyle = WS_CLIPCHILDREN | WS_POPUP | WS_BORDER | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
                if (mEnableMaximizeButton)
                        aWindowStyle |= WS_MAXIMIZEBOX;

                RECT aRect;
                aRect.left = 0;
                aRect.top = 0;
                aRect.right = mWidth;
                aRect.bottom = mHeight;
               
                BOOL worked = AdjustWindowRect(&aRect, aWindowStyle, FALSE);

                int aWidth = aRect.right - aRect.left;
                int aHeight = aRect.bottom - aRect.top;

                // Get the work area of the desktop to allow us to center
                RECT aDesktopRect;
                ::SystemParametersInfo(SPI_GETWORKAREA, NULL, &aDesktopRect, NULL);

                int aPlaceX = 64;
                int aPlaceY = 64;
               
                if (mPreferredX != -1)
                {
                        aPlaceX = mPreferredX;
                        aPlaceY = mPreferredY;

                        int aSpacing = 4;

                        if (aPlaceX < aDesktopRect.left + aSpacing)
                                aPlaceX = aDesktopRect.left + aSpacing;

                        if (aPlaceY < aDesktopRect.top + aSpacing)
                                aPlaceY = aDesktopRect.top + aSpacing;

                        if (aPlaceX + aWidth >= aDesktopRect.right - aSpacing)
                                aPlaceX = aDesktopRect.right - aWidth - aSpacing;
                       
                        if (aPlaceY + aHeight >= aDesktopRect.bottom - aSpacing)
                                aPlaceY = aDesktopRect.bottom - aHeight - aSpacing;
                }

                if (CheckFor98Mill())
                {
                        mHWnd = CreateWindowExA(
                                0,
                                "MainWindow",
                                SexyStringToStringFast(mTitle).c_str(),
                                aWindowStyle,
                                aPlaceX,
                                aPlaceY,
                                aWidth,
                                aHeight,
                                NULL,
                                NULL,
                                gHInstance,
                                0);
                }
                else
                {
                        mHWnd = CreateWindowEx(
                                0,
                                _S("MainWindow"),
                                mTitle.c_str(),
                                aWindowStyle,
                                aPlaceX,
                                aPlaceY,
                                aWidth,
                                aHeight,
                                NULL,
                                NULL,
                                gHInstance,
                                0);    
                }
               
                if (mPreferredX == -1)
                {                              
                        ::MoveWindow(mHWnd,
                                aDesktopRect.left + ((aDesktopRect.right - aDesktopRect.left) - aWidth)/2,
                                aDesktopRect.top + (int) (((aDesktopRect.bottom - aDesktopRect.top) - aHeight)*0.382),
                                aWidth, aHeight, FALSE);
                }

                mIsPhysWindowed = true;
        }
        else
        {
                if (CheckFor98Mill())
                {
                        mHWnd = CreateWindowExA(
                                WS_EX_TOPMOST,
                                "MainWindow",
                                SexyStringToStringFast(mTitle).c_str(),
                                WS_POPUP | WS_VISIBLE,
                                0,
                                0,
                                mWidth,
                                mHeight,
                                NULL,
                                NULL,
                                gHInstance,
                                0);
                }
                else
                {
                        mHWnd = CreateWindowEx(
                                WS_EX_TOPMOST,
                                _S("MainWindow"),
                                mTitle.c_str(),
                                WS_POPUP | WS_VISIBLE,
                                0,
                                0,
                                mWidth,
                                mHeight,
                                NULL,
                                NULL,
                                gHInstance,
                                0);
                }

                mIsPhysWindowed = false;
        }

        /*char aStr[256];
        sprintf(aStr, "HWND: %d\r\n", mHWnd);
        OutputDebugString(aStr);*/


        SetWindowLong(mHWnd, GWL_USERDATA, (LONG) this);       

        if (mDDInterface == NULL)
        {
                mDDInterface = new DDInterface(this);

                // Enable 3d setting
                bool is3D = false;
                bool is3DOptionSet = RegistryReadBoolean("Is3D", &is3D);
                if (is3DOptionSet)
                {
                        if (mAutoEnable3D)
                        {
                                mAutoEnable3D = false;
                                mTest3D = true;
                        }

                        if (is3D)
                                mTest3D = true;

                        mDDInterface->mIs3D = is3D;
                }
        }

        int aResult = InitDDInterface();

        if (mDDInterface->mD3DTester!=NULL && mDDInterface->mD3DTester->ResultsChanged())
                RegistryEraseValue(_S("Is3D"));

        if ((mIsWindowed) && (aResult == DDInterface::RESULT_INVALID_COLORDEPTH))
        {
                if (mForceWindowed)
                {
                        Popup(GetString("PLEASE_SET_COLOR_DEPTH", _S("Please set your desktop color depth to 16 bit.")));
                        DoExit(1);
                }
                else
                {
                        mForceFullscreen = true;
                        SwitchScreenMode(false);
                }
                return;
        }
        else if ((!mIsWindowed) &&
                ((aResult == DDInterface::RESULT_EXCLUSIVE_FAIL) ||
                 (aResult == DDInterface::RESULT_DISPCHANGE_FAIL)))
        {
                mForceWindowed = true;
                SwitchScreenMode(true);
        }
        else if (aResult == DDInterface::RESULT_3D_FAIL)
        {
                Set3DAcclerated(false);
                return;
        }
        else if (aResult != DDInterface::RESULT_OK)
        {
                if (Is3DAccelerated())
                {
                        Set3DAcclerated(false);
                        return;
                }
                else
                {
                        Popup(GetString("FAILED_INIT_DIRECTDRAW", _S("Failed to initialize DirectDraw: ")) + StringToSexyString(DDInterface::ResultToString(aResult) + " " + mDDInterface->mErrorString));
                        DoExit(1);
                }
        }

        bool isActive = mActive;
        mActive = GetActiveWindow() == mHWnd;

        mPhysMinimized = false;
        if (mMinimized)
        {
                if (mMuteOnLostFocus)
                        Unmute(true);

                mMinimized = false;
                isActive = mActive; // set this here so we don't call RehupFocus again.
                RehupFocus();
        }
       
        if (isActive != mActive)
                RehupFocus();

        ReInitImages();

        mWidgetManager->mImage = mDDInterface->GetScreenImage();
        mWidgetManager->MarkAllDirty();

        SetTimer(mHWnd, 100, mFrameTime, NULL);
}

void SexyAppBase::DeleteNativeImageData()
{
        MemoryImageSet::iterator anItr = mMemoryImageSet.begin();
        while (anItr != mMemoryImageSet.end())
        {
                MemoryImage* aMemoryImage = *anItr;            
                aMemoryImage->DeleteNativeData();                      
                ++anItr;
        }
}

void SexyAppBase::DeleteExtraImageData()
{
        AutoCrit anAutoCrit(mDDInterface->mCritSect);
        MemoryImageSet::iterator anItr = mMemoryImageSet.begin();
        while (anItr != mMemoryImageSet.end())
        {
                MemoryImage* aMemoryImage = *anItr;
                aMemoryImage->DeleteExtraBuffers();
                ++anItr;
        }
}

void SexyAppBase::ReInitImages()
{
        MemoryImageSet::iterator anItr = mMemoryImageSet.begin();
        while (anItr != mMemoryImageSet.end())
        {
                MemoryImage* aMemoryImage = *anItr;                            
                aMemoryImage->ReInit();
                ++anItr;
        }
}


void SexyAppBase::LoadingThreadProc()
{
}

void SexyAppBase::LoadingThreadCompleted()
{
}

void SexyAppBase::LoadingThreadProcStub(void *theArg)
{
        SexyAppBase* aSexyApp = (SexyAppBase*) theArg;
       
        aSexyApp->LoadingThreadProc();         

        char aStr[256];
        sprintf(aStr, "Resource Loading Time: %d\r\n", (GetTickCount() - aSexyApp->mTimeLoaded));
        OutputDebugStringA(aStr);

        aSexyApp->mLoadingThreadCompleted = true;
}

void SexyAppBase::StartLoadingThread()
{      
        if (!mLoadingThreadStarted)
        {
                mYieldMainThread = true;
                ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);               
                mLoadingThreadStarted = true;
                _beginthread(LoadingThreadProcStub, 0, this);
        }
}
void SexyAppBase::CursorThreadProc()
{
        ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);

        POINT aLastCursorPos = {0, 0};
        int aLastDrawCount = 0;

        while (!mShutdown)
        {
//              if (mProcessInTimer)
//                      PostMessage(mHWnd,WM_TIMER,101,0);

                POINT aCursorPos;

                if (mPlayingDemoBuffer)
                {
                        aCursorPos.x = mLastDemoMouseX;
                        aCursorPos.y = mLastDemoMouseY;
                }
                else
                {
                        ::GetCursorPos(&aCursorPos);
                        ::ScreenToClient(mHWnd, &aCursorPos);
                }

                if (aLastDrawCount != mDrawCount)
                {
                        // We did a draw so we may have committed a pending mNextCursorX/Y
                        aLastCursorPos.x = mDDInterface->mCursorX;
                        aLastCursorPos.y = mDDInterface->mCursorY;
                }

                if ((aCursorPos.x != aLastCursorPos.x) ||
                        (aCursorPos.y != aLastCursorPos.y))
                {      
                        DWORD aTimeNow = timeGetTime();
                        if (aTimeNow - mNextDrawTick > mDDInterface->mMillisecondsPerFrame + 5)
                        {
                                // Do the special drawing if we are rendering at less than full framerate                              
                                mDDInterface->SetCursorPos(aCursorPos.x, aCursorPos.y);
                                aLastCursorPos = aCursorPos;
                        }
                        else
                        {
                                // Set them up to get assigned in the next screen redraw
                                mDDInterface->mNextCursorX = aCursorPos.x;
                                mDDInterface->mNextCursorY = aCursorPos.y;
                        }                      
                }              

                Sleep(10);
        }
       
        mCursorThreadRunning = false;
}

void SexyAppBase::CursorThreadProcStub(void *theArg)
{
        CoInitialize(NULL);
        SexyAppBase* aSexyApp = (SexyAppBase*) theArg;
        aSexyApp->CursorThreadProc();
}

void SexyAppBase::StartCursorThread()
{
        if (!mCursorThreadRunning)
        {
                mCursorThreadRunning = true;
                ::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
                _beginthread(CursorThreadProcStub, 0, this);
        }
}

void SexyAppBase::SwitchScreenMode(bool wantWindowed, bool is3d, bool force)
{
        if (mForceFullscreen)
                wantWindowed = false;

        if (mIsWindowed == wantWindowed && !force)
        {
                Set3DAcclerated(is3d);
                return;
        }

        // Set 3d acceleration preference
        Set3DAcclerated(is3d,false);

        // Always make the app windowed when playing demos, in order to
        //  make it easier to track down bugs.  We place this after the
        //  sanity check just so things get re-initialized and stuff
        //if (mPlayingDemoBuffer)
        //      wantWindowed = true;

        mIsWindowed = wantWindowed;    

        MakeWindow();
       
        // We need to do this check to allow IE to get focus instead of
        //  stealing it away for ourselves
        if (!mIsOpeningURL)
        {
                ::ShowWindow(mHWnd, SW_NORMAL);
                ::SetForegroundWindow(mHWnd);
        }
        else
        {
                // Show it but don't activate it
                ::ShowWindow(mHWnd, SW_SHOWNOACTIVATE);
        }

        if (mSoundManager!=NULL)
        {
                mSoundManager->SetCooperativeWindow(mHWnd,mIsWindowed);
        }      

        mLastTime = timeGetTime();
}

void SexyAppBase::SwitchScreenMode(bool wantWindowed)
{
        SwitchScreenMode(wantWindowed, Is3DAccelerated());
}

void SexyAppBase::SwitchScreenMode()
{
        SwitchScreenMode(mIsWindowed, Is3DAccelerated(), true);
}

void SexyAppBase::SetAlphaDisabled(bool isDisabled)
{
        if (mAlphaDisabled != isDisabled)
        {
                mAlphaDisabled = isDisabled;
                mDDInterface->SetVideoOnlyDraw(mAlphaDisabled);        
                mWidgetManager->mImage = mDDInterface->GetScreenImage();
                mWidgetManager->MarkAllDirty();
        }
}

void SexyAppBase::EnforceCursor()
{
        bool wantSysCursor = true;

        if (mDDInterface == NULL)
                return;

        if ((mSEHOccured) || (!mMouseIn))
        {
                ::SetCursor(::LoadCursor(NULL, IDC_ARROW));    
                if (mDDInterface->SetCursorImage(NULL))
                        mCustomCursorDirty = true;
        }
        else
        {
                if ((mCursorImages[mCursorNum] == NULL) ||
                        ((!mPlayingDemoBuffer) && (!mCustomCursorsEnabled) && (mCursorNum != CURSOR_CUSTOM)))
                {
                        if (mOverrideCursor != NULL)
                                ::SetCursor(mOverrideCursor);
                        else if (mCursorNum == CURSOR_POINTER)
                                ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
                        else if (mCursorNum == CURSOR_HAND)
                                ::SetCursor(mHandCursor);
                        else if (mCursorNum == CURSOR_TEXT)
                                ::SetCursor(::LoadCursor(NULL, IDC_IBEAM));
                        else if (mCursorNum == CURSOR_DRAGGING)
                                ::SetCursor(mDraggingCursor);
                        else if (mCursorNum == CURSOR_CIRCLE_SLASH)
                                ::SetCursor(::LoadCursor(NULL, IDC_NO));
                        else if (mCursorNum == CURSOR_SIZEALL)
                                ::SetCursor(::LoadCursor(NULL, IDC_SIZEALL));
                        else if (mCursorNum == CURSOR_SIZENESW)
                                ::SetCursor(::LoadCursor(NULL, IDC_SIZENESW));
                        else if (mCursorNum == CURSOR_SIZENS)
                                ::SetCursor(::LoadCursor(NULL, IDC_SIZENS));
                        else if (mCursorNum == CURSOR_SIZENWSE)
                                ::SetCursor(::LoadCursor(NULL, IDC_SIZENWSE));
                        else if (mCursorNum == CURSOR_SIZEWE)
                                ::SetCursor(::LoadCursor(NULL, IDC_SIZEWE));
                        else if (mCursorNum == CURSOR_WAIT)
                                ::SetCursor(::LoadCursor(NULL, IDC_WAIT));
                        else if (mCursorNum == CURSOR_CUSTOM)
                                ::SetCursor(NULL); // Default to not showing anything
                        else if (mCursorNum == CURSOR_NONE)
                                ::SetCursor(NULL);                     
                        else
                                ::SetCursor(::LoadCursor(NULL, IDC_ARROW));

                        if (mDDInterface->SetCursorImage(NULL))
                                mCustomCursorDirty = true;
                }
                else
                {
                        if (mDDInterface->SetCursorImage(mCursorImages[mCursorNum]))
                                mCustomCursorDirty = true;

                        if (!mPlayingDemoBuffer)
                        {
                                ::SetCursor(NULL);
                        }
                        else
                        {
                                // Give the NO cursor in the client area and an arrow on the title bar

                                POINT aULCorner = {0, 0};
                                ::ClientToScreen(mHWnd, &aULCorner);

                                POINT aBRCorner = {mWidth, mHeight};
                                ::ClientToScreen(mHWnd, &aBRCorner);

                                POINT aPoint;
                                ::GetCursorPos(&aPoint);                       
                                                                       
                                if ((aPoint.x >= aULCorner.x) && (aPoint.y >= aULCorner.y) &&
                                        (aPoint.x < aBRCorner.x) && (aPoint.y < aBRCorner.y))
                                {
                                        ::SetCursor(::LoadCursor(NULL, IDC_NO));
                                }
                                else
                                {
                                        ::SetCursor(::LoadCursor(NULL, IDC_ARROW));
                                }
                        }

                        wantSysCursor = false;
                }
        }

        if (wantSysCursor != mSysCursor)
        {
                mSysCursor = wantSysCursor;

                // Don't hide the hardware cursor when playing back a demo buffer
//              if (!mPlayingDemoBuffer)
//                      ::ShowCursor(mSysCursor);
        }
}

void SexyAppBase::ProcessSafeDeleteList()
{
        MTAutoDisallowRand aDisallowRand;

        WidgetSafeDeleteList::iterator anItr = mSafeDeleteList.begin();
        while (anItr != mSafeDeleteList.end())
        {
                WidgetSafeDeleteInfo* aWidgetSafeDeleteInfo = &(*anItr);
                if (mUpdateAppDepth <= aWidgetSafeDeleteInfo->mUpdateAppDepth)
                {
                        delete aWidgetSafeDeleteInfo->mWidget;
                        anItr = mSafeDeleteList.erase(anItr);
                }
                else
                        ++anItr;
        }      
}

void SexyAppBase::UpdateFTimeAcc()
{
        DWORD aCurTime = timeGetTime();

        if (mLastTimeCheck != 0)
        {                              
                int aDeltaTime = aCurTime - mLastTimeCheck;            

                mUpdateFTimeAcc = min(mUpdateFTimeAcc + aDeltaTime, 200.0);

                if (mRelaxUpdateBacklogCount > 0)                              
                        mRelaxUpdateBacklogCount = max(mRelaxUpdateBacklogCount - aDeltaTime, 0);                              
        }

        mLastTimeCheck = aCurTime;
}

//int aNumCalls = 0;
//DWORD aLastCheck = 0;

bool SexyAppBase::Process(bool allowSleep)
{
        /*DWORD aTimeNow = GetTickCount();
        if (aTimeNow - aLastCheck >= 10000)
        {
                OutputDebugString(StrFormat(_S("FUpdates: %d\n"), aNumCalls).c_str());
                aLastCheck = aTimeNow;
                aNumCalls = 0;
        }*/


        if (mLoadingFailed)
                Shutdown();
       
        bool isVSynched = (!mPlayingDemoBuffer) && (mVSyncUpdates) && (!mLastDrawWasEmpty) && (!mVSyncBroken) &&
                ((!mIsPhysWindowed) || (mIsPhysWindowed && mWaitForVSync && !mSoftVSyncWait));
        double aFrameFTime;
        double anUpdatesPerUpdateF;

        if (mVSyncUpdates)
        {
                aFrameFTime = (1000.0 / mSyncRefreshRate) / mUpdateMultiplier;
                anUpdatesPerUpdateF = (float) (1000.0 / (mFrameTime * mSyncRefreshRate));
        }
        else
        {
                aFrameFTime = mFrameTime / mUpdateMultiplier;
                anUpdatesPerUpdateF = 1.0;
        }

        // Do we need to fast forward?
        if (mPlayingDemoBuffer)
        {
                if (mUpdateCount < mFastForwardToUpdateNum || mFastForwardToMarker)
                {
                        if (!mDemoMute && !mFastForwardStep)
                        {
                                mDemoMute = true;
                                Mute(true);
                        }

                        static DWORD aTick = GetTickCount();
                        while (mUpdateCount < mFastForwardToUpdateNum || mFastForwardToMarker)
                        {
                                ClearUpdateBacklog();
                                int aLastUpdateCount = mUpdateCount;
                                                               
                                // Actual updating code below
                                //////////////////////////////////////////////////////////////////////////
                               
                                bool hadRealUpdate = DoUpdateFrames();

                                if (hadRealUpdate)
                                {                                      
                                        mPendingUpdatesAcc += anUpdatesPerUpdateF;
                                        mPendingUpdatesAcc -= 1.0;
                                        ProcessSafeDeleteList();

                                        // Process any extra updates
                                        while (mPendingUpdatesAcc >= 1.0)
                                        {                                              
                                                // These should just be IDLE commands we have to clear out
                                                ProcessDemo();                                                                                         

                                                bool hasRealUpdate = DoUpdateFrames();
                                                DBG_ASSERTE(hasRealUpdate);

                                                if (!hasRealUpdate)
                                                        break;
                                                                                               
                                                ProcessSafeDeleteList();
                                                mPendingUpdatesAcc -= 1.0;
                                        }
                                                                               
                                        DoUpdateFramesF((float) anUpdatesPerUpdateF);                                  
                                        ProcessSafeDeleteList();
                                }

                                //////////////////////////////////////////////////////////////////////////                             

                                // If the update count doesn't change, its because we are
                                //  playing back a demo and need to read more
                                if (aLastUpdateCount == mUpdateCount)
                                        return true;

                                DWORD aNewTick = GetTickCount();
                                if (aNewTick - aTick >= 1000 || mFastForwardStep) // let the app draw some
                                {
                                        mFastForwardStep = false;
                                        aTick = GetTickCount();
                                        DrawDirtyStuff();                      
                                        return true;
                                }
                        }
                }

                if (mDemoMute)
                {
                        mDemoMute = false;
                        mSoundManager->StopAllSounds();
                        Unmute(true);
                }
        }

        // Make sure we're not paused
        if ((!mPaused) && (mUpdateMultiplier > 0))
        {
                ulong aStartTime = timeGetTime();
               
                ulong aCurTime = aStartTime;           
                int aCumSleepTime = 0;
               
                // When we are VSynching, only calculate this FTimeAcc right after drawing
               
                if (!isVSynched)               
                        UpdateFTimeAcc();                                      

                // mNonDrawCount is used to make sure we draw the screen at least
                // 10 times per second, even if it means we have to slow down
                // the updates to make it draw 10 times per second in "game time"
               
                bool didUpdate = false;
               
                if (mUpdateAppState == UPDATESTATE_PROCESS_1)
                {
                        if ((++mNonDrawCount < (int) ceil(10*mUpdateMultiplier)) || (!mLoaded))
                        {
                                bool doUpdate = false;
                               
                                if (isVSynched)
                                {
                                        // Synch'ed to vertical refresh, so update as soon as possible after draw
                                        doUpdate = (!mHasPendingDraw) || (mUpdateFTimeAcc >= (int) (aFrameFTime * 0.75));
                                }
                                else if (mUpdateFTimeAcc >= aFrameFTime)
                                {
                                        doUpdate = true;
                                }

                                if (doUpdate)
                                {
                                        // Do VSyncBroken test.  This test fails if we're in fullscreen and
                                        // "don't vsync" has been forced in Advanced settings up Display Properties
                                        if ((!mPlayingDemoBuffer) && (mUpdateMultiplier == 1.0))
                                        {
                                                mVSyncBrokenTestUpdates++;
                                                if (mVSyncBrokenTestUpdates >= (DWORD) ((1000+mFrameTime-1)/mFrameTime))
                                                {
                                                        // It has to be running 33% fast to be "broken" (25% = 1/0.800)
                                                        if (aStartTime - mVSyncBrokenTestStartTick <= 800)
                                                        {
                                                                // The test has to fail 3 times in a row before we decide that
                                                                //  vsync is broken overall
                                                                mVSyncBrokenCount++;
                                                                if (mVSyncBrokenCount >= 3)
                                                                        mVSyncBroken = true;
                                                        }
                                                        else
                                                                mVSyncBrokenCount = 0;

                                                        mVSyncBrokenTestStartTick = aStartTime;
                                                        mVSyncBrokenTestUpdates = 0;
                                                }
                                        }
                                       
                                        bool hadRealUpdate = DoUpdateFrames();
                                        if (hadRealUpdate)
                                                mUpdateAppState = UPDATESTATE_PROCESS_2;                                       

                                        mHasPendingDraw = true;
                                        didUpdate = true;
                                }
                        }
                }
                else if (mUpdateAppState == UPDATESTATE_PROCESS_2)
                {
                        mUpdateAppState = UPDATESTATE_PROCESS_DONE;
                       
                        mPendingUpdatesAcc += anUpdatesPerUpdateF;
                        mPendingUpdatesAcc -= 1.0;
                        ProcessSafeDeleteList();

                        // Process any extra updates
                        while (mPendingUpdatesAcc >= 1.0)
                        {      
                                // These should just be IDLE commands we have to clear out
                                ProcessDemo();                                                                                         

                                ++mNonDrawCount;
                                bool hasRealUpdate = DoUpdateFrames();
                                DBG_ASSERTE(hasRealUpdate);

                                if (!hasRealUpdate)
                                        break;
                                                                               
                                ProcessSafeDeleteList();
                                mPendingUpdatesAcc -= 1.0;
                        }                                      

                        //aNumCalls++;
                        DoUpdateFramesF((float) anUpdatesPerUpdateF);
                        ProcessSafeDeleteList();               
               
                        // Don't let mUpdateFTimeAcc dip below 0
                        //  Subtract an extra 0.2ms, because sometimes refresh rates have some
                        //  fractional component that gets truncated, and it's better to take off
                        //  too much to keep our timing tending toward occuring right after
                        //  redraws
                        if (isVSynched)
                                mUpdateFTimeAcc = max(mUpdateFTimeAcc - aFrameFTime - 0.2f, 0.0);
                        else
                                mUpdateFTimeAcc -= aFrameFTime;

                        if (mRelaxUpdateBacklogCount > 0)
                                mUpdateFTimeAcc = 0;

                        didUpdate = true;
                }
               
                if (!didUpdate)
                {                      
                        mUpdateAppState = UPDATESTATE_PROCESS_DONE;

                        mNonDrawCount = 0;
                       
                        if (mHasPendingDraw)
                        {
                                DrawDirtyStuff();
                        }
                        else
                        {
                                // Let us take into account the time it took to draw dirty stuff                       
                                int aTimeToNextFrame = (int) (aFrameFTime - mUpdateFTimeAcc);
                                if (aTimeToNextFrame > 0)
                                {
                                        if (!allowSleep)
                                                return false;

                                        // Wait till next processing cycle
                                        ++mSleepCount;
                                        Sleep(aTimeToNextFrame);

                                        aCumSleepTime += aTimeToNextFrame;                                     
                                }
                        }
                }

                if (mYieldMainThread)
                {
                        // This is to make sure that the title screen doesn't take up any more than
                        // 1/3 of the processor time

                        ulong anEndTime = timeGetTime();
                        int anElapsedTime = (anEndTime - aStartTime) - aCumSleepTime;
                        int aLoadingYieldSleepTime = min(250, (anElapsedTime * 2) - aCumSleepTime);

                        if (aLoadingYieldSleepTime >= 0)
                        {
                                if (!allowSleep)
                                        return false;

                                Sleep(aLoadingYieldSleepTime);
                        }
                }
        }

        ProcessSafeDeleteList();       
        return true;
}

/*void SexyAppBase::DoMainLoop()
{
        Dialog* aDialog = NULL;
        if (theModalDialogId != -1)
        {
                aDialog = GetDialog(theModalDialogId);
                DBG_ASSERTE(aDialog != NULL);
                if (aDialog == NULL)
                        return;
        }

        while (!mShutdown)
        {              
                MSG msg;
                while ((PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) && (!mShutdown))
                {
                        TranslateMessage(&msg);
                        DispatchMessage(&msg);
                }

                ProcessDemo();         
                ProcessDeferredMessages();

                if ((aDialog != NULL) && (aDialog->mResult != -1))
                        return;

                if (!mShutdown)
                {
                        //++aCount;
                        Process();
                }              
        }
}*/


void SexyAppBase::DoMainLoop()
{
        while (!mShutdown)
        {
                if (mExitToTop)
                        mExitToTop = false;
                UpdateApp();
        }
}

bool SexyAppBase::UpdateAppStep(bool* updated)
{
        if (updated != NULL)
                *updated = false;

        if (mExitToTop)
                return false;

        if (mUpdateAppState == UPDATESTATE_PROCESS_DONE)
                mUpdateAppState = UPDATESTATE_MESSAGES;

        mUpdateAppDepth++;

        // We update in two stages to avoid doing a Process if our loop termination
        //  condition has already been met by processing windows messages              
        if (mUpdateAppState == UPDATESTATE_MESSAGES)
        {              
                MSG msg;
                while ((PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) && (!mShutdown))
                {
                        TranslateMessage(&msg);
                        DispatchMessage(&msg);
                }

                ProcessDemo();
                if (!ProcessDeferredMessages(true))
                {                      
                        mUpdateAppState = UPDATESTATE_PROCESS_1;
                }
        }
        else
        {
                // Process changes state by itself
                if (mStepMode)
                {
                        if (mStepMode==2)
                        {
                                Sleep(mFrameTime);
                                mUpdateAppState = UPDATESTATE_PROCESS_DONE; // skip actual update until next step
                        }
                        else
                        {
                                mStepMode = 2;
                                DoUpdateFrames();
                                DoUpdateFramesF(1.0f);
                                DrawDirtyStuff();
                        }
                }
                else
                {
                        int anOldUpdateCnt = mUpdateCount;
                        Process();             
                        if (updated != NULL)
                                *updated = mUpdateCount != anOldUpdateCnt;                     
                }
        }

        mUpdateAppDepth--;

        return true;
}

bool SexyAppBase::UpdateApp()
{
        bool updated;
        for (;;)
        {
                if (!UpdateAppStep(&updated))
                        return false;
                if (updated)
                        return true;
        }
}

int SexyAppBase::InitDDInterface()
{
        PreDDInterfaceInitHook();
        DeleteNativeImageData();
        int aResult = mDDInterface->Init(mHWnd, mIsPhysWindowed);
        DemoSyncRefreshRate();
        if ( DDInterface::RESULT_OK == aResult )
        {
                mScreenBounds.mX = ( mWidth - mDDInterface->mWidth ) / 2;
                mScreenBounds.mY = ( mHeight - mDDInterface->mHeight ) / 2;
                mScreenBounds.mWidth = mDDInterface->mWidth;
                mScreenBounds.mHeight = mDDInterface->mHeight;
                mWidgetManager->Resize(mScreenBounds, mDDInterface->mPresentationRect);
                PostDDInterfaceInitHook();
        }
        return aResult;
}

void SexyAppBase::PreTerminate()
{
}

void SexyAppBase::Start()
{
        if (mShutdown)
                return;

        StartCursorThread();

        if (mAutoStartLoadingThread)
                StartLoadingThread();

        ::ShowWindow(mHWnd, SW_SHOW);  
        ::SetFocus(mHWnd);

        timeBeginPeriod(1);

        int aCount = 0;
        int aSleepCount = 0;

        DWORD aStartTime = timeGetTime();              

        mRunning = true;
        mLastTime = aStartTime;
        mLastUserInputTick = aStartTime;
        mLastTimerTime = aStartTime;

        DoMainLoop();
        ProcessSafeDeleteList();

        mRunning = false;

        WaitForLoadingThread();

        char aString[256];
        sprintf(aString, "Seconds       = %g\r\n", (timeGetTime() - aStartTime) / 1000.0);
        OutputDebugStringA(aString);
        //sprintf(aString, "Count         = %d\r\n", aCount);
        //OutputDebugString(aString);
        sprintf(aString, "Sleep Count   = %d\r\n", mSleepCount);
        OutputDebugStringA(aString);
        sprintf(aString, "Update Count  = %d\r\n", mUpdateCount);
        OutputDebugStringA(aString);
        sprintf(aString, "Draw Count    = %d\r\n", mDrawCount);
        OutputDebugStringA(aString);
        sprintf(aString, "Draw Time     = %d\r\n", mDrawTime);
        OutputDebugStringA(aString);
        sprintf(aString, "Screen Blt    = %d\r\n", mScreenBltTime);
        OutputDebugStringA(aString);
        if (mDrawTime+mScreenBltTime > 0)
        {
                sprintf(aString, "Avg FPS       = %d\r\n", (mDrawCount*1000)/(mDrawTime+mScreenBltTime));
                OutputDebugStringA(aString);
        }

        timeEndPeriod(1);      

        PreTerminate();

        WriteToRegistry();
}

bool SexyAppBase::CheckSignature(const Buffer& theBuffer, const std::string& theFileName)
{
        // Add your own signature checking code here
        return false;
}

bool SexyAppBase::LoadProperties(const std::string& theFileName, bool required, bool checkSig)
{
        Buffer aBuffer;
        if (!ReadBufferFromFile(theFileName, &aBuffer))
        {
                if (!required)
                        return true;
                else
                {
                        Popup(GetString("UNABLE_OPEN_PROPERTIES", _S("Unable to open properties file ")) + StringToSexyString(theFileName));
                        return false;
                }
        }
        if (checkSig)
        {
                if (!CheckSignature(aBuffer, theFileName))
                {
                        Popup(GetString("PROPERTIES_SIG_FAILED", _S("Signature check failed on ")) + StringToSexyString(theFileName + "'"));
                        return false;
                }
        }

        PropertiesParser aPropertiesParser(this);

        // Load required language-file properties
                if (!aPropertiesParser.ParsePropertiesBuffer(aBuffer))
                {
                        Popup(aPropertiesParser.GetErrorText());               
                        return false;
                }
                else
                        return true;
}

bool SexyAppBase::LoadProperties()
{
        // Load required language-file properties
        return LoadProperties("properties\\default.xml", true, false);
}

void SexyAppBase::LoadResourceManifest()
{
        if (!mResourceManager->ParseResourcesFile("properties\\resources.xml"))
                ShowResourceError(true);
}

void SexyAppBase::ShowResourceError(bool doExit)
{
        Popup(mResourceManager->GetErrorText());       
        if (doExit)
                DoExit(0);
}

bool SexyAppBase::GetBoolean(const std::string& theId)
{
        StringBoolMap::iterator anItr = mBoolProperties.find(theId);
        DBG_ASSERTE(anItr != mBoolProperties.end());
       
        if (anItr != mBoolProperties.end())    
                return anItr->second;
        else
                return false;
}

bool SexyAppBase::GetBoolean(const std::string& theId, bool theDefault)
{
        StringBoolMap::iterator anItr = mBoolProperties.find(theId);   
       
        if (anItr != mBoolProperties.end())    
                return anItr->second;
        else
                return theDefault;     
}

int SexyAppBase::GetInteger(const std::string& theId)
{
        StringIntMap::iterator anItr = mIntProperties.find(theId);
        DBG_ASSERTE(anItr != mIntProperties.end());
       
        if (anItr != mIntProperties.end())     
                return anItr->second;
        else
                return false;
}

int SexyAppBase::GetInteger(const std::string& theId, int theDefault)
{
        StringIntMap::iterator anItr = mIntProperties.find(theId);     
       
        if (anItr != mIntProperties.end())     
                return anItr->second;
        else
                return theDefault;     
}

double SexyAppBase::GetDouble(const std::string& theId)
{
        StringDoubleMap::iterator anItr = mDoubleProperties.find(theId);
        DBG_ASSERTE(anItr != mDoubleProperties.end());
       
        if (anItr != mDoubleProperties.end())  
                return anItr->second;
        else
                return false;
}

double SexyAppBase::GetDouble(const std::string& theId, double theDefault)
{
        StringDoubleMap::iterator anItr = mDoubleProperties.find(theId);       
       
        if (anItr != mDoubleProperties.end())  
                return anItr->second;
        else
                return theDefault;     
}

SexyString SexyAppBase::GetString(const std::string& theId)
{
        StringWStringMap::iterator anItr = mStringProperties.find(theId);
        DBG_ASSERTE(anItr != mStringProperties.end());
       
        if (anItr != mStringProperties.end())  
                return WStringToSexyString(anItr->second);
        else
                return _S("");
}

SexyString SexyAppBase::GetString(const std::string& theId, const SexyString& theDefault)
{
        StringWStringMap::iterator anItr = mStringProperties.find(theId);      
       
        if (anItr != mStringProperties.end())  
                return WStringToSexyString(anItr->second);
        else
                return theDefault;     
}

StringVector SexyAppBase::GetStringVector(const std::string& theId)
{
        StringStringVectorMap::iterator anItr = mStringVectorProperties.find(theId);
        DBG_ASSERTE(anItr != mStringVectorProperties.end());
       
        if (anItr != mStringVectorProperties.end())    
                return anItr->second;
        else
                return StringVector();
}

void SexyAppBase::SetString(const std::string& theId, const std::wstring& theValue)
{
        std::pair<StringWStringMap::iterator, bool> aPair = mStringProperties.insert(StringWStringMap::value_type(theId, theValue));
        if (!aPair.second) // Found it, change value
                aPair.first->second = theValue;
}


void SexyAppBase::SetBoolean(const std::string& theId, bool theValue)
{
        std::pair<StringBoolMap::iterator, bool> aPair = mBoolProperties.insert(StringBoolMap::value_type(theId, theValue));
        if (!aPair.second) // Found it, change value
                aPair.first->second = theValue;
}

void SexyAppBase::SetInteger(const std::string& theId, int theValue)
{
        std::pair<StringIntMap::iterator, bool> aPair = mIntProperties.insert(StringIntMap::value_type(theId, theValue));
        if (!aPair.second) // Found it, change value
                aPair.first->second = theValue;
}

void SexyAppBase::SetDouble(const std::string& theId, double theValue)
{
        std::pair<StringDoubleMap::iterator, bool> aPair = mDoubleProperties.insert(StringDoubleMap::value_type(theId, theValue));
        if (!aPair.second) // Found it, change value
                aPair.first->second = theValue;
}

void SexyAppBase::DoParseCmdLine()
{
        char* aCmdLine = GetCommandLineA();    
        char* aCmdLinePtr = aCmdLine;
        if (aCmdLinePtr[0] == '"')
        {
                aCmdLinePtr = strchr(aCmdLinePtr + 1, '"');
                if (aCmdLinePtr != NULL)
                        aCmdLinePtr++;
        }
       
        if (aCmdLinePtr != NULL)
        {
                aCmdLinePtr = strchr(aCmdLinePtr, ' ');
                if (aCmdLinePtr != NULL)
                        ParseCmdLine(aCmdLinePtr+1);   
        }

        mCmdLineParsed = true;
}

void SexyAppBase::ParseCmdLine(const std::string& theCmdLine)
{
        // Command line example:  -play -demofile="game demo.dmo"
        // Results in HandleCmdLineParam("-play", ""); HandleCmdLineParam("-demofile", "game demo.dmo");
        std::string aCurParamName;
        std::string aCurParamValue;

        int aSpacePos = 0;
        bool inQuote = false;
        bool onValue = false;

        for (int i = 0; i < (int) theCmdLine.length(); i++)
        {
                char c = theCmdLine[i];
                bool atEnd = false;
               
                if (c == '"')
                {
                        inQuote = !inQuote;

                        if (!inQuote)
                                atEnd = true;
                }
                else if ((c == ' ') && (!inQuote))
                        atEnd = true;
                else if (c == '=')
                        onValue = true;
                else if (onValue)
                        aCurParamValue += c;
                else
                        aCurParamName += c;
               
                if (i == theCmdLine.length() - 1)
                        atEnd = true;
               
                if (atEnd && !aCurParamName.empty())
                {
                        HandleCmdLineParam(aCurParamName, aCurParamValue);
                        aCurParamName = "";
                        aCurParamValue = "";
                        onValue = false;
                }      
        }
}

static int GetMaxDemoFileNum(const std::string& theDemoPrefix, int theMaxToKeep, bool doErase)
{
        WIN32_FIND_DATAA aData;
        HANDLE aHandle = FindFirstFileA((theDemoPrefix + "*.dmo").c_str(), &aData);
        if (aHandle==INVALID_HANDLE_VALUE)
                return 0;

        typedef std::set<int> IntSet;
        IntSet aSet;

        do {
                int aNum = 0;
                if (sscanf(aData.cFileName,(theDemoPrefix + "%d.dmo").c_str(), &aNum)==1)
                        aSet.insert(aNum);

        } while(FindNextFileA(aHandle,&aData));
        FindClose(aHandle);

        IntSet::iterator anItr = aSet.begin();
        if ((int)aSet.size()>theMaxToKeep-1 && doErase)
                DeleteFile(StrFormat((theDemoPrefix + "%d.dmo").c_str(),*anItr).c_str());

        if (aSet.empty())
                return 0;

        anItr = aSet.end();
        --anItr;
        return (*anItr);
}

void SexyAppBase::HandleCmdLineParam(const std::string& theParamName, const std::string& theParamValue)
{
        if (theParamName == "-play")
        {
                mPlayingDemoBuffer = true;
                mRecordingDemoBuffer = false;
        }
        else if (theParamName == "-recnum")
        {
                int aNum = atoi(theParamValue.c_str());
                if (aNum<=0)
                        aNum=5;

                int aDemoFileNum = GetMaxDemoFileNum(mDemoPrefix, aNum, true) + 1;
                mDemoFileName = SexyStringToString(StrFormat(StringToSexyString(mDemoPrefix + "%d.dmo").c_str(),aDemoFileNum));
                if (mDemoFileName.length() < 2 || (mDemoFileName[1] != ':' && mDemoFileName[2] != '\\'))
                {
                        mDemoFileName = GetAppDataFolder() + mDemoFileName;
                }
                mRecordingDemoBuffer = true;
                mPlayingDemoBuffer = false;
        }
        else if (theParamName == "-playnum")
        {
                int aNum = atoi(theParamValue.c_str())-1;
                if (aNum<0)
                        aNum=0;

                int aDemoFileNum = GetMaxDemoFileNum(mDemoPrefix, aNum, false)-aNum;
                mDemoFileName = SexyStringToString(StrFormat(StringToSexyString(mDemoPrefix + "%d.dmo").c_str(),aDemoFileNum));
                mRecordingDemoBuffer = false;
                mPlayingDemoBuffer = true;
        }
        else if (theParamName == "-record")
        {
                mRecordingDemoBuffer = true;
                mPlayingDemoBuffer = false;
        }
        else if (theParamName == "-demofile")
        {
                mDemoFileName = theParamValue;
                if (mDemoFileName.length() < 2 || (mDemoFileName[1] != ':' && mDemoFileName[2] != '\\'))
                {
                        mDemoFileName = GetAppDataFolder() + mDemoFileName;
                }
        }      
        else if (theParamName == "-crash")
        {
                // Try to access NULL
                char* a = 0;
                *a = '!';              
        }
        else if (theParamName == "-screensaver")
        {
                mIsScreenSaver = true;
        }
        else if (theParamName == "-changedir")
        {
                mChangeDirTo = theParamValue;
        }
        else
        {
                Popup(GetString("INVALID_COMMANDLINE_PARAM", _S("Invalid command line parameter: ")) + StringToSexyString(theParamName));
                DoExit(0);
        }
}

void SexyAppBase::PreDisplayHook()
{
}

void SexyAppBase::PreDDInterfaceInitHook()
{
}

void SexyAppBase::PostDDInterfaceInitHook()
{
}

bool SexyAppBase::ChangeDirHook(const char *theIntendedPath)
{
        return false;
}

MusicInterface* SexyAppBase::CreateMusicInterface(HWND theWindow)
{
        if (mNoSoundNeeded)
                return new MusicInterface;
        else if (mWantFMod)
                return new FModMusicInterface(mInvisHWnd);
        else
                return new BassMusicInterface(mInvisHWnd);
}

void SexyAppBase::InitPropertiesHook()
{
}

void SexyAppBase::InitHook()
{
}

void SexyAppBase::Init()
{
        mPrimaryThreadId = GetCurrentThreadId();       
       
        if (mShutdown)
                return;

        if (gDDrawDLL==NULL || gDSoundDLL==NULL)
        {
                MessageBox(NULL,
                                                GetString("APP_REQUIRES_DIRECTX", _S("This application requires DirectX to run.  You can get DirectX at http://www.microsoft.com/directx")).c_str(),
                                                GetString("YOU_NEED_DIRECTX", _S("You need DirectX")).c_str(),
                                                MB_OK | MB_ICONERROR);
                DoExit(0);
        }      

        InitPropertiesHook();
        ReadFromRegistry();    

        if (CheckForVista())
        {
                HMODULE aMod;
                SHGetFolderPathFunc aFunc = (SHGetFolderPathFunc)GetSHGetFolderPath("shell32.dll", &aMod);
                if (aFunc == NULL || aMod == NULL)
                        SHGetFolderPathFunc aFunc = (SHGetFolderPathFunc)GetSHGetFolderPath("shfolder.dll", &aMod);

                if (aMod != NULL)
                {
                        char aPath[MAX_PATH];
                        aFunc(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, aPath);

                        std::string aDataPath = RemoveTrailingSlash(aPath) + "\\" + mFullCompanyName + "\\" + mProdName;
                        SetAppDataFolder(aDataPath + "\\");
                        //MkDir(aDataPath);
                        //AllowAllAccess(aDataPath);
                        if (mDemoFileName.length() < 2 || (mDemoFileName[1] != ':' && mDemoFileName[2] != '\\'))
                        {
                                mDemoFileName = GetAppDataFolder() + mDemoFileName;
                        }

                        FreeLibrary(aMod);
                }
        }
       
        if (!mCmdLineParsed)
                DoParseCmdLine();

        if (IsScreenSaver())   
                mOnlyAllowOneCopyToRun = false;


        if(gHInstance==NULL)
                gHInstance = (HINSTANCE)GetModuleHandle(NULL);

        // Change directory
        if (!ChangeDirHook(mChangeDirTo.c_str()))
                chdir(mChangeDirTo.c_str());

        gPakInterface->AddPakFile("main.pak");

        // Create a message we can use to talk to ourselves inter-process
        mNotifyGameMessage = RegisterWindowMessage((_S("Notify") + StringToSexyString(mProdName)).c_str());

        // Create a globally unique mutex
        mMutex = CreateMutex(NULL, TRUE, (StringToSexyString(mProdName) + _S("Mutex")).c_str());
        if (::GetLastError() == ERROR_ALREADY_EXISTS)
                HandleGameAlreadyRunning();

        mRandSeed = GetTickCount();
        SRand(mRandSeed);      

        // Set up demo recording stuff
        if (mPlayingDemoBuffer)
        {
                std::string anError;
                if (!ReadDemoBuffer(anError))
                {
                        mPlayingDemoBuffer = false;
                        Popup(anError);
                        DoExit(0);
                }
        }

         
        srand(GetTickCount());

        if (CheckFor98Mill())
        {
                mIsWideWindow = false;

                WNDCLASSA wc;
                wc.style = CS_DBLCLKS;
                wc.cbClsExtra = 0;
                wc.cbWndExtra = 0;
                wc.hbrBackground = NULL;
                wc.hCursor = NULL;
                wc.hIcon = ::LoadIconA(gHInstance, "IDI_MAIN_ICON");
                wc.hInstance = gHInstance;
                wc.lpfnWndProc = WindowProc;
                wc.lpszClassName = "MainWindow";
                wc.lpszMenuName = NULL;
                bool success = RegisterClassA(&wc) != 0;
                DBG_ASSERTE(success);
               
                wc.style = 0;
                wc.cbClsExtra = 0;
                wc.cbWndExtra = 0;
                wc.hbrBackground = NULL;
                wc.hCursor = NULL;
                wc.hIcon = NULL;
                wc.hInstance = gHInstance;
                wc.lpfnWndProc = WindowProc;
                wc.lpszClassName = "InvisWindow";
                wc.lpszMenuName = NULL;
                success = RegisterClassA(&wc) != 0;
                DBG_ASSERTE(success);

                mInvisHWnd = CreateWindowExA(
                                0,
                                "InvisWindow",
                                SexyStringToStringFast(mTitle).c_str(),
                                0,
                                0,
                                0,
                                0,
                                0,
                                NULL,
                                NULL,
                                gHInstance,
                                0);    
                SetWindowLong(mInvisHWnd, GWL_USERDATA, (LONG) this);
        }
        else
        {
                mIsWideWindow = sizeof(SexyChar) == sizeof(wchar_t);

                WNDCLASS wc;
                wc.style = CS_DBLCLKS;
                wc.cbClsExtra = 0;
                wc.cbWndExtra = 0;
                wc.hbrBackground = NULL;
                wc.hCursor = NULL;
                wc.hIcon = ::LoadIconA(gHInstance, "IDI_MAIN_ICON");
                wc.hInstance = gHInstance;
                wc.lpfnWndProc = WindowProc;
                wc.lpszClassName = _S("MainWindow");
                wc.lpszMenuName = NULL;
                bool success = RegisterClass(&wc) != 0;
                DBG_ASSERTE(success);
               
                wc.style = 0;
                wc.cbClsExtra = 0;
                wc.cbWndExtra = 0;
                wc.hbrBackground = NULL;
                wc.hCursor = NULL;
                wc.hIcon = NULL;
                wc.hInstance = gHInstance;
                wc.lpfnWndProc = WindowProc;
                wc.lpszClassName = _S("InvisWindow");
                wc.lpszMenuName = NULL;
                success = RegisterClass(&wc) != 0;
                DBG_ASSERTE(success);

                mInvisHWnd = CreateWindowEx(
                                0,
                                _S("InvisWindow"),
                                mTitle.c_str(),
                                0,
                                0,
                                0,
                                0,
                                0,
                                NULL,
                                NULL,
                                gHInstance,
                                0);    
                SetWindowLong(mInvisHWnd, GWL_USERDATA, (LONG) this);
        }
               
        mHandCursor = CreateCursor(gHInstance, 11, 4, 32, 32, gFingerCursorData, gFingerCursorData+sizeof(gFingerCursorData)/2);
        mDraggingCursor = CreateCursor(gHInstance, 15, 10, 32, 32, gDraggingCursorData, gDraggingCursorData+sizeof(gDraggingCursorData)/2);
               
        // Let app do something before showing window, or switching to fullscreen mode
        // NOTE: Moved call to PreDisplayHook above mIsWindowed and GetSystemsMetrics
        // checks because the checks below use values that could change in PreDisplayHook.
        // PreDisplayHook must call mWidgetManager->Resize if it changes mWidth or mHeight.
        PreDisplayHook();

        mWidgetManager->Resize(Rect(0, 0, mWidth, mHeight), Rect(0, 0, mWidth, mHeight));

        // Check to see if we CAN run windowed or not...
        if (mIsWindowed && !mFullScreenWindow)
        {
                // How can we be windowed if our screen isn't even big enough?
                if ((mWidth >= GetSystemMetrics(SM_CXFULLSCREEN)) ||
                        (mHeight >= GetSystemMetrics(SM_CYFULLSCREEN)))
                {
                        mIsWindowed = false;
                        mForceFullscreen = true;
                }
        }

        if (mFullScreenWindow) // change resoultion using ChangeDisplaySettings
        {
                EnumWindows(ChangeDisplayWindowEnumProc,0); // record window pos
                DEVMODE dm;
                EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm );

                // Switch resolutions
                if (dm.dmPelsWidth!=mWidth || dm.dmPelsHeight!=mHeight || (dm.dmBitsPerPel!=16 && dm.dmBitsPerPel!=32))
                {
                        dm.dmPelsWidth = mWidth;
                        dm.dmPelsHeight = mHeight;
                        dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;

                        if (dm.dmBitsPerPel!=16 && dm.dmBitsPerPel!=32) // handle 24-bit/256 color case
                        {
                                dm.dmBitsPerPel = 16;
                                dm.dmFields |= DM_BITSPERPEL;
                        }

                        if (ChangeDisplaySettings(&dm,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
                        {
                                mFullScreenWindow = false;
                                mIsWindowed = false;
                        }
                }
        }

        MakeWindow();
               
        if (mPlayingDemoBuffer)
        {
                // Get video data
               
                PrepareDemoCommand(true);
                mDemoNeedsCommand = true;
               
                DBG_ASSERTE(!mDemoIsShortCmd);
                DBG_ASSERTE(mDemoCmdNum == DEMO_VIDEO_DATA);

                mIsWindowed = mDemoBuffer.ReadBoolean();
                mSyncRefreshRate = mDemoBuffer.ReadByte();
        }

        if (mSoundManager == NULL)             
                mSoundManager = new DSoundManager(mNoSoundNeeded?NULL:mHWnd, mWantFMod);

        SetSfxVolume(mSfxVolume);
       
        mMusicInterface = CreateMusicInterface(mInvisHWnd);    

        SetMusicVolume(mMusicVolume);  

        if (IsScreenSaver())
        {
                SetCursor(CURSOR_NONE);
        }

        InitHook();

        mInitialized = true;
}

void SexyAppBase::HandleGameAlreadyRunning()
{
        if(mOnlyAllowOneCopyToRun)
        {
                // Notify the other window and then shut ourselves down
                if (mNotifyGameMessage != 0)
                        PostMessage(HWND_BROADCAST, mNotifyGameMessage, 0, 0);

                DoExit(0);
        }
}

void SexyAppBase::CopyToClipboard(const std::string& theString)
{
        if (mPlayingDemoBuffer)
                return;

        HGLOBAL                         aGlobalHandle;
        char*                           theData;       
        WCHAR*                          theWData;

        if (OpenClipboard(mHWnd))
        {
                EmptyClipboard();

                aGlobalHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, theString.length()+1);
                theData = (char*) GlobalLock(aGlobalHandle);
                strcpy(theData, theString.c_str());
                GlobalUnlock(aGlobalHandle);
                SetClipboardData(CF_TEXT, aGlobalHandle);
                SetClipboardData(CF_OEMTEXT, aGlobalHandle);           
                SetClipboardData(CF_LOCALE, aGlobalHandle);

                int aSize = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, theString.c_str(), theString.length(), NULL, 0);
                aGlobalHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, (aSize + 1) * sizeof(WCHAR));
                theWData = (WCHAR*) GlobalLock(aGlobalHandle);
                MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, theString.c_str(), theString.length(), theWData, aSize);
                theWData[aSize] = '\0';
                GlobalUnlock(aGlobalHandle);
                SetClipboardData(CF_UNICODETEXT, aGlobalHandle);
               
                CloseClipboard();
        }
}

std::string     SexyAppBase::GetClipboard()
{
        HGLOBAL                         aGlobalHandle; 
        std::string                     aString;

        if (!mPlayingDemoBuffer)
        {
                if (OpenClipboard(mHWnd))
                {
                        aGlobalHandle = GetClipboardData(CF_TEXT);     
                        if (aGlobalHandle != NULL)
                        {
                                char* theData = (char*) GlobalLock(aGlobalHandle);
                                if (theData != NULL)
                                {
                                        aString = theData;             
                                        GlobalUnlock(aGlobalHandle);
                                }
                        }
                       
                        CloseClipboard();
                }
        }

        DemoSyncString(&aString);

        return aString;
}

void SexyAppBase::SetCursor(int theCursorNum)
{
        mCursorNum = theCursorNum;
        EnforceCursor();
}

int SexyAppBase::GetCursor()
{
        return mCursorNum;
}

void SexyAppBase::EnableCustomCursors(bool enabled)
{
        mCustomCursorsEnabled = enabled;
        EnforceCursor();
}

Sexy::DDImage* SexyAppBase::GetImage(const std::string& theFileName, bool commitBits)
{      
        ImageLib::Image* aLoadedImage = ImageLib::GetImage(theFileName, true);
       
        if (aLoadedImage == NULL)
                return NULL;   

        DDImage* anImage = new DDImage(mDDInterface);
        anImage->mFilePath = theFileName;
        anImage->SetBits(aLoadedImage->GetBits(), aLoadedImage->GetWidth(), aLoadedImage->GetHeight(), commitBits);    
        anImage->mFilePath = theFileName;
        delete aLoadedImage;
       
        return anImage;
}

Sexy::DDImage* SexyAppBase::CreateCrossfadeImage(Sexy::Image* theImage1, const Rect& theRect1, Sexy::Image* theImage2, const Rect& theRect2, double theFadeFactor)
{
        MemoryImage* aMemoryImage1 = dynamic_cast<MemoryImage*>(theImage1);
        MemoryImage* aMemoryImage2 = dynamic_cast<MemoryImage*>(theImage2);

        if ((aMemoryImage1 == NULL) || (aMemoryImage2 == NULL))
                return NULL;

        if ((theRect1.mX < 0) || (theRect1.mY < 0) ||
                (theRect1.mX + theRect1.mWidth > theImage1->GetWidth()) ||
                (theRect1.mY + theRect1.mHeight > theImage1->GetHeight()))
        {
                DBG_ASSERTE("Crossfade Rect1 out of bounds");
                return NULL;
        }

        if ((theRect2.mX < 0) || (theRect2.mY < 0) ||
                (theRect2.mX + theRect2.mWidth > theImage2->GetWidth()) ||
                (theRect2.mY + theRect2.mHeight > theImage2->GetHeight()))
        {
                DBG_ASSERTE("Crossfade Rect2 out of bounds");
                return NULL;
        }

        int aWidth = theRect1.mWidth;
        int aHeight = theRect1.mHeight;

        DDImage* anImage = new DDImage(mDDInterface);
        anImage->Create(aWidth, aHeight);

        ulong* aDestBits = anImage->GetBits();
        ulong* aSrcBits1 = aMemoryImage1->GetBits();
        ulong* aSrcBits2 = aMemoryImage2->GetBits();

        int aSrc1Width = aMemoryImage1->GetWidth();
        int aSrc2Width = aMemoryImage2->GetWidth();
        ulong aMult = (int) (theFadeFactor*256);
        ulong aOMM = (256 - aMult);

        for (int y = 0; y < aHeight; y++)
        {
                ulong* s1 = &aSrcBits1[(y+theRect1.mY)*aSrc1Width+theRect1.mX];
                ulong* s2 = &aSrcBits2[(y+theRect2.mY)*aSrc2Width+theRect2.mX];
                ulong* d = &aDestBits[y*aWidth];

                for (int x = 0; x < aWidth; x++)
                {
                        ulong p1 = *s1++;
                        ulong p2 = *s2++;

                        //p1 = 0;
                        //p2 = 0xFFFFFFFF;

                        *d++ =
                                ((((p1 & 0x000000FF)*aOMM + (p2 & 0x000000FF)*aMult)>>8) & 0x000000FF) |
                                ((((p1 & 0x0000FF00)*aOMM + (p2 & 0x0000FF00)*aMult)>>8) & 0x0000FF00) |
                                ((((p1 & 0x00FF0000)*aOMM + (p2 & 0x00FF0000)*aMult)>>8) & 0x00FF0000) |
                                ((((p1 >> 24)*aOMM + (p2 >> 24)*aMult)<<16) & 0xFF000000);
                }
        }

        anImage->BitsChanged();
       
        return anImage;
}

void SexyAppBase::ColorizeImage(Image* theImage, const Color& theColor)
{
        MemoryImage* aSrcMemoryImage = dynamic_cast<MemoryImage*>(theImage);

        if (aSrcMemoryImage == NULL)
                return;

        ulong* aBits;  
        int aNumColors;

        if (aSrcMemoryImage->mColorTable == NULL)
        {
                aBits = aSrcMemoryImage->GetBits();            
                aNumColors = theImage->GetWidth()*theImage->GetHeight();                               
        }
        else
        {
                aBits = aSrcMemoryImage->mColorTable;          
                aNumColors = 256;                              
        }
                                               
        if ((theColor.mAlpha <= 255) && (theColor.mRed <= 255) &&
                (theColor.mGreen <= 255) && (theColor.mBlue <= 255))
        {
                for (int i = 0; i < aNumColors; i++)
                {
                        ulong aColor = aBits[i];

                        aBits[i] =
                                ((((aColor & 0xFF000000) >> 8) * theColor.mAlpha) & 0xFF000000) |
                                ((((aColor & 0x00FF0000) * theColor.mRed) >> 8) & 0x00FF0000) |
                                ((((aColor & 0x0000FF00) * theColor.mGreen) >> 8) & 0x0000FF00)|
                                ((((aColor & 0x000000FF) * theColor.mBlue) >> 8) & 0x000000FF);
                }
        }
        else
        {
                for (int i = 0; i < aNumColors; i++)
                {
                        ulong aColor = aBits[i];

                        int aAlpha = ((aColor >> 24) * theColor.mAlpha) / 255;
                        int aRed = (((aColor >> 16) & 0xFF) * theColor.mRed) / 255;
                        int aGreen = (((aColor >> 8) & 0xFF) * theColor.mGreen) / 255;
                        int aBlue = ((aColor & 0xFF) * theColor.mBlue) / 255;

                        if (aAlpha > 255)
                                aAlpha = 255;
                        if (aRed > 255)
                                aRed = 255;
                        if (aGreen > 255)
                                aGreen = 255;
                        if (aBlue > 255)
                                aBlue = 255;

                        aBits[i] = (aAlpha << 24) | (aRed << 16) | (aGreen << 8) | (aBlue);
                }
        }      

        aSrcMemoryImage->BitsChanged();
}

DDImage* SexyAppBase::CreateColorizedImage(Image* theImage, const Color& theColor)
{
        MemoryImage* aSrcMemoryImage = dynamic_cast<MemoryImage*>(theImage);

        if (aSrcMemoryImage == NULL)
                return NULL;

        DDImage* anImage = new DDImage(mDDInterface);
       
        anImage->Create(theImage->GetWidth(), theImage->GetHeight());
       
        ulong* aSrcBits;
        ulong* aDestBits;
        int aNumColors;

        if (aSrcMemoryImage->mColorTable == NULL)
        {
                aSrcBits = aSrcMemoryImage->GetBits();
                aDestBits = anImage->GetBits();
                aNumColors = theImage->GetWidth()*theImage->GetHeight();                               
        }
        else
        {
                aSrcBits = aSrcMemoryImage->mColorTable;
                aDestBits = anImage->mColorTable = new ulong[256];
                aNumColors = 256;
               
                anImage->mColorIndices = new uchar[anImage->mWidth*theImage->mHeight];
                memcpy(anImage->mColorIndices, aSrcMemoryImage->mColorIndices, anImage->mWidth*theImage->mHeight);
        }
                                               
        if ((theColor.mAlpha <= 255) && (theColor.mRed <= 255) &&
                (theColor.mGreen <= 255) && (theColor.mBlue <= 255))
        {
                for (int i = 0; i < aNumColors; i++)
                {
                        ulong aColor = aSrcBits[i];

                        aDestBits[i] =
                                ((((aColor & 0xFF000000) >> 8) * theColor.mAlpha) & 0xFF000000) |
                                ((((aColor & 0x00FF0000) * theColor.mRed) >> 8) & 0x00FF0000) |
                                ((((aColor & 0x0000FF00) * theColor.mGreen) >> 8) & 0x0000FF00)|
                                ((((aColor & 0x000000FF) * theColor.mBlue) >> 8) & 0x000000FF);
                }
        }
        else
        {
                for (int i = 0; i < aNumColors; i++)
                {
                        ulong aColor = aSrcBits[i];

                        int aAlpha = ((aColor >> 24) * theColor.mAlpha) / 255;
                        int aRed = (((aColor >> 16) & 0xFF) * theColor.mRed) / 255;
                        int aGreen = (((aColor >> 8) & 0xFF) * theColor.mGreen) / 255;
                        int aBlue = ((aColor & 0xFF) * theColor.mBlue) / 255;

                        if (aAlpha > 255)
                                aAlpha = 255;
                        if (aRed > 255)
                                aRed = 255;
                        if (aGreen > 255)
                                aGreen = 255;
                        if (aBlue > 255)
                                aBlue = 255;

                        aDestBits[i] = (aAlpha << 24) | (aRed << 16) | (aGreen << 8) | (aBlue);
                }
        }      

        anImage->BitsChanged();

        return anImage;
}

DDImage* SexyAppBase::CopyImage(Image* theImage, const Rect& theRect)
{
        DDImage* anImage = new DDImage(mDDInterface);

        anImage->Create(theRect.mWidth, theRect.mHeight);
       
        Graphics g(anImage);
        g.DrawImage(theImage, -theRect.mX, -theRect.mY);

        anImage->CopyAttributes(theImage);

        return anImage;
}

DDImage* SexyAppBase::CopyImage(Image* theImage)
{
        return CopyImage(theImage, Rect(0, 0, theImage->GetWidth(), theImage->GetHeight()));
}

void SexyAppBase::MirrorImage(Image* theImage)
{
        MemoryImage* aSrcMemoryImage = dynamic_cast<MemoryImage*>(theImage);   

        ulong* aSrcBits = aSrcMemoryImage->GetBits();

        int aPhysSrcWidth = aSrcMemoryImage->mWidth;
        for (int y = 0; y < aSrcMemoryImage->mHeight; y++)
        {
                ulong* aLeftBits = aSrcBits + (y * aPhysSrcWidth);             
                ulong* aRightBits = aLeftBits + (aPhysSrcWidth - 1);

                for (int x = 0; x < (aPhysSrcWidth >> 1); x++)
                {
                        ulong aSwap = *aLeftBits;

                        *(aLeftBits++) = *aRightBits;
                        *(aRightBits--) = aSwap;
                }
        }

        aSrcMemoryImage->BitsChanged();
}

void SexyAppBase::FlipImage(Image* theImage)
{
        MemoryImage* aSrcMemoryImage = dynamic_cast<MemoryImage*>(theImage);

        ulong* aSrcBits = aSrcMemoryImage->GetBits();

        int aPhysSrcHeight = aSrcMemoryImage->mHeight;
        int aPhysSrcWidth = aSrcMemoryImage->mWidth;
        for (int x = 0; x < aPhysSrcWidth; x++)
        {
                ulong* aTopBits    = aSrcBits + x;
                ulong* aBottomBits = aTopBits + (aPhysSrcWidth * (aPhysSrcHeight - 1));

                for (int y = 0; y < (aPhysSrcHeight >> 1); y++)
                {
                        ulong aSwap = *aTopBits;

                        *aTopBits = *aBottomBits;
                        aTopBits += aPhysSrcWidth;
                        *aBottomBits = aSwap;
                        aBottomBits -= aPhysSrcWidth;
                }
        }

        aSrcMemoryImage->BitsChanged();
}

void SexyAppBase::RotateImageHue(Sexy::MemoryImage *theImage, int theDelta)
{
        while (theDelta < 0)
                theDelta += 256;

        int aSize = theImage->mWidth * theImage->mHeight;
        DWORD *aPtr = theImage->GetBits();
        for (int i=0; i<aSize; i++)
        {
                DWORD aPixel = *aPtr;
                int alpha = aPixel&0xff000000;
                int r = (aPixel>>16)&0xff;
                int g = (aPixel>>8) &0xff;
                int b = aPixel&0xff;

                int maxval = max(r, max(g, b));
                int minval = min(r, min(g, b));
                int h = 0;
                int s = 0;
                int l = (minval+maxval)/2;
                int delta = maxval - minval;

                if (delta != 0)
                {                      
                        s = (delta * 256) / ((l <= 128) ? (minval + maxval) : (512 - maxval - minval));
                       
                        if (r == maxval)
                                h = (g == minval ? 1280 + (((maxval-b) * 256) / delta) :  256 - (((maxval - g) * 256) / delta));
                        else if (g == maxval)
                                h = (b == minval ?  256 + (((maxval-r) * 256) / delta) :  768 - (((maxval - b) * 256) / delta));
                        else
                                h = (r == minval ?  768 + (((maxval-g) * 256) / delta) : 1280 - (((maxval - r) * 256) / delta));
                       
                        h /= 6;
                }

                h += theDelta;
                if (h >= 256)
                        h -= 256;

                double v= (l < 128) ? (l * (255+s))/255 :
                                (l+s-l*s/255);
               
                int y = (int) (2*l-v);

                int aColorDiv = (6 * h) / 256;
                int x = (int)(y+(v-y)*((h - (aColorDiv * 256 / 6)) * 6)/255);
                if (x > 255)
                        x = 255;

                int z = (int) (v-(v-y)*((h - (aColorDiv * 256 / 6)) * 6)/255);
                if (z < 0)
                        z = 0;
               
                switch (aColorDiv)
                {
                        case 0: r = (int) v; g = x; b = y; break;
                        case 1: r = z; g= (int) v; b = y; break;
                        case 2: r = y; g= (int) v; b = x; break;
                        case 3: r = y; g = z; b = (int) v; break;
                        case 4: r = x; g = y; b = (int) v; break;
                        case 5: r = (int) v; g = y; b = z; break;
                        default: r = (int) v; g = x; b = y; break;
                }

                *aPtr++ = alpha | (r<<16) | (g << 8) | (b);      

        }

        theImage->BitsChanged();
}

ulong SexyAppBase::HSLToRGB(int h, int s, int l)
{
        int r;
        int g;
        int b;

        double v= (l < 128) ? (l * (255+s))/255 :
                        (l+s-l*s/255);
       
        int y = (int) (2*l-v);

        int aColorDiv = (6 * h) / 256;
        int x = (int)(y+(v-y)*((h - (aColorDiv * 256 / 6)) * 6)/255);
        if (x > 255)
                x = 255;

        int z = (int) (v-(v-y)*((h - (aColorDiv * 256 / 6)) * 6)/255);
        if (z < 0)
                z = 0;
       
        switch (aColorDiv)
        {
                case 0: r = (int) v; g = x; b = y; break;
                case 1: r = z; g= (int) v; b = y; break;
                case 2: r = y; g= (int) v; b = x; break;
                case 3: r = y; g = z; b = (int) v; break;
                case 4: r = x; g = y; b = (int) v; break;
                case 5: r = (int) v; g = y; b = z; break;
                default: r = (int) v; g = x; b = y; break;
        }

        return 0xFF000000 | (r << 16) | (g << 8) | (b);
}

ulong SexyAppBase::RGBToHSL(int r, int g, int b)
{                                      
        int maxval = max(r, max(g, b));
        int minval = min(r, min(g, b));
        int hue = 0;
        int saturation = 0;
        int luminosity = (minval+maxval)/2;
        int delta = maxval - minval;

        if (delta != 0)
        {                      
                saturation = (delta * 256) / ((luminosity <= 128) ? (minval + maxval) : (512 - maxval - minval));
               
                if (r == maxval)
                        hue = (g == minval ? 1280 + (((maxval-b) * 256) / delta) :  256 - (((maxval - g) * 256) / delta));
                else if (g == maxval)
                        hue = (b == minval ?  256 + (((maxval-r) * 256) / delta) :  768 - (((maxval - b) * 256) / delta));
                else
                        hue = (r == minval ?  768 + (((maxval-g) * 256) / delta) : 1280 - (((maxval - r) * 256) / delta));
               
                hue /= 6;
        }

        return 0xFF000000 | (hue) | (saturation << 8) | (luminosity << 16);      
}

void SexyAppBase::HSLToRGB(const ulong* theSource, ulong* theDest, int theSize)
{
        for (int i = 0; i < theSize; i++)
        {
                ulong src = theSource[i];
                theDest[i] = (src & 0xFF000000) | (HSLToRGB((src & 0xFF), (src >> 8) & 0xFF, (src >> 16) & 0xFF) & 0x00FFFFFF);
        }
}

void SexyAppBase::RGBToHSL(const ulong* theSource, ulong* theDest, int theSize)
{
        for (int i = 0; i < theSize; i++)
        {
                ulong src = theSource[i];
                theDest[i] = (src & 0xFF000000) | (RGBToHSL(((src >> 16) & 0xFF), (src >> 8) & 0xFF, (src & 0xFF)) & 0x00FFFFFF);
        }
}

void SexyAppBase::PrecacheAdditive(MemoryImage* theImage)
{
        theImage->GetRLAdditiveData(mDDInterface);
}

void SexyAppBase::PrecacheAlpha(MemoryImage* theImage)
{
        theImage->GetRLAlphaData();
}

void SexyAppBase::PrecacheNative(MemoryImage* theImage)
{
        theImage->GetNativeAlphaData(mDDInterface);
}


void SexyAppBase::PlaySample(int theSoundNum)
{
        if (!mSoundManager)
                return;

        SoundInstance* aSoundInstance = mSoundManager->GetSoundInstance(theSoundNum);
        if (aSoundInstance != NULL)
        {
                aSoundInstance->Play(false, true);
        }
}


void SexyAppBase::PlaySample(int theSoundNum, int thePan)
{
        if (!mSoundManager)
                return;

        SoundInstance* aSoundInstance = mSoundManager->GetSoundInstance(theSoundNum);
        if (aSoundInstance != NULL)
        {
                aSoundInstance->SetPan(thePan);
                aSoundInstance->Play(false, true);
        }
}

bool SexyAppBase::IsMuted()
{
        return mMuteCount > 0;
}

void SexyAppBase::Mute(bool autoMute)
{      
        mMuteCount++;
        if (autoMute)
                mAutoMuteCount++;

        SetMusicVolume(mMusicVolume);
        SetSfxVolume(mSfxVolume);
}

void SexyAppBase::Unmute(bool autoMute)
{      
        if (mMuteCount > 0)
        {
                mMuteCount--;
                if (autoMute)
                        mAutoMuteCount--;
        }

        SetMusicVolume(mMusicVolume);
        SetSfxVolume(mSfxVolume);
}


double SexyAppBase::GetMusicVolume()
{
        return mMusicVolume;
}

void SexyAppBase::SetMusicVolume(double theVolume)
{
        mMusicVolume = theVolume;

        if (mMusicInterface != NULL)
                mMusicInterface->SetVolume((mMuteCount > 0) ? 0.0 : mMusicVolume);
}

double SexyAppBase::GetSfxVolume()
{
        return mSfxVolume;
}

void SexyAppBase::SetSfxVolume(double theVolume)
{
        mSfxVolume = theVolume;

        if (mSoundManager != NULL)
                mSoundManager->SetVolume((mMuteCount > 0) ? 0.0 : mSfxVolume);
}

double SexyAppBase::GetMasterVolume()
{
        return mSoundManager->GetMasterVolume();
}

void SexyAppBase::SetMasterVolume(double theMasterVolume)
{
        mSfxVolume = theMasterVolume;
        mSoundManager->SetMasterVolume(mSfxVolume);
}

void SexyAppBase::AddMemoryImage(MemoryImage* theMemoryImage)
{
        AutoCrit anAutoCrit(mDDInterface->mCritSect);
        mMemoryImageSet.insert(theMemoryImage);
}

void SexyAppBase::RemoveMemoryImage(MemoryImage* theMemoryImage)
{
        AutoCrit anAutoCrit(mDDInterface->mCritSect);
        MemoryImageSet::iterator anItr = mMemoryImageSet.find(theMemoryImage);
        if (anItr != mMemoryImageSet.end())
                mMemoryImageSet.erase(anItr);

        Remove3DData(theMemoryImage);
}

void SexyAppBase::Remove3DData(MemoryImage* theMemoryImage)
{
        if (mDDInterface)
                mDDInterface->Remove3DData(theMemoryImage);
}


bool SexyAppBase::Is3DAccelerated()
{
        return mDDInterface->mIs3D;
}

bool SexyAppBase::Is3DAccelerationSupported()
{
        if (mDDInterface->mD3DTester)
                return mDDInterface->mD3DTester->Is3DSupported();
        else
                return false;
}

bool SexyAppBase::Is3DAccelerationRecommended()
{
        if (mDDInterface->mD3DTester)
                return mDDInterface->mD3DTester->Is3DRecommended();
        else
                return false;
}

void SexyAppBase::DemoSyncRefreshRate()
{
        mSyncRefreshRate = mDDInterface->mRefreshRate;

        if (mRecordingDemoBuffer)
        {
                WriteDemoTimingBlock();
                mDemoBuffer.WriteNumBits(0, 1);
                mDemoBuffer.WriteNumBits(DEMO_VIDEO_DATA, 5);
                mDemoBuffer.WriteBoolean(mIsWindowed);
                uchar aByte = (uchar) mSyncRefreshRate;
                mDemoBuffer.WriteByte(aByte);          
        }
}

void SexyAppBase::Set3DAcclerated(bool is3D, bool reinit)
{
        if (mDDInterface->mIs3D == is3D)
                return;

        mUserChanged3DSetting = true;
        mDDInterface->mIs3D = is3D;
       
        if (reinit)
        {
                int aResult = InitDDInterface();

                if (is3D && aResult != DDInterface::RESULT_OK)
                {
                        Set3DAcclerated(false, reinit);
                        return;
                }
                else if (aResult != DDInterface::RESULT_OK)
                {
                        Popup(GetString("FAILED_INIT_DIRECTDRAW", _S("Failed to initialize DirectDraw: ")) + StringToSexyString(DDInterface::ResultToString(aResult) + " " + mDDInterface->mErrorString));
                        DoExit(1);
                }

                ReInitImages();

                mWidgetManager->mImage = mDDInterface->GetScreenImage();
                mWidgetManager->MarkAllDirty();
        }
}

SharedImageRef SexyAppBase::GetSharedImage(const std::string& theFileName, const std::string& theVariant, bool* isNew)
{      
        std::string anUpperFileName = StringToUpper(theFileName);
        std::string anUpperVariant = StringToUpper(theVariant);

        std::pair<SharedImageMap::iterator, bool> aResultPair;
        SharedImageRef aSharedImageRef;

        {
                AutoCrit anAutoCrit(mDDInterface->mCritSect);  
                aResultPair = mSharedImageMap.insert(SharedImageMap::value_type(SharedImageMap::key_type(anUpperFileName, anUpperVariant), SharedImage()));
                aSharedImageRef = &aResultPair.first->second;
        }

        if (isNew != NULL)
                *isNew = aResultPair.second;

        if (aResultPair.second)
        {
                // Pass in a '!' as the first char of the file name to create a new image
                if ((theFileName.length() > 0) && (theFileName[0] == '!'))
                        aSharedImageRef.mSharedImage->mImage = new DDImage(mDDInterface);
                else
                        aSharedImageRef.mSharedImage->mImage = GetImage(theFileName,false);
        }

        return aSharedImageRef;
}

void SexyAppBase::CleanSharedImages()
{
        AutoCrit anAutoCrit(mDDInterface->mCritSect);  

        if (mCleanupSharedImages)
        {
                // Delete shared images with reference counts of 0
                // This doesn't occur in ~SharedImageRef because sometimes we can not only access the image
                //  through the SharedImageRef returned by GetSharedImage, but also by calling GetSharedImage
                //  again with the same params -- so we can have instances where we do the 'final' deref on
                //  an image but immediately re-request it via GetSharedImage
                SharedImageMap::iterator aSharedImageItr = mSharedImageMap.begin();
                while (aSharedImageItr != mSharedImageMap.end())
                {
                        SharedImage* aSharedImage = &aSharedImageItr->second;
                        if (aSharedImage->mRefCount == 0)
                        {
                                delete aSharedImage->mImage;
                                mSharedImageMap.erase(aSharedImageItr++);
                        }
                        else
                                ++aSharedImageItr;
                }

                mCleanupSharedImages = false;
        }
}