- // network.cpp 
-   
- #include "common.h" 
-   
-   
- // handy definitions 
- #define REACH_NEXT_FIELD_ELSE(field,action) \ 
- { \ 
-    while (*(field) && !iswspace (*(field))) (field)++; if (*(field) == 0) action; \ 
-    while (*(field) && iswspace (*(field))) (field)++; if (*(field) == 0) action; \ 
- } 
- #define LOCATE_REPLY_END_FROM_START(end,start) \ 
- { \ 
-    (end) = wcschr ((start), L'\n'); \ 
-    while (((end) != NULL) && ((end)[1] == L'\\')) (end) = wcschr (&(end)[1], L'\n'); /* find the first line feed that is NOT followed by a backslash */ \ 
-    if ((end) != NULL) (end)++; /* skip it */ \ 
-    else (end) = (start) + wcslen (start); \ 
- } 
- #define ERASE_FROM_TO(beginning,end) \ 
- { \ 
-    while ((((end) != NULL) && ((beginning) < (end))) || (*(beginning) != 0)) \ 
-       *(beginning) = L' ', (beginning)++; /* replace all the area with spaces */ \ 
- } 
- #define IS_FIELD_PRESENT(field_var,string) \ 
-    (((field_var) = wcsstr (player->recvbuffer, (string))) != NULL) /* this text is present */ 
- #define IS_FIELD_PRESENT_AT_BEGINNING_OF_LINE(field_var,string) \ 
-    ((((field_var) = wcsstr (player->recvbuffer, (string))) != NULL) /* this text is present... */ \ 
-     && (ReachBeginningOfCurrentLine (player->recvbuffer, (field_var)) == (field_var))) /* ... at the beginning of a line */ 
-   
-   
-   
- // prototypes of local functions 
- static void ReadNickname (wchar_t *nickname, size_t nickname_size, wchar_t *from_string); 
- static void ReadGamename (wchar_t *gamename, size_t gamename_size, wchar_t *from_string); 
- static void ReadSpannedLine (wchar_t *outstring, size_t outstring_size, wchar_t *multiline_string); 
-   
-   
- void EvaluateServerReply_MOTD (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    // is the MOTD already filled ? 
-    if (server_motd[0] != 0) 
-       return; // if so, this reply can't be a MOTD so just return 
-   
-    // read all received text as part of the MOTD 
-    wcscpy_s (server_motd, WCHAR_SIZEOF (server_motd), player->recvbuffer); 
-   
-    // do we want the MOTD to be displayed ? 
-    if (options.network.want_servermessages && options.network.want_motdonconnect) 
-       Window_MOTD (); // display MOTD window if required 
-   
-    Player_SendBuffer_Add (player, 1000, L"style 12\n"); // set the style 12 (computer-friendly board display) 
-    Player_SendBuffer_Add (player, 1000, L"who\n"); // send the players update request 
-    Player_SendBuffer_Add (player, 1000, L"sought all\n"); // send the sought games update request 
-    if (options.network.want_publicchat) 
-       Player_SendBuffer_Add (player, 1000, L"inchannel\n"); // send the chatter channels update request 
-   
-    return; // finished evaluating the MOTD 
- } 
-   
-   
- void EvaluateServerReply_Announcement (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    // Announcement format: 
-    //RECEIVED:[ 
-    // 
-    //    **ANNOUNCEMENT** from relay: FICS is relaying the 11th Delhi International 
-    //\   Open Category A 2013 - Round 3, the 24th Villa de Roquetas International  
-    //\   Open 2013 - Round 8 and the 18th Bosnjaci International Open 2013 - Round  
-    //\   5. To find more about Relay type "tell relay help" 
-    //] 
-   
-    static wchar_t announcement_text[1024]; 
-   
-    wchar_t *announcement_start; 
-    wchar_t *announcement_end; 
-    wchar_t *field_start; 
-   
-    // are both of the possible announcement headers NOT present ? 
-    if (!IS_FIELD_PRESENT (announcement_start, L"\n\n    **ANNOUNCEMENT** from ") 
-        && !IS_FIELD_PRESENT (announcement_start, L"\n\n    **UNREG ANNOUNCEMENT** from ")) 
-       return; // if so, this reply can't be an announcement notification so just return 
-   
-    // this reply is indeed an announcement ; find where it ends 
-    LOCATE_REPLY_END_FROM_START (announcement_end, announcement_start + 2); 
-   
-    // are we concerned about server messages ? 
-    if (options.network.want_servermessages) 
-    { 
-       field_start = wcsstr (announcement_start, L": "); // reach the first colon+space, that's where the announcement starts 
-       if (field_start != NULL) 
-       { 
-          field_start += 2; // skip colon and space 
-          ReadSpannedLine (announcement_text, WCHAR_SIZEOF (announcement_text), field_start); // now format the announcement text well 
-          Scene_AddAnnouncement (&the_scene, announcement_text); // and put it in place 
-       } 
-    } 
-   
-    // now erase the announcement from the recvbuffer, so that it cannot be misinterpreted by further parsing 
-    ERASE_FROM_TO (announcement_start, announcement_end); 
-   
-    return; // finished evaluating this announcement 
- } 
-   
-   
- void EvaluateServerReply_ChannelMessage (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    static wchar_t channelmessage_text[1024]; 
-   
-    wchar_t nickname[32]; 
-    wchar_t channelname[64]; 
-    unsigned long rgbx_color; 
-    wchar_t *channelmessage_start; 
-    wchar_t *channelmessage_end; 
-    wchar_t *field_start; 
-    wchar_t *field_stop; 
-    int channel_index; 
-    int channel_number; 
-   
-    // is the discriminatives bit for a channel message NOT present ? 
-    if (!IS_FIELD_PRESENT (field_stop, L"): ")) 
-       return; // if so, this reply can't be a channel message so just return 
-   
-    // reach beginning of current line 
-    channelmessage_start = ReachBeginningOfCurrentLine (player->recvbuffer, field_stop); 
-   
-    // locate the first space in line (i.e. where the message text is supposed to start) 
-    field_start = wcschr (channelmessage_start, L' '); 
-    if (field_start != NULL) 
-       field_start++; // skip it 
-   
-    // is the first space BEFORE the discirminative bit ? 
-    if (field_start < field_stop) 
-       return; // if so, this reply can't be a channel message so just return 
-   
-    while ((field_stop > player->recvbuffer) && (*field_stop != L'(')) 
-       field_stop--; // parse the string backwards to find the channel index 
-    if (*field_stop != L'(') 
-       return; // drop bogus replies 
-    channel_number = _wtoi (&field_stop[1]); // read channel number 
-    if (channel_number == 0) 
-       return; // if what's between the parentheses is not a number, this reply can't be a channel message so just return 
-   
-    // this reply is indeed a channel message ; find where it ends 
-    LOCATE_REPLY_END_FROM_START (channelmessage_end, channelmessage_start); 
-   
-    // are we concerned about channel messages ? 
-    if (options.network.want_publicchat) 
-    { 
-       ReadNickname (nickname, WCHAR_SIZEOF (nickname), channelmessage_start); // get the nickname 
-       ReadSpannedLine (channelmessage_text, WCHAR_SIZEOF (channelmessage_text), field_start); // get the message 
-   
-       channel_number = _wtoi (&field_stop[1]); // and read channel number 
-       for (channel_index = 0; channel_index < chatterchannel_count; channel_index++) 
-          if (chatterchannels[channel_index].id == channel_number) 
-          { 
-             if (chatterchannels[channel_index].theme[0] != 0) 
-             { 
-                wcscpy_s (channelname, WCHAR_SIZEOF (channelname), chatterchannels[channel_index].theme); 
-                rgbx_color = chatterchannels[channel_index].color; 
-             } 
-             break; // break as soon as we find the channel's name and copy it if it exists 
-          } 
-       if (channelname[0] == 0) 
-       { 
-          swprintf_s (channelname, WCHAR_SIZEOF (channelname), L"%s %d", LOCALIZE (L"ChatterChannels_ColumnChannelNumber"), channel_number); // if it hasn't been filled, use the number 
-          rgbx_color = RGBA_TO_RGBACOLOR (17, 181, 205, 0); // default channel color 
-       } 
-   
-       // add CC reply 
-       Scene_AddCCReply (&the_scene, nickname, channelname, rgbx_color, channelmessage_text); 
-    } 
-   
-    // now erase the channel message from the recvbuffer, so that it cannot be misinterpreted by further parsing 
-    ERASE_FROM_TO (channelmessage_start, channelmessage_end); 
-   
-    return; // finished evaluating this channel message 
- } 
-   
-   
- void EvaluateServerReply_PrivateMessage (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    static wchar_t privatemessage_text[16384]; 
-   
-    wchar_t nickname[32]; 
-    wchar_t *privatemessage_start; 
-    wchar_t *privatemessage_end; 
-    wchar_t *field_start; 
-    wchar_t *first_space; 
-    int player_index; 
-   
-    // is the private message discriminative bit NOT present ? 
-    // private messages have " tells you: " just after the first space 
-    if (!IS_FIELD_PRESENT (field_start, L" tells you: ")) 
-       return; // if so, this reply can't be a private message so just return 
-   
-    // look up for the last line feed and the first space of the line 
-    privatemessage_start = ReachBeginningOfCurrentLine (player->recvbuffer, field_start); 
-    first_space = wcschr (privatemessage_start, L' '); 
-   
-    // is the first space BEFORE the discirminative bit ? 
-    if ((first_space != NULL) && (first_space < field_start)) 
-       return; // if so, this reply can't be a channel message so just return 
-   
-    // this reply is indeed a private message ; find where it ends 
-    LOCATE_REPLY_END_FROM_START (privatemessage_end, privatemessage_start + 2); 
-   
-    ReadNickname (nickname, WCHAR_SIZEOF (nickname), privatemessage_start); // get username 
-   
-    // is it NOT RoboAdmin OR do we care about server messages ? 
-    if ((_wcsicmp (L"ROBOadmin", nickname) != 0) || options.network.want_servermessages) 
-    { 
-       // see if this nickname exists in the list of connected players ; if not, refresh list 
-       for (player_index = 0; player_index < onlineplayer_count; player_index++) 
-          if (wcscmp (nickname, onlineplayers[player_index].nickname) == 0) 
-             break; // break as soon as we find it 
-   
-       // have we NOT found it ? 
-       if (player_index == onlineplayer_count) 
-          Player_SendBuffer_Add (player, 1000, L"who\n"); // if so, request a players list refresh 
-   
-       field_start += 12; // skip string break and reach the next colon, that's where the PM starts 
-       ReadSpannedLine (privatemessage_text, WCHAR_SIZEOF (privatemessage_text), field_start); // now format the PM text well 
-   
-       // find or create our interlocutor structure and append the chat text in it 
-       Interlocutor_Chat (Interlocutor_FindOrCreate (nickname), nickname, false, privatemessage_text); 
-    } 
-   
-    // now erase the private message from the recvbuffer, so that it cannot be misinterpreted by further parsing 
-    ERASE_FROM_TO (privatemessage_start, privatemessage_end); 
-   
-    return; // finished evaluating this private message 
- } 
-   
-   
- void EvaluateServerReply_Finger (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    wchar_t line_buffer[256]; 
-    wchar_t month_str[5]; 
-    wchar_t dayhrminsec[8]; 
-    wchar_t nickname[32]; 
-    gamestylerating_t gs; 
-    playercard_t *playercard; 
-    player_t *local_player; 
-    wchar_t *finger_start; 
-    wchar_t *finger_end; 
-    wchar_t *field_start; 
-    wchar_t *fingerdata_start; 
-    wchar_t *string_pointer; 
-    int onlineplayer_index; 
-    int days; 
-    int hours; 
-    int minutes; 
-    int number; 
-   
-    // is it a NEGATIVE finger reply ? "'gue$tpm' is not a valid handle." 
-    if (IS_FIELD_PRESENT (field_start, L"' is not a valid handle.")) 
-    { 
-       field_start = ReachBeginningOfCurrentLine (player->recvbuffer, field_start); // look up for the last line feed 
-       if (field_start[0] == L'\'') // first character must be an apostrophe 
-       { 
-          ReadNickname (nickname, WCHAR_SIZEOF (nickname), &field_start[1]); // read username 
-   
-          // test again with the complete finger reply header. Are we SURE it is a finger reply ? 
-          if (wcscmp (&field_start[1 + wcslen (nickname)], L"' is not a valid handle.") == 0) 
-          { 
-             // find or create our player card structure 
-             playercard = PlayerCard_FindOrCreate (nickname); 
-             playercard->doesnt_exist = true; // mark it as non-existing 
-             playercard->update_dialog = true; // and tell the dialog to update itself 
-   
-             return; // finished evaluating this finger reply 
-          } 
-       } 
-    } 
-   
-    // is it a NEGATIVE finger reply ? "Ambiguous name guestp:" 
-    if (IS_FIELD_PRESENT_AT_BEGINNING_OF_LINE (field_start, L"Ambiguous name ")) 
-    { 
-       ReadNickname (nickname, WCHAR_SIZEOF (nickname), &field_start[15]); // read username 
-   
-       // find or create our player card structure 
-       playercard = PlayerCard_FindOrCreate (nickname); 
-       playercard->doesnt_exist = true; // mark it as non-existing 
-       playercard->update_dialog = true; // and tell the dialog to update itself 
-   
-       return; // finished evaluating this finger reply 
-    } 
-   
-    // is it a NEGATIVE finger reply ? "There is no player matching the name guestpm." 
-    if (IS_FIELD_PRESENT_AT_BEGINNING_OF_LINE (field_start, L"There is no player matching the name ")) 
-    { 
-       ReadNickname (nickname, WCHAR_SIZEOF (nickname), &field_start[37]); // read username 
-   
-       // find or create our player card structure 
-       playercard = PlayerCard_FindOrCreate (nickname); 
-       playercard->doesnt_exist = true; // mark it as non-existing 
-       playercard->update_dialog = true; // and tell the dialog to update itself 
-   
-       return; // finished evaluating this finger reply 
-    } 
-   
-    // is it a POSITIVE finger reply ? "Finger of guestpm:" 
-    if (IS_FIELD_PRESENT_AT_BEGINNING_OF_LINE (field_start, L"Finger of ")) 
-    { 
-       ReadNickname (nickname, WCHAR_SIZEOF (nickname), &field_start[10]); // get username 
-   
-       // find or create our player card structure 
-       playercard = PlayerCard_FindOrCreate (nickname); 
-       playercard->got_reply = true; // remember it has actual data 
-   
-       // when we have it, update username 
-       wcscpy_s (playercard->nickname, WCHAR_SIZEOF (playercard->nickname), nickname); 
-   
-       // find local player and see whether this player card is ours 
-       local_player = Player_FindByType (PLAYER_HUMAN); 
-       if ((local_player != NULL) && (_wcsicmp (nickname, local_player->name) == 0)) 
-          playercard->is_own = true; // remember this player card is ours 
-   
-       /////////////////////////////////////////////////////////////////////// 
-       // Finger: parse the connection data (On for: n days n hours n minutes) 
-   
-       // reach the next double line-break, that's where connection data starts 
-       fingerdata_start = wcsstr (field_start, L"\n\n"); 
-       if (fingerdata_start == NULL) 
-          return; // nothing in finger reply ; reply evaluated 
-   
-       fingerdata_start += 2; // skip them 
-       wcsgets (line_buffer, WCHAR_SIZEOF (line_buffer), fingerdata_start); // copy that line into a buffer for easy parsing 
-   
-       // is the player currently online ? 
-       if (wcsncmp (line_buffer, L"On for: ", 8) == 0) 
-       { 
-          field_start = &line_buffer[8]; // skip the "On for: " text 
-   
-          // according to the data presentation, read and convert in the right form 
-          if (swscanf_s (field_start, L"%d %*s %d %*s %d %*s Idle: %d %s", &days, &hours, &minutes, &number, dayhrminsec, WCHAR_SIZEOF (dayhrminsec)) == 5) 
-          { 
-             playercard->minutes_online = (days * 60 * 24) + (hours * 60) + minutes; // days, hours, minutes 
-             playercard->seconds_idle = number * (dayhrminsec[0] == L'd' ? 60 * 60 * 24 : (dayhrminsec[0] == L'h' ? 60 * 60 : (dayhrminsec[0] == L'm' ? 60 : 1))); 
-          } 
-          else if (swscanf_s (field_start, L"%d %*s %d %*s Idle: %d %s", &hours, &minutes, &number, dayhrminsec, WCHAR_SIZEOF (dayhrminsec)) == 4) 
-          { 
-             playercard->minutes_online = (hours * 60) + minutes; // hours, minutes 
-             playercard->seconds_idle = number * (dayhrminsec[0] == L'd' ? 60 * 60 * 24 : (dayhrminsec[0] == L'h' ? 60 * 60 : (dayhrminsec[0] == L'm' ? 60 : 1))); 
-          } 
-          else if (swscanf_s (field_start, L"%*d secs Idle: %d %s", &number, dayhrminsec, WCHAR_SIZEOF (dayhrminsec)) == 2) 
-          { 
-             playercard->minutes_online = 1; // less than one minute, round to 1 
-             playercard->seconds_idle = number * (dayhrminsec[0] == L'd' ? 60 * 60 * 24 : (dayhrminsec[0] == L'h' ? 60 * 60 : (dayhrminsec[0] == L'm' ? 60 : 1))); 
-          } 
-          else if (swscanf_s (field_start, L"%d %*s Idle: %d %s", &minutes, &number, dayhrminsec, WCHAR_SIZEOF (dayhrminsec)) == 3) 
-          { 
-             playercard->minutes_online = minutes; // just minutes 
-             playercard->seconds_idle = number * (dayhrminsec[0] == L'd' ? 60 * 60 * 24 : (dayhrminsec[0] == L'h' ? 60 * 60 : (dayhrminsec[0] == L'm' ? 60 : 1))); 
-          } 
-   
-          playercard->update_dialog = true; // remember to update dialog 
-       } 
-   
-       // else has player already disconnected ? 
-       else if (wcsncmp (line_buffer, L"Last disconnected: ", 19) == 0) 
-       { 
-          field_start = &line_buffer[19]; // skip the "Last disconnected: " text 
-   
-          // read and convert the data in the right form 
-          if (swscanf_s (field_start, L"%*s %s %d, %*d:%*d %*s %d", month_str, WCHAR_SIZEOF (month_str), &days, &number) == 3) 
-          { 
-             playercard->disconnection_day = days; // reuse the "days" variable, which is an int, and we need an int in swscanf_s() 
-             playercard->disconnection_month = MonthStringToNumber (month_str); // convert month from string to number 
-             playercard->disconnection_year = number; // reuse the "number" variable, which is an int, and we need an int in swscanf_s() 
-          } 
-   
-          playercard->update_dialog = true; // remember to update dialog 
-       } 
-   
-       // else player has never connected 
-       else 
-       { 
-          playercard->disconnection_day = 0; // 0 everywhere means the player has never connected 
-          playercard->disconnection_month = 0; 
-          playercard->disconnection_year = 0; 
-   
-          playercard->update_dialog = true; // remember to update dialog 
-       } 
-   
-       //////////////////////////////////////////////////////////// 
-       // Finger: parse the status data (playing game N:aaa vs bbb) 
-   
-       // see if this player is currently playing a game 
-       field_start = wcsstr (fingerdata_start, L"\n(playing game "); 
-       if (field_start != NULL) 
-       { 
-          field_start += 15; // skip the "\n(playing game " substring 
-   
-          // update player activity in opponents list 
-          for (onlineplayer_index = 0; onlineplayer_index < onlineplayer_count; onlineplayer_index++) 
-             if (wcscmp (onlineplayers[onlineplayer_index].nickname, playercard->nickname) == 0) 
-             { 
-                // if this player is NOT involved in a tournament... 
-                if (onlineplayers[onlineplayer_index].handlestatus != HANDLESTATUS_INTOURNAMENT) 
-                { 
-                   if (playercard->seconds_idle < 5 * 60) 
-                      onlineplayers[onlineplayer_index].handlestatus = HANDLESTATUS_INGAME; // update status (playing) 
-                   else 
-                      onlineplayers[onlineplayer_index].handlestatus = HANDLESTATUS_INACTIVEORBUSY; // update status (idle) 
-                } 
-                break; // stop searching as soon as player is found 
-             } 
-   
-          // scan the game number and name 
-          swscanf_s (field_start, L"%d: %[^)]", &playercard->game_played, playercard->game_name, WCHAR_SIZEOF (playercard->game_name)); 
-          playercard->update_dialog = true; // remember to update dialog 
-       } 
-   
-       // see if this player is currently playing a game 
-       field_start = wcsstr (fingerdata_start, L"\n(examining game "); 
-       if (field_start != NULL) 
-       { 
-          field_start += 17; // skip the "\n(examining game " substring 
-   
-          // update player activity in opponents list 
-          for (onlineplayer_index = 0; onlineplayer_index < onlineplayer_count; onlineplayer_index++) 
-             if (wcscmp (onlineplayers[onlineplayer_index].nickname, playercard->nickname) == 0) 
-             { 
-                // if this player is NOT involved in a tournament... 
-                if (onlineplayers[onlineplayer_index].handlestatus != HANDLESTATUS_INTOURNAMENT) 
-                { 
-                   if (playercards->seconds_idle < 5 * 60) 
-                      onlineplayers[onlineplayer_index].handlestatus = HANDLESTATUS_EXAMININGAGAME; // update status (studying) 
-                   else 
-                      onlineplayers[onlineplayer_index].handlestatus = HANDLESTATUS_INACTIVEORBUSY; // update status (idle) 
-                } 
-                break; // stop searching as soon as player is found 
-             } 
-   
-          // scan the game number and name 
-          swscanf_s (field_start, L"%d: %[^)]", &playercard->game_played, playercard->game_name, WCHAR_SIZEOF (playercard->game_name)); 
-          playercard->update_dialog = true; // remember to update dialog 
-       } 
-   
-       ////////////////////////////////////////// 
-       // Finger: parse the game statistics array 
-   
-       // see if this player has game statistics, find it and jump to the first line 
-       field_start = wcsstr (fingerdata_start, L"          rating     RD      win    loss    draw   total   best\n"); 
-       if (field_start != NULL) 
-       { 
-          field_start += 64; // skip the rating table headers and its carriage return 
-   
-          // game statistics start here. Read line per line... 
-          string_pointer = field_start; // start at the beginning of the line 
-          while ((string_pointer = wcsgets (line_buffer, WCHAR_SIZEOF (line_buffer), string_pointer)) != NULL) 
-          { 
-             if (line_buffer[0] == 0) 
-                continue; // skip empty lines 
-   
-             memset (&gs, 0, sizeof (gs)); // reset all statistics we're about to read 
-   
-             // does it look like a valid game statistics line ? 
-             if ((swscanf_s (line_buffer, L"%s %d %f %d %d %d %d", gs.name, WCHAR_SIZEOF (gs.name), &gs.rating, &gs.rd, &gs.win_count, &gs.loss_count, &gs.draw_count, &gs.total_matches) == 7) 
-                 || (swscanf_s (line_buffer, L"%s ---- %f %d %d %d %d", gs.name, WCHAR_SIZEOF (gs.name), &gs.rd, &gs.win_count, &gs.loss_count, &gs.draw_count, &gs.total_matches) == 6)) 
-             { 
-                // reallocate space to hold one game style rating more in this player card 
-                playercard->gamestyleratings = (gamestylerating_t *) SAFE_realloc (playercard->gamestyleratings, playercard->gamestylerating_count, playercard->gamestylerating_count + 1, sizeof (gamestylerating_t), false); 
-                memcpy (&playercard->gamestyleratings[playercard->gamestylerating_count], &gs, sizeof (gamestylerating_t)); // copy data 
-                playercard->gamestylerating_count++; // we know now one game style rating more for this player card 
-             } 
-          } 
-   
-          playercard->update_dialog = true; // remember to update dialog 
-       } 
-   
-       ////////////////////////////////////// 
-       // Finger: parse the personal messages 
-   
-       // see if this player has personal data, find it and jump to the first line 
-       field_start = wcsstr (fingerdata_start, L"\n\n 1:"); 
-       if (field_start != NULL) 
-       { 
-          field_start += 2; // skip the two carriage returns 
-          finger_start = field_start; 
-   
-          // personal data starts here. Read line per line... 
-          string_pointer = field_start; // start at the beginning of the line 
-          while ((string_pointer = wcsgets (line_buffer, WCHAR_SIZEOF (line_buffer), string_pointer)) != NULL) 
-          { 
-             if (line_buffer[0] == 0) 
-                continue; // skip empty lines 
-   
-             // does it look like a valid personal finger data line ? 
-             if ((wcslen (line_buffer) > 4) && (wcsncmp (&line_buffer[2], L": ", 2) == 0)) 
-                PlayerCard_AppendPersonalData (playercard, &line_buffer[4]); // if so, append it to player's personal data 
-             else if (wcsncmp (line_buffer, L"\\   ", 3) == 0) 
-             { 
-                if (playercard->fingertext_length > 1) 
-                { 
-                   playercard->fingertext[playercard->fingertext_length - 1] = 0; // chop off the last carriage return in finger text 
-                   playercard->fingertext_length--; // UGLY: now size no longer reflects allocated space 
-                } 
-                PlayerCard_AppendPersonalData (playercard, &line_buffer[3]); // ...and append it to player's personal data 
-             } 
-          } 
-   
-          // now erase that text from the recvbuffer, so that it cannot be misinterpreted by further parsing 
-          LOCATE_REPLY_END_FROM_START (finger_end, finger_start); 
-          ERASE_FROM_TO (finger_start, finger_end); 
-   
-          playercard->update_dialog = true; // remember to update dialog 
-       } 
-   
-       return; // finished evaluating this finger reply 
-    } 
-   
-    return; // this was not a reply we could be concerned about 
- } 
-   
-   
- void EvaluateServerReply_Seek (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    wchar_t *field_start; 
-   
-    // are the two bits of the seek notification sentence not present ? 
-    if (!IS_FIELD_PRESENT (field_start, L") seeking ") || !IS_FIELD_PRESENT (field_start, L"\" to respond)")) 
-       return; // if so, this reply can't be a seek notification so just return 
-   
-    // only refresh the sought games list if we're displaying it 
-    if (IsWindow (hSoughtWnd) && (lastsought_time + 5.0f < current_time)) 
-       Player_SendBuffer_Add (player, 1000, L"sought all\n"); // send the sought games update request 
-   
-    return; // finished evaluating this seek notification 
- } 
-   
-   
- void EvaluateServerReply_Challenge (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    challenge_t chal; 
-    wchar_t *field_start; 
-   
-    // is the challenge line header NOT present ? 
-    if (!IS_FIELD_PRESENT_AT_BEGINNING_OF_LINE (field_start, L"Challenge: ")) 
-       return; // if so, this reply can't be a challenge notification so just return 
-   
-    // challenges can appear as 
-    // Challenge: IOEO (1370) pmbaty (----) unrated blitz 2 12. 
-    // Challenge: IOEO (1370) [black] pmbaty (----) unrated blitz 2 12. 
-   
-    field_start += 11; // skip the "Challenge: " substring 
-    ReadNickname (chal.challenger, WCHAR_SIZEOF (chal.challenger), field_start); // read challenger nickname 
-    REACH_NEXT_FIELD_ELSE (field_start, return); 
-    if (*field_start == L'(') 
-       field_start++; // skip the leading parenthesis 
-    if ((*field_start != L'-') && (*field_start != L'+')) 
-       chal.challenger_level = _wtoi (field_start); // read player rating 
-    else 
-       chal.challenger_level = 0; 
-    REACH_NEXT_FIELD_ELSE (field_start, return); 
-    if (_wcsnicmp (field_start, L"[black]", 7) == 0) 
-    { 
-       chal.color = COLOR_BLACK; // opponent wants to play black 
-       REACH_NEXT_FIELD_ELSE (field_start, return); 
-    } 
-    else if (_wcsnicmp (field_start, L"[white]", 7) == 0) 
-    { 
-       chal.color = COLOR_WHITE; // opponent wants to play white 
-       REACH_NEXT_FIELD_ELSE (field_start, return); 
-    } 
-    else 
-       chal.color = COLOR_UNSPECIFIED; // opponent has no preference over which color he wants to play 
-    REACH_NEXT_FIELD_ELSE (field_start, return); // skip our name 
-    REACH_NEXT_FIELD_ELSE (field_start, return); // skip our rating    
-    chal.is_rated = (*field_start == L'r' ? true : false); // read whether it is rated or not 
-    REACH_NEXT_FIELD_ELSE (field_start, return); 
-    ReadGamename (chal.game_type, WCHAR_SIZEOF (chal.game_type), field_start); // read game type 
-    REACH_NEXT_FIELD_ELSE (field_start, return); 
-    chal.initial_time = (float) _wtoi (field_start); // read initial time 
-    REACH_NEXT_FIELD_ELSE (field_start, return); 
-    chal.increment = (float) _wtoi (field_start); // read increment 
-   
-    // is this variant unsupported ? 
-    if ((_wcsicmp (chal.game_type, L"untimed") != 0) && (_wcsicmp (chal.game_type, L"standard") != 0) 
-        && (_wcsicmp (chal.game_type, L"blitz") != 0) && (_wcsicmp (chal.game_type, L"lightning") != 0) 
-       /* && (_wcsicmp (chal.game_type, L"losers") != 0) && (_wcsicmp (chal.game_type, L"atomic") != 0)*/) 
-       Player_SendBuffer_Add (player, 1000, L"decline %s\n", chal.challenger); // automatically decline all unsupported games 
-    else 
-    { 
-       // supported variant. Display a message box for the user to choose whether to accept or decline. 
-   
-       // request a player list update before displaying the dialog box 
-       if (lastonlineplayers_time + 5.0f < current_time) 
-          Player_SendBuffer_Add (player, 1000, L"who\n"); 
-   
-       // print a notification in this player's chat window 
-       Interlocutor_Notify (Interlocutor_FindOrCreate (chal.challenger), LOCALIZE (L"Chat_InvitationReceived"), chal.challenger); 
-   
-       // find or create our challenge structure and update its data 
-       Challenge_UpdateData (Challenge_FindOrCreate (chal.challenger), &chal); 
-    } 
-   
-    return; // finished evaluating this challenge notification 
- } 
-   
-   
- void EvaluateServerReply_ChallengeAccepted (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    wchar_t nickname[32]; 
-    wchar_t *line_start; 
-    wchar_t *field_start; 
-    interlocutor_t *interlocutor; 
-    int player_index; 
-   
-    // is the challenge accepted notification discriminative bit NOT present ? 
-    if (!IS_FIELD_PRESENT (field_start, L" accepts the match offer.")) 
-       return; // if so, this reply can't be a challenge reply notification so just return 
-   
-    line_start = ReachBeginningOfCurrentLine (player->recvbuffer, field_start); // look up for the last line feed 
-    ReadNickname (nickname, WCHAR_SIZEOF (nickname), line_start); // get username 
-   
-    // see if this nickname exists in the list of connected players ; if not, refresh list 
-    for (player_index = 0; player_index < onlineplayer_count; player_index++) 
-       if (wcscmp (nickname, onlineplayers[player_index].nickname) == 0) 
-          break; // break as soon as we find it 
-   
-    // have we NOT found it ? 
-    if (player_index == onlineplayer_count) 
-       Player_SendBuffer_Add (player, 1000, L"who\n"); // if so, request a players list refresh 
-   
-    // send a notification to this player's chat window 
-    interlocutor = Interlocutor_FindOrCreate (nickname); 
-    Interlocutor_Notify (interlocutor, LOCALIZE (L"Chat_InvitationAcceptedByOther"), nickname); 
-    if (IsWindow (interlocutor->hWnd)) 
-       ShowWindow (interlocutor->hWnd, SW_MINIMIZE); // minimize chat window immediately 
-   
-    // remember the game rules 
-    wcscpy_s (the_board.game_rules, WCHAR_SIZEOF (the_board.game_rules), L"standard"); // TODO: support other game rules 
-   
-    return; // finished evaluating this challenge reply notification 
- } 
-   
-   
- void EvaluateServerReply_ChallengeDeclined (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    wchar_t nickname[32]; 
-    wchar_t *line_start; 
-    wchar_t *field_start; 
-    int player_index; 
-   
-    // is the challenge declined notification discriminative bit NOT present ? 
-    if (!IS_FIELD_PRESENT (field_start, L" declines the match offer.")) 
-       return; // if so, this reply can't be a challenge reply notification so just return 
-   
-    line_start = ReachBeginningOfCurrentLine (player->recvbuffer, field_start); // look up for the last line feed 
-    ReadNickname (nickname, WCHAR_SIZEOF (nickname), line_start); // get username 
-   
-    // see if this nickname exists in the list of connected players ; if not, refresh list 
-    for (player_index = 0; player_index < onlineplayer_count; player_index++) 
-       if (wcscmp (nickname, onlineplayers[player_index].nickname) == 0) 
-          break; // break as soon as we find it 
-   
-    // have we NOT found it ? 
-    if (player_index == onlineplayer_count) 
-       Player_SendBuffer_Add (player, 1000, L"who\n"); // if so, request a players list refresh 
-   
-    // send a notification to this player's chat window 
-    Interlocutor_Notify (Interlocutor_FindOrCreate (nickname), LOCALIZE (L"Chat_InvitationDeclinedByOther"), nickname); 
-   
-    return; // finished evaluating this challenge reply notification 
- } 
-   
-   
- void EvaluateServerReply_Takeback (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    wchar_t *field_start; 
-    wchar_t *field_stop; 
-    int howmany_halfmoves; 
-   
-    // is the challenge declined notification discriminative bit NOT present ? 
-    if (!IS_FIELD_PRESENT (field_start, L" would like to take back ") || !IS_FIELD_PRESENT (field_stop, L" half move(s)")) 
-       return; // if so, this reply can't be a challenge reply notification so just return 
-   
-    // read the numbre of half moves the opponent reclaims 
-    swscanf_s (field_start, L" would like to take back %d ", &howmany_halfmoves); 
-   
-    // send a notification to the local player's chat window 
-    Interlocutor_Notify (Interlocutor_FindOrCreate (player->name), LOCALIZE (L"Chat_TakebackRequestReceived"), player->name, howmany_halfmoves); 
-    DialogBox_Takeback (howmany_halfmoves); // and fire up a modal dialog box to ask confirmation to the local player 
-   
-    return; // finished evaluating this challenge reply notification 
- } 
-   
-   
- void EvaluateServerReply_TakebackDeclinedByOther (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    wchar_t *field_start; 
-   
-    // is the takeback declined notification discriminative bit NOT present ? 
-    if (!IS_FIELD_PRESENT (field_start, L" declines the takeback request.")) 
-       return; // if so, this reply can't be a takeback reply notification so just return 
-   
-    // send a notification to the local player's chat window 
-    Interlocutor_Notify (Interlocutor_FindOrCreate (player->name), LOCALIZE (L"Chat_TakebackRefused"), player->name); 
-   
-    return; // finished evaluating this challenge reply notification 
- } 
-   
-   
- void EvaluateServerReply_TakebackDeclinedByYou (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    wchar_t *field_start; 
-   
-    // is the takeback declined notification discriminative bit NOT present ? 
-    if (!IS_FIELD_PRESENT_AT_BEGINNING_OF_LINE (field_start, L"You decline the takeback request from ")) 
-       return; // if so, this reply can't be a takeback reply notification so just return 
-   
-    // send a notification to the local player's chat window 
-    Interlocutor_Notify (Interlocutor_FindOrCreate (player->name), LOCALIZE (L"Chat_TakebackRefused"), player->name); 
-   
-    return; // finished evaluating this challenge reply notification 
- } 
-   
-   
- void EvaluateServerReply_PlayNotAllowed (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    wchar_t *field_start; 
-   
-    // is the play reply notification discriminative bit NOT present ? 
-    if (!IS_FIELD_PRESENT_AT_BEGINNING_OF_LINE (field_start, L"Only registered players can play rated games.")) 
-       return; // if so, this reply can't be a play reply notification so just return 
-   
-    // display a message box for the player to know that his opponent refuses to play 
-    messagebox.hWndParent = (IsWindow (hSoughtWnd) ? hSoughtWnd : hMainWnd); 
-    wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"PlayReply_ServerReply")); 
-    swprintf_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"PlayReply_OnlyRegisteredCanPlayRated")); 
-    messagebox.flags = MB_ICONINFORMATION | MB_OK; 
-    DialogBox_Message (&messagebox); // display a modeless error message box 
-   
-    return; // finished evaluating this play reply notification 
- } 
-   
-   
- void EvaluateServerReply_PlayUnexistent (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    wchar_t *field_start; 
-   
-    // is the play reply notification discriminative bit NOT present ? 
-    if (!IS_FIELD_PRESENT_AT_BEGINNING_OF_LINE (field_start, L"That seek is not available.")) 
-       return; // if so, this reply can't be a play reply so just return 
-   
-    // display a message box for the player to know that his opponent refuses to play 
-    messagebox.hWndParent = (IsWindow (hSoughtWnd) ? hSoughtWnd : hMainWnd); 
-    wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"PlayReply_ServerReply")); 
-    swprintf_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"PlayReply_UnexistentSeek")); 
-    messagebox.flags = MB_ICONINFORMATION | MB_OK; 
-    DialogBox_Message (&messagebox); // display a modeless error message box 
-   
-    return; // finished evaluating this play reply notification 
- } 
-   
-   
- void EvaluateServerReply_PlayWrongRating (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    wchar_t *field_start; 
-   
-    // is the play reply notification discriminative bit NOT present ? 
-    if (!IS_FIELD_PRESENT_AT_BEGINNING_OF_LINE (field_start, L"Your rating does not qualify for this seek.")) 
-       return; // if so, this reply can't be a play reply so just return 
-   
-    // display a message box for the player to know that his opponent refuses to play 
-    messagebox.hWndParent = (IsWindow (hSoughtWnd) ? hSoughtWnd : hMainWnd); 
-    wcscpy_s (messagebox.title, WCHAR_SIZEOF (messagebox.title), LOCALIZE (L"PlayReply_ServerReply")); 
-    swprintf_s (messagebox.text, WCHAR_SIZEOF (messagebox.text), LOCALIZE (L"PlayReply_WrongRating")); 
-    messagebox.flags = MB_ICONINFORMATION | MB_OK; 
-    DialogBox_Message (&messagebox); // display a modeless error message box 
-   
-    return; // finished evaluating this play reply notification 
- } 
-   
-   
- void EvaluateServerReply_ChannelsAndMembers (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    chatterchannel_t cc; 
-    wchar_t *field_start; 
-    wchar_t *field_stop; 
-    wchar_t *string_pointer; 
-    wchar_t *big_buffer; 
-    player_t *local_player; 
-    int previouslyselected_channelid; 
-    int naturallanguagechannel_index; 
-    int chatterchannel_index; 
-    int cctheme_length; 
-    int char_index; 
-   
-    // is the channels and members header bit NOT present ? 
-    if (!IS_FIELD_PRESENT_AT_BEGINNING_OF_LINE (field_start, L"Channel ")) 
-       return; // if so, this reply can't be a channels and members list so just return 
-   
-    local_player = Player_FindByType (PLAYER_HUMAN); // get a pointer to the human player 
-   
-    // is a chatter channel selected ? 
-    if (selected_chatterchannel != NULL) 
-       previouslyselected_channelid = selected_chatterchannel->id; // save its ID 
-    else 
-       previouslyselected_channelid = -1; // -1 will instruct us to select a default chatter channel 
-   
-    // for each chatter channel we know already... 
-    for (chatterchannel_index = 0; chatterchannel_index < chatterchannel_count; chatterchannel_index++) 
-    { 
-       SAFE_free ((void **) &chatterchannels[chatterchannel_index].members); // for each channel, free its members array 
-       chatterchannels[chatterchannel_index].member_count = 0; 
-    } 
-    SAFE_free ((void **) &chatterchannels); // free the chatter channel list we know 
-    chatterchannel_count = 0; // reset the chatter channel count 
-    naturallanguagechannel_index = -1; // reset the natural language channel index 
-   
-    // linearize the string 
-    string_pointer = field_start; 
-    while (string_pointer[1] != 0) 
-    { 
-       if ((string_pointer[0] == L'\n') && (string_pointer[1] == L'\\')) 
-          string_pointer[0] = string_pointer[1] = L' '; // replace every newline followed by a backslash by two spaces 
-       string_pointer++; 
-    } 
-   
-    // mallocate space for a big buffer 
-    big_buffer = (wchar_t *) SAFE_malloc (1024 * 1024, sizeof (wchar_t), false); 
-   
-    // read line per line 
-    string_pointer = ReachBeginningOfCurrentLine (player->recvbuffer, field_start); // start at the first character 
-    while ((string_pointer = wcsgets (big_buffer, 1024 * 1024, string_pointer)) != NULL) 
-    { 
-       if (big_buffer[0] == 0) 
-          break; // if it's an empty line, then the channel list is finished 
-   
-       // now parse the chatter channel data 
-       field_start = big_buffer; 
-       memset (&cc, 0, sizeof (cc)); 
-   
-       REACH_NEXT_FIELD_ELSE (field_start, continue); 
-       cc.id = _wtoi (field_start); // read chatter channel id 
-   
-       REACH_NEXT_FIELD_ELSE (field_start, continue); 
-       if (*field_start == L'"') 
-       { 
-          field_start++; // skip the quote 
-          field_stop = field_start; 
-          while (*field_stop && (*field_stop != L'"')) 
-             field_stop++; // reach the next quote 
-          if (*field_stop == 0) 
-             continue; // discard bogus lines 
-          *field_stop = 0; // break the string here 
-          wcscpy_s (cc.theme, WCHAR_SIZEOF (cc.theme), field_start); // copy theme 
-          cctheme_length = wcslen (cc.theme); 
-          for (char_index = 0; char_index < cctheme_length; char_index++) 
-             if (cc.theme[char_index] == L'_') 
-                cc.theme[char_index] = L' '; // convert underscores to spaces 
-          if (_wcsicmp (cc.theme, languages[language_id].name) == 0) 
-             naturallanguagechannel_index = chatterchannel_count; // if this channel is the natural language one, remember it 
-          field_start = field_stop + 1; // and continue reading the string 
-          REACH_NEXT_FIELD_ELSE (field_start, continue); 
-       } 
-   
-       // determine the channel color according to channel ID 
-       srand (1000 + cc.id); 
-       cc.color = RGBA_TO_RGBACOLOR (rand () % 256, rand () % 256, rand () % 256, 0xff); 
-   
-       // now read the channel members 
-       cc.members = NULL; 
-       cc.member_count = 0; 
-       while (*field_start != 0) 
-       { 
-          cc.members = (chatterchannelmember_t *) SAFE_realloc (cc.members, cc.member_count, cc.member_count + 1, sizeof (chatterchannelmember_t), false); 
-          if (*field_start == '{') 
-          { 
-             ReadNickname (cc.members[cc.member_count].nickname, WCHAR_SIZEOF (cc.members[cc.member_count].nickname), &field_start[1]); 
-             cc.members[cc.member_count].is_silenced = true; // this player plays in silence 
-          } 
-          else 
-          { 
-             ReadNickname (cc.members[cc.member_count].nickname, WCHAR_SIZEOF (cc.members[cc.member_count].nickname), field_start); 
-             cc.members[cc.member_count].is_silenced = false; // this player allows us to talk to him 
-          } 
-          if (wcscmp (cc.members[cc.member_count].nickname, local_player->name) == 0) 
-             cc.is_open = true; // if we are on this channel, mark this channel as open 
-          cc.member_count++; // reallocate, read player nickname and increase chatter channel members array size 
-          REACH_NEXT_FIELD_ELSE (field_start, continue); // and advance one player more 
-       } 
-   
-       // all parsing was OK, reallocate chatter channels list to have one channel more 
-       chatterchannels = (chatterchannel_t *) SAFE_realloc (chatterchannels, chatterchannel_count, chatterchannel_count + 1, sizeof (chatterchannel_t), true); 
-       memcpy (&chatterchannels[chatterchannel_count], &cc, sizeof (chatterchannel_t)); // now save data 
-       chatterchannel_count++; // we know now one sought game more 
-    } 
-   
-    // free the big buffer space we used 
-    SAFE_free ((void **) &big_buffer); 
-   
-    // now that the chatter channels are read, find again the one that was previously selected 
-   
-    // if no chatter channel is selected yet, and we have a natural language channel exists and this channel is not open yet... 
-    if ((previouslyselected_channelid == -1) && (naturallanguagechannel_index != -1) && !chatterchannels[naturallanguagechannel_index].is_open) 
-       Player_SendBuffer_Add (player, 1000, L"+channel %d\n", chatterchannels[naturallanguagechannel_index].id); // open this channel 
-   
-    // cycle through all the chatter channels we know... 
-    for (chatterchannel_index = 0; chatterchannel_index < chatterchannel_count; chatterchannel_index++) 
-       if ((previouslyselected_channelid != -1) && (chatterchannels[chatterchannel_index].id == previouslyselected_channelid)) 
-          break; // break as soon as we find it 
-   
-    // have we found none ? 
-    if (chatterchannel_index == chatterchannel_count) 
-    { 
-       // cycle through all the chatter channels we know... 
-       for (chatterchannel_index = 0; chatterchannel_index < chatterchannel_count; chatterchannel_index++) 
-          if (chatterchannels[chatterchannel_index].is_open && (wcsistr (chatterchannels[chatterchannel_index].theme, L"chat") != NULL)) 
-             break; // break on the first open general chatter channel we find 
-   
-       // have we found none ? 
-       if (chatterchannel_index == chatterchannel_count) 
-          chatterchannel_index = 0; // ultimate fallback, select the first channel 
-    } 
-   
-    selected_chatterchannel = &chatterchannels[chatterchannel_index]; // in the end, select the channel that was previously selected 
-   
-    chatterchannels_updated = true; // remember chatter channels list is to be updated 
-    return; // finished evaluating this channel list 
- } 
-   
-   
- void EvaluateServerReply_SoughtList (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    wchar_t line_buffer[256]; 
-    soughtgame_t sg; 
-    wchar_t *field_start; 
-    wchar_t *string_pointer; 
-   
-    // is the sought games footer bit NOT present ? 
-    if (!IS_FIELD_PRESENT (field_start, L" ads displayed.") && !IS_FIELD_PRESENT (field_start, L" ad displayed.")) 
-       return; // if so, this reply can't be a sought games list so just return 
-   
-    SAFE_free ((void **) &soughtgames); // free the sought games list we know 
-    soughtgame_count = 0; // reset the sought games count 
-   
-    // now read line per line 
-    string_pointer = player->recvbuffer; // start at the first character 
-    while ((string_pointer = wcsgets (line_buffer, sizeof (line_buffer), string_pointer)) != NULL) 
-    { 
-       if (line_buffer[0] == L'\n') 
-          continue; // discard empty lines 
-       else if ((wcsstr (line_buffer, L" ads displayed.") != NULL) || (wcsstr (line_buffer, L" ad displayed.") != NULL)) 
-          break; // if it's the end of the list, stop reading 
-   
-       // now parse the sought games data 
-       field_start = line_buffer; 
-       memset (&sg, 0, sizeof (sg)); 
-   
-       while (*field_start && iswspace (*field_start)) 
-          field_start++; // skip leading spaces 
-       if (*field_start == 0) 
-          continue; // discard bogus lines 
-   
-       sg.id = _wtoi (field_start); // read sought game id 
-       REACH_NEXT_FIELD_ELSE (field_start, continue); 
-       if ((*field_start != L'-') && (*field_start != L'+')) 
-          sg.rating = _wtoi (field_start); // read player rating 
-       REACH_NEXT_FIELD_ELSE (field_start, continue); 
-       ReadNickname (sg.nickname, WCHAR_SIZEOF (sg.nickname), field_start); // read nickname 
-       REACH_NEXT_FIELD_ELSE (field_start, continue); 
-       sg.initial_time = (float) _wtof (field_start); // read initial time 
-       REACH_NEXT_FIELD_ELSE (field_start, continue); 
-       sg.increment = (float) _wtof (field_start); // read Fischer increment 
-       REACH_NEXT_FIELD_ELSE (field_start, continue); 
-       sg.rating_type = (*field_start == L'r' ? GAMERATINGTYPE_SUPPORTEDRATED : GAMERATINGTYPE_SUPPORTEDUNRATED); // read whether it is rated or not 
-       REACH_NEXT_FIELD_ELSE (field_start, continue); 
-       ReadGamename (sg.game_type, WCHAR_SIZEOF (sg.game_type), field_start); // read game type 
-       sg.game_type[0] = towupper (sg.game_type[0]); // capitalize first character 
-       REACH_NEXT_FIELD_ELSE (field_start, continue); 
-       sg.color = COLOR_UNSPECIFIED; // set unspecified color until told otherwise 
-       if (*field_start == L'[') 
-       { 
-          if (field_start[1] == L'b') sg.color = COLOR_BLACK; // read specified color 
-          else if (field_start[1] == L'w') sg.color = COLOR_WHITE; 
-          REACH_NEXT_FIELD_ELSE (field_start, continue); 
-       } 
-       if (swscanf_s (field_start, L"%d-%d", &sg.lowest_accepted, &sg.highest_accepted) != 2) 
-          continue; // read minimal and maximal accepted ELO, and discard bogus lines 
-   
-       // read whether the game will start automatically and whether the player's filter formula will be checked 
-       if (wcsstr (field_start, L" mf") != NULL) 
-       { 
-          sg.manual_start = true; 
-          sg.formula_checked = true; 
-       } 
-       else if (wcsstr (field_start, L" m") != NULL) 
-          sg.manual_start = true; 
-       else if (wcsstr (field_start, L" f") != NULL) 
-          sg.formula_checked = true; 
-   
-       // is this variant unsupported ? 
-       if ((wcscmp (sg.game_type, L"Untimed") != 0) && (wcscmp (sg.game_type, L"Standard") != 0) 
-             && (wcscmp (sg.game_type, L"Blitz") != 0) && (wcscmp (sg.game_type, L"Lightning") != 0)) 
-       { 
-          sg.rating = 0; 
-          sg.initial_time = 0.0f; 
-          sg.increment = 0.0f; 
-          sg.rating_type = GAMERATINGTYPE_UNSUPPORTED; // if so, clear some values to clean up the display 
-          sg.color = COLOR_UNSPECIFIED; 
-          sg.lowest_accepted = 0; 
-          sg.highest_accepted = 0; 
-          sg.manual_start = true; 
-          sg.formula_checked = false; 
-       } 
-   
-       // all parsing was OK, reallocate sought games list to have one sought game more 
-       soughtgames = (soughtgame_t *) SAFE_realloc (soughtgames, soughtgame_count, soughtgame_count + 1, sizeof (soughtgame_t), true); 
-       memcpy (&soughtgames[soughtgame_count], &sg, sizeof (soughtgame_t)); // now save data 
-       soughtgame_count++; // we know now one sought game more 
-    } 
-   
-    soughtgames_updated = true; // remember sought games display is to be updated 
-    lastsought_time = current_time; // remember when we were last updated 
-    return; // finished evaluating this sought games list 
- } 
-   
-   
- void EvaluateServerReply_PlayersList (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    wchar_t line_buffer[256]; 
-    onlineplayer_t olp; 
-    wchar_t *field_stop; 
-    wchar_t *string_pointer; 
-    int buffer_length; 
-    int char_index; 
-   
-    // is the players list footer bit NOT present ? 
-    if (!IS_FIELD_PRESENT (field_stop, L"(*) indicates system administrator.")) 
-       return; // if so, this reply can't be a players list so just return 
-   
-    SAFE_free ((void **) &onlineplayers); // free the online players list we know 
-    onlineplayer_count = 0; // reset the players count 
-   
-    // format the player list well. We slightly modify recvbuffer here. 
-    // for each character in string... 
-    buffer_length = wcslen (player->recvbuffer); 
-    for (char_index = 0; char_index < buffer_length - 1; char_index++) 
-    { 
-       // is it a separator (two spaces) ? 
-       if ((player->recvbuffer[char_index] == L' ') && (player->recvbuffer[char_index + 1] == L' ')) 
-       { 
-          while (iswspace (player->recvbuffer[char_index])) 
-          { 
-             player->recvbuffer[char_index] = L'\n'; // replace all spaces by newlines 
-             char_index++; // skip all spaces and reach the next non-space character 
-          } 
-       } 
-    } 
-   
-    // now read line per line 
-    string_pointer = player->recvbuffer; // start at the first character 
-    while ((string_pointer = wcsgets (line_buffer, sizeof (line_buffer), string_pointer)) != NULL) 
-    { 
-       if (line_buffer[0] == L'\n') 
-          continue; // discard empty lines 
-       else if (wcsstr (line_buffer, L"(*) indicates system administrator.") != NULL) 
-          break; // if it's the end of the list, stop parsing 
-   
-       // now parse the player data 
-   
-       // parse the handle status 
-       if (wcschr (line_buffer, L'^') != NULL) olp.handlestatus = HANDLESTATUS_INGAME; // player is in game 
-       else if (wcschr (line_buffer, L'~') != NULL) olp.handlestatus = HANDLESTATUS_INSIMULATION; // player is in simulation 
-       else if (wcschr (line_buffer, L'&') != NULL) olp.handlestatus = HANDLESTATUS_INTOURNAMENT; // player is in tournament 
-       else if (wcschr (line_buffer, L'#') != NULL) olp.handlestatus = HANDLESTATUS_EXAMININGAGAME; // player is examining a game 
-       else if (wcschr (line_buffer, L':') != NULL) olp.handlestatus = HANDLESTATUS_NOTOPENFORAMATCH; // player is not open for a match 
-       else if (wcschr (line_buffer, L'.') != NULL) olp.handlestatus = HANDLESTATUS_INACTIVEORBUSY; // player is inactive or busy 
-       else olp.handlestatus = HANDLESTATUS_AVAILABLE; // player is available 
-   
-       // parse the handle codes 
-       olp.handlecodes = 0; 
-       if (wcsstr (line_buffer, L"(*)") != NULL) olp.handlecodes |= HANDLECODE_ADMINISTRATOR; // player is administrator 
-       if (wcsstr (line_buffer, L"(B)") != NULL) olp.handlecodes |= HANDLECODE_BLINDFOLD; // player is blindfold 
-       if (wcsstr (line_buffer, L"(C)") != NULL) olp.handlecodes |= HANDLECODE_COMPUTER; // player is a computer 
-       if (wcsstr (line_buffer, L"(T)") != NULL) olp.handlecodes |= HANDLECODE_TEAM; // player is several persons 
-       if (wcsstr (line_buffer, L"(U)") != NULL) olp.handlecodes |= HANDLECODE_UNREGISTERED; // player is unregistered 
-       if (wcsstr (line_buffer, L"(CA)") != NULL) olp.handlecodes |= HANDLECODE_CHESSADVISOR; // player is a chess advisor 
-       if (wcsstr (line_buffer, L"(SR)") != NULL) olp.handlecodes |= HANDLECODE_SERVICEREPRESENTATIVE; // player is a service representative 
-       if (wcsstr (line_buffer, L"(TD)") != NULL) olp.handlecodes |= HANDLECODE_TOURNAMENTDIRECTOR; // player is a tournament director 
-       if (wcsstr (line_buffer, L"(TM)") != NULL) olp.handlecodes |= HANDLECODE_MAMERMANAGER; // player is a mamer manager 
-       if (wcsstr (line_buffer, L"(FM)") != NULL) olp.handlecodes |= HANDLECODE_FIDEMASTER; // player is a FIDE master 
-       if (wcsstr (line_buffer, L"(IM)") != NULL) olp.handlecodes |= HANDLECODE_FIDEINTERNATIONALMASTER; // player is a FIDE international master 
-       if (wcsstr (line_buffer, L"(GM)") != NULL) olp.handlecodes |= HANDLECODE_FIDEGREATMASTER; // player is a FIDE grand master 
-       if (wcsstr (line_buffer, L"(WIM)") != NULL) olp.handlecodes |= HANDLECODE_FIDEWOMENSINTERNATIONALMASTER; // player is a FIDE woman international master 
-       if (wcsstr (line_buffer, L"(WGM)") != NULL) olp.handlecodes |= HANDLECODE_FIDEWOMENSGREATMASTER; // player is a FIDE woman great master 
-   
-       // get to the first non-numeric character 
-       buffer_length = wcslen (line_buffer); 
-       for (char_index = 0; char_index < buffer_length; char_index++) 
-          if (!iswdigit (line_buffer[char_index]) && (line_buffer[char_index] != L'+') && (line_buffer[char_index] != L'-')) 
-             break; // break as soon as we find it 
-   
-       if (char_index >= buffer_length - 1) 
-          continue; // consistency check: this player is bogus 
-   
-       // is it a E ? else is it a P ? 
-       if (line_buffer[char_index] == L'E') olp.ratingtype = OPPONENTRATINGTYPE_ESTIMATED; // this player's rating is estimated 
-       else if (line_buffer[char_index] == L'P') olp.ratingtype = OPPONENTRATINGTYPE_PROVISIONAL; // this player's rating is provisional 
-       else olp.ratingtype = OPPONENTRATINGTYPE_DEFAULT; // this player's rating is normal 
-   
-       wcscpy_s (olp.nickname, WCHAR_SIZEOF (olp.nickname), &line_buffer[char_index + 1]); // copy nickname 
-       if ((field_stop = wcschr (olp.nickname, L'(')) != NULL) 
-          *field_stop = 0; // separate nickname from its handle flags 
-   
-       // read opponent rating 
-       olp.rating = _wtoi (line_buffer); // ++++ (unregistered) and ---- (no rating) will be translated as 0 
-   
-       // all parsing was OK, reallocate online players list to have one player more 
-       onlineplayers = (onlineplayer_t *) SAFE_realloc (onlineplayers, onlineplayer_count, onlineplayer_count + 1, sizeof (onlineplayer_t), true); 
-       memcpy (&onlineplayers[onlineplayer_count], &olp, sizeof (onlineplayer_t)); // now save data 
-       onlineplayer_count++; // we know now one player more 
-    } 
-   
-    onlineplayers_updated = true; // remember online player list is to be updated 
-    lastonlineplayers_time = current_time; // remember when we were last updated 
-    return; // finished evaluating this players list 
- } 
-   
-   
- void EvaluateServerReply_GameStarting (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    wchar_t white_name[32]; 
-    wchar_t black_name[32]; 
-    wchar_t *field_start; 
-    player_t *local_player; 
-   
-    // is the game starting notification header bit NOT present ? 
-    if (!IS_FIELD_PRESENT_AT_BEGINNING_OF_LINE (field_start, L"Creating: ")) 
-       return; // if so, this reply can't be a game starting notification so just return 
-   
-    // read the interesting parameters, namely white player's name (so that we know which side to display) 
-    if (swscanf_s (field_start, L"Creating: %s %*s %s %*s %*s %*s %*d %*d", white_name, WCHAR_SIZEOF (white_name), black_name, WCHAR_SIZEOF (black_name)) != 2) 
-       Debug_Log (L"===WARNING: unable to parse game starting notification message!===\n%s\n======\n", field_start); 
-   
-    local_player = Player_FindByType (PLAYER_HUMAN); // get a pointer to the local player 
-   
-    // is local player NOT the white color AND should be, OR is local player white color AND should NOT be ? 
-    if (((local_player->color != COLOR_WHITE) && (wcscmp (white_name, local_player->name) == 0)) 
-        || ((local_player->color == COLOR_WHITE) && (wcscmp (white_name, local_player->name) != 0))) 
-    { 
-       Debug_Log (L"===Game starting and local player is not white and should be (or is white and should not be), swapping sides===\n"); 
-       the_board.want_playerswap = true; // swap sides 
-    } 
-   
-    // display the game starting message notification 
-    Scene_UpdateText (&the_scene.gui.central_text, RGBA_TO_RGBACOLOR (255, 255, 255, 191), 5.0f, true, L"\n\n\n\n\n%s\n%s (%s) %s %s (%s)", LOCALIZE (L"NewGame_Title"), white_name, LOCALIZE (L"Games_White"), LOCALIZE (L"Versus"), black_name, LOCALIZE (L"Games_Black")); 
-   
-    // close any possible singleton window 
-    if (IsWindow (hChatterChannelsWnd)) 
-       DestroyWindow (hChatterChannelsWnd); 
-    if (IsWindow (hGamesWnd)) 
-       DestroyWindow (hGamesWnd); // PGN games window (improbable) 
-    if (IsWindow (hMOTDWnd)) 
-       DestroyWindow (hMOTDWnd); 
-    if (IsWindow (hOpponentsWnd)) 
-       DestroyWindow (hOpponentsWnd); 
-    if (IsWindow (hSoughtWnd)) 
-       DestroyWindow (hSoughtWnd); 
-   
-    return; // finished evaluating the game starting notification 
- } 
-   
-   
- void EvaluateServerReply_GameState (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    // Format of style12 computer-parseable lines (ICC/FICS): 
-    //  
-    // <12> rnbqkb-r pppppppp -----n-- -------- ----P--- -------- PPPPKPPP RNBQ-BNR B -1 0 0 1 1 0 7 Newton Einstein 1 2 12 39 39 119 122 2 K/e1-e2 (0:06) Ke2 0 
-    //  
-    // This string always begins on a new line, and there are always exactly 31 non-empty fields separated by blanks. The fields are: 
-    //  
-    // * the string "<12>" to identify this line. 
-    // * eight fields representing the board position. First one is White's 8th rank (also Black's 1st rank), then White's 7th rank (also Black's 2nd), etc. 
-    // * color whose turn it is to move ("B" or "W") 
-    // * -1 if the previous move was NOT a double pawn push, otherwise the chess board file  (numbered 0--7 for a--h) in which the double push was made 
-    // * can White still castle short? (0=no, 1=yes) 
-    // * can White still castle long? 
-    // * can Black still castle short? 
-    // * can Black still castle long? 
-    // * the number of moves made since the last irreversible move (0 if last move was irreversible. If value is >= 100, game can be declared a draw due to the 50 move rule.) 
-    // * The game number 
-    // * White's name 
-    // * Black's name 
-    // * my relation to this game: 
-    //     -3 isolated position, such as for "ref 3" or the "sposition" command 
-    //     -2 I am observing game being examined 
-    //      2 I am the examiner of this game 
-    //     -1 I am playing, it is my opponent's move 
-    //      1 I am playing and it is my move 
-    //      0 I am observing a game being played 
-    // * initial time (in seconds) of the match 
-    // * increment In seconds) of the match 
-    // * White material strength 
-    // * Black material strength 
-    // * White's remaining time 
-    // * Black's remaining time 
-    // * the number of the move about to be made (standard chess numbering -- White's and Black's first moves are both 1, etc.) 
-    // * verbose coordinate notation for the previous move ("none" if there were none) [note this used to be broken for examined games] 
-    // * time taken to make previous move "(min:sec)". 
-    // * pretty notation for the previous move ("none" if there is none) 
-    // * flip field for board orientation: 1 = Black at bottom, 0 = White at bottom. 
-    //  
-    // In the future, new fields may be added to the end of the data string, so programs should parse from left to right. 
-    //  
-    // Special information for bughouse games 
-    // -------------------------------------- 
-    // When showing positions from bughouse games, a second line showing piece holding is given, with "<b1>" at the beginning, for example: 
-    //   <b1> game 6 white [PNBBB] black [PNB] 
-    // Also, when pieces are "passed" during bughouse, a short data string -- not the entire board position -- is sent.  For example: 
-    //   <b1> game 52 white [NB] black [N] <- BN 
-    // The final two letters indicate the piece that was passed; in the above example, a knight (N) was passed to Black. 
-    // A prompt may preceed the <b1> header. 
-   
-    wchar_t positions[65]; 
-    wchar_t move_color_as_string[2]; 
-    int move_color; 
-    int player_color; 
-    int pawnrush_column; 
-    int can_white_castle_short; 
-    int can_white_castle_long; 
-    int can_black_castle_short; 
-    int can_black_castle_long; 
-    int number_of_moves_since_last_irreversible_move; 
-    int game_number; 
-    wchar_t white_name[32]; 
-    wchar_t black_name[32]; 
-    int my_status; 
-    int white_remaining_time_in_seconds; 
-    int black_remaining_time_in_seconds; 
-    int turn_number_1_based; 
-    int move_index; 
-    wchar_t pretty_movestring[8]; 
-    boardmove_t move; 
-    wchar_t *field_start; 
-    int recognized_fields; 
-    boardmove_t *last_move; 
-   
-    // is it NOT a style12 line reply ? 
-    if (!IS_FIELD_PRESENT_AT_BEGINNING_OF_LINE (field_start, L"<12> ")) 
-       return; // if so, this reply can't be a style12 notification so just return 
-   
-    recognized_fields = swscanf_s (field_start, L"<12> " // style12 header 
-                                                L"%s %s %s %s %s %s %s %s " // "rnbqkbnr pppppppp -------- -------- -------- -------- PPPPPPPP RNBQKBNR" 
-                                                L"%s %d %d %d %d %d %d %d " // "W -1 1 1 1 1 0 2" 
-                                                L"%s %s %d %*d %*d %*d %*d " // "guestpmtelnet GuestJXTJ -1 2 12 39 39" 
-                                                L"%d %d %d %*s %*s %s %*d", // "120 120 1 none (0:00) none 1 0 0" 
-                                                &positions[0 * 8], 9, &positions[1 * 8], 9, &positions[2 * 8], 9, &positions[3 * 8], 9, &positions[4 * 8], 9, &positions[5 * 8], 9, &positions[6 * 8], 9, &positions[7 * 8], 9, 
-                                                move_color_as_string, WCHAR_SIZEOF (move_color_as_string), &pawnrush_column, &can_white_castle_short, &can_white_castle_long, &can_black_castle_short, &can_black_castle_long, &number_of_moves_since_last_irreversible_move, &game_number, 
-                                                white_name, WCHAR_SIZEOF (white_name), black_name, WCHAR_SIZEOF (black_name), &my_status, 
-                                                &white_remaining_time_in_seconds, &black_remaining_time_in_seconds, &turn_number_1_based, pretty_movestring, WCHAR_SIZEOF (pretty_movestring)); 
-    if (recognized_fields != 23) 
-       return; // unparseable style12 reply 
-   
-    // remember we are in game and which game it is 
-    player->is_in_game = true; 
-    player->game_number = game_number; 
-   
-    // convert some data into easier formats first. The color which just moved is the opposite of the color to move now 
-    move_color = (towupper (move_color_as_string[0]) == L'W' ? COLOR_BLACK : COLOR_WHITE); 
-    move_index = (2 * turn_number_1_based) - 2 + (move_color == COLOR_WHITE ? 1 : 0); 
-   
-    // save the unquestionable parameters (direct it to the opposite player if the board is going to be swapped) 
-    player_color = the_board.want_playerswap ? COLOR_WHITE : COLOR_BLACK; 
-    ReadNickname (the_board.players[player_color].name, WCHAR_SIZEOF (the_board.players[player_color].name), black_name); 
-    the_board.players[player_color].remaining_seconds = black_remaining_time_in_seconds; 
-   
-    player_color = the_board.want_playerswap ? COLOR_BLACK : COLOR_WHITE; 
-    ReadNickname (the_board.players[player_color].name, WCHAR_SIZEOF (the_board.players[player_color].name), white_name); 
-    the_board.players[player_color].remaining_seconds = white_remaining_time_in_seconds; 
-   
-    // is it an initial board position ? 
-    if (wcscmp (pretty_movestring, L"none") == 0) 
-    { 
-       Debug_Log (L"===Setting up board according to chess server's specifications and beginning new game===\n"); 
-   
-       // set up the board according to what the chess server tells us 
-       memset (&move, 0, sizeof (move)); 
-       Move_SetupFromStyle12 (&move, positions, move_color, pawnrush_column, 
-                               (can_white_castle_short != 0), (can_white_castle_long != 0), (can_black_castle_short != 0), (can_black_castle_long != 0), pretty_movestring); 
-       Board_Reset (&the_board, move.fen_string); 
-    } 
-   
-    // else play the move and swap sides 
-    else 
-    { 
-       Debug_Log (L"===Received board status update from chess server===\n"); 
-   
-       // are we appending a new move ? 
-       if (((move_color == player->color) && (move_index == the_board.move_count)) // either a new move by our opponent 
-           || ((move_color != player->color) && (move_index == the_board.move_count - 1))) // or a confirmation of a move already played by the local player 
-       { 
-          Debug_Log (L"===Appending new move %d===\n", move_index); 
-   
-          // get a pointer to the previous move 
-          last_move = &the_board.moves[the_board.move_count - 1]; 
-   
-          // is the server reporting the remote player's move ? 
-          if (move_color == player->color) 
-          { 
-             // evaluate the move string 
-             wcscpy_s (move.pgntext, WCHAR_SIZEOF (move.pgntext), pretty_movestring); 
-             if (!Move_SetupFromSAN (last_move, &move, player->color)) 
-                Debug_Log (L"===WARNING: unable to interpret server's table state while evaluating its notification of remote player move!===\n%s\n======\n", pretty_movestring); 
-   
-             // play the remote opponent's move 
-             Board_AppendMove (&the_board, move.source[0], move.source[1], move.target[0], move.target[1], move.promotion_type, NULL); 
-   
-             the_board.players[1 - player->color].should_wakeup = true; // tell the opposite player to wake up 
-             animation_endtime = current_time + ANIMATION_DURATION; // play move animation now 
-          } 
-   
-          // else the server is acknowledging the local player's move 
-          else 
-          { 
-             // evaluate the move string 
-             wcscpy_s (last_move->pgntext, WCHAR_SIZEOF (last_move->pgntext), pretty_movestring); 
-             if (!Move_SetupFromSAN (&the_board.moves[the_board.move_count - 2], last_move, last_move->color)) 
-                Debug_Log (L"===WARNING: unable to interpret server's table state while evaluating its reply after local player move!===\n%s\n======\n", pretty_movestring); 
-          } 
-   
-          // in case a new move was appended, update the last move pointer 
-          last_move = &the_board.moves[the_board.move_count - 1]; 
-       } 
-   
-       // else the server must be time-warping the game backwards 
-       else 
-       { 
-          Debug_Log (L"===Backing up to move %d===\n", move_index); 
-   
-          last_move = &the_board.moves[move_index]; // get a pointer to the move we're backing up to 
-          the_board.move_count = move_index + 1; // update the board's move count 
-          if (the_board.viewed_move > the_board.move_count - 1) 
-             the_board.viewed_move = the_board.move_count - 1; // and the board's viewed move as well 
-   
-          // send a notification to the player's chat window 
-          Interlocutor_Notify (Interlocutor_FindOrCreate (player->name), LOCALIZE (L"Chat_TakebackAccepted"), NULL); 
-       } 
-   
-       // make the server set it up correctly 
-       Move_SetupFromStyle12 (last_move, positions, move_color, pawnrush_column, 
-                               (can_white_castle_short != 0), (can_white_castle_long != 0), (can_black_castle_short != 0), (can_black_castle_long != 0), pretty_movestring); 
-   
-       Debug_LogMove (&the_board.moves[the_board.move_count - 1], L"===Here is the updated board state as reported by the engine===\n"); 
-    } 
-   
-    the_board.game_state = STATE_PLAYING; // remember that a game is currently playing (this may be overwritten later by EvaluateServerReply_GameResults) 
-    the_board.reevaluate = true; // and reeevaluate the board 
-    return; // finished evaluating this style12 notification line 
- } 
-   
-   
- void EvaluateServerReply_GameResults (player_t *player) 
- { 
-    // this function parses a network reply and evaluates it, deciding what to do 
-   
-    wchar_t *field_start; 
-    int game_number; 
-   
-    // is the game results notification header bit NOT present ? 
-    if (!IS_FIELD_PRESENT_AT_BEGINNING_OF_LINE (field_start, L"{Game ")) 
-       return; // if so, this reply can't be a game results notification so just return 
-   
-    // are we NOT in game yet OR is it a game creation message ? 
-    if (!player->is_in_game || (wcsstr (field_start, L") Creating") != NULL)) 
-       return; // if so, this message is not a game results but a game creation message 
-   
-    // verify it's for the game we're playing 
-    if (swscanf_s (field_start, L"{Game %d ", &game_number) != 1) 
-    { 
-       Debug_Log (L"===WARNING: unable to parse game results notification message!===\n%s\n======\n", field_start); 
-       return; // on error, drop a warning in the log file and return 
-    } 
-    if (game_number != player->game_number) 
-    { 
-       Debug_Log (L"===WARNING: received game results notification message with wrong game number! Ignoring.===\n"); 
-       return; // on error, drop a warning in the log file and return 
-    } 
-   
-    //////////////////////////// 
-    // interpret the game result 
-   
-    // do the white win ? 
-    if (wcsstr (field_start, L"} 1-0") != NULL) 
-    { 
-       // is it a checkmate ? 
-       if (wcsstr (field_start, L"checkmate") != NULL) 
-       { 
-          Debug_Log (L"===Server tells us that black is checkmate: white wins!===\n"); 
-          the_board.game_state = STATE_WHITEWIN_CHECKMATE; // remember game state 
-       } 
-   
-       // else it must be a resign, a forfeit or an adjudication 
-       else 
-       { 
-          Debug_Log (L"===Server tells us that black [resigns|forfeits|loses adjudication]: white wins!===\n"); 
-          the_board.game_state = STATE_WHITEWIN_RESIGNORFORFEIT; // remember game state 
-       } 
-   
-       // if white player is human, play the victory sound, else, play defeat sound at the center of the board 
-       Audio_PlaySound (the_board.players[COLOR_WHITE].type == PLAYER_HUMAN ? SOUNDTYPE_VICTORY : SOUNDTYPE_DEFEAT, 0.0f, 0.0f, 0.04f); 
-    } 
-   
-    // else do the black win ? 
-    else if (wcsstr (field_start, L"} 0-1") != NULL) 
-    { 
-       // is it a checkmate ? 
-       if (wcsstr (field_start, L" checkmate") != NULL) 
-       { 
-          Debug_Log (L"===Server tells us that white is checkmate: black wins!===\n"); 
-          the_board.game_state = STATE_BLACKWIN_CHECKMATE; // remember game state 
-       } 
-   
-       // else it must be a resign, a forfeit or an adjudication 
-       else 
-       { 
-          Debug_Log (L"===Server tells us that white [resigns|forfeits|loses adjudication]: black wins!===\n"); 
-          the_board.game_state = STATE_BLACKWIN_RESIGNORFORFEIT; // remember game state 
-       } 
-   
-       // if black player is human, play the victory sound, else, play defeat sound at the center of the board 
-       Audio_PlaySound (the_board.players[COLOR_BLACK].type == PLAYER_HUMAN ? SOUNDTYPE_VICTORY : SOUNDTYPE_DEFEAT, 0.0f, 0.0f, 0.04f); 
-    } 
-   
-    // else is it a draw ? 
-    else if (wcsstr (field_start, L"} 1/2-1/2") != NULL) 
-    { 
-       // is it a stalemate ? 
-       if (wcsstr (field_start, L" stalemate") != NULL) 
-       { 
-          Debug_Log (L"===Server tells us it's a stalemate: game is a draw===\n"); 
-          the_board.game_state = STATE_DRAW_STALEMATE; // remember game state 
-       } 
-   
-       // else is it a mutual agreement ? 
-       else if (wcsstr (field_start, L" mutual agreement") != NULL) 
-       { 
-          Debug_Log (L"===Server tells us it's a draw by mutual agreement: game is a draw===\n"); 
-          the_board.game_state = STATE_DRAW_AGREEMENT; // remember game state 
-       } 
-   
-       // else it's another reason 
-       else 
-       { 
-          Debug_Log (L"===Server tells us it's a draw for another reason: game is a draw===\n"); 
-          the_board.game_state = STATE_DRAW_OTHER; // remember game state 
-       } 
-   
-       // play a defeat sound at the center of the board 
-       Audio_PlaySound (SOUNDTYPE_DEFEAT, 0.0f, 0.0f, 0.04f); 
-    } 
-   
-    // else is it an adjournment ? 
-    else if (wcsstr (field_start, L"} *") != NULL) 
-    { 
-       Debug_Log (L"===Server tells the game is adjourned: game adjourned===\n"); 
-       the_board.game_state = STATE_ADJOURNED; // remember game state 
-    } 
-   
-    // else we can't interpret the game state 
-    else 
-    { 
-       Debug_Log (L"===WARNING: unable to interpret game results notification message!===\n%s\n======\n", field_start); 
-       return; // on error, drop a warning in the log file and return 
-    } 
-   
-    // remember player is no longer in game 
-    player->is_in_game = false; 
-    player->game_number = 0; 
-   
-    // reevaluate the board and display the endgame dialog box 
-    the_board.reevaluate = true; 
-    DialogBox_EndGame (); 
-   
-    return; // finished evaluating the game results notification 
- } 
-   
-   
- static void ReadNickname (wchar_t *nickname, size_t nickname_size, wchar_t *from_string) 
- { 
-    // helper function to read a nickname and strip it from its eventual flags 
-   
-    unsigned int char_index; 
-   
-    // as long as we don't read a forbidden character... 
-    for (char_index = 0; char_index < nickname_size; char_index++) 
-       if (iswalpha (from_string[char_index])) 
-          nickname[char_index] = from_string[char_index]; // copy nickname one character after the other 
-       else 
-          break; // else stop copying immediately 
-   
-    if (char_index < nickname_size) 
-       nickname[char_index] = 0; // finish the string ourselves 
-    else 
-       nickname[nickname_size - 1] = 0; // truncate it if neeeded 
-   
-    return; // finished 
- } 
-   
-   
- static void ReadGamename (wchar_t *gamename, size_t gamename_size, wchar_t *from_string) 
- { 
-    // helper function to read a game name 
-   
-    unsigned int char_index; 
-   
-    // as long as we don't read a forbidden character... 
-    for (char_index = 0; char_index < gamename_size; char_index++) 
-       if (iswgraph (from_string[char_index])) 
-          gamename[char_index] = from_string[char_index]; // copy game name one character after the other 
-       else 
-          break; // else stop copying immediately 
-   
-    if (char_index < gamename_size) 
-       gamename[char_index] = 0; // finish the string ourselves 
-    else 
-       gamename[gamename_size - 1] = 0; // truncate it if neeeded 
-   
-    return; // finished 
- } 
-   
-   
- static void ReadSpannedLine (wchar_t *outstring, size_t outstring_size, wchar_t *multiline_string) 
- { 
-    // this function linearizes a multiline string and takes out any special formatting character of it 
-   
-    int length; 
-    int read_index; 
-    unsigned int write_index; 
-   
-    length = wcslen (multiline_string); // get text length first 
-   
-    // for each character in string... 
-    write_index = 0; 
-    for (read_index = 0; read_index < length; read_index++) 
-    { 
-       if (wcsncmp (&multiline_string[read_index], L"\\   ", 4) == 0) 
-          read_index += 4; // if it's a new line indentation, skip it 
-   
-       // are we NOT reading a newline followed by a backslash AND is it still room in the output string ? 
-       if (!((read_index < length - 1) && (multiline_string[read_index] == L'\n') && (multiline_string[read_index + 1] == L'\\')) 
-           && (write_index < outstring_size - 1)) 
-       { 
-          if (multiline_string[read_index] == L'\n') 
-             break; // if it's a newline (without backslash following), stop reading 
-   
-          outstring[write_index] = multiline_string[read_index]; // else copy this character to output string 
-          write_index++; // and advance in string 
-       } 
-    } 
-   
-    outstring[write_index] = 0; // finish string 
-    return; // and return 
- } 
-