Engine directory for ticket #1

This commit is contained in:
DavidWyand-GG 2012-09-19 11:15:01 -04:00
parent 352279af7a
commit 7dbfe6994d
3795 changed files with 1363358 additions and 0 deletions

View file

@ -0,0 +1,38 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by zipCat.rc
//
#define IDR_ZIPFILE 500
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 501
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View file

@ -0,0 +1,81 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/strings/stringFunctions.h"
#include "console/console.h"
#include "platformWin32/platformWin32.h"
void initDisplayDeviceInfo()
{
Con::printf( "Reading Display Device information..." );
U8 i = 0;
DISPLAY_DEVICEA ddData;
ddData.cb = sizeof( DISPLAY_DEVICEA );
// Search for the primary display adapter, because that is what the rendering
// context will get created on.
while( EnumDisplayDevicesA( NULL, i, &ddData, 0 ) != 0 )
{
// If we find the primary display adapter, break out
if( ddData.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE )
break;
i++;
}
Con::printf( " Primary Display Device Found:" );
// Ok, now we have the primary display device. Parse the device information.
char ven[9];
char dev[9];
ven[8] = dev[8] = '\0';
// It may seem a bit silly here to cast, but there are two implimentations in Platform.h
// This usage is the "const" version...
char *pos = dStrstr( ddData.DeviceID, (const char *)"VEN_");
dStrncpy( ven, ( pos ) ? pos : "VEN_0000", 8 );
Con::printf( " Vendor Id: %s", ven );
pos = dStrstr( ddData.DeviceID, (const char *)"DEV_" );
dStrncpy( dev, ( pos ) ? pos : "DEV_0000", 8 );
Con::printf( " Device Id: %s", dev );
// We now have the information, set them to console variables so we can parse
// the file etc in script using getField and so on.
Con::setVariable( "$PCI_VEN", ven );
Con::setVariable( "$PCI_DEV", dev );
}
ConsoleFunction( initDisplayDeviceInfo, void, 1, 1, "()"
"@brief Initializes variables that track device and vendor information/IDs\n\n"
"@ingroup Rendering")
{
initDisplayDeviceInfo();
}

View file

@ -0,0 +1,171 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platformWin32/platformWin32.h"
#include "platform/menus/menuBar.h"
#include "platform/menus/popupMenu.h"
#include "gui/core/guiCanvas.h"
#include "windowManager/platformWindowMgr.h"
#include "windowManager/win32/win32Window.h"
#include "core/util/safeDelete.h"
//-----------------------------------------------------------------------------
// Platform Data
//-----------------------------------------------------------------------------
// class PlatformMenuBarData
// {
//
// };
//-----------------------------------------------------------------------------
// MenuBar Methods
//-----------------------------------------------------------------------------
void MenuBar::createPlatformPopupMenuData()
{
// mData = new PlatformMenuBarData;
// [tom, 6/4/2007] Nothing currently needed for win32
mData = NULL;
}
void MenuBar::deletePlatformPopupMenuData()
{
// SAFE_DELETE(mData);
}
//-----------------------------------------------------------------------------
void MenuBar::updateMenuBar(PopupMenu *menu /* = NULL */)
{
if(! isAttachedToCanvas())
return;
if(menu == NULL)
{
// [tom, 6/4/2007] Kludgetastic
GuiCanvas *oldCanvas = mCanvas;
S32 pos = -1;
PopupMenu *mnu = dynamic_cast<PopupMenu *>(at(0));
if(mnu)
pos = mnu->getPosOnMenuBar();
removeFromCanvas();
attachToCanvas(oldCanvas, pos);
return;
}
menu->removeFromMenuBar();
SimSet::iterator itr = find(begin(), end(), menu);
if(itr == end())
return;
menu->attachToMenuBar(mCanvas, itr - begin());
Win32Window *pWindow = dynamic_cast<Win32Window*>(mCanvas->getPlatformWindow());
if(pWindow == NULL)
return;
HWND hWindow = pWindow->getHWND();
DrawMenuBar(hWindow);
}
//-----------------------------------------------------------------------------
void MenuBar::attachToCanvas(GuiCanvas *owner, S32 pos)
{
if(owner == NULL || isAttachedToCanvas())
return;
// This is set for popup menus in the onAttachToMenuBar() callback
mCanvas = owner;
Win32Window *pWindow = dynamic_cast<Win32Window*>(owner->getPlatformWindow());
if(pWindow == NULL)
return;
// Setup the native menu bar
HMENU hWindowMenu = pWindow->getMenuHandle();
if(hWindowMenu == NULL && !Journal::IsPlaying())
{
hWindowMenu = CreateMenu();
if(hWindowMenu)
{
pWindow->setMenuHandle( hWindowMenu);
}
}
// Add the items
for(S32 i = 0;i < size();++i)
{
PopupMenu *mnu = dynamic_cast<PopupMenu *>(at(i));
if(mnu == NULL)
{
Con::warnf("MenuBar::attachToMenuBar - Non-PopupMenu object in set");
continue;
}
if(mnu->isAttachedToMenuBar())
mnu->removeFromMenuBar();
mnu->attachToMenuBar(owner, pos + i);
}
HWND hWindow = pWindow->getHWND();
DrawMenuBar(hWindow);
}
void MenuBar::removeFromCanvas()
{
if(mCanvas == NULL || ! isAttachedToCanvas())
return;
Win32Window *pWindow = dynamic_cast<Win32Window*>(mCanvas->getPlatformWindow());
if(pWindow == NULL)
return;
// Setup the native menu bar
HMENU hWindowMenu = pWindow->getMenuHandle();
if(hWindowMenu == NULL)
return;
// Add the items
for(S32 i = 0;i < size();++i)
{
PopupMenu *mnu = dynamic_cast<PopupMenu *>(at(i));
if(mnu == NULL)
{
Con::warnf("MenuBar::removeFromMenuBar - Non-PopupMenu object in set");
continue;
}
mnu->removeFromMenuBar();
}
HWND hWindow = pWindow->getHWND();
DrawMenuBar(hWindow);
mCanvas = NULL;
}

View file

@ -0,0 +1,742 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/menus/popupMenu.h"
#include "platformWin32/platformWin32.h"
#include "console/consoleTypes.h"
#include "gui/core/guiCanvas.h"
#include "windowManager/platformWindowMgr.h"
#include "windowManager/win32/win32Window.h"
#include "core/util/safeDelete.h"
#include "sim/actionMap.h"
#include "platform/platformInput.h"
//////////////////////////////////////////////////////////////////////////
// Platform Menu Data
//////////////////////////////////////////////////////////////////////////
struct PlatformPopupMenuData
{
static U32 mLastPopupMenuID;
static const U32 PopupMenuIDRange;
HMENU mMenu;
U32 mMenuID;
U32 mLastID;
Win32Window::AcceleratorList mAccelerators;
Win32Window::AcceleratorList mDisabledAccelerators;
PlatformPopupMenuData()
{
mMenu = NULL;
mMenuID = mLastPopupMenuID++;
mLastID = 0;
}
~PlatformPopupMenuData()
{
if(mMenu)
DestroyMenu(mMenu);
}
void insertAccelerator(EventDescriptor &desc, U32 id);
void removeAccelerator(U32 id);
void setAccelleratorEnabled(U32 id, bool enabled);
};
U32 PlatformPopupMenuData::mLastPopupMenuID = 0;
const U32 PlatformPopupMenuData::PopupMenuIDRange = 100;
//////////////////////////////////////////////////////////////////////////
void PlatformPopupMenuData::insertAccelerator(EventDescriptor &desc, U32 id)
{
if(desc.eventType != SI_KEY)
return;
Win32Window::AcceleratorList::iterator i;
for(i = mAccelerators.begin();i != mAccelerators.end();++i)
{
if(i->mID == id)
{
// Update existing entry
i->mDescriptor.eventType = desc.eventType;
i->mDescriptor.eventCode = desc.eventCode;
i->mDescriptor.flags = desc.flags;
return;
}
if(i->mDescriptor.eventType == desc.eventType && i->mDescriptor.eventCode == desc.eventCode && i->mDescriptor.flags == desc.flags)
{
// Already have a matching accelerator, don't add another one
return;
}
}
Win32Window::Accelerator accel;
accel.mDescriptor = desc;
accel.mID = id;
mAccelerators.push_back(accel);
}
void PlatformPopupMenuData::removeAccelerator(U32 id)
{
Win32Window::AcceleratorList::iterator i;
for(i = mAccelerators.begin();i != mAccelerators.end();++i)
{
if(i->mID == id)
{
mAccelerators.erase(i);
return;
}
}
}
void PlatformPopupMenuData::setAccelleratorEnabled( U32 id, bool enabled )
{
Win32Window::AcceleratorList *src = NULL;
Win32Window::AcceleratorList *dst = NULL;
if ( enabled )
{
src = &mDisabledAccelerators;
dst = &mAccelerators;
}
else
{
src = &mAccelerators;
dst = &mDisabledAccelerators;
}
Win32Window::AcceleratorList::iterator i;
for ( i = src->begin(); i != src->end(); ++i )
{
if ( i->mID == id )
{
Win32Window::Accelerator tmp = *i;
src->erase( i );
dst->push_back( tmp );
return;
}
}
}
//////////////////////////////////////////////////////////////////////////
void PopupMenu::createPlatformPopupMenuData()
{
mData = new PlatformPopupMenuData;
}
void PopupMenu::deletePlatformPopupMenuData()
{
SAFE_DELETE(mData);
}
void PopupMenu::createPlatformMenu()
{
mData->mMenu = mIsPopup ? CreatePopupMenu() : CreateMenu();
AssertFatal(mData->mMenu, "Unable to create menu");
MENUINFO mi;
mi.cbSize = sizeof(mi);
mi.fMask = MIM_MENUDATA;
mi.dwMenuData = (ULONG_PTR)this;
SetMenuInfo(mData->mMenu, &mi);
}
//////////////////////////////////////////////////////////////////////////
// Public Methods
//////////////////////////////////////////////////////////////////////////
S32 PopupMenu::insertItem(S32 pos, const char *title, const char* accelerator)
{
Win32Window *pWindow = mCanvas ? dynamic_cast<Win32Window*>(mCanvas->getPlatformWindow()) : NULL;
bool isAttached = isAttachedToMenuBar();
if(isAttached && pWindow == NULL)
return -1;
MENUITEMINFOA mi;
mi.cbSize = sizeof(mi);
mi.fMask = MIIM_ID|MIIM_TYPE;
mi.wID = (mData->mMenuID * PlatformPopupMenuData::PopupMenuIDRange) + mData->mLastID + 1;
mData->mLastID++;
if(title && *title)
mi.fType = MFT_STRING;
else
mi.fType = MFT_SEPARATOR;
char buf[1024];
if(accelerator && *accelerator)
{
dSprintf(buf, sizeof(buf), "%s\t%s", title, accelerator);
if(isAttached)
pWindow->removeAccelerators(mData->mAccelerators);
// Build entry for accelerator table
EventDescriptor accelDesc;
if(ActionMap::createEventDescriptor(accelerator, &accelDesc))
mData->insertAccelerator(accelDesc, mi.wID);
else
Con::errorf("PopupMenu::insertItem - Could not create event descriptor for accelerator \"%s\"", accelerator);
if(isAttached)
pWindow->addAccelerators(mData->mAccelerators);
}
else
dSprintf(buf, sizeof(buf), "%s", title);
mi.dwTypeData = (LPSTR)buf;
if(InsertMenuItemA(mData->mMenu, pos, TRUE, &mi))
{
if(isAttached)
{
HWND hWindow = pWindow->getHWND();
DrawMenuBar(hWindow);
}
return mi.wID;
}
return -1;
}
S32 PopupMenu::insertSubMenu(S32 pos, const char *title, PopupMenu *submenu)
{
Win32Window *pWindow = mCanvas ? dynamic_cast<Win32Window*>(mCanvas->getPlatformWindow()) : NULL;
bool isAttached = isAttachedToMenuBar();
if(isAttached && pWindow == NULL)
return -1;
for(S32 i = 0;i < mSubmenus->size();i++)
{
if(submenu == (*mSubmenus)[i])
{
Con::errorf("PopupMenu::insertSubMenu - Attempting to add submenu twice");
return -1;
}
}
MENUITEMINFOA mi;
mi.cbSize = sizeof(mi);
mi.fMask = MIIM_ID|MIIM_TYPE|MIIM_SUBMENU|MIIM_DATA;
mi.wID = (mData->mMenuID * PlatformPopupMenuData::PopupMenuIDRange) + mData->mLastID + 1;
if(title && *title)
mi.fType = MFT_STRING;
else
mi.fType = MFT_SEPARATOR;
mi.dwTypeData = (LPSTR)title;
mi.hSubMenu = submenu->mData->mMenu;
mi.dwItemData = (ULONG_PTR)submenu;
if(InsertMenuItemA(mData->mMenu, pos, TRUE, &mi))
{
mSubmenus->addObject(submenu);
if(isAttached)
{
pWindow->addAccelerators(submenu->mData->mAccelerators);
HWND hWindow = pWindow->getHWND();
DrawMenuBar(hWindow);
}
return mi.wID;
}
return -1;
}
bool PopupMenu::setItem(S32 pos, const char *title, const char* accelerator)
{
Win32Window *pWindow = mCanvas ? dynamic_cast<Win32Window*>(mCanvas->getPlatformWindow()) : NULL;
bool isAttached = isAttachedToMenuBar();
if(isAttached && pWindow == NULL)
return false;
// Are we out of range?
if ( pos >= getItemCount() )
return false;
MENUITEMINFOA mi;
mi.cbSize = sizeof(mi);
mi.fMask = MIIM_TYPE;
if(title && *title)
mi.fType = MFT_STRING;
else
mi.fType = MFT_SEPARATOR;
char buf[1024];
if(accelerator && *accelerator)
{
dSprintf(buf, sizeof(buf), "%s\t%s", title, accelerator);
if(isAttached)
pWindow->removeAccelerators(mData->mAccelerators);
// Build entry for accelerator table
EventDescriptor accelDesc;
if(ActionMap::createEventDescriptor(accelerator, &accelDesc))
mData->insertAccelerator(accelDesc, pos);
else
Con::errorf("PopupMenu::setItem - Could not create event descriptor for accelerator \"%s\"", accelerator);
if(isAttached)
pWindow->addAccelerators(mData->mAccelerators);
}
else
dSprintf(buf, sizeof(buf), "%s", title);
mi.dwTypeData = (LPSTR)buf;
if(SetMenuItemInfoA(mData->mMenu, pos, TRUE, &mi))
{
if(isAttached)
{
HWND hWindow = pWindow->getHWND();
DrawMenuBar(hWindow);
}
return true;
}
return false;
}
void PopupMenu::removeItem(S32 itemPos)
{
Win32Window *pWindow = mCanvas ? dynamic_cast<Win32Window*>(mCanvas->getPlatformWindow()) : NULL;
bool isAttached = isAttachedToMenuBar();
if(isAttached && pWindow == NULL)
return;
MENUITEMINFOA mi;
mi.cbSize = sizeof(mi);
mi.fMask = MIIM_DATA|MIIM_ID;
if(GetMenuItemInfoA(mData->mMenu, itemPos, TRUE, &mi))
{
bool submenu = false;
// Update list of submenus if this is a submenu
if(mi.fMask & MIIM_DATA)
{
PopupMenu *mnu = (PopupMenu *)mi.dwItemData;
if( mnu != NULL )
{
if(isAttached)
pWindow->removeAccelerators(mnu->mData->mAccelerators);
mSubmenus->removeObject(mnu);
submenu = true;
}
}
if(! submenu)
{
// Update accelerators if this has an accelerator and wasn't a sub menu
for(S32 i = 0;i < mData->mAccelerators.size();++i)
{
if(mData->mAccelerators[i].mID == mi.wID)
{
if(isAttached)
pWindow->removeAccelerators(mData->mAccelerators);
mData->mAccelerators.erase(i);
if(isAttached)
pWindow->addAccelerators(mData->mAccelerators);
break;
}
}
}
}
else
return;
RemoveMenu(mData->mMenu, itemPos, MF_BYPOSITION);
if(isAttached)
{
HWND hWindow = pWindow->getHWND();
DrawMenuBar(hWindow);
}
}
//////////////////////////////////////////////////////////////////////////
void PopupMenu::enableItem( S32 pos, bool enable )
{
U32 flags = enable ? MF_ENABLED : MF_GRAYED;
EnableMenuItem( mData->mMenu, pos, MF_BYPOSITION|flags );
// Update accelerators.
// NOTE: This really DOES need to happen. A disabled menu item
// should not still have an accelerator mapped to it.
//
// Unfortunately, the editors currently only set menu items
// enabled/disabled when the menu itself is selected which means our
// accelerators would be out of synch.
/*
Win32Window *pWindow = mCanvas ? dynamic_cast<Win32Window*>( mCanvas->getPlatformWindow() ) : NULL;
bool isAttached = isAttachedToMenuBar();
if ( isAttached && pWindow == NULL )
return;
MENUITEMINFOA mi;
mi.cbSize = sizeof(mi);
mi.fMask = MIIM_DATA|MIIM_ID;
if ( !GetMenuItemInfoA( mData->mMenu, pos, TRUE, &mi) )
return;
if ( isAttached )
pWindow->removeAccelerators( mData->mAccelerators );
mData->setAccelleratorEnabled( mi.wID, enable );
if ( isAttached )
pWindow->addAccelerators( mData->mAccelerators );
*/
}
void PopupMenu::checkItem(S32 pos, bool checked)
{
// U32 flags = checked ? MF_CHECKED : MF_UNCHECKED;
// CheckMenuItem(mData->mMenu, pos, MF_BYPOSITION|flags);
MENUITEMINFOA mi;
mi.cbSize = sizeof(mi);
mi.fMask = MIIM_STATE;
mi.fState = checked ? MFS_CHECKED : MFS_UNCHECKED;
SetMenuItemInfoA(mData->mMenu, pos, TRUE, &mi);
}
void PopupMenu::checkRadioItem(S32 firstPos, S32 lastPos, S32 checkPos)
{
CheckMenuRadioItem(mData->mMenu, firstPos, lastPos, checkPos, MF_BYPOSITION);
}
bool PopupMenu::isItemChecked(S32 pos)
{
MENUITEMINFOA mi;
mi.cbSize = sizeof(mi);
mi.fMask = MIIM_STATE;
if(GetMenuItemInfoA(mData->mMenu, pos, TRUE, &mi) && (mi.fState & MFS_CHECKED))
return true;
return false;
}
U32 PopupMenu::getItemCount()
{
return GetMenuItemCount( mData->mMenu );
}
//////////////////////////////////////////////////////////////////////////
bool PopupMenu::canHandleID(U32 id)
{
for(S32 i = 0;i < mSubmenus->size();i++)
{
PopupMenu *subM = dynamic_cast<PopupMenu *>((*mSubmenus)[i]);
if(subM == NULL)
continue;
if(subM->canHandleID(id))
return true;
}
if(id >= mData->mMenuID * PlatformPopupMenuData::PopupMenuIDRange &&
id < (mData->mMenuID+1) * PlatformPopupMenuData::PopupMenuIDRange)
{
return true;
}
return false;
}
bool PopupMenu::handleSelect(U32 command, const char *text /* = NULL */)
{
// [tom, 8/20/2006] Pass off to a sub menu if it's for them
for(S32 i = 0;i < mSubmenus->size();i++)
{
PopupMenu *subM = dynamic_cast<PopupMenu *>((*mSubmenus)[i]);
if(subM == NULL)
continue;
if(subM->canHandleID(command))
{
return subM->handleSelect(command, text);
}
}
// [tom, 8/21/2006] Cheesey hack to find the position based on ID
char buf[512];
MENUITEMINFOA mi;
mi.cbSize = sizeof(mi);
mi.dwTypeData = NULL;
S32 numItems = GetMenuItemCount(mData->mMenu);
S32 pos = -1;
for(S32 i = 0;i < numItems;i++)
{
mi.fMask = MIIM_ID|MIIM_STRING|MIIM_STATE;
if(GetMenuItemInfoA(mData->mMenu, i, TRUE, &mi))
{
if(mi.wID == command)
{
if(text == NULL)
{
mi.dwTypeData = buf;
mi.cch++;
GetMenuItemInfoA(mData->mMenu, i, TRUE, &mi);
// [tom, 5/11/2007] Don't do anything if the menu item is disabled
if(mi.fState & MFS_DISABLED)
return false;
text = StringTable->insert(mi.dwTypeData);
}
pos = i;
break;
}
}
}
if(pos == -1)
{
Con::errorf("PopupMenu::handleSelect - Could not find menu item position for ID %d ... this shouldn't happen!", command);
return false;
}
// [tom, 8/20/2006] Wasn't handled by a submenu, pass off to script
return dAtob(Con::executef(this, "onSelectItem", Con::getIntArg(pos), text ? text : ""));
}
//////////////////////////////////////////////////////////////////////////
void PopupMenu::showPopup(GuiCanvas *owner, S32 x /* = -1 */, S32 y /* = -1 */)
{
if( owner == NULL )
{
Con::warnf("PopupMenu::showPopup - Invalid canvas supplied!");
return;
}
// [tom, 6/4/2007] showPopup() blocks until the menu is closed by the user,
// so the canvas pointer is not needed beyond the scope of this function
// when working with context menus. Setting mCanvas here will cause undesired
// behavior in relation to the menu bar.
Win32Window *pWindow = dynamic_cast<Win32Window*>(owner->getPlatformWindow());
if(pWindow == NULL)
return;
HWND hWindow = pWindow->getHWND();
POINT p;
if(x == -1 && y == -1)
GetCursorPos(&p);
else
{
p.x = x;
p.y = y;
ClientToScreen(hWindow, &p);
}
winState.renderThreadBlocked = true;
U32 opt = (int)TrackPopupMenu(mData->mMenu, TPM_NONOTIFY|TPM_RETURNCMD, p.x, p.y, 0, hWindow, NULL);
if(opt > 0)
handleSelect(opt, NULL);
winState.renderThreadBlocked = false;
}
//////////////////////////////////////////////////////////////////////////
void PopupMenu::attachToMenuBar(GuiCanvas *owner, S32 pos, const char *title)
{
if(owner == NULL || isAttachedToMenuBar())
return;
// This is set for sub-menus in the onAttachToMenuBar() callback
mCanvas = owner;
Win32Window *pWindow = dynamic_cast<Win32Window*>(owner->getPlatformWindow());
if(pWindow == NULL)
return;
HMENU hWindowMenu = pWindow->getMenuHandle();
if(hWindowMenu == NULL)
{
hWindowMenu = CreateMenu();
if(hWindowMenu)
{
pWindow->setMenuHandle( hWindowMenu);
}
}
MENUITEMINFOA mii;
mii.cbSize = sizeof(MENUITEMINFOA);
mii.fMask = MIIM_STRING|MIIM_DATA;
mii.dwTypeData = (LPSTR)title;
mii.fMask |= MIIM_ID;
mii.wID = mData->mMenuID;
mii.fMask |= MIIM_SUBMENU;
mii.hSubMenu = mData->mMenu;
mii.dwItemData = (ULONG_PTR)this;
InsertMenuItemA(hWindowMenu, pos, TRUE, &mii);
HWND hWindow = pWindow->getHWND();
DrawMenuBar(hWindow);
pWindow->addAccelerators(mData->mAccelerators);
// Add accelerators for sub menus
for(SimSet::iterator i = mSubmenus->begin();i != mSubmenus->end();++i)
{
PopupMenu *submenu = dynamic_cast<PopupMenu *>(*i);
if(submenu == NULL)
continue;
pWindow->addAccelerators(submenu->mData->mAccelerators);
}
onAttachToMenuBar(owner, pos, title);
}
// New version of above for use by MenuBar class. Do not use yet.
void PopupMenu::attachToMenuBar(GuiCanvas *owner, S32 pos)
{
Win32Window *pWindow = dynamic_cast<Win32Window*>(owner->getPlatformWindow());
if(pWindow == NULL)
return;
//When playing a journal, the system menu is not actually shown
if (Journal::IsPlaying())
{
onAttachToMenuBar(owner, pos, mBarTitle);
return;
}
HMENU hWindowMenu = pWindow->getMenuHandle();
MENUITEMINFOA mii;
mii.cbSize = sizeof(MENUITEMINFOA);
mii.fMask = MIIM_STRING|MIIM_DATA;
mii.dwTypeData = (LPSTR)mBarTitle;
mii.fMask |= MIIM_ID;
mii.wID = mData->mMenuID;
mii.fMask |= MIIM_SUBMENU;
mii.hSubMenu = mData->mMenu;
mii.dwItemData = (ULONG_PTR)this;
InsertMenuItemA(hWindowMenu, pos, TRUE, &mii);
pWindow->addAccelerators(mData->mAccelerators);
// Add accelerators for sub menus (have to do this here as it's platform specific)
for(SimSet::iterator i = mSubmenus->begin();i != mSubmenus->end();++i)
{
PopupMenu *submenu = dynamic_cast<PopupMenu *>(*i);
if(submenu == NULL)
continue;
pWindow->addAccelerators(submenu->mData->mAccelerators);
}
onAttachToMenuBar(owner, pos, mBarTitle);
}
void PopupMenu::removeFromMenuBar()
{
S32 pos = getPosOnMenuBar();
if(pos == -1)
return;
Win32Window *pWindow = mCanvas ? dynamic_cast<Win32Window*>(mCanvas->getPlatformWindow()) : NULL;
if(pWindow == NULL)
return;
HMENU hMenuHandle = pWindow->getMenuHandle();
if(!hMenuHandle)
return;
RemoveMenu(hMenuHandle, pos, MF_BYPOSITION);
HWND hWindow = pWindow->getHWND();
DrawMenuBar(hWindow);
pWindow->removeAccelerators(mData->mAccelerators);
// Remove accelerators for sub menus
for(SimSet::iterator i = mSubmenus->begin();i != mSubmenus->end();++i)
{
PopupMenu *submenu = dynamic_cast<PopupMenu *>(*i);
if(submenu == NULL)
continue;
pWindow->removeAccelerators(submenu->mData->mAccelerators);
}
onRemoveFromMenuBar(mCanvas);
}
S32 PopupMenu::getPosOnMenuBar()
{
if(mCanvas == NULL)
return -1;
Win32Window *pWindow = mCanvas ? dynamic_cast<Win32Window*>(mCanvas->getPlatformWindow()) : NULL;
if(pWindow == NULL)
return -1;
HMENU hMenuHandle = pWindow->getMenuHandle();
S32 numItems = GetMenuItemCount(hMenuHandle);
S32 pos = -1;
for(S32 i = 0;i < numItems;i++)
{
MENUITEMINFOA mi;
mi.cbSize = sizeof(mi);
mi.fMask = MIIM_DATA;
if(GetMenuItemInfoA(hMenuHandle, i, TRUE, &mi))
{
if(mi.fMask & MIIM_DATA)
{
PopupMenu *mnu = (PopupMenu *)mi.dwItemData;
if(mnu == this)
{
pos = i;
break;
}
}
}
}
return pos;
}

View file

@ -0,0 +1,337 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE )
#include "platformWin32/platformWin32.h"
#include "platformWin32/minidump/winStackWalker.h"
#include "core/fileio.h"
#include "core/strings/stringFunctions.h"
#include "console/console.h"
#include "app/net/serverQuery.h"
#pragma pack(push,8)
#include <DbgHelp.h>
#include <time.h>
#pragma pack(pop)
#pragma comment(lib, "dbghelp.lib")
extern Win32PlatState winState;
//Forward declarations for the dialog functions
BOOL CALLBACK MiniDumpDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT DisplayMiniDumpDialog(HINSTANCE hinst, HWND hwndOwner);
LPWORD lpwAlign(LPWORD lpIn);
char gUserInput[4096];
//Console variables
extern StringTableEntry gMiniDumpDir;
extern StringTableEntry gMiniDumpUser;
extern StringTableEntry gMiniDumpExec;
extern StringTableEntry gMiniDumpParams;
extern StringTableEntry gMiniDumpExecDir;
char* dStrrstr(char* dst, const char* src, const char* findStr, char* replaceStr)
{
//see if str contains findStr, if not then return
const char* findpos = strstr(src, findStr);
if(!findpos)
{
strcpy(dst, src);
}
else
{
//copy the new string to the buffer
dst[0]='\0';
strncat(dst, src, findpos-src);
strcat(dst, replaceStr);
const char* cur = findpos + strlen(findStr);
strcat(dst, cur);
}
return dst;
}
//-----------------------------------------------------------------------------------------------------------------------------------------
// CreateMiniDump()
//-----------------------------------------------------------------------------------------------------------------------------------------
INT CreateMiniDump( LPEXCEPTION_POINTERS ExceptionInfo)
{
//Get any information we can from the user and store it in gUserInput
try
{
while(ShowCursor(TRUE) < 0);
DisplayMiniDumpDialog(winState.appInstance, winState.appWindow);
}
catch(...)
{
//dSprintf(gUserInput, 4096, "The user could not enter a description of what was occurring.\n\n\n");
}
//Build a Game, Date and Time stamped MiniDump folder
time_t theTime;
time(&theTime);
tm* pLocalTime = localtime(&theTime);
char crashFolder[2048];
dSprintf(crashFolder, 2048, "%s_%02d.%02d_%02d.%02d.%02d",
Platform::getExecutableName(),
pLocalTime->tm_mon+1, pLocalTime->tm_mday,
pLocalTime->tm_hour, pLocalTime->tm_min, pLocalTime->tm_sec);
//Builed the fully qualified MiniDump path
char crashPath[2048];
char fileName[2048];
if(gMiniDumpDir==NULL)
{
dSprintf(crashPath, 2048, "%s/MiniDump/%s", Platform::getCurrentDirectory(), crashFolder);
}
else
{
dSprintf(crashPath, 2048, "%s/%s", gMiniDumpDir, crashFolder);
}
dSprintf(fileName, 2048, "%s/Minidump.dmp",crashPath);
if (!Platform::createPath (fileName))return false; //create the directory
//Save the minidump
File fileObject;
if(fileObject.open(fileName, File::Write) == File::Ok)
{
MINIDUMP_EXCEPTION_INFORMATION DumpExceptionInfo;
DumpExceptionInfo.ThreadId = GetCurrentThreadId();
DumpExceptionInfo.ExceptionPointers = ExceptionInfo;
DumpExceptionInfo.ClientPointers = true;
MiniDumpWriteDump( GetCurrentProcess(), GetCurrentProcessId(), (HANDLE)fileObject.getHandle(), MiniDumpNormal, &DumpExceptionInfo, NULL, NULL );
fileObject.close();
}
//copy over the log file
char fromFile[2048];
dSprintf(fromFile, 2048, "%s/%s", Platform::getCurrentDirectory(), "console.log" );
dSprintf(fileName, 2048, "%s/console.log", crashPath);
Con::setLogMode(3); //ensure that the log file is closed (so it can be copied)
dPathCopy(fromFile, fileName, true);
//copy over the exe file
char exeName[1024];
dSprintf(exeName, 1024, Platform::getExecutableName());
exeName[dStrlen(exeName)-4]=0;
dSprintf(fromFile, 2048, "%s/%s.dll", Platform::getCurrentDirectory(), exeName );
dSprintf(fileName, 2048, "%s/%s.dll", crashPath, exeName );
dPathCopy(fromFile, fileName, true);
//copy over the pdb file
char pdbName[1024];
dStrcpy(pdbName, exeName);
dStrncat(pdbName, ".pdb", 4);
dSprintf(fromFile, 2048, "%s/%s", Platform::getCurrentDirectory(), pdbName );
dSprintf(fileName, 2048, "%s/%s", crashPath, pdbName );
dPathCopy(fromFile, fileName, true);
//save the call stack
char traceBuffer[65536];
traceBuffer[0] = 0;
dGetStackTrace( traceBuffer, *static_cast<const CONTEXT *>(ExceptionInfo->ContextRecord) );
//save the user input and the call stack to a file
char crashlogFile[2048];
dSprintf(crashlogFile, 2048, "%s/crash.log", crashPath);
if(fileObject.open(crashlogFile, File::Write) == File::Ok)
{
fileObject.write(strlen(gUserInput), gUserInput);
fileObject.write(strlen(traceBuffer), traceBuffer);
fileObject.close();
}
//call the external program indicated in script
if(gMiniDumpExec!= NULL)
{
//replace special variables in gMiniDumpParams
if(gMiniDumpParams)
{
char updateParams[4096];
char finalParams[4096];
dStrrstr(finalParams, gMiniDumpParams, "%crashpath%", crashPath);
dStrrstr(updateParams, finalParams, "%crashfolder%", crashFolder);
dStrrstr(finalParams, updateParams, "%crashlog%", crashlogFile);
ShellExecuteA(NULL, "", gMiniDumpExec, finalParams, gMiniDumpExecDir ? gMiniDumpExecDir : "", SW_SHOWNORMAL);
}
else
{
ShellExecuteA(NULL, "", gMiniDumpExec, "", gMiniDumpExecDir ? gMiniDumpExecDir : "", SW_SHOWNORMAL);
}
}
return EXCEPTION_EXECUTE_HANDLER;
}
//-----------------------------------------------------------------------------------------------------------------------------------------
// MiniDumpDialogProc - Used By DisplayMiniDumpDialog
//-----------------------------------------------------------------------------------------------------------------------------------------
const int ID_TEXT=200;
const int ID_USERTEXT=300;
const int ID_DONE=400;
BOOL CALLBACK MiniDumpDialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
char text[128]= "";
switch (message)
{
case WM_INITDIALOG :
SetDlgItemTextA ( hwndDlg, ID_USERTEXT, text );
return TRUE ;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case ID_DONE:
if( !GetDlgItemTextA(hwndDlg, ID_USERTEXT, gUserInput, 4096) ) gUserInput[0]='\0';
strcat(gUserInput, "\n\n\n");
EndDialog(hwndDlg, wParam);
return TRUE;
default:
return TRUE;
}
}
return FALSE;
}
//-----------------------------------------------------------------------------------------------------------------------------------------
// Helper function to DWORD align the Dialog Box components (Used in DisplayMiniDumpDialog()
//-----------------------------------------------------------------------------------------------------------------------------------------
LPWORD lpwAlign(LPWORD lpIn)
{
ULONG ul;
ul = (ULONG)lpIn;
ul ++;
ul >>=1;
ul <<=1;
return (LPWORD)ul;
}
//-----------------------------------------------------------------------------------------------------------------------------------------
// Create the Dialog Box to get input from the user
//-----------------------------------------------------------------------------------------------------------------------------------------
LRESULT DisplayMiniDumpDialog(HINSTANCE hinst, HWND hwndOwner)
{
HGLOBAL hgbl = GlobalAlloc(GMEM_ZEROINIT, 1024);
if (!hgbl) return -1;
//-----------------------------------------------------------------
// Define the dialog box
//-----------------------------------------------------------------
LPDLGTEMPLATE lpdt = (LPDLGTEMPLATE)GlobalLock(hgbl);
lpdt->style = WS_POPUP | WS_BORDER | DS_MODALFRAME | WS_CAPTION;
lpdt->cdit = 3; // Number of controls
lpdt->x = 100;
lpdt->y = 100;
lpdt->cx = 300;
lpdt->cy = 90;
LPWORD lpw = (LPWORD)(lpdt + 1);
*lpw++ = 0; // No menu
*lpw++ = 0; // Predefined dialog box class (by default)
LPWSTR lpwsz = (LPWSTR)lpw;
int nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "MiniDump Crash Report", -1, lpwsz, 50);
lpw += nchar;
//-----------------------------------------------------------------
// Define a static text message
//-----------------------------------------------------------------
lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
LPDLGITEMTEMPLATE lpdit = (LPDLGITEMTEMPLATE)lpw;
lpdit->x = 10;
lpdit->y = 10;
lpdit->cx = 290;
lpdit->cy = 10;
lpdit->id = ID_TEXT; // Text identifier
lpdit->style = WS_CHILD | WS_VISIBLE | SS_LEFT;
lpw = (LPWORD)(lpdit + 1);
*lpw++ = 0xFFFF;
*lpw++ = 0x0082; // Static class
LPSTR msg = "The program has crashed. Please describe what was happening:";
for (lpwsz = (LPWSTR)lpw; *lpwsz++ = (WCHAR)*msg++;);
lpw = (LPWORD)lpwsz;
*lpw++ = 0; // No creation data
//-----------------------------------------------------------------
// Define a DONE button
//-----------------------------------------------------------------
lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
lpdit = (LPDLGITEMTEMPLATE)lpw;
lpdit->x = 265;
lpdit->y = 75;
lpdit->cx = 25;
lpdit->cy = 12;
lpdit->id = ID_DONE; // OK button identifier
lpdit->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;// | BS_DEFPUSHBUTTON;
lpw = (LPWORD)(lpdit + 1);
*lpw++ = 0xFFFF;
*lpw++ = 0x0080; // Button class
lpwsz = (LPWSTR)lpw;
nchar = 1 + MultiByteToWideChar(CP_ACP, 0, "Done", -1, lpwsz, 50);
lpw += nchar;
*lpw++ = 0; // No creation data
//-----------------------------------------------------------------
// Define a text entry message
//-----------------------------------------------------------------
lpw = lpwAlign(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
lpdit = (LPDLGITEMTEMPLATE)lpw;
lpdit->x = 10;
lpdit->y = 22;
lpdit->cx = 280;
lpdit->cy = 50;
lpdit->id = ID_USERTEXT; // Text identifier
lpdit->style = ES_LEFT | WS_BORDER | WS_TABSTOP | WS_CHILD | WS_VISIBLE;
lpw = (LPWORD)(lpdit + 1);
*lpw++ = 0xFFFF;
*lpw++ = 0x0081; // Text edit class
*lpw++ = 0; // No creation data
GlobalUnlock(hgbl);
LRESULT ret = DialogBoxIndirect( hinst,
(LPDLGTEMPLATE)hgbl,
hwndOwner,
(DLGPROC)MiniDumpDialogProc);
GlobalFree(hgbl);
return ret;
}
#endif

View file

@ -0,0 +1,825 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE )
//-----------------------------------------------------------------------------------------------------------------------------------------
// Sourced from http://www.codeproject.com/threads/StackWalker.asp
//-----------------------------------------------------------------------------------------------------------------------------------------
#include "winStackWalker.h"
#undef UNICODE
#include <tchar.h>
#include <Tlhelp32.h>
#include <stdio.h>
#include <psapi.h>
#pragma comment(lib, "version.lib") // for "VerQueryValue"
#pragma comment(lib, "dbghelp.lib")
#pragma comment(lib, "psapi.lib")
// secure-CRT_functions are only available starting with VC8
#if _MSC_VER < 1400
#define strcpy_s strcpy
#define strcat_s(dst, len, src) strcat(dst, src)
#define _snprintf_s _snprintf
#define _tcscat_s _tcscat
#endif
// Entry for each Callstack-Entry
const int STACKWALK_MAX_NAMELEN = 1024; // max name length for symbols
struct CallstackEntry
{
DWORD64 offset; // if 0, we have no valid entry
char name[STACKWALK_MAX_NAMELEN];
char undName[STACKWALK_MAX_NAMELEN];
char undFullName[STACKWALK_MAX_NAMELEN];
DWORD64 offsetFromSmybol;
DWORD64 offsetFromLine;
DWORD lineNumber;
char lineFileName[STACKWALK_MAX_NAMELEN];
DWORD symType;
const char * symTypeString;
char moduleName[STACKWALK_MAX_NAMELEN];
DWORD64 baseOfImage;
char loadedImageName[STACKWALK_MAX_NAMELEN];
};
//-----------------------------------------------------------------------------------------------------------------------------------------
// Implementation of platform functions
//-----------------------------------------------------------------------------------------------------------------------------------------
void dGetStackTrace(char * traceBuffer, CONTEXT const & ContextRecord)
{
StackWalker sw;
sw.setOutputBuffer(traceBuffer);
sw.ShowCallstack(GetCurrentThread(), ContextRecord);
sw.setOutputBuffer(NULL);
}
//-----------------------------------------------------------------------------------------------------------------------------------------
//Constructor
//-----------------------------------------------------------------------------------------------------------------------------------------
StackWalker::StackWalker(DWORD options, LPCSTR szSymPath)
: m_dwProcessId(GetCurrentProcessId()),
m_hProcess(GetCurrentProcess()),
m_options(options),
m_modulesLoaded(false),
m_pOutputBuffer(NULL)
{
if (szSymPath != NULL)
{
m_szSymPath = _strdup(szSymPath);
m_options |= SymBuildPath;
}
else
m_szSymPath = NULL;
}
//-----------------------------------------------------------------------------------------------------------------------------------------
//Destructor
//-----------------------------------------------------------------------------------------------------------------------------------------
StackWalker::~StackWalker()
{
SymCleanup(m_hProcess);
if (m_szSymPath != NULL) free(m_szSymPath);
m_szSymPath = NULL;
}
//-----------------------------------------------------------------------------------------------------------------------------------------
//setOutputBuffer - Points the StackWalker at a buffer where it will write out any output. If this is set to NULL then the output
// will only be sent to DebugString
//-----------------------------------------------------------------------------------------------------------------------------------------
void StackWalker::setOutputBuffer(char * buffer)
{
m_pOutputBuffer = buffer;
}
//-----------------------------------------------------------------------------------------------------------------------------------------
//OnOutput
//-----------------------------------------------------------------------------------------------------------------------------------------
void StackWalker::OnOutput(LPCSTR buffer)
{
OutputDebugStringA(buffer);
if(m_pOutputBuffer) strcat(m_pOutputBuffer, buffer);
}
//-----------------------------------------------------------------------------------------------------------------------------------------
//Init
//-----------------------------------------------------------------------------------------------------------------------------------------
bool StackWalker::Init(LPCSTR szSymPath)
{
// SymInitialize
if (szSymPath != NULL) m_szSymPath = _strdup(szSymPath);
if (SymInitialize(m_hProcess, m_szSymPath, FALSE) == FALSE)
{
this->OnDbgHelpErr("SymInitialize", GetLastError(), 0);
}
DWORD symOptions = SymGetOptions();
symOptions |= SYMOPT_LOAD_LINES;
symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS;
symOptions = SymSetOptions(symOptions);
char buf[STACKWALK_MAX_NAMELEN] = {0};
if(SymGetSearchPath(m_hProcess, buf, STACKWALK_MAX_NAMELEN) == FALSE)
{
this->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0);
}
char szUserName[1024] = {0};
DWORD dwSize = 1024;
GetUserNameA(szUserName, &dwSize);
this->OnSymInit(buf, symOptions, szUserName);
return TRUE;
}
//-----------------------------------------------------------------------------------------------------------------------------------------
//LoadModules
//-----------------------------------------------------------------------------------------------------------------------------------------
bool StackWalker::LoadModules()
{
if (m_modulesLoaded) return true;
// Build the sym-path:
char *szSymPath = NULL;
if ( (m_options & SymBuildPath) != 0)
{
const size_t nSymPathLen = 4096;
szSymPath = (char*) malloc(nSymPathLen);
if (szSymPath == NULL)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return false;
}
szSymPath[0] = 0;
// Now first add the (optional) provided sympath:
if (m_szSymPath != NULL)
{
strcat_s(szSymPath, nSymPathLen, m_szSymPath);
strcat_s(szSymPath, nSymPathLen, ";");
}
strcat_s(szSymPath, nSymPathLen, ".;");
const size_t nTempLen = 1024;
CHAR szTemp[nTempLen];
// Now add the current directory:
if (GetCurrentDirectoryA(nTempLen, szTemp) > 0)
{
szTemp[nTempLen-1] = 0;
strcat_s(szSymPath, nSymPathLen, szTemp);
strcat_s(szSymPath, nSymPathLen, ";");
}
// Now add the path for the main-module:
if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0)
{
szTemp[nTempLen-1] = 0;
for (char *p = (szTemp+strlen(szTemp)-1); p >= szTemp; --p)
{
// locate the rightmost path separator
if ( (*p == '\\') || (*p == '/') || (*p == ':') )
{
*p = 0;
break;
}
} // for (search for path separator...)
if (strlen(szTemp) > 0)
{
strcat_s(szSymPath, nSymPathLen, szTemp);
strcat_s(szSymPath, nSymPathLen, ";");
}
}
if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0)
{
szTemp[nTempLen-1] = 0;
strcat_s(szSymPath, nSymPathLen, szTemp);
strcat_s(szSymPath, nSymPathLen, ";");
}
if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0)
{
szTemp[nTempLen-1] = 0;
strcat_s(szSymPath, nSymPathLen, szTemp);
strcat_s(szSymPath, nSymPathLen, ";");
}
if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0)
{
szTemp[nTempLen-1] = 0;
strcat_s(szSymPath, nSymPathLen, szTemp);
strcat_s(szSymPath, nSymPathLen, ";");
// also add the "system32"-directory:
strcat_s(szTemp, nTempLen, "\\system32");
strcat_s(szSymPath, nSymPathLen, szTemp);
strcat_s(szSymPath, nSymPathLen, ";");
}
if ( (m_options & SymBuildPath) != 0 )
{
if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0)
{
szTemp[nTempLen-1] = 0;
strcat_s(szSymPath, nSymPathLen, "SRV*");
strcat_s(szSymPath, nSymPathLen, szTemp);
strcat_s(szSymPath, nSymPathLen, "\\websymbols");
strcat_s(szSymPath, nSymPathLen, "*http://msdl.microsoft.com/download/symbols;");
}
else
strcat_s(szSymPath, nSymPathLen, "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;");
}
}
// First Init the whole stuff...
bool bRet = Init(szSymPath);
if (szSymPath != NULL)
{
free(szSymPath);
szSymPath = NULL;
}
if (bRet == false)
{
this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0);
SetLastError(ERROR_DLL_INIT_FAILED);
return false;
}
if(GetModuleListTH32(m_hProcess, m_dwProcessId))
{
m_modulesLoaded = true;
return true;
}
// then try psapi
if(GetModuleListPSAPI(m_hProcess))
{
m_modulesLoaded = true;
return true;
}
return false;
}
//-----------------------------------------------------------------------------------------------------------------------------------------
//LoadModule
//-----------------------------------------------------------------------------------------------------------------------------------------
DWORD StackWalker::LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size)
{
CHAR *szImg = _strdup(img);
CHAR *szMod = _strdup(mod);
DWORD result = ERROR_SUCCESS;
if ( (szImg == NULL) || (szMod == NULL) )
result = ERROR_NOT_ENOUGH_MEMORY;
else
{
if (SymLoadModule64(hProcess, 0, szImg, szMod, baseAddr, size) == 0)
result = GetLastError();
}
ULONGLONG fileVersion = 0;
if(szImg != NULL)
{
// try to retrieve the file-version:
if ( (m_options & StackWalker::RetrieveFileVersion) != 0)
{
VS_FIXEDFILEINFO *fInfo = NULL;
DWORD dwHandle;
DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle);
if (dwSize > 0)
{
LPVOID vData = malloc(dwSize);
if (vData != NULL)
{
if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0)
{
UINT len;
char szSubBlock[] = _T("\\");
if (VerQueryValueA(vData, szSubBlock, (LPVOID*) &fInfo, &len) == 0)
fInfo = NULL;
else
{
fileVersion = ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32);
}
}
free(vData);
}
}
}
// Retrive some additional-infos about the module
IMAGEHLP_MODULE64 Module;
const char *szSymType = "-unknown-";
if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE)
{
switch(Module.SymType)
{
case SymNone:
szSymType = "-nosymbols-";
break;
case SymCoff:
szSymType = "COFF";
break;
case SymCv:
szSymType = "CV";
break;
case SymPdb:
szSymType = "PDB";
break;
case SymExport:
szSymType = "-exported-";
break;
case SymDeferred:
szSymType = "-deferred-";
break;
case SymSym:
szSymType = "SYM";
break;
case 8: //SymVirtual:
szSymType = "Virtual";
break;
case 9: // SymDia:
szSymType = "DIA";
break;
}
}
this->OnLoadModule(img, mod, baseAddr, size, result, szSymType, Module.LoadedImageName, fileVersion);
}
if (szImg != NULL) free(szImg);
if (szMod != NULL) free(szMod);
return result;
}
// The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction
// This has to be done due to a problem with the "hProcess"-parameter in x64...
// Because this class is in no case multi-threading-enabled (because of the limitations of dbghelp.dll) it is "safe" to use a static-variable
static StackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = NULL;
static LPVOID s_readMemoryFunction_UserData = NULL;
//-----------------------------------------------------------------------------------------------------------------------------------------
//ShowCallstack
//-----------------------------------------------------------------------------------------------------------------------------------------
bool StackWalker::ShowCallstack(HANDLE hThread, CONTEXT const & context, PReadProcessMemoryRoutine readMemoryFunction, LPVOID pUserData)
{
CONTEXT c = context;
IMAGEHLP_SYMBOL64 *pSym = NULL;
IMAGEHLP_MODULE64 Module;
IMAGEHLP_LINE64 Line;
int frameNum;
if (!m_modulesLoaded) LoadModules();
s_readMemoryFunction = readMemoryFunction;
s_readMemoryFunction_UserData = pUserData;
// init STACKFRAME for first call
STACKFRAME64 s; // in/out stackframe
memset(&s, 0, sizeof(s));
DWORD imageType;
#ifdef _M_IX86
// normally, call ImageNtHeader() and use machine info from PE header
imageType = IMAGE_FILE_MACHINE_I386;
s.AddrPC.Offset = c.Eip;
s.AddrPC.Mode = AddrModeFlat;
s.AddrFrame.Offset = c.Ebp;
s.AddrFrame.Mode = AddrModeFlat;
s.AddrStack.Offset = c.Esp;
s.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
imageType = IMAGE_FILE_MACHINE_AMD64;
s.AddrPC.Offset = c.Rip;
s.AddrPC.Mode = AddrModeFlat;
s.AddrFrame.Offset = c.Rsp;
s.AddrFrame.Mode = AddrModeFlat;
s.AddrStack.Offset = c.Rsp;
s.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
imageType = IMAGE_FILE_MACHINE_IA64;
s.AddrPC.Offset = c.StIIP;
s.AddrPC.Mode = AddrModeFlat;
s.AddrFrame.Offset = c.IntSp;
s.AddrFrame.Mode = AddrModeFlat;
s.AddrBStore.Offset = c.RsBSP;
s.AddrBStore.Mode = AddrModeFlat;
s.AddrStack.Offset = c.IntSp;
s.AddrStack.Mode = AddrModeFlat;
#else
#error "Platform not supported!"
#endif
pSym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
if (!pSym) goto cleanup; // not enough memory...
memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN);
pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
pSym->MaxNameLength = STACKWALK_MAX_NAMELEN;
memset(&Line, 0, sizeof(Line));
Line.SizeOfStruct = sizeof(Line);
memset(&Module, 0, sizeof(Module));
Module.SizeOfStruct = sizeof(Module);
for (frameNum = 0; ; ++frameNum )
{
// get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64())
// if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can
// assume that either you are done, or that the stack is so hosed that the next
// deeper frame could not be found.
// CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386!
if ( ! StackWalk64(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, SymFunctionTableAccess64, SymGetModuleBase64, NULL) )
{
this->OnDbgHelpErr("StackWalk64", GetLastError(), s.AddrPC.Offset);
break;
}
CallstackEntry csEntry;
csEntry.offset = s.AddrPC.Offset;
csEntry.name[0] = 0;
csEntry.undName[0] = 0;
csEntry.undFullName[0] = 0;
csEntry.offsetFromSmybol = 0;
csEntry.offsetFromLine = 0;
csEntry.lineFileName[0] = 0;
csEntry.lineNumber = 0;
csEntry.loadedImageName[0] = 0;
csEntry.moduleName[0] = 0;
if (s.AddrPC.Offset == s.AddrReturn.Offset)
{
OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset);
break;
}
if (s.AddrPC.Offset != 0)
{
// we seem to have a valid PC, show procedure info
if (SymGetSymFromAddr64(m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), pSym) != FALSE)
{
// TODO: Mache dies sicher...!
strcpy_s(csEntry.name, pSym->Name);
UnDecorateSymbolName( pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY );
UnDecorateSymbolName( pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE );
}
else
{
this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset);
}
// show line number info, NT5.0-method
if (SymGetLineFromAddr64(this->m_hProcess, s.AddrPC.Offset, (PDWORD)&(csEntry.offsetFromLine), &Line) != FALSE)
{
csEntry.lineNumber = Line.LineNumber;
// TODO: Mache dies sicher...!
strcpy_s(csEntry.lineFileName, Line.FileName);
}
else
{
this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset);
}
// show module info
if( GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module) )
{
switch ( Module.SymType )
{
case SymNone:
csEntry.symTypeString = "-nosymbols-";
break;
case SymCoff:
csEntry.symTypeString = "COFF";
break;
case SymCv:
csEntry.symTypeString = "CV";
break;
case SymPdb:
csEntry.symTypeString = "PDB";
break;
case SymExport:
csEntry.symTypeString = "-exported-";
break;
case SymDeferred:
csEntry.symTypeString = "-deferred-";
break;
case SymSym:
csEntry.symTypeString = "SYM";
break;
case SymDia:
csEntry.symTypeString = "DIA";
break;
case SymVirtual:
csEntry.symTypeString = "Virtual";
break;
default:
//_snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType );
csEntry.symTypeString = NULL;
break;
}
// TODO: Mache dies sicher...!
strcpy_s(csEntry.moduleName, Module.ModuleName);
csEntry.baseOfImage = Module.BaseOfImage;
strcpy_s(csEntry.loadedImageName, Module.LoadedImageName);
}
else
{
OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset);
}
}
CallstackEntryType et = nextEntry;
if (frameNum == 0) et = firstEntry;
OnCallstackEntry(et, csEntry);
if (s.AddrReturn.Offset == 0)
{
OnCallstackEntry(lastEntry, csEntry);
SetLastError(ERROR_SUCCESS);
break;
}
}
cleanup:
if (pSym) free( pSym );
return true;
}
BOOL __stdcall StackWalker::myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead)
{
if (s_readMemoryFunction == NULL)
{
SIZE_T st;
BOOL bRet = ReadProcessMemory(hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, &st);
*lpNumberOfBytesRead = (DWORD) st;
//printf("ReadMemory: hProcess: %p, baseAddr: %p, buffer: %p, size: %d, read: %d, result: %d\n", hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, (DWORD) st, (DWORD) bRet);
return bRet;
}
else
{
return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData);
}
}
//-----------------------------------------------------------------------------------------------------------------------------------------
//OnLoadModule
//-----------------------------------------------------------------------------------------------------------------------------------------
void StackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion)
{
CHAR buffer[STACKWALK_MAX_NAMELEN];
if (fileVersion == 0)
_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName);
else
{
DWORD v4 = (DWORD) fileVersion & 0xFFFF;
DWORD v3 = (DWORD) (fileVersion>>16) & 0xFFFF;
DWORD v2 = (DWORD) (fileVersion>>32) & 0xFFFF;
DWORD v1 = (DWORD) (fileVersion>>48) & 0xFFFF;
_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName, v1, v2, v3, v4);
}
if(OutputModules & m_options) OnOutput(buffer);
}
//-----------------------------------------------------------------------------------------------------------------------------------------
//GetModuleInfo
//-----------------------------------------------------------------------------------------------------------------------------------------
bool StackWalker::GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64 *pModuleInfo)
{
pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
void *pData = malloc(4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites...
if (pData == NULL)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return false;
}
memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64));
if (SymGetModuleInfo64(hProcess, baseAddr, (IMAGEHLP_MODULE64*) pData) != FALSE)
{
// only copy as much memory as is reserved...
memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64));
pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64);
free(pData);
return true;
}
free(pData);
SetLastError(ERROR_DLL_INIT_FAILED);
return false;
}
//-----------------------------------------------------------------------------------------------------------------------------------------
//GetModuleListTH32
//-----------------------------------------------------------------------------------------------------------------------------------------
bool StackWalker::GetModuleListTH32(HANDLE hProcess, DWORD pid)
{
// CreateToolhelp32Snapshot()
typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID);
// Module32First()
typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
// Module32Next()
typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);
// try both dlls...
const char *dllname[] = { "kernel32.dll", "tlhelp32.dll" };
HINSTANCE hToolhelp = NULL;
tCT32S pCT32S = NULL;
tM32F pM32F = NULL;
tM32N pM32N = NULL;
HANDLE hSnap;
MODULEENTRY32 me;
me.dwSize = sizeof(me);
BOOL keepGoing;
size_t i;
for (i = 0; i<(sizeof(dllname) / sizeof(dllname[0])); i++ )
{
hToolhelp = LoadLibraryA( dllname[i] );
if (hToolhelp == NULL)
continue;
pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot");
pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First");
pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next");
if ( (pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL) )
break; // found the functions!
FreeLibrary(hToolhelp);
hToolhelp = NULL;
}
if (hToolhelp == NULL)
return false;
hSnap = pCT32S( TH32CS_SNAPMODULE, pid );
if (hSnap == (HANDLE) -1)
return false;
keepGoing = !!pM32F( hSnap, &me );
int cnt = 0;
while (keepGoing)
{
this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64) me.modBaseAddr, me.modBaseSize);
cnt++;
keepGoing = !!pM32N( hSnap, &me );
}
CloseHandle(hSnap);
FreeLibrary(hToolhelp);
if (cnt <= 0) return false;
return true;
}
//-----------------------------------------------------------------------------------------------------------------------------------------
//GetModuleListPSAPI
//-----------------------------------------------------------------------------------------------------------------------------------------
bool StackWalker::GetModuleListPSAPI(HANDLE hProcess)
{
DWORD i;
//ModuleEntry e;
DWORD cbNeeded;
MODULEINFO mi;
HMODULE *hMods = 0;
char *tt = NULL;
char *tt2 = NULL;
const SIZE_T TTBUFLEN = 8096;
int cnt = 0;
hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE));
tt = (char*) malloc(sizeof(char) * TTBUFLEN);
tt2 = (char*) malloc(sizeof(char) * TTBUFLEN);
if ( (hMods == NULL) || (tt == NULL) || (tt2 == NULL) )
goto cleanup;
if ( !EnumProcessModules(hProcess, hMods, TTBUFLEN, &cbNeeded) )
{
//_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle );
goto cleanup;
}
if ( cbNeeded > TTBUFLEN )
{
//_ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) );
goto cleanup;
}
for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ )
{
// base address, size
GetModuleInformation(hProcess, hMods[i], &mi, sizeof mi );
// image file name
tt[0] = 0;
GetModuleFileNameExA(hProcess, hMods[i], tt, TTBUFLEN );
// module name
tt2[0] = 0;
GetModuleBaseNameA(hProcess, hMods[i], tt2, TTBUFLEN );
DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64) mi.lpBaseOfDll, mi.SizeOfImage);
if (dwRes != ERROR_SUCCESS)
this->OnDbgHelpErr("LoadModule", dwRes, 0);
cnt++;
}
cleanup:
if (tt2 != NULL) free(tt2);
if (tt != NULL) free(tt);
if (hMods != NULL) free(hMods);
return cnt != 0;
}
//-----------------------------------------------------------------------------------------------------------------------------------------
//OnCallstackEntry
//-----------------------------------------------------------------------------------------------------------------------------------------
void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry)
{
CHAR buffer[STACKWALK_MAX_NAMELEN];
if ( (eType != lastEntry) && (entry.offset != 0) )
{
if (entry.name[0] == 0) strcpy_s(entry.name, "(function-name not available)");
if (entry.undName[0] != 0) strcpy_s(entry.name, entry.undName);
if (entry.undFullName[0] != 0) strcpy_s(entry.name, entry.undFullName);
if (entry.lineFileName[0] == 0)
{
strcpy_s(entry.lineFileName, "(filename not available)");
if (entry.moduleName[0] == 0) strcpy_s(entry.moduleName, "(module-name not available)");
_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s: %s\n", (LPVOID) entry.offset, entry.moduleName, entry.lineFileName, entry.name);
}
else
{
_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber, entry.name);
}
OnOutput(buffer);
}
}
//-----------------------------------------------------------------------------------------------------------------------------------------
//OnDbgHelpErr
//-----------------------------------------------------------------------------------------------------------------------------------------
void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr)
{
CHAR buffer[STACKWALK_MAX_NAMELEN];
_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, (LPVOID) addr);
OnOutput(buffer);
}
//-----------------------------------------------------------------------------------------------------------------------------------------
//OnSymInit
//-----------------------------------------------------------------------------------------------------------------------------------------
void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName)
{
//Symbol Search path
CHAR buffer[STACKWALK_MAX_NAMELEN];
_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", szSearchPath, symOptions, szUserName);
if(OutputSymPath & m_options) OnOutput(buffer);
//OS-version
OSVERSIONINFOEX ver;
ZeroMemory(&ver, sizeof(OSVERSIONINFOEX));
ver.dwOSVersionInfoSize = sizeof(ver);
if (GetVersionEx( (OSVERSIONINFO*) &ver) != FALSE)
{
_snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n",
ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber,
ver.szCSDVersion, ver.wSuiteMask, ver.wProductType);
if(OutputOS & m_options) OnOutput(buffer);
}
}
#endif

View file

@ -0,0 +1,102 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef __WIN_PLATFORM_STACKWALKER__
#define __WIN_PLATFORM_STACKWALKER__
#if defined( TORQUE_MINIDUMP ) && defined( TORQUE_RELEASE )
#include <windows.h>
#include <dbghelp.h>
class StackWalker
{
public:
typedef enum StackWalkOptions
{
// RetrieveNone = 0, // No additional info will be retrieved (only the address is available)
// RetrieveSymbol = 1, // Try to get the symbol-name
// RetrieveLine = 2, // Try to get the line for this symbol
// RetrieveModuleInfo = 4, // Try to retrieve the module-infos
RetrieveFileVersion = 8, // Also retrieve the version for the DLL/EXE
RetrieveVerbose = 0xF, // Contains all of the above retrieve options
SymBuildPath = 0x10, // Generate a "good" symbol-search-path
SymUseSymSrv = 0x20, // Also use a public Symbol Server
SymAll = 0x30, // Contains all of the above Symbol options
OutputSymPath = 0x80, //print out the symbol path
OutputOS = 0x100, //print out the OS path
OutputModules = 0x200, //print out the Modules
OptionsDefault = 0x3F, // Less verbose output (default)
OptionsAll = 0x2FF // Contains all options
};
StackWalker(DWORD optionFlags = OptionsDefault, LPCSTR szSymPath = NULL);
virtual ~StackWalker();
typedef BOOL (__stdcall *PReadProcessMemoryRoutine)(HANDLE hProcess,
DWORD64 qwBaseAddress,
PVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead,
LPVOID pUserData // optional data, which was passed in "ShowCallstack"
);
// pUserData is optional to identify some data in the 'readMemoryFunction'-callback
bool ShowCallstack(HANDLE hThread, CONTEXT const & Context, PReadProcessMemoryRoutine readMemoryFunction = NULL, LPVOID pUserData = NULL);
void setOutputBuffer(char * buffer);
private:
typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry};
HANDLE m_hProcess;
DWORD m_dwProcessId;
bool m_modulesLoaded;
LPSTR m_szSymPath;
int m_options;
char * m_pOutputBuffer;
static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead);
bool Init(LPCSTR szSymPath);
virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion);
virtual void OnCallstackEntry(CallstackEntryType eType, struct CallstackEntry &entry);
virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
virtual void OnOutput(LPCSTR szText);
bool LoadModules();
DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size);
bool GetModuleListTH32(HANDLE hProcess, DWORD pid);
bool GetModuleListPSAPI(HANDLE hProcess);
bool GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64 *pModuleInfo);
};
void dGetStackTrace(char * traceBuffer, CONTEXT const & ContextRecord);
#endif
#endif

View file

@ -0,0 +1,948 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "console/simBase.h"
#include "platform/nativeDialogs/fileDialog.h"
#include "platform/threads/mutex.h"
#include "platformWin32/platformWin32.h"
#include "core/util/safeDelete.h"
#include "math/mMath.h"
#include "core/strings/unicode.h"
#include "console/consoleTypes.h"
#include "platform/profiler.h"
#include <ShlObj.h>
#include <WindowsX.h>
#include "console/engineAPI.h"
#ifdef TORQUE_TOOLS
//-----------------------------------------------------------------------------
// PlatformFileDlgData Implementation
//-----------------------------------------------------------------------------
FileDialogData::FileDialogData()
{
// Default Path
//
// Try to provide consistent experience by recalling the last file path
// - else
// Default to Working Directory if last path is not set or is invalid
mDefaultPath = StringTable->insert( Con::getVariable("Tools::FileDialogs::LastFilePath") );
if( mDefaultPath == StringTable->lookup("") || !Platform::isDirectory( mDefaultPath ) )
mDefaultPath = Platform::getCurrentDirectory();
mDefaultFile = StringTable->insert("");
mFilters = StringTable->insert("");
mFile = StringTable->insert("");
mTitle = StringTable->insert("");
mStyle = 0;
}
FileDialogData::~FileDialogData()
{
}
static LRESULT PASCAL OKBtnFolderHackProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
WNDPROC oldProc = (WNDPROC)GetProp(hWnd, dT("OldWndProc"));
switch(uMsg)
{
case WM_COMMAND:
if(LOWORD(wParam) == IDOK)
{
LPOPENFILENAME ofn = (LPOPENFILENAME)GetProp(hWnd, dT("OFN"));
if(ofn == NULL)
break;
SendMessage(hWnd, CDM_GETFILEPATH, ofn->nMaxFile, (LPARAM)ofn->lpstrFile);
char *filePath;
#ifdef UNICODE
char fileBuf[MAX_PATH];
convertUTF16toUTF8(ofn->lpstrFile, fileBuf, sizeof(fileBuf));
filePath = fileBuf;
#else
filePath = ofn->lpstrFile;
#endif
if(Platform::isDirectory(filePath))
{
// Got a directory
EndDialog(hWnd, IDOK);
}
}
break;
}
if(oldProc)
return CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
else
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
static UINT_PTR CALLBACK FolderHookProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam){
HWND hParent = GetParent(hdlg);
switch(uMsg)
{
case WM_INITDIALOG:
{
LPOPENFILENAME lpofn = (LPOPENFILENAME)lParam;
SendMessage(hParent, CDM_SETCONTROLTEXT, stc3, (LPARAM)dT("Folder name:"));
SendMessage(hParent, CDM_HIDECONTROL, cmb1, 0);
SendMessage(hParent, CDM_HIDECONTROL, stc2, 0);
LONG oldProc = SetWindowLong(hParent, GWL_WNDPROC, (LONG)OKBtnFolderHackProc);
SetProp(hParent, dT("OldWndProc"), (HANDLE)oldProc);
SetProp(hParent, dT("OFN"), (HANDLE)lpofn);
}
break;
case WM_NOTIFY:
{
LPNMHDR nmhdr = (LPNMHDR)lParam;
switch(nmhdr->code)
{
case CDN_FOLDERCHANGE:
{
LPOFNOTIFY lpofn = (LPOFNOTIFY)lParam;
OpenFolderDialog *ofd = (OpenFolderDialog *)lpofn->lpOFN->lCustData;
#ifdef UNICODE
UTF16 buf[MAX_PATH];
#else
char buf[MAX_PATH];
#endif
SendMessage(hParent, CDM_GETFOLDERPATH, sizeof(buf), (LPARAM)buf);
char filePath[MAX_PATH];
#ifdef UNICODE
convertUTF16toUTF8(buf, filePath, sizeof(filePath));
#else
dStrcpy( filePath, buf );
#endif
// [tom, 12/8/2006] Hack to remove files from the list because
// CDN_INCLUDEITEM doesn't work for regular files and folders.
HWND shellView = GetDlgItem(hParent, lst2);
HWND listView = FindWindowEx(shellView, 0, WC_LISTVIEW, NULL);
if(listView)
{
S32 count = ListView_GetItemCount(listView);
for(S32 i = count - 1;i >= 0;--i)
{
ListView_GetItemText(listView, i, 0, buf, sizeof(buf));
#ifdef UNICODE
char buf2[MAX_PATH];
convertUTF16toUTF8(buf, buf2, sizeof(buf2));
#else
char *buf2 = buf;
#endif
char full[MAX_PATH];
dSprintf(full, sizeof(full), "%s\\%s", filePath, buf2);
if(!Platform::isDirectory(full))
{
ListView_DeleteItem(listView, i);
}
}
}
if(ofd->mMustExistInDir == NULL || *ofd->mMustExistInDir == 0)
break;
HWND hOK = GetDlgItem(hParent, IDOK);
if(hOK == NULL)
break;
char checkPath[MAX_PATH];
dSprintf(checkPath, sizeof(checkPath), "%s\\%s", filePath, ofd->mMustExistInDir);
EnableWindow(hOK, Platform::isFile(checkPath));
}
break;
}
}
break;
}
return 0;
}
//-----------------------------------------------------------------------------
// FileDialog Implementation
//-----------------------------------------------------------------------------
IMPLEMENT_CONOBJECT(FileDialog);
ConsoleDocClass( FileDialog,
"@brief Base class responsible for displaying an OS file browser.\n\n"
"FileDialog is a platform agnostic dialog interface for querying the user for "
"file locations. It is designed to be used through the exposed scripting interface.\n\n"
"FileDialog is the base class for Native File Dialog controls in Torque. It provides these basic areas of functionality:\n\n"
" - Inherits from SimObject and is exposed to the scripting interface\n"
" - Provides blocking interface to allow instant return to script execution\n"
" - Simple object configuration makes practical use easy and effective\n\n"
"FileDialog is *NOT* intended to be used directly in script and is only exposed to script to expose generic file dialog attributes.\n\n"
"This base class is usable in TorqueScript, but is does not specify what functionality is intended (open or save?). "
"Its children, OpenFileDialog and SaveFileDialog, do make use of DialogStyle flags and do make use of specific funcationality. "
"These are the preferred classes to use\n\n"
"However, the FileDialog base class does contain the key properties and important method for file browing. The most "
"important function is Execute(). This is used by both SaveFileDialog and OpenFileDialog to initiate the browser.\n\n"
"@tsexample\n"
"// NOTE: This is not he preferred class to use, but this still works\n\n"
"// Create the file dialog\n"
"%baseFileDialog = new FileDialog()\n"
"{\n"
" // Allow browsing of all file types\n"
" filters = \"*.*\";\n\n"
" // No default file\n"
" defaultFile = "";\n\n"
" // Set default path relative to project\n"
" defaultPath = \"./\";\n\n"
" // Set the title\n"
" title = \"Durpa\";\n\n"
" // Allow changing of path you are browsing\n"
" changePath = true;\n"
"};\n\n"
" // Launch the file dialog\n"
" %baseFileDialog.Execute();\n"
" \n"
" // Don't forget to cleanup\n"
" %baseFileDialog.delete();\n\n\n"
"@endtsexample\n\n"
"@note FileDialog and its related classes are only availble in a Tools build of Torque.\n\n"
"@see OpenFileDialog for a practical example on opening a file\n"
"@see SaveFileDialog for a practical example of saving a file\n"
"@ingroup FileSystem\n"
);
FileDialog::FileDialog() : mData()
{
// Default to File Must Exist Open Dialog style
mData.mStyle = FileDialogData::FDS_OPEN | FileDialogData::FDS_MUSTEXIST;
mChangePath = false;
}
FileDialog::~FileDialog()
{
}
void FileDialog::initPersistFields()
{
addProtectedField( "defaultPath", TypeString, Offset(mData.mDefaultPath, FileDialog), &setDefaultPath, &defaultProtectedGetFn,
"The default directory path when the dialog is shown." );
addProtectedField( "defaultFile", TypeString, Offset(mData.mDefaultFile, FileDialog), &setDefaultFile, &defaultProtectedGetFn,
"The default file path when the dialog is shown." );
addProtectedField( "fileName", TypeString, Offset(mData.mFile, FileDialog), &setFile, &defaultProtectedGetFn,
"The default file name when the dialog is shown." );
addProtectedField( "filters", TypeString, Offset(mData.mFilters, FileDialog), &setFilters, &defaultProtectedGetFn,
"The filter string for limiting the types of files visible in the dialog. It makes use of the pipe symbol '|' "
"as a delimiter. For example:\n\n"
"'All Files|*.*'\n\n"
"'Image Files|*.png;*.jpg|Png Files|*.png|Jepg Files|*.jpg'" );
addField( "title", TypeString, Offset(mData.mTitle, FileDialog),
"The title for the dialog." );
addProtectedField( "changePath", TypeBool, Offset(mChangePath, FileDialog), &setChangePath, &getChangePath,
"True/False whether to set the working directory to the directory returned by the dialog." );
Parent::initPersistFields();
}
static const U32 convertUTF16toUTF8DoubleNULL( const UTF16 *unistring, UTF8 *outbuffer, U32 len)
{
AssertFatal(len >= 1, "Buffer for unicode conversion must be large enough to hold at least the null terminator.");
PROFILE_START(convertUTF16toUTF8DoubleNULL);
U32 walked, nCodeunits, codeunitLen;
UTF32 middleman;
nCodeunits=0;
while( ! (*unistring == '\0' && *(unistring + 1) == '\0') && nCodeunits + 3 < len )
{
walked = 1;
middleman = oneUTF16toUTF32(unistring,&walked);
codeunitLen = oneUTF32toUTF8(middleman, &outbuffer[nCodeunits]);
unistring += walked;
nCodeunits += codeunitLen;
}
nCodeunits = getMin(nCodeunits,len - 1);
outbuffer[nCodeunits] = '\0';
outbuffer[nCodeunits+1] = '\0';
PROFILE_END();
return nCodeunits;
}
//
// Execute Method
//
bool FileDialog::Execute()
{
static char pszResult[MAX_PATH];
#ifdef UNICODE
UTF16 pszFile[MAX_PATH];
UTF16 pszInitialDir[MAX_PATH];
UTF16 pszTitle[MAX_PATH];
UTF16 pszFilter[1024];
UTF16 pszFileTitle[MAX_PATH];
UTF16 pszDefaultExtension[MAX_PATH];
// Convert parameters to UTF16*'s
convertUTF8toUTF16((UTF8 *)mData.mDefaultFile, pszFile, sizeof(pszFile));
convertUTF8toUTF16((UTF8 *)mData.mDefaultPath, pszInitialDir, sizeof(pszInitialDir));
convertUTF8toUTF16((UTF8 *)mData.mTitle, pszTitle, sizeof(pszTitle));
convertUTF8toUTF16((UTF8 *)mData.mFilters, pszFilter, sizeof(pszFilter) );
#else
// Not Unicode, All char*'s!
char pszFile[MAX_PATH];
char pszFilter[1024];
char pszFileTitle[MAX_PATH];
dStrcpy( pszFile, mData.mDefaultFile );
dStrcpy( pszFilter, mData.mFilters );
const char* pszInitialDir = mData.mDefaultPath;
const char* pszTitle = mData.mTitle;
#endif
pszFileTitle[0] = 0;
// Convert Filters
U32 filterLen = dStrlen( pszFilter );
S32 dotIndex = -1;
for( U32 i = 0; i < filterLen; i++ )
{
if( pszFilter[i] == '|' )
pszFilter[i] = '\0';
if( pszFilter[ i ] == '.' && dotIndex == -1 )
dotIndex = i;
}
// Add second NULL terminator at the end
pszFilter[ filterLen + 1 ] = '\0';
// Get default extension.
dMemset( pszDefaultExtension, 0, sizeof( pszDefaultExtension ) );
if( dotIndex != -1 )
{
for( U32 i = 0; i < MAX_PATH; ++ i )
{
UTF16 ch = pszFilter[ dotIndex + 1 + i ];
if( !ch || ch == ';' || ch == '|' || dIsspace( ch ) )
break;
pszDefaultExtension[ i ] = ch;
}
}
OPENFILENAME ofn;
dMemset(&ofn, 0, sizeof(ofn));
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = getWin32WindowHandle();
ofn.lpstrFile = pszFile;
if( !dStrncmp( mData.mDefaultFile, "", 1 ) )
ofn.lpstrFile[0] = '\0';
ofn.nMaxFile = sizeof(pszFile);
ofn.lpstrFilter = pszFilter;
ofn.nFilterIndex = 1;
ofn.lpstrInitialDir = pszInitialDir;
ofn.lCustData = (LPARAM)this;
ofn.lpstrFileTitle = pszFileTitle;
ofn.nMaxFileTitle = sizeof(pszFileTitle);
ofn.lpstrDefExt = pszDefaultExtension[ 0 ] ? pszDefaultExtension : NULL;
if( mData.mTitle != StringTable->lookup("") )
ofn.lpstrTitle = pszTitle;
// Build Proper Flags.
ofn.Flags = OFN_EXPLORER | OFN_ENABLESIZING | OFN_HIDEREADONLY;
if(mData.mStyle & FileDialogData::FDS_BROWSEFOLDER)
{
ofn.lpfnHook = FolderHookProc;
ofn.Flags |= OFN_ENABLEHOOK;
}
if( !(mData.mStyle & FileDialogData::FDS_CHANGEPATH) )
ofn.Flags |= OFN_NOCHANGEDIR;
if( mData.mStyle & FileDialogData::FDS_MUSTEXIST )
ofn.Flags |= OFN_FILEMUSTEXIST;
if( mData.mStyle & FileDialogData::FDS_OPEN && mData.mStyle & FileDialogData::FDS_MULTIPLEFILES )
ofn.Flags |= OFN_ALLOWMULTISELECT;
if( mData.mStyle & FileDialogData::FDS_OVERWRITEPROMPT )
ofn.Flags |= OFN_OVERWRITEPROMPT;
// Flag we're showing file browser so we can do some render hacking
winState.renderThreadBlocked = true;
// Get the current working directory, so we can back up to it once Windows has
// done its craziness and messed with it.
StringTableEntry cwd = Platform::getCurrentDirectory();
// Execute Dialog (Blocking Call)
bool dialogSuccess = false;
if( mData.mStyle & FileDialogData::FDS_OPEN )
dialogSuccess = GetOpenFileName(&ofn);
else if( mData.mStyle & FileDialogData::FDS_SAVE )
dialogSuccess = GetSaveFileName(&ofn);
// Dialog is gone.
winState.renderThreadBlocked = false;
// Restore the working directory.
Platform::setCurrentDirectory( cwd );
// Did we select a file?
if( !dialogSuccess )
return false;
// Handle Result Properly for Unicode as well as ANSI
#ifdef UNICODE
if(pszFileTitle[0] || ! ( mData.mStyle & FileDialogData::FDS_OPEN && mData.mStyle & FileDialogData::FDS_MULTIPLEFILES ))
convertUTF16toUTF8( (UTF16*)pszFile, (UTF8*)pszResult, sizeof(pszResult));
else
convertUTF16toUTF8DoubleNULL( (UTF16*)pszFile, (UTF8*)pszResult, sizeof(pszResult));
#else
if(pszFileTitle[0] || ! ( mData.mStyle & FileDialogData::FDS_OPEN && mData.mStyle & FileDialogData::FDS_MULTIPLEFILES ))
dStrcpy(pszResult,pszFile);
else
{
// [tom, 1/4/2007] pszResult is a double-NULL terminated, NULL separated list in this case so we can't just dSstrcpy()
char *sptr = pszFile, *dptr = pszResult;
while(! (*sptr == 0 && *(sptr+1) == 0))
*dptr++ = *sptr++;
*dptr++ = 0;
}
#endif
forwardslash(pszResult);
// [tom, 1/5/2007] Windows is ridiculously dumb. If you select a single file in a multiple
// select file dialog then it will return the file the same way as it would in a single
// select dialog. The only difference is pszFileTitle is empty if multiple files
// are selected.
// Store the result on our object
if( mData.mStyle & FileDialogData::FDS_BROWSEFOLDER || ( pszFileTitle[0] && ! ( mData.mStyle & FileDialogData::FDS_OPEN && mData.mStyle & FileDialogData::FDS_MULTIPLEFILES )))
{
// Single file selection, do it the easy way
mData.mFile = StringTable->insert( pszResult );
}
else if(pszFileTitle[0] && ( mData.mStyle & FileDialogData::FDS_OPEN && mData.mStyle & FileDialogData::FDS_MULTIPLEFILES ))
{
// Single file selection in a multiple file selection dialog
setDataField(StringTable->insert("files"), "0", pszResult);
setDataField(StringTable->insert("fileCount"), NULL, "1");
}
else
{
// Multiple file selection, break out into an array
S32 numFiles = 0;
const char *dir = pszResult;
const char *file = dir + dStrlen(dir) + 1;
char buffer[1024];
while(*file)
{
Platform::makeFullPathName(file, buffer, sizeof(buffer), dir);
setDataField(StringTable->insert("files"), Con::getIntArg(numFiles++), buffer);
file = file + dStrlen(file) + 1;
}
setDataField(StringTable->insert("fileCount"), NULL, Con::getIntArg(numFiles));
}
// Return success.
return true;
}
DefineEngineMethod( FileDialog, Execute, bool, (),,
"@brief Launches the OS file browser\n\n"
"After an Execute() call, the chosen file name and path is available in one of two areas. "
"If only a single file selection is permitted, the results will be stored in the @a fileName "
"attribute.\n\n"
"If multiple file selection is permitted, the results will be stored in the "
"@a files array. The total number of files in the array will be stored in the "
"@a fileCount attribute.\n\n"
"@tsexample\n"
"// NOTE: This is not he preferred class to use, but this still works\n\n"
"// Create the file dialog\n"
"%baseFileDialog = new FileDialog()\n"
"{\n"
" // Allow browsing of all file types\n"
" filters = \"*.*\";\n\n"
" // No default file\n"
" defaultFile = "";\n\n"
" // Set default path relative to project\n"
" defaultPath = \"./\";\n\n"
" // Set the title\n"
" title = \"Durpa\";\n\n"
" // Allow changing of path you are browsing\n"
" changePath = true;\n"
"};\n\n"
" // Launch the file dialog\n"
" %baseFileDialog.Execute();\n"
" \n"
" // Don't forget to cleanup\n"
" %baseFileDialog.delete();\n\n\n"
" // A better alternative is to use the \n"
" // derived classes which are specific to file open and save\n\n"
" // Create a dialog dedicated to opening files\n"
" %openFileDlg = new OpenFileDialog()\n"
" {\n"
" // Look for jpg image files\n"
" // First part is the descriptor|second part is the extension\n"
" Filters = \"Jepg Files|*.jpg\";\n"
" // Allow browsing through other folders\n"
" ChangePath = true;\n\n"
" // Only allow opening of one file at a time\n"
" MultipleFiles = false;\n"
" };\n\n"
" // Launch the open file dialog\n"
" %result = %openFileDlg.Execute();\n\n"
" // Obtain the chosen file name and path\n"
" if ( %result )\n"
" {\n"
" %seletedFile = %openFileDlg.file;\n"
" }\n"
" else\n"
" {\n"
" %selectedFile = \"\";\n"
" }\n"
" // Cleanup\n"
" %openFileDlg.delete();\n\n\n"
" // Create a dialog dedicated to saving a file\n"
" %saveFileDlg = new SaveFileDialog()\n"
" {\n"
" // Only allow for saving of COLLADA files\n"
" Filters = \"COLLADA Files (*.dae)|*.dae|\";\n\n"
" // Default save path to where the WorldEditor last saved\n"
" DefaultPath = $pref::WorldEditor::LastPath;\n\n"
" // No default file specified\n"
" DefaultFile = \"\";\n\n"
" // Do not allow the user to change to a new directory\n"
" ChangePath = false;\n\n"
" // Prompt the user if they are going to overwrite an existing file\n"
" OverwritePrompt = true;\n"
" };\n\n"
" // Launch the save file dialog\n"
" %result = %saveFileDlg.Execute();\n\n"
" // Obtain the file name\n"
" %selectedFile = \"\";\n"
" if ( %result )\n"
" %selectedFile = %saveFileDlg.file;\n\n"
" // Cleanup\n"
" %saveFileDlg.delete();\n"
"@endtsexample\n\n"
"@return True if the file was selected was successfully found (opened) or declared (saved).")
{
return object->Execute();
}
//-----------------------------------------------------------------------------
// Dialog Filters
//-----------------------------------------------------------------------------
bool FileDialog::setFilters( void *object, const char *index, const char *data )
{
// Will do validate on write at some point.
if( !data )
return true;
return true;
};
//-----------------------------------------------------------------------------
// Default Path Property - String Validated on Write
//-----------------------------------------------------------------------------
bool FileDialog::setDefaultPath( void *object, const char *index, const char *data )
{
if( !data || !dStrncmp( data, "", 1 ) )
return true;
// Copy and Backslash the path (Windows dialogs are VERY picky about this format)
static char szPathValidate[512];
dStrcpy( szPathValidate, data );
Platform::makeFullPathName( data,szPathValidate, sizeof(szPathValidate));
backslash( szPathValidate );
// Remove any trailing \'s
S8 validateLen = dStrlen( szPathValidate );
if( szPathValidate[ validateLen - 1 ] == '\\' )
szPathValidate[ validateLen - 1 ] = '\0';
// Now check
if( Platform::isDirectory( szPathValidate ) )
{
// Finally, assign in proper format.
FileDialog *pDlg = static_cast<FileDialog*>( object );
pDlg->mData.mDefaultPath = StringTable->insert( szPathValidate );
}
#ifdef TORQUE_DEBUG
else
Con::errorf(ConsoleLogEntry::GUI, "FileDialog - Invalid Default Path Specified!");
#endif
return false;
};
//-----------------------------------------------------------------------------
// Default File Property - String Validated on Write
//-----------------------------------------------------------------------------
bool FileDialog::setDefaultFile( void *object, const char *index, const char *data )
{
if( !data || !dStrncmp( data, "", 1 ) )
return true;
// Copy and Backslash the path (Windows dialogs are VERY picky about this format)
static char szPathValidate[512];
Platform::makeFullPathName( data,szPathValidate, sizeof(szPathValidate) );
backslash( szPathValidate );
// Remove any trailing \'s
S8 validateLen = dStrlen( szPathValidate );
if( szPathValidate[ validateLen - 1 ] == '\\' )
szPathValidate[ validateLen - 1 ] = '\0';
// Finally, assign in proper format.
FileDialog *pDlg = static_cast<FileDialog*>( object );
pDlg->mData.mDefaultFile = StringTable->insert( szPathValidate );
return false;
};
//-----------------------------------------------------------------------------
// ChangePath Property - Change working path on successful file selection
//-----------------------------------------------------------------------------
bool FileDialog::setChangePath( void *object, const char *index, const char *data )
{
bool bMustExist = dAtob( data );
FileDialog *pDlg = static_cast<FileDialog*>( object );
if( bMustExist )
pDlg->mData.mStyle |= FileDialogData::FDS_CHANGEPATH;
else
pDlg->mData.mStyle &= ~FileDialogData::FDS_CHANGEPATH;
return true;
};
const char* FileDialog::getChangePath(void* obj, const char* data)
{
FileDialog *pDlg = static_cast<FileDialog*>( obj );
if( pDlg->mData.mStyle & FileDialogData::FDS_CHANGEPATH )
return StringTable->insert("true");
else
return StringTable->insert("false");
}
bool FileDialog::setFile( void *object, const char *index, const char *data )
{
return false;
};
//-----------------------------------------------------------------------------
// OpenFileDialog Implementation
//-----------------------------------------------------------------------------
ConsoleDocClass( OpenFileDialog,
"@brief Derived from FileDialog, this class is responsible for opening a file browser with the intention of opening a file.\n\n"
"The core usage of this dialog is to locate a file in the OS and return the path and name. This does not handle "
"the actual file parsing or data manipulation. That functionality is left up to the FileObject class.\n\n"
"@tsexample\n"
" // Create a dialog dedicated to opening files\n"
" %openFileDlg = new OpenFileDialog()\n"
" {\n"
" // Look for jpg image files\n"
" // First part is the descriptor|second part is the extension\n"
" Filters = \"Jepg Files|*.jpg\";\n"
" // Allow browsing through other folders\n"
" ChangePath = true;\n\n"
" // Only allow opening of one file at a time\n"
" MultipleFiles = false;\n"
" };\n\n"
" // Launch the open file dialog\n"
" %result = %openFileDlg.Execute();\n\n"
" // Obtain the chosen file name and path\n"
" if ( %result )\n"
" {\n"
" %seletedFile = %openFileDlg.file;\n"
" }\n"
" else\n"
" {\n"
" %selectedFile = \"\";\n"
" }\n\n"
" // Cleanup\n"
" %openFileDlg.delete();\n\n\n"
"@endtsexample\n\n"
"@note FileDialog and its related classes are only availble in a Tools build of Torque.\n\n"
"@see FileDialog\n"
"@see SaveFileDialog\n"
"@see FileObject\n"
"@ingroup FileSystem\n"
);
OpenFileDialog::OpenFileDialog()
{
// Default File Must Exist
mData.mStyle = FileDialogData::FDS_OPEN | FileDialogData::FDS_MUSTEXIST;
}
OpenFileDialog::~OpenFileDialog()
{
mMustExist = true;
mMultipleFiles = false;
}
IMPLEMENT_CONOBJECT(OpenFileDialog);
//-----------------------------------------------------------------------------
// Console Properties
//-----------------------------------------------------------------------------
void OpenFileDialog::initPersistFields()
{
addProtectedField("MustExist", TypeBool, Offset(mMustExist, OpenFileDialog), &setMustExist, &getMustExist, "True/False whether the file returned must exist or not" );
addProtectedField("MultipleFiles", TypeBool, Offset(mMultipleFiles, OpenFileDialog), &setMultipleFiles, &getMultipleFiles, "True/False whether multiple files may be selected and returned or not" );
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
// File Must Exist - Boolean
//-----------------------------------------------------------------------------
bool OpenFileDialog::setMustExist( void *object, const char *index, const char *data )
{
bool bMustExist = dAtob( data );
OpenFileDialog *pDlg = static_cast<OpenFileDialog*>( object );
if( bMustExist )
pDlg->mData.mStyle |= FileDialogData::FDS_MUSTEXIST;
else
pDlg->mData.mStyle &= ~FileDialogData::FDS_MUSTEXIST;
return true;
};
const char* OpenFileDialog::getMustExist(void* obj, const char* data)
{
OpenFileDialog *pDlg = static_cast<OpenFileDialog*>( obj );
if( pDlg->mData.mStyle & FileDialogData::FDS_MUSTEXIST )
return StringTable->insert("true");
else
return StringTable->insert("false");
}
//-----------------------------------------------------------------------------
// Can Select Multiple Files - Boolean
//-----------------------------------------------------------------------------
bool OpenFileDialog::setMultipleFiles( void *object, const char *index, const char *data )
{
bool bMustExist = dAtob( data );
OpenFileDialog *pDlg = static_cast<OpenFileDialog*>( object );
if( bMustExist )
pDlg->mData.mStyle |= FileDialogData::FDS_MULTIPLEFILES;
else
pDlg->mData.mStyle &= ~FileDialogData::FDS_MULTIPLEFILES;
return true;
};
const char* OpenFileDialog::getMultipleFiles(void* obj, const char* data)
{
OpenFileDialog *pDlg = static_cast<OpenFileDialog*>( obj );
if( pDlg->mData.mStyle & FileDialogData::FDS_MULTIPLEFILES )
return StringTable->insert("true");
else
return StringTable->insert("false");
}
//-----------------------------------------------------------------------------
// SaveFileDialog Implementation
//-----------------------------------------------------------------------------
ConsoleDocClass( SaveFileDialog,
"@brief Derived from FileDialog, this class is responsible for opening a file browser with the intention of saving a file.\n\n"
"The core usage of this dialog is to locate a file in the OS and return the path and name. This does not handle "
"the actual file writing or data manipulation. That functionality is left up to the FileObject class.\n\n"
"@tsexample\n"
" // Create a dialog dedicated to opening file\n"
" %saveFileDlg = new SaveFileDialog()\n"
" {\n"
" // Only allow for saving of COLLADA files\n"
" Filters = \"COLLADA Files (*.dae)|*.dae|\";\n\n"
" // Default save path to where the WorldEditor last saved\n"
" DefaultPath = $pref::WorldEditor::LastPath;\n\n"
" // No default file specified\n"
" DefaultFile = \"\";\n\n"
" // Do not allow the user to change to a new directory\n"
" ChangePath = false;\n\n"
" // Prompt the user if they are going to overwrite an existing file\n"
" OverwritePrompt = true;\n"
" };\n\n"
" // Launch the save file dialog\n"
" %saveFileDlg.Execute();\n\n"
" if ( %result )\n"
" {\n"
" %seletedFile = %openFileDlg.file;\n"
" }\n"
" else\n"
" {\n"
" %selectedFile = \"\";\n"
" }\n\n"
" // Cleanup\n"
" %saveFileDlg.delete();\n"
"@endtsexample\n\n"
"@note FileDialog and its related classes are only availble in a Tools build of Torque.\n\n"
"@see FileDialog\n"
"@see OpenFileDialog\n"
"@see FileObject\n"
"@ingroup FileSystem\n"
);
SaveFileDialog::SaveFileDialog()
{
// Default File Must Exist
mData.mStyle = FileDialogData::FDS_SAVE | FileDialogData::FDS_OVERWRITEPROMPT;
mOverwritePrompt = true;
}
SaveFileDialog::~SaveFileDialog()
{
}
IMPLEMENT_CONOBJECT(SaveFileDialog);
//-----------------------------------------------------------------------------
// Console Properties
//-----------------------------------------------------------------------------
void SaveFileDialog::initPersistFields()
{
addProtectedField("OverwritePrompt", TypeBool, Offset(mOverwritePrompt, SaveFileDialog), &setOverwritePrompt, &getOverwritePrompt, "True/False whether the dialog should prompt before accepting an existing file name" );
Parent::initPersistFields();
}
//-----------------------------------------------------------------------------
// Prompt on Overwrite - Boolean
//-----------------------------------------------------------------------------
bool SaveFileDialog::setOverwritePrompt( void *object, const char *index, const char *data )
{
bool bMustExist = dAtob( data );
SaveFileDialog *pDlg = static_cast<SaveFileDialog*>( object );
if( bMustExist )
pDlg->mData.mStyle |= FileDialogData::FDS_OVERWRITEPROMPT;
else
pDlg->mData.mStyle &= ~FileDialogData::FDS_OVERWRITEPROMPT;
return true;
};
const char* SaveFileDialog::getOverwritePrompt(void* obj, const char* data)
{
SaveFileDialog *pDlg = static_cast<SaveFileDialog*>( obj );
if( pDlg->mData.mStyle & FileDialogData::FDS_OVERWRITEPROMPT )
return StringTable->insert("true");
else
return StringTable->insert("false");
}
//-----------------------------------------------------------------------------
// OpenFolderDialog Implementation
//-----------------------------------------------------------------------------
OpenFolderDialog::OpenFolderDialog()
{
mData.mStyle = FileDialogData::FDS_OPEN | FileDialogData::FDS_OVERWRITEPROMPT | FileDialogData::FDS_BROWSEFOLDER;
mMustExistInDir = "";
}
IMPLEMENT_CONOBJECT(OpenFolderDialog);
ConsoleDocClass( OpenFolderDialog,
"@brief OS level dialog used for browsing folder structures.\n\n"
"This is essentially an OpenFileDialog, but only used for returning directory paths, not files.\n\n"
"@note FileDialog and its related classes are only availble in a Tools build of Torque.\n\n"
"@see OpenFileDialog for more details on functionality.\n\n"
"@ingroup FileSystem\n"
);
void OpenFolderDialog::initPersistFields()
{
addField("fileMustExist", TypeFilename, Offset(mMustExistInDir, OpenFolderDialog), "File that must be in selected folder for it to be valid");
Parent::initPersistFields();
}
#endif

View file

@ -0,0 +1,129 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "platformWin32/platformWin32.h"
#include "platform/platformInput.h"
#include "platform/nativeDialogs/msgBox.h"
#include "console/console.h"
#include "core/strings/unicode.h"
#include "windowManager/platformWindowMgr.h"
#include "windowManager/win32/win32Window.h"
struct _FlagMap
{
S32 num;
U32 flag;
};
static _FlagMap sgButtonMap[] =
{
{ MBOk, MB_OK },
{ MBOkCancel, MB_OKCANCEL },
{ MBRetryCancel, MB_RETRYCANCEL },
{ MBSaveDontSave, MB_YESNO },
{ MBSaveDontSaveCancel, MB_YESNOCANCEL },
{ 0xffffffff, 0xffffffff }
};
static _FlagMap sgIconMap[] =
{
{ MIWarning, MB_ICONWARNING },
{ MIInformation, MB_ICONINFORMATION },
{ MIQuestion, MB_ICONQUESTION },
{ MIStop, MB_ICONSTOP },
{ 0xffffffff, 0xffffffff }
};
static _FlagMap sgMsgBoxRetMap[] =
{
{ IDCANCEL, MRCancel },
{ IDNO, MRDontSave },
{ IDOK, MROk},
{ IDRETRY, MRRetry },
{ IDYES, MROk },
{ 0xffffffff, 0xffffffff }
};
//-----------------------------------------------------------------------------
static U32 getMaskFromID(_FlagMap *map, S32 id)
{
for(S32 i = 0;map[i].num != 0xffffffff && map[i].flag != 0xffffffff;++i)
{
if(map[i].num == id)
return map[i].flag;
}
return 0;
}
//-----------------------------------------------------------------------------
S32 Platform::messageBox(const UTF8 *title, const UTF8 *message, MBButtons buttons, MBIcons icon)
{
PlatformWindow *pWindow = WindowManager->getFirstWindow();
// Get us rendering while we're blocking.
winState.renderThreadBlocked = true;
// We don't keep a locked mouse or else we're going
// to end up possibly locking our mouse out of the
// message box area
bool cursorLocked = pWindow && pWindow->isMouseLocked();
if( cursorLocked )
pWindow->setMouseLocked( false );
// Need a visible cursor to click stuff accurately
bool cursorVisible = !pWindow || pWindow->isCursorVisible();
if( !cursorVisible )
pWindow->setCursorVisible(true);
#ifdef UNICODE
const UTF16 *msg = convertUTF8toUTF16(message);
const UTF16 *t = convertUTF8toUTF16(title);
#else
const UTF8 *msg = message;
const UTF8 *t = title;
#endif
HWND parent = pWindow ? static_cast<Win32Window*>(pWindow)->getHWND() : NULL;
S32 ret = ::MessageBox( parent, msg, t, getMaskFromID(sgButtonMap, buttons) | getMaskFromID(sgIconMap, icon));
#ifdef UNICODE
delete [] msg;
delete [] t;
#endif
// Dialog is gone.
winState.renderThreadBlocked = false;
if( cursorVisible == false )
pWindow->setCursorVisible( false );
if( cursorLocked == true )
pWindow->setMouseLocked( true );
return getMaskFromID(sgMsgBoxRetMap, ret);
}

View file

@ -0,0 +1,138 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _PLATFORMWIN32_H_
#define _PLATFORMWIN32_H_
// Sanity check for UNICODE
#ifdef TORQUE_UNICODE
# ifndef UNICODE
# error "ERROR: You must have UNICODE defined in your preprocessor settings (ie, /DUNICODE) if you have TORQUE_UNICODE enabled in torqueConfig.h!"
# endif
#endif
#include <windows.h>
#ifndef _PLATFORM_H_
#include "platform/platform.h"
#endif
#ifndef _MRECT_H_
#include "math/mRect.h"
#endif
#if defined(TORQUE_COMPILER_CODEWARRIOR)
# include <ansi_prefix.win32.h>
# include <stdio.h>
# include <string.h>
#else
# include <stdio.h>
# include <string.h>
#endif
#ifdef _MSC_VER
#pragma warning(disable: 4996) // turn off "deprecation" warnings
#endif
#define NOMINMAX
// Hack to get a correct HWND instead of using global state.
extern HWND getWin32WindowHandle();
struct Win32PlatState
{
FILE *log_fp;
HINSTANCE hinstOpenGL;
HINSTANCE hinstGLU;
HINSTANCE hinstOpenAL;
HWND appWindow;
HDC appDC;
HINSTANCE appInstance;
HGLRC hGLRC;
DWORD processId;
bool renderThreadBlocked;
S32 nMessagesPerFrame; ///< The max number of messages to dispatch per frame
HMENU appMenu; ///< The menu bar for the window
#ifdef UNICODE
//HIMC imeHandle;
#endif
S32 desktopBitsPixel;
S32 desktopWidth;
S32 desktopHeight;
S32 desktopClientWidth;
S32 desktopClientHeight;
U32 currentTime;
// minimum time per frame
U32 sleepTicks;
// are we in the background?
bool backgrounded;
Win32PlatState();
};
extern Win32PlatState winState;
extern void setModifierKeys( S32 modKeys );
//-------------------------------------- Helper Functions
template< typename T >
inline void forwardslashT( T *str )
{
while(*str)
{
if(*str == '\\')
*str = '/';
str++;
}
}
inline void forwardslash( char* str )
{
forwardslashT< char >( str );
}
inline void forwardslash( WCHAR* str )
{
forwardslashT< WCHAR >( str );
}
template< typename T >
inline void backslashT( T *str )
{
while(*str)
{
if(*str == '/')
*str = '\\';
str++;
}
}
inline void backslash( char* str )
{
backslashT< char >( str );
}
inline void backslash( WCHAR* str )
{
backslashT< WCHAR >( str );
}
#endif //_PLATFORMWIN32_H_

View file

@ -0,0 +1,74 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/threads/mutex.h"
#include "platformWin32/platformWin32.h"
#include "core/util/safeDelete.h"
//-----------------------------------------------------------------------------
// Mutex Data
//-----------------------------------------------------------------------------
struct PlatformMutexData
{
CRITICAL_SECTION mCriticalSection;
};
//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
Mutex::Mutex()
{
mData = new PlatformMutexData;
InitializeCriticalSection( &mData->mCriticalSection );
}
Mutex::~Mutex()
{
AssertFatal( TryEnterCriticalSection( &mData->mCriticalSection ), "Mutex::~Mutex - Critical section is locked!" );
DeleteCriticalSection( &mData->mCriticalSection );
SAFE_DELETE( mData );
}
//-----------------------------------------------------------------------------
// Public Methods
//-----------------------------------------------------------------------------
bool Mutex::lock( bool block )
{
AssertFatal( mData, "Mutex::lock - No data!" );
if( !block )
return TryEnterCriticalSection( &mData->mCriticalSection );
else
{
EnterCriticalSection( &mData->mCriticalSection );
return true;
}
}
void Mutex::unlock()
{
AssertFatal( mData, "Mutex::unlock - No data!" );
LeaveCriticalSection( &mData->mCriticalSection );
}

View file

@ -0,0 +1,208 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef TORQUE_OS_XENON
#include "platformWin32/platformWin32.h"
#endif
#include "platform/threads/thread.h"
#include "platform/threads/semaphore.h"
#include "platform/platformIntrinsics.h"
#include "core/util/safeDelete.h"
#include <process.h> // [tom, 4/20/2006] for _beginthread()
ThreadManager::MainThreadId ThreadManager::smMainThreadId;
//-----------------------------------------------------------------------------
// Thread data
//-----------------------------------------------------------------------------
class PlatformThreadData
{
public:
ThreadRunFunction mRunFunc;
void* mRunArg;
Thread* mThread;
HANDLE mThreadHnd;
Semaphore mGateway;
U32 mThreadID;
U32 mDead;
PlatformThreadData()
{
mRunFunc = NULL;
mRunArg = 0;
mThread = 0;
mThreadHnd = 0;
mDead = false;
};
};
//-----------------------------------------------------------------------------
// Static Functions/Methods
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Function: ThreadRunHandler
// Summary: Calls Thread::run() with the thread's specified run argument.
// Neccesary because Thread::run() is provided as a non-threaded
// way to execute the thread's run function. So we have to keep
// track of the thread's lock here.
static unsigned int __stdcall ThreadRunHandler(void * arg)
{
PlatformThreadData* mData = reinterpret_cast<PlatformThreadData*>(arg);
mData->mThreadID = GetCurrentThreadId();
ThreadManager::addThread(mData->mThread);
mData->mThread->run(mData->mRunArg);
ThreadManager::removeThread(mData->mThread);
bool autoDelete = mData->mThread->autoDelete;
mData->mThreadHnd = NULL; // mark as dead
dCompareAndSwap( mData->mDead, false, true );
mData->mGateway.release(); // don't access data after this.
if( autoDelete )
delete mData->mThread; // Safe as we own the data.
_endthreadex( 0 );
return 0;
}
//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
Thread::Thread(ThreadRunFunction func /* = 0 */, void *arg /* = 0 */, bool start_thread /* = true */, bool autodelete /*= false*/)
: autoDelete( autodelete )
{
AssertFatal( !start_thread, "Thread::Thread() - auto-starting threads from ctor has been disallowed since the run() method is virtual" );
mData = new PlatformThreadData;
mData->mRunFunc = func;
mData->mRunArg = arg;
mData->mThread = this;
}
Thread::~Thread()
{
stop();
if( isAlive() )
join();
SAFE_DELETE(mData);
}
//-----------------------------------------------------------------------------
// Public Methods
//-----------------------------------------------------------------------------
void Thread::start( void* arg )
{
AssertFatal( !mData->mThreadHnd,
"Thread::start() - thread already started" );
// cause start to block out other pthreads from using this Thread,
// at least until ThreadRunHandler exits.
mData->mGateway.acquire();
// reset the shouldStop flag, so we'll know when someone asks us to stop.
shouldStop = false;
mData->mDead = false;
if( !mData->mRunArg )
mData->mRunArg = arg;
mData->mThreadHnd = (HANDLE)_beginthreadex(0, 0, ThreadRunHandler, mData, 0, 0);
}
bool Thread::join()
{
mData->mGateway.acquire();
AssertFatal( !isAlive(), "Thread::join() - thread still alive after join" );
mData->mGateway.release(); // release for further joins
return true;
}
void Thread::run(void *arg /* = 0 */)
{
if(mData->mRunFunc)
mData->mRunFunc(arg);
}
bool Thread::isAlive()
{
return ( !mData->mDead );
}
U32 Thread::getId()
{
return mData->mThreadID;
}
void Thread::_setName( const char* name )
{
#if defined( TORQUE_DEBUG ) && defined( TORQUE_COMPILER_VISUALC ) && defined( TORQUE_OS_WIN32 )
// See http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
#define MS_VC_EXCEPTION 0x406D1388
#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
DWORD dwType; // Must be 0x1000.
LPCSTR szName; // Pointer to name (in user addr space).
DWORD dwThreadID; // Thread ID (-1=caller thread).
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)
Sleep(10);
THREADNAME_INFO info;
info.dwType = 0x1000;
info.szName = name;
info.dwThreadID = getId();
info.dwFlags = 0;
__try
{
RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
#endif
}
U32 ThreadManager::getCurrentThreadId()
{
return GetCurrentThreadId();
}
bool ThreadManager::compare(U32 threadId_1, U32 threadId_2)
{
return (threadId_1 == threadId_2);
}

View file

@ -0,0 +1,579 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#define _WIN32_DCOM
//#include <comdef.h>
#include <wbemidl.h>
//#include <atlconv.h>
#pragma comment(lib, "comsuppw.lib")
#pragma comment(lib, "wbemuuid.lib")
#include "platformWin32/videoInfo/wmiVideoInfo.h"
#include "core/util/safeRelease.h"
#include "console/console.h"
// http://www.spectranaut.net/sourcecode/WMI.cpp
// Add constructor to GUID.
struct MYGUID : public GUID
{
MYGUID( DWORD a, SHORT b, SHORT c, BYTE d, BYTE e, BYTE f, BYTE g, BYTE h, BYTE i, BYTE j, BYTE k )
{
Data1 = a;
Data2 = b;
Data3 = c;
Data4[ 0 ] = d;
Data4[ 1 ] = e;
Data4[ 2 ] = f;
Data4[ 3 ] = g;
Data4[ 4 ] = h;
Data4[ 5 ] = i;
Data4[ 6 ] = j;
Data4[ 7 ] = k;
}
};
//------------------------------------------------------------------------------
// DXGI decls for retrieving device info on Vista. We manually declare that
// stuff here, so we don't depend on headers and compile on any setup. At
// run-time, it depends on whether we can successfully load the DXGI DLL; if
// not, nothing of this here will be used.
struct IDXGIObject;
struct IDXGIFactory;
struct IDXGIAdapter;
struct IDXGIOutput;
struct DXGI_SWAP_CHAIN_DESC;
struct DXGI_ADAPTER_DESC;
struct IDXGIObject : public IUnknown
{
virtual HRESULT STDMETHODCALLTYPE SetPrivateData( REFGUID, UINT, const void* ) = 0;
virtual HRESULT STDMETHODCALLTYPE SetPrivateDataInterface( REFGUID, const IUnknown* ) = 0;
virtual HRESULT STDMETHODCALLTYPE GetPrivateData( REFGUID, UINT*, void* ) = 0;
virtual HRESULT STDMETHODCALLTYPE GetParent( REFIID, void** ) = 0;
};
struct IDXGIFactory : public IDXGIObject
{
virtual HRESULT STDMETHODCALLTYPE EnumAdapters( UINT, IDXGIAdapter** ) = 0;
virtual HRESULT STDMETHODCALLTYPE MakeWindowAssociation( HWND, UINT ) = 0;
virtual HRESULT STDMETHODCALLTYPE GetWindowAssociation( HWND ) = 0;
virtual HRESULT STDMETHODCALLTYPE CreateSwapChain( IUnknown*, DXGI_SWAP_CHAIN_DESC* ) = 0;
virtual HRESULT STDMETHODCALLTYPE CreateSoftwareAdapter( HMODULE, IDXGIAdapter** ) = 0;
};
struct IDXGIAdapter : public IDXGIObject
{
virtual HRESULT STDMETHODCALLTYPE EnumOutputs( UINT, IDXGIOutput** ) = 0;
virtual HRESULT STDMETHODCALLTYPE GetDesc( DXGI_ADAPTER_DESC* ) = 0;
virtual HRESULT STDMETHODCALLTYPE CheckInterfaceSupport( REFGUID, LARGE_INTEGER* ) = 0;
};
struct DXGI_ADAPTER_DESC
{
WCHAR Description[ 128 ];
UINT VendorId;
UINT DeviceId;
UINT SubSysId;
UINT Revision;
SIZE_T DedicatedVideoMemory;
SIZE_T DedicatedSystemMemory;
SIZE_T SharedSystemMemory;
LUID AdapterLuid;
};
static MYGUID IID_IDXGIFactory( 0x7b7166ec, 0x21c7, 0x44ae, 0xb2, 0x1a, 0xc9, 0xae, 0x32, 0x1a, 0xe3, 0x69 );
//------------------------------------------------------------------------------
// DXDIAG declarations.
struct DXDIAG_INIT_PARAMS
{
DWORD dwSize;
DWORD dwDxDiagHeaderVersion;
BOOL bAllowWHQLChecks;
LPVOID pReserved;
};
struct IDxDiagContainer : public IUnknown
{
virtual HRESULT STDMETHODCALLTYPE GetNumberOfChildContaiiners( DWORD* pdwCount ) = 0;
virtual HRESULT STDMETHODCALLTYPE EnumChildContainerNames( DWORD dwIndex, LPWSTR pwszContainer, DWORD cchContainer ) = 0;
virtual HRESULT STDMETHODCALLTYPE GetChildContainer( LPCWSTR pwszContainer, IDxDiagContainer** ppInstance ) = 0;
virtual HRESULT STDMETHODCALLTYPE GetNumberOfProps( DWORD* pdwCount ) = 0;
virtual HRESULT STDMETHODCALLTYPE EnumPropNames( DWORD dwIndex, LPWSTR pwszPropName, DWORD cchPropName ) = 0;
virtual HRESULT STDMETHODCALLTYPE GetProp( LPCWSTR pwszPropName, VARIANT* pvarProp ) = 0;
};
struct IDxDiagProvider : public IUnknown
{
virtual HRESULT STDMETHODCALLTYPE Initialize( DXDIAG_INIT_PARAMS* pParams ) = 0;
virtual HRESULT STDMETHODCALLTYPE GetRootContainer( IDxDiagContainer** ppInstance ) = 0;
};
static MYGUID CLSID_DxDiagProvider( 0xA65B8071, 0x3BFE, 0x4213, 0x9A, 0x5B, 0x49, 0x1D, 0xA4, 0x46, 0x1C, 0xA7 );
static MYGUID IID_IDxDiagProvider( 0x9C6B4CB0, 0x23F8, 0x49CC, 0xA3, 0xED, 0x45, 0xA5, 0x50, 0x00, 0xA6, 0xD2 );
static MYGUID IID_IDxDiagContainer( 0x7D0F462F, 0x4064, 0x4862, 0xBC, 0x7F, 0x93, 0x3E, 0x50, 0x58, 0xC1, 0x0F );
//------------------------------------------------------------------------------
WCHAR *WMIVideoInfo::smPVIQueryTypeToWMIString [] =
{
L"MaxNumberControlled", //PVI_NumDevices
L"Description", //PVI_Description
L"Name", //PVI_Name
L"VideoProcessor", //PVI_ChipSet
L"DriverVersion", //PVI_DriverVersion
L"AdapterRAM", //PVI_VRAM
};
//------------------------------------------------------------------------------
WMIVideoInfo::WMIVideoInfo()
: PlatformVideoInfo(),
mLocator( NULL ),
mServices( NULL ),
mComInitialized( false ),
mDXGIModule( NULL ),
mDXGIFactory( NULL ),
mDxDiagProvider( NULL )
{
}
//------------------------------------------------------------------------------
WMIVideoInfo::~WMIVideoInfo()
{
SAFE_RELEASE( mLocator );
SAFE_RELEASE( mServices );
if( mDxDiagProvider )
SAFE_RELEASE( mDxDiagProvider );
if( mDXGIFactory )
SAFE_RELEASE( mDXGIFactory );
if( mDXGIModule )
FreeLibrary( ( HMODULE ) mDXGIModule );
if( mComInitialized )
CoUninitialize();
}
//------------------------------------------------------------------------------
bool WMIVideoInfo::_initialize()
{
// Init COM
HRESULT hr = CoInitialize( NULL );
mComInitialized = SUCCEEDED( hr );
if( !mComInitialized )
return false;
bool success = false;
success |= _initializeDXGI();
success |= _initializeDxDiag();
success |= _initializeWMI();
return success;
}
bool WMIVideoInfo::_initializeWMI()
{
//// Set security levels
//hr = CoInitializeSecurity(
// NULL,
// -1, // COM authentication
// NULL, // Authentication services
// NULL, // Reserved
// RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
// RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
// NULL, // Authentication info
// EOAC_NONE, // Additional capabilities
// NULL // Reserved
// );
//if( FAILED( hr ) )
//{
// Con::errorf( "WMIVideoInfo: Failed to initialize com security." );
// return false;
//}
// Obtain the locator to WMI
HRESULT hr = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator,
(void**)&mLocator
);
if( FAILED( hr ) )
{
Con::errorf( "WMIVideoInfo: Failed to create instance of IID_IWbemLocator." );
return false;
}
// Connect to the root\cimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hr = mLocator->ConnectServer(
BSTR(L"ROOT\\CIMV2"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (e.g. Kerberos)
0, // Context object
&mServices // pointer to IWbemServices proxy
);
if( FAILED( hr ) )
{
Con::errorf( "WMIVideoInfo: Connect server failed." );
return false;
}
// Set security levels on the proxy
hr = CoSetProxyBlanket(
mServices, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if( FAILED( hr ) )
{
Con::errorf( "WMIVideoInfo: CoSetProxyBlanket failed" );
return false;
}
return true;
}
bool WMIVideoInfo::_initializeDXGI()
{
// Try going for DXGI. Will only succeed on Vista.
#if 0
mDXGIModule = ( HMODULE ) LoadLibrary( L"dxgi.dll" );
if( mDXGIModule != 0 )
{
typedef HRESULT (* CreateDXGIFactoryFuncType )( REFIID, void** );
CreateDXGIFactoryFuncType factoryFunction =
( CreateDXGIFactoryFuncType ) GetProcAddress( ( HMODULE ) mDXGIModule, "CreateDXGIFactory" );
if( factoryFunction && factoryFunction( IID_IDXGIFactory, ( void** ) &mDXGIFactory ) == S_OK )
return true;
else
{
FreeLibrary( ( HMODULE ) mDXGIModule );
mDXGIModule = 0;
}
}
#endif
return false;
}
bool WMIVideoInfo::_initializeDxDiag()
{
if( CoCreateInstance( CLSID_DxDiagProvider, NULL, CLSCTX_INPROC_SERVER, IID_IDxDiagProvider, ( void** ) &mDxDiagProvider ) == S_OK )
{
DXDIAG_INIT_PARAMS params;
dMemset( &params, 0, sizeof( DXDIAG_INIT_PARAMS ) );
params.dwSize = sizeof( DXDIAG_INIT_PARAMS );
params.dwDxDiagHeaderVersion = 111;
params.bAllowWHQLChecks = false;
HRESULT result = mDxDiagProvider->Initialize( &params );
if( result != S_OK )
{
Con::errorf( "WMIVideoInfo: DxDiag initialization failed (%i)", result );
SAFE_RELEASE( mDxDiagProvider );
return false;
}
else
{
Con::printf( "WMIVideoInfo: DxDiag initialized" );
return true;
}
}
return false;
}
//------------------------------------------------------------------------------
// http://msdn2.microsoft.com/en-us/library/aa394512.aspx
//
// The Win32_VideoController WMI class represents the capabilities and management capacity of the
// video controller on a computer system running Windows.
//
// Starting with Windows Vista, hardware that is not compatible with Windows Display Driver Model (WDDM)
// returns inaccurate property values for instances of this class.
//
// Windows Server 2003, Windows XP, Windows 2000, and Windows NT 4.0: This class is supported.
//------------------------------------------------------------------------------
bool WMIVideoInfo::_queryProperty( const PVIQueryType queryType, const U32 adapterId, String *outValue )
{
if( _queryPropertyDXGI( queryType, adapterId, outValue ) )
return true;
else if( _queryPropertyDxDiag( queryType, adapterId, outValue ) )
return true;
else
return _queryPropertyWMI( queryType, adapterId, outValue );
}
bool WMIVideoInfo::_queryPropertyDxDiag( const PVIQueryType queryType, const U32 adapterId, String *outValue )
{
if( mDxDiagProvider != 0 )
{
IDxDiagContainer* rootContainer = 0;
IDxDiagContainer* displayDevicesContainer = 0;
IDxDiagContainer* deviceContainer = 0;
WCHAR adapterIdString[ 2 ];
adapterIdString[ 0 ] = L'0' + adapterId;
adapterIdString[ 1 ] = L'\0';
String value;
if( mDxDiagProvider->GetRootContainer( &rootContainer ) == S_OK
&& rootContainer->GetChildContainer( L"DxDiag_DisplayDevices", &displayDevicesContainer ) == S_OK
&& displayDevicesContainer->GetChildContainer( adapterIdString, &deviceContainer ) == S_OK )
{
const WCHAR* propertyName = 0;
switch( queryType )
{
case PVI_Description:
propertyName = L"szDescription";
break;
case PVI_Name:
propertyName = L"szDeviceName";
break;
case PVI_ChipSet:
propertyName = L"szChipType";
break;
case PVI_DriverVersion:
propertyName = L"szDriverVersion";
break;
// Don't get VRAM via DxDiag as that won't tell us about the actual amount of dedicated
// video memory but rather some dedicated+shared RAM value.
}
if( propertyName )
{
VARIANT val;
if( deviceContainer->GetProp( propertyName, &val ) == S_OK )
switch( val.vt )
{
case VT_BSTR:
value = String( val.bstrVal );
break;
default:
AssertWarn( false, avar( "WMIVideoInfo: property type '%i' not implemented", val.vt ) );
}
}
}
if( rootContainer )
SAFE_RELEASE( rootContainer );
if( displayDevicesContainer )
SAFE_RELEASE( displayDevicesContainer );
if( deviceContainer )
SAFE_RELEASE( deviceContainer );
if( value.isNotEmpty() )
{
// Try to get the DxDiag data into some canonical form. Otherwise, we
// won't be giving the card profiler much opportunity for matching up
// its data with profile scripts.
switch( queryType )
{
case PVI_ChipSet:
if( value.compare( "ATI", 3, String::NoCase ) == 0 )
value = "ATI Technologies Inc.";
else if( value.compare( "NVIDIA", 6, String::NoCase ) == 0 )
value = "NVIDIA";
else if( value.compare( "INTEL", 5, String::NoCase ) == 0 )
value = "INTEL";
else if( value.compare( "MATROX", 6, String::NoCase ) == 0 )
value = "MATROX";
break;
case PVI_Description:
if( value.compare( "ATI ", 4, String::NoCase ) == 0 )
{
value = value.substr( 4, value.length() - 4 );
if( value.compare( " Series", 7, String::NoCase | String::Right ) == 0 )
value = value.substr( 0, value.length() - 7 );
}
else if( value.compare( "NVIDIA ", 7, String::NoCase ) == 0 )
value = value.substr( 7, value.length() - 7 );
else if( value.compare( "INTEL ", 6, String::NoCase ) == 0 )
value = value.substr( 6, value.length() - 6 );
else if( value.compare( "MATROX ", 7, String::NoCase ) == 0 )
value = value.substr( 7, value.length() - 7 );
break;
}
*outValue = value;
return true;
}
}
return false;
}
bool WMIVideoInfo::_queryPropertyDXGI( const PVIQueryType queryType, const U32 adapterId, String *outValue )
{
#if 0
if( mDXGIFactory )
{
IDXGIAdapter* adapter;
if( mDXGIFactory->EnumAdapters( adapterId, &adapter ) != S_OK )
return false;
DXGI_ADAPTER_DESC desc;
if( adapter->GetDesc( &desc ) != S_OK )
{
adapter->Release();
return false;
}
String value;
switch( queryType )
{
case PVI_Description:
value = String( desc.Description );
break;
case PVI_Name:
value = String( avar( "%i", desc.DeviceId ) );
break;
case PVI_VRAM:
value = String( avar( "%i", desc.DedicatedVideoMemory / 1048576 ) );
break;
//RDTODO
}
adapter->Release();
*outValue = value;
return true;
}
#endif
return false;
}
bool WMIVideoInfo::_queryPropertyWMI( const PVIQueryType queryType, const U32 adapterId, String *outValue )
{
if( mServices == NULL )
return false;
BSTR bstrWQL = SysAllocString(L"WQL");
BSTR bstrPath = SysAllocString(L"select * from Win32_VideoController");
IEnumWbemClassObject* enumerator;
// Use the IWbemServices pointer to make requests of WMI
HRESULT hr = mServices->ExecQuery(bstrWQL, bstrPath, WBEM_FLAG_FORWARD_ONLY, NULL, &enumerator);
if( FAILED( hr ) )
return false;
IWbemClassObject *adapter = NULL;
ULONG uReturned;
// Get the appropriate adapter.
for ( S32 i = 0; i <= adapterId; i++ )
{
hr = enumerator->Next(WBEM_INFINITE, 1, &adapter, &uReturned );
if ( FAILED( hr ) || uReturned == 0 )
{
enumerator->Release();
return false;
}
}
// Now get the property
VARIANT v;
hr = adapter->Get( smPVIQueryTypeToWMIString[queryType], 0, &v, NULL, NULL );
bool result = SUCCEEDED( hr );
if ( result )
{
switch( v.vt )
{
case VT_I4:
{
LONG longVal = v.lVal;
if( queryType == PVI_VRAM )
longVal = longVal >> 20; // Convert to megabytes
*outValue = String::ToString( (S32)longVal );
break;
}
case VT_UI4:
{
*outValue = String::ToString( (U32)v.ulVal );
break;
}
case VT_BSTR:
{
*outValue = String( v.bstrVal );
break;
}
case VT_LPSTR:
case VT_LPWSTR:
break;
}
}
// Cleanup
adapter->Release();
enumerator->Release();
return result;
}

View file

@ -0,0 +1,63 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _WMI_CARDINFO_H_
#define _WMI_CARDINFO_H_
#include "platform/platformVideoInfo.h"
struct IWbemLocator;
struct IWbemServices;
struct IDXGIFactory;
struct IDxDiagProvider;
class WMIVideoInfo : public PlatformVideoInfo
{
private:
IWbemLocator *mLocator;
IWbemServices *mServices;
bool mComInitialized;
void* mDXGIModule;
IDXGIFactory* mDXGIFactory;
IDxDiagProvider* mDxDiagProvider;
bool _initializeDXGI();
bool _initializeDxDiag();
bool _initializeWMI();
bool _queryPropertyDXGI( const PVIQueryType queryType, const U32 adapterId, String *outValue );
bool _queryPropertyDxDiag( const PVIQueryType queryType, const U32 adapterId, String *outValue );
bool _queryPropertyWMI( const PVIQueryType queryType, const U32 adapterId, String *outValue );
protected:
static WCHAR *smPVIQueryTypeToWMIString [];
bool _queryProperty( const PVIQueryType queryType, const U32 adapterId, String *outValue );
bool _initialize();
public:
WMIVideoInfo();
~WMIVideoInfo();
};
#endif

View file

@ -0,0 +1,207 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "math/mMath.h"
#include "gfx/bitmap/gBitmap.h"
#include "gfx/bitmap/bitmapUtils.h"
#if !defined(__MWERKS__) && defined(_MSC_VER)
#define asm _asm
#endif
//--------------------------------------------------------------------------
void bitmapExtrude5551_asm(const void *srcMip, void *mip, U32 height, U32 width)
{
const U16 *src = (const U16 *) srcMip;
U16 *dst = (U16 *) mip;
U32 stride = width << 1;
for(U32 y = 0; y < height; y++)
{
for(U32 x = 0; x < width; x++)
{
U32 a = src[0];
U32 b = src[1];
U32 c = src[stride];
U32 d = src[stride+1];
dst[x] = ((((a >> 11) + (b >> 11) + (c >> 11) + (d >> 11)) >> 2) << 11) |
((( ((a >> 6) & 0x1f) + ((b >> 6) & 0x1f) + ((c >> 6) & 0x1f) + ((d >> 6) & 0x1F) ) >> 2) << 6) |
((( ((a >> 1) & 0x1F) + ((b >> 1) & 0x1F) + ((c >> 1) & 0x1f) + ((d >> 1) & 0x1f)) >> 2) << 1);
src += 2;
}
src += stride;
dst += width;
}
}
#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM)
//--------------------------------------------------------------------------
void bitmapExtrudeRGB_mmx(const void *srcMip, void *mip, U32 srcHeight, U32 srcWidth)
{
if (srcHeight == 1 || srcWidth == 1) {
bitmapExtrudeRGB_c(srcMip, mip, srcHeight, srcWidth);
return;
}
U32 width = srcWidth >> 1;
U32 height = srcHeight >> 1;
if (width <= 1)
{
bitmapExtrudeRGB_c(srcMip, mip, srcHeight, srcWidth);
return;
}
U64 ZERO = 0x0000000000000000;
const U8 *src = (const U8 *) srcMip;
U8 *dst = (U8 *) mip;
U32 srcStride = (width << 1) * 3;
U32 dstStride = width * 3;
for(U32 y = 0; y < height; y++)
{
asm
{
mov eax, src
mov ebx, eax
add ebx, srcStride
mov ecx, dst
mov edx, width
//--------------------------------------
row_loop:
punpcklbw mm0, [eax]
psrlw mm0, 8
punpcklbw mm1, [eax+3]
psrlw mm1, 8
paddw mm0, mm1
punpcklbw mm1, [ebx]
psrlw mm1, 8
paddw mm0, mm1
punpcklbw mm1, [ebx+3]
psrlw mm1, 8
paddw mm0, mm1
psrlw mm0, 2
//pxor mm1, mm1
packuswb mm0, ZERO // mm1
movd [ecx], mm0
add eax, 6
add ebx, 6
add ecx, 3
dec edx
jnz row_loop
}
src += srcStride + srcStride; // advance to next line
dst += dstStride;
}
asm
{
emms
}
}
//--------------------------------------------------------------------------
void bitmapConvertRGB_to_5551_mmx(U8 *src, U32 pixels)
{
U64 MULFACT = 0x0008200000082000; // RGB quad word multiplier
U64 REDBLUE = 0x00f800f800f800f8; // Red-Blue mask
U64 GREEN = 0x0000f8000000f800; // Green mask
U64 ALPHA = 0x0000000000010001; // 100% Alpha mask
U64 ZERO = 0x0000000000000000;
U32 evenPixels = pixels >> 1; // the MMX loop can only do an even number
U32 oddPixels = pixels & 1; // of pixels since it processes 2 at a time
U16 *dst = (U16*)src;
if (evenPixels)
{
asm
{
mov eax, src // YES, src = dst at start
mov ebx, dst // convert image in place
mov edx, evenPixels
pixel_loop2:
movd mm0, [eax] // get first 24-bit pixel
movd mm1, [eax+3] // get second 24-bit pixel
punpckldq mm0, mm1 // put second in high dword
movq mm1, mm0 // save the original data
pand mm0, REDBLUE // mask out all but the 5MSBits of red and blue
pmaddwd mm0, MULFACT // multiply each word by
// 2**13, 2**3, 2**13, 2**3 and add results
pand mm1, GREEN // mask out all but the 5MSBits of green
por mm0, mm1 // combine the red, green, and blue bits
psrld mm0, 6 // shift into position
packssdw mm0, ZERO // pack into single dword
pslld mm0, 1 // shift into final position
por mm0, ALPHA // add the alpha bit
movd [ebx], mm0
add eax, 6
add ebx, 4
dec edx
jnz pixel_loop2
mov src, eax
mov dst, ebx
emms
}
}
if (oddPixels)
{
U32 r = src[0] >> 3;
U32 g = src[1] >> 3;
U32 b = src[2] >> 3;
*dst = (b << 1) | (g << 6) | (r << 11) | 1;
}
}
#endif
//--------------------------------------------------------------------------
void PlatformBlitInit()
{
bitmapExtrude5551 = bitmapExtrude5551_asm;
bitmapExtrudeRGB = bitmapExtrudeRGB_c;
if (Platform::SystemInfo.processor.properties & CPU_PROP_MMX)
{
#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM)
bitmapExtrudeRGB = bitmapExtrudeRGB_mmx;
bitmapConvertRGB_to_5551 = bitmapConvertRGB_to_5551_mmx;
#endif
}
}

View file

@ -0,0 +1,84 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
// For VS2005.
#define _WIN32_WINNT 0x501
#ifndef TORQUE_OS_XENON
#include "platformWin32/platformWin32.h"
#endif
#include "platform/async/asyncUpdate.h"
AsyncUpdateThread::AsyncUpdateThread( String name, AsyncUpdateList* updateList )
: Parent( 0, 0, false, false ),
mUpdateList( updateList ),
mName( name )
{
// Create an auto-reset event in non-signaled state.
mUpdateEvent = CreateEvent( NULL, false, false, NULL );
}
AsyncUpdateThread::~AsyncUpdateThread()
{
CloseHandle( ( HANDLE ) mUpdateEvent );
}
void AsyncUpdateThread::_waitForEventAndReset()
{
WaitForSingleObject( ( HANDLE ) mUpdateEvent, INFINITE );
}
void AsyncUpdateThread::triggerUpdate()
{
SetEvent( ( HANDLE ) mUpdateEvent );
}
AsyncPeriodicUpdateThread::AsyncPeriodicUpdateThread( String name,
AsyncUpdateList* updateList,
U32 intervalMS )
: Parent( name, updateList )
{
mUpdateTimer = CreateWaitableTimer( NULL, FALSE, NULL );
// This is a bit contrived. The 'dueTime' is in 100 nanosecond intervals
// and relative if it is negative. The period is in milliseconds.
LARGE_INTEGER deltaTime;
deltaTime.QuadPart = - LONGLONG( intervalMS * 10 /* micro */ * 1000 /* milli */ );
SetWaitableTimer( ( HANDLE ) mUpdateTimer, &deltaTime, intervalMS, NULL, NULL, FALSE );
}
AsyncPeriodicUpdateThread::~AsyncPeriodicUpdateThread()
{
CloseHandle( ( HANDLE ) mUpdateTimer );
}
void AsyncPeriodicUpdateThread::_waitForEventAndReset()
{
HANDLE handles[ 2 ];
handles[ 0 ] = ( HANDLE ) mUpdateEvent;
handles[ 1 ] = ( HANDLE ) mUpdateTimer;
WaitForMultipleObjects( 2, handles, FALSE, INFINITE );
}

View file

@ -0,0 +1,197 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "platformWin32/platformWin32.h"
#include "console/console.h"
#include "core/stringTable.h"
#include <math.h>
Platform::SystemInfo_struct Platform::SystemInfo;
extern void PlatformBlitInit();
extern void SetProcessorInfo(Platform::SystemInfo_struct::Processor& pInfo,
char* vendor, U32 processor, U32 properties, U32 properties2); // platform/platformCPU.cc
#if defined(TORQUE_SUPPORTS_NASM)
// asm cpu detection routine from platform code
extern "C"
{
void detectX86CPUInfo(char *vendor, U32 *processor, U32 *properties);
}
#endif
void Processor::init()
{
// Reference:
// www.cyrix.com
// www.amd.com
// www.intel.com
// http://developer.intel.com/design/PentiumII/manuals/24512701.pdf
Con::printf("Processor Init:");
Platform::SystemInfo.processor.type = CPU_X86Compatible;
Platform::SystemInfo.processor.name = StringTable->insert("Unknown x86 Compatible");
Platform::SystemInfo.processor.mhz = 0;
Platform::SystemInfo.processor.properties = CPU_PROP_C | CPU_PROP_LE;
char vendor[13] = {0,};
U32 properties = 0;
U32 processor = 0;
U32 properties2 = 0;
#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM)
__asm
{
//--------------------------------------
// is CPUID supported
push ebx
push edx
push ecx
pushfd
pushfd // save EFLAGS to stack
pop eax // move EFLAGS into EAX
mov ebx, eax
xor eax, 0x200000 // flip bit 21
push eax
popfd // restore EFLAGS
pushfd
pop eax
cmp eax, ebx
jz EXIT // doesn't support CPUID instruction
//--------------------------------------
// Get Vendor Informaion using CPUID eax==0
xor eax, eax
cpuid
mov DWORD PTR vendor, ebx
mov DWORD PTR vendor+4, edx
mov DWORD PTR vendor+8, ecx
// get Generic Extended CPUID info
mov eax, 1
cpuid // eax=1, so cpuid queries feature information
and eax, 0x0fff3fff
mov processor, eax // just store the model bits
mov properties, edx
mov properties2, ecx
// Want to check for 3DNow(tm). Need to see if extended cpuid functions present.
mov eax, 0x80000000
cpuid
cmp eax, 0x80000000
jbe MAYBE_3DLATER
mov eax, 0x80000001
cpuid
and edx, 0x80000000 // 3DNow if bit 31 set -> put bit in our properties
or properties, edx
MAYBE_3DLATER:
EXIT:
popfd
pop ecx
pop edx
pop ebx
}
#elif defined(TORQUE_SUPPORTS_NASM)
detectX86CPUInfo(vendor, &processor, &properties);
#endif
SetProcessorInfo(Platform::SystemInfo.processor, vendor, processor, properties, properties2);
// now calculate speed of processor...
U32 nearmhz = 0; // nearest rounded mhz
U32 mhz = 0; // calculated value.
LONG result;
DWORD data = 0;
DWORD dataSize = 4;
HKEY hKey;
result = ::RegOpenKeyExA (HKEY_LOCAL_MACHINE,"Hardware\\Description\\System\\CentralProcessor\\0", 0, KEY_QUERY_VALUE, &hKey);
if (result == ERROR_SUCCESS)
{
result = ::RegQueryValueExA (hKey, "~MHz",NULL, NULL,(LPBYTE)&data, &dataSize);
if (result == ERROR_SUCCESS)
nearmhz = mhz = data;
::RegCloseKey(hKey);
}
Platform::SystemInfo.processor.mhz = mhz;
if (mhz==0)
{
Con::printf(" %s, (Unknown) Mhz", Platform::SystemInfo.processor.name);
// stick SOMETHING in so it isn't ZERO.
Platform::SystemInfo.processor.mhz = 200; // seems a decent value.
}
else
{
if (nearmhz >= 1000)
Con::printf(" %s, ~%.2f Ghz", Platform::SystemInfo.processor.name, ((float)nearmhz)/1000.0f);
else
Con::printf(" %s, ~%d Mhz", Platform::SystemInfo.processor.name, nearmhz);
if (nearmhz != mhz)
{
if (mhz >= 1000)
Con::printf(" (timed at roughly %.2f Ghz)", ((float)mhz)/1000.0f);
else
Con::printf(" (timed at roughly %d Mhz)", mhz);
}
}
if( Platform::SystemInfo.processor.numAvailableCores > 0
|| Platform::SystemInfo.processor.numPhysicalProcessors > 0
|| Platform::SystemInfo.processor.isHyperThreaded )
Platform::SystemInfo.processor.properties |= CPU_PROP_MP;
if (Platform::SystemInfo.processor.properties & CPU_PROP_FPU)
Con::printf( " FPU detected" );
if (Platform::SystemInfo.processor.properties & CPU_PROP_MMX)
Con::printf( " MMX detected" );
if (Platform::SystemInfo.processor.properties & CPU_PROP_3DNOW)
Con::printf( " 3DNow detected" );
if (Platform::SystemInfo.processor.properties & CPU_PROP_SSE)
Con::printf( " SSE detected" );
if( Platform::SystemInfo.processor.properties & CPU_PROP_SSE2 )
Con::printf( " SSE2 detected" );
if( Platform::SystemInfo.processor.isHyperThreaded )
Con::printf( " HT detected" );
if( Platform::SystemInfo.processor.properties & CPU_PROP_MP )
Con::printf( " MP detected [%i cores, %i logical, %i physical]",
Platform::SystemInfo.processor.numAvailableCores,
Platform::SystemInfo.processor.numLogicalProcessors,
Platform::SystemInfo.processor.numPhysicalProcessors );
Con::printf(" ");
PlatformBlitInit();
}

View file

@ -0,0 +1,341 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/util/rawData.h"
#include "core/strings/stringFunctions.h"
#include "core/strings/unicode.h"
#include "platformWin32/platformWin32.h"
#include "platformWin32/winConsole.h"
#include "console/consoleTypes.h"
#include "core/util/journal/process.h"
WinConsole *WindowsConsole = NULL;
namespace Con
{
extern bool alwaysUseDebugOutput;
}
ConsoleFunction(enableWinConsole, void, 2, 2, "enableWinConsole(bool);")
{
argc;
WindowsConsole->enable(dAtob(argv[1]));
}
void WinConsole::create()
{
if( !WindowsConsole )
WindowsConsole = new WinConsole();
}
void WinConsole::destroy()
{
if( WindowsConsole )
delete WindowsConsole;
WindowsConsole = NULL;
}
void WinConsole::enable(bool enabled)
{
winConsoleEnabled = enabled;
if(winConsoleEnabled)
{
AllocConsole();
const char *title = Con::getVariable("Con::WindowTitle");
if (title && *title)
{
#ifdef UNICODE
UTF16 buf[512];
convertUTF8toUTF16((UTF8 *)title, buf, sizeof(buf));
SetConsoleTitle(buf);
#else
SetConsoleTitle(title);
#endif
}
stdOut = GetStdHandle(STD_OUTPUT_HANDLE);
stdIn = GetStdHandle(STD_INPUT_HANDLE);
stdErr = GetStdHandle(STD_ERROR_HANDLE);
printf("%s", Con::getVariable("Con::Prompt"));
}
}
bool WinConsole::isEnabled()
{
if ( WindowsConsole )
return WindowsConsole->winConsoleEnabled;
return false;
}
static void winConsoleConsumer(U32 level, const char *line)
{
if (WindowsConsole)
{
WindowsConsole->processConsoleLine(line);
#ifndef TORQUE_SHIPPING
// see console.cpp for a description of Con::alwaysUseDebugOutput
if( level == ConsoleLogEntry::Error || Con::alwaysUseDebugOutput)
{
// [rene, 04/05/2008] This is incorrect. Should do conversion from UTF8 here.
// Skipping for the sake of speed. Not meant to be seen by user anyway.
OutputDebugStringA( line );
OutputDebugStringA( "\n" );
}
#endif
}
}
WinConsole::WinConsole()
{
for (S32 iIndex = 0; iIndex < MAX_CMDS; iIndex ++)
rgCmds[iIndex][0] = '\0';
iCmdIndex = 0;
winConsoleEnabled = false;
Con::addConsumer(winConsoleConsumer);
inpos = 0;
lineOutput = false;
Process::notify(this, &WinConsole::process, PROCESS_LAST_ORDER);
}
WinConsole::~WinConsole()
{
Process::remove(this, &WinConsole::process);
Con::removeConsumer(winConsoleConsumer);
}
void WinConsole::printf(const char *s, ...)
{
// Get the line into a buffer.
static const int BufSize = 4096;
static char buffer[4096];
DWORD bytes;
va_list args;
va_start(args, s);
_vsnprintf(buffer, BufSize, s, args);
// Replace tabs with carats, like the "real" console does.
char *pos = buffer;
while (*pos) {
if (*pos == '\t') {
*pos = '^';
}
pos++;
}
// Axe the color characters.
Con::stripColorChars(buffer);
// Print it.
WriteFile(stdOut, buffer, strlen(buffer), &bytes, NULL);
FlushFileBuffers( stdOut );
}
void WinConsole::processConsoleLine(const char *consoleLine)
{
if(winConsoleEnabled)
{
inbuf[inpos] = 0;
if(lineOutput)
printf("%s\n", consoleLine);
else
printf("%c%s\n%s%s", '\r', consoleLine, Con::getVariable("Con::Prompt"), inbuf);
}
}
void WinConsole::process()
{
if(winConsoleEnabled)
{
DWORD numEvents;
GetNumberOfConsoleInputEvents(stdIn, &numEvents);
if(numEvents)
{
INPUT_RECORD rec[20];
char outbuf[512];
S32 outpos = 0;
ReadConsoleInput(stdIn, rec, 20, &numEvents);
DWORD i;
for(i = 0; i < numEvents; i++)
{
if(rec[i].EventType == KEY_EVENT)
{
KEY_EVENT_RECORD *ke = &(rec[i].Event.KeyEvent);
if(ke->bKeyDown)
{
switch (ke->uChar.AsciiChar)
{
// If no ASCII char, check if it's a handled virtual key
case 0:
switch (ke->wVirtualKeyCode)
{
// UP ARROW
case 0x26 :
// Go to the previous command in the cyclic array
if ((-- iCmdIndex) < 0)
iCmdIndex = MAX_CMDS - 1;
// If this command isn't empty ...
if (rgCmds[iCmdIndex][0] != '\0')
{
// Obliterate current displayed text
for (S32 i = outpos = 0; i < inpos; i ++)
{
outbuf[outpos ++] = '\b';
outbuf[outpos ++] = ' ';
outbuf[outpos ++] = '\b';
}
// Copy command into command and display buffers
for (inpos = 0; inpos < (S32)strlen(rgCmds[iCmdIndex]); inpos ++, outpos ++)
{
outbuf[outpos] = rgCmds[iCmdIndex][inpos];
inbuf [inpos ] = rgCmds[iCmdIndex][inpos];
}
}
// If previous is empty, stay on current command
else if ((++ iCmdIndex) >= MAX_CMDS)
{
iCmdIndex = 0;
}
break;
// DOWN ARROW
case 0x28 : {
// Go to the next command in the command array, if
// it isn't empty
if (rgCmds[iCmdIndex][0] != '\0' && (++ iCmdIndex) >= MAX_CMDS)
iCmdIndex = 0;
// Obliterate current displayed text
for (S32 i = outpos = 0; i < inpos; i ++)
{
outbuf[outpos ++] = '\b';
outbuf[outpos ++] = ' ';
outbuf[outpos ++] = '\b';
}
// Copy command into command and display buffers
for (inpos = 0; inpos < (S32)strlen(rgCmds[iCmdIndex]); inpos ++, outpos ++)
{
outbuf[outpos] = rgCmds[iCmdIndex][inpos];
inbuf [inpos ] = rgCmds[iCmdIndex][inpos];
}
}
break;
// LEFT ARROW
case 0x25 :
break;
// RIGHT ARROW
case 0x27 :
break;
default :
break;
}
break;
case '\b':
if(inpos > 0)
{
outbuf[outpos++] = '\b';
outbuf[outpos++] = ' ';
outbuf[outpos++] = '\b';
inpos--;
}
break;
case '\t':
// In the output buffer, we're going to have to erase the current line (in case
// we're cycling through various completions) and write out the whole input
// buffer, so (inpos * 3) + complen <= 512. Should be OK. The input buffer is
// also 512 chars long so that constraint will also be fine for the input buffer.
{
// Erase the current line.
U32 i;
for (i = 0; i < inpos; i++) {
outbuf[outpos++] = '\b';
outbuf[outpos++] = ' ';
outbuf[outpos++] = '\b';
}
// Modify the input buffer with the completion.
U32 maxlen = 512 - (inpos * 3);
if (ke->dwControlKeyState & SHIFT_PRESSED) {
inpos = Con::tabComplete(inbuf, inpos, maxlen, false);
}
else {
inpos = Con::tabComplete(inbuf, inpos, maxlen, true);
}
// Copy the input buffer to the output.
for (i = 0; i < inpos; i++) {
outbuf[outpos++] = inbuf[i];
}
}
break;
case '\n':
case '\r':
outbuf[outpos++] = '\r';
outbuf[outpos++] = '\n';
inbuf[inpos] = 0;
outbuf[outpos] = 0;
printf("%s", outbuf);
// Pass the line along to the console for execution.
{
RawData rd;
rd.size = inpos + 1;
rd.data = ( S8* ) inbuf;
Con::smConsoleInput.trigger(rd);
}
// If we've gone off the end of our array, wrap
// back to the beginning
if (iCmdIndex >= MAX_CMDS)
iCmdIndex %= MAX_CMDS;
// Put the new command into the array
strcpy(rgCmds[iCmdIndex ++], inbuf);
printf("%s", Con::getVariable("Con::Prompt"));
inpos = outpos = 0;
break;
default:
inbuf[inpos++] = ke->uChar.AsciiChar;
outbuf[outpos++] = ke->uChar.AsciiChar;
break;
}
}
}
}
if(outpos)
{
outbuf[outpos] = 0;
printf("%s", outbuf);
}
}
}
}

View file

@ -0,0 +1,65 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _WINCONSOLE_H_
#define _WINCONSOLE_H_
#define MAX_CMDS 10
#ifndef _CONSOLE_H_
#include "console/console.h"
#endif
#ifndef _EVENT_H_
#include "platform/event.h"
#endif
class WinConsole
{
bool winConsoleEnabled;
HANDLE stdOut;
HANDLE stdIn;
HANDLE stdErr;
char inbuf[512];
S32 inpos;
bool lineOutput;
char curTabComplete[512];
S32 tabCompleteStart;
char rgCmds[MAX_CMDS][512];
S32 iCmdIndex;
void printf(const char *s, ...);
public:
WinConsole();
~WinConsole();
void process();
void enable(bool);
void processConsoleLine(const char *consoleLine);
static void create();
static void destroy();
static bool isEnabled();
};
extern WinConsole *WindowsConsole;
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,162 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _WINDINPUTDEVICE_H_
#define _WINDINPUTDEVICE_H_
#ifndef _PLATFORMWIN32_H_
#include "platformWin32/platformWin32.h"
#endif
#ifndef _PLATFORMINPUT_H_
#include "platform/platformInput.h"
#endif
#ifndef _EVENT_H_
#include "platform/event.h"
#endif
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
class DInputDevice : public InputDevice
{
public:
static LPDIRECTINPUT8 smDInputInterface;
protected:
enum Constants
{
QUEUED_BUFFER_SIZE = 128,
SIZEOF_BUTTON = 1, // size of an object's data in bytes
SIZEOF_KEY = 1,
SIZEOF_AXIS = 4,
SIZEOF_POV = 4,
};
static U8 smDeviceCount[ NUM_INPUT_DEVICE_TYPES ];
static bool smInitialized;
/// Are we an XInput device?
bool mIsXInput;
//--------------------------------------
LPDIRECTINPUTDEVICE8 mDevice;
DIDEVICEINSTANCE mDeviceInstance;
DIDEVCAPS mDeviceCaps;
U8 mDeviceType;
U8 mDeviceID;
bool mAcquired;
bool mNeedSync;
LPDIRECTINPUTEFFECT mForceFeedbackEffect; ///< Holds our DirectInput FF Effect
DWORD mNumForceFeedbackAxes; ///< # axes (we only support 0, 1, or 2
DWORD mForceFeedbackAxes[2]; ///< Force Feedback axes offsets into DIOBJECTFORMAT
//--------------------------------------
DIDEVICEOBJECTINSTANCE* mObjInstance;
DIOBJECTDATAFORMAT* mObjFormat;
ObjInfo* mObjInfo;
U8* mObjBuffer1; // polled device input buffers
U8* mObjBuffer2;
U8* mPrevObjBuffer; // points to buffer 1 or 2
// Hack for POV
S32 mPrevPOVPos;
U32 mObjBufferSize; // size of objBuffer*
U32 mObjCount; // number of objects on this device
U32 mObjEnumCount; // used during enumeration ONLY
U32 mObjBufferOfs; // used during enumeration ONLY
static BOOL CALLBACK EnumObjectsProc( const DIDEVICEOBJECTINSTANCE *doi, LPVOID pvRef );
bool enumerateObjects();
bool processAsync();
bool processImmediate();
DWORD findObjInstance( DWORD offset );
bool buildEvent( DWORD offset, S32 newData, S32 oldData );
public:
DInputDevice( const DIDEVICEINSTANCE* deviceInst );
~DInputDevice();
static void init();
bool create();
void destroy();
bool acquire();
bool unacquire();
bool isAcquired();
bool isPolled();
U8 getDeviceType();
U8 getDeviceID();
const char* getName();
const char* getProductName();
// Constant Effect Force Feedback
void rumble( float x, float y );
// Console interface functions:
const char* getJoystickAxesString();
static bool joystickDetected();
//
bool process();
};
//------------------------------------------------------------------------------
inline bool DInputDevice::isAcquired()
{
return mAcquired;
}
//------------------------------------------------------------------------------
inline bool DInputDevice::isPolled()
{
//return true;
return ( mDeviceCaps.dwFlags & DIDC_POLLEDDEVICE ) != 0;
}
//------------------------------------------------------------------------------
inline U8 DInputDevice::getDeviceType()
{
return mDeviceType;
}
//------------------------------------------------------------------------------
inline U8 DInputDevice::getDeviceID()
{
return mDeviceID;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
InputObjectInstances DIK_to_Key( U8 dikCode );
U8 Key_to_DIK( U16 keyCode );
#endif // _H_WINDINPUTDEVICE_

View file

@ -0,0 +1,954 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platformWin32/platformWin32.h"
#include "platformWin32/winDirectInput.h"
#include "platformWin32/winDInputDevice.h"
#include "platform/event.h"
#include "console/console.h"
#include "console/consoleTypes.h"
#include "sim/actionMap.h"
#include <xinput.h>
//------------------------------------------------------------------------------
// Static class variables:
bool DInputManager::smJoystickEnabled = true;
bool DInputManager::smXInputEnabled = true;
// Type definitions:
typedef HRESULT (WINAPI* FN_DirectInputCreate)(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter);
//------------------------------------------------------------------------------
DInputManager::DInputManager()
{
mEnabled = false;
mDInputLib = NULL;
mDInputInterface = NULL;
mJoystickActive = mXInputActive = true;
mXInputLib = NULL;
for(S32 i=0; i<4; i++)
mLastDisconnectTime[i] = -1;
}
//------------------------------------------------------------------------------
void DInputManager::init()
{
Con::addVariable( "pref::Input::JoystickEnabled", TypeBool, &smJoystickEnabled,
"@brief If true, the joystick is currently enabled.\n\n"
"@ingroup Game");
}
//------------------------------------------------------------------------------
bool DInputManager::enable()
{
FN_DirectInputCreate fnDInputCreate;
disable();
// Dynamically load the XInput 9 DLL and cache function pointers to the
// two APIs we use
#ifdef LOG_INPUT
Input::log( "Enabling XInput...\n" );
#endif
mXInputLib = LoadLibrary( dT("xinput9_1_0.dll") );
if ( mXInputLib )
{
mfnXInputGetState = (FN_XInputGetState) GetProcAddress( mXInputLib, "XInputGetState" );
mfnXInputSetState = (FN_XInputSetState) GetProcAddress( mXInputLib, "XInputSetState" );
if ( mfnXInputGetState && mfnXInputSetState )
{
#ifdef LOG_INPUT
Input::log( "XInput detected.\n" );
#endif
mXInputStateReset = true;
mXInputDeadZoneOn = true;
smXInputEnabled = true;
}
}
else
{
#ifdef LOG_INPUT
Input::log( "XInput was not found.\n" );
mXInputStateReset = false;
mXInputDeadZoneOn = false;
#endif
}
#ifdef LOG_INPUT
Input::log( "Enabling DirectInput...\n" );
#endif
mDInputLib = LoadLibrary( dT("DInput8.dll") );
if ( mDInputLib )
{
fnDInputCreate = (FN_DirectInputCreate) GetProcAddress( mDInputLib, "DirectInput8Create" );
if ( fnDInputCreate )
{
bool result = SUCCEEDED( fnDInputCreate( winState.appInstance, DIRECTINPUT_VERSION, IID_IDirectInput8, reinterpret_cast<void**>(&mDInputInterface), NULL ));
if ( result )
{
#ifdef LOG_INPUT
Input::log( "DirectX 8 or greater detected.\n" );
#endif
}
if ( result )
{
enumerateDevices();
mEnabled = true;
return true;
}
}
}
disable();
#ifdef LOG_INPUT
Input::log( "Failed to enable DirectInput.\n" );
#endif
return false;
}
//------------------------------------------------------------------------------
void DInputManager::disable()
{
unacquire( SI_ANY, SI_ANY );
DInputDevice* dptr;
iterator ptr = begin();
while ( ptr != end() )
{
dptr = dynamic_cast<DInputDevice*>( *ptr );
if ( dptr )
{
removeObject( dptr );
//if ( dptr->getManager() )
//dptr->getManager()->unregisterObject( dptr );
delete dptr;
ptr = begin();
}
else
ptr++;
}
if ( mDInputInterface )
{
mDInputInterface->Release();
mDInputInterface = NULL;
}
if ( mDInputLib )
{
FreeLibrary( mDInputLib );
mDInputLib = NULL;
}
if ( mfnXInputGetState )
{
mXInputStateReset = true;
mfnXInputGetState = NULL;
mfnXInputSetState = NULL;
}
if ( mXInputLib )
{
FreeLibrary( mXInputLib );
mXInputLib = NULL;
}
mEnabled = false;
}
//------------------------------------------------------------------------------
void DInputManager::onDeleteNotify( SimObject* object )
{
Parent::onDeleteNotify( object );
}
//------------------------------------------------------------------------------
bool DInputManager::onAdd()
{
if ( !Parent::onAdd() )
return false;
acquire( SI_ANY, SI_ANY );
return true;
}
//------------------------------------------------------------------------------
void DInputManager::onRemove()
{
unacquire( SI_ANY, SI_ANY );
Parent::onRemove();
}
//------------------------------------------------------------------------------
bool DInputManager::acquire( U8 deviceType, U8 deviceID )
{
bool anyActive = false;
DInputDevice* dptr;
for ( iterator ptr = begin(); ptr != end(); ptr++ )
{
dptr = dynamic_cast<DInputDevice*>( *ptr );
if ( dptr
&& ( ( deviceType == SI_ANY ) || ( dptr->getDeviceType() == deviceType ) )
&& ( ( deviceID == SI_ANY ) || ( dptr->getDeviceID() == deviceID ) ) )
{
if ( dptr->acquire() )
anyActive = true;
}
}
return anyActive;
}
//------------------------------------------------------------------------------
void DInputManager::unacquire( U8 deviceType, U8 deviceID )
{
DInputDevice* dptr;
for ( iterator ptr = begin(); ptr != end(); ptr++ )
{
dptr = dynamic_cast<DInputDevice*>( *ptr );
if ( dptr
&& ( ( deviceType == SI_ANY ) || ( dptr->getDeviceType() == deviceType ) )
&& ( ( deviceID == SI_ANY ) || ( dptr->getDeviceID() == deviceID ) ) )
dptr->unacquire();
}
}
//------------------------------------------------------------------------------
void DInputManager::process()
{
// Because the XInput APIs manage everything for all four controllers trivially,
// we don't need the abstraction of a DInputDevice for an unknown number of devices
// with varying buttons & whistles... nice and easy!
if ( isXInputActive() )
processXInput();
DInputDevice* dptr;
for ( iterator ptr = begin(); ptr != end(); ptr++ )
{
dptr = dynamic_cast<DInputDevice*>( *ptr );
if ( dptr )
dptr->process();
}
}
//------------------------------------------------------------------------------
void DInputManager::enumerateDevices()
{
if ( mDInputInterface )
{
#ifdef LOG_INPUT
Input::log( "Enumerating input devices...\n" );
#endif
DInputDevice::init();
DInputDevice::smDInputInterface = mDInputInterface;
mDInputInterface->EnumDevices( DI8DEVTYPE_KEYBOARD, EnumDevicesProc, this, DIEDFL_ATTACHEDONLY );
mDInputInterface->EnumDevices( DI8DEVTYPE_MOUSE, EnumDevicesProc, this, DIEDFL_ATTACHEDONLY );
mDInputInterface->EnumDevices( DI8DEVCLASS_GAMECTRL, EnumDevicesProc, this, DIEDFL_ATTACHEDONLY );
}
}
//------------------------------------------------------------------------------
BOOL CALLBACK DInputManager::EnumDevicesProc( const DIDEVICEINSTANCE* pddi, LPVOID pvRef )
{
DInputManager* manager = (DInputManager*) pvRef;
DInputDevice* newDevice = new DInputDevice( pddi );
manager->addObject( newDevice );
if ( !newDevice->create() )
{
manager->removeObject( newDevice );
delete newDevice;
}
return (DIENUM_CONTINUE);
}
//------------------------------------------------------------------------------
bool DInputManager::enableJoystick()
{
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
if ( !mgr || !mgr->isEnabled() )
return( false );
if ( smJoystickEnabled && mgr->isJoystickActive() )
return( true );
smJoystickEnabled = true;
if ( Input::isActive() )
smJoystickEnabled = mgr->activateJoystick();
if ( smJoystickEnabled )
{
Con::printf( "DirectInput joystick enabled." );
#ifdef LOG_INPUT
Input::log( "Joystick enabled.\n" );
#endif
}
else
{
Con::warnf( "DirectInput joystick failed to enable!" );
#ifdef LOG_INPUT
Input::log( "Joystick failed to enable!\n" );
#endif
}
return( smJoystickEnabled );
}
//------------------------------------------------------------------------------
void DInputManager::disableJoystick()
{
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
if ( !mgr || !mgr->isEnabled() || !smJoystickEnabled )
return;
mgr->deactivateJoystick();
smJoystickEnabled = false;
Con::printf( "DirectInput joystick disabled." );
#ifdef LOG_INPUT
Input::log( "Joystick disabled.\n" );
#endif
}
//------------------------------------------------------------------------------
bool DInputManager::isJoystickEnabled()
{
return( smJoystickEnabled );
}
//------------------------------------------------------------------------------
bool DInputManager::activateJoystick()
{
if ( !mEnabled || !Input::isActive() || !smJoystickEnabled )
return( false );
mJoystickActive = acquire( JoystickDeviceType, SI_ANY );
#ifdef LOG_INPUT
Input::log( mJoystickActive ? "Joystick activated.\n" : "Joystick failed to activate!\n" );
#endif
return( mJoystickActive );
}
//------------------------------------------------------------------------------
void DInputManager::deactivateJoystick()
{
if ( mEnabled && mJoystickActive )
{
unacquire( JoystickDeviceType, SI_ANY );
mJoystickActive = false;
#ifdef LOG_INPUT
Input::log( "Joystick deactivated.\n" );
#endif
}
}
//------------------------------------------------------------------------------
const char* DInputManager::getJoystickAxesString( U32 deviceID )
{
DInputDevice* dptr;
for ( iterator ptr = begin(); ptr != end(); ptr++ )
{
dptr = dynamic_cast<DInputDevice*>( *ptr );
if ( dptr && ( dptr->getDeviceType() == JoystickDeviceType ) && ( dptr->getDeviceID() == deviceID ) )
return( dptr->getJoystickAxesString() );
}
return( "" );
}
//------------------------------------------------------------------------------
bool DInputManager::enableXInput()
{
// Largely, this series of functions is identical to the Joystick versions,
// except that XInput cannot be "activated" or "deactivated". You either have
// the DLL or you don't. Beyond that, you have up to four controllers
// connected at any given time
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
if ( !mgr || !mgr->isEnabled() )
return( false );
if ( mgr->isXInputActive() )
return( true );
if ( Input::isActive() )
mgr->activateXInput();
if ( smXInputEnabled )
{
Con::printf( "XInput enabled." );
#ifdef LOG_INPUT
Input::log( "XInput enabled.\n" );
#endif
}
else
{
Con::warnf( "XInput failed to enable!" );
#ifdef LOG_INPUT
Input::log( "XInput failed to enable!\n" );
#endif
}
return( smXInputEnabled );
}
//------------------------------------------------------------------------------
void DInputManager::disableXInput()
{
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
if ( !mgr || !mgr->isEnabled())
return;
mgr->deactivateXInput();
Con::printf( "XInput disabled." );
#ifdef LOG_INPUT
Input::log( "XInput disabled.\n" );
#endif
}
//------------------------------------------------------------------------------
bool DInputManager::isXInputEnabled()
{
return( smXInputEnabled );
}
//------------------------------------------------------------------------------
bool DInputManager::isXInputConnected(int controllerID)
{
return( mXInputStateNew[controllerID].bConnected );
}
int DInputManager::getXInputState(int controllerID, int property, bool current)
{
int retVal;
switch(property)
{
#define CHECK_PROP_ANALOG(prop, stateTest) \
case prop: (current) ? retVal = mXInputStateNew[controllerID].state.Gamepad.##stateTest : retVal = mXInputStateOld[controllerID].state.Gamepad.##stateTest; return retVal;
CHECK_PROP_ANALOG(XI_THUMBLX, sThumbLX)
CHECK_PROP_ANALOG(XI_THUMBLY, sThumbLY)
CHECK_PROP_ANALOG(XI_THUMBRX, sThumbRX)
CHECK_PROP_ANALOG(XI_THUMBRY, sThumbRY)
CHECK_PROP_ANALOG(XI_LEFT_TRIGGER, bLeftTrigger)
CHECK_PROP_ANALOG(XI_RIGHT_TRIGGER, bRightTrigger)
#undef CHECK_PROP_ANALOG
#define CHECK_PROP_DIGITAL(prop, stateTest) \
case prop: (current) ? retVal = (( mXInputStateNew[controllerID].state.Gamepad.wButtons & stateTest ) != 0 ) : retVal = (( mXInputStateOld[controllerID].state.Gamepad.wButtons & stateTest ) != 0 ); return retVal;
CHECK_PROP_DIGITAL(SI_UPOV, XINPUT_GAMEPAD_DPAD_UP)
CHECK_PROP_DIGITAL(SI_DPOV, XINPUT_GAMEPAD_DPAD_DOWN)
CHECK_PROP_DIGITAL(SI_LPOV, XINPUT_GAMEPAD_DPAD_LEFT)
CHECK_PROP_DIGITAL(SI_RPOV, XINPUT_GAMEPAD_DPAD_RIGHT)
CHECK_PROP_DIGITAL(XI_START, XINPUT_GAMEPAD_START)
CHECK_PROP_DIGITAL(XI_BACK, XINPUT_GAMEPAD_BACK)
CHECK_PROP_DIGITAL(XI_LEFT_THUMB, XINPUT_GAMEPAD_LEFT_THUMB)
CHECK_PROP_DIGITAL(XI_RIGHT_THUMB, XINPUT_GAMEPAD_RIGHT_THUMB)
CHECK_PROP_DIGITAL(XI_LEFT_SHOULDER, XINPUT_GAMEPAD_LEFT_SHOULDER)
CHECK_PROP_DIGITAL(XI_RIGHT_SHOULDER, XINPUT_GAMEPAD_RIGHT_SHOULDER)
CHECK_PROP_DIGITAL(XI_A, XINPUT_GAMEPAD_A)
CHECK_PROP_DIGITAL(XI_B, XINPUT_GAMEPAD_B)
CHECK_PROP_DIGITAL(XI_X, XINPUT_GAMEPAD_X)
CHECK_PROP_DIGITAL(XI_Y, XINPUT_GAMEPAD_Y)
#undef CHECK_PROP_DIGITAL
}
return -1;
}
//------------------------------------------------------------------------------
bool DInputManager::activateXInput()
{
if ( !mEnabled || !Input::isActive())
return( false );
mXInputActive = true; //acquire( GamepadDeviceType, SI_ANY );
#ifdef LOG_INPUT
Input::log( mXInputActive ? "XInput activated.\n" : "XInput failed to activate!\n" );
#endif
return( mXInputActive );
}
//------------------------------------------------------------------------------
void DInputManager::deactivateXInput()
{
if ( mEnabled && mXInputActive )
{
unacquire( GamepadDeviceType, SI_ANY );
mXInputActive = false;
#ifdef LOG_INPUT
Input::log( "XInput deactivated.\n" );
#endif
}
}
//------------------------------------------------------------------------------
bool DInputManager::rumble( const char *pDeviceName, float x, float y )
{
// Determine the device
U32 deviceType;
U32 deviceInst;
// Find the requested device
if ( !ActionMap::getDeviceTypeAndInstance(pDeviceName, deviceType, deviceInst) )
{
Con::printf("DInputManager::rumble: unknown device: %s", pDeviceName);
return false;
}
// clamp (x, y) to the range of [0 ... 1] each
x = mClampF(x, 0.f, 1.f);
y = mClampF(y, 0.f, 1.f);
// Easy path for xinput devices.
if(deviceType == GamepadDeviceType)
{
XINPUT_VIBRATION vibration;
vibration.wLeftMotorSpeed = static_cast<int>(x * 65535);
vibration.wRightMotorSpeed = static_cast<int>(y * 65535);
return ( mfnXInputSetState(deviceInst, &vibration) == ERROR_SUCCESS );
}
switch ( deviceType )
{
case JoystickDeviceType:
// Find the device and shake it!
DInputDevice* dptr;
for ( iterator ptr = begin(); ptr != end(); ptr++ )
{
dptr = dynamic_cast<DInputDevice*>( *ptr );
if ( dptr )
{
if (deviceType == dptr->getDeviceType() && deviceInst == dptr->getDeviceID())
{
dptr->rumble(x, y);
return true;
}
}
}
// We should never get here... something's really messed up
Con::errorf( "DInputManager::rumbleJoystick - Couldn't find device to rumble! This should never happen!\n" );
return false;
default:
Con::printf("DInputManager::rumble - only supports joystick and xinput/gamepad devices");
return false;
}
}
void DInputManager::buildXInputEvent( U32 deviceInst, InputEventType objType, InputObjectInstances objInst, InputActionType action, float fValue )
{
InputEventInfo newEvent;
newEvent.deviceType = GamepadDeviceType;
newEvent.deviceInst = deviceInst;
newEvent.objType = objType;
newEvent.objInst = objInst;
newEvent.action = action;
newEvent.fValue = fValue;
newEvent.postToSignal(Input::smInputEvent);
}
// The next three functions: fireXInputConnectEvent, fireXInputMoveEvent, and fireXInputButtonEvent
// determine whether a "delta" has occurred between the last captured controller state and the
// currently captured controller state and only if so, do we fire an event. The shortcutter
// "mXInputStateReset" is the exception and is true whenever DirectInput gets reset (because
// the user ALT-TABBED away, for example). That means that after every context switch,
// you will get a full set of updates on the "true" state of the controller.
inline void DInputManager::fireXInputConnectEvent( int controllerID, bool condition, bool connected )
{
if ( mXInputStateReset || condition )
{
#ifdef LOG_INPUT
Input::log( "EVENT (XInput): xinput%d CONNECT %s\n", controllerID, connected ? "make" : "break" );
#endif
buildXInputEvent( controllerID, SI_BUTTON, XI_CONNECT, connected ? SI_MAKE : SI_BREAK, 0);
}
}
inline void DInputManager::fireXInputMoveEvent( int controllerID, bool condition, InputObjectInstances objInst, float fValue )
{
if ( mXInputStateReset || condition )
{
#ifdef LOG_INPUT
char *objName;
switch (objInst)
{
case XI_THUMBLX: objName = "THUMBLX"; break;
case XI_THUMBLY: objName = "THUMBLY"; break;
case XI_THUMBRX: objName = "THUMBRX"; break;
case XI_THUMBRY: objName = "THUMBRY"; break;
case XI_LEFT_TRIGGER: objName = "LEFT_TRIGGER"; break;
case XI_RIGHT_TRIGGER: objName = "RIGHT_TRIGGER"; break;
default: objName = "UNKNOWN"; break;
}
Input::log( "EVENT (XInput): xinput%d %s MOVE %.1f.\n", controllerID, objName, fValue );
#endif
buildXInputEvent( controllerID, SI_AXIS, objInst, SI_MOVE, fValue );
}
}
inline void DInputManager::fireXInputButtonEvent( int controllerID, bool forceFire, int button, InputObjectInstances objInst )
{
if ( mXInputStateReset || forceFire || ((mXInputStateNew[controllerID].state.Gamepad.wButtons & button) != (mXInputStateOld[controllerID].state.Gamepad.wButtons & button)) )
{
#ifdef LOG_INPUT
char *objName;
switch (objInst)
{
/*
case XI_DPAD_UP: objName = "DPAD_UP"; break;
case XI_DPAD_DOWN: objName = "DPAD_DOWN"; break;
case XI_DPAD_LEFT: objName = "DPAD_LEFT"; break;
case XI_DPAD_RIGHT: objName = "DPAD_RIGHT"; break;
*/
case XI_START: objName = "START"; break;
case XI_BACK: objName = "BACK"; break;
case XI_LEFT_THUMB: objName = "LEFT_THUMB"; break;
case XI_RIGHT_THUMB: objName = "RIGHT_THUMB"; break;
case XI_LEFT_SHOULDER: objName = "LEFT_SHOULDER"; break;
case XI_RIGHT_SHOULDER: objName = "RIGHT_SHOULDER"; break;
case XI_A: objName = "A"; break;
case XI_B: objName = "B"; break;
case XI_X: objName = "X"; break;
case XI_Y: objName = "Y"; break;
default: objName = "UNKNOWN"; break;
}
Input::log( "EVENT (XInput): xinput%d %s %s\n", controllerID, objName, ((mXInputStateNew[controllerID].state.Gamepad.wButtons & button) != 0) ? "make" : "break" );
#endif
InputActionType action = ((mXInputStateNew[controllerID].state.Gamepad.wButtons & button) != 0) ? SI_MAKE : SI_BREAK;
buildXInputEvent( controllerID, SI_BUTTON, objInst, action, ( action == SI_MAKE ? 1 : 0 ) );
}
}
void DInputManager::processXInput( void )
{
const U32 curTime = Platform::getRealMilliseconds();
// We only want to check one disconnected device per frame.
bool foundDisconnected = false;
if ( mfnXInputGetState )
{
for ( int i=0; i<4; i++ )
{
// Calling XInputGetState on a disconnected controller takes a fair
// amount of time (probably because it tries to locate it), so we
// add a delay - only check every 250ms or so.
if(mLastDisconnectTime[i] != -1)
{
// If it's not -1, then it was disconnected list time we checked.
// So skip until it's time.
if((curTime-mLastDisconnectTime[i]) < csmDisconnectedSkipDelay)
{
continue;
}
// If we already checked a disconnected controller, don't try any
// further potentially disconnected devices.
if(foundDisconnected)
{
// If we're skipping this, defer it out by the skip delay
// so we don't get clumped checks.
mLastDisconnectTime[i] += csmDisconnectedSkipDelay;
continue;
}
}
mXInputStateOld[i] = mXInputStateNew[i];
mXInputStateNew[i].bConnected = ( mfnXInputGetState( i, &mXInputStateNew[i].state ) == ERROR_SUCCESS );
// Update the last connected time.
if(mXInputStateNew[i].bConnected)
mLastDisconnectTime[i] = -1;
else
{
foundDisconnected = true;
mLastDisconnectTime[i] = curTime;
}
// trim the controller's thumbsticks to zero if they are within the deadzone
if( mXInputDeadZoneOn )
{
// Zero value if thumbsticks are within the dead zone
if( (mXInputStateNew[i].state.Gamepad.sThumbLX < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbLX > -XINPUT_DEADZONE) &&
(mXInputStateNew[i].state.Gamepad.sThumbLY < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbLY > -XINPUT_DEADZONE) )
{
mXInputStateNew[i].state.Gamepad.sThumbLX = 0;
mXInputStateNew[i].state.Gamepad.sThumbLY = 0;
}
if( (mXInputStateNew[i].state.Gamepad.sThumbRX < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbRX > -XINPUT_DEADZONE) &&
(mXInputStateNew[i].state.Gamepad.sThumbRY < XINPUT_DEADZONE && mXInputStateNew[i].state.Gamepad.sThumbRY > -XINPUT_DEADZONE) )
{
mXInputStateNew[i].state.Gamepad.sThumbRX = 0;
mXInputStateNew[i].state.Gamepad.sThumbRY = 0;
}
}
// this controller was connected or disconnected
bool bJustConnected = ( ( mXInputStateOld[i].bConnected != mXInputStateNew[i].bConnected ) && ( mXInputStateNew[i].bConnected ) );
fireXInputConnectEvent( i, (mXInputStateOld[i].bConnected != mXInputStateNew[i].bConnected), mXInputStateNew[i].bConnected );
// If this controller is disconnected, stop reporting events for it
if ( !mXInputStateNew[i].bConnected )
continue;
// == LEFT THUMBSTICK ==
fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbLX != mXInputStateOld[i].state.Gamepad.sThumbLX), XI_THUMBLX, (mXInputStateNew[i].state.Gamepad.sThumbLX / 32768.0f) );
fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbLY != mXInputStateOld[i].state.Gamepad.sThumbLY), XI_THUMBLY, (mXInputStateNew[i].state.Gamepad.sThumbLY / 32768.0f) );
// == RIGHT THUMBSTICK ==
fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbRX != mXInputStateOld[i].state.Gamepad.sThumbRX), XI_THUMBRX, (mXInputStateNew[i].state.Gamepad.sThumbRX / 32768.0f) );
fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.sThumbRY != mXInputStateOld[i].state.Gamepad.sThumbRY), XI_THUMBRY, (mXInputStateNew[i].state.Gamepad.sThumbRY / 32768.0f) );
// == LEFT & RIGHT REAR TRIGGERS ==
fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.bLeftTrigger != mXInputStateOld[i].state.Gamepad.bLeftTrigger), XI_LEFT_TRIGGER, (mXInputStateNew[i].state.Gamepad.bLeftTrigger / 255.0f) );
fireXInputMoveEvent( i, ( bJustConnected ) || (mXInputStateNew[i].state.Gamepad.bRightTrigger != mXInputStateOld[i].state.Gamepad.bRightTrigger), XI_RIGHT_TRIGGER, (mXInputStateNew[i].state.Gamepad.bRightTrigger / 255.0f) );
// == BUTTONS: DPAD ==
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_UP, SI_UPOV );
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_DOWN, SI_DPOV );
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_LEFT, SI_LPOV );
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_DPAD_RIGHT, SI_RPOV );
// == BUTTONS: START & BACK ==
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_START, XI_START );
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_BACK, XI_BACK );
// == BUTTONS: LEFT AND RIGHT THUMBSTICK ==
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_LEFT_THUMB, XI_LEFT_THUMB );
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_RIGHT_THUMB, XI_RIGHT_THUMB );
// == BUTTONS: LEFT AND RIGHT SHOULDERS (formerly WHITE and BLACK on Xbox 1) ==
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_LEFT_SHOULDER, XI_LEFT_SHOULDER );
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_RIGHT_SHOULDER, XI_RIGHT_SHOULDER );
// == BUTTONS: A, B, X, and Y ==
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_A, XI_A );
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_B, XI_B );
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_X, XI_X );
fireXInputButtonEvent( i, bJustConnected, XINPUT_GAMEPAD_Y, XI_Y );
}
if ( mXInputStateReset )
mXInputStateReset = false;
}
}
ConsoleFunction( enableJoystick, bool, 1, 1, "()"
"@brief Enables use of the joystick.\n\n"
"@note DirectInput must be enabled and active to use this function.\n\n"
"@ingroup Input")
{
argc; argv;
return( DInputManager::enableJoystick() );
}
//------------------------------------------------------------------------------
ConsoleFunction( disableJoystick, void, 1, 1,"()"
"@brief Disables use of the joystick.\n\n"
"@note DirectInput must be enabled and active to use this function.\n\n"
"@ingroup Input")
{
argc; argv;
DInputManager::disableJoystick();
}
//------------------------------------------------------------------------------
ConsoleFunction( isJoystickEnabled, bool, 1, 1, "()"
"@brief Queries input manager to see if a joystick is enabled\n\n"
"@return 1 if a joystick exists and is enabled, 0 if it's not.\n"
"@ingroup Input")
{
argc; argv;
return DInputManager::isJoystickEnabled();
}
//------------------------------------------------------------------------------
ConsoleFunction( enableXInput, bool, 1, 1, "()"
"@brief Enables XInput for Xbox 360 controllers.\n\n"
"@note XInput is enabled by default. Disable to use an Xbox 360 "
"Controller as a joystick device.\n\n"
"@ingroup Input")
{
// Although I said above that you couldn't change the "activation" of XInput,
// you can enable and disable it. It gets enabled by default if you have the
// DLL. You would want to disable it if you have 360 controllers and want to
// read them as joysticks... why you'd want to do that is beyond me
argc; argv;
return( DInputManager::enableXInput() );
}
//------------------------------------------------------------------------------
ConsoleFunction( disableXInput, void, 1, 1, "()"
"@brief Disables XInput for Xbox 360 controllers.\n\n"
"@ingroup Input")
{
argc; argv;
DInputManager::disableXInput();
}
//------------------------------------------------------------------------------
ConsoleFunction( resetXInput, void, 1, 1, "()"
"@brief Rebuilds the XInput section of the InputManager\n\n"
"Requests a full refresh of events for all controllers. Useful when called at the beginning "
"of game code after actionMaps are set up to hook up all appropriate events.\n\n"
"@ingroup Input")
{
// This function requests a full "refresh" of events for all controllers the
// next time we go through the input processing loop. This is useful to call
// at the beginning of your game code after your actionMap is set up to hook
// all of the appropriate events
argc; argv;
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
if ( mgr && mgr->isEnabled() )
mgr->resetXInput();
}
//------------------------------------------------------------------------------
ConsoleFunction( isXInputConnected, bool, 2, 2, "( int controllerID )"
"@brief Checks to see if an Xbox 360 controller is connected\n\n"
"@param controllerID Zero-based index of the controller to check.\n"
"@return 1 if the controller is connected, 0 if it isn't, and 205 if XInput "
"hasn't been initialized."
"@ingroup Input")
{
argc; argv;
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
if ( mgr && mgr->isEnabled() ) return mgr->isXInputConnected( atoi( argv[1] ) );
return false;
}
//------------------------------------------------------------------------------
ConsoleFunction( getXInputState, int, 3, 4, "( int controllerID, string property, bool current )"
"@brief Queries the current state of a connected Xbox 360 controller.\n\n"
"XInput Properties:\n\n"
" - XI_THUMBLX, XI_THUMBLY - X and Y axes of the left thumbstick. \n"
" - XI_THUMBRX, XI_THUMBRY - X and Y axes of the right thumbstick. \n"
" - XI_LEFT_TRIGGER, XI_RIGHT_TRIGGER - Left and Right triggers. \n"
" - SI_UPOV, SI_DPOV, SI_LPOV, SI_RPOV - Up, Down, Left, and Right on the directional pad.\n"
" - XI_START, XI_BACK - The Start and Back buttons.\n"
" - XI_LEFT_THUMB, XI_RIGHT_THUMB - Clicking in the left and right thumbstick.\n"
" - XI_LEFT_SHOULDER, XI_RIGHT_SHOULDER - Left and Right bumpers.\n"
" - XI_A, XI_B , XI_X, XI_Y - The A, B, X, and Y buttons.\n\n"
"@param controllerID Zero-based index of the controller to return information about.\n"
"@param property Name of input action being queried, such as \"XI_THUMBLX\".\n"
"@param current True checks current device in action.\n"
"@return Button queried - 1 if the button is pressed, 0 if it's not.\n"
"@return Thumbstick queried - Int representing displacement from rest position."
"@return %Trigger queried - Int from 0 to 255 representing how far the trigger is displaced."
"@ingroup Input")
{
argc; argv;
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
if ( !mgr || !mgr->isEnabled() )
return -1;
// Use a little bit of macro magic to simplify this otherwise monolothic
// block of code.
#define GET_XI_STATE(constName) \
if (!dStricmp(argv[2], #constName)) \
return mgr->getXInputState( dAtoi( argv[1] ), constName, ( dAtoi ( argv[3] ) == 1) );
GET_XI_STATE(XI_THUMBLX);
GET_XI_STATE(XI_THUMBLY);
GET_XI_STATE(XI_THUMBRX);
GET_XI_STATE(XI_THUMBRY);
GET_XI_STATE(XI_LEFT_TRIGGER);
GET_XI_STATE(XI_RIGHT_TRIGGER);
GET_XI_STATE(SI_UPOV);
GET_XI_STATE(SI_DPOV);
GET_XI_STATE(SI_LPOV);
GET_XI_STATE(SI_RPOV);
GET_XI_STATE(XI_START);
GET_XI_STATE(XI_BACK);
GET_XI_STATE(XI_LEFT_THUMB);
GET_XI_STATE(XI_RIGHT_THUMB);
GET_XI_STATE(XI_LEFT_SHOULDER);
GET_XI_STATE(XI_RIGHT_SHOULDER);
GET_XI_STATE(XI_A);
GET_XI_STATE(XI_B);
GET_XI_STATE(XI_X);
GET_XI_STATE(XI_Y);
#undef GET_XI_STATE
return -1;
}
//------------------------------------------------------------------------------
ConsoleFunction( echoInputState, void, 1, 1, "()"
"@brief Prints information to the console stating if DirectInput and a Joystick are enabled and active.\n\n"
"@ingroup Input")
{
argc; argv;
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
if ( mgr && mgr->isEnabled() )
{
Con::printf( "DirectInput is enabled %s.", Input::isActive() ? "and active" : "but inactive" );
Con::printf( "- Joystick is %sabled and %sactive.",
mgr->isJoystickEnabled() ? "en" : "dis",
mgr->isJoystickActive() ? "" : "in" );
}
else
Con::printf( "DirectInput is not enabled." );
}
ConsoleFunction( rumble, void, 4, 4, "(string device, float xRumble, float yRumble)"
"@brief Activates the vibration motors in the specified controller.\n\n"
"The controller will constantly at it's xRumble and yRumble intensities until "
"changed or told to stop."
"Valid inputs for xRumble/yRumble are [0 - 1].\n"
"@param device Name of the device to rumble.\n"
"@param xRumble Intensity to apply to the left motor.\n"
"@param yRumble Intensity to apply to the right motor.\n"
"@note in an Xbox 360 controller, the left motor is low-frequency, "
"while the right motor is high-frequency."
"@ingroup Input")
{
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
if ( mgr && mgr->isEnabled() )
{
mgr->rumble(argv[1], dAtof(argv[2]), dAtof(argv[3]));
}
else
{
Con::printf( "DirectInput/XInput is not enabled." );
}
}

View file

@ -0,0 +1,132 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _WINDIRECTINPUT_H_
#define _WINDIRECTINPUT_H_
#ifndef _PLATFORMWIN32_H_
#include "platformWin32/platformWin32.h"
#endif
#ifndef _PLATFORMINPUT_H_
#include "platform/platformInput.h"
#endif
#ifndef _WINDINPUTDEVICE_H_
#include "platformWin32/winDInputDevice.h"
#endif
#define DIRECTINPUT_VERSION 0x0800
#include <dinput.h>
#include <xinput.h>
// XInput related definitions
typedef DWORD (WINAPI* FN_XInputGetState)(DWORD dwUserIndex, XINPUT_STATE* pState);
typedef DWORD (WINAPI* FN_XInputSetState)(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration);
#define XINPUT_MAX_CONTROLLERS 4 // XInput handles up to 4 controllers
#define XINPUT_DEADZONE ( 0.24f * FLOAT(0x7FFF) ) // Default to 24% of the +/- 32767 range. This is a reasonable default value but can be altered if needed.
struct XINPUT_CONTROLLER_STATE
{
XINPUT_STATE state;
bool bConnected;
};
//------------------------------------------------------------------------------
class DInputManager : public InputManager
{
private:
typedef SimGroup Parent;
// XInput state
HMODULE mXInputLib;
FN_XInputGetState mfnXInputGetState;
FN_XInputSetState mfnXInputSetState;
XINPUT_CONTROLLER_STATE mXInputStateOld[XINPUT_MAX_CONTROLLERS];
XINPUT_CONTROLLER_STATE mXInputStateNew[XINPUT_MAX_CONTROLLERS];
U32 mLastDisconnectTime[XINPUT_MAX_CONTROLLERS];
bool mXInputStateReset;
bool mXInputDeadZoneOn;
/// Number of milliseconds to skip checking an xinput device if it was
/// disconnected on last check.
const static U32 csmDisconnectedSkipDelay = 250;
HMODULE mDInputLib;
LPDIRECTINPUT8 mDInputInterface;
static bool smJoystickEnabled;
static bool smXInputEnabled;
bool mJoystickActive;
bool mXInputActive;
void enumerateDevices();
static BOOL CALLBACK EnumDevicesProc( const DIDEVICEINSTANCE *pddi, LPVOID pvRef );
bool acquire( U8 deviceType, U8 deviceID );
void unacquire( U8 deviceType, U8 deviceID );
// XInput worker functions
void buildXInputEvent( U32 deviceInst, InputEventType objType, InputObjectInstances objInst, InputActionType action, float fValue );
void fireXInputConnectEvent( int controllerID, bool condition, bool connected );
void fireXInputMoveEvent( int controllerID, bool condition, InputObjectInstances objInst, float fValue );
void fireXInputButtonEvent( int controllerID, bool forceFire, int button, InputObjectInstances objInst );
void processXInput();
public:
DInputManager();
bool enable();
void disable();
void onDeleteNotify( SimObject* object );
bool onAdd();
void onRemove();
void process();
// DirectInput functions:
static void init();
static bool enableJoystick();
static void disableJoystick();
static bool isJoystickEnabled();
bool activateJoystick();
void deactivateJoystick();
bool isJoystickActive() { return( mJoystickActive ); }
static bool enableXInput();
static void disableXInput();
static bool isXInputEnabled();
bool activateXInput();
void deactivateXInput();
bool isXInputActive() { return( mXInputActive ); }
void resetXInput() { mXInputStateReset = true; }
bool isXInputConnected(int controllerID);
int getXInputState(int controllerID, int property, bool current);
// Console interface:
const char* getJoystickAxesString( U32 deviceID );
bool rumble( const char *pDeviceName, float x, float y );
};
#endif // _H_WINDIRECTINPUT_

View file

@ -0,0 +1,92 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "platform/types.h"
#include "platform/platformDlibrary.h"
class Win32DLibrary: public DLibrary
{
HMODULE _handle;
public:
Win32DLibrary();
~Win32DLibrary();
bool open(const char* file);
void close();
void *bind(const char *name);
};
Win32DLibrary::Win32DLibrary()
{
_handle = 0;
}
Win32DLibrary::~Win32DLibrary()
{
close();
}
bool Win32DLibrary::open(const char* file)
{
// dlopen should also include the RTLD_LOCAL flag, but it seems to be
// missing from cygwin
_handle = LoadLibraryA(file);
if (!_handle)
return false;
bool (*open)() = (bool(*)())bind("dllopen");
if (open && !(*open)()) {
FreeLibrary(_handle);
_handle = 0;
return false;
}
return true;
}
void Win32DLibrary::close()
{
if (_handle) {
void (*close)() = (void(*)())bind("dllclose");
if (close)
(*close)();
FreeLibrary(_handle);
_handle = 0;
}
}
void* Win32DLibrary::bind(const char *name)
{
return _handle? (void*)GetProcAddress(_handle,name): 0;
}
DLibraryRef OsLoadLibrary(const char* file)
{
Win32DLibrary* library = new Win32DLibrary();
if (!library->open(file)) {
delete library;
return 0;
}
return library;
}

View file

@ -0,0 +1,188 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platformWin32/platformWin32.h"
#include "console/console.h"
#include "console/simBase.h"
#include "core/strings/unicode.h"
#include "platform/threads/thread.h"
#include "platform/threads/mutex.h"
#include "core/util/safeDelete.h"
#include "util/tempAlloc.h"
//-----------------------------------------------------------------------------
// Thread for executing in
//-----------------------------------------------------------------------------
class ExecuteThread : public Thread
{
// [tom, 12/14/2006] mProcess is only used in the constructor before the thread
// is started and in the thread itself so we should be OK without a mutex.
HANDLE mProcess;
public:
ExecuteThread(const char *executable, const char *args = NULL, const char *directory = NULL);
virtual void run(void *arg = 0);
};
//-----------------------------------------------------------------------------
// Event for cleanup
//-----------------------------------------------------------------------------
class ExecuteCleanupEvent : public SimEvent
{
ExecuteThread *mThread;
bool mOK;
public:
ExecuteCleanupEvent(ExecuteThread *thread, bool ok)
{
mThread = thread;
mOK = ok;
}
virtual void process(SimObject *object)
{
if( Con::isFunction( "onExecuteDone" ) )
Con::executef( "onExecuteDone", Con::getIntArg( mOK ) );
SAFE_DELETE(mThread);
}
};
//-----------------------------------------------------------------------------
ExecuteThread::ExecuteThread(const char *executable, const char *args /* = NULL */, const char *directory /* = NULL */) : Thread(0, NULL, false)
{
SHELLEXECUTEINFO shl;
dMemset(&shl, 0, sizeof(shl));
shl.cbSize = sizeof(shl);
shl.fMask = SEE_MASK_NOCLOSEPROCESS;
char exeBuf[1024];
Platform::makeFullPathName(executable, exeBuf, sizeof(exeBuf));
TempAlloc< TCHAR > dirBuf( ( directory ? dStrlen( directory ) : 0 ) + 1 );
dirBuf[ dirBuf.size - 1 ] = 0;
#ifdef UNICODE
WCHAR exe[ 1024 ];
convertUTF8toUTF16( exeBuf, exe, sizeof( exe ) / sizeof( exe[ 0 ] ) );
TempAlloc< WCHAR > argsBuf( ( args ? dStrlen( args ) : 0 ) + 1 );
argsBuf[ argsBuf.size - 1 ] = 0;
if( args )
convertUTF8toUTF16( args, argsBuf, argsBuf.size );
if( directory )
convertUTF8toUTF16( directory, dirBuf, dirBuf.size );
#else
char* exe = exeBuf;
char* argsBuf = args;
if( directory )
dStrpcy( dirBuf, directory );
#endif
backslash( exe );
backslash( dirBuf );
shl.lpVerb = TEXT( "open" );
shl.lpFile = exe;
shl.lpParameters = argsBuf;
shl.lpDirectory = dirBuf;
shl.nShow = SW_SHOWNORMAL;
if(ShellExecuteEx(&shl) && shl.hProcess)
{
mProcess = shl.hProcess;
start();
}
}
void ExecuteThread::run(void *arg /* = 0 */)
{
if(mProcess == NULL)
return;
DWORD wait = WAIT_OBJECT_0 - 1; // i.e., not WAIT_OBJECT_0
while(! checkForStop() && (wait = WaitForSingleObject(mProcess, 200)) != WAIT_OBJECT_0) ;
Sim::postEvent(Sim::getRootGroup(), new ExecuteCleanupEvent(this, wait == WAIT_OBJECT_0), -1);
}
//-----------------------------------------------------------------------------
// Console Functions
//-----------------------------------------------------------------------------
ConsoleFunction(shellExecute, bool, 2, 4, "(string executable, string args, string directory)"
"@brief Launches an outside executable or batch file\n\n"
"@param executable Name of the executable or batch file\n"
"@param args Optional list of arguments, in string format, to pass to the executable\n"
"@param directory Optional string containing path to output or shell\n"
"@ingroup Platform")
{
ExecuteThread *et = new ExecuteThread(argv[1], argc > 2 ? argv[2] : NULL, argc > 3 ? argv[3] : NULL);
if(! et->isAlive())
{
delete et;
return false;
}
return true;
}
void Platform::openFolder(const char* path )
{
char filePath[1024];
Platform::makeFullPathName(path, filePath, sizeof(filePath));
#ifdef UNICODE
WCHAR p[ 1024 ];
convertUTF8toUTF16( filePath, p, sizeof( p ) / sizeof( p[ 0 ] ) );
#else
char* p = filePath;
#endif
backslash( p );
::ShellExecute( NULL,TEXT("explore"),p, NULL, NULL, SW_SHOWNORMAL);
}
void Platform::openFile(const char* path )
{
char filePath[1024];
Platform::makeFullPathName(path, filePath, sizeof(filePath));
#ifdef UNICODE
WCHAR p[ 1024 ];
convertUTF8toUTF16( filePath, p, sizeof( p ) / sizeof( p[ 0 ] ) );
#else
char* p = filePath;
#endif
backslash( p );
::ShellExecute( NULL,TEXT("open"),p, NULL, NULL, SW_SHOWNORMAL);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,311 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platformWin32/platformWin32.h"
#include "platformWin32/winFont.h"
#include "gfx/gFont.h"
#include "gfx/bitmap/gBitmap.h"
#include "math/mRect.h"
#include "console/console.h"
#include "core/strings/unicode.h"
#include "core/strings/stringFunctions.h"
#include "core/stringTable.h"
static HDC fontHDC = NULL;
static HBITMAP fontBMP = NULL;
static U32 charsetMap[]=
{
ANSI_CHARSET,
SYMBOL_CHARSET,
SHIFTJIS_CHARSET,
HANGEUL_CHARSET,
HANGUL_CHARSET,
GB2312_CHARSET,
CHINESEBIG5_CHARSET,
OEM_CHARSET,
JOHAB_CHARSET,
HEBREW_CHARSET,
ARABIC_CHARSET,
GREEK_CHARSET,
TURKISH_CHARSET,
VIETNAMESE_CHARSET,
THAI_CHARSET,
EASTEUROPE_CHARSET,
RUSSIAN_CHARSET,
MAC_CHARSET,
BALTIC_CHARSET,
};
#define NUMCHARSETMAP (sizeof(charsetMap) / sizeof(U32))
void createFontInit(void);
void createFontShutdown(void);
void CopyCharToBitmap(GBitmap *pDstBMP, HDC hSrcHDC, const RectI &r);
void createFontInit()
{
//shared library sets the appInstance here
winState.appInstance = GetModuleHandle(NULL);
fontHDC = CreateCompatibleDC(NULL);
fontBMP = CreateCompatibleBitmap(fontHDC, 256, 256);
}
void createFontShutdown()
{
DeleteObject(fontBMP);
DeleteObject(fontHDC);
}
void CopyCharToBitmap(GBitmap *pDstBMP, HDC hSrcHDC, const RectI &r)
{
for (S32 i = r.point.y; i < r.point.y + r.extent.y; i++)
{
for (S32 j = r.point.x; j < r.point.x + r.extent.x; j++)
{
COLORREF color = GetPixel(hSrcHDC, j, i);
if (color)
*pDstBMP->getAddress(j, i) = 255;
else
*pDstBMP->getAddress(j, i) = 0;
}
}
}
//-----------------------------------------------------------------------------
// WinFont class
//-----------------------------------------------------------------------------
BOOL CALLBACK EnumFamCallBack(LPLOGFONT logFont, LPNEWTEXTMETRIC textMetric, DWORD fontType, LPARAM lParam)
{
if( !( fontType & TRUETYPE_FONTTYPE ) )
return true;
Vector<StringTableEntry>* fonts = (Vector< StringTableEntry>*)lParam;
const U32 len = dStrlen( logFont->lfFaceName ) * 3 + 1;
FrameTemp<UTF8> buffer( len );
convertUTF16toUTF8( logFont->lfFaceName, buffer, len );
fonts->push_back( StringTable->insert( buffer ) );
return true;
}
void PlatformFont::enumeratePlatformFonts( Vector<StringTableEntry>& fonts, UTF16* fontFamily )
{
EnumFontFamilies( fontHDC, fontFamily, (FONTENUMPROC)EnumFamCallBack, (LPARAM)&fonts );
}
PlatformFont *createPlatformFont(const char *name, U32 size, U32 charset /* = TGE_ANSI_CHARSET */)
{
PlatformFont *retFont = new WinFont;
if(retFont->create(name, size, charset))
return retFont;
delete retFont;
return NULL;
}
WinFont::WinFont() : mFont(NULL)
{
}
WinFont::~WinFont()
{
if(mFont)
{
DeleteObject(mFont);
}
}
bool WinFont::create(const char *name, U32 size, U32 charset /* = TGE_ANSI_CHARSET */)
{
if(name == NULL || size < 1)
return false;
if(charset > NUMCHARSETMAP)
charset = TGE_ANSI_CHARSET;
U32 weight = 0;
U32 doItalic = 0;
String nameStr = name;
nameStr = nameStr.trim();
bool haveModifier;
do
{
haveModifier = false;
if( nameStr.compare( "Bold", 4, String::NoCase | String::Right ) == 0 )
{
weight = 700;
nameStr = nameStr.substr( 0, nameStr.length() - 4 ).trim();
haveModifier = true;
}
if( nameStr.compare( "Italic", 6, String::NoCase | String::Right ) == 0 )
{
doItalic = 1;
nameStr = nameStr.substr( 0, nameStr.length() - 6 ).trim();
haveModifier = true;
}
}
while( haveModifier );
#ifdef UNICODE
const UTF16* n = nameStr.utf16();
mFont = CreateFont(size,0,0,0,weight,doItalic,0,0,DEFAULT_CHARSET,OUT_TT_PRECIS,0,PROOF_QUALITY,0,n);
#else
mFont = CreateFont(size,0,0,0,weight,doItalic,0,0,charsetMap[charset],OUT_TT_PRECIS,0,PROOF_QUALITY,0,name);
#endif
if(mFont == NULL)
return false;
SelectObject(fontHDC, fontBMP);
SelectObject(fontHDC, mFont);
GetTextMetrics(fontHDC, &mTextMetric);
return true;
}
bool WinFont::isValidChar(const UTF16 ch) const
{
return ch != 0 /* && (ch >= mTextMetric.tmFirstChar && ch <= mTextMetric.tmLastChar)*/;
}
bool WinFont::isValidChar(const UTF8 *str) const
{
return isValidChar(oneUTF8toUTF32(str));
}
PlatformFont::CharInfo &WinFont::getCharInfo(const UTF16 ch) const
{
static PlatformFont::CharInfo c;
dMemset(&c, 0, sizeof(c));
c.bitmapIndex = -1;
static U8 scratchPad[65536];
COLORREF backgroundColorRef = RGB( 0, 0, 0);
COLORREF foregroundColorRef = RGB(255, 255, 255);
SelectObject(fontHDC, fontBMP);
SelectObject(fontHDC, mFont);
SetBkColor(fontHDC, backgroundColorRef);
SetTextColor(fontHDC, foregroundColorRef);
MAT2 matrix;
GLYPHMETRICS metrics;
RectI clip;
FIXED zero;
zero.fract = 0;
zero.value = 0;
FIXED one;
one.fract = 0;
one.value = 1;
matrix.eM11 = one;
matrix.eM12 = zero;
matrix.eM21 = zero;
matrix.eM22 = one;
if(GetGlyphOutline(
fontHDC, // handle of device context
ch, // character to query
GGO_GRAY8_BITMAP, // format of data to return
&metrics, // address of structure for metrics
sizeof(scratchPad), // size of buffer for data
scratchPad, // address of buffer for data
&matrix // address of transformation matrix structure
) != GDI_ERROR)
{
U32 rowStride = (metrics.gmBlackBoxX + 3) & ~3; // DWORD aligned
U32 size = rowStride * metrics.gmBlackBoxY;
// [neo, 5/7/2007 - #3055]
// If we get large font sizes rowStride * metrics.gmBlackBoxY will
// be larger than scratch pad size and so overwrite mem, boom!
// Added range check < scratchPad for now but we need to review what
// to do here - do we want to call GetGlyphOutline() first with null
// values and get the real size to alloc buffer?
//if( size > sizeof( scratchPad ) )
// DebugBreak();
for(U32 j = 0; j < size && j < sizeof(scratchPad); j++)
{
U32 pad = U32(scratchPad[j]) << 2;
if(pad > 255)
pad = 255;
scratchPad[j] = pad;
}
S32 inc = metrics.gmCellIncX;
if(inc < 0)
inc = -inc;
c.xOffset = 0;
c.yOffset = 0;
c.width = metrics.gmBlackBoxX;
c.height = metrics.gmBlackBoxY;
c.xOrigin = metrics.gmptGlyphOrigin.x;
c.yOrigin = metrics.gmptGlyphOrigin.y;
c.xIncrement = metrics.gmCellIncX;
c.bitmapData = new U8[c.width * c.height];
AssertFatal( c.bitmapData != NULL, "Could not allocate memory for font bitmap data!");
for(U32 y = 0; S32(y) < c.height; y++)
{
U32 x;
for(x = 0; x < c.width; x++)
{
// [neo, 5/7/2007 - #3055]
// See comments above about scratchPad overrun
S32 spi = y * rowStride + x;
if( spi >= sizeof(scratchPad) )
return c;
c.bitmapData[y * c.width + x] = scratchPad[spi];
}
}
}
else
{
SIZE size;
GetTextExtentPoint32W(fontHDC, &ch, 1, &size);
if(size.cx)
{
c.xIncrement = size.cx;
c.bitmapIndex = 0;
}
}
return c;
}
PlatformFont::CharInfo &WinFont::getCharInfo(const UTF8 *str) const
{
return getCharInfo(oneUTF8toUTF32(str));
}

View file

@ -0,0 +1,60 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _PLATFORMFONT_H_
#include "platform/platformFont.h"
#endif
#ifndef _WINFONT_H_
#define _WINFONT_H_
class WinFont : public PlatformFont
{
private:
HFONT mFont;
TEXTMETRIC mTextMetric;
public:
WinFont();
virtual ~WinFont();
// PlatformFont virtual methods
virtual bool isValidChar(const UTF16 ch) const;
virtual bool isValidChar(const UTF8 *str) const;
inline virtual U32 getFontHeight() const
{
return mTextMetric.tmHeight;
}
inline virtual U32 getFontBaseLine() const
{
return mTextMetric.tmAscent;
}
virtual PlatformFont::CharInfo &getCharInfo(const UTF16 ch) const;
virtual PlatformFont::CharInfo &getCharInfo(const UTF8 *str) const;
virtual bool create(const char *name, dsize_t size, U32 charset = TGE_ANSI_CHARSET);
};
#endif // _WINFONT_H_

View file

@ -0,0 +1,868 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platformWin32/platformWin32.h"
#include "platform/platformInput.h"
#include "platformWin32/winDirectInput.h"
#include "platform/event.h"
#include "console/console.h"
#include "core/util/journal/process.h"
#include "windowManager/platformWindowMgr.h"
#ifdef LOG_INPUT
#include <time.h>
#include <stdarg.h>
#endif
// Static class variables:
InputManager* Input::smManager;
bool Input::smActive;
U8 Input::smModifierKeys;
bool Input::smLastKeyboardActivated;
bool Input::smLastMouseActivated;
bool Input::smLastJoystickActivated;
InputEvent Input::smInputEvent;
#ifdef LOG_INPUT
static HANDLE gInputLog;
#endif
static void fillAsciiTable();
//------------------------------------------------------------------------------
//
// This function gets the standard ASCII code corresponding to our key code
// and the existing modifier key state.
//
//------------------------------------------------------------------------------
struct AsciiData
{
struct KeyData
{
U16 ascii;
bool isDeadChar;
};
KeyData upper;
KeyData lower;
KeyData goofy;
};
#define NUM_KEYS ( KEY_OEM_102 + 1 )
#define KEY_FIRST KEY_ESCAPE
static AsciiData AsciiTable[NUM_KEYS];
//------------------------------------------------------------------------------
void Input::init()
{
Con::printf( "Input Init:" );
destroy();
#ifdef LOG_INPUT
struct tm* newTime;
time_t aclock;
time( &aclock );
newTime = localtime( &aclock );
asctime( newTime );
gInputLog = CreateFile( L"input.log", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
log( "Input log opened at %s\n", asctime( newTime ) );
#endif
smActive = false;
smLastKeyboardActivated = true;
smLastMouseActivated = true;
smLastJoystickActivated = true;
OSVERSIONINFO OSVersionInfo;
dMemset( &OSVersionInfo, 0, sizeof( OSVERSIONINFO ) );
OSVersionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
if ( GetVersionEx( &OSVersionInfo ) )
{
#ifdef LOG_INPUT
log( "Operating System:\n" );
switch ( OSVersionInfo.dwPlatformId )
{
case VER_PLATFORM_WIN32s:
log( " Win32s on Windows 3.1 version %d.%d\n", OSVersionInfo.dwMajorVersion, OSVersionInfo.dwMinorVersion );
break;
case VER_PLATFORM_WIN32_WINDOWS:
log( " Windows 95 version %d.%d\n", OSVersionInfo.dwMajorVersion, OSVersionInfo.dwMinorVersion );
log( " Build number %d\n", LOWORD( OSVersionInfo.dwBuildNumber ) );
break;
case VER_PLATFORM_WIN32_NT:
log( " WinNT version %d.%d\n", OSVersionInfo.dwMajorVersion, OSVersionInfo.dwMinorVersion );
log( " Build number %d\n", OSVersionInfo.dwBuildNumber );
break;
}
if ( OSVersionInfo.szCSDVersion != NULL )
log( " %s\n", OSVersionInfo.szCSDVersion );
log( "\n" );
#endif
if ( !( OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT && OSVersionInfo.dwMajorVersion < 5 ) )
{
smManager = new DInputManager;
if ( !smManager->enable() )
{
Con::printf( " DirectInput not enabled." );
delete smManager;
smManager = NULL;
}
else
{
DInputManager::init();
Con::printf( " DirectInput enabled." );
}
}
else
Con::printf( " WinNT detected -- DirectInput not enabled." );
}
// Init the current modifier keys
setModifierKeys(0);
fillAsciiTable();
Con::printf( "" );
// Set ourselves to participate in per-frame processing.
Process::notify(Input::process, PROCESS_INPUT_ORDER);
}
//------------------------------------------------------------------------------
ConsoleFunction( isJoystickDetected, bool, 1, 1, "isJoystickDetected()" )
{
argc; argv;
return( DInputDevice::joystickDetected() );
}
//------------------------------------------------------------------------------
ConsoleFunction( getJoystickAxes, const char*, 2, 2, "getJoystickAxes( instance )" )
{
argc;
DInputManager* mgr = dynamic_cast<DInputManager*>( Input::getManager() );
if ( mgr )
return( mgr->getJoystickAxesString( dAtoi( argv[1] ) ) );
return( "" );
}
//------------------------------------------------------------------------------
static void fillAsciiTable()
{
#ifdef LOG_INPUT
char buf[256];
Input::log( "--- Filling the ASCII table! ---\n" );
#endif
//HKL layout = GetKeyboardLayout( 0 );
U8 state[256];
U16 ascii[2];
U32 dikCode, vKeyCode, keyCode;
S32 result;
dMemset( &AsciiTable, 0, sizeof( AsciiTable ) );
dMemset( &state, 0, sizeof( state ) );
for ( keyCode = KEY_FIRST; keyCode < NUM_KEYS; keyCode++ )
{
ascii[0] = ascii[1] = 0;
dikCode = Key_to_DIK( keyCode );
// This is a special case for numpad keys.
//
// The KEY_NUMPAD# torque types represent the event generated when a
// numpad key is pressed WITH NUMLOCK ON. Therefore it does have an ascii
// value, but to get it from windows we must specify in the keyboard state
// that numlock is on.
if ( KEY_NUMPAD0 <= keyCode && keyCode <= KEY_NUMPAD9 )
{
state[VK_NUMLOCK] = 0x80;
// Also, numpad keys return completely different keycodes when
// numlock is not pressed (home,insert,etc) and MapVirtualKey
// appears to always return those values.
//
// So I'm using TranslateKeyCodeToOS instead. Really it looks
// like we could be using this method for all of them and
// cut out the Key_to_DIK middleman.
//
vKeyCode = TranslateKeyCodeToOS( keyCode );
result = ToAscii( vKeyCode, dikCode, state, ascii, 0 );
AsciiTable[keyCode].lower.ascii = ascii[0];
AsciiTable[keyCode].upper.ascii = 0;
AsciiTable[keyCode].goofy.ascii = 0;
state[VK_NUMLOCK] = 0;
continue;
}
if ( dikCode )
{
//vKeyCode = MapVirtualKeyEx( dikCode, 1, layout );
vKeyCode = MapVirtualKey( dikCode, 1 );
#ifdef LOG_INPUT
dSprintf( buf, sizeof( buf ), "KC: %#04X DK: %#04X VK: %#04X\n",
keyCode, dikCode, vKeyCode );
Input::log( buf );
#endif
// Lower case:
ascii[0] = ascii[1] = 0;
//result = ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout );
result = ToAscii( vKeyCode, dikCode, state, ascii, 0 );
#ifdef LOG_INPUT
dSprintf( buf, sizeof( buf ), " LOWER- R: %d A[0]: %#06X A[1]: %#06X\n",
result, ascii[0], ascii[1] );
Input::log( buf );
#endif
if ( result == 2 )
AsciiTable[keyCode].lower.ascii = ascii[1] ? ascii[1] : ( ascii[0] >> 8 );
else if ( result == 1 )
AsciiTable[keyCode].lower.ascii = ascii[0];
else if ( result < 0 )
{
AsciiTable[keyCode].lower.ascii = ascii[0];
AsciiTable[keyCode].lower.isDeadChar = true;
// Need to clear the dead character from the keyboard layout:
//ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout );
ToAscii( vKeyCode, dikCode, state, ascii, 0 );
}
// Upper case:
ascii[0] = ascii[1] = 0;
state[VK_SHIFT] = 0x80;
//result = ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout );
result = ToAscii( vKeyCode, dikCode, state, ascii, 0 );
#ifdef LOG_INPUT
dSprintf( buf, sizeof( buf ), " UPPER- R: %d A[0]: %#06X A[1]: %#06X\n",
result, ascii[0], ascii[1] );
Input::log( buf );
#endif
if ( result == 2 )
AsciiTable[keyCode].upper.ascii = ascii[1] ? ascii[1] : ( ascii[0] >> 8 );
else if ( result == 1 )
AsciiTable[keyCode].upper.ascii = ascii[0];
else if ( result < 0 )
{
AsciiTable[keyCode].upper.ascii = ascii[0];
AsciiTable[keyCode].upper.isDeadChar = true;
// Need to clear the dead character from the keyboard layout:
//ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout );
ToAscii( vKeyCode, dikCode, state, ascii, 0 );
}
state[VK_SHIFT] = 0;
// Foreign mod case:
ascii[0] = ascii[1] = 0;
state[VK_CONTROL] = 0x80;
state[VK_MENU] = 0x80;
//result = ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout );
result = ToAscii( vKeyCode, dikCode, state, ascii, 0 );
#ifdef LOG_INPUT
dSprintf( buf, sizeof( buf ), " GOOFY- R: %d A[0]: %#06X A[1]: %#06X\n",
result, ascii[0], ascii[1] );
Input::log( buf );
#endif
if ( result == 2 )
AsciiTable[keyCode].goofy.ascii = ascii[1] ? ascii[1] : ( ascii[0] >> 8 );
else if ( result == 1 )
AsciiTable[keyCode].goofy.ascii = ascii[0];
else if ( result < 0 )
{
AsciiTable[keyCode].goofy.ascii = ascii[0];
AsciiTable[keyCode].goofy.isDeadChar = true;
// Need to clear the dead character from the keyboard layout:
//ToAsciiEx( vKeyCode, dikCode, state, ascii, 0, layout );
ToAscii( vKeyCode, dikCode, state, ascii, 0 );
}
state[VK_CONTROL] = 0;
state[VK_MENU] = 0;
}
}
#ifdef LOG_INPUT
Input::log( "--- Finished filling the ASCII table! ---\n\n" );
#endif
}
//------------------------------------------------------------------------------
U16 Input::getKeyCode( U16 asciiCode )
{
U16 keyCode = 0;
U16 i;
// This is done three times so the lowerkey will always
// be found first. Some foreign keyboards have duplicate
// chars on some keys.
for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
{
if ( AsciiTable[i].lower.ascii == asciiCode )
{
keyCode = i;
break;
};
}
for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
{
if ( AsciiTable[i].upper.ascii == asciiCode )
{
keyCode = i;
break;
};
}
for ( i = KEY_FIRST; i < NUM_KEYS && !keyCode; i++ )
{
if ( AsciiTable[i].goofy.ascii == asciiCode )
{
keyCode = i;
break;
};
}
return( keyCode );
}
//------------------------------------------------------------------------------
U16 Input::getAscii( U16 keyCode, KEY_STATE keyState )
{
if ( keyCode >= NUM_KEYS )
return 0;
switch ( keyState )
{
case STATE_LOWER:
return AsciiTable[keyCode].lower.ascii;
case STATE_UPPER:
return AsciiTable[keyCode].upper.ascii;
case STATE_GOOFY:
return AsciiTable[keyCode].goofy.ascii;
default:
return(0);
}
}
//------------------------------------------------------------------------------
void Input::destroy()
{
Process::remove(Input::process);
#ifdef LOG_INPUT
if ( gInputLog )
{
log( "*** CLOSING LOG ***\n" );
CloseHandle( gInputLog );
gInputLog = NULL;
}
#endif
if ( smManager && smManager->isEnabled() )
{
smManager->disable();
delete smManager;
smManager = NULL;
}
}
//------------------------------------------------------------------------------
bool Input::enable()
{
if ( smManager && !smManager->isEnabled() )
return( smManager->enable() );
return( false );
}
//------------------------------------------------------------------------------
void Input::disable()
{
if ( smManager && smManager->isEnabled() )
smManager->disable();
}
//------------------------------------------------------------------------------
void Input::activate()
{
#ifdef UNICODE
//winState.imeHandle = ImmGetContext( getWin32WindowHandle() );
//ImmReleaseContext( getWin32WindowHandle(), winState.imeHandle );
#endif
if ( !Con::getBoolVariable( "$enableDirectInput" ) )
return;
if ( smManager && smManager->isEnabled() && !smActive )
{
Con::printf( "Activating DirectInput..." );
#ifdef LOG_INPUT
Input::log( "Activating DirectInput...\n" );
#endif
smActive = true;
DInputManager* dInputManager = dynamic_cast<DInputManager*>( smManager );
if ( dInputManager )
{
if ( dInputManager->isJoystickEnabled() && smLastJoystickActivated )
dInputManager->activateJoystick();
}
}
}
//------------------------------------------------------------------------------
void Input::deactivate()
{
if ( smManager && smManager->isEnabled() && smActive )
{
#ifdef LOG_INPUT
Input::log( "Deactivating DirectInput...\n" );
#endif
DInputManager* dInputManager = dynamic_cast<DInputManager*>( smManager );
if ( dInputManager )
{
smLastJoystickActivated = dInputManager->isJoystickActive();
dInputManager->deactivateJoystick();
}
smActive = false;
Con::printf( "DirectInput deactivated." );
}
}
//------------------------------------------------------------------------------
bool Input::isEnabled()
{
if ( smManager )
return smManager->isEnabled();
return false;
}
//------------------------------------------------------------------------------
bool Input::isActive()
{
return smActive;
}
//------------------------------------------------------------------------------
void Input::process()
{
if ( smManager && smManager->isEnabled() && smActive )
smManager->process();
}
//------------------------------------------------------------------------------
InputManager* Input::getManager()
{
return( smManager );
}
#ifdef LOG_INPUT
//------------------------------------------------------------------------------
void Input::log( const char* format, ... )
{
if ( !gInputLog )
return;
va_list argptr;
va_start( argptr, format );
char buffer[512];
dVsprintf( buffer, 511, format, argptr );
DWORD bytes;
WriteFile( gInputLog, buffer, dStrlen( buffer ), &bytes, NULL );
va_end( argptr );
}
ConsoleFunction( inputLog, void, 2, 2, "inputLog( string )" )
{
argc;
Input::log( "%s\n", argv[1] );
}
#endif // LOG_INPUT
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
static U8 VcodeRemap[256] =
{
0, // 0x00
0, // 0x01 VK_LBUTTON
0, // 0x02 VK_RBUTTON
0, // 0x03 VK_CANCEL
0, // 0x04 VK_MBUTTON
0, // 0x05
0, // 0x06
0, // 0x07
KEY_BACKSPACE, // 0x08 VK_BACK
KEY_TAB, // 0x09 VK_TAB
0, // 0x0A
0, // 0x0B
0, // 0x0C VK_CLEAR
KEY_RETURN, // 0x0D VK_RETURN
0, // 0x0E
0, // 0x0F
KEY_SHIFT, // 0x10 VK_SHIFT
KEY_CONTROL, // 0x11 VK_CONTROL
KEY_ALT, // 0x12 VK_MENU
KEY_PAUSE, // 0x13 VK_PAUSE
KEY_CAPSLOCK, // 0x14 VK_CAPITAL
0, // 0x15 VK_KANA, VK_HANGEUL, VK_HANGUL
0, // 0x16
0, // 0x17 VK_JUNJA
0, // 0x18 VK_FINAL
0, // 0x19 VK_HANJA, VK_KANJI
0, // 0x1A
KEY_ESCAPE, // 0x1B VK_ESCAPE
0, // 0x1C VK_CONVERT
0, // 0x1D VK_NONCONVERT
0, // 0x1E VK_ACCEPT
0, // 0x1F VK_MODECHANGE
KEY_SPACE, // 0x20 VK_SPACE
KEY_PAGE_UP, // 0x21 VK_PRIOR
KEY_PAGE_DOWN, // 0x22 VK_NEXT
KEY_END, // 0x23 VK_END
KEY_HOME, // 0x24 VK_HOME
KEY_LEFT, // 0x25 VK_LEFT
KEY_UP, // 0x26 VK_UP
KEY_RIGHT, // 0x27 VK_RIGHT
KEY_DOWN, // 0x28 VK_DOWN
0, // 0x29 VK_SELECT
KEY_PRINT, // 0x2A VK_PRINT
0, // 0x2B VK_EXECUTE
0, // 0x2C VK_SNAPSHOT
KEY_INSERT, // 0x2D VK_INSERT
KEY_DELETE, // 0x2E VK_DELETE
KEY_HELP, // 0x2F VK_HELP
KEY_0, // 0x30 VK_0 VK_0 thru VK_9 are the same as ASCII '0' thru '9' (// 0x30 - // 0x39)
KEY_1, // 0x31 VK_1
KEY_2, // 0x32 VK_2
KEY_3, // 0x33 VK_3
KEY_4, // 0x34 VK_4
KEY_5, // 0x35 VK_5
KEY_6, // 0x36 VK_6
KEY_7, // 0x37 VK_7
KEY_8, // 0x38 VK_8
KEY_9, // 0x39 VK_9
0, // 0x3A
0, // 0x3B
0, // 0x3C
0, // 0x3D
0, // 0x3E
0, // 0x3F
0, // 0x40
KEY_A, // 0x41 VK_A VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (// 0x41 - // 0x5A)
KEY_B, // 0x42 VK_B
KEY_C, // 0x43 VK_C
KEY_D, // 0x44 VK_D
KEY_E, // 0x45 VK_E
KEY_F, // 0x46 VK_F
KEY_G, // 0x47 VK_G
KEY_H, // 0x48 VK_H
KEY_I, // 0x49 VK_I
KEY_J, // 0x4A VK_J
KEY_K, // 0x4B VK_K
KEY_L, // 0x4C VK_L
KEY_M, // 0x4D VK_M
KEY_N, // 0x4E VK_N
KEY_O, // 0x4F VK_O
KEY_P, // 0x50 VK_P
KEY_Q, // 0x51 VK_Q
KEY_R, // 0x52 VK_R
KEY_S, // 0x53 VK_S
KEY_T, // 0x54 VK_T
KEY_U, // 0x55 VK_U
KEY_V, // 0x56 VK_V
KEY_W, // 0x57 VK_W
KEY_X, // 0x58 VK_X
KEY_Y, // 0x59 VK_Y
KEY_Z, // 0x5A VK_Z
KEY_WIN_LWINDOW, // 0x5B VK_LWIN
KEY_WIN_RWINDOW, // 0x5C VK_RWIN
KEY_WIN_APPS, // 0x5D VK_APPS
0, // 0x5E
0, // 0x5F
KEY_NUMPAD0, // 0x60 VK_NUMPAD0
KEY_NUMPAD1, // 0x61 VK_NUMPAD1
KEY_NUMPAD2, // 0x62 VK_NUMPAD2
KEY_NUMPAD3, // 0x63 VK_NUMPAD3
KEY_NUMPAD4, // 0x64 VK_NUMPAD4
KEY_NUMPAD5, // 0x65 VK_NUMPAD5
KEY_NUMPAD6, // 0x66 VK_NUMPAD6
KEY_NUMPAD7, // 0x67 VK_NUMPAD7
KEY_NUMPAD8, // 0x68 VK_NUMPAD8
KEY_NUMPAD9, // 0x69 VK_NUMPAD9
KEY_MULTIPLY, // 0x6A VK_MULTIPLY
KEY_ADD, // 0x6B VK_ADD
KEY_SEPARATOR, // 0x6C VK_SEPARATOR
KEY_SUBTRACT, // 0x6D VK_SUBTRACT
KEY_DECIMAL, // 0x6E VK_DECIMAL
KEY_DIVIDE, // 0x6F VK_DIVIDE
KEY_F1, // 0x70 VK_F1
KEY_F2, // 0x71 VK_F2
KEY_F3, // 0x72 VK_F3
KEY_F4, // 0x73 VK_F4
KEY_F5, // 0x74 VK_F5
KEY_F6, // 0x75 VK_F6
KEY_F7, // 0x76 VK_F7
KEY_F8, // 0x77 VK_F8
KEY_F9, // 0x78 VK_F9
KEY_F10, // 0x79 VK_F10
KEY_F11, // 0x7A VK_F11
KEY_F12, // 0x7B VK_F12
KEY_F13, // 0x7C VK_F13
KEY_F14, // 0x7D VK_F14
KEY_F15, // 0x7E VK_F15
KEY_F16, // 0x7F VK_F16
KEY_F17, // 0x80 VK_F17
KEY_F18, // 0x81 VK_F18
KEY_F19, // 0x82 VK_F19
KEY_F20, // 0x83 VK_F20
KEY_F21, // 0x84 VK_F21
KEY_F22, // 0x85 VK_F22
KEY_F23, // 0x86 VK_F23
KEY_F24, // 0x87 VK_F24
0, // 0x88
0, // 0x89
0, // 0x8A
0, // 0x8B
0, // 0x8C
0, // 0x8D
0, // 0x8E
0, // 0x8F
KEY_NUMLOCK, // 0x90 VK_NUMLOCK
KEY_SCROLLLOCK, // 0x91 VK_OEM_SCROLL
0, // 0x92
0, // 0x93
0, // 0x94
0, // 0x95
0, // 0x96
0, // 0x97
0, // 0x98
0, // 0x99
0, // 0x9A
0, // 0x9B
0, // 0x9C
0, // 0x9D
0, // 0x9E
0, // 0x9F
KEY_LSHIFT, // 0xA0 VK_LSHIFT
KEY_RSHIFT, // 0xA1 VK_RSHIFT
KEY_LCONTROL, // 0xA2 VK_LCONTROL
KEY_RCONTROL, // 0xA3 VK_RCONTROL
KEY_LALT, // 0xA4 VK_LMENU
KEY_RALT, // 0xA5 VK_RMENU
0, // 0xA6
0, // 0xA7
0, // 0xA8
0, // 0xA9
0, // 0xAA
0, // 0xAB
0, // 0xAC
0, // 0xAD
0, // 0xAE
0, // 0xAF
0, // 0xB0
0, // 0xB1
0, // 0xB2
0, // 0xB3
0, // 0xB4
0, // 0xB5
0, // 0xB6
0, // 0xB7
0, // 0xB8
0, // 0xB9
KEY_SEMICOLON, // 0xBA VK_OEM_1
KEY_EQUALS, // 0xBB VK_OEM_PLUS
KEY_COMMA, // 0xBC VK_OEM_COMMA
KEY_MINUS, // 0xBD VK_OEM_MINUS
KEY_PERIOD, // 0xBE VK_OEM_PERIOD
KEY_SLASH, // 0xBF VK_OEM_2
KEY_TILDE, // 0xC0 VK_OEM_3
0, // 0xC1
0, // 0xC2
0, // 0xC3
0, // 0xC4
0, // 0xC5
0, // 0xC6
0, // 0xC7
0, // 0xC8
0, // 0xC9
0, // 0xCA
0, // 0xCB
0, // 0xCC
0, // 0xCD
0, // 0xCE
0, // 0xCF
0, // 0xD0
0, // 0xD1
0, // 0xD2
0, // 0xD3
0, // 0xD4
0, // 0xD5
0, // 0xD6
0, // 0xD7
0, // 0xD8
0, // 0xD9
0, // 0xDA
KEY_LBRACKET, // 0xDB VK_OEM_4
KEY_BACKSLASH, // 0xDC VK_OEM_5
KEY_RBRACKET, // 0xDD VK_OEM_6
KEY_APOSTROPHE, // 0xDE VK_OEM_7
0, // 0xDF VK_OEM_8
0, // 0xE0
0, // 0xE1 VK_OEM_AX AX key on Japanese AX keyboard
KEY_OEM_102, // 0xE2 VK_OEM_102
0, // 0xE3
0, // 0xE4
0, // 0xE5 VK_PROCESSKEY
0, // 0xE6
0, // 0xE7
0, // 0xE8
0, // 0xE9
0, // 0xEA
0, // 0xEB
0, // 0xEC
0, // 0xED
0, // 0xEE
0, // 0xEF
0, // 0xF0
0, // 0xF1
0, // 0xF2
0, // 0xF3
0, // 0xF4
0, // 0xF5
0, // 0xF6 VK_ATTN
0, // 0xF7 VK_CRSEL
0, // 0xF8 VK_EXSEL
0, // 0xF9 VK_EREOF
0, // 0xFA VK_PLAY
0, // 0xFB VK_ZOOM
0, // 0xFC VK_NONAME
0, // 0xFD VK_PA1
0, // 0xFE VK_OEM_CLEAR
0 // 0xFF
};
//------------------------------------------------------------------------------
//
// This function translates a virtual key code to our corresponding internal
// key code using the preceding table.
//
//------------------------------------------------------------------------------
U8 TranslateOSKeyCode(U8 vcode)
{
return VcodeRemap[vcode];
}
U8 TranslateKeyCodeToOS(U8 keycode)
{
for(S32 i = 0;i < sizeof(VcodeRemap) / sizeof(U8);++i)
{
if(VcodeRemap[i] == keycode)
return i;
}
return 0;
}
//-----------------------------------------------------------------------------
// Clipboard functions
const char* Platform::getClipboard()
{
HGLOBAL hGlobal;
LPVOID pGlobal;
//make sure we can access the clipboard
if (!IsClipboardFormatAvailable(CF_TEXT))
return "";
if (!OpenClipboard(NULL))
return "";
hGlobal = GetClipboardData(CF_TEXT);
pGlobal = GlobalLock(hGlobal);
S32 cbLength = strlen((char *)pGlobal);
char *returnBuf = Con::getReturnBuffer(cbLength + 1);
strcpy(returnBuf, (char *)pGlobal);
returnBuf[cbLength] = '\0';
GlobalUnlock(hGlobal);
CloseClipboard();
//note - this function never returns NULL
return returnBuf;
}
//-----------------------------------------------------------------------------
bool Platform::setClipboard(const char *text)
{
if (!text)
return false;
//make sure we can access the clipboard
if (!OpenClipboard(NULL))
return false;
S32 cbLength = strlen(text);
HGLOBAL hGlobal;
LPVOID pGlobal;
hGlobal = GlobalAlloc(GHND, cbLength + 1);
pGlobal = GlobalLock (hGlobal);
strcpy((char *)pGlobal, text);
GlobalUnlock(hGlobal);
EmptyClipboard();
SetClipboardData(CF_TEXT, hGlobal);
CloseClipboard();
return true;
}

View file

@ -0,0 +1,129 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "core/strings/stringFunctions.h"
#include "console/console.h"
#include "math/mMath.h"
extern void mInstallLibrary_C();
extern void mInstallLibrary_ASM();
extern void mInstall_AMD_Math();
extern void mInstall_Library_SSE();
//--------------------------------------
ConsoleFunction( mathInit, void, 1, 10, "( ... )"
"@brief Install the math library with specified extensions.\n\n"
"Possible parameters are:\n\n"
" - 'DETECT' Autodetect math lib settings.\n\n"
" - 'C' Enable the C math routines. C routines are always enabled.\n\n"
" - 'FPU' Enable floating point unit routines.\n\n"
" - 'MMX' Enable MMX math routines.\n\n"
" - '3DNOW' Enable 3dNow! math routines.\n\n"
" - 'SSE' Enable SSE math routines.\n\n"
"@ingroup Math")
{
U32 properties = CPU_PROP_C; // C entensions are always used
if (argc == 1)
{
Math::init(0);
return;
}
for (argc--, argv++; argc; argc--, argv++)
{
if (dStricmp(*argv, "DETECT") == 0) {
Math::init(0);
return;
}
if (dStricmp(*argv, "C") == 0) {
properties |= CPU_PROP_C;
continue;
}
if (dStricmp(*argv, "FPU") == 0) {
properties |= CPU_PROP_FPU;
continue;
}
if (dStricmp(*argv, "MMX") == 0) {
properties |= CPU_PROP_MMX;
continue;
}
if (dStricmp(*argv, "3DNOW") == 0) {
properties |= CPU_PROP_3DNOW;
continue;
}
if (dStricmp(*argv, "SSE") == 0) {
properties |= CPU_PROP_SSE;
continue;
}
Con::printf("Error: MathInit(): ignoring unknown math extension '%s'", *argv);
}
Math::init(properties);
}
//------------------------------------------------------------------------------
void Math::init(U32 properties)
{
if (!properties)
// detect what's available
properties = Platform::SystemInfo.processor.properties;
else
// Make sure we're not asking for anything that's not supported
properties &= Platform::SystemInfo.processor.properties;
Con::printf("Math Init:");
Con::printf(" Installing Standard C extensions");
mInstallLibrary_C();
Con::printf(" Installing Assembly extensions");
mInstallLibrary_ASM();
if (properties & CPU_PROP_FPU)
{
Con::printf(" Installing FPU extensions");
}
if (properties & CPU_PROP_MMX)
{
Con::printf(" Installing MMX extensions");
if (properties & CPU_PROP_3DNOW)
{
Con::printf(" Installing 3DNow extensions");
mInstall_AMD_Math();
}
}
if (properties & CPU_PROP_SSE)
{
Con::printf(" Installing SSE extensions");
mInstall_Library_SSE();
}
Con::printf(" ");
}

View file

@ -0,0 +1,109 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "math/mMath.h"
#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM)
static S32 m_mulDivS32_ASM(S32 a, S32 b, S32 c)
{ // a * b / c
S32 r;
_asm
{
mov eax, a
imul b
idiv c
mov r, eax
}
return r;
}
static U32 m_mulDivU32_ASM(S32 a, S32 b, U32 c)
{ // a * b / c
S32 r;
_asm
{
mov eax, a
mov edx, 0
mul b
div c
mov r, eax
}
return r;
}
static void m_sincos_ASM( F32 angle, F32 *s, F32 *c )
{
_asm
{
fld angle
fsincos
mov eax, c
fstp dword ptr [eax]
mov eax, s
fstp dword ptr [eax]
}
}
U32 Platform::getMathControlState()
{
U16 cw;
_asm
{
fstcw cw
}
return cw;
}
void Platform::setMathControlState(U32 state)
{
U16 cw = state;
_asm
{
fldcw cw
}
}
void Platform::setMathControlStateKnown()
{
U16 cw = 0x27F;
_asm
{
fldcw cw
}
}
#endif
//------------------------------------------------------------------------------
void mInstallLibrary_ASM()
{
#if defined(TORQUE_SUPPORTS_VC_INLINE_X86_ASM)
m_mulDivS32 = m_mulDivS32_ASM;
m_mulDivU32 = m_mulDivU32_ASM;
m_sincos = m_sincos_ASM;
#endif
}

View file

@ -0,0 +1,74 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platformWin32/platformWin32.h"
#include <xmmintrin.h>
void* dMemcpy(void *dst, const void *src, unsigned size)
{
return memcpy(dst,src,size);
}
//--------------------------------------
void* dMemmove(void *dst, const void *src, unsigned size)
{
return memmove(dst,src,size);
}
//--------------------------------------
void* dMemset(void *dst, S32 c, unsigned size)
{
return memset(dst,c,size);
}
//--------------------------------------
S32 dMemcmp(const void *ptr1, const void *ptr2, unsigned len)
{
return memcmp(ptr1, ptr2, len);
}
#if defined(TORQUE_COMPILER_MINGW)
#include <stdlib.h>
#endif
//--------------------------------------
void* dRealMalloc(dsize_t s)
{
return malloc(s);
}
void dRealFree(void* p)
{
free(p);
}
void *dMalloc_aligned(dsize_t in_size, int alignment)
{
return _mm_malloc(in_size, alignment);
}
void dFree_aligned(void* p)
{
return _mm_free(p);
}

View file

@ -0,0 +1,70 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platformWin32/platformWin32.h"
#include "core/strings/stringFunctions.h"
void Platform::postQuitMessage(const U32 in_quitVal)
{
if (!Platform::getWebDeployment())
PostQuitMessage(in_quitVal);
}
void Platform::debugBreak()
{
DebugBreak();
}
void Platform::forceShutdown(S32 returnValue)
{
// Don't do an ExitProcess here or you'll wreak havoc in a multithreaded
// environment.
exit( returnValue );
}
void Platform::outputDebugString( const char *string, ... )
{
// Expand string.
char buffer[ 2048 ];
va_list args;
va_start( args, string );
dVsprintf( buffer, sizeof( buffer ), string, args );
va_end( args );
// Append a newline to buffer. This is better than calling OutputDebugStringA
// twice as in a multi-threaded environment, some other thread may output some
// stuff in between the two calls.
U32 length = strlen( buffer );
if( length == ( sizeof( buffer ) - 1 ) )
length --;
buffer[ length ] = '\n';
buffer[ length + 1 ] = '\0';
OutputDebugStringA( buffer );
}

View file

@ -0,0 +1,497 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platformWin32/platformWin32.h"
#include "platform/platformRedBook.h"
#include "core/strings/unicode.h"
#include "core/strings/stringFunctions.h"
class Win32RedBookDevice : public RedBookDevice
{
private:
typedef RedBookDevice Parent;
U32 mDeviceId;
void setLastError(const char *);
void setLastError(U32);
MIXERCONTROLDETAILS mMixerVolumeDetails;
MIXERCONTROLDETAILS_UNSIGNED mMixerVolumeValue;
union {
HMIXEROBJ mVolumeDeviceId;
UINT mAuxVolumeDeviceId;
};
U32 mOriginalVolume;
bool mVolumeInitialized;
bool mUsingMixer;
void openVolume();
void closeVolume();
public:
Win32RedBookDevice();
~Win32RedBookDevice();
U32 getDeviceId();
bool open();
bool close();
bool play(U32);
bool stop();
bool getTrackCount(U32 *);
bool getVolume(F32 *);
bool setVolume(F32);
};
//------------------------------------------------------------------------------
// Win32 specific
//------------------------------------------------------------------------------
void installRedBookDevices()
{
U32 bufSize = ::GetLogicalDriveStrings(0,0);
char * buf = new char[bufSize];
::GetLogicalDriveStringsA(bufSize, buf);
char * str = buf;
while(*str)
{
if(::GetDriveTypeA(str) == DRIVE_CDROM)
{
Win32RedBookDevice * device = new Win32RedBookDevice;
device->mDeviceName = new char[dStrlen(str) + 1];
dStrcpy(device->mDeviceName, str);
RedBook::installDevice(device);
}
str += dStrlen(str) + 1;
}
delete [] buf;
}
void handleRedBookCallback(U32 code, U32 deviceId)
{
if(code != MCI_NOTIFY_SUCCESSFUL)
return;
Win32RedBookDevice * device = dynamic_cast<Win32RedBookDevice*>(RedBook::getCurrentDevice());
if(!device)
return;
if(device->getDeviceId() != deviceId)
return;
// only installed callback on play (no callback if play is aborted)
RedBook::handleCallback(RedBook::PlayFinished);
}
//------------------------------------------------------------------------------
// Class: Win32RedBookDevice
//------------------------------------------------------------------------------
Win32RedBookDevice::Win32RedBookDevice()
{
mVolumeInitialized = false;
}
Win32RedBookDevice::~Win32RedBookDevice()
{
close();
}
U32 Win32RedBookDevice::getDeviceId()
{
return(mDeviceId);
}
bool Win32RedBookDevice::open()
{
if(mAcquired)
{
setLastError("Device is already open.");
return(false);
}
U32 error;
// open the device
MCI_OPEN_PARMS openParms;
#ifdef UNICODE
openParms.lpstrDeviceType = (LPCWSTR)MCI_DEVTYPE_CD_AUDIO;
UTF16 buf[512];
convertUTF8toUTF16((UTF8 *)mDeviceName, buf, sizeof(buf));
openParms.lpstrElementName = buf;
#else
openParms.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO;
openParms.lpstrElementName = mDeviceName;
#endif
error = mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID, (DWORD_PTR)(LPMCI_OPEN_PARMS)&openParms);
if(error)
{
// attempt to open as a shared device
error = mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID|MCI_OPEN_SHAREABLE, (DWORD_PTR)(LPMCI_OPEN_PARMS)&openParms);
if(error)
{
setLastError(error);
return(false);
}
}
// set time mode to milliseconds
MCI_SET_PARMS setParms;
setParms.dwTimeFormat = MCI_FORMAT_MILLISECONDS;
error = mciSendCommand(openParms.wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD_PTR)(LPMCI_SET_PARMS)&setParms);
if(error)
{
setLastError(error);
return(false);
}
//
mDeviceId = openParms.wDeviceID;
mAcquired = true;
openVolume();
setLastError("");
return(true);
}
bool Win32RedBookDevice::close()
{
if(!mAcquired)
{
setLastError("Device has not been acquired");
return(false);
}
stop();
U32 error;
MCI_GENERIC_PARMS closeParms;
error = mciSendCommand(mDeviceId, MCI_CLOSE, 0, (DWORD_PTR)(LPMCI_GENERIC_PARMS)&closeParms);
if(error)
{
setLastError(error);
return(false);
}
mAcquired = false;
closeVolume();
setLastError("");
return(true);
}
bool Win32RedBookDevice::play(U32 track)
{
if(!mAcquired)
{
setLastError("Device has not been acquired");
return(false);
}
U32 numTracks;
if(!getTrackCount(&numTracks))
return(false);
if(track >= numTracks)
{
setLastError("Track index is out of range");
return(false);
}
MCI_STATUS_PARMS statusParms;
// get track start time
statusParms.dwItem = MCI_STATUS_POSITION;
statusParms.dwTrack = track + 1;
U32 error;
error = mciSendCommand(mDeviceId, MCI_STATUS, MCI_STATUS_ITEM|MCI_TRACK|MCI_WAIT,
(DWORD_PTR)(LPMCI_STATUS_PARMS)&statusParms);
if(error)
{
setLastError(error);
return(false);
}
MCI_PLAY_PARMS playParms;
playParms.dwFrom = statusParms.dwReturn;
// get track end time
statusParms.dwItem = MCI_STATUS_LENGTH;
error = mciSendCommand(mDeviceId, MCI_STATUS, MCI_STATUS_ITEM|MCI_TRACK|MCI_WAIT,
(DWORD_PTR)(LPMCI_STATUS_PARMS)&statusParms);
if(error)
{
setLastError(error);
return(false);
}
playParms.dwTo = playParms.dwFrom + statusParms.dwReturn;
// play the track
playParms.dwCallback = MAKELONG(getWin32WindowHandle(), 0);
error = mciSendCommand(mDeviceId, MCI_PLAY, MCI_FROM|MCI_TO|MCI_NOTIFY,
(DWORD_PTR)(LPMCI_PLAY_PARMS)&playParms);
if(error)
{
setLastError(error);
return(false);
}
setLastError("");
return(true);
}
bool Win32RedBookDevice::stop()
{
if(!mAcquired)
{
setLastError("Device has not been acquired");
return(false);
}
MCI_GENERIC_PARMS genParms;
U32 error = mciSendCommand(mDeviceId, MCI_STOP, 0, (DWORD_PTR)(LPMCI_GENERIC_PARMS)&genParms);
if(error)
{
setLastError(error);
return(false);
}
setLastError("");
return(true);
}
bool Win32RedBookDevice::getTrackCount(U32 * numTracks)
{
if(!mAcquired)
{
setLastError("Device has not been acquired");
return(false);
}
MCI_STATUS_PARMS statusParms;
statusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
U32 error = mciSendCommand(mDeviceId, MCI_STATUS, MCI_STATUS_ITEM | MCI_WAIT, (DWORD_PTR)(LPMCI_STATUS_PARMS)&statusParms);
if(error)
{
setLastError(error);
return(false);
}
*numTracks = statusParms.dwReturn;
return(true);
}
bool Win32RedBookDevice::getVolume(F32 * volume)
{
if(!mAcquired)
{
setLastError("Device has not been acquired");
return(false);
}
if(!mVolumeInitialized)
{
setLastError("Volume failed to initialize");
return(false);
}
U32 vol = 0;
if(mUsingMixer)
{
mixerGetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_GETCONTROLDETAILSF_VALUE);
vol = mMixerVolumeValue.dwValue;
}
else
auxGetVolume(mAuxVolumeDeviceId, (unsigned long *)&vol);
vol &= 0xffff;
*volume = F32(vol) / 65535.f;
setLastError("");
return(true);
}
bool Win32RedBookDevice::setVolume(F32 volume)
{
if(!mAcquired)
{
setLastError("Device has not been acquired");
return(false);
}
if(!mVolumeInitialized)
{
setLastError("Volume failed to initialize");
return(false);
}
// move into a U32 - left/right U16 volumes
U32 vol = U32(volume * 65536.f);
if(vol > 0xffff)
vol = 0xffff;
if(mUsingMixer)
{
mMixerVolumeValue.dwValue = vol;
mixerSetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_SETCONTROLDETAILSF_VALUE);
}
else
{
vol |= vol << 16;
auxSetVolume(mAuxVolumeDeviceId, vol);
}
setLastError("");
return(true);
}
//------------------------------------------------------------------------------
void Win32RedBookDevice::openVolume()
{
setLastError("");
// first attempt to get the volume control through the mixer API
S32 i;
for(i = mixerGetNumDevs() - 1; i >= 0; i--)
{
// open the mixer
if(mixerOpen((HMIXER*)&mVolumeDeviceId, i, 0, 0, 0) == MMSYSERR_NOERROR)
{
MIXERLINE lineInfo;
memset(&lineInfo, 0, sizeof(lineInfo));
lineInfo.cbStruct = sizeof(lineInfo);
lineInfo.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC;
// get the cdaudio line
if(mixerGetLineInfo(mVolumeDeviceId, &lineInfo, MIXER_GETLINEINFOF_COMPONENTTYPE) == MMSYSERR_NOERROR)
{
MIXERLINECONTROLS lineControls;
MIXERCONTROL volumeControl;
memset(&lineControls, 0, sizeof(lineControls));
lineControls.cbStruct = sizeof(lineControls);
lineControls.dwLineID = lineInfo.dwLineID;
lineControls.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
lineControls.cControls = 1;
lineControls.cbmxctrl = sizeof(volumeControl);
lineControls.pamxctrl = &volumeControl;
memset(&volumeControl, 0, sizeof(volumeControl));
volumeControl.cbStruct = sizeof(volumeControl);
// get the volume control
if(mixerGetLineControls(mVolumeDeviceId, &lineControls, MIXER_GETLINECONTROLSF_ONEBYTYPE) == MMSYSERR_NOERROR)
{
memset(&mMixerVolumeDetails, 0, sizeof(mMixerVolumeDetails));
mMixerVolumeDetails.cbStruct = sizeof(mMixerVolumeDetails);
mMixerVolumeDetails.dwControlID = volumeControl.dwControlID;
mMixerVolumeDetails.cChannels = 1;
mMixerVolumeDetails.cbDetails = sizeof(mMixerVolumeValue);
mMixerVolumeDetails.paDetails = &mMixerVolumeValue;
memset(&mMixerVolumeValue, 0, sizeof(mMixerVolumeValue));
// query the current value
if(mixerGetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_GETCONTROLDETAILSF_VALUE) == MMSYSERR_NOERROR)
{
mUsingMixer = true;
mVolumeInitialized = true;
mOriginalVolume = mMixerVolumeValue.dwValue;
return;
}
}
}
}
mixerClose((HMIXER)mVolumeDeviceId);
}
// try aux
for(i = auxGetNumDevs() - 1; i >= 0; i--)
{
AUXCAPS caps;
auxGetDevCaps(i, &caps, sizeof(AUXCAPS));
if((caps.wTechnology == AUXCAPS_CDAUDIO) && (caps.dwSupport & AUXCAPS_VOLUME))
{
mAuxVolumeDeviceId = i;
mVolumeInitialized = true;
mUsingMixer = false;
auxGetVolume(i, (unsigned long *)&mOriginalVolume);
return;
}
}
setLastError("Volume failed to initialize");
}
void Win32RedBookDevice::closeVolume()
{
setLastError("");
if(!mVolumeInitialized)
return;
if(mUsingMixer)
{
mMixerVolumeValue.dwValue = mOriginalVolume;
mixerSetControlDetails(mVolumeDeviceId, &mMixerVolumeDetails, MIXER_SETCONTROLDETAILSF_VALUE);
mixerClose((HMIXER)mVolumeDeviceId);
}
else
auxSetVolume(mAuxVolumeDeviceId, mOriginalVolume);
mVolumeInitialized = false;
}
//------------------------------------------------------------------------------
void Win32RedBookDevice::setLastError(const char * error)
{
RedBook::setLastError(error);
}
void Win32RedBookDevice::setLastError(U32 errorId)
{
char buffer[256];
if(!mciGetErrorStringA(errorId, buffer, sizeof(buffer) - 1))
setLastError("Failed to get MCI error string!");
else
setLastError(buffer);
}

View file

@ -0,0 +1,75 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platformWin32/platformWin32.h"
#include "platform/threads/semaphore.h"
class PlatformSemaphore
{
public:
HANDLE *semaphore;
PlatformSemaphore(S32 initialCount)
{
semaphore = new HANDLE;
*semaphore = CreateSemaphore(0, initialCount, S32_MAX, 0);
}
~PlatformSemaphore()
{
CloseHandle(*(HANDLE*)(semaphore));
delete semaphore;
semaphore = NULL;
}
};
Semaphore::Semaphore(S32 initialCount)
{
mData = new PlatformSemaphore(initialCount);
}
Semaphore::~Semaphore()
{
AssertFatal(mData && mData->semaphore, "Semaphore::destroySemaphore: invalid semaphore");
delete mData;
}
bool Semaphore::acquire(bool block, S32 timeoutMS)
{
AssertFatal(mData && mData->semaphore, "Semaphore::acquireSemaphore: invalid semaphore");
if(block)
{
WaitForSingleObject(*(HANDLE*)(mData->semaphore), timeoutMS != -1 ? timeoutMS : INFINITE );
return(true);
}
else
{
DWORD result = WaitForSingleObject(*(HANDLE*)(mData->semaphore), 0);
return(result == WAIT_OBJECT_0);
}
}
void Semaphore::release()
{
AssertFatal(mData && mData->semaphore, "Semaphore::releaseSemaphore: invalid semaphore");
ReleaseSemaphore(*(HANDLE*)(mData->semaphore), 1, 0);
}

View file

@ -0,0 +1,90 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platformTLS.h"
#include "platformWin32/platformWin32.h"
#include "core/util/safeDelete.h"
#define TORQUE_ALLOC_STORAGE(member, cls, data) \
AssertFatal(sizeof(cls) <= sizeof(data), avar("Error, storage for %s must be %d bytes.", #cls, sizeof(cls))); \
member = (cls *) data; \
constructInPlace(member)
//-----------------------------------------------------------------------------
struct PlatformThreadStorage
{
DWORD mTlsIndex;
};
//-----------------------------------------------------------------------------
ThreadStorage::ThreadStorage()
{
TORQUE_ALLOC_STORAGE(mThreadStorage, PlatformThreadStorage, mStorage);
mThreadStorage->mTlsIndex = TlsAlloc();
}
ThreadStorage::~ThreadStorage()
{
TlsFree(mThreadStorage->mTlsIndex);
destructInPlace(mThreadStorage);
}
void *ThreadStorage::get()
{
return TlsGetValue(mThreadStorage->mTlsIndex);
}
void ThreadStorage::set(void *value)
{
TlsSetValue(mThreadStorage->mTlsIndex, value);
}
/* POSIX IMPLEMENTATION LOOKS LIKE THIS:
class PlatformThreadStorage
{
pthread_key_t mThreadKey;
};
ThreadStorage::ThreadStorage()
{
TORQUE_ALLOC_STORAGE(mThreadStorage, PlatformThreadStorage, mStorage);
pthread_key_create(&mThreadStorage->mThreadKey, NULL);
}
ThreadStorage::~ThreadStorage()
{
pthread_key_delete(mThreadStorage->mThreadKey);
}
void *ThreadStorage::get()
{
return pthread_getspecific(mThreadStorage->mThreadKey);
}
void ThreadStorage::set(void *value)
{
pthread_setspecific(mThreadStorage->mThreadKey, value);
}
*/

View file

@ -0,0 +1,168 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "platformWin32/platformWin32.h"
#include "time.h"
void Platform::sleep(U32 ms)
{
Sleep(ms);
}
//--------------------------------------
void Platform::getLocalTime(LocalTime &lt)
{
struct tm *systime;
time_t long_time;
time( &long_time ); // Get time as long integer.
systime = localtime( &long_time ); // Convert to local time.
lt.sec = systime->tm_sec;
lt.min = systime->tm_min;
lt.hour = systime->tm_hour;
lt.month = systime->tm_mon;
lt.monthday = systime->tm_mday;
lt.weekday = systime->tm_wday;
lt.year = systime->tm_year;
lt.yearday = systime->tm_yday;
lt.isdst = systime->tm_isdst;
}
String Platform::localTimeToString( const LocalTime &lt )
{
// Converting a LocalTime to SYSTEMTIME
// requires a few annoying adjustments.
SYSTEMTIME st;
st.wMilliseconds = 0;
st.wSecond = lt.sec;
st.wMinute = lt.min;
st.wHour = lt.hour;
st.wDay = lt.monthday;
st.wDayOfWeek = lt.weekday;
st.wMonth = lt.month + 1;
st.wYear = lt.year + 1900;
TCHAR buffer[1024] = {0};
int result = 0;
String outStr;
// Note: The 'Ex' version of GetDateFormat and GetTimeFormat are preferred
// and have better support for supplemental locales but are not supported
// for version of windows prior to Vista.
//
// Would be nice if Torque was more aware of the OS version and
// take full advantage of it.
result = GetDateFormat( LOCALE_USER_DEFAULT,
DATE_SHORTDATE,
&st,
NULL,
(LPTSTR)buffer,
1024 );
// Also would be nice to have a standard system for torque to
// retrieve and display windows level errors using GetLastError and
// FormatMessage...
AssertWarn( result != 0, "Platform::getLocalTime" );
outStr += buffer;
outStr += "\t";
result = GetTimeFormat( LOCALE_USER_DEFAULT,
0,
&st,
NULL,
(LPTSTR)buffer,
1024 );
AssertWarn( result != 0, "Platform::localTimeToString, error occured!" );
outStr += buffer;
return outStr;
}
U32 Platform::getTime()
{
time_t long_time;
time( &long_time );
return long_time;
}
void Platform::fileToLocalTime(const FileTime & ft, LocalTime * lt)
{
if(!lt)
return;
dMemset(lt, 0, sizeof(LocalTime));
FILETIME winFileTime;
winFileTime.dwLowDateTime = ft.v1;
winFileTime.dwHighDateTime = ft.v2;
SYSTEMTIME winSystemTime;
// convert the filetime to local time
FILETIME convertedFileTime;
if(::FileTimeToLocalFileTime(&winFileTime, &convertedFileTime))
{
// get the time into system time struct
if(::FileTimeToSystemTime((const FILETIME *)&convertedFileTime, &winSystemTime))
{
SYSTEMTIME * time = &winSystemTime;
// fill it in...
lt->sec = time->wSecond;
lt->min = time->wMinute;
lt->hour = time->wHour;
lt->month = time->wMonth;
lt->monthday = time->wDay;
lt->weekday = time->wDayOfWeek;
lt->year = (time->wYear < 1900) ? 1900 : (time->wYear - 1900);
// not calculated
lt->yearday = 0;
lt->isdst = false;
}
}
}
U32 Platform::getRealMilliseconds()
{
return GetTickCount();
}
U32 Platform::getVirtualMilliseconds()
{
return winState.currentTime;
}
void Platform::advanceTime(U32 delta)
{
winState.currentTime += delta;
}

View file

@ -0,0 +1,88 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
// Grab the win32 headers so we can access QPC
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include "platform/platformTimer.h"
#include "math/mMath.h"
class Win32Timer : public PlatformTimer
{
private:
U32 mTickCountCurrent;
U32 mTickCountNext;
S64 mPerfCountCurrent;
S64 mPerfCountNext;
S64 mFrequency;
F64 mPerfCountRemainderCurrent;
F64 mPerfCountRemainderNext;
bool mUsingPerfCounter;
public:
Win32Timer()
{
mPerfCountRemainderCurrent = 0.0f;
// Attempt to use QPC for high res timing, otherwise fallback to GTC.
mUsingPerfCounter = QueryPerformanceFrequency((LARGE_INTEGER *) &mFrequency);
if(mUsingPerfCounter)
mUsingPerfCounter = QueryPerformanceCounter((LARGE_INTEGER *) &mPerfCountCurrent);
if(!mUsingPerfCounter)
mTickCountCurrent = GetTickCount();
}
const S32 getElapsedMs()
{
if(mUsingPerfCounter)
{
// Use QPC, update remainders so we don't leak time, and return the elapsed time.
QueryPerformanceCounter( (LARGE_INTEGER *) &mPerfCountNext);
F64 elapsedF64 = (1000.0 * F64(mPerfCountNext - mPerfCountCurrent) / F64(mFrequency));
elapsedF64 += mPerfCountRemainderCurrent;
U32 elapsed = (U32)mFloor(elapsedF64);
mPerfCountRemainderNext = elapsedF64 - F64(elapsed);
return elapsed;
}
else
{
// Do something naive with GTC.
mTickCountNext = GetTickCount();
return mTickCountNext - mTickCountCurrent;
}
}
void reset()
{
// Do some simple copying to reset the timer to 0.
mTickCountCurrent = mTickCountNext;
mPerfCountCurrent = mPerfCountNext;
mPerfCountRemainderCurrent = mPerfCountRemainderNext;
}
};
PlatformTimer *PlatformTimer::create()
{
return new Win32Timer();
}

View file

@ -0,0 +1,225 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "console/console.h"
#include "core/stringTable.h"
#include "core/strings/unicode.h"
typedef long SHANDLE_PTR;
#include <shlobj.h>
#include <windows.h>
#include <lmcons.h>
#define CSIDL_PROFILE 0x0028
const char *Platform::getUserDataDirectory()
{
TCHAR szBuffer[ MAX_PATH + 1 ];
if(! SHGetSpecialFolderPath( NULL, szBuffer, CSIDL_APPDATA, true ) )
return "";
TCHAR *ptr = szBuffer;
while(*ptr)
{
if(*ptr == '\\')
*ptr = '/';
++ptr;
}
#ifdef UNICODE
char path[ MAX_PATH * 3 + 1 ];
convertUTF16toUTF8( szBuffer, path, sizeof( path ) );
#else
char* path = szBuffer;
#endif
return StringTable->insert( path );
}
const char *Platform::getUserHomeDirectory()
{
TCHAR szBuffer[ MAX_PATH + 1 ];
if(! SHGetSpecialFolderPath( NULL, szBuffer, CSIDL_PERSONAL, false ) )
if(! SHGetSpecialFolderPath( NULL, szBuffer, CSIDL_COMMON_DOCUMENTS, false ) )
return "";
TCHAR *ptr = szBuffer;
while(*ptr)
{
if(*ptr == '\\')
*ptr = '/';
++ptr;
}
#ifdef UNICODE
char path[ MAX_PATH * 3 + 1 ];
convertUTF16toUTF8( szBuffer, path, sizeof( path ) );
#else
char* path = szBuffer;
#endif
return StringTable->insert( path );
}
bool Platform::getUserIsAdministrator()
{
BOOL fReturn = FALSE;
DWORD dwStatus;
DWORD dwAccessMask;
DWORD dwAccessDesired;
DWORD dwACLSize;
DWORD dwStructureSize = sizeof(PRIVILEGE_SET);
PACL pACL = NULL;
PSID psidAdmin = NULL;
HANDLE hToken = NULL;
HANDLE hImpersonationToken = NULL;
PRIVILEGE_SET ps;
GENERIC_MAPPING GenericMapping;
PSECURITY_DESCRIPTOR psdAdmin = NULL;
SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY;
/*
Determine if the current thread is running as a user that is a member of
the local admins group. To do this, create a security descriptor that
has a DACL which has an ACE that allows only local aministrators access.
Then, call AccessCheck with the current thread's token and the security
descriptor. It will say whether the user could access an object if it
had that security descriptor. Note: you do not need to actually create
the object. Just checking access against the security descriptor alone
will be sufficient.
*/
const DWORD ACCESS_READ = 1;
const DWORD ACCESS_WRITE = 2;
__try
{
/*
AccessCheck() requires an impersonation token. We first get a primary
token and then create a duplicate impersonation token. The
impersonation token is not actually assigned to the thread, but is
used in the call to AccessCheck. Thus, this function itself never
impersonates, but does use the identity of the thread. If the thread
was impersonating already, this function uses that impersonation context.
*/
if (!OpenThreadToken(GetCurrentThread(), TOKEN_DUPLICATE|TOKEN_QUERY, TRUE, &hToken))
{
if (GetLastError() != ERROR_NO_TOKEN)
__leave;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE|TOKEN_QUERY, &hToken))
__leave;
}
if (!DuplicateToken (hToken, SecurityImpersonation, &hImpersonationToken))
__leave;
/*
Create the binary representation of the well-known SID that
represents the local administrators group. Then create the security
descriptor and DACL with an ACE that allows only local admins access.
After that, perform the access check. This will determine whether
the current user is a local admin.
*/
if (!AllocateAndInitializeSid(&SystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdmin))
__leave;
psdAdmin = LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
if (psdAdmin == NULL)
__leave;
if (!InitializeSecurityDescriptor(psdAdmin, SECURITY_DESCRIPTOR_REVISION))
__leave;
// Compute size needed for the ACL.
dwACLSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psidAdmin) - sizeof(DWORD);
pACL = (PACL)LocalAlloc(LPTR, dwACLSize);
if (pACL == NULL)
__leave;
if (!InitializeAcl(pACL, dwACLSize, ACL_REVISION2))
__leave;
dwAccessMask= ACCESS_READ | ACCESS_WRITE;
if (!AddAccessAllowedAce(pACL, ACL_REVISION2, dwAccessMask, psidAdmin))
__leave;
if (!SetSecurityDescriptorDacl(psdAdmin, TRUE, pACL, FALSE))
__leave;
/*
AccessCheck validates a security descriptor somewhat; set the group
and owner so that enough of the security descriptor is filled out to
make AccessCheck happy.
*/
SetSecurityDescriptorGroup(psdAdmin, psidAdmin, FALSE);
SetSecurityDescriptorOwner(psdAdmin, psidAdmin, FALSE);
if (!IsValidSecurityDescriptor(psdAdmin))
__leave;
dwAccessDesired = ACCESS_READ;
/*
Initialize GenericMapping structure even though you
do not use generic rights.
*/
GenericMapping.GenericRead = ACCESS_READ;
GenericMapping.GenericWrite = ACCESS_WRITE;
GenericMapping.GenericExecute = 0;
GenericMapping.GenericAll = ACCESS_READ | ACCESS_WRITE;
if (!AccessCheck(psdAdmin, hImpersonationToken, dwAccessDesired,
&GenericMapping, &ps, &dwStructureSize, &dwStatus,
&fReturn))
{
fReturn = FALSE;
__leave;
}
}
__finally
{
// Clean up.
if (pACL) LocalFree(pACL);
if (psdAdmin) LocalFree(psdAdmin);
if (psidAdmin) FreeSid(psidAdmin);
if (hImpersonationToken) CloseHandle (hImpersonationToken);
if (hToken) CloseHandle (hToken);
}
return fReturn;
}

View file

@ -0,0 +1,111 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platformWin32/platformWin32.h"
#include "platform/platformVFS.h"
#include "console/console.h"
#include "core/stream/memstream.h"
#include "core/util/zip/zipArchive.h"
#include "core/util/safeDelete.h"
#include "VFSRes.h"
//-----------------------------------------------------------------------------
struct Win32VFSState
{
S32 mRefCount;
HGLOBAL mResData;
MemStream *mZipStream;
Zip::ZipArchive *mZip;
Win32VFSState() : mResData(NULL), mZip(NULL), mRefCount(0), mZipStream(NULL)
{
}
};
static Win32VFSState gVFSState;
//-----------------------------------------------------------------------------
Zip::ZipArchive *openEmbeddedVFSArchive()
{
if(gVFSState.mZip)
{
++gVFSState.mRefCount;
return gVFSState.mZip;
}
HRSRC hRsrc = FindResource(NULL, MAKEINTRESOURCE(IDR_ZIPFILE), dT("RT_RCDATA"));
if(hRsrc == NULL)
return NULL;
if((gVFSState.mResData = LoadResource(NULL, hRsrc)) == NULL)
return NULL;
void * mem = LockResource(gVFSState.mResData);
if(mem != NULL)
{
U32 size = SizeofResource(NULL, hRsrc);
gVFSState.mZipStream = new MemStream(size, mem, true, false);
gVFSState.mZip = new Zip::ZipArchive;
if(gVFSState.mZip->openArchive(gVFSState.mZipStream))
{
++gVFSState.mRefCount;
return gVFSState.mZip;
}
SAFE_DELETE(gVFSState.mZip);
SAFE_DELETE(gVFSState.mZipStream);
}
FreeResource(gVFSState.mResData);
gVFSState.mResData = NULL;
return NULL;
}
void closeEmbeddedVFSArchive()
{
if(gVFSState.mRefCount == 0)
return;
--gVFSState.mRefCount;
if(gVFSState.mRefCount < 1)
{
SAFE_DELETE(gVFSState.mZip);
SAFE_DELETE(gVFSState.mZipStream);
if(gVFSState.mResData)
{
FreeResource(gVFSState.mResData);
gVFSState.mResData = NULL;
}
}
}

View file

@ -0,0 +1,786 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include <windows.h>
#include "core/crc.h"
#include "core/frameAllocator.h"
#include "core/util/str.h"
#include "core/strings/stringFunctions.h"
#include "core/strings/unicode.h"
#include "platform/platformVolume.h"
#include "platformWin32/winVolume.h"
#include "console/console.h"
#ifndef NGROUPS_UMAX
#define NGROUPS_UMAX 32
#endif
namespace Torque
{
namespace Win32
{
// If the file is a Directory, Offline, System or Temporary then FALSE
#define S_ISREG(Flags) \
!((Flags) & \
(FILE_ATTRIBUTE_DIRECTORY | \
FILE_ATTRIBUTE_OFFLINE | \
FILE_ATTRIBUTE_SYSTEM | \
FILE_ATTRIBUTE_TEMPORARY))
#define S_ISDIR(Flags) \
((Flags) & FILE_ATTRIBUTE_DIRECTORY)
//-----------------------------------------------------------------------------
class Win32FileSystemChangeNotifier : public FileSystemChangeNotifier
{
public:
Win32FileSystemChangeNotifier( FileSystem *fs )
: FileSystemChangeNotifier( fs )
{
VECTOR_SET_ASSOCIATION( mHandleList );
VECTOR_SET_ASSOCIATION( mDirs );
}
// for use in the thread itself
U32 getNumHandles() const { return mHandleList.size(); }
HANDLE *getHANDLES() { return mHandleList.address(); }
private:
virtual void internalProcessOnce();
virtual bool internalAddNotification( const Path &dir );
virtual bool internalRemoveNotification( const Path &dir );
Vector<Path> mDirs;
Vector<HANDLE> mHandleList;
};
//-----------------------------------------------------------------------------
static String _BuildFileName(const String& prefix,const Path& path)
{
// Need to join the path (minus the root) with our
// internal path name.
String file = prefix;
file = Path::Join(file, '/', path.getPath());
file = Path::Join(file, '/', path.getFileName());
file = Path::Join(file, '.', path.getExtension());
return file;
}
/*
static bool _IsFile(const String& file)
{
// Get file info
WIN32_FIND_DATA info;
HANDLE handle = ::FindFirstFile(PathToOS(file).utf16(), &info);
::FindClose(handle);
if (handle == INVALID_HANDLE_VALUE)
return false;
return S_ISREG(info.dwFileAttributes);
}
*/
static bool _IsDirectory(const String& file)
{
// Get file info
WIN32_FIND_DATAW info;
HANDLE handle = ::FindFirstFileW(PathToOS(file).utf16(), &info);
::FindClose(handle);
if (handle == INVALID_HANDLE_VALUE)
return false;
return S_ISDIR(info.dwFileAttributes);
}
//-----------------------------------------------------------------------------
static void _CopyStatAttributes(const WIN32_FIND_DATAW& info, FileNode::Attributes* attr)
{
// Fill in the return struct.
attr->flags = 0;
if (S_ISDIR(info.dwFileAttributes))
attr->flags |= FileNode::Directory;
if (S_ISREG(info.dwFileAttributes))
attr->flags |= FileNode::File;
if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
attr->flags |= FileNode::ReadOnly;
attr->size = info.nFileSizeLow;
attr->mtime = Win32FileTimeToTime(
info.ftLastWriteTime.dwLowDateTime,
info.ftLastWriteTime.dwHighDateTime);
attr->atime = Win32FileTimeToTime(
info.ftLastAccessTime.dwLowDateTime,
info.ftLastAccessTime.dwHighDateTime);
}
//-----------------------------------------------------------------------------
bool Win32FileSystemChangeNotifier::internalAddNotification( const Path &dir )
{
for ( U32 i = 0; i < mDirs.size(); ++i )
{
if ( mDirs[i] == dir )
return false;
}
Path fullFSPath = mFS->mapTo( dir );
String osPath = PathToOS( fullFSPath );
// Con::printf( "[Win32FileSystemChangeNotifier::internalAddNotification] : [%s]", osPath.c_str() );
HANDLE changeHandle = ::FindFirstChangeNotificationW(
osPath.utf16(), // directory to watch
FALSE, // do not watch subtree
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES); // watch file write changes
if (changeHandle == INVALID_HANDLE_VALUE || changeHandle == NULL)
{
Con::errorf("[Win32FileSystemChangeNotifier::internalAddNotification] : failed on [%s] [%d]", osPath.c_str(), GetLastError());
return false;
}
mDirs.push_back( dir );
mHandleList.push_back( changeHandle );
return true;
}
bool Win32FileSystemChangeNotifier::internalRemoveNotification( const Path &dir )
{
for ( U32 i = 0; i < mDirs.size(); ++i )
{
if ( mDirs[i] != dir )
continue;
::FindCloseChangeNotification( mHandleList[i] );
mDirs.erase( i );
mHandleList.erase( i );
return true;
}
return false;
}
void Win32FileSystemChangeNotifier::internalProcessOnce()
{
// WaitForMultipleObjects has a limit of MAXIMUM_WAIT_OBJECTS (64 at
// the moment), so we have to loop till we've handled the entire set.
for ( U32 i=0; i < mHandleList.size(); i += MAXIMUM_WAIT_OBJECTS )
{
U32 numHandles = getMin( (U32)MAXIMUM_WAIT_OBJECTS, (U32)mHandleList.size() - i );
DWORD dwWaitStatus = WaitForMultipleObjects( numHandles, mHandleList.address()+i, FALSE, 0);
if ( dwWaitStatus == WAIT_FAILED || dwWaitStatus == WAIT_TIMEOUT )
continue;
if ( dwWaitStatus >= WAIT_OBJECT_0 && dwWaitStatus <= (WAIT_OBJECT_0 + numHandles - 1))
{
U32 index = i + dwWaitStatus;
// reset our notification
// NOTE: we do this before letting the volume system check mod times so we don't miss any.
// It is going to loop over the files and check their mod time vs. the saved time.
// This may result in extra calls to internalNotifyDirChanged(), but it will simpley check mod times again.
::FindNextChangeNotification( mHandleList[index] );
internalNotifyDirChanged( mDirs[index] );
}
}
}
//-----------------------------------------------------------------------------
Win32FileSystem::Win32FileSystem(String volume)
{
mVolume = volume;
mChangeNotifier = new Win32FileSystemChangeNotifier( this );
}
Win32FileSystem::~Win32FileSystem()
{
}
FileNodeRef Win32FileSystem::resolve(const Path& path)
{
String file = _BuildFileName(mVolume,path);
WIN32_FIND_DATAW info;
HANDLE handle = ::FindFirstFileW(PathToOS(file).utf16(), &info);
::FindClose(handle);
if (handle != INVALID_HANDLE_VALUE)
{
if (S_ISREG(info.dwFileAttributes))
return new Win32File(path,file);
if (S_ISDIR(info.dwFileAttributes))
return new Win32Directory(path,file);
}
return 0;
}
FileNodeRef Win32FileSystem::create(const Path& path, FileNode::Mode mode)
{
// The file will be created on disk when it's opened.
if (mode & FileNode::File)
return new Win32File(path,_BuildFileName(mVolume,path));
// Create with default permissions.
if (mode & FileNode::Directory)
{
String file = PathToOS(_BuildFileName(mVolume,path));
if (::CreateDirectoryW(file.utf16(), 0))
return new Win32Directory(path, file);
}
return 0;
}
bool Win32FileSystem::remove(const Path& path)
{
// Should probably check for outstanding files or directory objects.
String file = PathToOS(_BuildFileName(mVolume,path));
WIN32_FIND_DATAW info;
HANDLE handle = ::FindFirstFileW(file.utf16(), &info);
::FindClose(handle);
if (handle == INVALID_HANDLE_VALUE)
return false;
if (S_ISDIR(info.dwFileAttributes))
return ::RemoveDirectoryW(file.utf16());
return ::DeleteFileW(file.utf16());
}
bool Win32FileSystem::rename(const Path& from,const Path& to)
{
String fa = PathToOS(_BuildFileName(mVolume,from));
String fb = PathToOS(_BuildFileName(mVolume,to));
return MoveFile(fa.utf16(),fb.utf16());
}
Path Win32FileSystem::mapTo(const Path& path)
{
return _BuildFileName(mVolume,path);
}
Path Win32FileSystem::mapFrom(const Path& path)
{
const String::SizeType volumePathLen = mVolume.length();
String pathStr = path.getFullPath();
if ( mVolume.compare( pathStr, volumePathLen, String::NoCase ))
return Path();
return pathStr.substr( volumePathLen, pathStr.length() - volumePathLen );
}
//-----------------------------------------------------------------------------
Win32File::Win32File(const Path& path,String name)
{
mPath = path;
mName = name;
mStatus = Closed;
mHandle = 0;
}
Win32File::~Win32File()
{
if (mHandle)
close();
}
Path Win32File::getName() const
{
return mPath;
}
FileNode::Status Win32File::getStatus() const
{
return mStatus;
}
bool Win32File::getAttributes(Attributes* attr)
{
WIN32_FIND_DATAW info;
HANDLE handle = ::FindFirstFileW(PathToOS(mName).utf16(), &info);
::FindClose(handle);
if (handle == INVALID_HANDLE_VALUE)
return false;
_CopyStatAttributes(info,attr);
attr->name = mPath;
return true;
}
U64 Win32File::getSize()
{
U64 size;
if (mStatus == Open)
{
// Special case if file is open (handles unflushed buffers)
if ( !GetFileSizeEx(mHandle, (PLARGE_INTEGER)&size) )
size = 0;
return size;
}
else
{
// Fallback to generic function
size = File::getSize();
}
return size;
}
U32 Win32File::calculateChecksum()
{
if (!open( Read ))
return 0;
U64 fileSize = getSize();
U32 bufSize = 1024 * 1024 * 4; // 4MB
FrameTemp<U8> buf( bufSize );
U32 crc = CRC::INITIAL_CRC_VALUE;
while ( fileSize > 0 )
{
U32 bytesRead = getMin( fileSize, bufSize );
if ( read( buf, bytesRead ) != bytesRead )
{
close();
return 0;
}
fileSize -= bytesRead;
crc = CRC::calculateCRC(buf, bytesRead, crc);
}
close();
return crc;
}
bool Win32File::open(AccessMode mode)
{
close();
if (mName.isEmpty())
return mStatus;
struct Mode
{
DWORD mode,share,open;
} Modes[] =
{
{ GENERIC_READ,FILE_SHARE_READ,OPEN_EXISTING }, // Read
{ GENERIC_WRITE,0,CREATE_ALWAYS }, // Write
{ GENERIC_WRITE | GENERIC_READ,0,OPEN_ALWAYS }, // ReadWrite
{ GENERIC_WRITE,0,OPEN_ALWAYS } // WriteAppend
};
Mode& m = (mode == Read)? Modes[0]: (mode == Write)? Modes[1]:
(mode == ReadWrite)? Modes[2]: Modes[3];
mHandle = (void*)::CreateFileW(PathToOS(mName).utf16(),
m.mode, m.share,
NULL, m.open,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if ( mHandle == INVALID_HANDLE_VALUE || mHandle == NULL )
{
_updateStatus();
return false;
}
mStatus = Open;
return true;
}
bool Win32File::close()
{
if (mHandle)
{
::CloseHandle((HANDLE)mHandle);
mHandle = 0;
}
mStatus = Closed;
return true;
}
U32 Win32File::getPosition()
{
if (mStatus == Open || mStatus == EndOfFile)
return ::SetFilePointer((HANDLE)mHandle,0,0,FILE_CURRENT);
return 0;
}
U32 Win32File::setPosition(U32 delta, SeekMode mode)
{
if (mStatus != Open && mStatus != EndOfFile)
return 0;
DWORD fmode;
switch (mode)
{
case Begin: fmode = FILE_BEGIN; break;
case Current: fmode = FILE_CURRENT; break;
case End: fmode = FILE_END; break;
default: fmode = 0; break;
}
DWORD pos = ::SetFilePointer((HANDLE)mHandle,delta,0,fmode);
if (pos == INVALID_SET_FILE_POINTER)
{
mStatus = UnknownError;
return 0;
}
mStatus = Open;
return pos;
}
U32 Win32File::read(void* dst, U32 size)
{
if (mStatus != Open && mStatus != EndOfFile)
return 0;
DWORD bytesRead;
if (!::ReadFile((HANDLE)mHandle,dst,size,&bytesRead,0))
_updateStatus();
else if (bytesRead != size)
mStatus = EndOfFile;
return bytesRead;
}
U32 Win32File::write(const void* src, U32 size)
{
if ((mStatus != Open && mStatus != EndOfFile) || !size)
return 0;
DWORD bytesWritten;
if (!::WriteFile((HANDLE)mHandle,src,size,&bytesWritten,0))
_updateStatus();
return bytesWritten;
}
void Win32File::_updateStatus()
{
switch (::GetLastError())
{
case ERROR_INVALID_ACCESS: mStatus = AccessDenied; break;
case ERROR_TOO_MANY_OPEN_FILES: mStatus = UnknownError; break;
case ERROR_PATH_NOT_FOUND: mStatus = NoSuchFile; break;
case ERROR_FILE_NOT_FOUND: mStatus = NoSuchFile; break;
case ERROR_SHARING_VIOLATION: mStatus = SharingViolation; break;
case ERROR_HANDLE_DISK_FULL: mStatus = FileSystemFull; break;
case ERROR_ACCESS_DENIED: mStatus = AccessDenied; break;
default: mStatus = UnknownError; break;
}
}
//-----------------------------------------------------------------------------
Win32Directory::Win32Directory(const Path& path,String name)
{
mPath = path;
mName = name;
mStatus = Closed;
mHandle = 0;
}
Win32Directory::~Win32Directory()
{
if (mHandle)
close();
}
Path Win32Directory::getName() const
{
return mPath;
}
bool Win32Directory::open()
{
if (!_IsDirectory(mName))
{
mStatus = NoSuchFile;
return false;
}
mStatus = Open;
return true;
}
bool Win32Directory::close()
{
if (mHandle)
{
::FindClose((HANDLE)mHandle);
mHandle = 0;
return true;
}
return false;
}
bool Win32Directory::read(Attributes* entry)
{
if (mStatus != Open)
return false;
WIN32_FIND_DATA info;
if (!mHandle)
{
mHandle = ::FindFirstFileW((PathToOS(mName) + "\\*").utf16(), &info);
if (mHandle == NULL)
{
_updateStatus();
return false;
}
}
else
if (!::FindNextFileW((HANDLE)mHandle, &info))
{
_updateStatus();
return false;
}
// Skip "." and ".." entries
if (info.cFileName[0] == '.' && (info.cFileName[1] == '\0' ||
(info.cFileName[1] == '.' && info.cFileName[2] == '\0')))
return read(entry);
_CopyStatAttributes(info,entry);
entry->name = info.cFileName;
return true;
}
U32 Win32Directory::calculateChecksum()
{
// Return checksum of current entry
return 0;
}
bool Win32Directory::getAttributes(Attributes* attr)
{
WIN32_FIND_DATA info;
HANDLE handle = ::FindFirstFileW(PathToOS(mName).utf16(), &info);
::FindClose(handle);
if (handle == INVALID_HANDLE_VALUE)
{
_updateStatus();
return false;
}
_CopyStatAttributes(info,attr);
attr->name = mPath;
return true;
}
FileNode::Status Win32Directory::getStatus() const
{
return mStatus;
}
void Win32Directory::_updateStatus()
{
switch (::GetLastError())
{
case ERROR_NO_MORE_FILES: mStatus = EndOfFile; break;
case ERROR_INVALID_ACCESS: mStatus = AccessDenied; break;
case ERROR_PATH_NOT_FOUND: mStatus = NoSuchFile; break;
case ERROR_SHARING_VIOLATION: mStatus = SharingViolation; break;
case ERROR_ACCESS_DENIED: mStatus = AccessDenied; break;
default: mStatus = UnknownError; break;
}
}
} // Namespace Win32
bool FS::VerifyWriteAccess(const Path &path)
{
// due to UAC's habit of creating "virtual stores" when permission isn't actually available
// actually create, write, read, verify, and delete a file to the folder being tested
String temp = path.getFullPath();
temp += "\\torque_writetest.tmp";
// first, (try and) delete the file if it exists
::DeleteFileW(temp.utf16());
// now, create the file
HANDLE hFile = ::CreateFileW(PathToOS(temp).utf16(),
GENERIC_WRITE, 0,
NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if ( hFile == INVALID_HANDLE_VALUE || hFile == NULL )
return false;
U32 t = Platform::getTime();
DWORD bytesWritten;
if (!::WriteFile(hFile,&t,sizeof(t),&bytesWritten,0))
{
::CloseHandle(hFile);
::DeleteFileW(temp.utf16());
return false;
}
// close the file
::CloseHandle(hFile);
// open for read
hFile = ::CreateFileW(PathToOS(temp).utf16(),
GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
NULL);
if ( hFile == INVALID_HANDLE_VALUE || hFile == NULL )
return false;
U32 t2 = 0;
DWORD bytesRead;
if (!::ReadFile(hFile,&t2,sizeof(t2),&bytesRead,0))
{
::CloseHandle(hFile);
::DeleteFileW(temp.utf16());
return false;
}
::CloseHandle(hFile);
::DeleteFileW(temp.utf16());
return t == t2;
}
} // Namespace Torque
//-----------------------------------------------------------------------------
Torque::FS::FileSystemRef Platform::FS::createNativeFS( const String &volume )
{
return new Win32::Win32FileSystem( volume );
}
String Platform::FS::getAssetDir()
{
char cen_buf[2048];
#ifdef TORQUE_UNICODE
if (!Platform::getWebDeployment())
{
TCHAR buf[ 2048 ];
::GetModuleFileNameW( NULL, buf, sizeof( buf ) );
convertUTF16toUTF8( buf, cen_buf, sizeof( cen_buf ) );
}
else
{
TCHAR buf[ 2048 ];
GetCurrentDirectoryW( sizeof( buf ) / sizeof( buf[ 0 ] ), buf );
convertUTF16toUTF8( buf, cen_buf, sizeof( cen_buf ) );
return Path::CleanSeparators(cen_buf);
}
#else
::GetModuleFileNameA( NULL, cen_buf, 2047);
#endif
char *delimiter = dStrrchr( cen_buf, '\\' );
if( delimiter != NULL )
*delimiter = '\0';
return Path::CleanSeparators(cen_buf);
}
/// Function invoked by the kernel layer to install OS specific
/// file systems.
bool Platform::FS::InstallFileSystems()
{
WCHAR buffer[1024];
// [8/24/2009 tomb] This stops Windows from complaining about drives that have no disks in
SetErrorMode(SEM_FAILCRITICALERRORS);
// Load all the Win32 logical drives.
DWORD mask = ::GetLogicalDrives();
char drive[] = "A";
char volume[] = "A:/";
while (mask)
{
if (mask & 1)
{
volume[0] = drive[0];
Platform::FS::Mount(drive, Platform::FS::createNativeFS(volume));
}
mask >>= 1;
drive[0]++;
}
// Set the current working dir. Windows normally returns
// upper case driver letters, but the cygwin bash shell
// seems to make it return lower case drives. Force upper
// to be consistent with the mounts.
::GetCurrentDirectory(sizeof(buffer), buffer);
if (buffer[1] == ':')
buffer[0] = dToupper(buffer[0]);
String wd = buffer;
wd += '/';
Platform::FS::SetCwd(wd);
return true;
}

View file

@ -0,0 +1,130 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#ifndef _WINVOLUME_H_
#define _WINVOLUME_H_
#ifndef _VOLUME_H_
#include "core/volume.h"
#endif
namespace Torque
{
using namespace FS;
namespace Win32
{
//-----------------------------------------------------------------------------
class Win32FileSystem: public FileSystem
{
public:
Win32FileSystem(String volume);
~Win32FileSystem();
String getTypeStr() const { return "Win32"; }
FileNodeRef resolve(const Path& path);
FileNodeRef create(const Path& path,FileNode::Mode);
bool remove(const Path& path);
bool rename(const Path& from,const Path& to);
Path mapTo(const Path& path);
Path mapFrom(const Path& path);
private:
String mVolume;
};
//-----------------------------------------------------------------------------
/// Win32 stdio file access.
/// This class makes use the fopen, fread and fwrite for buffered io.
class Win32File: public File
{
public:
~Win32File();
Path getName() const;
Status getStatus() const;
bool getAttributes(Attributes*);
U64 getSize();
U32 getPosition();
U32 setPosition(U32,SeekMode);
bool open(AccessMode);
bool close();
U32 read(void* dst, U32 size);
U32 write(const void* src, U32 size);
private:
friend class Win32FileSystem;
U32 calculateChecksum();
Path mPath;
String mName;
void *mHandle;
Status mStatus;
Win32File(const Path &path, String name);
bool _updateInfo();
void _updateStatus();
};
//-----------------------------------------------------------------------------
class Win32Directory: public Directory
{
public:
~Win32Directory();
Path getName() const;
Status getStatus() const;
bool getAttributes(Attributes*);
bool open();
bool close();
bool read(Attributes*);
private:
friend class Win32FileSystem;
U32 calculateChecksum();
Path mPath;
String mName;
void *mHandle;
Status mStatus;
Win32Directory(const Path &path,String name);
void _updateStatus();
};
} // Namespace
} // Namespace
#endif

View file

@ -0,0 +1,631 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform/platform.h"
#include "platform/event.h"
#include "platformWin32/platformWin32.h"
#include "platformWin32/winConsole.h"
#include "platformWin32/winDirectInput.h"
#include "windowManager/win32/win32Window.h"
#include "console/console.h"
#include "math/mRandom.h"
#include "core/stream/fileStream.h"
#include "T3D/resource.h"
#include <d3d9.h>
#include "gfx/gfxInit.h"
#include "gfx/gfxDevice.h"
#include "core/strings/unicode.h"
#include "gui/core/guiCanvas.h"
extern void createFontInit();
extern void createFontShutdown();
extern void installRedBookDevices();
extern void handleRedBookCallback(U32, U32);
static MRandomLCG sgPlatRandom;
static bool sgQueueEvents;
// is keyboard input a standard (non-changing) VK keycode
#define dIsStandardVK(c) (((0x08 <= (c)) && ((c) <= 0x12)) || \
((c) == 0x1b) || \
((0x20 <= (c)) && ((c) <= 0x2e)) || \
((0x30 <= (c)) && ((c) <= 0x39)) || \
((0x41 <= (c)) && ((c) <= 0x5a)) || \
((0x70 <= (c)) && ((c) <= 0x7B)))
extern InputObjectInstances DIK_to_Key( U8 dikCode );
// static helper variables
static HANDLE gMutexHandle = NULL;
static bool sgDoubleByteEnabled = false;
// track window states
Win32PlatState winState;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------
//
// Microsoft Layer for Unicode
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/mslu/winprog/compiling_your_application_with_the_microsoft_layer_for_unicode.asp
//
//-----------------------------------------------------------------------------------------------------------------------------------------------------------
#ifdef UNICODE
HMODULE LoadUnicowsProc(void)
{
return(LoadLibraryA("unicows.dll"));
}
#ifdef _cplusplus
extern "C" {
#endif
extern FARPROC _PfnLoadUnicows = (FARPROC) &LoadUnicowsProc;
#ifdef _cplusplus
}
#endif
#endif
//--------------------------------------
Win32PlatState::Win32PlatState()
{
log_fp = NULL;
hinstOpenGL = NULL;
hinstGLU = NULL;
hinstOpenAL = NULL;
appDC = NULL;
appInstance = NULL;
currentTime = 0;
processId = 0;
}
//--------------------------------------
bool Platform::excludeOtherInstances(const char *mutexName)
{
#ifdef UNICODE
UTF16 b[512];
convertUTF8toUTF16((UTF8 *)mutexName, b, sizeof(b));
gMutexHandle = CreateMutex(NULL, true, b);
#else
gMutexHandle = CreateMutex(NULL, true, mutexName);
#endif
if(!gMutexHandle)
return false;
if(GetLastError() == ERROR_ALREADY_EXISTS)
{
CloseHandle(gMutexHandle);
gMutexHandle = NULL;
return false;
}
return true;
}
void Platform::restartInstance()
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
TCHAR cen_buf[2048];
GetModuleFileName( NULL, cen_buf, 2047);
// Start the child process.
if( CreateProcess( cen_buf,
NULL, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
!= false )
{
WaitForInputIdle( pi.hProcess, 5000 );
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
}
}
///just check if the app's global mutex exists, and if so,
///return true - otherwise, false. Should be called before ExcludeOther
/// at very start of app execution.
bool Platform::checkOtherInstances(const char *mutexName)
{
#ifdef TORQUE_MULTITHREAD
HANDLE pMutex = NULL;
#ifdef UNICODE
UTF16 b[512];
convertUTF8toUTF16((UTF8 *)mutexName, b, sizeof(b));
pMutex = CreateMutex(NULL, true, b);
#else
pMutex = CreateMutex(NULL, true, mutexName);
#endif
if(!pMutex)
return false;
if(GetLastError() == ERROR_ALREADY_EXISTS)
{
//another mutex of the same name exists
//close ours
CloseHandle(pMutex);
pMutex = NULL;
return true;
}
CloseHandle(pMutex);
pMutex = NULL;
#endif
//we don;t care, always false
return false;
}
//--------------------------------------
void Platform::AlertOK(const char *windowTitle, const char *message)
{
ShowCursor(true);
#ifdef UNICODE
UTF16 m[1024], t[512];
convertUTF8toUTF16((UTF8 *)windowTitle, t, sizeof(t));
convertUTF8toUTF16((UTF8 *)message, m, sizeof(m));
MessageBox(NULL, m, t, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OK);
#else
MessageBox(NULL, message, windowTitle, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OK);
#endif
}
//--------------------------------------
bool Platform::AlertOKCancel(const char *windowTitle, const char *message)
{
ShowCursor(true);
#ifdef UNICODE
UTF16 m[1024], t[512];
convertUTF8toUTF16((UTF8 *)windowTitle, t, sizeof(t));
convertUTF8toUTF16((UTF8 *)message, m, sizeof(m));
return MessageBox(NULL, m, t, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OKCANCEL) == IDOK;
#else
return MessageBox(NULL, message, windowTitle, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_OKCANCEL) == IDOK;
#endif
}
//--------------------------------------
bool Platform::AlertRetry(const char *windowTitle, const char *message)
{
ShowCursor(true);
#ifdef UNICODE
UTF16 m[1024], t[512];
convertUTF8toUTF16((UTF8 *)windowTitle, t, sizeof(t));
convertUTF8toUTF16((UTF8 *)message, m, sizeof(m));
return (MessageBox(NULL, m, t, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_RETRYCANCEL) == IDRETRY);
#else
return (MessageBox(NULL, message, windowTitle, MB_ICONINFORMATION | MB_SETFOREGROUND | MB_TASKMODAL | MB_RETRYCANCEL) == IDRETRY);
#endif
}
//--------------------------------------
HIMC gIMEContext;
static void InitInput()
{
#ifndef TORQUE_LIB
#ifdef UNICODE
//gIMEContext = ImmGetContext(getWin32WindowHandle());
//ImmReleaseContext( getWin32WindowHandle(), gIMEContext );
#endif
#endif
}
//--------------------------------------
void Platform::init()
{
Con::printf("Initializing platform...");
// Set the platform variable for the scripts
Con::setVariable( "$platform", "windows" );
WinConsole::create();
if ( !WinConsole::isEnabled() )
Input::init();
InitInput(); // in case DirectInput falls through
installRedBookDevices();
sgDoubleByteEnabled = GetSystemMetrics( SM_DBCSENABLED );
sgQueueEvents = true;
Con::printf("Done");
}
//--------------------------------------
void Platform::shutdown()
{
sgQueueEvents = false;
if(gMutexHandle)
CloseHandle(gMutexHandle);
Input::destroy();
GFXDevice::destroy();
WinConsole::destroy();
}
extern bool LinkConsoleFunctions;
#ifndef TORQUE_SHARED
extern S32 TorqueMain(S32 argc, const char **argv);
//--------------------------------------
static S32 run(S32 argc, const char **argv)
{
// Console hack to ensure consolefunctions get linked in
LinkConsoleFunctions=true;
createFontInit();
S32 ret = TorqueMain(argc, argv);
createFontShutdown();
return ret;
}
//--------------------------------------
S32 main(S32 argc, const char **argv)
{
winState.appInstance = GetModuleHandle(NULL);
return run(argc, argv);
}
//--------------------------------------
#include "unit/test.h"
#include "app/mainLoop.h"
S32 PASCAL WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR lpszCmdLine, S32)
{
#if 0
// Run a unit test.
StandardMainLoop::initCore();
UnitTesting::TestRun tr;
tr.test("Platform", true);
#else
Vector<char *> argv( __FILE__, __LINE__ );
char moduleName[256];
#ifdef TORQUE_UNICODE
{
TCHAR buf[ 256 ];
GetModuleFileNameW( NULL, buf, sizeof( buf ) );
convertUTF16toUTF8( buf, moduleName, sizeof( moduleName ) );
}
#else
GetModuleFileNameA(NULL, moduleName, sizeof(moduleName));
#endif
argv.push_back(moduleName);
for (const char* word,*ptr = lpszCmdLine; *ptr; )
{
// Eat white space
for (; dIsspace(*ptr) && *ptr; ptr++)
;
// Pick out the next word
for (word = ptr; !dIsspace(*ptr) && *ptr; ptr++)
;
// Add the word to the argument list.
if (*word)
{
int len = ptr - word;
char *arg = (char *) dMalloc(len + 1);
dStrncpy(arg, word, len);
arg[len] = 0;
argv.push_back(arg);
}
}
winState.appInstance = hInstance;
S32 retVal = run(argv.size(), (const char **) argv.address());
for(U32 j = 1; j < argv.size(); j++)
dFree(argv[j]);
return retVal;
#endif
}
#else //TORQUE_SHARED
extern "C"
{
bool torque_engineinit(int argc, const char **argv);
int torque_enginetick();
bool torque_engineshutdown();
};
int TorqueMain(int argc, const char **argv)
{
if (!torque_engineinit(argc, argv))
return 1;
while(torque_enginetick())
{
}
torque_engineshutdown();
return 0;
}
extern "C" {
S32 torque_winmain( HINSTANCE hInstance, HINSTANCE, LPSTR lpszCmdLine, S32)
{
Vector<char *> argv( __FILE__, __LINE__ );
char moduleName[256];
#ifdef TORQUE_UNICODE
{
TCHAR buf[ 256 ];
GetModuleFileNameW( NULL, buf, sizeof( buf ) );
convertUTF16toUTF8( buf, moduleName, sizeof( moduleName ) );
}
#else
GetModuleFileNameA(NULL, moduleName, sizeof(moduleName));
#endif
argv.push_back(moduleName);
for (const char* word,*ptr = lpszCmdLine; *ptr; )
{
// Eat white space
for (; dIsspace(*ptr) && *ptr; ptr++)
;
// Test for quotes
bool withinQuotes = dIsquote(*ptr);
if (!withinQuotes)
{
// Pick out the next word
for (word = ptr; !dIsspace(*ptr) && *ptr; ptr++)
;
}
else
{
// Advance past the first quote. We don't want to include it.
ptr++;
// Pick out the next quote
for (word = ptr; !dIsquote(*ptr) && *ptr; ptr++)
;
}
// Add the word to the argument list.
if (*word)
{
int len = ptr - word;
char *arg = (char *) dMalloc(len + 1);
dStrncpy(arg, word, len);
arg[len] = 0;
argv.push_back(arg);
}
// If we had a quote, skip past it for the next arg
if (withinQuotes && *ptr)
{
ptr++;
}
}
winState.appInstance = hInstance;
S32 retVal = TorqueMain(argv.size(), (const char **) argv.address());
for(U32 j = 1; j < argv.size(); j++)
dFree(argv[j]);
return retVal;
}
} // extern "C"
#endif
//--------------------------------------
F32 Platform::getRandom()
{
return sgPlatRandom.randF();
}
////--------------------------------------
/// Spawn the default Operating System web browser with a URL
/// @param webAddress URL to pass to browser
/// @return true if browser successfully spawned
bool Platform::openWebBrowser( const char* webAddress )
{
static bool sHaveKey = false;
static wchar_t sWebKey[512];
char utf8WebKey[512];
{
HKEY regKey;
DWORD size = sizeof( sWebKey );
if ( RegOpenKeyEx( HKEY_CLASSES_ROOT, dT("\\http\\shell\\open\\command"), 0, KEY_QUERY_VALUE, &regKey ) != ERROR_SUCCESS )
{
Con::errorf( ConsoleLogEntry::General, "Platform::openWebBrowser - Failed to open the HKCR\\http registry key!!!");
return( false );
}
if ( RegQueryValueEx( regKey, dT(""), NULL, NULL, (unsigned char *)sWebKey, &size ) != ERROR_SUCCESS )
{
Con::errorf( ConsoleLogEntry::General, "Platform::openWebBrowser - Failed to query the open command registry key!!!" );
return( false );
}
RegCloseKey( regKey );
sHaveKey = true;
convertUTF16toUTF8(sWebKey,utf8WebKey,512);
#ifdef UNICODE
char *p = dStrstr((const char *)utf8WebKey, "%1");
#else
char *p = strstr( (const char *) sWebKey , "%1");
#endif
if (p) *p = 0;
}
STARTUPINFO si;
dMemset( &si, 0, sizeof( si ) );
si.cb = sizeof( si );
char buf[1024];
#ifdef UNICODE
dSprintf( buf, sizeof( buf ), "%s %s", utf8WebKey, webAddress );
UTF16 b[1024];
convertUTF8toUTF16((UTF8 *)buf, b, sizeof(b));
#else
dSprintf( buf, sizeof( buf ), "%s %s", sWebKey, webAddress );
#endif
//Con::errorf( ConsoleLogEntry::General, "** Web browser command = %s **", buf );
PROCESS_INFORMATION pi;
dMemset( &pi, 0, sizeof( pi ) );
CreateProcess( NULL,
#ifdef UNICODE
b,
#else
buf,
#endif
NULL,
NULL,
false,
CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP,
NULL,
NULL,
&si,
&pi );
return( true );
}
//--------------------------------------
// Login password routines:
//--------------------------------------
#ifdef UNICODE
static const UTF16* TorqueRegKey = dT("SOFTWARE\\GarageGames\\Torque");
#else
static const char* TorqueRegKey = "SOFTWARE\\GarageGames\\Torque";
#endif
const char* Platform::getLoginPassword()
{
HKEY regKey;
char* returnString = NULL;
if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, TorqueRegKey, 0, KEY_QUERY_VALUE, &regKey ) == ERROR_SUCCESS )
{
U8 buf[32];
DWORD size = sizeof( buf );
if ( RegQueryValueEx( regKey, dT("LoginPassword"), NULL, NULL, buf, &size ) == ERROR_SUCCESS )
{
returnString = Con::getReturnBuffer( size + 1 );
dStrcpy( returnString, (const char*) buf );
}
RegCloseKey( regKey );
}
if ( returnString )
return( returnString );
else
return( "" );
}
//--------------------------------------
bool Platform::setLoginPassword( const char* password )
{
HKEY regKey;
if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, TorqueRegKey, 0, KEY_WRITE, &regKey ) == ERROR_SUCCESS )
{
if ( RegSetValueEx( regKey, dT("LoginPassword"), 0, REG_SZ, (const U8*) password, dStrlen( password ) + 1 ) != ERROR_SUCCESS )
Con::errorf( ConsoleLogEntry::General, "setLoginPassword - Failed to set the subkey value!" );
RegCloseKey( regKey );
return( true );
}
else
Con::errorf( ConsoleLogEntry::General, "setLoginPassword - Failed to open the Torque registry key!" );
return( false );
}
//--------------------------------------
// Silly Korean registry key checker:
//
// NOTE: "Silly" refers to the nature of this hack, and is not intended
// as commentary on Koreans as a nationality. Thank you for your
// attention.
//--------------------------------------
ConsoleFunction( isKoreanBuild, bool, 1, 1, "isKoreanBuild()" )
{
argc; argv;
HKEY regKey;
bool result = false;
if ( RegOpenKeyEx( HKEY_LOCAL_MACHINE, TorqueRegKey, 0, KEY_QUERY_VALUE, &regKey ) == ERROR_SUCCESS )
{
DWORD val;
DWORD size = sizeof( val );
if ( RegQueryValueEx( regKey, dT("Korean"), NULL, NULL, (U8*) &val, &size ) == ERROR_SUCCESS )
result = ( val > 0 );
RegCloseKey( regKey );
}
return( result );
}

View file

@ -0,0 +1,54 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//------------------------------
//win_common_prefix.h
//------------------------------
//------------------------------
// basic build defines
// define our platform
//#define TARG_WIN32 1
//#define WIN32 1
// normally, this should be on for a PC build
//#define USEASSEMBLYTERRBLEND 1
//------------------------------
// setup compiler-specific flags
// METROWERKS CODEWARRIOR
//#if __MWERKS__
//#pragma once
// we turn this on to use inlined CW6-safe ASM in CWProject builds.
// ... and thus not require NASM for CWProject building ...
//#define TARG_INLINED_ASM 1
//#endif
// these are general build flags Torque uses.
//#define PNG_NO_READ_tIME 1
//#define PNG_NO_WRITE_TIME 1
//#define NO_MILES_OPENAL 1

View file

@ -0,0 +1,41 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//win_debug_prefix.h
#include "win_common_prefix.h"
// our defines
//#define BUILD_SUFFIX "_DEBUG"
//#define DEBUG 1
//#define ENABLE_ASSERTS 1
// the PC doesn't need this. just the mac...
//#define ITFDUMP_NOASM 1
//#define USEASSEMBLYTERRBLEND 1
//#define PNG_NO_READ_tIME 1
//#define PNG_NO_WRITE_TIME 1
//#define NO_MILES_OPENAL 1

View file

@ -0,0 +1,45 @@
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
//win_debug_prefix.h
#include "win_common_prefix.h"
// our defines
//#define TGE_RELEASE 1
//#define TGE_NO_ASSERTS 1
//#define BUILD_SUFFIX ""
// these should be off in a release build
//#define DEBUG 1
//#define ENABLE_ASSERTS 1
// the PC doesn't need this. just the mac...
//#define ITFDUMP_NOASM 1
//#define USEASSEMBLYTERRBLEND 1
//#define PNG_NO_READ_tIME 1
//#define PNG_NO_WRITE_TIME 1
//#define NO_MILES_OPENAL 1