Kawe Mazidjatari b3a68ed095 Add EABase, EAThread and DirtySDK to R5sdk
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
2024-04-05 18:29:03 +02:00

1258 lines
60 KiB
C

/*H*************************************************************************************************
\File netconnwin.c
\Description
Provides network setup and teardown support. Does not actually create any
kind of network connections (see NetPlay).
\Copyright
Copyright (c) 2001-2010 Electronic Arts Inc.
\Version 03/12/2001 (gschaefer) First version
\Version 12/16/2001 (sbevan) Edited config file hack in NetConnDevStart
\Version 06/23/2009 (mclouatre) _NetConFindAdapter() changed to _NetConnFindMacAddress()
*************************************************************************************************H*/
/*** Include files ********************************************************************************/
#define WIN32_LEAN_AND_MEAN 1 // avoid extra stuff
#pragma warning(push,0)
#include <windows.h>
#pragma warning(pop)
#include <winsock2.h>
#include <iphlpapi.h> // for GetAdaptersAddresses()
#include <iptypes.h> // for PIP_ADAPTER_ADDRESSES
#include <stdlib.h> // for strtol()
#include "DirtySDK/dirtysock.h"
#include "DirtySDK/dirtyvers.h"
#include "DirtySDK/dirtysock/dirtymem.h"
#include "DirtySDK/dirtysock/dirtycert.h"
#include "DirtySDK/dirtylang.h" // for locality macros and definitions
#include "DirtySDK/dirtysock/netconn.h"
#include "DirtySDK/proto/protossl.h"
#include "DirtySDK/proto/protoupnp.h"
#include "netconncommon.h"
/*** Defines **************************************************************************************/
// LOCALE_SNAME: defined in WinNls.h (WINVER >= 0x0600). This block is just there for the case where the define is absent.
#ifndef LOCALE_SNAME
#define LOCALE_SNAME 0x0000005c // locale name (ie: en-us)
#endif
//! GetGeoInfo returns XX for unknown country
#define NETCONNWIN_COUNTRY_UNKNOWN_STR ("XX")
//! UPNP port
#define NETCONN_DEFAULT_UPNP_PORT (3659)
/*** Macros ***************************************************************************************/
/*** Type Definitions *****************************************************************************/
//! private module state
typedef struct NetConnRefT
{
NetConnCommonRefT Common; //!< cross-platform netconn data (must come first!)
enum
{
ST_INIT,
ST_CONN,
ST_IDLE,
} eState;
int32_t iPeerPort; //!< peer port to be opened by upnp; if zero, still find upnp router but don't open a port
ProtoUpnpRefT *pProtoUpnp; //!< protoupnp module state
int32_t iThreadCpuAffinity; //!< cpu affinity we use for our internal threads
} NetConnRefT;
/*** Function Prototypes **************************************************************************/
/*** Variables ************************************************************************************/
//! mapping table to map Windows Language IDs to DirtySock encodings
/*
Language Identifier Constants and Strings: http://msdn.microsoft.com/en-us/library/dd318693(v=vs.85).aspx
1. In order to reduce the size of the mapping table:
if locality(Primary Id, SUBLANG_NEUTRAL) == locality(Primary Id, SUBLANG_DEFAULT),
then only locality(Primary Id, SUBLANG_DEFAULT) is included by the table.
That means if we can't find locality(Primary Id, SUBLANG_NEUTRAL) in the table,
we should search the table again with langid(Primary Id, SUBLANG_DEFAULT).
Defined in WinNT.h:
#define SUBLANG_NEUTRAL 0x00 // language neutral
#define SUBLANG_DEFAULT 0x01 // user default
#define SUBLANG_ARABIC_SAUDI_ARABIA 0x01 // Arabic (Saudi Arabia)
// normally, specific sublangs (ex. SUBLANG_ARABIC_SAUDI_ARABIA) are used by the table instead of using SUBLANG_DEFAULT.
Another reason why we don't define all locality(Primary Id, SUBLANG_NEUTRAL) is:
Normally Windows API returns langid(Primary Id, specific_sublang) instead of langid(Primary Id, SUBLANG_NEUTRAL).
2. The reason why we use a mapping table is because Windows API GetLocaleInfo(LOCALE_SNAME) is only available under Vista (or later).
3. The result of GetLocaleInfo(LOCALE_SNAME) will be used to verify if the mapping table is good by _NetConnVerifyAllLocales.
(debug version only, Vista / Windows7 only)
4. NetConnStatus('locl', 0, NULL, 0) can be used to obtain locality for local system.
The return value of NetConnStatus('locl', 0, NULL, 0) is similar to the return value of "locl" on the XBOX 360, ex. 'enUS'.
*/
static uint16_t _NetConn_WindowsLocaleMap[214][3] =
{
{ MAKELANGID(LANG_AFRIKAANS, SUBLANG_AFRIKAANS_SOUTH_AFRICA), LOBBYAPI_LANGUAGE_AFRIKAANS, LOBBYAPI_COUNTRY_SOUTH_AFRICA },
{ MAKELANGID(LANG_ALBANIAN, SUBLANG_ALBANIAN_ALBANIA), LOBBYAPI_LANGUAGE_ALBANIAN, LOBBYAPI_COUNTRY_ALBANIA },
{ MAKELANGID(LANG_AMHARIC, SUBLANG_AMHARIC_ETHIOPIA), LOBBYAPI_LANGUAGE_AMHARIC, LOBBYAPI_COUNTRY_ETHIOPIA },
{ MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_ALGERIA), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_ALGERIA },
{ MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_BAHRAIN), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_BAHRAIN },
{ MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_EGYPT), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_EGYPT },
{ MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_IRAQ), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_IRAQ },
{ MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_JORDAN), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_JORDAN },
{ MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_KUWAIT), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_KUWAIT },
{ MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_LEBANON), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_LEBANON },
{ MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_LIBYA), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_LIBYAN_ARAB_JAMAHIRIYA },
{ MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_MOROCCO), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_MOROCCO },
{ MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_OMAN), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_OMAN },
{ MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_QATAR), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_QATAR },
{ MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_SAUDI_ARABIA), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_SAUDI_ARABIA },
{ MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_SYRIA), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_SYRIAN_ARAB_REPUBLIC },
{ MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_TUNISIA), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_TUNISIA },
{ MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_UAE), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_UNITED_ARAB_EMIRATES },
{ MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_YEMEN), LOBBYAPI_LANGUAGE_ARABIC, LOBBYAPI_COUNTRY_YEMEN },
{ MAKELANGID(LANG_ARMENIAN, SUBLANG_ARMENIAN_ARMENIA), LOBBYAPI_LANGUAGE_ARMENIAN, LOBBYAPI_COUNTRY_ARMENIA },
{ MAKELANGID(LANG_ASSAMESE, SUBLANG_ASSAMESE_INDIA), LOBBYAPI_LANGUAGE_ASSAMESE, LOBBYAPI_COUNTRY_INDIA },
{ MAKELANGID(LANG_AZERI, SUBLANG_AZERI_CYRILLIC), LOBBYAPI_LANGUAGE_AZERBAIJANI, LOBBYAPI_COUNTRY_AZERBAIJAN },
{ MAKELANGID(LANG_AZERI, SUBLANG_AZERI_LATIN), LOBBYAPI_LANGUAGE_AZERBAIJANI, LOBBYAPI_COUNTRY_AZERBAIJAN },
{ MAKELANGID(LANG_BASHKIR, SUBLANG_BASHKIR_RUSSIA), LOBBYAPI_LANGUAGE_BASHKIR, LOBBYAPI_COUNTRY_RUSSIAN_FEDERATION },
{ MAKELANGID(LANG_BASQUE, SUBLANG_BASQUE_BASQUE), LOBBYAPI_LANGUAGE_BASQUE, LOBBYAPI_COUNTRY_SPAIN },
{ MAKELANGID(LANG_BELARUSIAN, SUBLANG_BELARUSIAN_BELARUS), LOBBYAPI_LANGUAGE_BYELORUSSIAN, LOBBYAPI_COUNTRY_BELARUS },
{ MAKELANGID(LANG_BENGALI, SUBLANG_BENGALI_BANGLADESH), LOBBYAPI_LANGUAGE_BENGALI, LOBBYAPI_COUNTRY_BANGLADESH },
{ MAKELANGID(LANG_BENGALI, SUBLANG_BENGALI_INDIA), LOBBYAPI_LANGUAGE_BENGALI, LOBBYAPI_COUNTRY_INDIA },
{ MAKELANGID(LANG_BOSNIAN, SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_CYRILLIC), LOBBYAPI_LANGUAGE_BOSNIAN, LOBBYAPI_COUNTRY_BOSNIA_HERZEGOVINA },
{ MAKELANGID(LANG_BOSNIAN, SUBLANG_BOSNIAN_BOSNIA_HERZEGOVINA_LATIN), LOBBYAPI_LANGUAGE_BOSNIAN, LOBBYAPI_COUNTRY_BOSNIA_HERZEGOVINA },
{ MAKELANGID(LANG_BRETON, SUBLANG_BRETON_FRANCE), LOBBYAPI_LANGUAGE_BRETON, LOBBYAPI_COUNTRY_FRANCE },
{ MAKELANGID(LANG_BULGARIAN, SUBLANG_BULGARIAN_BULGARIA), LOBBYAPI_LANGUAGE_BULGARIAN, LOBBYAPI_COUNTRY_BULGARIA },
{ MAKELANGID(LANG_CATALAN, SUBLANG_CATALAN_CATALAN), LOBBYAPI_LANGUAGE_CATALAN, LOBBYAPI_COUNTRY_SPAIN },
{ MAKELANGID(LANG_CHINESE, SUBLANG_NEUTRAL), LOBBYAPI_LANGUAGE_CHINESE, LOBBYAPI_COUNTRY_CHINA },
{ MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_HONGKONG), LOBBYAPI_LANGUAGE_CHINESE, LOBBYAPI_COUNTRY_HONG_KONG },
{ MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_MACAU), LOBBYAPI_LANGUAGE_CHINESE, LOBBYAPI_COUNTRY_MACAU },
{ MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SINGAPORE), LOBBYAPI_LANGUAGE_CHINESE, LOBBYAPI_COUNTRY_SINGAPORE },
{ MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED), LOBBYAPI_LANGUAGE_CHINESE, LOBBYAPI_COUNTRY_CHINA },
{ MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL), LOBBYAPI_LANGUAGE_CHINESE, LOBBYAPI_COUNTRY_TAIWAN },
{ MAKELANGID(LANG_CORSICAN, SUBLANG_CORSICAN_FRANCE), LOBBYAPI_LANGUAGE_CORSICAN, LOBBYAPI_COUNTRY_FRANCE },
{ MAKELANGID(LANG_CROATIAN, SUBLANG_CROATIAN_BOSNIA_HERZEGOVINA_LATIN), LOBBYAPI_LANGUAGE_CROATIAN, LOBBYAPI_COUNTRY_BOSNIA_HERZEGOVINA },
{ MAKELANGID(LANG_CROATIAN, SUBLANG_CROATIAN_CROATIA), LOBBYAPI_LANGUAGE_CROATIAN, LOBBYAPI_COUNTRY_CROATIA },
{ MAKELANGID(LANG_CZECH, SUBLANG_CZECH_CZECH_REPUBLIC), LOBBYAPI_LANGUAGE_CZECH, LOBBYAPI_COUNTRY_CZECH_REPUBLIC },
{ MAKELANGID(LANG_DANISH, SUBLANG_DANISH_DENMARK), LOBBYAPI_LANGUAGE_DANISH, LOBBYAPI_COUNTRY_DENMARK },
{ MAKELANGID(LANG_DIVEHI, SUBLANG_DIVEHI_MALDIVES), LOBBYAPI_LANGUAGE_DIVEHI, LOBBYAPI_COUNTRY_MALDIVES },
{ MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH_BELGIAN), LOBBYAPI_LANGUAGE_DUTCH, LOBBYAPI_COUNTRY_BELGIUM },
{ MAKELANGID(LANG_DUTCH, SUBLANG_DUTCH), LOBBYAPI_LANGUAGE_DUTCH, LOBBYAPI_COUNTRY_NETHERLANDS },
{ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_AUS), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_AUSTRALIA },
{ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_BELIZE), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_BELIZE },
{ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_CAN), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_CANADA },
{ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_CARIBBEAN), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_UNKNOWN },
{ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_INDIA), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_INDIA },
{ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_EIRE), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_IRELAND },
{ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_JAMAICA), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_JAMAICA },
{ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_MALAYSIA), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_MALAYSIA },
{ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_NZ), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_NEW_ZEALAND },
{ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_PHILIPPINES), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_PHILIPPINES },
{ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_SINGAPORE), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_SINGAPORE },
{ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_SOUTH_AFRICA), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_SOUTH_AFRICA },
{ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_TRINIDAD), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_TRINIDAD_AND_TOBAGO },
{ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_UK), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_UNITED_KINGDOM },
{ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_UNITED_STATES },
{ MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_ZIMBABWE), LOBBYAPI_LANGUAGE_ENGLISH, LOBBYAPI_COUNTRY_ZIMBABWE },
{ MAKELANGID(LANG_ESTONIAN, SUBLANG_ESTONIAN_ESTONIA), LOBBYAPI_LANGUAGE_ESTONIAN, LOBBYAPI_COUNTRY_ESTONIA },
{ MAKELANGID(LANG_FAEROESE, SUBLANG_FAEROESE_FAROE_ISLANDS), LOBBYAPI_LANGUAGE_FAEROESE, LOBBYAPI_COUNTRY_FAEROE_ISLANDS },
{ MAKELANGID(LANG_FINNISH, SUBLANG_FINNISH_FINLAND), LOBBYAPI_LANGUAGE_FINNISH, LOBBYAPI_COUNTRY_FINLAND },
{ MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_BELGIAN), LOBBYAPI_LANGUAGE_FRENCH, LOBBYAPI_COUNTRY_BELGIUM },
{ MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_CANADIAN), LOBBYAPI_LANGUAGE_FRENCH, LOBBYAPI_COUNTRY_CANADA },
{ MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH), LOBBYAPI_LANGUAGE_FRENCH, LOBBYAPI_COUNTRY_FRANCE },
{ MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_LUXEMBOURG), LOBBYAPI_LANGUAGE_FRENCH, LOBBYAPI_COUNTRY_LUXEMBOURG },
{ MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_MONACO), LOBBYAPI_LANGUAGE_FRENCH, LOBBYAPI_COUNTRY_MONACO },
{ MAKELANGID(LANG_FRENCH, SUBLANG_FRENCH_SWISS), LOBBYAPI_LANGUAGE_FRENCH, LOBBYAPI_COUNTRY_SWITZERLAND },
{ MAKELANGID(LANG_FRISIAN, SUBLANG_FRISIAN_NETHERLANDS), LOBBYAPI_LANGUAGE_FRISIAN, LOBBYAPI_COUNTRY_NETHERLANDS },
{ MAKELANGID(LANG_GALICIAN, SUBLANG_GALICIAN_GALICIAN), LOBBYAPI_LANGUAGE_GALICIAN, LOBBYAPI_COUNTRY_SPAIN },
{ MAKELANGID(LANG_GEORGIAN, SUBLANG_GEORGIAN_GEORGIA), LOBBYAPI_LANGUAGE_GEORGIAN, LOBBYAPI_COUNTRY_GEORGIA },
{ MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_AUSTRIAN), LOBBYAPI_LANGUAGE_GERMAN, LOBBYAPI_COUNTRY_AUSTRIA },
{ MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN), LOBBYAPI_LANGUAGE_GERMAN, LOBBYAPI_COUNTRY_GERMANY },
{ MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_LIECHTENSTEIN), LOBBYAPI_LANGUAGE_GERMAN, LOBBYAPI_COUNTRY_LIECHTENSTEIN },
{ MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_LUXEMBOURG), LOBBYAPI_LANGUAGE_GERMAN, LOBBYAPI_COUNTRY_LUXEMBOURG },
{ MAKELANGID(LANG_GERMAN, SUBLANG_GERMAN_SWISS), LOBBYAPI_LANGUAGE_GERMAN, LOBBYAPI_COUNTRY_SWITZERLAND },
{ MAKELANGID(LANG_GREEK, SUBLANG_GREEK_GREECE), LOBBYAPI_LANGUAGE_GREEK, LOBBYAPI_COUNTRY_GREECE },
{ MAKELANGID(LANG_GREENLANDIC, SUBLANG_GREENLANDIC_GREENLAND), LOBBYAPI_LANGUAGE_GREENLANDIC, LOBBYAPI_COUNTRY_GREENLAND },
{ MAKELANGID(LANG_GUJARATI, SUBLANG_GUJARATI_INDIA), LOBBYAPI_LANGUAGE_GUJARATI, LOBBYAPI_COUNTRY_INDIA },
{ MAKELANGID(LANG_HAUSA, SUBLANG_HAUSA_NIGERIA_LATIN), LOBBYAPI_LANGUAGE_HAUSA, LOBBYAPI_COUNTRY_NIGERIA },
{ MAKELANGID(LANG_HEBREW, SUBLANG_HEBREW_ISRAEL), LOBBYAPI_LANGUAGE_HEBREW, LOBBYAPI_COUNTRY_ISRAEL },
{ MAKELANGID(LANG_HINDI, SUBLANG_HINDI_INDIA), LOBBYAPI_LANGUAGE_HINDI, LOBBYAPI_COUNTRY_INDIA },
{ MAKELANGID(LANG_HUNGARIAN, SUBLANG_HUNGARIAN_HUNGARY), LOBBYAPI_LANGUAGE_HUNGARIAN, LOBBYAPI_COUNTRY_HUNGARY },
{ MAKELANGID(LANG_ICELANDIC, SUBLANG_ICELANDIC_ICELAND), LOBBYAPI_LANGUAGE_ICELANDIC, LOBBYAPI_COUNTRY_ICELAND },
{ MAKELANGID(LANG_IGBO, SUBLANG_IGBO_NIGERIA), LOBBYAPI_LANGUAGE_IGBO, LOBBYAPI_COUNTRY_NIGERIA },
{ MAKELANGID(LANG_INDONESIAN, SUBLANG_INDONESIAN_INDONESIA), LOBBYAPI_LANGUAGE_INDONESIAN, LOBBYAPI_COUNTRY_INDONESIA },
{ MAKELANGID(LANG_INUKTITUT, SUBLANG_INUKTITUT_CANADA_LATIN), LOBBYAPI_LANGUAGE_INUKTITUT, LOBBYAPI_COUNTRY_CANADA },
{ MAKELANGID(LANG_INUKTITUT, SUBLANG_INUKTITUT_CANADA), LOBBYAPI_LANGUAGE_INUKTITUT, LOBBYAPI_COUNTRY_CANADA },
{ MAKELANGID(LANG_IRISH, SUBLANG_NEUTRAL), LOBBYAPI_LANGUAGE_IRISH, LOBBYAPI_COUNTRY_IRELAND },
{ MAKELANGID(LANG_IRISH, SUBLANG_IRISH_IRELAND), LOBBYAPI_LANGUAGE_IRISH, LOBBYAPI_COUNTRY_IRELAND },
{ MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN), LOBBYAPI_LANGUAGE_ITALIAN, LOBBYAPI_COUNTRY_ITALY },
{ MAKELANGID(LANG_ITALIAN, SUBLANG_ITALIAN_SWISS), LOBBYAPI_LANGUAGE_ITALIAN, LOBBYAPI_COUNTRY_SWITZERLAND },
{ MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), LOBBYAPI_LANGUAGE_JAPANESE, LOBBYAPI_COUNTRY_JAPAN },
{ MAKELANGID(LANG_KANNADA, SUBLANG_KANNADA_INDIA), LOBBYAPI_LANGUAGE_KANNADA, LOBBYAPI_COUNTRY_INDIA },
{ MAKELANGID(LANG_KAZAK, SUBLANG_KAZAK_KAZAKHSTAN), LOBBYAPI_LANGUAGE_KAZAKH, LOBBYAPI_COUNTRY_KAZAKHSTAN },
{ MAKELANGID(LANG_KHMER, SUBLANG_KHMER_CAMBODIA), LOBBYAPI_LANGUAGE_CAMBODIAN, LOBBYAPI_COUNTRY_CAMBODIA },
{ MAKELANGID(LANG_KINYARWANDA, SUBLANG_KINYARWANDA_RWANDA), LOBBYAPI_LANGUAGE_KINYARWANDA, LOBBYAPI_COUNTRY_RWANDA },
{ MAKELANGID(LANG_KOREAN, SUBLANG_KOREAN), LOBBYAPI_LANGUAGE_KOREAN, LOBBYAPI_COUNTRY_KOREA_REPUBLIC_OF },
{ MAKELANGID(LANG_KYRGYZ, SUBLANG_KYRGYZ_KYRGYZSTAN), LOBBYAPI_LANGUAGE_KIRGHIZ, LOBBYAPI_COUNTRY_KYRGYZSTAN },
{ MAKELANGID(LANG_LAO, SUBLANG_LAO_LAO), LOBBYAPI_LANGUAGE_LAOTHIAN, LOBBYAPI_COUNTRY_LAO_PEOPLES_DEMOCRATIC_REPUBLIC },
{ MAKELANGID(LANG_LATVIAN, SUBLANG_LATVIAN_LATVIA), LOBBYAPI_LANGUAGE_LATVIAN_LETTISH, LOBBYAPI_COUNTRY_LATVIA },
{ MAKELANGID(LANG_LITHUANIAN, SUBLANG_LITHUANIAN), LOBBYAPI_LANGUAGE_LITHUANIAN, LOBBYAPI_COUNTRY_LITHUANIA },
{ MAKELANGID(LANG_LUXEMBOURGISH, SUBLANG_LUXEMBOURGISH_LUXEMBOURG), LOBBYAPI_LANGUAGE_LUXEMBOURGISH, LOBBYAPI_COUNTRY_LUXEMBOURG },
{ MAKELANGID(LANG_MACEDONIAN, SUBLANG_MACEDONIAN_MACEDONIA), LOBBYAPI_LANGUAGE_MACEDONIAN, LOBBYAPI_COUNTRY_MACEDONIA_THE_FORMER_YUGOSLAV_REPUBLIC_OF },
{ MAKELANGID(LANG_MALAY, SUBLANG_MALAY_BRUNEI_DARUSSALAM), LOBBYAPI_LANGUAGE_MALAY, LOBBYAPI_COUNTRY_BRUNEI_DARUSSALAM },
{ MAKELANGID(LANG_MALAY, SUBLANG_MALAY_MALAYSIA), LOBBYAPI_LANGUAGE_MALAY, LOBBYAPI_COUNTRY_MALAYSIA },
{ MAKELANGID(LANG_MALAYALAM, SUBLANG_MALAYALAM_INDIA), LOBBYAPI_LANGUAGE_MALAYALAM, LOBBYAPI_COUNTRY_INDIA },
{ MAKELANGID(LANG_MALTESE, SUBLANG_MALTESE_MALTA), LOBBYAPI_LANGUAGE_MALTESE, LOBBYAPI_COUNTRY_MALTA },
{ MAKELANGID(LANG_MAORI, SUBLANG_MAORI_NEW_ZEALAND), LOBBYAPI_LANGUAGE_MAORI, LOBBYAPI_COUNTRY_NEW_ZEALAND },
{ MAKELANGID(LANG_MARATHI, SUBLANG_MARATHI_INDIA), LOBBYAPI_LANGUAGE_MARATHI, LOBBYAPI_COUNTRY_INDIA },
{ MAKELANGID(LANG_MONGOLIAN, SUBLANG_MONGOLIAN_CYRILLIC_MONGOLIA), LOBBYAPI_LANGUAGE_MONGOLIAN, LOBBYAPI_COUNTRY_MONGOLIA },
{ MAKELANGID(LANG_MONGOLIAN, SUBLANG_MONGOLIAN_PRC), LOBBYAPI_LANGUAGE_MONGOLIAN, LOBBYAPI_COUNTRY_CHINA },
{ MAKELANGID(LANG_NEPALI, SUBLANG_NEPALI_NEPAL), LOBBYAPI_LANGUAGE_NEPALI, LOBBYAPI_COUNTRY_NEPAL },
{ MAKELANGID(LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL), LOBBYAPI_LANGUAGE_NORWEGIAN_BOKMAL, LOBBYAPI_COUNTRY_NORWAY },
{ MAKELANGID(LANG_NORWEGIAN, SUBLANG_NORWEGIAN_NYNORSK), LOBBYAPI_LANGUAGE_NORWEGIAN_NYNORSK, LOBBYAPI_COUNTRY_NORWAY },
{ MAKELANGID(LANG_OCCITAN, SUBLANG_OCCITAN_FRANCE), LOBBYAPI_LANGUAGE_OCCITAN, LOBBYAPI_COUNTRY_FRANCE },
{ MAKELANGID(LANG_ORIYA, SUBLANG_ORIYA_INDIA), LOBBYAPI_LANGUAGE_ORIYA, LOBBYAPI_COUNTRY_INDIA },
{ MAKELANGID(LANG_PASHTO, SUBLANG_PASHTO_AFGHANISTAN), LOBBYAPI_LANGUAGE_PASHTO_PUSHTO, LOBBYAPI_COUNTRY_AFGHANISTAN },
{ MAKELANGID(LANG_PERSIAN, SUBLANG_PERSIAN_IRAN), LOBBYAPI_LANGUAGE_PERSIAN, LOBBYAPI_COUNTRY_IRAN },
{ MAKELANGID(LANG_POLISH, SUBLANG_POLISH_POLAND), LOBBYAPI_LANGUAGE_POLISH, LOBBYAPI_COUNTRY_POLAND },
{ MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE_BRAZILIAN), LOBBYAPI_LANGUAGE_PORTUGUESE, LOBBYAPI_COUNTRY_BRAZIL },
{ MAKELANGID(LANG_PORTUGUESE, SUBLANG_PORTUGUESE), LOBBYAPI_LANGUAGE_PORTUGUESE, LOBBYAPI_COUNTRY_PORTUGAL },
{ MAKELANGID(LANG_PUNJABI, SUBLANG_PUNJABI_INDIA), LOBBYAPI_LANGUAGE_PUNJABI, LOBBYAPI_COUNTRY_INDIA },
{ MAKELANGID(LANG_QUECHUA, SUBLANG_QUECHUA_BOLIVIA), LOBBYAPI_LANGUAGE_QUECHUA, LOBBYAPI_COUNTRY_BOLIVIA },
{ MAKELANGID(LANG_QUECHUA, SUBLANG_QUECHUA_ECUADOR), LOBBYAPI_LANGUAGE_QUECHUA, LOBBYAPI_COUNTRY_ECUADOR },
{ MAKELANGID(LANG_QUECHUA, SUBLANG_QUECHUA_PERU), LOBBYAPI_LANGUAGE_QUECHUA, LOBBYAPI_COUNTRY_PERU },
{ MAKELANGID(LANG_ROMANIAN, SUBLANG_ROMANIAN_ROMANIA), LOBBYAPI_LANGUAGE_ROMANIAN, LOBBYAPI_COUNTRY_ROMANIA },
{ MAKELANGID(LANG_ROMANSH, SUBLANG_ROMANSH_SWITZERLAND), LOBBYAPI_LANGUAGE_RHAETO_ROMANCE, LOBBYAPI_COUNTRY_SWITZERLAND },
{ MAKELANGID(LANG_RUSSIAN, SUBLANG_RUSSIAN_RUSSIA), LOBBYAPI_LANGUAGE_RUSSIAN, LOBBYAPI_COUNTRY_RUSSIAN_FEDERATION },
{ MAKELANGID(LANG_SAMI, SUBLANG_SAMI_NORTHERN_FINLAND), LOBBYAPI_LANGUAGE_SAMI, LOBBYAPI_COUNTRY_FINLAND },
{ MAKELANGID(LANG_SAMI, SUBLANG_SAMI_NORTHERN_NORWAY), LOBBYAPI_LANGUAGE_SAMI, LOBBYAPI_COUNTRY_NORWAY },
{ MAKELANGID(LANG_SAMI, SUBLANG_SAMI_NORTHERN_SWEDEN), LOBBYAPI_LANGUAGE_SAMI, LOBBYAPI_COUNTRY_SWEDEN },
{ MAKELANGID(LANG_SANSKRIT, SUBLANG_SANSKRIT_INDIA), LOBBYAPI_LANGUAGE_SANSKRIT, LOBBYAPI_COUNTRY_INDIA },
{ MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_CYRILLIC), LOBBYAPI_LANGUAGE_SERBIAN, LOBBYAPI_COUNTRY_BOSNIA_HERZEGOVINA },
{ MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_BOSNIA_HERZEGOVINA_LATIN),LOBBYAPI_LANGUAGE_SERBIAN, LOBBYAPI_COUNTRY_BOSNIA_HERZEGOVINA },
{ MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_CYRILLIC), LOBBYAPI_LANGUAGE_SERBIAN, LOBBYAPI_COUNTRY_SERBIA_AND_MONTENEGRO },
{ MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_LATIN), LOBBYAPI_LANGUAGE_SERBIAN, LOBBYAPI_COUNTRY_SERBIA_AND_MONTENEGRO },
{ MAKELANGID(LANG_SERBIAN, 0x0C), LOBBYAPI_LANGUAGE_SERBIAN, LOBBYAPI_COUNTRY_MONTENEGRO },
{ MAKELANGID(LANG_SERBIAN, 0x0B), LOBBYAPI_LANGUAGE_SERBIAN, LOBBYAPI_COUNTRY_MONTENEGRO },
{ MAKELANGID(LANG_SERBIAN, 0x0A), LOBBYAPI_LANGUAGE_SERBIAN, LOBBYAPI_COUNTRY_SERBIA },
{ MAKELANGID(LANG_SERBIAN, 0x09), LOBBYAPI_LANGUAGE_SERBIAN, LOBBYAPI_COUNTRY_SERBIA },
{ MAKELANGID(LANG_SINHALESE, SUBLANG_SINHALESE_SRI_LANKA), LOBBYAPI_LANGUAGE_SINGHALESE, LOBBYAPI_COUNTRY_SRI_LANKA },
{ MAKELANGID(LANG_SLOVAK, SUBLANG_SLOVAK_SLOVAKIA), LOBBYAPI_LANGUAGE_SLOVAK, LOBBYAPI_COUNTRY_SLOVAKIA },
{ MAKELANGID(LANG_SLOVENIAN, SUBLANG_SLOVENIAN_SLOVENIA), LOBBYAPI_LANGUAGE_SLOVENIAN, LOBBYAPI_COUNTRY_SLOVENIA },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_ARGENTINA), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_ARGENTINA },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_BOLIVIA), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_BOLIVIA },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_CHILE), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_CHILE },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_COLOMBIA), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_COLOMBIA },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_COSTA_RICA), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_COSTA_RICA },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_DOMINICAN_REPUBLIC), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_DOMINICAN_REPUBLIC },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_ECUADOR), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_ECUADOR },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_EL_SALVADOR), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_EL_SALVADOR },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_GUATEMALA), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_GUATEMALA },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_HONDURAS), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_HONDURAS },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MEXICAN), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_MEXICO },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_NICARAGUA), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_NICARAGUA },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_PANAMA), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_PANAMA },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_PARAGUAY), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_PARAGUAY },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_PERU), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_PERU },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_PUERTO_RICO), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_PUERTO_RICO },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_MODERN), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_SPAIN },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_SPAIN },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_US), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_UNITED_STATES },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_URUGUAY), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_URUGUAY },
{ MAKELANGID(LANG_SPANISH, SUBLANG_SPANISH_VENEZUELA), LOBBYAPI_LANGUAGE_SPANISH, LOBBYAPI_COUNTRY_VENEZUELA },
{ MAKELANGID(LANG_SWAHILI, SUBLANG_DEFAULT), LOBBYAPI_LANGUAGE_SWAHILI, LOBBYAPI_COUNTRY_KENYA },
{ MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH_FINLAND), LOBBYAPI_LANGUAGE_SWEDISH, LOBBYAPI_COUNTRY_FINLAND },
{ MAKELANGID(LANG_SWEDISH, SUBLANG_SWEDISH), LOBBYAPI_LANGUAGE_SWEDISH, LOBBYAPI_COUNTRY_SWEDEN },
{ MAKELANGID(LANG_TAJIK, SUBLANG_TAJIK_TAJIKISTAN), LOBBYAPI_LANGUAGE_TAJIK, LOBBYAPI_COUNTRY_TAJIKISTAN },
{ MAKELANGID(LANG_TAMIL, SUBLANG_TAMIL_INDIA), LOBBYAPI_LANGUAGE_TAMIL, LOBBYAPI_COUNTRY_INDIA },
{ MAKELANGID(LANG_TATAR, SUBLANG_TATAR_RUSSIA), LOBBYAPI_LANGUAGE_TATAR, LOBBYAPI_COUNTRY_RUSSIAN_FEDERATION },
{ MAKELANGID(LANG_TELUGU, SUBLANG_TELUGU_INDIA), LOBBYAPI_LANGUAGE_TELUGU, LOBBYAPI_COUNTRY_INDIA },
{ MAKELANGID(LANG_THAI, SUBLANG_THAI_THAILAND), LOBBYAPI_LANGUAGE_THAI, LOBBYAPI_COUNTRY_THAILAND },
{ MAKELANGID(LANG_TIBETAN, SUBLANG_TIBETAN_PRC), LOBBYAPI_LANGUAGE_TIBETAN, LOBBYAPI_COUNTRY_CHINA },
{ MAKELANGID(LANG_TSWANA, SUBLANG_TSWANA_SOUTH_AFRICA), LOBBYAPI_LANGUAGE_SETSWANA, LOBBYAPI_COUNTRY_SOUTH_AFRICA },
{ MAKELANGID(LANG_TURKISH, SUBLANG_TURKISH_TURKEY), LOBBYAPI_LANGUAGE_TURKISH, LOBBYAPI_COUNTRY_TURKEY },
{ MAKELANGID(LANG_TURKMEN, SUBLANG_TURKMEN_TURKMENISTAN), LOBBYAPI_LANGUAGE_TURKMEN, LOBBYAPI_COUNTRY_TURKMENISTAN },
{ MAKELANGID(LANG_UIGHUR, SUBLANG_UIGHUR_PRC), LOBBYAPI_LANGUAGE_UIGHUR, LOBBYAPI_COUNTRY_CHINA },
{ MAKELANGID(LANG_UKRAINIAN, SUBLANG_UKRAINIAN_UKRAINE), LOBBYAPI_LANGUAGE_UKRAINIAN, LOBBYAPI_COUNTRY_UKRAINE },
{ MAKELANGID(LANG_URDU, SUBLANG_URDU_PAKISTAN), LOBBYAPI_LANGUAGE_URDU, LOBBYAPI_COUNTRY_PAKISTAN },
{ MAKELANGID(LANG_UZBEK, SUBLANG_UZBEK_CYRILLIC), LOBBYAPI_LANGUAGE_UZBEK, LOBBYAPI_COUNTRY_UZBEKISTAN },
{ MAKELANGID(LANG_UZBEK, SUBLANG_UZBEK_LATIN), LOBBYAPI_LANGUAGE_UZBEK, LOBBYAPI_COUNTRY_UZBEKISTAN },
{ MAKELANGID(LANG_VIETNAMESE, SUBLANG_VIETNAMESE_VIETNAM), LOBBYAPI_LANGUAGE_VIETNAMESE, LOBBYAPI_COUNTRY_VIETNAM },
{ MAKELANGID(LANG_WELSH, SUBLANG_WELSH_UNITED_KINGDOM), LOBBYAPI_LANGUAGE_WELSH, LOBBYAPI_COUNTRY_UNITED_KINGDOM },
{ MAKELANGID(LANG_WOLOF, SUBLANG_WOLOF_SENEGAL), LOBBYAPI_LANGUAGE_WOLOF, LOBBYAPI_COUNTRY_SENEGAL },
{ MAKELANGID(LANG_XHOSA, SUBLANG_XHOSA_SOUTH_AFRICA), LOBBYAPI_LANGUAGE_XHOSA, LOBBYAPI_COUNTRY_SOUTH_AFRICA },
{ MAKELANGID(LANG_YI, SUBLANG_YI_PRC), LOBBYAPI_LANGUAGE_YI, LOBBYAPI_COUNTRY_CHINA },
{ MAKELANGID(LANG_YORUBA, SUBLANG_YORUBA_NIGERIA), LOBBYAPI_LANGUAGE_YORUBA, LOBBYAPI_COUNTRY_NIGERIA },
{ MAKELANGID(LANG_ZULU, SUBLANG_ZULU_SOUTH_AFRICA), LOBBYAPI_LANGUAGE_ZULU, LOBBYAPI_COUNTRY_SOUTH_AFRICA },
{ MAKELANGID(0x91, SUBLANG_DEFAULT), LOBBYAPI_LANGUAGE_SCOTS_GAELIC, LOBBYAPI_COUNTRY_UNITED_KINGDOM },
// the following are valid Windows locales, but not covered by ISO639-1, LOBBYAPI_LANGUAGE_UNKNOWN will be returned for them.
{ MAKELANGID(LANG_SAMI, SUBLANG_SAMI_INARI_FINLAND), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_FINLAND },
{ MAKELANGID(LANG_SAMI, SUBLANG_SAMI_LULE_NORWAY), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_NORWAY },
{ MAKELANGID(LANG_SAMI, SUBLANG_SAMI_LULE_SWEDEN), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_SWEDEN },
{ MAKELANGID(LANG_SAMI, SUBLANG_SAMI_SKOLT_FINLAND), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_FINLAND },
{ MAKELANGID(LANG_SAMI, SUBLANG_SAMI_SOUTHERN_NORWAY), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_NORWAY },
{ MAKELANGID(LANG_SAMI, SUBLANG_SAMI_SOUTHERN_SWEDEN), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_SWEDEN },
{ MAKELANGID(LANG_UPPER_SORBIAN, SUBLANG_UPPER_SORBIAN_GERMANY), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_GERMANY },
{ MAKELANGID(LANG_LOWER_SORBIAN, SUBLANG_LOWER_SORBIAN_GERMANY), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_GERMANY },
{ MAKELANGID(LANG_KONKANI, SUBLANG_KONKANI_INDIA), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_INDIA },
{ MAKELANGID(LANG_SYRIAC, SUBLANG_DEFAULT), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_SYRIAN_ARAB_REPUBLIC },
{ MAKELANGID(LANG_TAMAZIGHT, SUBLANG_NEUTRAL), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_ALGERIA },
{ MAKELANGID(LANG_TAMAZIGHT, SUBLANG_TAMAZIGHT_ALGERIA_LATIN), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_ALGERIA },
{ MAKELANGID(LANG_FILIPINO, SUBLANG_FILIPINO_PHILIPPINES), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_PHILIPPINES },
{ MAKELANGID(LANG_SOTHO, SUBLANG_SOTHO_NORTHERN_SOUTH_AFRICA), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_SOUTH_AFRICA },
{ MAKELANGID(LANG_MAPUDUNGUN, SUBLANG_MAPUDUNGUN_CHILE), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_CHILE },
{ MAKELANGID(LANG_MOHAWK, SUBLANG_MOHAWK_MOHAWK), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_CANADA },
{ MAKELANGID(LANG_ALSATIAN, SUBLANG_ALSATIAN_FRANCE), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_FRANCE },
{ MAKELANGID(LANG_YAKUT, SUBLANG_YAKUT_RUSSIA), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_RUSSIAN_FEDERATION },
{ MAKELANGID(LANG_KICHE, SUBLANG_KICHE_GUATEMALA), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_GUATEMALA },
{ MAKELANGID(LANG_DARI, SUBLANG_DARI_AFGHANISTAN), LOBBYAPI_LANGUAGE_UNKNOWN, LOBBYAPI_COUNTRY_AFGHANISTAN }
};
//! global module ref
static NetConnRefT *_NetConn_pRef = NULL;
/*** Private Functions ****************************************************************************/
/*F********************************************************************************/
/*!
\Function _NetConnGetLocaleInfo
\Description
To obtain locale information for specified lang id.
\Input uLangId - the lang id used to extract locale info from
\Output
int32_t - the same output to NetConnStatus('locl'), ex. enUS
\Version 04/14/2011 (szhu)
*/
/********************************************************************************F*/
static int32_t _NetConnGetLocaleInfo(uint16_t uLangId)
{
uint16_t uLanguage, uCountry;
uint32_t uIndex;
// search the pre-defined mapping table
for (uIndex = 0; uIndex < (sizeof(_NetConn_WindowsLocaleMap)/sizeof(_NetConn_WindowsLocaleMap[0])); uIndex++)
{
// _NetConn_WindowsLocaleMap[uIndex][0]: LangId, [1]: language, [2]: COUNTRY
if (uLangId == _NetConn_WindowsLocaleMap[uIndex][0])
{
uLanguage = _NetConn_WindowsLocaleMap[uIndex][1];
uCountry = _NetConn_WindowsLocaleMap[uIndex][2];
return(LOBBYAPI_LocalizerTokenCreate(uLanguage, uCountry));
}
}
// not found in pre-defined mapping table, search again with SUBLANG_DEFAULT (only for SUBLANG_NEUTRAL)
if ((SUBLANGID(uLangId) == SUBLANG_NEUTRAL) && (SUBLANG_NEUTRAL != SUBLANG_DEFAULT))
{
uLangId = PRIMARYLANGID(uLangId);
return(_NetConnGetLocaleInfo(MAKELANGID(uLangId, SUBLANG_DEFAULT)));
}
NetPrintf(("netconnwin: failed to obtain locale info for localid (0x%04x), %s is returned.\n", (uint32_t)uLangId, LOBBYAPI_LOCALITY_UNKNOWN_STR));
// not found, zzZZ will be returned
return(LOBBYAPI_LOCALITY_UNKNOWN);
}
#if DIRTYCODE_LOGGING
/*F********************************************************************************/
/*!
\Function _NetConnVerifyLocale
\Description
Check if a _NetConn_WindowsLocaleMap entry is good by comparing with Windows API (GetLocaleInfo)'s result.
\Input *pLocale - a _NetConn_WindowsLocaleMap entry -- uint16_t[3]
\Input iAlwaysPrint - (boolean) TRUE, always prints debug trace; FALSE, prints debug trace upon error
\Output
int32_t - (boolean) TRUE=success, FALSE=failure
\Notes
For debugging purpose only.
\Version 03/25/2011 (szhu)
*/
/********************************************************************************F*/
static int32_t _NetConnVerifyLocale(uint16_t *pLocale, int32_t iAlwaysPrint)
{
int32_t iResult = FALSE;
char strLocale[LOCALE_NAME_MAX_LENGTH], strDefined[32];
char *pTemp1, *pTemp2;
uint32_t uToken;
// convert array to string like enUS
uToken = LOBBYAPI_LocalizerTokenCreate(pLocale[1], pLocale[2]);
LOBBYAPI_LocalizerTokenCreateLocalityString(strDefined, uToken);
// compare between mapping table value and Windows API GetLocaleInfo(LOCALE_SNAME) to verify whether the mapping table value is good.
ds_memclr(strLocale, sizeof(strLocale));
if (GetLocaleInfo(pLocale[0], LOCALE_SNAME, strLocale, LOCALE_NAME_MAX_LENGTH) > 0)
{
// GetLocaleInfo may return name-script-COUNTRY. If so, we remove the script: name-script-COUNTRY --> nameCOUNTRY
if ((pTemp1 = strchr(strLocale, '-')) != NULL)
{
// name-script-COUNTRY --> nameCOUNTRY
if ((pTemp2 = strchr(pTemp1 + 1, '-')) != NULL)
{
pTemp2++; // skip '-'
memmove(pTemp1, pTemp2, strlen(pTemp2) + 1); // plus '\0'
}
else // name-COUNTRY --> nameCOUNTRY
{
memmove(pTemp1, pTemp1 + 1, strlen(pTemp1 + 1) + 1);
}
}
// GetLocaleInfo may return name-COUNTRY_xxxx. If so, we remove _xxxx: nameCOUNTRY_xxxx --> nameCOUNTRY
if ((pTemp1 = strchr(strLocale, '_')) != NULL)
{
pTemp1[0] = 0;
}
// the language name should be in lowercase while the country name should be in uppercase.
// 1, enUS == enUS (perfect); 2, zzCA == xxxCA (not covered by ISO639-1); 3, xxCA == xxyCA (not covered by ISO639-1); 4, enZZ == enxxx;
if((strcmp(strDefined, strLocale) == 0)
|| (strncmp(strDefined, LOBBYAPI_LANGUAGE_UNKNOWN_STR, 2) == 0 && strncmp(strDefined + 2, strLocale + 3, 2) == 0)
|| (strncmp(strDefined, strLocale, 2) == 0 && strncmp(strDefined + 2, strLocale + 3, 2) == 0)
|| (strncmp(strDefined, strLocale, 2) == 0 && strncmp(strDefined + 2, LOBBYAPI_COUNTRY_UNKNOWN_STR, 2) == 0 && strlen(strLocale) != 4))
{
// check if _NetConnGetLocaleInfo behaves correctly
if ((uint32_t)_NetConnGetLocaleInfo(pLocale[0]) == uToken)
{
iResult = TRUE;
if (iAlwaysPrint != FALSE)
{
NetPrintf(("netconnwin: passed -- GetLocaleInfo(0x%04x) returned (%s)!\n", (uint32_t)pLocale[0], strLocale));
}
// if not perfect (enUS == enUS), print debug trace
if (strcmp(strDefined, strLocale) != 0)
{
NetPrintf(("netconnwin: notice -- GetLocaleInfo(0x%04x) returned (%s) - (%s)!\n", (uint32_t)pLocale[0], strLocale, strDefined));
}
}
}
}
if (iResult == FALSE)
{
NetPrintf(("netconnwin: ERROR -- GetLocaleInfo(0x%04x) returned (%s) - (%s)!\n", (uint32_t)pLocale[0], strLocale, strDefined));
}
return(iResult);
}
/*F********************************************************************************/
/*!
\Function _NetConnVerifyAllLocales
\Description
Verify if _NetConn_WindowsLocaleMap items are good. For debugging purpose only.
\Notes
For unknown language, zz will be returned;
For unknown country, ZZ will be returned;
For unknown locality, zzZZ will be returned.
\Version 03/25/2011 (szhu)
*/
/********************************************************************************F*/
static void _NetConnVerifyAllLocales(void)
{
uint16_t aNeutral[3];
uint32_t uIndex, uToken;
char strLocale[LOCALE_NAME_MAX_LENGTH];
for (uIndex = 0; uIndex < (sizeof(_NetConn_WindowsLocaleMap)/sizeof(_NetConn_WindowsLocaleMap[0])); uIndex++)
{
if (_NetConnVerifyLocale(_NetConn_WindowsLocaleMap[uIndex], FALSE) == FALSE)
{
NetPrintf(("netconnwin: error found in _NetConn_WindowsLocaleMap[%u] (0x%04x)!\n", uIndex, (uint32_t)_NetConn_WindowsLocaleMap[uIndex][0]));
DebugBreak();
}
// verify if langid(Primary ID, SUBLANG_NEUTRAL) works out
// exception 0x0404: 0x0404 --> ZH_TW, 0x0004 --> ZH_CN
if ((_NetConn_WindowsLocaleMap[uIndex][0] != 0x0404) && (SUBLANGID(_NetConn_WindowsLocaleMap[uIndex][0]) == SUBLANG_DEFAULT))
{
aNeutral[0] = MAKELANGID(PRIMARYLANGID(_NetConn_WindowsLocaleMap[uIndex][0]), SUBLANG_NEUTRAL);
aNeutral[1] = _NetConn_WindowsLocaleMap[uIndex][1];
aNeutral[2] = _NetConn_WindowsLocaleMap[uIndex][2];
if (_NetConnVerifyLocale(aNeutral, FALSE) == FALSE)
{
NetPrintf(("netconnwin: error found in _NetConn_WindowsLocaleMap[%u](Neutral) (0x%04x)!\n", uIndex, (uint32_t)aNeutral[0]));
DebugBreak();
}
}
}
NetPrintf(("netconnwin: checked _NetConn_WindowsLocaleMap -- %u entries in total.\n", uIndex));
// print locale information
uToken = NetConnStatus('locl', 0, NULL, 0);
NetPrintf(("netconnwin: locality returned by NetConnStatus(locl,0): (%c%c%c%c).\n", LOBBYAPI_LocalizerTokenPrintCharArray(uToken)));
// test if _NetConnGetLocaleInfo works fine when given an invalid langid
uToken = _NetConnGetLocaleInfo(0x9FFF);
NetPrintf(("netconnwin: _NetConnGetLocaleInfo(0x9FFF) returned: (%c%c%c%c) - (%s).\n", LOBBYAPI_LocalizerTokenPrintCharArray(uToken), LOBBYAPI_LOCALITY_UNKNOWN_STR));
// list locales that not covered by the mapping table
for (uIndex = 1; uIndex <= 0x8200; uIndex++)
{
if ((PRIMARYLANGID(uIndex) == LANG_NEUTRAL)
|| (uIndex == MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL))
|| (uIndex == MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)))
{
continue;
}
if ((GetLocaleInfo(uIndex, LOCALE_SNAME, strLocale, LOCALE_NAME_MAX_LENGTH) > 0)
&& (_NetConnGetLocaleInfo(uIndex) == 'zzZZ'))
{
NetPrintf(("netconnwin: not covered (0x%04x): %s!\n", uIndex, strLocale));
}
}
}
#endif //DIRTYCODE_LOGGING
/*F********************************************************************************/
/*!
\Function _NetConnIsValidMacAddress
\Description
Checks to make sure the address is valid
\Input *pAddr - The current address we are checking for validity
\Input bCheckIP - Should we check the IP Address of the NIC to make sure it is active
\Output
uint8_t - TRUE=valid, FALSE=invalid
*/
/********************************************************************************F*/
static uint8_t _NetConnIsValidMacAddress(PIP_ADAPTER_ADDRESSES pAddr, uint8_t bCheckIP)
{
PIP_ADAPTER_UNICAST_ADDRESS pUnicastAddr;
// If the address is not 6-byte long it is invalid
if (pAddr->PhysicalAddressLength != 6)
{
return(FALSE);
}
// If we are not checking the IP address then the MAC is valid
// as far as we are concerned
if (bCheckIP == FALSE)
{
return(TRUE);
}
// Make sure the address has a valid IP in multiple NIC situation
for (pUnicastAddr = pAddr->FirstUnicastAddress; pUnicastAddr != NULL; pUnicastAddr = pUnicastAddr->Next)
{
if ((pUnicastAddr->Flags & IP_ADAPTER_ADDRESS_DNS_ELIGIBLE) == IP_ADAPTER_ADDRESS_DNS_ELIGIBLE)
{
return(TRUE);
}
}
return(FALSE);
}
/*F********************************************************************************/
/*!
\Function _NetConnFindMacAddress
\Description
Scans through adapter list and finds an active adapter.
\Input *pMacAddr - user-provided buffer to be filled with MAC address
\Input iAddrSize - The size of the buffer
\Input bCheckIP - If we should check for a valid IP on the NIC
\Output
int32_t - Negative=error, zero=success
\Version 06/23/2009 (mclouatre)
*/
/********************************************************************************F*/
static int32_t _NetConnFindMacAddress(uint8_t *pMacAddr, int32_t iAddrSize, uint8_t bCheckIP)
{
NetConnRefT *pRef = _NetConn_pRef;
DWORD dwMsRetVal;
ULONG ulOutBufLen;
ULONG ulDummyBuffer;
PIP_ADAPTER_ADDRESSES pAdaptersAddrBuf;
PIP_ADAPTER_ADDRESSES pCurrAddr;
#if DIRTYCODE_LOGGING
uint32_t uByteIndex;
char strPrintBuf[32];
int32_t iCurrLen;
#endif
int32_t iRetVal = 0;
// make an initial call to GetAdaptersAddresses() to get the
// size needed into the ulOutBufLen variable
ulOutBufLen = 1; // make sure that size provided is too small to store the info.
dwMsRetVal = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, (PIP_ADAPTER_ADDRESSES) &ulDummyBuffer, &ulOutBufLen);
if (dwMsRetVal != ERROR_BUFFER_OVERFLOW)
{
// if windows does not return an overflow error, something is wrong...
NetPrintf(("netconnwin: GetAdaptersAddresses() failed with err %d when querying for output buffer size\n", dwMsRetVal));
return(-1);
}
/*
allocate a big enough buffer.
note:
* it is not a good idea to preallocate this as we will have to seriously overallocate to support rare cases
with a long list of IP_ADAPTER_ADDRESSES returned. MS doc states that Windows will "typically" not
require a buffer larger than 15 Kbytes http://msdn.microsoft.com/en-us/library/aa365915(VS.85).aspx
* it is not a good idea to allocate this buffer on the stack as it can be quite huge and could lead to a
stack overflow.
*/
if ((pAdaptersAddrBuf = DirtyMemAlloc(ulOutBufLen, NETCONN_MEMID, pRef->Common.iMemGroup, pRef->Common.pMemGroupUserData)) == NULL)
{
NetPrintf(("netconnwin: unable to allocate output buffer required to call GetAdaptersAddresses()\n"));
return(-2);
}
// make a second call to GetAdaptersAddresses() to get the actual data we want
dwMsRetVal = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, pAdaptersAddrBuf, &ulOutBufLen);
if (dwMsRetVal == NO_ERROR)
{
// loop through the adapters and match first entry with valid MAC address
pCurrAddr = pAdaptersAddrBuf;
while (pCurrAddr)
{
// make sure interface type can have a MAC address
if ((pCurrAddr->IfType == IF_TYPE_ETHERNET_CSMACD) || (pCurrAddr->IfType == IF_TYPE_IEEE80211))
{
// make sure physical address is 6-byte long and has a valid ip address; if not, it is not a MAC address
if (_NetConnIsValidMacAddress(pCurrAddr, bCheckIP) == TRUE)
{
ds_memcpy_s(pMacAddr, iAddrSize, pCurrAddr->PhysicalAddress, pCurrAddr->PhysicalAddressLength);
#if DIRTYCODE_LOGGING
// We need to use sprintf here because if we try to NetPrintf we will
// have timestamps inbetween where we are printing out the mac address
ds_memclr(strPrintBuf, sizeof(strPrintBuf));
iCurrLen = 0;
for (uByteIndex = 0; uByteIndex < (int32_t) pCurrAddr->PhysicalAddressLength; uByteIndex++)
{
if (uByteIndex == (pCurrAddr->PhysicalAddressLength - 1))
{
iCurrLen += ds_snzprintf(strPrintBuf + iCurrLen, sizeof(strPrintBuf) - iCurrLen, "%.2X", (int32_t)pCurrAddr->PhysicalAddress[uByteIndex]);
}
else
{
iCurrLen += ds_snzprintf(strPrintBuf + iCurrLen, sizeof(strPrintBuf) - iCurrLen, "%.2X-", (int32_t)pCurrAddr->PhysicalAddress[uByteIndex]);
}
}
NetPrintf(("netconnwin: valid MAC address found --> %s\n", strPrintBuf));
#endif
// valid MAC address found! exit the function with success
break;
}
}
pCurrAddr = pCurrAddr->Next;
}
if (pCurrAddr == NULL)
{
NetPrintf(("netconnwin: could not find any valid adapters to get a MAC address from\n"));
iRetVal = -3;
}
}
else
{
NetPrintf(("netconnwin: GetAdaptersAddresses() failed with err %d when querying for adapters data size\n", dwMsRetVal));
iRetVal = -4;
}
// free buffer used with GetAdaptersAddresses
DirtyMemFree(pAdaptersAddrBuf, NETCONN_MEMID, pRef->Common.iMemGroup, pRef->Common.pMemGroupUserData);
return(iRetVal);
}
/*F********************************************************************************/
/*!
\Function _NetConnShutdownInternal
\Description
Shutdown the network code and return to idle state for internal use
\Input pRef - netconn ref
\Input bStopEE - ignored in PC implementation
\Output
int32_t - negative=error, else zero
\Version 1/13/2020 (tcho)
*/
/********************************************************************************F*/
int32_t _NetConnShutdownInternal(NetConnRefT *pRef, uint32_t bStopEE)
{
int32_t iResult = 0;
// decrement and check the refcount
if ((iResult = NetConnCommonCheckRef((NetConnCommonRefT*)pRef)) < 0)
{
return(iResult);
}
if (_NetConn_pRef != NULL)
{
NetConnDisconnect();
}
// destroy the upnp ref
if (pRef->pProtoUpnp != NULL)
{
ProtoUpnpDestroy(pRef->pProtoUpnp);
pRef->pProtoUpnp = NULL;
}
// shut down protossl
ProtoSSLShutdown();
// shut down dirtycert
DirtyCertDestroy();
// shut down Idle handler
NetConnIdleShutdown();
// shut down dirtysock
SocketDestroy(0);
// common shutdown (must come last as this frees the memory)
NetConnCommonShutdown(&pRef->Common);
_NetConn_pRef = NULL;
return(0);
}
/*** Public functions *****************************************************************************/
/*F********************************************************************************/
/*!
\Function NetConnStartup
\Description
Bring the network connection module to life. Does not actually start any
network activity.
\Input *pParams - startup parameters
\Output
int32_t - zero=success, negative=failure
\Notes
NetConnRefT::iRefCount serves as a counter for the number of times
NetConnStartup has been called. This allows us to track how many modules
are using it and how many times we expect NetConnShutdown to the called.
In the past we only allowed a single call to NetConnStartup but some
libraries may need to networking without a guarentee that the game has
already started it.
pParams can contain the following terms:
\verbatim
-noupnp : disable UPNP
-servicename=<game-year-platform> : set servicename required for SSL use
-affinity=<mask as hex> : cpu affinity we set for our internal threads
\endverbatim
\Version 3/10/01 (GWS)
*/
/********************************************************************************F*/
int32_t NetConnStartup(const char *pParams)
{
NetConnRefT *pRef = _NetConn_pRef;
int32_t iResult = 0;
char strThreadCpuAffinity[16];
// allow NULL params
if (pParams == NULL)
{
pParams = "";
}
// debug display of input params
NetPrintf(("netconnwin: startup params='%s'\n", pParams));
// common startup
// pRef shall hold the address of the NetConnRefT after completion if no error occured
iResult = NetConnCommonStartup(sizeof(*pRef), pParams, (NetConnCommonRefT**)(&pRef));
// treat the result of the common startup, if already started simply early out
if (iResult == NETCONN_ERROR_ALREADY_STARTED)
{
return(0);
}
// otherwise, if an error occured report it
else if (iResult < 0)
{
return(iResult);
}
pRef->eState = ST_INIT;
pRef->iPeerPort = NETCONN_DEFAULT_UPNP_PORT;
// get the cpu affinity string from our startup params, defaulting to 0x0
ds_memclr(strThreadCpuAffinity, sizeof(strThreadCpuAffinity));
NetConnCopyParam(strThreadCpuAffinity, sizeof(strThreadCpuAffinity), "-affinity=", pParams, "0x0");
pRef->iThreadCpuAffinity = strtol(strThreadCpuAffinity, NULL, 16);
// start up dirtysock
if (SocketCreate(THREAD_PRIORITY_HIGHEST, 0, pRef->iThreadCpuAffinity) != 0)
{
_NetConnShutdownInternal(pRef, FALSE);
NetPrintf(("netconnwin: unable to start up dirtysock\n"));
return(NETCONN_ERROR_SOCKET_CREATE);
}
// create and configure dirtycert
if (NetConnDirtyCertCreate(pParams))
{
_NetConnShutdownInternal(pRef, FALSE);
NetPrintf(("netconnwin: unable to create dirtycert\n"));
return(NETCONN_ERROR_DIRTYCERT_CREATE);
}
// start up protossl
if (ProtoSSLStartup() < 0)
{
_NetConnShutdownInternal(pRef, FALSE);
NetPrintf(("netconnwin: unable to start up protossl\n"));
return(NETCONN_ERROR_PROTOSSL_CREATE);
}
if (strstr(pParams, "-noupnp") == NULL)
{
pRef->pProtoUpnp = ProtoUpnpCreate();
if (pRef->pProtoUpnp == NULL)
{
_NetConnShutdownInternal(pRef, FALSE);
NetPrintf(("netconnwin: unable to start up protoupnp\n"));
return(NETCONN_ERROR_PROTOUPNP_CREATE);
}
}
// save ref
_NetConn_pRef = pRef;
#if DIRTYCODE_LOGGING
// if we made changes to the mapping table (_NetConn_WindowsLocaleMap),
// uncomment the following line and build-debug-run to verify if the changes are good.
//_NetConnVerifyAllLocales();
#endif
return(0);
}
/*F*************************************************************************************************/
/*!
\Function NetConnQuery
\Description
Query the list of available connection configurations. This list is loaded
from the specified device. The list is returned in a simple fixed width
array with one string per array element. The caller can find the user portion
of the config name via strchr(item, '#')+1.
\Input *pDevice - device to scan (mc0:, mc1:, pfs0:, pfs1:)
\Input *pList - buffer to store output array in
\Input iSize - length of buffer in bytes
\Output
int32_t - negative=error, else number of configurations
\Version 01/18/02 (GWS)
*/
/*************************************************************************************************F*/
int32_t NetConnQuery(const char *pDevice, NetConfigRecT *pList, int32_t iSize)
{
return(0);
}
/*F*************************************************************************************************/
/*!
\Function NetConnConnect
\Description
Used to bring the networking online with a specific configuration. Uses the
configuration returned by NetConnQuery.
\Input *pConfig - the configuration entry from NetConnQuery
\Input *pOption - asciiz list of config parameters
"peerport=<port>" to specify peer port to be opened by upnp.
\Input iData - platform-specific
\Output
int32_t - negative=error, zero=success
\Version 01/18/02 (GWS)
*/
/*************************************************************************************************F*/
int32_t NetConnConnect(const NetConfigRecT *pConfig, const char *pOption, int32_t iData)
{
NetConnRefT *pRef = _NetConn_pRef;
// check connection options, if present
if (pOption != NULL)
{
const char *pOpt;
// check for specification of peer port
if ((pOpt = strstr(pOption, "peerport=")) != NULL)
{
pRef->iPeerPort = strtol(pOpt+9, NULL, 10);
}
}
NetPrintf(("netconnwin: upnp peerport=%d %s\n",
pRef->iPeerPort, (pRef->iPeerPort == NETCONN_DEFAULT_UPNP_PORT ? "(default)" : "(selected via netconnconnect param)")));
// make sure we aren't already connected
if (pRef->eState == ST_INIT)
{
// connecting
pRef->eState = ST_CONN;
// discover upnp router information
if (pRef->pProtoUpnp != NULL)
{
if (pRef->iPeerPort != 0)
{
ProtoUpnpControl(pRef->pProtoUpnp, 'port', pRef->iPeerPort, 0, NULL);
ProtoUpnpControl(pRef->pProtoUpnp, 'macr', 'upnp', 0, NULL);
}
else
{
ProtoUpnpControl(pRef->pProtoUpnp, 'macr', 'dscg', 0, NULL);
}
}
}
else
{
NetPrintf(("netconnwin: NetConnConnect() ignored because already connected!\n"));
}
return(0);
}
/*F*************************************************************************************************/
/*!
\Function NetConnDisconnect
\Description
Used to bring down the network connection. After calling this, it would
be necessary to call NetConnConnect to bring the connection back up or
NetConnShutdown to completely shutdown all network support.
\Output
int32_t - negative=error, zero=success
\Version 02/09/02 (GWS)
*/
/*************************************************************************************************F*/
int32_t NetConnDisconnect(void)
{
NetConnRefT *pRef = _NetConn_pRef;
if (pRef->eState == ST_CONN)
{
// shutdown the interfaces (but leave API running)
pRef->eState = ST_INIT;
}
// abort upnp operations
if (pRef->pProtoUpnp != NULL)
{
ProtoUpnpControl(pRef->pProtoUpnp, 'abrt', 0, 0, NULL);
}
return(0);
}
/*F********************************************************************************/
/*!
\Function NetConnControl
\Description
Set module behavior based on input selector.
\Input iControl - input selector
\Input iValue - selector input
\Input iValue2 - selector input
\Input *pValue - selector input
\Input *pValue2 - selector input
\Output
int32_t - selector result
\Notes
iControl can be one of the following:
\verbatim
snam: set DirtyCert service name
\endverbatim
Unhandled selectors are passed through to NetConnCommonControl()
\Version 04/27/2006 (jbrookes)
*/
/********************************************************************************F*/
int32_t NetConnControl(int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue, void *pValue2)
{
NetConnRefT *pRef = _NetConn_pRef;
// make sure module is started before allowing any other control calls
if (pRef == NULL)
{
NetPrintf(("netconnwin: warning - calling NetConnControl() while module is not initialized\n"));
return(-1);
}
// set dirtycert service name
if (iControl == 'snam')
{
return(DirtyCertControl('snam', 0, 0, pValue));
}
// pass through unhandled selectors to NetConnCommon
return(NetConnCommonControl(&pRef->Common, iControl, iValue, iValue2, pValue, pValue2));
}
/*F*************************************************************************************************/
/*!
\Function NetConnStatus
\Description
Check general network connection status. Different selectors return
different status attributes.
\Input iKind - status selector
\Input iData - (optional) selector specific
\Input *pBuf - (optional) pointer to output buffer
\Input iBufSize - (optional) size of output buffer
\Output
int32_t - selector specific
\Notes
iKind can be one of the following:
\verbatim
addr: ip address of client
affn: get the thread cpu affinity setting
bbnd: broadband true or false
conn: true/false indication of whether connection in progress
ethr: mac address of adapter based on iData (0=first, otherwise=first active) (returned in pBuf), 0=success, negative=error
locl: return locality (Windows locale) for local system, ex. 'enUS'
For unrecognized locale, 'zzZZ' will be returned.
locn: return location (Windows location) for local system, ex. 'zzCA'
The language part of the return value should be ignored by the caller.
If local system has 'no location' set, 'zzZZ' will be returned.
macx: mac address of adapter based on iData (0=first, otherwise=first active) (returned in pBuf), 0=success, negative=error
onln: true/false indication of whether network is operational
open: true/false indication of whether network code running
type: NETCONN_IFTYPE_*
upnp: return protoupnp port info, if available
vers: return DirtySDK version
\endverbatim
Unhandled selectors are passed through to NetConnCommonStatus()
\Version 03/10/2001 (gschaefer)
*/
/*************************************************************************************************F*/
int32_t NetConnStatus(int32_t iKind, int32_t iData, void *pBuf, int32_t iBufSize)
{
NetConnRefT *pRef = _NetConn_pRef;
// see if network code is initialized
if (iKind == 'open')
{
return(pRef != NULL);
}
// return DirtySDK version
if (iKind == 'vers')
{
return(DIRTYSDK_VERSION);
}
// make sure module is started before allowing any other status calls
if (pRef == NULL)
{
NetPrintf(("netconn: warning - calling NetConnStatus() while module is not initialized\n"));
return(-1);
}
// return client ip address
if (iKind == 'addr')
{
return(SocketGetLocalAddr());
}
// return the thread cpu affinity
if (iKind == 'affn')
{
return(pRef->iThreadCpuAffinity);
}
// return whether we are broadband or not
if (iKind == 'bbnd')
{
// assume broadband
return(TRUE);
}
// return connection status
if (iKind == 'conn')
{
return((pRef->eState == ST_CONN) ? '+onl' : '-dsc');
}
// return host mac address
if ((iKind == 'ethr') || (iKind == 'macx'))
{
if (pBuf != NULL)
{
// clear out the buffer
ds_memclr(pBuf, iBufSize);
// find a valid MAC address
return(_NetConnFindMacAddress(pBuf, iBufSize, iData == 0 ? FALSE : TRUE));
}
}
// see if connected to ISP/LAN
if (iKind == 'onln')
{
return(pRef->eState == ST_CONN);
}
// return what type of interface we are connected with
if (iKind == 'type')
{
// assume broadband
return(NETCONN_IFTYPE_ETHER);
}
// return upnp addportmap info, if available
if (iKind == 'upnp')
{
// if protoupnp is available, and we've added a port map, return the external port for the port mapping
if ((pRef->pProtoUpnp != NULL) && (ProtoUpnpStatus(pRef->pProtoUpnp, 'stat', NULL, 0) & PROTOUPNP_STATUS_ADDPORTMAP))
{
return(ProtoUpnpStatus(pRef->pProtoUpnp, 'extp', NULL, 0));
}
}
// return Windows locale information
if (iKind == 'locl')
{
return(_NetConnGetLocaleInfo(GetUserDefaultLangID()));
}
// return Windows location information
if (iKind == 'locn')
{
GEOID iGeoID;
char strCountry[8]; // ISO2, [8] is enough
uint16_t uLanguage = LOBBYAPI_LANGUAGE_UNKNOWN, // language is always set to zz
uCountry = LOBBYAPI_COUNTRY_UNKNOWN; // default to ZZ
// check if local system has location set
if ((iGeoID = GetUserGeoID(GEOCLASS_NATION)) == GEOID_NOT_AVAILABLE)
{
return(LOBBYAPI_LOCALITY_UNKNOWN);
}
ds_memclr(strCountry, sizeof(strCountry));
if ((GetGeoInfoA(iGeoID, GEO_ISO2, strCountry, sizeof(strCountry), 0) > 0)
&& (strlen(strCountry) == sizeof(uint16_t))
&& (ds_stricmp(strCountry, NETCONNWIN_COUNTRY_UNKNOWN_STR) != 0))
{
// a valid country code: 2-letter, and not equal to XX
uCountry = LOBBYAPI_LocalizerTokenGetShortFromString(strCountry);
}
return(LOBBYAPI_LocalizerTokenCreate(uLanguage, uCountry));
}
// pass unrecognized options to NetConnCommon
return(NetConnCommonStatus(&pRef->Common, iKind, iData, pBuf, iBufSize));
}
/*F*************************************************************************************************/
/*!
\Function NetConnShutdown
\Description
Shutdown the network code and return to idle state.
\Input bStopEE - ignored in PC implementation
\Output
negative=error, zero=success
\Version 3/10/01 (GWS)
*/
/*************************************************************************************************F*/
int32_t NetConnShutdown(uint32_t bStopEE)
{
NetConnRefT *pRef = _NetConn_pRef;
// error see if already stopped
if (pRef == NULL)
{
return(NETCONN_ERROR_NOTACTIVE);
}
return(_NetConnShutdownInternal(pRef, bStopEE));
}
/*F*************************************************************************************************/
/*!
\Function NetConnSleep
\Description
Sleep the application (yield thread) for some number of milliseconds.
\Input iMilliSecs - number of milliseconds to block for
\Output
None
\Version 04/30/02 (GWS)
*/
/*************************************************************************************************F*/
void NetConnSleep(int32_t iMilliSecs)
{
Sleep(iMilliSecs);
}