summaryrefslogtreecommitdiff
path: root/osframework/source/SexyAppFramework/SexyAppBase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'osframework/source/SexyAppFramework/SexyAppBase.cpp')
-rw-r--r--osframework/source/SexyAppFramework/SexyAppBase.cpp7087
1 files changed, 7087 insertions, 0 deletions
diff --git a/osframework/source/SexyAppFramework/SexyAppBase.cpp b/osframework/source/SexyAppFramework/SexyAppBase.cpp
new file mode 100644
index 0000000..2f2f5fb
--- /dev/null
+++ b/osframework/source/SexyAppFramework/SexyAppBase.cpp
@@ -0,0 +1,7087 @@
+//#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;
+ }
+}