3rdparty: Add RAInterface

This commit is contained in:
Connor McLaughlin 2022-04-18 20:16:24 +10:00 committed by refractionpcsx2
parent 9f2c4c6b59
commit 843b0b3eb1
10 changed files with 1645 additions and 0 deletions

1
3rdparty/rainterface/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.o

21
3rdparty/rainterface/LICENSE vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 RetroAchievements.org
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.

84
3rdparty/rainterface/RA_Consoles.h vendored Normal file
View File

@ -0,0 +1,84 @@
#ifndef RA_CONSOLES_H
#define RA_CONSOLES_H
/* this list should match the list in rcheevos/include/rconsoles.h */
enum ConsoleID
{
UnknownConsoleID = 0,
MegaDrive = 1,
N64 = 2,
SNES = 3,
GB = 4,
GBA = 5,
GBC = 6,
NES = 7,
PCEngine = 8,
SegaCD = 9,
Sega32X = 10,
MasterSystem = 11,
PlayStation = 12,
Lynx = 13,
NeoGeoPocket = 14,
GameGear = 15,
GameCube = 16,
Jaguar = 17,
DS = 18,
WII = 19,
WIIU = 20,
PlayStation2 = 21,
Xbox = 22,
MagnavoxOdyssey = 23,
PokemonMini = 24,
Atari2600 = 25,
MSDOS = 26,
Arcade = 27,
VirtualBoy = 28,
MSX = 29,
C64 = 30,
ZX81 = 31,
Oric = 32,
SG1000 = 33,
VIC20 = 34,
Amiga = 35,
AtariST = 36,
AmstradCPC = 37,
AppleII = 38,
Saturn = 39,
Dreamcast = 40,
PSP = 41,
CDi = 42,
ThreeDO = 43,
Colecovision = 44,
Intellivision = 45,
Vectrex = 46,
PC8800 = 47,
PC9800 = 48,
PCFX = 49,
Atari5200 = 50,
Atari7800 = 51,
X68K = 52,
WonderSwan = 53,
CassetteVision = 54,
SuperCassetteVision = 55,
NeoGeoCD = 56,
FairchildChannelF = 57,
FMTowns = 58,
ZXSpectrum = 59,
GameAndWatch = 60,
NokiaNGage = 61,
Nintendo3DS = 62,
Supervision = 63,
SharpX1 = 64,
Tic80 = 65,
ThomsonTO8 = 66,
PC6000 = 67,
Pico = 68,
MegaDuck = 69,
Zeebo = 70,
Arduboy = 71,
WASM4 = 72,
NumConsoleIDs
};
#endif /* !RA_CONSOLES_H */

23
3rdparty/rainterface/RA_Emulators.h vendored Normal file
View File

@ -0,0 +1,23 @@
#ifndef RA_EMULATORS_H
#define RA_EMULATORS_H
enum EmulatorID
{
RA_Gens = 0,
RA_Project64 = 1,
RA_Snes9x = 2,
RA_VisualboyAdvance = 3,
RA_Nester = 4,
RA_FCEUX = 5,
RA_PCE = 6,
RA_Libretro = 7,
RA_Meka = 8,
RA_QUASI88 = 9,
RA_AppleWin = 10,
RA_Oricutron = 11,
NumEmulatorIDs,
UnknownEmulator = NumEmulatorIDs
};
#endif /* !RA_EMULATORS_H */

1051
3rdparty/rainterface/RA_Interface.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

386
3rdparty/rainterface/RA_Interface.h vendored Normal file
View File

@ -0,0 +1,386 @@
#ifndef RA_INTERFACE_H
#define RA_INTERFACE_H
#include <wtypes.h> /* HWND */
#ifdef __cplusplus
extern "C" {
#endif
/******************************
* Initialization *
******************************/
/**
* Loads and initializes the DLL.
*
* Must be called before using any of the other functions. Will automatically download the DLL
* if not found, or prompt the user to upgrade if a newer version is available
*
* @param hMainHWND the handle of the main window
* @param nEmulatorID the unique idenfier of the emulator
* @param sClientVersion the current version of the emulator (will be validated against the minimum version for the specified emulator ID)
*/
extern void RA_Init(HWND hMainHWND, int nEmulatorID, const char* sClientVersion);
/**
* Loads and initializes the DLL.
*
* Must be called before using any of the other functions. Will automatically download the DLL
* if not found, or prompt the user to upgrade if a newer version is available
*
* @param hMainHWND the handle of the main window
* @param sClientName the name of the client, displayed in the title bar and included in the User-Agent for API calls
* @param sClientVersion the current version of the client
*/
extern void RA_InitClient(HWND hMainHWND, const char* sClientName, const char* sClientVersion);
/**
* Defines callbacks that the DLL can use to interact with the client.
*
* @param fpUnusedIsActive [no longer used] returns non-zero if a game is running
* @param fpCauseUnpause unpauses the emulator
* @param fpCausePause pauses the emulator
* @param fpRebuildMenu notifies the client that the popup menu has changed (@see RA_CreatePopupMenu)
* @param fpEstimateTitle gets a short description for the game being loaded (parameter is a 256-byte buffer to fill)
* @param fpResetEmulator resets the emulator
* @param fpLoadROM [currently unused] tells the emulator to load a specific game
*/
extern void RA_InstallSharedFunctions(int (*fpUnusedIsActive)(void),
void (*fpCauseUnpause)(void), void (*fpCausePause)(void), void (*fpRebuildMenu)(void),
void (*fpEstimateTitle)(char*), void (*fpResetEmulator)(void), void (*fpLoadROM)(const char*));
/**
* Tells the DLL to use UpdateWindow instead of InvalidateRect when the UI needs to be repainted. This is primarily
* necessary when integrating with an emulator using the SDL library as it keeps the message queue flooded so the
* InvalidateRect messages never get turned into WM_PAINT messages.
*
* @param bEnable non-zero if InvalidateRect calls should be replaced with UpdateWindow
*/
extern void RA_SetForceRepaint(int bEnable);
/**
* Creates a popup menu that can be appended to the main menu of the emulator.
*
* @return handle to the menu. if not attached to the program menu, caller must destroy it themselves.
*/
extern HMENU RA_CreatePopupMenu(void);
/* Resource values for menu items - needed by MFC ON_COMMAND_RANGE macros or WM_COMMAND WndProc handlers
* they're not all currently used, allowing additional items without forcing recompilation of the emulators
*/
#define IDM_RA_MENUSTART 1700
#define IDM_RA_MENUEND 1739
typedef struct RA_MenuItem
{
LPCWSTR sLabel;
LPARAM nID;
int bChecked;
} RA_MenuItem;
/**
* Gets items for building a popup menu.
*
* @param pItems Pre-allocated array to populate [should contain space for at least 32 items]
* @return Number of items populated in the items array
*/
extern int RA_GetPopupMenuItems(RA_MenuItem *pItems);
/**
* Called when a menu item in the popup menu is selected.
*
* @param nID the ID of the menu item (will be between IDM_RA_MENUSTART and IDM_RA_MENUEND)
*/
extern void RA_InvokeDialog(LPARAM nID);
/**
* Provides additional information to include in the User-Agent string for API calls.
*
* This is primarily used to identify dependencies or configurations (such as libretro core versions)
*
* @param sDetail the additional information to include
*/
extern void RA_SetUserAgentDetail(const char* sDetail);
/**
* Attempts to log in to the retroachievements.org site.
*
* Prompts the user for their login credentials and performs the login. If they've previously logged in and have
* chosen to store their credentials, the login occurs without prompting.
*
* @param bBlocking if zero, control is returned to the calling process while the login request is processed by the server.
*/
extern void RA_AttemptLogin(int bBlocking);
/**
* Specifies the console associated to the emulator.
*
* May be called just before loading a game if the emulator supports multiple consoles.
*
* @param nConsoleID the unique identifier of the console associated to the game being loaded.
*/
extern void RA_SetConsoleID(unsigned int nConsoleID);
/**
* Resets memory references created by previous calls to RA_InstallMemoryBank.
*/
extern void RA_ClearMemoryBanks(void);
typedef unsigned char (RA_ReadMemoryFunc)(unsigned int nAddress);
typedef void (RA_WriteMemoryFunc)(unsigned int nAddress, unsigned char nValue);
/**
* Exposes a block of memory to the DLL.
*
* The blocks of memory expected by the DLL are unique per console ID. To identify the correct map for a console,
* view the consoleinfo.c file in the rcheevos repository.
*
* @param nBankID the index of the bank to update. will replace any existing bank at that index.
* @param pReader a function to read from the bank. parameter is the offset within the bank to read from.
* @param pWriter a function to write to the bank. parameters are the offset within the bank to write to and an 8-bit value to write.
* @param nBankSize the size of the bank.
*/
extern void RA_InstallMemoryBank(int nBankID, RA_ReadMemoryFunc pReader, RA_WriteMemoryFunc pWriter, int nBankSize);
typedef unsigned int (RA_ReadMemoryBlockFunc)(unsigned int nAddress, unsigned char* pBuffer, unsigned int nBytes);
extern void RA_InstallMemoryBankBlockReader(int nBankID, RA_ReadMemoryBlockFunc pReader);
/**
* Deinitializes and unloads the DLL.
*/
extern void RA_Shutdown(void);
/******************************
* Overlay *
******************************/
/**
* Determines if the overlay is fully visible.
*
* Precursor check before calling RA_NavigateOverlay
*
* @return non-zero if the overlay is fully visible, zero if it is not.
*/
extern int RA_IsOverlayFullyVisible(void);
/**
* Called to show or hide the overlay.
*
* @param bIsPaused true to show the overlay, false to hide it.
*/
extern void RA_SetPaused(bool bIsPaused);
struct ControllerInput
{
int m_bUpPressed;
int m_bDownPressed;
int m_bLeftPressed;
int m_bRightPressed;
int m_bConfirmPressed; /* Usually C or A */
int m_bCancelPressed; /* Usually B */
int m_bQuitPressed; /* Usually Start */
};
/**
* Passes controller input to the overlay.
*
* Does nothing if the overlay is not fully visible.
*
* @param pInput pointer to a ControllerInput structure indicating which inputs are active.
*/
extern void RA_NavigateOverlay(struct ControllerInput* pInput);
/**
* [deprecated] Updates the overlay for a single frame.
*
* This function just calls RA_NavigateOverlay. Updating and rendering is now handled internally to the DLL.
*/
extern void RA_UpdateRenderOverlay(HDC, struct ControllerInput* pInput, float, RECT*, bool, bool);
/**
* Updates the handle to the main window.
*
* The main window handle is used to anchor the overlay. If the client recreates the handle as the result of switching
* from windowed mode to full screen, or for any other reason, it should call this to reattach the overlay.
*
* @param hMainHWND the new handle of the main window
*/
extern void RA_UpdateHWnd(HWND hMainHWND);
/******************************
* Game Management *
******************************/
/**
* Identifies the game associated to a block of memory.
*
* The block of memory is the fully buffered game file. If more complicated identification is required, the caller
* needs to link against rcheevos/rhash directly to generate the hash, and call RA_IdentifyHash with the result.
* Can be called when switching discs to ensure the additional discs are still associated to the loaded game.
*
* @param pROMData the contents of the game file
* @param nROMSize the size of the game file
* @return the unique identifier of the game, 0 if no association available.
*/
extern unsigned int RA_IdentifyRom(BYTE* pROMData, unsigned int nROMSize);
/**
* Identifies the game associated to a pre-generated hash.
*
* Used when the hash algorithm is something other than full file.
* Can be called when switching discs to ensure the additional discs are still associated to the loaded game.
*
* @param sHash the hash generated by rcheevos/rhash
* @return the unique identifier of the game, 0 if no association available.
*/
extern unsigned int RA_IdentifyHash(const char* sHash);
/**
* Fetches all retroachievements related data for the specified game.
*
* @param nGameId the unique identifier of the game to activate
*/
extern void RA_ActivateGame(unsigned int nGameId);
/**
* Identifies and activates the game associated to a block of memory.
*
* Functions as a call to RA_IdentifyRom followed by a call to RA_ActivateGame.
*
* @param pROMData the contents of the game file
* @param nROMSize the size of the game file
*/
extern void RA_OnLoadNewRom(BYTE* pROMData, unsigned int nROMSize);
/**
* Called before unloading the game to allow the user to save any changes they might have.
*
* @param bIsQuitting non-zero to change the messaging to indicate the emulator is closing.
* @return zero to abort the unload. non-zero to continue.
*/
extern int RA_ConfirmLoadNewRom(int bIsQuitting);
/******************************
* Runtime Functionality *
******************************/
/**
* Does all achievement-related processing for a single frame.
*/
extern void RA_DoAchievementsFrame(void);
/**
* Temporarily disables forced updating of tool windows.
*
* Primarily used while fast-forwarding.
*/
extern void RA_SuspendRepaint(void);
/**
* Resumes forced updating of tool windows.
*/
extern void RA_ResumeRepaint(void);
/**
* [deprecated] Used to be used to ensure the asynchronous server calls are processed on the UI thread.
* That's all managed within the DLL now. Calling this function does nothing.
*/
extern void RA_HandleHTTPResults(void);
/**
* Adds flavor text to the application title bar.
*
* Application title bar is managed by the DLL. Value will be "ClientName - Version - Flavor Text - Username"
*
* @param sCustomMessage the flavor text to include in the title bar.
*/
extern void RA_UpdateAppTitle(const char* sCustomMessage);
/**
* Get the user name of the currently logged in user.
*
* @return user name of the currently logged in user, empty if no user is logged in.
*/
const char* RA_UserName(void);
/**
* Determines if the user is currently playing with hardcore enabled.
*
* The client should disable any features that would give the player an unfair advantage if this returns non-zero.
* Things like loading states, using cheats, modifying RAM, disabling rendering layers, viewing decoded tilemaps, etc.
*
* @return non-zero if hardcore mode is currently active.
*/
extern int RA_HardcoreModeIsActive(void);
/**
* Warns the user they're about to do something that will disable hardcore mode.
*
* @param sActivity what the user is about to do (i.e. "load a state").
* @return non-zero if the user disabled hardcore and the activity is allowed.
* zero if the user declined to disable hardcore and the activity should be aborted.
*/
extern int RA_WarnDisableHardcore(const char* sActivity);
/**
* Disables hardcore mode without prompting or notifying the user.
*
* Should only be called if the client does its own prompting/notification.
* Typically used when an activity cannot be aborted.
*/
extern void RA_DisableHardcore(void);
/**
* Notifies the DLL that the game has been reset.
*
* Disables active leaderboards and resets hit counts on all active achievements.
*/
extern void RA_OnReset(void);
/**
* Notifies the DLL that a save state has been created.
*
* Creates a .rap file next to the state file that contains achievement-related information for the save state.
*
* @param sFilename full path to the save state file.
*/
extern void RA_OnSaveState(const char* sFilename);
/**
* Notifies the DLL that a save state has been loaded.
*
* Loads the .rap file next to the state file that contains achievement-related information for the save state being loaded.
*
* @param sFilename full path to the save state file.
*/
extern void RA_OnLoadState(const char* sFilename);
/**
* Captures the current state of the achievement runtime for inclusion in a save state.
*
* @param pBuffer buffer to write achievement state information to
* @param nBufferSize the size of the buffer
* @return the number of bytes needed to capture the achievement state. if less than nBufferSize, pBuffer
* will not be populated. the function should be called again with a larger buffer.
*/
extern int RA_CaptureState(char* pBuffer, int nBufferSize);
/**
* Restores the state of the achievement runtime from a previously captured state.
*
* @param pBuffer buffer containing previously serialized achievement state information
*/
extern void RA_RestoreState(const char* pBuffer);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif // !RA_INTERFACE_H

5
3rdparty/rainterface/README.md vendored Normal file
View File

@ -0,0 +1,5 @@
# RAInterface
This code is intended to be loaded into another repository as a submodule.
An emulator should include RA_Interface.cpp in its build and link against winhttp.lib. Then, the emulator can be modified to call the hooks provided in RA_Interface.cpp at appropriate times to integrate with the RetroAchievements server via the RA_Integration.dll. See wiki for more details.

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(SolutionDir)common\vsprops\BaseProjectConfig.props" />
<Import Project="$(SolutionDir)common\vsprops\WinSDK.props" />
<PropertyGroup Label="Globals">
<ProjectGuid>{95DD0A0C-D14D-4CFF-A593-820EF26EFCC8}</ProjectGuid>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<WholeProgramOptimization Condition="$(Configuration.Contains(Release))">true</WholeProgramOptimization>
<UseDebugLibraries Condition="$(Configuration.Contains(Debug))">true</UseDebugLibraries>
<UseDebugLibraries Condition="!$(Configuration.Contains(Debug))">false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="PropertySheets">
<Import Project="..\DefaultProjectRootDir.props" />
<Import Project="..\3rdparty.props" />
<Import Condition="$(Configuration.Contains(Debug))" Project="..\..\common\vsprops\CodeGen_Debug.props" />
<Import Condition="$(Configuration.Contains(Devel))" Project="..\..\common\vsprops\CodeGen_Devel.props" />
<Import Condition="$(Configuration.Contains(Release))" Project="..\..\common\vsprops\CodeGen_Release.props" />
<Import Condition="!$(Configuration.Contains(Release))" Project="..\..\common\vsprops\IncrementalLinking.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="RA_Consoles.h" />
<ClInclude Include="RA_Emulators.h" />
<ClInclude Include="RA_Interface.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="RA_Interface.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClInclude Include="RA_Emulators.h" />
<ClInclude Include="RA_Interface.h" />
<ClInclude Include="RA_Consoles.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="RA_Interface.cpp" />
</ItemGroup>
</Project>

View File

@ -59,6 +59,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpuinfo", "3rdparty\cpuinfo
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rcheevos", "3rdparty\rcheevos\rcheevos.vcxproj", "{6D5B5AD9-1525-459B-939F-A5E1082AF6B3}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rcheevos", "3rdparty\rcheevos\rcheevos.vcxproj", "{6D5B5AD9-1525-459B-939F-A5E1082AF6B3}"
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rainterface", "3rdparty\rainterface\rainterface.vcxproj", "{95DD0A0C-D14D-4CFF-A593-820EF26EFCC8}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug AVX2|x64 = Debug AVX2|x64 Debug AVX2|x64 = Debug AVX2|x64
@ -393,6 +395,18 @@ Global
{6D5B5AD9-1525-459B-939F-A5E1082AF6B3}.Release AVX2|x64.Build.0 = Release|x64 {6D5B5AD9-1525-459B-939F-A5E1082AF6B3}.Release AVX2|x64.Build.0 = Release|x64
{6D5B5AD9-1525-459B-939F-A5E1082AF6B3}.Release|x64.ActiveCfg = Release|x64 {6D5B5AD9-1525-459B-939F-A5E1082AF6B3}.Release|x64.ActiveCfg = Release|x64
{6D5B5AD9-1525-459B-939F-A5E1082AF6B3}.Release|x64.Build.0 = Release|x64 {6D5B5AD9-1525-459B-939F-A5E1082AF6B3}.Release|x64.Build.0 = Release|x64
{95DD0A0C-D14D-4CFF-A593-820EF26EFCC8}.Debug AVX2|x64.ActiveCfg = Debug|x64
{95DD0A0C-D14D-4CFF-A593-820EF26EFCC8}.Debug AVX2|x64.Build.0 = Debug|x64
{95DD0A0C-D14D-4CFF-A593-820EF26EFCC8}.Debug|x64.ActiveCfg = Debug|x64
{95DD0A0C-D14D-4CFF-A593-820EF26EFCC8}.Debug|x64.Build.0 = Debug|x64
{95DD0A0C-D14D-4CFF-A593-820EF26EFCC8}.Devel AVX2|x64.ActiveCfg = Devel|x64
{95DD0A0C-D14D-4CFF-A593-820EF26EFCC8}.Devel AVX2|x64.Build.0 = Devel|x64
{95DD0A0C-D14D-4CFF-A593-820EF26EFCC8}.Devel|x64.ActiveCfg = Devel|x64
{95DD0A0C-D14D-4CFF-A593-820EF26EFCC8}.Devel|x64.Build.0 = Devel|x64
{95DD0A0C-D14D-4CFF-A593-820EF26EFCC8}.Release AVX2|x64.ActiveCfg = Release|x64
{95DD0A0C-D14D-4CFF-A593-820EF26EFCC8}.Release AVX2|x64.Build.0 = Release|x64
{95DD0A0C-D14D-4CFF-A593-820EF26EFCC8}.Release|x64.ActiveCfg = Release|x64
{95DD0A0C-D14D-4CFF-A593-820EF26EFCC8}.Release|x64.Build.0 = Release|x64
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -421,6 +435,7 @@ Global
{A4323327-3F2B-4271-83D9-7F9A3C66B6B2} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} {A4323327-3F2B-4271-83D9-7F9A3C66B6B2} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
{7E183337-A7E9-460C-9D3D-568BC9F9BCC1} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} {7E183337-A7E9-460C-9D3D-568BC9F9BCC1} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
{6D5B5AD9-1525-459B-939F-A5E1082AF6B3} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38} {6D5B5AD9-1525-459B-939F-A5E1082AF6B3} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
{95DD0A0C-D14D-4CFF-A593-820EF26EFCC8} = {78EBE642-7A4D-4EA7-86BE-5639C6646C38}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0BC474EA-3628-45D3-9DBC-E22D0B7E0F77} SolutionGuid = {0BC474EA-3628-45D3-9DBC-E22D0B7E0F77}