security fix: replaced sprintf with snprintf
[xboard.git] / winboard / winboard.c
index aa13836..b535d44 100644 (file)
@@ -5,7 +5,7 @@
  * Massachusetts. \r
  *\r
  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006,\r
- * 2007, 2008, 2009 Free Software Foundation, Inc.\r
+ * 2007, 2008, 2009, 2010 Free Software Foundation, Inc.\r
  *\r
  * Enhancements Copyright 2005 Alessandro Scotti\r
  *\r
@@ -108,7 +108,7 @@ int FinishMove P((ChessMove moveType, int fromX, int fromY, int toX, int toY,
 void AnimateAtomicCapture(int fromX, int fromY, int toX, int toY, int nFrames);\r
 void DisplayMove P((int moveNumber));\r
 Boolean ParseFEN P((Board board, int *blackPlaysFirst, char *fen));\r
-void ChatPopUp P(());\r
+void ChatPopUp P((char *s));\r
 typedef struct {\r
   ChessSquare piece;  \r
   POINT pos;      /* window coordinates of current pos */\r
@@ -134,6 +134,8 @@ typedef struct {
 \r
 static HighlightInfo highlightInfo        = { {{-1, -1}, {-1, -1}} };\r
 static HighlightInfo premoveHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
+static HighlightInfo partnerHighlightInfo = { {{-1, -1}, {-1, -1}} };\r
+static HighlightInfo oldPartnerHighlight  = { {{-1, -1}, {-1, -1}} };\r
 \r
 typedef struct { // [HGM] atomic\r
   int fromX, fromY, toX, toY, radius;\r
@@ -159,7 +161,7 @@ BoardSize boardSize;
 Boolean chessProgram;\r
 //static int boardX, boardY;\r
 int  minX, minY; // [HGM] placement: volatile limits on upper-left corner\r
-static int squareSize, lineGap, minorSize;\r
+int squareSize, lineGap, minorSize;\r
 static int winW, winH;\r
 static RECT messageRect, whiteRect, blackRect, leftLogoRect, rightLogoRect; // [HGM] logo\r
 static int logoHeight = 0;\r
@@ -223,6 +225,253 @@ static struct { int x; int y; int mode; } backTextureSquareInfo[BOARD_RANKS][BOA
 #endif\r
 #endif\r
 \r
+#define INTERNATIONAL\r
+\r
+#ifdef INTERNATIONAL\r
+#  define _(s) T_(s)\r
+#  define N_(s) s\r
+#else\r
+#  define _(s) s\r
+#  define N_(s) s\r
+#  define T_(s) s\r
+#  define Translate(x, y)\r
+#  define LoadLanguageFile(s)\r
+#endif\r
+\r
+#ifdef INTERNATIONAL\r
+\r
+Boolean barbaric; // flag indicating if translation is needed\r
+\r
+// list of item numbers used in each dialog (used to alter language at run time)\r
+\r
+#define ABOUTBOX -1  /* not sure why these are needed */\r
+#define ABOUTBOX2 -1\r
+\r
+int dialogItems[][40] = {\r
+{ ABOUTBOX, IDOK, 400 }, \r
+{ DLG_TimeControl, IDC_Babble, OPT_TCUseMoves, OPT_TCUseInc, OPT_TCUseFixed, \r
+  OPT_TCtext1, OPT_TCtext2, OPT_TCitext1, OPT_TCitext2, OPT_TCftext, GPB_Factors,   IDC_Factor1, IDC_Factor2, IDOK, IDCANCEL }, \r
+{ DLG_LoadOptions, OPT_Autostep, OPT_AStext1, IDOK, IDCANCEL }, \r
+{ DLG_SaveOptions, OPT_Autosave, OPT_AVPrompt, OPT_AVToFile, OPT_AVBrowse,\r
+  801, OPT_PGN, OPT_Old, OPT_OutOfBookInfo, IDOK, IDCANCEL }, \r
+{ 1536, 1090, IDC_Directories, 1089, 1091, IDOK, IDCANCEL, 1038, IDC_IndexNr, 1037 }, \r
+{ DLG_CommPort, IDOK, IDCANCEL, IDC_Port, IDC_Rate, IDC_Bits, IDC_Parity,\r
+  IDC_Stop, IDC_Flow, OPT_SerialHelp }, \r
+{ DLG_EditComment, IDOK, OPT_CancelComment, OPT_ClearComment, OPT_EditComment }, \r
+{ DLG_PromotionKing, PB_Chancellor, PB_Archbishop, PB_Queen, PB_Rook, \r
+  PB_Bishop, PB_Knight, PB_King, IDCANCEL, IDC_Yes, IDC_No, IDC_Centaur }, \r
+{ ABOUTBOX2, IDC_ChessBoard }, \r
+{ DLG_GameList, OPT_GameListLoad, OPT_GameListPrev, OPT_GameListNext, \r
+  OPT_GameListClose, IDC_GameListDoFilter }, \r
+{ DLG_EditTags, IDOK, OPT_TagsCancel, OPT_EditTags }, \r
+{ DLG_Error, IDOK }, \r
+{ DLG_Colorize, IDOK, IDCANCEL, OPT_ChooseColor, OPT_Bold, OPT_Italic,\r
+  OPT_Underline, OPT_Strikeout, OPT_Sample }, \r
+{ DLG_Question, IDOK, IDCANCEL, OPT_QuestionText }, \r
+{ DLG_Startup, IDC_Welcome, OPT_ChessEngine, OPT_ChessServer, OPT_View,\r
+  IDC_SPECIFY_ENG_STATIC, IDC_SPECIFY_SERVER_STATIC, OPT_AnyAdditional,\r
+  IDOK, IDCANCEL, IDM_HELPCONTENTS }, \r
+{ DLG_IndexNumber, IDC_Index }, \r
+{ DLG_TypeInMove, IDOK, IDCANCEL }, \r
+{ DLG_TypeInName, IDOK, IDCANCEL }, \r
+{ DLG_Sound, IDC_Event, OPT_NoSound, OPT_DefaultBeep, OPT_BuiltInSound,\r
+  OPT_WavFile, OPT_BrowseSound, OPT_DefaultSounds, IDOK, IDCANCEL, OPT_PlaySound }, \r
+{ DLG_GeneralOptions, IDOK, IDCANCEL, OPT_AlwaysOnTop, OPT_HighlightLastMove,\r
+  OPT_AlwaysQueen, OPT_PeriodicUpdates, OPT_AnimateDragging, OPT_PonderNextMove,\r
+  OPT_AnimateMoving, OPT_PopupExitMessage, OPT_AutoFlag, OPT_PopupMoveErrors,\r
+  OPT_AutoFlipView, OPT_ShowButtonBar, OPT_AutoRaiseBoard, OPT_ShowCoordinates,\r
+  OPT_Blindfold, OPT_ShowThinking, OPT_HighlightDragging, OPT_TestLegality,\r
+  OPT_SaveExtPGN, OPT_HideThinkFromHuman, OPT_ExtraInfoInMoveHistory,\r
+  OPT_HighlightMoveArrow, OPT_AutoLogo }, \r
+{ DLG_IcsOptions, IDOK, IDCANCEL, OPT_AutoComment, OPT_AutoKibitz, OPT_AutoObserve,\r
+  OPT_GetMoveList, OPT_LocalLineEditing, OPT_QuietPlay, OPT_SeekGraph, OPT_AutoRefresh,\r
+  OPT_BgObserve, OPT_DualBoard, OPT_Premove, OPT_PremoveWhite, OPT_PremoveBlack,\r
+  OPT_SmartMove, OPT_IcsAlarm, IDC_Sec, OPT_ChooseShoutColor, OPT_ChooseSShoutColor,\r
+  OPT_ChooseChannel1Color, OPT_ChooseChannelColor, OPT_ChooseKibitzColor,\r
+  OPT_ChooseTellColor, OPT_ChooseChallengeColor, OPT_ChooseRequestColor,\r
+  OPT_ChooseSeekColor, OPT_ChooseNormalColor, OPT_ChooseBackgroundColor,\r
+  OPT_DefaultColors, OPT_DontColorize, IDC_Boxes, GPB_Colors, GPB_Premove,\r
+  GPB_General, GPB_Alarm }, \r
+{ DLG_BoardOptions, IDOK, IDCANCEL, OPT_SizeTiny, OPT_SizeTeeny, OPT_SizeDinky,\r
+  OPT_SizePetite, OPT_SizeSlim, OPT_SizeSmall, OPT_SizeMediocre, OPT_SizeMiddling,\r
+  OPT_SizeAverage, OPT_SizeModerate, OPT_SizeMedium, OPT_SizeBulky, OPT_SizeLarge,\r
+  OPT_SizeBig, OPT_SizeHuge, OPT_SizeGiant, OPT_SizeColossal, OPT_SizeTitanic,\r
+  OPT_ChooseLightSquareColor, OPT_ChooseDarkSquareColor, OPT_ChooseWhitePieceColor,\r
+  OPT_ChooseBlackPieceColor, OPT_ChooseHighlightSquareColor, OPT_ChoosePremoveHighlightColor,\r
+  OPT_Monochrome, OPT_AllWhite, OPT_UpsideDown, OPT_DefaultBoardColors, GPB_Colors,\r
+  IDC_Light, IDC_Dark, IDC_White, IDC_Black, IDC_High, IDC_PreHigh, GPB_Size }, \r
+{ DLG_NewVariant, IDOK, IDCANCEL, OPT_VariantNormal, OPT_VariantFRC, OPT_VariantWildcastle,\r
+  OPT_VariantNocastle, OPT_VariantLosers, OPT_VariantGiveaway, OPT_VariantSuicide,\r
+  OPT_Variant3Check, OPT_VariantTwoKings, OPT_VariantAtomic, OPT_VariantCrazyhouse,\r
+  OPT_VariantBughouse, OPT_VariantTwilight, OPT_VariantShogi, OPT_VariantSuper,\r
+  OPT_VariantKnightmate, OPT_VariantBerolina, OPT_VariantCylinder, OPT_VariantFairy,\r
+  OPT_VariantMakruk, OPT_VariantGothic, OPT_VariantCapablanca, OPT_VariantJanus,\r
+  OPT_VariantCRC, OPT_VariantFalcon, OPT_VariantCourier, OPT_VariantGreat,\r
+  OPT_VariantShatranj, OPT_VariantXiangqi, GPB_Variant, GPB_Board, IDC_Height,\r
+  IDC_Width, IDC_Hand, IDC_Pieces, IDC_Def }, \r
+{ DLG_Fonts, IDOK, IDCANCEL, OPT_ChooseClockFont, OPT_ChooseMessageFont,\r
+  OPT_ChooseCoordFont, OPT_ChooseTagFont, OPT_ChooseCommentsFont,  OPT_ChooseConsoleFont, OPT_ChooseMoveHistoryFont, OPT_DefaultFonts,\r
+  OPT_ClockFont, OPT_MessageFont, OPT_CoordFont, OPT_EditTagsFont,\r
+  OPT_CommentsFont, OPT_MessageFont5, GPB_Current, GPB_All, OPT_MessageFont6 }, \r
+{ DLG_NewGameFRC, IDC_NFG_Label, IDC_NFG_Random, IDOK, IDCANCEL }, \r
+{ DLG_GameListOptions, IDC_GLT, IDC_GLT_Up, IDC_GLT_Down, IDC_GLT_Restore,\r
+  IDC_GLT_Default, IDOK, IDCANCEL, IDC_GLT_RestoreTo }, \r
+{ DLG_MoveHistory }, \r
+{ DLG_EvalGraph }, \r
+{ DLG_EngineOutput, IDC_EngineLabel1, IDC_Engine1_NPS, IDC_EngineLabel2, IDC_Engine2_NPS }, \r
+{ DLG_Chat, IDC_Partner, IDC_Clear, IDC_Send,  }, \r
+{ DLG_EnginePlayOptions, IDC_EpPonder, IDC_EpShowThinking, IDC_EpHideThinkingHuman,\r
+  IDC_EpPeriodicUpdates, GPB_Adjudications, IDC_Draw, IDC_Moves, IDC_Threshold,\r
+  IDC_Centi, IDC_TestClaims, IDC_DetectMates, IDC_MaterialDraws, IDC_TrivialDraws,\r
+  GPB_Apply, IDC_Rule, IDC_Repeats, IDC_ScoreAbs1, IDC_ScoreAbs2, IDOK, IDCANCEL }, \r
+{ DLG_OptionsUCI, IDC_PolyDir, IDC_BrowseForPolyglotDir, IDC_Hash, IDC_Path,\r
+  IDC_BrowseForEGTB, IDC_Cache, IDC_UseBook, IDC_BrowseForBook, IDC_CPU, IDC_OwnBook1,\r
+  IDC_OwnBook2, IDC_Depth, IDC_Variation, IDC_DefGames, IDOK, IDCANCEL },\r
+{ 0 }\r
+};\r
+\r
+static char languageBuf[50000], *foreign[1000], *english[1000], *languageFile[MSG_SIZ];\r
+static int lastChecked;\r
+static char oldLanguage[MSG_SIZ], *menuText[10][25];\r
+extern int tinyLayout;\r
+extern char * menuBarText[][8];\r
+\r
+void\r
+LoadLanguageFile(char *name)\r
+{   //load the file with translations, and make a list of the strings to be translated, and their translations\r
+    FILE *f;\r
+    int i=0, j=0, n=0, k;\r
+    char buf[MSG_SIZ];\r
+\r
+    if(!name || name[0] == NULLCHAR) return;\r
+      snprintf(buf, MSG_SIZ, "%s%s", name, strchr(name, '.') ? "" : ".lng"); // auto-append lng extension\r
+    if(!strcmp(buf, oldLanguage)) { barbaric = 1; return; } // this language already loaded; just switch on\r
+    if((f = fopen(buf, "r")) == NULL) return;\r
+    while((k = fgetc(f)) != EOF) {\r
+        if(i >= sizeof(languageBuf)) { DisplayError("Language file too big", 0); return; }\r
+        languageBuf[i] = k;\r
+        if(k == '\n') {\r
+            if(languageBuf[n] == '"' && languageBuf[i-1] == '"') {\r
+                char *p;\r
+                if(p = strstr(languageBuf + n + 1, "\" === \"")) {\r
+                    if(p > languageBuf+n+2 && p+8 < languageBuf+i) {\r
+                        if(j >= sizeof(english)) { DisplayError("Too many translated strings", 0); return; }\r
+                        english[j] = languageBuf + n + 1; *p = 0;\r
+                        foreign[j++] = p + 7; languageBuf[i-1] = 0;\r
+//if(appData.debugMode) fprintf(debugFP, "translation: replace '%s' by '%s'\n", english[j-1], foreign[j-1]);\r
+                    }\r
+                }\r
+            }\r
+            n = i + 1;\r
+        } else if(i > 0 && languageBuf[i-1] == '\\') {\r
+            switch(k) {\r
+              case 'n': k = '\n'; break;\r
+              case 'r': k = '\r'; break;\r
+              case 't': k = '\t'; break;\r
+            }\r
+            languageBuf[--i] = k;\r
+        }\r
+        i++;\r
+    }\r
+    fclose(f);\r
+    barbaric = (j != 0);\r
+    safeStrCpy(oldLanguage, buf, sizeof(oldLanguage)/sizeof(oldLanguage[0]) );\r
+}\r
+\r
+char *\r
+T_(char *s)\r
+{   // return the translation of the given string\r
+    // efficiency can be improved a lot...\r
+    int i=0;\r
+if(appData.debugMode) fprintf(debugFP, "T_(%s)\n", s);\r
+    if(!barbaric) return s;\r
+    if(!s) return ""; // sanity\r
+    while(english[i]) {\r
+        if(!strcmp(s, english[i])) return foreign[i];\r
+        i++;\r
+    }\r
+    return s;\r
+}\r
+\r
+void\r
+Translate(HWND hDlg, int dialogID)\r
+{   // translate all text items in the given dialog\r
+    int i=0, j, k;\r
+    char buf[MSG_SIZ], *s;\r
+//if(appData.debugMode) fprintf(debugFP, "Translate(%d)\n", dialogID);\r
+    if(!barbaric) return;\r
+    while(dialogItems[i][0] && dialogItems[i][0] != dialogID) i++; // find the dialog description\r
+    if(dialogItems[i][0] != dialogID) return; // unknown dialog, should not happen\r
+    GetWindowText( hDlg, buf, MSG_SIZ );\r
+    s = T_(buf);\r
+if(appData.debugMode) fprintf(debugFP, "WindowText '%s' -> '%s'\n", buf, s);\r
+    if(strcmp(buf, s)) SetWindowText(hDlg, s); // replace by translated string (if different)\r
+    for(j=1; k=dialogItems[i][j]; j++) { // translate all listed dialog items\r
+        GetDlgItemText(hDlg, k, buf, MSG_SIZ);\r
+        if(strlen(buf) == 0) continue;\r
+        s = T_(buf);\r
+        if(strcmp(buf, s)) SetDlgItemText(hDlg, k, s); // replace by translated string (if different)\r
+    }\r
+}\r
+\r
+void\r
+TranslateMenus(int addLanguage)\r
+{\r
+    int i, j;\r
+    WIN32_FIND_DATA fileData;\r
+    HANDLE hFind;\r
+#define IDM_English 1895\r
+    if(1) {\r
+        HMENU mainMenu = GetMenu(hwndMain);\r
+        for (i=GetMenuItemCount(mainMenu)-1; i>=0; i--) {\r
+          HMENU subMenu = GetSubMenu(mainMenu, i);\r
+          ModifyMenu(mainMenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP|EnableMenuItem(mainMenu, i, MF_BYPOSITION),\r
+                                                                  (UINT) subMenu, T_(menuBarText[tinyLayout][i]));\r
+          for(j=GetMenuItemCount(subMenu)-1; j>=0; j--){\r
+            char buf[MSG_SIZ];\r
+            UINT k = GetMenuItemID(subMenu, j);\r
+             if(menuText[i][j])
+               safeStrCpy(buf, menuText[i][j], sizeof(buf)/sizeof(buf[0]) ); else {\r
+                GetMenuString(subMenu, j, buf, MSG_SIZ, MF_BYPOSITION);\r
+                menuText[i][j] = strdup(buf); // remember original on first change\r
+            }\r
+            if(buf[0] == NULLCHAR) continue;\r
+//fprintf(debugFP, "menu(%d,%d) = %s (%08x, %08x) %d\n", i, j, buf, mainMenu, subMenu, k);\r
+            ModifyMenu(subMenu, j, MF_STRING|MF_BYPOSITION\r
+                                   |CheckMenuItem(subMenu, j, MF_BYPOSITION)\r
+                                   |EnableMenuItem(subMenu, j, MF_BYPOSITION), k, T_(buf));\r
+          }\r
+        }\r
+        DrawMenuBar(hwndMain);\r
+    }\r
+\r
+    if(!addLanguage) return;\r
+    if((hFind = FindFirstFile("*.LNG", &fileData)) != INVALID_HANDLE_VALUE) {\r
+        HMENU mainMenu = GetMenu(hwndMain);\r
+        HMENU subMenu = GetSubMenu(mainMenu, GetMenuItemCount(mainMenu)-1);\r
+        AppendMenu(subMenu, MF_SEPARATOR, (UINT_PTR) 0, NULL);\r
+        AppendMenu(subMenu, MF_ENABLED|MF_STRING|(barbaric?MF_UNCHECKED:MF_CHECKED), (UINT_PTR) IDM_English, (LPCTSTR) "English");\r
+        i = 0; lastChecked = IDM_English;\r
+        do {\r
+            char *p, *q = fileData.cFileName;\r
+            int checkFlag = MF_UNCHECKED;\r
+            languageFile[i] = strdup(q);\r
+            if(barbaric && !strcmp(oldLanguage, q)) {\r
+                checkFlag = MF_CHECKED;\r
+                lastChecked = IDM_English + i + 1;\r
+                CheckMenuItem(mainMenu, IDM_English, MF_BYCOMMAND|MF_UNCHECKED);\r
+            }\r
+            *q = ToUpper(*q); while(*++q) *q = ToLower(*q);\r
+            p = strstr(fileData.cFileName, ".lng");\r
+            if(p) *p = 0;\r
+            AppendMenu(subMenu, MF_ENABLED|MF_STRING|checkFlag, (UINT_PTR) IDM_English + ++i, (LPCTSTR) fileData.cFileName);\r
+        } while(FindNextFile(hFind, &fileData));\r
+        FindClose(hFind);\r
+    }\r
+}\r
+\r
+#endif\r
+\r
 typedef struct {\r
   char *name;\r
   int squareSize;\r
@@ -302,8 +551,8 @@ MyButtonDesc buttonDesc[N_BUTTONS] =
 int tinyLayout = 0, smallLayout = 0;\r
 #define MENU_BAR_ITEMS 7\r
 char *menuBarText[2][MENU_BAR_ITEMS+1] = {\r
-  { "&File", "&Mode", "&Action", "&Step", "&Options", "&Help", NULL },\r
-  { "&F", "&M", "&A", "&S", "&O", "&H", NULL },\r
+  { N_("&File"), N_("&Mode"), N_("&Action"), N_("&Step"), N_("&Options"), N_("&Help"), NULL },\r
+  { N_("&F"), N_("&M"), N_("&A"), N_("&S"), N_("&O"), N_("&H"), NULL },\r
 };\r
 \r
 \r
@@ -311,17 +560,17 @@ MySound sounds[(int)NSoundClasses];
 MyTextAttribs textAttribs[(int)NColorClasses];\r
 \r
 MyColorizeAttribs colorizeAttribs[] = {\r
-  { (COLORREF)0, 0, "Shout Text" },\r
-  { (COLORREF)0, 0, "SShout/CShout" },\r
-  { (COLORREF)0, 0, "Channel 1 Text" },\r
-  { (COLORREF)0, 0, "Channel Text" },\r
-  { (COLORREF)0, 0, "Kibitz Text" },\r
-  { (COLORREF)0, 0, "Tell Text" },\r
-  { (COLORREF)0, 0, "Challenge Text" },\r
-  { (COLORREF)0, 0, "Request Text" },\r
-  { (COLORREF)0, 0, "Seek Text" },\r
-  { (COLORREF)0, 0, "Normal Text" },\r
-  { (COLORREF)0, 0, "None" }\r
+  { (COLORREF)0, 0, N_("Shout Text") },\r
+  { (COLORREF)0, 0, N_("SShout/CShout") },\r
+  { (COLORREF)0, 0, N_("Channel 1 Text") },\r
+  { (COLORREF)0, 0, N_("Channel Text") },\r
+  { (COLORREF)0, 0, N_("Kibitz Text") },\r
+  { (COLORREF)0, 0, N_("Tell Text") },\r
+  { (COLORREF)0, 0, N_("Challenge Text") },\r
+  { (COLORREF)0, 0, N_("Request Text") },\r
+  { (COLORREF)0, 0, N_("Seek Text") },\r
+  { (COLORREF)0, 0, N_("Normal Text") },\r
+  { (COLORREF)0, 0, N_("None") }\r
 };\r
 \r
 \r
@@ -632,9 +881,9 @@ SetUserLogo()
     if(appData.autoLogo) {\r
          curName = UserName();\r
          if(strcmp(curName, oldUserName)) {\r
-               sprintf(oldUserName, "logos\\%s.bmp", curName);\r
+           snprintf(oldUserName, MSG_SIZ, "logos\\%s.bmp", curName);\r
                userLogo = LoadImage( 0, oldUserName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );    \r
-               strcpy(oldUserName, curName);\r
+               safeStrCpy(oldUserName, curName, sizeof(oldUserName)/sizeof(oldUserName[0]) );\r
          }\r
     }\r
 }\r
@@ -726,6 +975,8 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
     setbuf(debugFP, NULL);\r
   }\r
 \r
+  LoadLanguageFile(appData.language);\r
+\r
   InitBackEnd1();\r
 \r
 //  InitEngineUCI( installDir, &first ); // [HGM] incorporated in InitBackEnd1()\r
@@ -753,7 +1004,7 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
   } else if(appData.autoLogo) {\r
       if(appData.firstDirectory && appData.firstDirectory[0]) {\r
        char buf[MSG_SIZ];\r
-       sprintf(buf, "%s/logo.bmp", appData.firstDirectory);\r
+         snprintf(buf, MSG_SIZ, "%s/logo.bmp", appData.firstDirectory);\r
        first.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );   \r
       }\r
   }\r
@@ -767,11 +1018,11 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
   } else if(appData.autoLogo) {\r
       char buf[MSG_SIZ];\r
       if(appData.icsActive) { // [HGM] logo: in ICS mode second can be used for ICS\r
-       sprintf(buf, "logos\\%s.bmp", appData.icsHost);\r
+       snprintf(buf, MSG_SIZ, "logos\\%s.bmp", appData.icsHost);\r
        second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );\r
       } else\r
       if(appData.secondDirectory && appData.secondDirectory[0]) {\r
-       sprintf(buf, "%s\\logo.bmp", appData.secondDirectory);\r
+       snprintf(buf, MSG_SIZ, "%s\\logo.bmp", appData.secondDirectory);\r
        second.programLogo = LoadImage( 0, buf, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );  \r
       }\r
   }\r
@@ -797,6 +1048,7 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
   }\r
 \r
   InitDrawingSizes(boardSize, 0);\r
+  TranslateMenus(1);\r
   InitMenuChecks();\r
   buttonCount = GetSystemMetrics(SM_CMOUSEBUTTONS);\r
 \r
@@ -836,8 +1088,6 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
       EngineOutputPopUp();\r
   }\r
 \r
-  InitBackEnd2();\r
-\r
   /* Make the window visible; update its client area; and return "success" */\r
   EnsureOnScreen(&wpMain.x, &wpMain.y, minX, minY);\r
   wp.length = sizeof(WINDOWPLACEMENT);\r
@@ -850,6 +1100,8 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
   wp.rcNormalPosition.bottom = wpMain.y + wpMain.height;\r
   SetWindowPlacement(hwndMain, &wp);\r
 \r
+  InitBackEnd2(); // [HGM] moved until after all windows placed, to save correct position if fatal error on engine start\r
+\r
   if(!appData.noGUI) SetWindowPos(hwndMain, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,\r
                0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
 \r
@@ -859,6 +1111,16 @@ InitInstance(HINSTANCE hInstance, int nCmdShow, LPSTR lpCmdLine)
                  0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);\r
 #endif\r
     ShowWindow(hwndConsole, nCmdShow);\r
+    if(appData.chatBoxes) { // [HGM] chat: open chat boxes\r
+      char buf[MSG_SIZ], *p = buf, *q;\r
+       safeStrCpy(buf, appData.chatBoxes, sizeof(buf)/sizeof(buf[0]) );\r
+      do {\r
+       q = strchr(p, ';');\r
+       if(q) *q++ = 0;\r
+       if(*p) ChatPopUp(p);\r
+      } while(p=q);\r
+    }\r
+    SetActiveWindow(hwndConsole);\r
   }\r
   if(!appData.noGUI)   UpdateWindow(hwnd);  else ShowWindow(hwnd, SW_MINIMIZE);\r
   if(gameListDialog) SetFocus(gameListDialog); // [HGM] jaws: for if we clicked multi-game game file\r
@@ -911,7 +1173,7 @@ LFfromMFP(LOGFONT* lf, MyFontParams *mfp)
   lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;\r
   lf->lfQuality = DEFAULT_QUALITY;\r
   lf->lfPitchAndFamily = DEFAULT_PITCH|FF_DONTCARE;\r
-  strcpy(lf->lfFaceName, mfp->faceName);\r
+    safeStrCpy(lf->lfFaceName, mfp->faceName, sizeof(lf->lfFaceName)/sizeof(lf->lfFaceName[0]) );\r
 }\r
 \r
 void\r
@@ -956,7 +1218,7 @@ ParseFontName(char *name, MyFontParams *mfp)
   q = strchr(p, ':');\r
   if (q) {\r
     if (q - p >= sizeof(mfp->faceName))\r
-      ExitArgError("Font name too long:", name);\r
+      ExitArgError(_("Font name too long:"), name);\r
     memcpy(mfp->faceName, p, q - p);\r
     mfp->faceName[q - p] = NULLCHAR;\r
     p = q + 1;\r
@@ -965,12 +1227,12 @@ ParseFontName(char *name, MyFontParams *mfp)
     while (*p && !isdigit(*p)) {\r
       *q++ = *p++;\r
       if (q - mfp->faceName >= sizeof(mfp->faceName))\r
-       ExitArgError("Font name too long:", name);\r
+       ExitArgError(_("Font name too long:"), name);\r
     }\r
     while (q > mfp->faceName && q[-1] == ' ') q--;\r
     *q = NULLCHAR;\r
   }\r
-  if (!*p) ExitArgError("Font point size missing:", name);\r
+  if (!*p) ExitArgError(_("Font point size missing:"), name);\r
   mfp->pointSize = (float) atof(p);\r
   mfp->bold = (strchr(p, 'b') != NULL);\r
   mfp->italic = (strchr(p, 'i') != NULL);\r
@@ -1027,7 +1289,7 @@ ParseColorName(char *name)
       &red, &green, &blue);\r
   }\r
   if (count != 3) {\r
-    sprintf(buf, "Can't parse color name %s", name);\r
+    snprintf(buf, MSG_SIZ, _("Can't parse color name %s"), name);\r
     DisplayError(buf, 0);\r
     return RGB(0, 0, 0);\r
   }\r
@@ -1076,7 +1338,7 @@ ParseBoardSize(void *addr, char *name)
     }\r
     bs++;\r
   }\r
-  ExitArgError("Unrecognized board size value", name);\r
+  ExitArgError(_("Unrecognized board size value"), name);\r
 }\r
 \r
 void\r
@@ -1210,7 +1472,19 @@ PrintCommPortSettings(FILE *f, char *name)
 int\r
 MySearchPath(char *installDir, char *name, char *fullname)\r
 {\r
-  char *dummy;\r
+  char *dummy, buf[MSG_SIZ], *p = name, *q;\r
+  if(name[0]== '%') {\r
+    fullname[0] = 0; // [HGM] first expand any environment variables in the given name\r
+    while(*p == '%' && (q = strchr(p+1, '%'))) { // [HGM] recognize %*% as environment variable\r
+      safeStrCpy(buf, p+1, sizeof(buf)/sizeof(buf[0]) );\r
+      *strchr(buf, '%') = 0;\r
+      strcat(fullname, getenv(buf));\r
+      p = q+1; while(*p == '\\') { strcat(fullname, "\\"); p++; }\r
+    }\r
+    strcat(fullname, p); // after environment variables (if any), take the remainder of the given name\r
+    if(appData.debugMode) fprintf(debugFP, "name = '%s', expanded name = '%s'\n", name, fullname);\r
+    return (int) strlen(fullname);\r
+  }\r
   return (int) SearchPath(installDir, name, NULL, MSG_SIZ, fullname, &dummy);\r
 }\r
 \r
@@ -1232,6 +1506,7 @@ PopUpStartupDialog()
 {\r
     FARPROC lpProc;\r
     \r
+    LoadLanguageFile(appData.language);\r
     lpProc = MakeProcInstance((FARPROC)StartupDialog, hInst);\r
     DialogBox(hInst, MAKEINTRESOURCE(DLG_Startup), NULL, (DLGPROC)lpProc);\r
     FreeProcInstance(lpProc);\r
@@ -1786,11 +2061,11 @@ DoLoadBitmap(HINSTANCE hinst, char *piece, int squareSize, char *suffix)
 {\r
   char name[128];\r
 \r
-  sprintf(name, "%s%d%s", piece, squareSize, suffix);\r
+    snprintf(name, sizeof(name)/sizeof(name[0]), "%s%d%s", piece, squareSize, suffix);\r
   if (gameInfo.event &&\r
       strcmp(gameInfo.event, "Easter Egg Hunt") == 0 &&\r
       strcmp(name, "k80s") == 0) {\r
-    strcpy(name, "tim");\r
+    safeStrCpy(name, "tim", sizeof(name)/sizeof(name[0]) );\r
   }\r
   return LoadBitmap(hinst, name);\r
 }\r
@@ -1807,7 +2082,7 @@ InsertInPalette(COLORREF color)
   LPPALETTEENTRY pe = &(pLogPal->palPalEntry[pLogPal->palNumEntries]);\r
 \r
   if (pLogPal->palNumEntries++ >= PALETTESIZE) {\r
-    DisplayFatalError("Too many colors", 0, 1);\r
+    DisplayFatalError(_("Too many colors"), 0, 1);\r
     pLogPal->palNumEntries--;\r
     return;\r
   }\r
@@ -1896,6 +2171,7 @@ ResizeBoard(int newSizeX, int newSizeY, int flags)
 }\r
 \r
 \r
+extern Boolean twoBoards, partnerUp; // [HGM] dual\r
 \r
 VOID\r
 InitDrawingSizes(BoardSize boardSize, int flags)\r
@@ -1948,7 +2224,7 @@ InitDrawingSizes(BoardSize boardSize, int flags)
 \r
     for (i=0; menuBarText[tinyLayout][i]; i++) {\r
       ModifyMenu(hmenu, i, MF_STRING|MF_BYPOSITION|MF_POPUP, \r
-       (UINT)GetSubMenu(hmenu, i), menuBarText[tinyLayout][i]);\r
+       (UINT)GetSubMenu(hmenu, i), T_(menuBarText[tinyLayout][i]));\r
     }\r
     DrawMenuBar(hwndMain);\r
   }\r
@@ -1959,14 +2235,14 @@ InitDrawingSizes(BoardSize boardSize, int flags)
   /* Get text area sizes */\r
   hdc = GetDC(hwndMain);\r
   if (appData.clockMode) {\r
-    sprintf(buf, "White: %s", TimeString(23*60*60*1000L));\r
+    snprintf(buf, MSG_SIZ, _("White: %s"), TimeString(23*60*60*1000L));\r
   } else {\r
-    sprintf(buf, "White");\r
+    snprintf(buf, MSG_SIZ, _("White"));\r
   }\r
   oldFont = SelectObject(hdc, font[boardSize][CLOCK_FONT]->hf);\r
   GetTextExtentPoint(hdc, buf, strlen(buf), &clockSize);\r
   SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
-  str = "We only care about the height here";\r
+  str = _("We only care about the height here");\r
   GetTextExtentPoint(hdc, str, strlen(str), &messageSize);\r
   SelectObject(hdc, oldFont);\r
   ReleaseDC(hwndMain, hdc);\r
@@ -2005,6 +2281,8 @@ InitDrawingSizes(BoardSize boardSize, int flags)
     blackRect.right = blackRect.left + boardWidth/2 - 1;\r
     blackRect.top = whiteRect.top;\r
     blackRect.bottom = whiteRect.bottom;\r
+\r
+    logoHeight = 0; // [HGM] logo: suppress logo after change to tiny layout!\r
   }\r
 \r
   messageRect.left = OUTER_MARGIN + MESSAGE_LINE_LEFTMARGIN;\r
@@ -2029,6 +2307,7 @@ InitDrawingSizes(BoardSize boardSize, int flags)
   winW = 2 * GetSystemMetrics(SM_CXFRAME) + boardRect.right + OUTER_MARGIN;\r
   winH = 2 * GetSystemMetrics(SM_CYFRAME) + GetSystemMetrics(SM_CYMENU) +\r
     GetSystemMetrics(SM_CYCAPTION) + boardRect.bottom + OUTER_MARGIN;\r
+  winW *= 1 + twoBoards;\r
   if(suppressVisibleEffects) return; // [HGM] when called for filling sizeInfo only\r
   wpMain.width = winW;  // [HGM] placement: set through temporary which can used by initial sizing choice\r
   wpMain.height = winH; //       without disturbing window attachments\r
@@ -2475,23 +2754,14 @@ DrawHighlightOnDC(HDC hdc, BOOLEAN on, int x, int y, int pen)
 }\r
 \r
 VOID\r
-DrawHighlightsOnDC(HDC hdc)\r
+DrawHighlightsOnDC(HDC hdc, HighlightInfo *h, int pen)\r
 {\r
   int i;\r
   for (i=0; i<2; i++) {\r
-    if (highlightInfo.sq[i].x >= 0 && highlightInfo.sq[i].y >= 0) \r
+    if (h->sq[i].x >= 0 && h->sq[i].y >= 0) \r
       DrawHighlightOnDC(hdc, TRUE,\r
-                       highlightInfo.sq[i].x, highlightInfo.sq[i].y,\r
-                       HIGHLIGHT_PEN);\r
-  }\r
-  for (i=0; i<2; i++) {\r
-    if (premoveHighlightInfo.sq[i].x >= 0 && \r
-       premoveHighlightInfo.sq[i].y >= 0) {\r
-       DrawHighlightOnDC(hdc, TRUE,\r
-                         premoveHighlightInfo.sq[i].x, \r
-                         premoveHighlightInfo.sq[i].y,\r
-                         PREMOVE_PEN);\r
-    }\r
+                       h->sq[i].x, h->sq[i].y,\r
+                       pen);\r
   }\r
 }\r
 \r
@@ -2508,7 +2778,7 @@ DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y,
   if (appData.blindfold) return;\r
 \r
   /* [AS] Use font-based pieces if needed */\r
-  if( fontBitmapSquareSize >= 0 && squareSize > 32 ) {\r
+  if( fontBitmapSquareSize >= 0 && (squareSize > 32 || gameInfo.variant >= VariantShogi)) {\r
     /* Create piece bitmaps, or do nothing if piece set is up to date */\r
     CreatePiecesFromFont();\r
 \r
@@ -2517,6 +2787,9 @@ DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y,
 \r
         SelectObject( tmphdc, hPieceMask[ index ] );\r
 \r
+      if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
+        StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCAND);\r
+      else\r
         BitBlt( hdc,\r
             x, y,\r
             squareSize, squareSize,\r
@@ -2526,6 +2799,9 @@ DrawPieceOnDC(HDC hdc, ChessSquare piece, int color, int sqcolor, int x, int y,
 \r
         SelectObject( tmphdc, hPieceFace[ index ] );\r
 \r
+      if(appData.upsideDown ? color==flipView : (flipView && gameInfo.variant == VariantShogi))\r
+        StretchBlt(hdc, x+squareSize, y+squareSize, -squareSize, -squareSize, tmphdc, 0, 0, squareSize, squareSize, SRCPAINT);\r
+      else\r
         BitBlt( hdc,\r
             x, y,\r
             squareSize, squareSize,\r
@@ -2635,7 +2911,13 @@ VOID RebuildTextureSquareInfo()
             if( (col + row) & 1 ) {\r
                 /* Lite square */\r
                 if( lite_w >= squareSize && lite_h >= squareSize ) {\r
+                  if( lite_w >= squareSize*BOARD_WIDTH )\r
+                    backTextureSquareInfo[row][col].x = (2*col+1)*lite_w/(2*BOARD_WIDTH) - squareSize/2;  /* [HGM] cut out of center of virtual square */\r
+                  else\r
                     backTextureSquareInfo[row][col].x = col * (lite_w - squareSize) / (BOARD_WIDTH-1);  /* [HGM] divide by size-1 in stead of size! */\r
+                  if( lite_h >= squareSize*BOARD_HEIGHT )\r
+                    backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1)*lite_h/(2*BOARD_HEIGHT) - squareSize/2;\r
+                  else\r
                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (lite_h - squareSize) / (BOARD_HEIGHT-1);\r
                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(liteBackTextureMode);\r
                 }\r
@@ -2643,7 +2925,13 @@ VOID RebuildTextureSquareInfo()
             else {\r
                 /* Dark square */\r
                 if( dark_w >= squareSize && dark_h >= squareSize ) {\r
+                  if( dark_w >= squareSize*BOARD_WIDTH )\r
+                    backTextureSquareInfo[row][col].x = (2*col+1) * dark_w / (2*BOARD_WIDTH) - squareSize/2;\r
+                  else\r
                     backTextureSquareInfo[row][col].x = col * (dark_w - squareSize) / (BOARD_WIDTH-1);\r
+                  if( dark_h >= squareSize*BOARD_HEIGHT )\r
+                    backTextureSquareInfo[row][col].y = (2*(BOARD_HEIGHT-row)-1) * dark_h / (2*BOARD_HEIGHT) - squareSize/2;\r
+                  else\r
                     backTextureSquareInfo[row][col].y = (BOARD_HEIGHT-1-row) * (dark_h - squareSize) / (BOARD_HEIGHT-1);\r
                     backTextureSquareInfo[row][col].mode = GetBackTextureMode(darkBackTextureMode);\r
                 }\r
@@ -3082,13 +3370,69 @@ DrawLogoOnDC(HDC hdc, RECT logoRect, HBITMAP logo)
   DeleteDC(tmphdc);\r
 }\r
 \r
+static HDC hdcSeek;\r
+\r
+// [HGM] seekgraph\r
+void DrawSeekAxis( int x, int y, int xTo, int yTo )\r
+{\r
+    POINT stPt;\r
+    HPEN hp = SelectObject( hdcSeek, gridPen );\r
+    MoveToEx( hdcSeek, boardRect.left+x, boardRect.top+y, &stPt );\r
+    LineTo( hdcSeek, boardRect.left+xTo, boardRect.top+yTo );\r
+    SelectObject( hdcSeek, hp );\r
+}\r
+\r
+// front-end wrapper for drawing functions to do rectangles\r
+void DrawSeekBackground( int left, int top, int right, int bottom )\r
+{\r
+    HPEN hp;\r
+    RECT rc;\r
+\r
+    if (hdcSeek == NULL) {\r
+    hdcSeek = GetDC(hwndMain);\r
+      if (!appData.monoMode) {\r
+        SelectPalette(hdcSeek, hPal, FALSE);\r
+        RealizePalette(hdcSeek);\r
+      }\r
+    }\r
+    hp = SelectObject( hdcSeek, gridPen );\r
+    rc.top = boardRect.top+top; rc.left = boardRect.left+left; \r
+    rc.bottom = boardRect.top+bottom; rc.right = boardRect.left+right;\r
+    FillRect( hdcSeek, &rc, lightSquareBrush );\r
+    SelectObject( hdcSeek, hp );\r
+}\r
+\r
+// front-end wrapper for putting text in graph\r
+void DrawSeekText(char *buf, int x, int y)\r
+{\r
+        SIZE stSize;\r
+       SetBkMode( hdcSeek, TRANSPARENT );\r
+        GetTextExtentPoint32( hdcSeek, buf, strlen(buf), &stSize );\r
+        TextOut( hdcSeek, boardRect.left+x-3, boardRect.top+y-stSize.cy/2, buf, strlen(buf) );\r
+}\r
+\r
+void DrawSeekDot(int x, int y, int color)\r
+{\r
+       int square = color & 0x80;\r
+       HBRUSH oldBrush = SelectObject(hdcSeek, \r
+                       color == 0 ? markerBrush : color == 1 ? darkSquareBrush : explodeBrush);\r
+       color &= 0x7F;\r
+       if(square)\r
+           Rectangle(hdcSeek, boardRect.left+x - squareSize/9, boardRect.top+y - squareSize/9,\r
+                              boardRect.left+x + squareSize/9, boardRect.top+y + squareSize/9);\r
+       else\r
+           Ellipse(hdcSeek, boardRect.left+x - squareSize/8, boardRect.top+y - squareSize/8,\r
+                            boardRect.left+x + squareSize/8, boardRect.top+y + squareSize/8);\r
+           SelectObject(hdcSeek, oldBrush);\r
+}\r
+\r
 VOID\r
 HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)\r
 {\r
-  static Board lastReq, lastDrawn;\r
+  static Board lastReq[2], lastDrawn[2];\r
   static HighlightInfo lastDrawnHighlight, lastDrawnPremove;\r
   static int lastDrawnFlipView = 0;\r
-  static int lastReqValid = 0, lastDrawnValid = 0;\r
+  static int lastReqValid[2] = {0, 0}, lastDrawnValid[2] = {0, 0};\r
   int releaseDC, x, y, x2, y2, row, column, num_clips = 0, i;\r
   HDC tmphdc;\r
   HDC hdcmem;\r
@@ -3097,6 +3441,7 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
   RECT Rect;\r
   HRGN clips[MAX_CLIPS];\r
   ChessSquare dragged_piece = EmptySquare;\r
+  int nr = twoBoards*partnerUp;\r
 \r
   /* I'm undecided on this - this function figures out whether a full\r
    * repaint is necessary on its own, so there's no real reason to have the\r
@@ -3108,18 +3453,20 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
    */\r
   Boolean fullrepaint = repaint;\r
 \r
+  if(DrawSeekGraph()) return; // [HG} seekgraph: suppress printing board if seek graph up\r
+\r
   if( DrawPositionNeedsFullRepaint() ) {\r
       fullrepaint = TRUE;\r
   }\r
 \r
   if (board == NULL) {\r
-    if (!lastReqValid) {\r
+    if (!lastReqValid[nr]) {\r
       return;\r
     }\r
-    board = lastReq;\r
+    board = lastReq[nr];\r
   } else {\r
-    CopyBoard(lastReq, board);\r
-    lastReqValid = 1;\r
+    CopyBoard(lastReq[nr], board);\r
+    lastReqValid[nr] = 1;\r
   }\r
 \r
   if (doingSizing) {\r
@@ -3165,16 +3512,17 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
    * newest board with the last drawn board and checking if\r
    * flipping has changed.\r
    */\r
-  if (!fullrepaint && lastDrawnValid && lastDrawnFlipView == flipView) {\r
+  if (!fullrepaint && lastDrawnValid[nr] && (nr == 1 || lastDrawnFlipView == flipView)) {\r
     for (row = 0; row < BOARD_HEIGHT; row++) { /* [HGM] true size, not 8 */\r
       for (column = 0; column < BOARD_WIDTH; column++) {\r
-       if (lastDrawn[row][column] != board[row][column]) {\r
+       if (lastDrawn[nr][row][column] != board[row][column]) {\r
          SquareToPos(row, column, &x, &y);\r
          clips[num_clips++] =\r
            CreateRectRgn(x, y, x + squareSize, y + squareSize);\r
        }\r
       }\r
     }\r
+   if(nr == 0) { // [HGM] dual: no highlights on second board\r
     for (i=0; i<2; i++) {\r
       if (lastDrawnHighlight.sq[i].x != highlightInfo.sq[i].x ||\r
          lastDrawnHighlight.sq[i].y != highlightInfo.sq[i].y) {\r
@@ -3215,6 +3563,30 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
        }\r
       }\r
     }\r
+   } else { // nr == 1\r
+       partnerHighlightInfo.sq[0].y = board[EP_STATUS-4];\r
+       partnerHighlightInfo.sq[0].x = board[EP_STATUS-3];\r
+       partnerHighlightInfo.sq[1].y = board[EP_STATUS-2];\r
+       partnerHighlightInfo.sq[1].x = board[EP_STATUS-1];\r
+      for (i=0; i<2; i++) {\r
+       if (partnerHighlightInfo.sq[i].x >= 0 &&\r
+           partnerHighlightInfo.sq[i].y >= 0) {\r
+         SquareToPos(partnerHighlightInfo.sq[i].y,\r
+                     partnerHighlightInfo.sq[i].x, &x, &y);\r
+         clips[num_clips++] =\r
+           CreateRectRgn(x - lineGap, y - lineGap, \r
+                         x + squareSize + lineGap, y + squareSize + lineGap);\r
+       }\r
+       if (oldPartnerHighlight.sq[i].x >= 0 && \r
+           oldPartnerHighlight.sq[i].y >= 0) {\r
+         SquareToPos(oldPartnerHighlight.sq[i].y, \r
+                     oldPartnerHighlight.sq[i].x, &x, &y);\r
+         clips[num_clips++] =\r
+           CreateRectRgn(x - lineGap, y - lineGap, \r
+                         x + squareSize + lineGap, y + squareSize + lineGap);\r
+       }\r
+      }\r
+   }\r
   } else {\r
     fullrepaint = TRUE;\r
   }\r
@@ -3274,7 +3646,7 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
         explodes.  The old and new positions both had an empty square\r
         at the destination, but animation has drawn a piece there and\r
         we have to remember to erase it. [HGM] moved until after setting lastDrawn */\r
-      lastDrawn[animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
+      lastDrawn[0][animInfo.to.y][animInfo.to.x] = animInfo.piece;\r
     }\r
   }\r
 \r
@@ -3304,16 +3676,24 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
          ExtSelectClipRgn(hdcmem, clips[num_clips++], RGN_OR);\r
        }\r
        DrawGridOnDC(hdcmem);\r
-       DrawHighlightsOnDC(hdcmem);\r
+       DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
+       DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
        DrawBoardOnDC(hdcmem, board, tmphdc);\r
        oldBrush = SelectObject(hdcmem, explodeBrush);\r
        Ellipse(hdcmem, x-r, y-r, x+r, y+r);\r
        SelectObject(hdcmem, oldBrush);\r
   } else {\r
     DrawGridOnDC(hdcmem);\r
-    DrawHighlightsOnDC(hdcmem);\r
+    if(nr == 0) { // [HGM] dual: decide which highlights to draw\r
+       DrawHighlightsOnDC(hdcmem, &highlightInfo, HIGHLIGHT_PEN);\r
+       DrawHighlightsOnDC(hdcmem, &premoveHighlightInfo, PREMOVE_PEN);\r
+    } else {\r
+       DrawHighlightsOnDC(hdcmem, &partnerHighlightInfo, HIGHLIGHT_PEN);\r
+       oldPartnerHighlight = partnerHighlightInfo;\r
+    }\r
     DrawBoardOnDC(hdcmem, board, tmphdc);\r
   }\r
+  if(nr == 0) // [HGM] dual: markers only on left board\r
   for (row = 0; row < BOARD_HEIGHT; row++) {\r
     for (column = 0; column < BOARD_WIDTH; column++) {\r
        if (marker[row][column]) { // marker changes only occur with full repaint!\r
@@ -3368,7 +3748,7 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
 \r
   DrawCoordsOnDC(hdcmem);\r
 \r
-  CopyBoard(lastDrawn, board); /* [HGM] Moved to here from end of routine, */\r
+  CopyBoard(lastDrawn[nr], board); /* [HGM] Moved to here from end of routine, */\r
                  /* to make sure lastDrawn contains what is actually drawn */\r
 \r
   /* Put the dragged piece back into place and draw it (out of place!) */\r
@@ -3405,6 +3785,13 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
 \r
   /* Set clipping on the target DC */\r
   if (!fullrepaint) {\r
+    if(nr == 1) for (x = 0; x < num_clips; x++) { // [HGM] dual: translate clips\r
+       RECT rect;\r
+       GetRgnBox(clips[x], &rect);\r
+       DeleteObject(clips[x]);\r
+       clips[x] = CreateRectRgn(rect.left + wpMain.width/2, rect.top, \r
+                         rect.right + wpMain.width/2, rect.bottom);\r
+    }\r
     SelectClipRgn(hdc, clips[0]);\r
     for (x = 1; x < num_clips; x++) {\r
       if (ExtSelectClipRgn(hdc, clips[x], RGN_OR) == ERROR)\r
@@ -3416,7 +3803,7 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
    * This way we avoid any flickering\r
    */\r
   oldBitmap = SelectObject(tmphdc, bufferBitmap);\r
-  BitBlt(hdc, boardRect.left, boardRect.top,\r
+  BitBlt(hdc, boardRect.left + twoBoards*partnerUp*wpMain.width/2, boardRect.top, // [HGM] dual\r
         boardRect.right - boardRect.left,\r
         boardRect.bottom - boardRect.top,\r
         tmphdc, boardRect.left, boardRect.top, SRCCOPY);\r
@@ -3504,7 +3891,7 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
   if (releaseDC) \r
     ReleaseDC(hwndMain, hdc);\r
   \r
-  if (lastDrawnFlipView != flipView) {\r
+  if (lastDrawnFlipView != flipView && nr == 0) {\r
     if (flipView)\r
       CheckMenuItem(GetMenu(hwndMain),IDM_FlipView, MF_BYCOMMAND|MF_CHECKED);\r
     else\r
@@ -3515,7 +3902,7 @@ HDCDrawPosition(HDC hdc, BOOLEAN repaint, Board board)
   lastDrawnHighlight = highlightInfo;\r
   lastDrawnPremove   = premoveHighlightInfo;\r
   lastDrawnFlipView = flipView;\r
-  lastDrawnValid = 1;\r
+  lastDrawnValid[nr] = 1;\r
 }\r
 \r
 /* [HGM] diag: Save the current board display to the given open file and close the file */\r
@@ -3556,6 +3943,11 @@ PaintProc(HWND hwnd)
        RealizePalette(hdc);\r
       }\r
       HDCDrawPosition(hdc, 1, NULL);\r
+      if(twoBoards) { // [HGM] dual: also redraw other board in other orientation\r
+       flipView = !flipView; partnerUp = !partnerUp;\r
+       HDCDrawPosition(hdc, 1, NULL);\r
+       flipView = !flipView; partnerUp = !partnerUp;\r
+      }\r
       oldFont =\r
        SelectObject(hdc, font[boardSize][MESSAGE_FONT]->hf);\r
       ExtTextOut(hdc, messageRect.left, messageRect.top,\r
@@ -3600,11 +3992,11 @@ typedef struct {
 } DropEnable;\r
 \r
 DropEnable dropEnables[] = {\r
-  { 'P', DP_Pawn, "Pawn" },\r
-  { 'N', DP_Knight, "Knight" },\r
-  { 'B', DP_Bishop, "Bishop" },\r
-  { 'R', DP_Rook, "Rook" },\r
-  { 'Q', DP_Queen, "Queen" },\r
+  { 'P', DP_Pawn, N_("Pawn") },\r
+  { 'N', DP_Knight, N_("Knight") },\r
+  { 'B', DP_Bishop, N_("Bishop") },\r
+  { 'R', DP_Rook, N_("Rook") },\r
+  { 'Q', DP_Queen, N_("Queen") },\r
 };\r
 \r
 VOID\r
@@ -3620,7 +4012,7 @@ SetupDropMenu(HMENU hmenu)
               dropEnables[i].piece);\r
     count = 0;\r
     while (p && *p++ == dropEnables[i].piece) count++;\r
-    sprintf(item, "%s  %d", dropEnables[i].name, count);\r
+      snprintf(item, MSG_SIZ, "%s  %d", T_(dropEnables[i].name), count);\r
     enable = count > 0 || !appData.testLegality\r
       /*!!temp:*/ || (gameInfo.variant == VariantCrazyhouse\r
                      && !appData.icsActive);\r
@@ -3652,7 +4044,7 @@ void DragPieceEnd(int x, int y)
 VOID\r
 MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)\r
 {\r
-  int x, y;\r
+  int x, y, menuNr;\r
   POINT pt;\r
   static int recursive = 0;\r
   HMENU hmenu;\r
@@ -3686,20 +4078,20 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
         if (gameMode == EditPosition) {\r
          SetWhiteToPlayEvent();\r
+        } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
+          AdjustClock(flipClock, -1);\r
        } else if (gameMode == IcsPlayingBlack ||\r
                   gameMode == MachinePlaysWhite) {\r
          CallFlagEvent();\r
-        } else if (gameMode == EditGame) {\r
-          AdjustClock(flipClock, -1);\r
         }\r
       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
        if (gameMode == EditPosition) {\r
          SetBlackToPlayEvent();\r
+        } else if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) {\r
+          AdjustClock(!flipClock, -1);\r
        } else if (gameMode == IcsPlayingWhite ||\r
                   gameMode == MachinePlaysBlack) {\r
          CallFlagEvent();\r
-        } else if (gameMode == EditGame) {\r
-          AdjustClock(!flipClock, -1);\r
        }\r
       }\r
       dragInfo.start.x = dragInfo.start.y = -1;\r
@@ -3719,7 +4111,8 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
     break;\r
 \r
   case WM_MOUSEMOVE:\r
-    MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);
+    if(SeekGraphClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, 1)) break;\r
+    MovePV(pt.x - boardRect.left, pt.y - boardRect.top, boardRect.bottom - boardRect.top);\r
     if ((appData.animateDragging || appData.highlightDragging)\r
        && (wParam & MK_LBUTTON)\r
        && dragInfo.from.x >= 0) \r
@@ -3747,22 +4140,22 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
        /* Mouse Wheel is being rolled forward\r
         * Play moves forward\r
         */\r
-       if((short)HIWORD(wParam) > 0 && currentMove < forwardMostMove) \r
+       if((short)HIWORD(wParam) < 0 && currentMove < forwardMostMove) \r
                { if(lastDir == 1) ForwardEvent(); else lastDir = 1; } // [HGM] suppress first event in direction\r
        /* Mouse Wheel is being rolled backward\r
         * Play moves backward\r
         */\r
-       if((short)HIWORD(wParam) < 0 && currentMove > backwardMostMove) \r
+       if((short)HIWORD(wParam) > 0 && currentMove > backwardMostMove) \r
                { if(lastDir == -1) BackwardEvent(); else lastDir = -1; }\r
     }\r
     break;\r
 \r
-  case WM_MBUTTONUP:
-  case WM_RBUTTONUP:
-    ReleaseCapture();
-    UnLoadPV();
-    break;
+  case WM_MBUTTONUP:\r
+  case WM_RBUTTONUP:\r
+    ReleaseCapture();\r
+    RightClick(Release, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
+    break;\r
\r
   case WM_MBUTTONDOWN:\r
   case WM_RBUTTONDOWN:\r
     ErrorPopDown();\r
@@ -3778,21 +4171,17 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
     if(y == -2) {\r
       /* [HGM] right mouse button in clock area edit-game mode ups clock */\r
       if (PtInRect((LPRECT) &whiteRect, pt)) {\r
-          if (gameMode == EditGame) AdjustClock(flipClock, 1);\r
+          if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(flipClock, 1);\r
       } else if (PtInRect((LPRECT) &blackRect, pt)) {\r
-          if (gameMode == EditGame) AdjustClock(!flipClock, 1);\r
+          if (gameMode == EditGame || GetKeyState(VK_SHIFT) < 0) AdjustClock(!flipClock, 1);\r
       }\r
+      break;\r
     }\r
     DrawPosition(TRUE, NULL);\r
 \r
-    switch (gameMode) {\r
-    case IcsExamining:\r
-      if(x < BOARD_LEFT || x >= BOARD_RGHT) break;\r
-    case EditPosition:\r
-      if (x == BOARD_LEFT-1 || x == BOARD_RGHT) break;\r
-      if (x < 0 || y < 0) break;\r
-      fromX = x;\r
-      fromY = y;\r
+    menuNr = RightClick(Press, pt.x - boardRect.left, pt.y - boardRect.top, &fromX, &fromY);\r
+    switch (menuNr) {\r
+    case 0:\r
       if (message == WM_MBUTTONDOWN) {\r
        buttonCount = 3;  /* even if system didn't think so */\r
        if (wParam & MK_SHIFT) \r
@@ -3809,35 +4198,13 @@ MouseEvent(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
             MenuPopup(hwnd, pt, LoadMenu(hInst, "ShogiPieceMenu"), -1);\r
       }\r
       break;\r
-    case IcsObserving:
-      if(!appData.icsEngineAnalyze) break;
-    case IcsPlayingWhite:
-    case IcsPlayingBlack:
-      if(!appData.zippyPlay) goto noZip;
-    case MachinePlaysWhite:
-    case MachinePlaysBlack:
-    case TwoMachinesPlay:
-    case AnalyzeMode:
-    case AnalyzeFile:
-      if (!appData.dropMenu) {
-        SetCapture(hwndMain);
-        LoadPV(pt.x - boardRect.left, pt.y - boardRect.top);
-        break;
-      }
-      if(gameMode == TwoMachinesPlay || gameMode == AnalyzeMode ||
-         gameMode == AnalyzeFile || gameMode == IcsObserving) break;
-    case EditGame:
-    noZip:
-      if (x < 0 || y < 0) break;
-      if (!appData.dropMenu || appData.testLegality &&
-         gameInfo.variant != VariantBughouse &&
-         gameInfo.variant != VariantCrazyhouse) break;
-      fromX = x;\r
-      fromY = y;\r
+    case 2:\r
+      SetCapture(hwndMain);
+      break;\r
+    case 1:\r
       hmenu = LoadMenu(hInst, "DropPieceMenu");\r
       SetupDropMenu(hmenu);\r
       MenuPopup(hwnd, pt, hmenu, -1);\r
-      break;\r
     default:\r
       break;\r
     }\r
@@ -3901,6 +4268,7 @@ Promotion(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
   case WM_INITDIALOG: /* message: initialize dialog box */\r
     /* Center the dialog over the application window */\r
     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
+    Translate(hDlg, DLG_PromotionKing);\r
     ShowWindow(GetDlgItem(hDlg, PB_King), \r
       (!appData.testLegality || gameInfo.variant == VariantSuicide ||\r
        gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuper ) ?\r
@@ -4025,7 +4393,7 @@ LoadGameDialog(HWND hwnd, char* title)
     if (number == 0) {\r
       int error = GameListBuild(f);\r
       if (error) {\r
-        DisplayError("Cannot build game list", error);\r
+        DisplayError(_("Cannot build game list"), error);\r
       } else if (!ListEmpty(&gameList) &&\r
                  ((ListGame *) gameList.tailPred)->number > 1) {\r
        GameListPopUp(f, fileTitle);\r
@@ -4101,7 +4469,8 @@ ChangedConsoleFont()
 \r
   cfmt.cbSize = sizeof(CHARFORMAT);\r
   cfmt.dwMask = CFM_FACE|CFM_SIZE|CFM_CHARSET;\r
-  strcpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName);\r
+    safeStrCpy(cfmt.szFaceName, font[boardSize][CONSOLE_FONT]->mfp.faceName,\r
+              sizeof(cfmt.szFaceName)/sizeof(cfmt.szFaceName[0]) );\r
   /* yHeight is expressed in twips.  A twip is 1/20 of a font's point\r
    * size.  This was undocumented in the version of MSVC++ that I had\r
    * when I wrote the code, but is apparently documented now.\r
@@ -4247,7 +4616,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       break;\r
 \r
     case IDM_LoadGame:\r
-      LoadGameDialog(hwnd, "Load Game from File");\r
+      LoadGameDialog(hwnd, _("Load Game from File"));\r
       break;\r
 \r
     case IDM_LoadNextGame:\r
@@ -4270,7 +4639,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       f = OpenFileDialog(hwnd, "rb", "",\r
                         appData.oldSaveStyle ? "pos" : "fen",\r
                         POSITION_FILT,\r
-                        "Load Position from File", &number, fileTitle, NULL);\r
+                        _("Load Position from File"), &number, fileTitle, NULL);\r
       if (f != NULL) {\r
        LoadPosition(f, number, fileTitle);\r
       }\r
@@ -4293,7 +4662,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       f = OpenFileDialog(hwnd, "a", defName,\r
                         appData.oldSaveStyle ? "gam" : "pgn",\r
                         GAME_FILT,\r
-                        "Save Game to File", NULL, fileTitle, NULL);\r
+                        _("Save Game to File"), NULL, fileTitle, NULL);\r
       if (f != NULL) {\r
        SaveGame(f, 0, "");\r
       }\r
@@ -4304,7 +4673,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       f = OpenFileDialog(hwnd, "a", defName,\r
                         appData.oldSaveStyle ? "pos" : "fen",\r
                         POSITION_FILT,\r
-                        "Save Position to File", NULL, fileTitle, NULL);\r
+                        _("Save Position to File"), NULL, fileTitle, NULL);\r
       if (f != NULL) {\r
        SavePosition(f, 0, "");\r
       }\r
@@ -4388,7 +4757,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       break;\r
 \r
     case IDM_NewChat:\r
-      ChatPopUp();\r
+      ChatPopUp(NULL);\r
       break;\r
 \r
     case IDM_CopyPosition:\r
@@ -4444,6 +4813,12 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       SAY("computer starts playing black");\r
       break;\r
 \r
+    case IDM_Match: // [HGM] match: flows into next case, after setting Match Mode and nr of Games\r
+      if(gameMode != BeginningOfGame) break; // allow menu item to remain enabled for better mode highligting\r
+      matchMode = 2;// distinguish from command-line-triggered case (matchMode=1)\r
+      appData.matchGames = appData.defaultMatchGames;\r
+      matchGame = 1;\r
+\r
     case IDM_TwoMachines:\r
       TwoMachinesEvent();\r
       /*\r
@@ -4455,19 +4830,19 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
          TagsPopUp(tags, CmailMsg());\r
          free(tags);\r
       }\r
-      SAY("programs start playing each other");\r
+      SAY("computer starts playing both sides");\r
       break;\r
 \r
     case IDM_AnalysisMode:\r
       if (!first.analysisSupport) {\r
-        sprintf(buf, "%s does not support analysis", first.tidy);\r
+        snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
         DisplayError(buf, 0);\r
       } else {\r
        SAY("analyzing current position");\r
         /* [DM] icsEngineAnlyze [HGM] Why is this front-end??? */\r
         if (appData.icsActive) {\r
                if (gameMode != IcsObserving) {\r
-                       sprintf(buf, "You are not observing a game");\r
+                snprintf(buf, MSG_SIZ, "You are not observing a game");\r
                        DisplayError(buf, 0);\r
                        /* secure check */\r
                        if (appData.icsEngineAnalyze) {\r
@@ -4497,12 +4872,12 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
     case IDM_AnalyzeFile:\r
       if (!first.analysisSupport) {\r
         char buf[MSG_SIZ];\r
-        sprintf(buf, "%s does not support analysis", first.tidy);\r
+         snprintf(buf, MSG_SIZ, _("%s does not support analysis"), first.tidy);\r
         DisplayError(buf, 0);\r
       } else {\r
        if (!appData.showThinking) ToggleShowThinking();\r
        AnalyzeFileEvent();\r
-       LoadGameDialog(hwnd, "Analyze Game from File");\r
+       LoadGameDialog(hwnd, _("Analyze Game from File"));\r
        AnalysisPeriodicEvent(1);\r
       }\r
       break;\r
@@ -4518,7 +4893,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 \r
     case IDM_EditPosition:\r
       EditPositionEvent();\r
-      SAY("to set up a position type a FEN");\r
+      SAY("enter a FEN string or setup a position on the board using the control R pop up menu");\r
       break;\r
 \r
     case IDM_Training:\r
@@ -4585,6 +4960,10 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       StopExaminingEvent();\r
       break;\r
 \r
+    case IDM_Upload:\r
+      UploadGameEvent();\r
+      break;\r
+\r
     case IDM_TypeInMove:\r
       PopUpMoveDialog('\000');\r
       break;\r
@@ -4616,7 +4995,11 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       break;\r
 \r
     case IDM_Revert:\r
-      RevertEvent();\r
+      RevertEvent(FALSE);\r
+      break;\r
+\r
+    case IDM_Annotate: // [HGM] vari: revert with annotation\r
+      RevertEvent(TRUE);\r
       break;\r
 \r
     case IDM_TruncateGame:\r
@@ -4743,7 +5126,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       if (!MyHelp (hwnd, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS") &&\r
          !HtmlHelp(hwnd, "winboard.chm", 0, 0) ) {\r
          MessageBox (GetFocus(),\r
-                   "Unable to activate help",\r
+                   _("Unable to activate help"),\r
                    szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
       }\r
       break;\r
@@ -4752,7 +5135,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
         if (!MyHelp (hwnd, "winboard.hlp", HELP_PARTIALKEY, (DWORD)(LPSTR)"") &&\r
            !HtmlHelp(hwnd, "winboard.chm", 0, 0)       ) {\r
        MessageBox (GetFocus(),\r
-                   "Unable to activate help",\r
+                   _("Unable to activate help"),\r
                    szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
       }\r
       break;\r
@@ -4760,7 +5143,7 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
     case IDM_HELPHELP:\r
       if(!WinHelp(hwnd, (LPSTR)NULL, HELP_HELPONHELP, 0)) {\r
        MessageBox (GetFocus(),\r
-                   "Unable to activate help",\r
+                   _("Unable to activate help"),\r
                    szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
       }\r
       break;\r
@@ -4774,12 +5157,12 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       break;\r
 \r
     case IDM_DirectCommand1:\r
-      AskQuestionEvent("Direct Command",\r
-                      "Send to chess program:", "", "1");\r
+      AskQuestionEvent(_("Direct Command"),\r
+                      _("Send to chess program:"), "", "1");\r
       break;\r
     case IDM_DirectCommand2:\r
-      AskQuestionEvent("Direct Command",\r
-                      "Send to second chess program:", "", "2");\r
+      AskQuestionEvent(_("Direct Command"),\r
+                      _("Send to second chess program:"), "", "2");\r
       break;\r
 \r
     case EP_WhitePawn:\r
@@ -4957,7 +5340,23 @@ WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       fromX = fromY = -1;\r
       break;\r
 \r
+    case IDM_English:\r
+      barbaric = 0;\r
+      TranslateMenus(0);\r
+      CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
+      CheckMenuItem(GetMenu(hwndMain), IDM_English, MF_BYCOMMAND|MF_CHECKED);\r
+      lastChecked = wmId;\r
+      break;\r
+\r
     default:\r
+      if(wmId > IDM_English && wmId < IDM_English+5) {\r
+          LoadLanguageFile(languageFile[wmId - IDM_English - 1]);\r
+          TranslateMenus(0);\r
+          CheckMenuItem(GetMenu(hwndMain), lastChecked, MF_BYCOMMAND|MF_UNCHECKED);\r
+          CheckMenuItem(GetMenu(hwndMain), wmId, MF_BYCOMMAND|MF_CHECKED);\r
+          lastChecked = wmId;\r
+          break;\r
+      }\r
       return (DefWindowProc(hwnd, message, wParam, lParam));\r
     }\r
     break;\r
@@ -5196,7 +5595,7 @@ MyLoadSound(MySound *ms)
   }\r
   if (!ok) {\r
     char buf[MSG_SIZ];\r
-    sprintf(buf, "Error loading sound %s", ms->name);\r
+      snprintf(buf, MSG_SIZ, _("Error loading sound %s"), ms->name);\r
     DisplayError(buf, GetLastError());\r
   }\r
   return ok;\r
@@ -5262,6 +5661,7 @@ OldOpenFileHook(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
       number = NULL;\r
     }\r
     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
+    Translate(hDlg, 1536);\r
     return FALSE;  /* Allow for further processing */\r
 \r
   case WM_COMMAND:\r
@@ -5281,6 +5681,7 @@ OpenFileHook(HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam)
   OFNOTIFY *ofnot;\r
   switch (uiMsg) {\r
   case WM_INITDIALOG:\r
+    Translate(hdlg, DLG_IndexNumber);\r
     ofname = (OPENFILENAME *)lParam;\r
     number = (UINT *)(ofname->lCustData);\r
     break;\r
@@ -5306,12 +5707,12 @@ OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] dia
 \r
   if (fileName == NULL) fileName = buf1;\r
   if (defName == NULL) {\r
-    strcpy(fileName, "*.");\r
+    safeStrCpy(fileName, "*.", sizeof(fileName)/sizeof(fileName[0]) );\r
     strcat(fileName, defExt);\r
   } else {\r
-    strcpy(fileName, defName);\r
+    safeStrCpy(fileName, defName, sizeof(fileName)/sizeof(fileName[0]) );\r
   }\r
-  if (fileTitle) strcpy(fileTitle, "");\r
+    if (fileTitle) safeStrCpy(fileTitle, "", sizeof(fileTitle)/sizeof(fileTitle[0]) );\r
   if (number) *number = 0;\r
 \r
   openFileName.lStructSize       = sizeof(OPENFILENAME);\r
@@ -5344,13 +5745,13 @@ OpenFileDialog(HWND hwnd, char *write, char *defName, char *defExt, // [HGM] dia
     /* open the file */\r
     f = fopen(openFileName.lpstrFile, write);\r
     if (f == NULL) {\r
-      MessageBox(hwnd, "File open failed", NULL,\r
+      MessageBox(hwnd, _("File open failed"), NULL,\r
                 MB_OK|MB_ICONEXCLAMATION);\r
       return NULL;\r
     }\r
   } else {\r
     int err = CommDlgExtendedError();\r
-    if (err != 0) DisplayError("Internal error in file dialog box", err);\r
+    if (err != 0) DisplayError(_("Internal error in file dialog box"), err);\r
     return FALSE;\r
   }\r
   return f;\r
@@ -5510,7 +5911,7 @@ InitComboStrings(HANDLE hwndCombo, char **cd)
   SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0);\r
 \r
   while (*cd != NULL) {\r
-    SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) *cd);\r
+    SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM) T_(*cd));\r
     cd++;\r
   }\r
 }\r
@@ -5589,10 +5990,10 @@ InitEngineBox(HWND hDlg, HWND hwndCombo, char* nthcp, char* nthd, char* nthdir,
 \r
   InitComboStringsFromOption(hwndCombo, nthnames);\r
   q = QuoteForFilename(nthcp);\r
-  sprintf(buf, "%s%s%s", q, nthcp, q);\r
+    snprintf(buf, MSG_SIZ, "%s%s%s", q, nthcp, q);\r
   if (*nthdir != NULLCHAR) {\r
     q = QuoteForFilename(nthdir);\r
-    sprintf(buf + strlen(buf), " /%s=%s%s%s", nthd, q, nthdir, q);\r
+      snprintf(buf + strlen(buf), MSG_SIZ, " /%s=%s%s%s", nthd, q, nthdir, q);\r
   }\r
   if (*nthcp == NULLCHAR) {\r
     SendMessage(hwndCombo, CB_SETCURSEL, (WPARAM) 0, (LPARAM) 0);\r
@@ -5613,6 +6014,7 @@ StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
   case WM_INITDIALOG:\r
     /* Center the dialog */\r
     CenterWindow (hDlg, GetDesktopWindow());\r
+    Translate(hDlg, DLG_Startup);\r
     /* Initialize the dialog items */\r
     InitEngineBox(hDlg, GetDlgItem(hDlg, OPT_ChessEngineName),\r
                  appData.firstChessProgram, "fd", appData.firstDirectory,\r
@@ -5622,7 +6024,7 @@ StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
                  secondChessProgramNames);\r
     hwndCombo = GetDlgItem(hDlg, OPT_ChessServerName);\r
     InitComboStringsFromOption(hwndCombo, icsNames);    \r
-    sprintf(buf, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
+      snprintf(buf, MSG_SIZ, "%s /icsport=%s", appData.icsHost, appData.icsPort);\r
     if (*appData.icsHelper != NULLCHAR) {\r
       char *q = QuoteForFilename(appData.icsHelper);\r
       sprintf(buf + strlen(buf), " /icshelper=%s%s%s", q, appData.icsHelper, q);\r
@@ -5652,23 +6054,23 @@ StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
     switch (LOWORD(wParam)) {\r
     case IDOK:\r
       if (IsDlgButtonChecked(hDlg, OPT_ChessEngine)) {\r
-        strcpy(buf, "/fcp=");\r
+        safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
        GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
         p = buf;\r
        ParseArgs(StringGet, &p);\r
-        strcpy(buf, "/scp=");\r
+       safeStrCpy(buf, "/scp=", sizeof(buf)/sizeof(buf[0]) );\r
        GetDlgItemText(hDlg, OPT_SecondChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
         p = buf;\r
        ParseArgs(StringGet, &p);\r
        appData.noChessProgram = FALSE;\r
        appData.icsActive = FALSE;\r
       } else if (IsDlgButtonChecked(hDlg, OPT_ChessServer)) {\r
-        strcpy(buf, "/ics /icshost=");\r
+        safeStrCpy(buf, "/ics /icshost=", sizeof(buf)/sizeof(buf[0]) );\r
        GetDlgItemText(hDlg, OPT_ChessServerName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
         p = buf;\r
        ParseArgs(StringGet, &p);\r
        if (appData.zippyPlay) {\r
-         strcpy(buf, "/fcp=");\r
+         safeStrCpy(buf, "/fcp=", sizeof(buf)/sizeof(buf[0]) );\r
          GetDlgItemText(hDlg, OPT_ChessEngineName, buf + strlen(buf), sizeof(buf) - strlen(buf));\r
          p = buf;\r
          ParseArgs(StringGet, &p);\r
@@ -5677,8 +6079,8 @@ StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
        appData.noChessProgram = TRUE;\r
        appData.icsActive = FALSE;\r
       } else {\r
-       MessageBox(hDlg, "Choose an option, or cancel to exit",\r
-                  "Option Error", MB_OK|MB_ICONEXCLAMATION);\r
+       MessageBox(hDlg, _("Choose an option, or cancel to exit"),\r
+                  _("Option Error"), MB_OK|MB_ICONEXCLAMATION);\r
        return TRUE;\r
       }\r
       if (IsDlgButtonChecked(hDlg, OPT_AnyAdditional)) {\r
@@ -5696,7 +6098,7 @@ StartupDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
     case IDM_HELPCONTENTS:\r
       if (!WinHelp (hDlg, "winboard.hlp", HELP_KEY,(DWORD)(LPSTR)"CONTENTS")) {\r
        MessageBox (GetFocus(),\r
-                   "Unable to activate help",\r
+                   _("Unable to activate help"),\r
                    szAppName, MB_SYSTEMMODAL|MB_OK|MB_ICONHAND);\r
       }\r
       break;\r
@@ -5725,6 +6127,7 @@ About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
     /* Center the dialog over the application window */\r
     CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));\r
     SetDlgItemText(hDlg, ABOUTBOX_Version, programVersion);\r
+    Translate(hDlg, ABOUTBOX);\r
     JAWS_COPYRIGHT\r
     return (TRUE);\r
 \r
@@ -5758,6 +6161,7 @@ CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
   switch (message) {\r
   case WM_INITDIALOG: /* message: initialize dialog box */\r
     /* Initialize the dialog items */\r
+    Translate(hDlg, DLG_EditComment);\r
     hwndText = GetDlgItem(hDlg, OPT_CommentText);\r
     SetDlgItemText(hDlg, OPT_CommentText, commentText);\r
     EnableWindow(GetDlgItem(hDlg, OPT_CancelComment), editComment);\r
@@ -5803,6 +6207,7 @@ CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
        sizeY = newSizeY;\r
       }\r
     }\r
+    SendDlgItemMessage( hDlg, OPT_CommentText, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS );\r
     return FALSE;\r
 \r
   case WM_COMMAND: /* message: received a command */\r
@@ -5847,6 +6252,36 @@ CommentDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
     }\r
     break;\r
 \r
+  case WM_NOTIFY: // [HGM] vari: cloned from whistory.c\r
+        if( wParam == OPT_CommentText ) {\r
+            MSGFILTER * lpMF = (MSGFILTER *) lParam;\r
+\r
+            if( lpMF->msg == WM_RBUTTONDOWN && (lpMF->wParam & (MK_CONTROL | MK_SHIFT)) == 0 ) {\r
+                POINTL pt;\r
+                LRESULT index;\r
+\r
+                pt.x = LOWORD( lpMF->lParam );\r
+                pt.y = HIWORD( lpMF->lParam );\r
+\r
+                index = SendDlgItemMessage( hDlg, OPT_CommentText, EM_CHARFROMPOS, 0, (LPARAM) &pt );\r
+\r
+               hwndText = GetDlgItem(hDlg, OPT_CommentText); // cloned from above\r
+               len = GetWindowTextLength(hwndText);\r
+               str = (char *) malloc(len + 1);\r
+               GetWindowText(hwndText, str, len + 1);\r
+               ReplaceComment(commentIndex, str);\r
+               if(commentIndex != currentMove) ToNrEvent(commentIndex);\r
+                LoadVariation( index, str ); // [HGM] also does the actual moving to it, now\r
+               free(str);\r
+\r
+                /* Zap the message for good: apparently, returning non-zero is not enough */\r
+                lpMF->msg = WM_USER;\r
+\r
+                return TRUE;\r
+            }\r
+        }\r
+        break;\r
+\r
   case WM_SIZE:\r
     newSizeX = LOWORD(lParam);\r
     newSizeY = HIWORD(lParam);\r
@@ -5921,6 +6356,7 @@ TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
     move[0] = (char) lParam;\r
     move[1] = NULLCHAR;\r
     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
+    Translate(hDlg, DLG_TypeInMove);\r
     hInput = GetDlgItem(hDlg, OPT_Move);\r
     SetWindowText(hInput, move);\r
     SetFocus(hInput);\r
@@ -5947,7 +6383,7 @@ TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
       }\r
       if (gameMode != EditGame && currentMove != forwardMostMove && \r
        gameMode != Training) {\r
-       DisplayMoveError("Displayed move is not current");\r
+       DisplayMoveError(_("Displayed move is not current"));\r
       } else {\r
 //     GetDlgItemText(hDlg, OPT_Move, move, sizeof(move)); // moved upstream\r
        int ok = ParseOneMove(move, gameMode == EditPosition ? blackPlaysFirst : currentMove, \r
@@ -5959,7 +6395,7 @@ TypeInMoveDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
              forwardMostMove = currentMove;\r
          UserMoveEvent(fromX, fromY, toX, toY, promoChar);     \r
        } else {\r
-         DisplayMoveError("Could not parse move");\r
+         DisplayMoveError(_("Could not parse move"));\r
        }\r
       }\r
       EndDialog(hDlg, TRUE);\r
@@ -6013,6 +6449,7 @@ TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
     move[0] = (char) lParam;\r
     move[1] = NULLCHAR;\r
     CenterWindowEx(hDlg, GetWindow(hDlg, GW_OWNER), 1 );\r
+    Translate(hDlg, DLG_TypeInName);\r
     hInput = GetDlgItem(hDlg, OPT_Name);\r
     SetWindowText(hInput, move);\r
     SetFocus(hInput);\r
@@ -6025,6 +6462,12 @@ TypeInNameDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
       GetDlgItemText(hDlg, OPT_Name, move, sizeof(move));\r
       appData.userName = strdup(move);\r
       SetUserLogo();\r
+      SetGameInfo();\r
+      if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack) {\r
+       snprintf(move, MSG_SIZ, "%s vs. %s", gameInfo.white, gameInfo.black);\r
+       DisplayTitle(move);\r
+      }\r
+\r
 \r
       EndDialog(hDlg, TRUE);\r
       return TRUE;\r
@@ -6129,6 +6572,7 @@ ErrorDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
         For now, just give it a default position.\r
     */\r
     SetWindowPos(hDlg, NULL, boardRect.left+8, boardRect.top+8, 0, 0, SWP_NOZORDER|SWP_NOSIZE);\r
+    Translate(hDlg, DLG_Error);\r
 \r
     errorDialog = hDlg;\r
     SetWindowText(hDlg, errorTitle);\r
@@ -6279,13 +6723,14 @@ LoadIcsTextMenu(IcsTextMenuEntry *e)
   while (e->item) {\r
     if (strcmp(e->item, "-") == 0) {\r
       AppendMenu(h, MF_SEPARATOR, 0, 0);\r
-    } else {\r
+    } else { // [HGM] re-written a bit to use only one AppendMenu call for both cases (| or no |)\r
+      int flags = MF_STRING, j = 0;\r
       if (e->item[0] == '|') {\r
-       AppendMenu(h, MF_STRING|MF_MENUBARBREAK,\r
-                  IDM_CommandX + i, &e->item[1]);\r
-      } else {\r
-       AppendMenu(h, MF_STRING, IDM_CommandX + i, e->item);\r
+       flags |= MF_MENUBARBREAK;\r
+        j++;\r
       }\r
+      if(!strcmp(e->command, "none")) flags |= MF_GRAYED; // [HGM] chatclick: provide inactive dummy\r
+      AppendMenu(h, flags, IDM_CommandX + i, e->item + j);\r
     }\r
     e++;\r
     i++;\r
@@ -6350,11 +6795,12 @@ CommandX(HWND hwnd, char *command, BOOLEAN getname, BOOLEAN immediate)
     SendMessage(hwnd, EM_GETSELTEXT, 0, (LPARAM) name);\r
   }\r
   if (immediate) {\r
-    sprintf(buf, "%s %s", command, name);\r
+    snprintf(buf, MSG_SIZ, "%s %s", command, name);\r
     SetWindowText(hInput, buf);\r
     SendMessage(hInput, WM_CHAR, '\r', 0);\r
   } else {\r
-    sprintf(buf, "%s %s ", command, name); /* trailing space */\r
+    if(!strcmp(command, "chat")) { ChatPopUp(name); return; }\r
+      snprintf(buf, MSG_SIZ, "%s %s ", command, name); /* trailing space */\r
     SetWindowText(hInput, buf);\r
     sel.cpMin = 999999;\r
     sel.cpMax = 999999;\r
@@ -6406,11 +6852,20 @@ ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
     }\r
     return 0;\r
    } // [HGM] navigate: for Ctrl+R, flow into nex case (moved up here) to summon up menu\r
-  case WM_RBUTTONUP:\r
-    if (GetKeyState(VK_SHIFT) & ~1) {\r
-      SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
-        WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
-    } else {\r
+  case WM_RBUTTONDOWN:\r
+    if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
+      /* Move selection here if it was empty */\r
+      POINT pt;\r
+      pt.x = LOWORD(lParam);\r
+      pt.y = HIWORD(lParam);\r
+      SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
+      if (sel.cpMin == sel.cpMax) {\r
+        sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
+       sel.cpMax = sel.cpMin;\r
+       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
+      }\r
+      SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
+{ // [HGM] chatclick: code moved here from WM_RBUTTONUP case, to have menu appear on down-click\r
       POINT pt;\r
       HMENU hmenu = LoadIcsTextMenu(icsTextMenuEntry);\r
       SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
@@ -6421,9 +6876,17 @@ ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
       if (!IsClipboardFormatAvailable(CF_TEXT)) {\r
         EnableMenuItem(hmenu, IDM_Paste, MF_BYCOMMAND|MF_GRAYED);\r
       }\r
-      pt.x = LOWORD(lParam);\r
-      pt.y = HIWORD(lParam);\r
+      pt.x = LOWORD(lParam)-30; // [HGM] chatclick: make menu pop up with pointer above upper-right item\r
+      pt.y = HIWORD(lParam)-10; //       make it appear as if mouse moved there, so it will be selected on up-click\r
+      PostMessage(hwnd, WM_MOUSEMOVE, wParam, lParam+5);\r
       MenuPopup(hwnd, pt, hmenu, -1);\r
+}\r
+    }\r
+    return 0;\r
+  case WM_RBUTTONUP:\r
+    if (GetKeyState(VK_SHIFT) & ~1) {\r
+      SendDlgItemMessage(hwndConsole, OPT_ConsoleText, \r
+        WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
     }\r
     return 0;\r
   case WM_PASTE:\r
@@ -6432,21 +6895,6 @@ ConsoleTextSubclass(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
     return SendMessage(hInput, message, wParam, lParam);\r
   case WM_MBUTTONDOWN:\r
     return SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDM_QuickPaste, 0), 0);\r
-  case WM_RBUTTONDOWN:\r
-    if (!(GetKeyState(VK_SHIFT) & ~1)) {\r
-      /* Move selection here if it was empty */\r
-      POINT pt;\r
-      pt.x = LOWORD(lParam);\r
-      pt.y = HIWORD(lParam);\r
-      SendMessage(hwnd, EM_EXGETSEL, 0, (LPARAM)&sel);\r
-      if (sel.cpMin == sel.cpMax) {\r
-        sel.cpMin = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM)&pt); /*doc is wrong*/\r
-       sel.cpMax = sel.cpMin;\r
-       SendMessage(hwnd, EM_EXSETSEL, 0, (LPARAM)&sel);\r
-      }\r
-      SendMessage(hwnd, EM_HIDESELECTION, FALSE, FALSE);\r
-    }\r
-    return 0;\r
   case WM_COMMAND:\r
     switch (LOWORD(wParam)) {\r
     case IDM_QuickPaste:\r
@@ -6918,7 +7366,8 @@ DisplayHoldingsCount(HDC hdc, int x, int y, int rightAlign, int copyNumber)
   HFONT oldFont;\r
   RECT rect;\r
 \r
-  if(copyNumber > 1) sprintf(buf, "%d", copyNumber); else buf[0] = 0;\r
+  if(copyNumber > 1)
+    snprintf(buf, sizeof(buf)/sizeof(buf[0]),"%d", copyNumber); else buf[0] = 0;\r
 \r
   oldFg = SetTextColor(hdc, RGB(255, 255, 255)); /* white */\r
   oldBg = SetBkColor(hdc, RGB(0, 0, 0)); /* black */\r
@@ -6951,9 +7400,9 @@ DisplayAClock(HDC hdc, int timeRemaining, int highlight,
 \r
   if (appData.clockMode) {\r
     if (tinyLayout)\r
-      sprintf(buf, "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
+      snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%c %s %s", color[0], TimeString(timeRemaining), flagFell);\r
     else\r
-      sprintf(buf, "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
+      snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s:%c%s %s", color, (logoHeight>0 ? 0 : ' '), TimeString(timeRemaining), flagFell);\r
     str = buf;\r
   } else {\r
     str = color;\r
@@ -6975,7 +7424,7 @@ DisplayAClock(HDC hdc, int timeRemaining, int highlight,
             rect, str, strlen(str), NULL);\r
   if(logoHeight > 0 && appData.clockMode) {\r
       RECT r;\r
-      sprintf(buf, "%s %s", buf+7, flagFell);\r
+      snprintf(buf, sizeof(buf)/sizeof(buf[0]), "%s %s", buf+7, flagFell);\r
       r.top = rect->top + logoHeight/2;\r
       r.left = rect->left;\r
       r.right = rect->right;\r
@@ -7254,6 +7703,7 @@ GreyRevert(Boolean grey)
 { // [HGM] vari: for retracting variations in local mode\r
   HMENU hmenu = GetMenu(hwndMain);\r
   EnableMenuItem(hmenu, IDM_Revert, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
+  EnableMenuItem(hmenu, IDM_Annotate, MF_BYCOMMAND|(grey ? MF_GRAYED : MF_ENABLED));\r
 }\r
 \r
 VOID\r
@@ -7275,7 +7725,9 @@ Enables gnuEnables[] = {
   { IDM_Adjourn, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_StopExamining, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_StopObserving, MF_BYCOMMAND|MF_GRAYED },\r
+  { IDM_Upload, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
+  { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_NewChat, MF_BYCOMMAND|MF_GRAYED },\r
   { -1, -1 }\r
 };\r
@@ -7286,6 +7738,7 @@ Enables icsEnables[] = {
   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
+  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_MachineBoth, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_AnalysisMode, MF_BYCOMMAND|MF_ENABLED },\r
   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
@@ -7296,10 +7749,11 @@ Enables icsEnables[] = {
   { IDM_IcsOptions, MF_BYCOMMAND|MF_ENABLED },\r
   { IDM_Engine1Options, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_Engine2Options, MF_BYCOMMAND|MF_GRAYED },\r
+  { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
   { -1, -1 }\r
 };\r
 \r
-#ifdef ZIPPY\r
+#if ZIPPY\r
 Enables zippyEnables[] = {\r
   { IDM_MoveNow, MF_BYCOMMAND|MF_ENABLED },\r
   { IDM_Hint, MF_BYCOMMAND|MF_ENABLED },\r
@@ -7315,11 +7769,13 @@ Enables ncpEnables[] = {
   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
+  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_AnalysisMode, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_AnalyzeFile, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_IcsClient, MF_BYCOMMAND|MF_GRAYED },\r
   { ACTION_POS, MF_BYPOSITION|MF_GRAYED },\r
   { IDM_Revert, MF_BYCOMMAND|MF_GRAYED },\r
+  { IDM_Annotate, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_MoveNow, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_TimeControl, MF_BYCOMMAND|MF_GRAYED },\r
@@ -7382,6 +7838,7 @@ Enables machineThinkingEnables[] = {
   { IDM_MachineWhite, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_MachineBlack, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_TwoMachines, MF_BYCOMMAND|MF_GRAYED },\r
+  { IDM_Match, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_TypeInMove, MF_BYCOMMAND|MF_GRAYED },\r
   { IDM_RetractMove, MF_BYCOMMAND|MF_GRAYED },\r
   { -1, -1 }\r
@@ -7401,6 +7858,7 @@ Enables userThinkingEnables[] = {
   { IDM_MachineWhite, MF_BYCOMMAND|MF_ENABLED },\r
   { IDM_MachineBlack, MF_BYCOMMAND|MF_ENABLED },\r
   { IDM_TwoMachines, MF_BYCOMMAND|MF_ENABLED },\r
+  { IDM_Match, MF_BYCOMMAND|MF_ENABLED },\r
   { IDM_TypeInMove, MF_BYCOMMAND|MF_ENABLED },\r
   { IDM_RetractMove, MF_BYCOMMAND|MF_ENABLED },\r
   { -1, -1 }\r
@@ -7442,7 +7900,7 @@ ModeHighlight()
     nowChecked = IDM_MachineWhite;\r
     break;\r
   case TwoMachinesPlay:\r
-    nowChecked = IDM_TwoMachines;\r
+    nowChecked = matchMode ? IDM_Match : IDM_TwoMachines; // [HGM] match\r
     break;\r
   case AnalyzeMode:\r
     nowChecked = IDM_AnalysisMode;\r
@@ -7509,7 +7967,7 @@ SetICSMode()
   SetMenuEnables(hmenu, icsEnables);\r
   EnableMenuItem(GetSubMenu(hmenu, OPTIONS_POS), ICS_POS,\r
     MF_BYPOSITION|MF_ENABLED);\r
-#ifdef ZIPPY\r
+#if ZIPPY\r
   if (appData.zippyPlay) {\r
     SetMenuEnables(hmenu, zippyEnables);\r
     if (!appData.noChessProgram)     /* [DM] icsEngineAnalyze */\r
@@ -7583,7 +8041,7 @@ SetMachineThinkingEnables()
   } else if (gameMode == MachinePlaysWhite) {\r
     (void)EnableMenuItem(hMenu, IDM_MachineWhite, flags);\r
   } else if (gameMode == TwoMachinesPlay) {\r
-    (void)EnableMenuItem(hMenu, IDM_TwoMachines, flags);\r
+    (void)EnableMenuItem(hMenu, matchMode ? IDM_Match : IDM_TwoMachines, flags); // [HGM] match\r
   }\r
 }\r
 \r
@@ -7593,17 +8051,17 @@ DisplayTitle(char *str)
 {\r
   char title[MSG_SIZ], *host;\r
   if (str[0] != NULLCHAR) {\r
-    strcpy(title, str);\r
+    safeStrCpy(title, str, sizeof(title)/sizeof(title[0]) );\r
   } else if (appData.icsActive) {\r
     if (appData.icsCommPort[0] != NULLCHAR)\r
       host = "ICS";\r
     else \r
       host = appData.icsHost;\r
-    sprintf(title, "%s: %s", szTitle, host);\r
+      snprintf(title, MSG_SIZ, "%s: %s", szTitle, host);\r
   } else if (appData.noChessProgram) {\r
-    strcpy(title, szTitle);\r
+    safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
   } else {\r
-    strcpy(title, szTitle);\r
+    safeStrCpy(title, szTitle, sizeof(title)/sizeof(title[0]) );\r
     strcat(title, ": ");\r
     strcat(title, first.tidy);\r
   }\r
@@ -7658,25 +8116,25 @@ DisplayError(char *str, int error)
   int len;\r
 \r
   if (error == 0) {\r
-    strcpy(buf, str);\r
+    safeStrCpy(buf, str, sizeof(buf)/sizeof(buf[0]) );\r
   } else {\r
     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
                        NULL, error, LANG_NEUTRAL,\r
                        (LPSTR) buf2, MSG_SIZ, NULL);\r
     if (len > 0) {\r
-      sprintf(buf, "%s:\n%s", str, buf2);\r
+      snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
     } else {\r
       ErrorMap *em = errmap;\r
       while (em->err != 0 && em->err != error) em++;\r
       if (em->err != 0) {\r
-       sprintf(buf, "%s:\n%s", str, em->msg);\r
+       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
       } else {\r
-       sprintf(buf, "%s:\nError code %d", str, error);\r
+       snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
       }\r
     }\r
   }\r
   \r
-  ErrorPopUp("Error", buf);\r
+  ErrorPopUp(_("Error"), buf);\r
 }\r
 \r
 \r
@@ -7687,7 +8145,7 @@ DisplayMoveError(char *str)
   ClearHighlights();\r
   DrawPosition(FALSE, NULL);\r
   if (appData.popupMoveErrors) {\r
-    ErrorPopUp("Error", str);\r
+    ErrorPopUp(_("Error"), str);\r
   } else {\r
     DisplayMessage(str, "");\r
     moveErrorMessageUp = TRUE;\r
@@ -7699,21 +8157,21 @@ DisplayFatalError(char *str, int error, int exitStatus)
 {\r
   char buf[2*MSG_SIZ], buf2[MSG_SIZ];\r
   int len;\r
-  char *label = exitStatus ? "Fatal Error" : "Exiting";\r
+  char *label = exitStatus ? _("Fatal Error") : _("Exiting");\r
 \r
   if (error != 0) {\r
     len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,\r
                        NULL, error, LANG_NEUTRAL,\r
                        (LPSTR) buf2, MSG_SIZ, NULL);\r
     if (len > 0) {\r
-      sprintf(buf, "%s:\n%s", str, buf2);\r
+      snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, buf2);\r
     } else {\r
       ErrorMap *em = errmap;\r
       while (em->err != 0 && em->err != error) em++;\r
       if (em->err != 0) {\r
-       sprintf(buf, "%s:\n%s", str, em->msg);\r
+       snprintf(buf, 2*MSG_SIZ, "%s:\n%s", str, em->msg);\r
       } else {\r
-       sprintf(buf, "%s:\nError code %d", str, error);\r
+       snprintf(buf, 2*MSG_SIZ, "%s:\nError code %d", str, error);\r
       }\r
     }\r
     str = buf;\r
@@ -7732,14 +8190,14 @@ DisplayFatalError(char *str, int error, int exitStatus)
 VOID\r
 DisplayInformation(char *str)\r
 {\r
-  (void) MessageBox(hwndMain, str, "Information", MB_OK|MB_ICONINFORMATION);\r
+  (void) MessageBox(hwndMain, str, _("Information"), MB_OK|MB_ICONINFORMATION);\r
 }\r
 \r
 \r
 VOID\r
 DisplayNote(char *str)\r
 {\r
-  ErrorPopUp("Note", str);\r
+  ErrorPopUp(_("Note"), str);\r
 }\r
 \r
 \r
@@ -7759,6 +8217,7 @@ QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
   case WM_INITDIALOG:\r
     qp = (QuestionParams *) lParam;\r
     CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
+    Translate(hDlg, DLG_Question);\r
     SetWindowText(hDlg, qp->title);\r
     SetDlgItemText(hDlg, OPT_QuestionText, qp->question);\r
     SetFocus(GetDlgItem(hDlg, OPT_QuestionInput));\r
@@ -7767,14 +8226,14 @@ QuestionDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
   case WM_COMMAND:\r
     switch (LOWORD(wParam)) {\r
     case IDOK:\r
-      strcpy(reply, qp->replyPrefix);\r
+      safeStrCpy(reply, qp->replyPrefix, sizeof(reply)/sizeof(reply[0]) );\r
       if (*reply) strcat(reply, " ");\r
       len = strlen(reply);\r
       GetDlgItemText(hDlg, OPT_QuestionInput, reply + len, sizeof(reply) - len);\r
       strcat(reply, "\n");\r
       OutputToProcess(qp->pr, reply, strlen(reply), &err);\r
       EndDialog(hDlg, TRUE);\r
-      if (err) DisplayFatalError("Error writing to chess program", err, 1);\r
+      if (err) DisplayFatalError(_("Error writing to chess program"), err, 1);\r
       return TRUE;\r
     case IDCANCEL:\r
       EndDialog(hDlg, FALSE);\r
@@ -7816,6 +8275,7 @@ LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM
         lpIndexFRC = (int *) lParam;\r
 \r
         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
+        Translate(hDlg, DLG_NewGameFRC);\r
 \r
         SendDlgItemMessage( hDlg, IDC_NFG_Edit, EM_SETLIMITTEXT, sizeof(buf)-1, 0 );\r
         SetDlgItemInt( hDlg, IDC_NFG_Edit, *lpIndexFRC, TRUE );\r
@@ -7842,7 +8302,7 @@ LRESULT CALLBACK NewGameFRC_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM
             }\r
             return TRUE;\r
         case IDC_NFG_Random:\r
-            sprintf( buf, "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
+         snprintf( buf, sizeof(buf)/sizeof(buf[0]), "%d", myrandom() ); /* [HGM] shuffle: no longer limit to 960 */\r
             SetDlgItemText(hDlg, IDC_NFG_Edit, buf );\r
             return TRUE;\r
         }\r
@@ -7868,104 +8328,42 @@ int NewGameFRC()
     return result;\r
 }\r
 \r
-/* [AS] Game list options */\r
-typedef struct {\r
-    char id;\r
-    char * name;\r
-} GLT_Item;\r
-\r
-static GLT_Item GLT_ItemInfo[] = {\r
-    { GLT_EVENT,      "Event" },\r
-    { GLT_SITE,       "Site" },\r
-    { GLT_DATE,       "Date" },\r
-    { GLT_ROUND,      "Round" },\r
-    { GLT_PLAYERS,    "Players" },\r
-    { GLT_RESULT,     "Result" },\r
-    { GLT_WHITE_ELO,  "White Rating" },\r
-    { GLT_BLACK_ELO,  "Black Rating" },\r
-    { GLT_TIME_CONTROL,"Time Control" },\r
-    { GLT_VARIANT,    "Variant" },\r
-    { GLT_OUT_OF_BOOK,PGN_OUT_OF_BOOK },\r
-    { GLT_RESULT_COMMENT, "Result Comment" }, // [HGM] rescom\r
-    { 0, 0 }\r
-};\r
-\r
-const char * GLT_FindItem( char id )\r
-{\r
-    const char * result = 0;\r
+/* [AS] Game list options. Refactored by HGM */\r
 \r
-    GLT_Item * list = GLT_ItemInfo;\r
+HWND gameListOptionsDialog;\r
 \r
-    while( list->id != 0 ) {\r
-        if( list->id == id ) {\r
-            result = list->name;\r
-            break;\r
-        }\r
-\r
-        list++;\r
-    }\r
-\r
-    return result;\r
+// low-level front-end: clear text edit / list widget\r
+void\r
+GLT_ClearList()\r
+{\r
+    SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
 }\r
 \r
-void GLT_AddToList( HWND hDlg, int iDlgItem, char id, int index )\r
+// low-level front-end: clear text edit / list widget\r
+void\r
+GLT_DeSelectList()\r
 {\r
-    const char * name = GLT_FindItem( id );\r
-\r
-    if( name != 0 ) {\r
-        if( index >= 0 ) {\r
-            SendDlgItemMessage( hDlg, iDlgItem, LB_INSERTSTRING, index, (LPARAM) name );\r
-        }\r
-        else {\r
-            SendDlgItemMessage( hDlg, iDlgItem, LB_ADDSTRING, 0, (LPARAM) name );\r
-        }\r
-    }\r
+    SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
 }\r
 \r
-void GLT_TagsToList( HWND hDlg, char * tags )\r
+// low-level front-end: append line to text edit / list widget\r
+void\r
+GLT_AddToList( char *name )\r
 {\r
-    char * pc = tags;\r
-\r
-    SendDlgItemMessage( hDlg, IDC_GameListTags, LB_RESETCONTENT, 0, 0 );\r
-\r
-    while( *pc ) {\r
-        GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
-        pc++;\r
-    }\r
-\r
-    SendDlgItemMessage( hDlg, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) "\t --- Hidden tags ---" );\r
-\r
-    pc = GLT_ALL_TAGS;\r
-\r
-    while( *pc ) {\r
-        if( strchr( tags, *pc ) == 0 ) {\r
-            GLT_AddToList( hDlg, IDC_GameListTags, *pc, -1 );\r
-        }\r
-        pc++;\r
+    if( name != 0 ) {\r
+            SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_ADDSTRING, 0, (LPARAM) name );\r
     }\r
-\r
-    SendDlgItemMessage( hDlg, IDC_GameListTags, LB_SETCURSEL, 0, 0 );\r
 }\r
 \r
-char GLT_ListItemToTag( HWND hDlg, int index )\r
+// low-level front-end: get line from text edit / list widget\r
+Boolean\r
+GLT_GetFromList( int index, char *name )\r
 {\r
-    char result = '\0';\r
-    char name[128];\r
-\r
-    GLT_Item * list = GLT_ItemInfo;\r
-\r
-    if( SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR ) {\r
-        while( list->id != 0 ) {\r
-            if( strcmp( list->name, name ) == 0 ) {\r
-                result = list->id;\r
-                break;\r
-            }\r
-\r
-            list++;\r
-        }\r
+    if( name != 0 ) {\r
+           if( SendDlgItemMessage( gameListOptionsDialog, IDC_GameListTags, LB_GETTEXT, index, (LPARAM) name ) != LB_ERR )\r
+               return TRUE;\r
     }\r
-\r
-    return result;\r
+    return FALSE;\r
 }\r
 \r
 void GLT_MoveSelection( HWND hDlg, int delta )\r
@@ -7986,20 +8384,16 @@ void GLT_MoveSelection( HWND hDlg, int delta )
 \r
 LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)\r
 {\r
-    static char glt[64];\r
-    static char * lpUserGLT;\r
-\r
     switch( message )\r
     {\r
     case WM_INITDIALOG:\r
-        lpUserGLT = (char *) lParam;\r
+       gameListOptionsDialog = hDlg; // [HGM] pass through global to keep out off back-end\r
         \r
-        strcpy( glt, lpUserGLT );\r
-\r
         CenterWindow(hDlg, GetWindow(hDlg, GW_OWNER));\r
+        Translate(hDlg, DLG_GameListOptions);\r
 \r
         /* Initialize list */\r
-        GLT_TagsToList( hDlg, glt );\r
+        GLT_TagsToList( lpUserGLT );\r
 \r
         SetFocus( GetDlgItem(hDlg, IDC_GameListTags) );\r
 \r
@@ -8008,19 +8402,7 @@ LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LP
     case WM_COMMAND:\r
         switch( LOWORD(wParam) ) {\r
         case IDOK:\r
-            {\r
-                char * pc = lpUserGLT;\r
-                int idx = 0;\r
-//                int cnt = (int) SendDlgItemMessage( hDlg, IDC_GameListTags, LB_GETCOUNT, 0, 0 );\r
-                char id;\r
-\r
-                do {\r
-                    id = GLT_ListItemToTag( hDlg, idx );\r
-\r
-                    *pc++ = id;\r
-                    idx++;\r
-                } while( id != '\0' );\r
-            }\r
+           GLT_ParseList();\r
             EndDialog( hDlg, 0 );\r
             return TRUE;\r
         case IDCANCEL:\r
@@ -8028,13 +8410,11 @@ LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LP
             return TRUE;\r
 \r
         case IDC_GLT_Default:\r
-            strcpy( glt, GLT_DEFAULT_TAGS );\r
-            GLT_TagsToList( hDlg, glt );\r
+            GLT_TagsToList( GLT_DEFAULT_TAGS );\r
             return TRUE;\r
 \r
         case IDC_GLT_Restore:\r
-            strcpy( glt, lpUserGLT );\r
-            GLT_TagsToList( hDlg, glt );\r
+            GLT_TagsToList( appData.gameListTags );\r
             return TRUE;\r
 \r
         case IDC_GLT_Up:\r
@@ -8054,29 +8434,27 @@ LRESULT CALLBACK GameListOptions_Proc(HWND hDlg, UINT message, WPARAM wParam, LP
 \r
 int GameListOptions()\r
 {\r
-    char glt[64];\r
     int result;\r
     FARPROC lpProc = MakeProcInstance( (FARPROC) GameListOptions_Proc, hInst );\r
 \r
-    strcpy( glt, appData.gameListTags );\r
+      safeStrCpy( lpUserGLT, appData.gameListTags ,LPUSERGLT_SIZE ); \r
 \r
-    result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)glt );\r
+    result = DialogBoxParam( hInst, MAKEINTRESOURCE(DLG_GameListOptions), hwndMain, (DLGPROC)lpProc, (LPARAM)lpUserGLT );\r
 \r
     if( result == 0 ) {\r
         /* [AS] Memory leak here! */\r
-        appData.gameListTags = strdup( glt ); \r
+        appData.gameListTags = strdup( lpUserGLT ); \r
     }\r
 \r
     return result;\r
 }\r
 \r
-\r
 VOID\r
 DisplayIcsInteractionTitle(char *str)\r
 {\r
   char consoleTitle[MSG_SIZ];\r
 \r
-  sprintf(consoleTitle, "%s: %s", szConsoleTitle, str);\r
+    snprintf(consoleTitle, MSG_SIZ, "%s: %s", szConsoleTitle, str);\r
   SetWindowText(hwndConsole, consoleTitle);\r
 }\r
 \r
@@ -8105,6 +8483,7 @@ ResetFrontEnd()
     ReleaseCapture();\r
     DrawPosition(TRUE, NULL);\r
   }\r
+  TagsPopDown();\r
 }\r
 \r
 \r
@@ -8112,7 +8491,7 @@ VOID
 CommentPopUp(char *title, char *str)\r
 {\r
   HWND hwnd = GetActiveWindow();\r
-  EitherCommentPopUp(0, title, str, FALSE);\r
+  EitherCommentPopUp(currentMove, title, str, FALSE); // [HGM] vari: fake move index, rather than 0\r
   SAY(str);\r
   SetActiveWindow(hwnd);\r
 }\r
@@ -8214,7 +8593,7 @@ UserName()
   }\r
   if (!GetUserName(buf, &bufsiz)) {\r
     /*DisplayError("Error getting user name", GetLastError());*/\r
-    strcpy(buf, "User");\r
+    safeStrCpy(buf, _("User"), sizeof(buf)/sizeof(buf[0]) );\r
   }\r
   return buf;\r
 }\r
@@ -8227,7 +8606,7 @@ HostName()
 \r
   if (!GetComputerName(buf, &bufsiz)) {\r
     /*DisplayError("Error getting host name", GetLastError());*/\r
-    strcpy(buf, "Unknown");\r
+    safeStrCpy(buf, _("Unknown"), sizeof(buf)/sizeof(buf[0]) );\r
   }\r
   return buf;\r
 }\r
@@ -8265,7 +8644,7 @@ DisplayWhiteClock(long timeRemaining, int highlight)
   hdc = GetDC(hwndMain);\r
   if (!IsIconic(hwndMain)) {\r
     DisplayAClock(hdc, timeRemaining, highlight, \r
-                       flipClock ? &blackRect : &whiteRect, "White", flag);\r
+                       flipClock ? &blackRect : &whiteRect, _("White"), flag);\r
   }\r
   if (highlight && iconCurrent == iconBlack) {\r
     iconCurrent = iconWhite;\r
@@ -8289,7 +8668,7 @@ DisplayBlackClock(long timeRemaining, int highlight)
   hdc = GetDC(hwndMain);\r
   if (!IsIconic(hwndMain)) {\r
     DisplayAClock(hdc, timeRemaining, highlight, \r
-                       flipClock ? &whiteRect : &blackRect, "Black", flag);\r
+                       flipClock ? &whiteRect : &blackRect, _("Black"), flag);\r
   }\r
   if (highlight && iconCurrent == iconWhite) {\r
     iconCurrent = iconBlack;\r
@@ -8337,7 +8716,7 @@ AutoSaveGame()
   f = OpenFileDialog(hwndMain, "a", defName,\r
                     appData.oldSaveStyle ? "gam" : "pgn",\r
                     GAME_FILT, \r
-                    "Save Game to File", NULL, fileTitle, NULL);\r
+                    _("Save Game to File"), NULL, fileTitle, NULL);\r
   if (f != NULL) {\r
     SaveGame(f, 0, "");\r
     fclose(f);\r
@@ -8641,9 +9020,9 @@ OpenTelnet(char *host, char *port, ProcRef *pr)
   char cmdLine[MSG_SIZ];\r
 \r
   if (port[0] == NULLCHAR) {\r
-    sprintf(cmdLine, "%s %s", appData.telnetProgram, host);\r
+    snprintf(cmdLine, MSG_SIZ, "%s %s", appData.telnetProgram, host);\r
   } else {\r
-    sprintf(cmdLine, "%s %s %s", appData.telnetProgram, host, port);\r
+    snprintf(cmdLine, MSG_SIZ, "%s %s %s", appData.telnetProgram, host, port);\r
   }\r
   return StartChildProcess(cmdLine, "", pr);\r
 }\r
@@ -8742,9 +9121,9 @@ OpenCommPort(char *name, ProcRef *pr)
   char fullname[MSG_SIZ];\r
 \r
   if (*name != '\\')\r
-    sprintf(fullname, "\\\\.\\%s", name);\r
+    snprintf(fullname, MSG_SIZ, "\\\\.\\%s", name);\r
   else\r
-    strcpy(fullname, name);\r
+    safeStrCpy(fullname, name, sizeof(fullname)/sizeof(fullname[0]) );\r
 \r
   h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,\r
                 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);\r
@@ -8776,7 +9155,7 @@ OpenCommPort(char *name, ProcRef *pr)
 int\r
 OpenLoopback(ProcRef *pr)\r
 {\r
-  DisplayFatalError("Not implemented", 0, 1);\r
+  DisplayFatalError(_("Not implemented"), 0, 1);\r
   return NO_ERROR;\r
 }\r
 \r
@@ -8914,7 +9293,7 @@ OpenRcmd(char* host, char* user, char* cmd, ProcRef* pr)
     break;\r
   }\r
   prevStderrPort = fromPort; // remember port used\r
-  sprintf(stderrPortStr, "%d", fromPort);\r
+  snprintf(stderrPortStr, MSG_SIZ, "%d", fromPort);\r
 \r
   if (send(s, stderrPortStr, strlen(stderrPortStr) + 1, 0) == SOCKET_ERROR) {\r
     err = WSAGetLastError();\r
@@ -9146,7 +9525,7 @@ void
 CmailSigHandlerCallBack(InputSourceRef isr, VOIDSTAR closure,\r
                        char *buf, int count, int error)\r
 {\r
-  DisplayFatalError("Not implemented", 0, 1);\r
+  DisplayFatalError(_("Not implemented"), 0, 1);\r
 }\r
 \r
 /* see wgamelist.c for Game List functions */\r