diff --git a/libcore/include/atlsubset.h b/libcore/include/atlsubset.h
new file mode 100644
index 00000000000..6b5667467f8
--- /dev/null
+++ b/libcore/include/atlsubset.h
@@ -0,0 +1,180 @@
+/* Copyright (C) 2003-2013 Runtime Revolution Ltd.
+
+This file is part of LiveCode.
+
+LiveCode is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License v3 as published by the Free
+Software Foundation.
+
+LiveCode is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with LiveCode. If not see . */
+
+#ifndef __MINI_ATL_H
+#define __MINI_ATL_H
+#define __ATLBASE_H__
+
+#include
+
+////////////////////////////////////////////////////////////////////////////////
+// String Conversion
+
+inline char *_ConvW2A(char *p_ansi, const WCHAR *p_wide, int p_ansi_chars)
+{
+ int t_conv;
+ t_conv = WideCharToMultiByte(CP_ACP, 0, p_wide, -1, p_ansi, p_ansi_chars, NULL, NULL);
+ if (t_conv == 0)
+ return NULL;
+ return p_ansi;
+}
+
+inline WCHAR *_ConvA2W(WCHAR *p_wide, const char *p_ansi, int p_wide_chars)
+{
+ int t_conv;
+ t_conv = MultiByteToWideChar(CP_ACP, 0, p_ansi, -1, p_wide, p_wide_chars);
+ if (t_conv == 0)
+ return NULL;
+ return p_wide;
+}
+
+#define USES_CONVERSION int _conv_chars; char *_conv_ansi_str; WCHAR *_conv_wide_str; (_conv_chars,_conv_ansi_str,_conv_wide_str);
+
+#define W2A(w) (w == NULL ? NULL : (_conv_chars = WideCharToMultiByte(CP_ACP, 0, w, -1, NULL, 0, NULL, NULL), _conv_ansi_str = (char*)alloca(_conv_chars), _ConvW2A(_conv_ansi_str, w, _conv_chars)))
+#define A2W(a) (a == NULL ? NULL : (_conv_chars = MultiByteToWideChar(CP_ACP, 0, a, -1, NULL, 0), _conv_wide_str = (WCHAR*)alloca(_conv_chars * sizeof(WCHAR)), _ConvA2W(_conv_wide_str, a, _conv_chars)))
+
+#if defined(_UNICODE)
+#define W2T(a) (a)
+#define T2W(a) (a)
+#else
+#define W2T W2A
+#define T2W A2W
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// CComPtr
+
+template
+class CComPtr
+{
+public:
+ T** operator & (void)
+ {
+ return &m_ptr;
+ }
+
+ bool operator ! (void)
+ {
+ return m_ptr == NULL;
+ }
+
+ T* operator -> (void)
+ {
+ return m_ptr;
+ }
+
+ operator T* (void)
+ {
+ return m_ptr;
+ }
+
+ T* operator = (const CComPtr &p_comptr)
+ {
+ if (m_ptr != p_comptr.m_ptr)
+ {
+ Release();
+ m_ptr = p_comptr.m_ptr;
+
+ if (m_ptr != NULL)
+ m_ptr->AddRef();
+ }
+
+ return m_ptr;
+ }
+
+ CComPtr()
+ {
+ m_ptr = NULL;
+ }
+
+ //CComPtr(T*);
+ //CComPtr(const CComPtr&);
+
+ //template
+ //CComPtr(const CComPtr&);
+
+ CComPtr(int p_null)
+ {
+ // initialise to NULL
+ m_ptr = NULL;
+ }
+
+ ~CComPtr()
+ {
+ Release();
+ }
+
+ HRESULT CoCreateInstance(GUID p_id, IUnknown *p_outer = NULL, DWORD p_cls_context = CLSCTX_ALL)
+ {
+ HRESULT t_result;
+ t_result = ::CoCreateInstance(p_id, p_outer, p_cls_context, __uuidof(T), (void**)&m_ptr);
+
+ return t_result;
+ }
+
+ void Release(void)
+ {
+ if (m_ptr != NULL)
+ m_ptr->Release();
+ m_ptr = NULL;
+ }
+
+ T* Detach(void)
+ {
+ T* t_ptr = m_ptr;
+ m_ptr = NULL;
+ return t_ptr;
+ }
+
+protected:
+ T *m_ptr;
+};
+
+template
+class CComQIPtr : public CComPtr
+{
+public:
+ T* operator = (IUnknown *p_unknown)
+ {
+ Query(p_unknown);
+ return m_ptr;
+ }
+
+ CComQIPtr() : CComPtr()
+ {
+ }
+
+ //CComQIPtr(T*);
+ CComQIPtr(IUnknown *p_unknown)
+ {
+ Query(p_unknown);
+ }
+
+protected:
+ void Query(IUnknown *p_unknown)
+ {
+ IUnknown *t_queried = NULL;
+ if (p_unknown != NULL)
+ p_unknown->QueryInterface(*I, (void**)&t_queried);
+
+ Release();
+ m_ptr = (T*)t_queried;
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/revspeech/revspeech.vcproj b/revspeech/revspeech.vcproj
index 972eef19cc8..249730cf817 100644
--- a/revspeech/revspeech.vcproj
+++ b/revspeech/revspeech.vcproj
@@ -57,6 +57,7 @@
/>
", m_Npitch);
strcpy( pstrTotal, strXMLtag);
strcat( pstrTotal, p_string);
- szWTextString = new WCHAR[ strlen(pstrTotal) + 20 ];
- wcscpy( szWTextString, A2W(pstrTotal));
+
+ szWTextString = A2W(pstrTotal);
}
else
- {
- int t_length;
- t_length = strlen(p_string);
- szWTextString = new WCHAR[ t_length + 1 ];
-
- wcscpy( szWTextString, A2W(p_string));
- }
+ szWTextString = A2W(p_string);
hr = m_cpVoice->Speak(szWTextString, SPF_ASYNC | SPF_IS_XML, NULL );
- delete[] szWTextString;
-
if( SUCCEEDED( hr ) )
{
isspeaking = true;
@@ -235,13 +227,15 @@ bool WindowsSAPI5Narrator::SpeakToFile(const char* p_string, const char* p_file)
bool t_success;
t_success = true;
- // Set the audio format
- CSpStreamFormat t_audio_format;
- hr = t_audio_format.AssignFormat(SPSF_44kHz16BitStereo);
- if (!SUCCEEDED(hr))
- {
- t_success = false;
- }
+ // Setup the wave format structure - 44kHz, 16bit, stereo
+ WAVEFORMATEX t_waveformat;
+ t_waveformat.wFormatTag = WAVE_FORMAT_PCM;
+ t_waveformat.nChannels = 2;
+ t_waveformat.nBlockAlign = 4;
+ t_waveformat.nSamplesPerSec = 44100;
+ t_waveformat.wBitsPerSample = 16;
+ t_waveformat.nAvgBytesPerSec = t_waveformat.nSamplesPerSec * t_waveformat.nBlockAlign;
+ t_waveformat.cbSize = 0;
// unicode string containg file path
WCHAR* t_file;
@@ -265,10 +259,17 @@ bool WindowsSAPI5Narrator::SpeakToFile(const char* p_string, const char* p_file)
wcscpy(t_file, A2W(t_native_path));
// Bind the output to the stream
- hr = SPBindToFile(t_file, SPFM_CREATE_ALWAYS, &t_cstream, &t_audio_format.FormatId(), t_audio_format.WaveFormatExPtr(), SPFEI_ALL_EVENTS);
- if (!SUCCEEDED(hr))
+ hr = CoCreateInstance(CLSID_SpStream, NULL, CLSCTX_ALL, __uuidof(t_cstream), (void**)&t_cstream);
+ t_success = SUCCEEDED(hr);
+ if (t_success)
{
- t_success = false;
+ hr = t_cstream->BindToFile(t_file, SPFM_CREATE_ALWAYS, &SPDFID_WaveFormatEx, &t_waveformat, SPFEI_ALL_EVENTS);
+ t_success = SUCCEEDED(hr);
+ if (!t_success)
+ {
+ t_cstream->Release();
+ t_cstream = NULL;
+ }
}
}
}
@@ -409,6 +410,156 @@ bool WindowsSAPI5Narrator::GetVolume(int& p_volume)
}
}
+bool _GetCategoryFromId(const WCHAR *p_id, ISpObjectTokenCategory** r_category)
+{
+ bool t_success = true;
+
+ CComPtr t_token_category = NULL;
+ t_success = SUCCEEDED(t_token_category.CoCreateInstance(CLSID_SpObjectTokenCategory));
+
+ if (t_success)
+ t_success = SUCCEEDED(t_token_category->SetId(p_id, FALSE));
+
+ if (t_success)
+ *r_category = t_token_category.Detach();
+
+ return t_success;
+}
+
+bool _EnumTokens(const WCHAR *p_category_id, const WCHAR *p_req_attribs, const WCHAR *p_opt_attribs, IEnumSpObjectTokens **r_enum)
+{
+ bool t_success = true;
+
+ CComPtr t_category;
+ t_success = _GetCategoryFromId(p_category_id, &t_category);
+
+ if (t_success)
+ t_success = SUCCEEDED(t_category->EnumTokens(p_req_attribs, p_opt_attribs, r_enum));
+
+ return t_success;
+}
+
+bool _GetDescription(ISpObjectToken *p_token, WCHAR **r_description)
+{
+ typedef HRESULT (WINAPI *_RegLoadMUIStringWPtr)(HKEY, LPCWSTR, LPWSTR, DWORD, LPDWORD, DWORD, LPCWSTR);
+ static HMODULE s_advapi32 = NULL;
+ static _RegLoadMUIStringWPtr s_RegLoadMUIStringW = NULL;
+
+ if (s_advapi32 == NULL)
+ {
+ // Attempt to load function RegLoadMUIStringW which should be available if we're running on Vista & later
+ s_advapi32 = LoadLibraryA("advapi32.dll");
+ if(s_advapi32 != NULL)
+ s_RegLoadMUIStringW = (_RegLoadMUIStringWPtr)GetProcAddress(s_advapi32, "RegLoadMUIStringW");
+ }
+
+ bool t_success = true;
+
+ if (r_description == NULL)
+ {
+ return false;
+ }
+
+ WCHAR *t_description = NULL;
+
+ if(s_RegLoadMUIStringW != NULL)
+ {
+ WCHAR* t_path = NULL;
+ WCHAR* t_reg_key = NULL;
+ HKEY t_hkey = NULL;
+
+ t_success = SUCCEEDED(p_token->GetId(&t_reg_key));
+
+ if (t_success)
+ {
+ // Split the path & base of the registry key - path is invalid if there is no separator
+ t_path = wcschr(t_reg_key, L'\\');
+ t_success = t_path != NULL;
+ }
+ if(t_success)
+ {
+ *t_path++ = L'\0';
+
+ if (wcscmp(t_reg_key, L"HKEY_LOCAL_MACHINE") == 0)
+ t_success = ERROR_SUCCESS == RegOpenKeyExW(HKEY_LOCAL_MACHINE, t_path, 0, KEY_QUERY_VALUE, &t_hkey);
+ else if (wcscmp(t_reg_key, L"HKEY_CURRENT_USER") == 0)
+ t_success = ERROR_SUCCESS == RegOpenKeyExW(HKEY_CURRENT_USER, t_path, 0, KEY_QUERY_VALUE, &t_hkey);
+ else
+ t_success = false;
+
+ DWORD t_size = 0;
+ if (t_success)
+ t_success = ERROR_MORE_DATA == s_RegLoadMUIStringW(t_hkey, L"Description", NULL, 0, &t_size, 0, NULL);
+ if (t_success)
+ t_success = NULL != (t_description = (WCHAR*) CoTaskMemAlloc(t_size));
+ if (t_success)
+ t_success = ERROR_SUCCESS == s_RegLoadMUIStringW(t_hkey, L"Description", t_description, t_size, NULL, 0, NULL);
+ }
+
+ // Cleanup
+ if(t_hkey)
+ RegCloseKey(t_hkey);
+ if(t_reg_key)
+ CoTaskMemFree(t_reg_key);
+
+ if (!t_success && t_description != NULL)
+ {
+ CoTaskMemFree(t_description);
+ t_description = NULL;
+ }
+ }
+
+ // fetch from the registry - if all else fails, fallback to the default attribute
+ if (s_RegLoadMUIStringW == NULL || !t_success)
+ {
+ WCHAR t_lang_id[10];
+
+ if (_ultow_s(GetUserDefaultUILanguage(), t_lang_id, 9, 16))
+ {
+ t_lang_id[0] = L'0';
+ t_lang_id[1] = 0;
+ }
+
+ HRESULT hr;
+ hr = p_token->GetStringValue(t_lang_id, &t_description);
+ if (hr == SPERR_NOT_FOUND)
+ {
+ hr = p_token->GetStringValue(NULL, &t_description);
+ }
+ t_success = SUCCEEDED(hr);
+ }
+
+ if (t_success)
+ *r_description = t_description;
+
+ return t_success;
+}
+
+// Helper function, frees tokens allocated by ListVoices.
+void ManageCleanup( WCHAR** ppszTokenIds, CSpDynamicString* ppcDesciptionString, ULONG ulNumTokens)
+{
+ ULONG ulIndex;
+
+ // Free all allocated token ids
+ if ( ppszTokenIds )
+ {
+ for ( ulIndex = 0; ulIndex < ulNumTokens; ulIndex++ )
+ {
+ if ( NULL != ppszTokenIds[ulIndex] )
+ {
+ CoTaskMemFree( ppszTokenIds[ulIndex] );
+ }
+ }
+
+ delete [] ppszTokenIds;
+ }
+
+ if ( ppcDesciptionString )
+ {
+ delete [] ppcDesciptionString;
+ }
+}
+
// Returns
// true if listing voices suceeeded, false otherwise.
// Description
@@ -418,6 +569,9 @@ bool WindowsSAPI5Narrator::GetVolume(int& p_volume)
// to get a token enumerator for the voices or if out of memory when listing voices.
bool WindowsSAPI5Narrator::ListVoices(NarratorGender p_gender, NarratorListVoicesCallback p_callback, void* p_context)
{
+ bool t_success = true;
+
+ USES_CONVERSION;
if(!bInited)
return false;
@@ -451,41 +605,40 @@ bool WindowsSAPI5Narrator::ListVoices(NarratorGender p_gender, NarratorListVoice
}
// Get a token enumerator for tts voices available
- HRESULT hr = SpEnumTokens(SPCAT_VOICES, szRequiredAttributes, NULL, &cpEnum);
+ t_success = _EnumTokens(SPCAT_VOICES, szRequiredAttributes, NULL, &cpEnum);
- if (SUCCEEDED(hr))
+ if (t_success)
{
- hr = cpEnum->GetCount( &ulNumTokens );
+ t_success = SUCCEEDED(cpEnum->GetCount( &ulNumTokens ));
- if ( SUCCEEDED( hr ) && 0 != ulNumTokens )
+ if ( t_success && 0 != ulNumTokens )
{
ppcDesciptionString = new CSpDynamicString [ulNumTokens];
if ( NULL == ppcDesciptionString )
{
- hr = E_OUTOFMEMORY;
+ /* TODO - CLEANUP */
return false;
}
ppszTokenIds = new WCHAR* [ulNumTokens];
if ( NULL == ppszTokenIds )
{
- hr = E_OUTOFMEMORY;
+ /* TODO - CLEANUP */
return false;
}
ZeroMemory( ppszTokenIds, ulNumTokens*sizeof( WCHAR* ) );
// Get the next token in the enumeration
// State is maintained in the enumerator
- while (cpEnum->Next(1, &pToken, NULL) == S_OK)
+ while (t_success && cpEnum->Next(1, &pToken, NULL) == S_OK)
{
// Get a string which describes the token, in our case, the voice name
- hr = SpGetDescription( pToken, &ppcDesciptionString[ulIndex] );
- _ASSERTE( SUCCEEDED( hr ) );
+ t_success = SUCCEEDED(_GetDescription( pToken, &ppcDesciptionString[ulIndex] ));
// Get the token id, for a low overhead way to retrieve the token later
// without holding on to the object itself
- hr = pToken->GetId( &ppszTokenIds[ulIndex] );
- _ASSERTE( SUCCEEDED( hr ) );
+ if (t_success)
+ t_success = SUCCEEDED(pToken->GetId( &ppszTokenIds[ulIndex] ));
ulIndex++;
@@ -495,7 +648,7 @@ bool WindowsSAPI5Narrator::ListVoices(NarratorGender p_gender, NarratorListVoice
}
}
// if we've failed to properly initialize, then we should completely shut-down
- if ( S_OK != hr )
+ if ( !t_success )
{
if ( pToken )
{
@@ -509,17 +662,14 @@ bool WindowsSAPI5Narrator::ListVoices(NarratorGender p_gender, NarratorListVoice
for ( ulIndex = 0; ulIndex < ulNumTokens; ulIndex++ )
{
- char* string = ppcDesciptionString[ulIndex].CopyToChar();
+ const char* string = W2A(ppcDesciptionString[ulIndex]);
p_callback(p_context, p_gender, (const char *)string);
}
ManageCleanup( ppszTokenIds, ppcDesciptionString, ulNumTokens );
- return true;
- }
- else
- {
- return false;
}
+
+ return t_success;
}
// Parameters
@@ -533,41 +683,40 @@ bool WindowsSAPI5Narrator::ListVoices(NarratorGender p_gender, NarratorListVoice
// the matching token fails then also returns false.
bool WindowsSAPI5Narrator::SetVoice(const char* p_voice)
{
+ USES_CONVERSION;
if(!bInited)
return false;
+ bool t_success = true;
+
+ const WCHAR *t_wvoice = A2W(p_voice);
+
CComPtr cpEnum;// Pointer to token enumerator
ULONG ulIndex = 0, ulCurTokenIndex = 0, ulNumTokens = 0;
// Find out which token corresponds to our voice which is currently in use
ISpObjectToken *pToken = NULL; // Token interface pointer
- HRESULT hr = SpEnumTokens(SPCAT_VOICES, NULL, NULL, &cpEnum);
+ t_success = _EnumTokens(SPCAT_VOICES, NULL, NULL, &cpEnum);
- if (SUCCEEDED(hr) )
- {
- hr = cpEnum->GetCount( &ulNumTokens );
- }
- else
- {
- return false;
- }
+ if (t_success)
+ t_success = SUCCEEDED(cpEnum->GetCount( &ulNumTokens ));
+ if (t_success)
+ t_success = ulNumTokens > 0;
- if ( SUCCEEDED(hr) && 0 != ulNumTokens)
- {
- while (cpEnum->Next(1, &pToken, NULL) == S_OK)
- {
- CSpDynamicString ppcDesciptionString;
- // Get a string which describes the token, in our case, the voice name
- hr = SpGetDescription( pToken, &ppcDesciptionString);
- _ASSERTE( SUCCEEDED( hr ) );
-
- // Release the token itself
- pToken->Release();
- pToken = NULL;
+ while (t_success && cpEnum->Next(1, &pToken, NULL) == S_OK)
+ {
+ CSpDynamicString ppcDesciptionString;
+ // Get a string which describes the token, in our case, the voice name
+ t_success = _GetDescription( pToken, &ppcDesciptionString);
+
+ // Release the token itself
+ pToken->Release();
+ pToken = NULL;
- char* string = ppcDesciptionString.CopyToChar();
- if(strcmp(string, p_voice) == 0)
+ if (t_success)
+ {
+ if(wcscmp(ppcDesciptionString, t_wvoice) == 0)
{
ulCurTokenIndex = ulIndex;
break;
@@ -575,27 +724,18 @@ bool WindowsSAPI5Narrator::SetVoice(const char* p_voice)
ulIndex++;
}
}
- else
- return false;
- if ( ulIndex < ulNumTokens )
+ if (t_success)
+ t_success = ulIndex < ulNumTokens;
+
+ if (t_success)
{
cpEnum->Item( ulCurTokenIndex, &pToken );
- hr = m_cpVoice->SetVoice( pToken );
- if (SUCCEEDED(hr))
- {
- pToken->Release();
- return true;
- }
- else
- {
- return false;
- }
- }
- else
- {
- return false;
+ t_success = SUCCEEDED(m_cpVoice->SetVoice( pToken ));
+ pToken->Release();
}
+
+ return t_success;
}
// Parameters
@@ -682,28 +822,3 @@ bool WindowsSAPI5Narrator::GetPitch(int& p_pitch)
return true;
}
-
-// Helper function, frees tokens allocated by ListVoices.
-void WindowsSAPI5Narrator::ManageCleanup( WCHAR** ppszTokenIds, CSpDynamicString* ppcDesciptionString, ULONG ulNumTokens)
-{
- ULONG ulIndex;
-
- // Free all allocated token ids
- if ( ppszTokenIds )
- {
- for ( ulIndex = 0; ulIndex < ulNumTokens; ulIndex++ )
- {
- if ( NULL != ppszTokenIds[ulIndex] )
- {
- CoTaskMemFree( ppszTokenIds[ulIndex] );
- }
- }
-
- delete [] ppszTokenIds;
- }
-
- if ( ppcDesciptionString )
- {
- delete [] ppcDesciptionString;
- }
-}
\ No newline at end of file
diff --git a/revspeech/src/w32sapi5speech.h b/revspeech/src/w32sapi5speech.h
index 22e741975e8..8c3ee905a4a 100644
--- a/revspeech/src/w32sapi5speech.h
+++ b/revspeech/src/w32sapi5speech.h
@@ -21,12 +21,13 @@ along with LiveCode. If not see . */
#include "revspeech.h"
#endif
-#include
#include
-#include
+#include
#include
#include
+#include "atlsubset.h"
+
class WindowsSAPI5Narrator: public INarrator
{
public:
@@ -67,10 +68,39 @@ class WindowsSAPI5Narrator: public INarrator
// Pointer to our tts voice
CComPtr m_cpVoice;
- void ManageCleanup( WCHAR** ppszTokenIds, CSpDynamicString* ppcDesciptionString, ULONG ulNumTokens);
bool m_bUseDefaultPitch;
short m_Npitch;
};
+////////////////////////////////////////////////////////////////////////////////
+
+class CSpDynamicString
+{
+public:
+ CSpDynamicString()
+ {
+ m_ptr = NULL;
+ }
+
+ ~CSpDynamicString()
+ {
+ if (m_ptr != NULL)
+ CoTaskMemFree(m_ptr);
+ }
+
+ WCHAR** operator &(void)
+ {
+ return &m_ptr;
+ }
+
+ operator WCHAR* (void)
+ {
+ return m_ptr;
+ }
+
+protected:
+ WCHAR *m_ptr;
+};
+
#endif
diff --git a/revvideograbber/revvideograbber.vcproj b/revvideograbber/revvideograbber.vcproj
index cc1fc146dcc..34c8b5eea1a 100644
--- a/revvideograbber/revvideograbber.vcproj
+++ b/revvideograbber/revvideograbber.vcproj
@@ -56,7 +56,7 @@
/>
. */
#define DXVIDEO_H
#include "videograbber.h"
-#include
-extern CComModule _Module;
-#include
-
#include
#include "qedit.h"
#include
+#include "atlsubset.h"
+
#define MAX_VIDEODEVICE 10
#define MAX_AUDIODEVICE 10
#define MAX_CODEC 50
#define MAX_DEVICENAME_LENGTH 256
-
// defines for video dialog setting
enum E_VideoDialog
{
diff --git a/stage.sln b/stage.sln
index 80148bd8a85..ef9efbb01a5 100644
--- a/stage.sln
+++ b/stage.sln
@@ -110,9 +110,7 @@ Global
{90699A04-E42A-460B-B294-40DDC3E6166B}.Release|Win32.ActiveCfg = Release|Win32
{90699A04-E42A-460B-B294-40DDC3E6166B}.Release|Win32.Build.0 = Release|Win32
{9EF7E3FF-92D7-401E-BB4F-3F5B359DB131}.Debug|Win32.ActiveCfg = Debug|Win32
- {9EF7E3FF-92D7-401E-BB4F-3F5B359DB131}.Debug|Win32.Build.0 = Debug|Win32
{9EF7E3FF-92D7-401E-BB4F-3F5B359DB131}.Release|Win32.ActiveCfg = Release|Win32
- {9EF7E3FF-92D7-401E-BB4F-3F5B359DB131}.Release|Win32.Build.0 = Release|Win32
{4E727414-3B14-45B7-9B89-BFFFE396E1C9}.Debug|Win32.ActiveCfg = Debug|Win32
{4E727414-3B14-45B7-9B89-BFFFE396E1C9}.Debug|Win32.Build.0 = Debug|Win32
{4E727414-3B14-45B7-9B89-BFFFE396E1C9}.Release|Win32.ActiveCfg = Release|Win32