/*
 *  Copyright (C) 2002-2021  The DOSBox Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dosbox.h"
#include "cross.h"
#include "logging.h"
#include "support.h"
#include "setup.h"
#include "render.h"
#include "control.h"
#include "shell.h"
#include "menu.h"
#include "jfont.h"
#include "mapper.h"
#include <map>
#include <list>
#include <string>
#include <memory>
#include <unordered_map>
#include <fstream>
#include <iostream>
#if defined(__MINGW32__) && !defined(HX_DOS)
#include <imm.h>
#endif
using namespace std;

int msgcodepage = 0, lastmsgcp = 0;
bool morelen = false, inmsg = false, loadlang = false;
bool uselangcp = false; // True if Language file is loaded via CONFIG -set langcp option. Use codepage specified in the language file 
bool isSupportedCP(int newCP), CodePageHostToGuestUTF8(char *d/*CROSS_LEN*/,const char *s/*CROSS_LEN*/), CodePageGuestToHostUTF8(char *d/*CROSS_LEN*/,const char *s/*CROSS_LEN*/), systemmessagebox(char const * aTitle, char const * aMessage, char const * aDialogType, char const * aIconType, int aDefaultButton), OpenGL_using(void);
void InitFontHandle(void), ShutFontHandle(void), refreshExtChar(void), SetIME(void), runRescan(const char *str), menu_update_dynamic(void), menu_update_autocycle(void), update_bindbutton_text(void), set_eventbutton_text(const char *eventname, const char *buttonname), JFONT_Init(), DOSBox_SetSysMenu(), UpdateSDLDrawTexture(), makestdcp950table(), makeseacp951table();
std::string langname = "", langnote = "", GetDOSBoxXPath(bool withexe=false);
extern int lastcp, FileDirExistUTF8(std::string &localname, const char *name), toSetCodePage(DOS_Shell *shell, int newCP, int opt);
extern bool dos_kernel_disabled, force_conversion, showdbcs, dbcs_sbcs, enableime, tonoime, chinasea, CHCP_changed;
extern uint16_t GetDefaultCP();
Bitu DOS_ChangeKeyboardLayout(const char* layoutname, int32_t codepage);
Bitu DOS_ChangeCodepage(int32_t codepage, const char* codepagefile);
Bitu DOS_LoadKeyboardLayout(const char* layoutname, int32_t codepage, const char* codepagefile);
const char* DOS_GetLoadedLayout(void);
bool CheckDBCSCP(int32_t codepage);
void MSG_Init(void);
void get_yesno_chars(void);
extern char char_yes, char_no;

#define LINE_IN_MAXLEN 2048

struct MessageBlock {
    std::string name;
    std::string val;
    MessageBlock(const char* _name, const char* _val) : name(_name), val(_val) {}
};

std::list<MessageBlock> LangList; // list of translation messages, order maintained in inserted order

// Map for fast lookup
std::unordered_map<std::string, std::list<MessageBlock>::iterator> LangMap;

void MSG_Add(const char* _name, const char* _val) { //add messages to the translation message list
    // If the message already exists, ignore it
    if(LangMap.find(_name) != LangMap.end()) return;

    // Add the message to the end of the list
    LangList.emplace_back(_name, _val);

    // Save the position in the map
    LangMap[_name] = std::prev(LangList.end());
}

void MSG_Replace(const char* _name, const char* _val) {
    auto it = LangMap.find(_name);

    if(it != LangMap.end()) {
        // Update the value while maintaining the order
        it->second->val = _val;
    }
    else {
        // If not found, add the message
        LangList.emplace_back(_name, _val);
        LangMap[_name] = std::prev(LangList.end());
    }
}

const char* MSG_Get(const char* msg) { // add messages to the translation message list
    auto it = LangMap.find(msg);

    if(it != LangMap.end())
        return it->second->val.c_str(); // Return the value if found

    return msg; // Return the original name if not found
}

const char* MSG_GetUTF8(const char* msg)
{
    thread_local std::string storage;

    const char* guest = MSG_Get(msg);

    size_t len = strlen(guest);
    std::vector<char> buf(len * 4 + 1, 0);

    CodePageGuestToHostUTF8(buf.data(), guest);

    storage = buf.data();
    return storage.c_str();
}



std::string formatString(const char* format, ...) {
    /**
      * @brief Generates a formatted string using a format specifier and variable arguments.
      *
      * @param format A format string (e.g., "File: '%s', Error Code: %d, Language: '%s'.")
      * @param ...    Values corresponding to format specifiers (e.g., "lang_en.msg", 404, "English").
      * @return std::string A formatted string.
      */
    va_list args;
    va_start(args, format);

    // Determine required buffer size
    int size = vsnprintf(nullptr, 0, format, args);
    va_end(args);

    if(size < 0) {
        return ""; // Return empty string on error
    }

    std::vector<char> buffer(size + 1); // Allocate buffer (+1 for null-terminator)
    va_start(args, format);
    vsnprintf(buffer.data(), buffer.size(), format, args);
    va_end(args);

    return std::string(buffer.data());
}

bool InitCodePage() {
    if (!dos.loaded_codepage || dos_kernel_disabled || force_conversion) {
        if (((control->opt_langcp && msgcodepage != dos.loaded_codepage) || uselangcp) && msgcodepage>0) {
            dos.loaded_codepage = msgcodepage;
            return true;
        }
        Section_prop *section = static_cast<Section_prop *>(control->GetSection("config"));
        if (section!=NULL && !control->opt_noconfig && !IS_PC98_ARCH && !IS_JEGA_ARCH && !IS_DOSV) {
            char *countrystr = (char *)section->Get_string("country"), *r=strchr(countrystr, ',');
            if (r!=NULL && *(r+1)) {
                int cp = atoi(trim(r+1));
                if(cp > 0 && isSupportedCP(cp) && !msgcodepage) {
                    dos.loaded_codepage = cp;
                    return true;
                }
                else dos.loaded_codepage = msgcodepage;
                return true;
            }
        }
        if (msgcodepage>0) {
            dos.loaded_codepage = msgcodepage;
            return true;
        }
    }
    if (!dos.loaded_codepage) {
        dos.loaded_codepage = GetDefaultCP();
        return false;
    } else
        return true;
}

void AddMessages() {
    MSG_Add("AUTOEXEC_CONFIGFILE_HELP",
        "Lines in this section will be run at startup.\n"
        "You can put your MOUNT lines here.\n"
    );
    MSG_Add("CONFIGFILE_INTRO",
            "# This is the configuration file for DOSBox-X %s. (Please use the latest version of DOSBox-X)\n"
            "# Lines starting with a # are comment lines and are ignored by DOSBox-X.\n"
            "# They are used to (briefly) document the effect of each option.\n"
        "# To write out ALL options, use command 'config -all' with -wc or -writeconf options.\n");
    MSG_Add("CONFIG_SUGGESTED_VALUES", "Possible values");
    MSG_Add("CONFIG_ADVANCED_OPTION", "Advanced options (see full configuration reference file [dosbox-x.reference.full.conf] for more details)");
    MSG_Add("CONFIG_TOOL","DOSBox-X Configuration Tool");
    MSG_Add("CONFIG_TOOL_EXIT","Exit configuration tool");
    MSG_Add("MAPPER_EDITOR_EXIT","Exit mapper editor");
    MSG_Add("SAVE_MAPPER_FILE","Save mapper file");
    MSG_Add("WARNING","Warning");
    MSG_Add("ERROR", "Error");
    MSG_Add("INFORMATION", "Information");
    MSG_Add("YES","Yes");
    MSG_Add("NO","No");
    MSG_Add("OK","OK");
    MSG_Add("CANCEL","Cancel");
    MSG_Add("CLOSE","Close");
    MSG_Add("THEME","Theme");
    MSG_Add("DEBUGCMD","Enter Debugger Command");
    MSG_Add("ADD","Add");
    MSG_Add("DEL","Del");
    MSG_Add("NEXT","Next");
    MSG_Add("SAVE","Save");
    MSG_Add("EXIT","Exit");
    MSG_Add("CAPTURE","Capture");
    MSG_Add("SAVE_CONFIGURATION","Save configuration");
    MSG_Add("SAVE_LANGUAGE","Save language file");
    MSG_Add("SAVE_RESTART","Save & Restart");
    MSG_Add("PASTE_CLIPBOARD","Paste Clipboard");
    MSG_Add("APPEND_HISTORY","Append History");
    MSG_Add("EXECUTE_NOW","Execute Now");
    MSG_Add("ADDITION_CONTENT","Additional Content:");
    MSG_Add("CONTENT","Content:");
    MSG_Add("EDIT_FOR","Edit %s");
    MSG_Add("HELP_FOR","Help for %s");
    MSG_Add("SELECT_VALUE", "Select property value");
    MSG_Add("CONFIGURATION","Configuration");
    MSG_Add("SETTINGS","Settings");
    MSG_Add("LOGGING_OUTPUT","DOSBox-X logging output");
    MSG_Add("CODE_OVERVIEW","Code overview");
    MSG_Add("VISIT_HOMEPAGE","Visit Homepage");
    MSG_Add("GET_STARTED","Getting Started");
    MSG_Add("CDROM_SUPPORT","CD-ROM Support");
    MSG_Add("DRIVE_INFORMATION","Drive information");
    MSG_Add("MOUNTED_DRIVE_NUMBER","Mounted drive numbers");
    MSG_Add("IDE_CONTROLLER_ASSIGNMENT","IDE controller assignment");
    MSG_Add("HELP_COMMAND","Help on DOS command");
    MSG_Add("CURRENT_VOLUME","Current sound mixer volumes");
    MSG_Add("CURRENT_SBCONFIG","Sound Blaster configuration");
    MSG_Add("CURRENT_MIDICONFIG","Current MIDI/OPL configuration");
    MSG_Add("CREATE_IMAGE","Create blank disk image");
    MSG_Add("NETWORK_LIST","Network interface list");
    MSG_Add("PRINTER_LIST","Printer device list");
    MSG_Add("INTRODUCTION","Introduction");
    MSG_Add("SHOW_ADVOPT", "Show advanced options");
    MSG_Add("USE_PRIMARYCONFIG", "Use primary config file");
    MSG_Add("USE_PORTABLECONFIG", "Use portable config file");
    MSG_Add("USE_USERCONFIG", "Use user config file");
    MSG_Add("CONFIG_SAVETO", "Enter filename for the configuration file to save to:");
    MSG_Add("CONFIG_SAVEALL", "Save all (including advanced) config options to the configuration file");
    MSG_Add("LANG_FILENAME", "Enter filename for language file:");
    MSG_Add("LANG_LANGNAME", "Language name (optional):");
    MSG_Add("INTRO_MESSAGE", "Welcome to DOSBox-X, a free and complete DOS emulation package.\nDOSBox-X creates a DOS shell which looks like the plain DOS.\nYou can also run Windows 3.x and 9x/Me inside the DOS machine.");
    MSG_Add("DRIVE","Drive");
    MSG_Add("TYPE","Type");
    MSG_Add("LABEL","Label");
    MSG_Add("DRIVE_NUMBER","Drive number");
    MSG_Add("DISK_NAME","Disk name");
    MSG_Add("IDE_POSITION","IDE position");
    MSG_Add("SWAP_SLOT","Swap slot");
    MSG_Add("EMPTY_SLOT","Empty slot");
    MSG_Add("SLOT","Slot");
    MSG_Add("SELECT_EVENT", "Select an event to change.");
    MSG_Add("SELECT_DIFFERENT_EVENT", "Select an event or press Add/Del/Next buttons.");
    MSG_Add("PRESS_JOYSTICK_KEY", "Press a key/joystick button or move the joystick.");
    MSG_Add("CAPTURE_ENABLED", "Capture enabled. Hit ESC to release capture.");
    MSG_Add("MAPPER_FILE_SAVED", "Mapper file saved");
    MSG_Add("AUTO_CYCLE_MAX","Auto cycles [max]");
    MSG_Add("AUTO_CYCLE_AUTO","Auto cycles [auto]");
    MSG_Add("AUTO_CYCLE_OFF","Auto cycles [off]");
    MSG_Add("LANG_LOAD_ERROR", "Could not load language message file %s. The default language will be used.");
    MSG_Add("LANG_JP_INCOMPATIBLE", "You have specified a language file which uses a code page incompatible with the Japanese PC-98 or JEGA/AX system.\n\n"
        "Are you sure to use the language file for this machine type?");
    MSG_Add("LANG_DOSV_INCOMPATIBLE", "You have specified a language file which uses a code page incompatible with the current DOS/V system.\n\n"
        "Are you sure to use the language file for this system type?");
    MSG_Add("LANG_CHANGE_CP", "The specified language file uses code page %d. Do you want to change to this code page accordingly?");
    MSG_Add("HELP_ABOUT_VERSION","DOSBox-X ver.%s (%s %s %s-bit)%s");
    MSG_Add("HELP_ABOUT_UPDATED","Build date/time: %s");
    MSG_Add("HELP_ABOUT_COPYRIGHT","Copyright %s-%s %s");
    MSG_Add("HELP_ABOUT_MAINTAINER","Project maintainer: %s");
    MSG_Add("HELP_ABOUT_HOMEPAGE","DOSBox-X homepage: %s");
}

// True if specified codepage is a DBCS codepage
bool CheckDBCSCP(int32_t codepage) {
    if(codepage == 932 || codepage == 936 || codepage == 949 || codepage == 950 || codepage == 951) {
        //LOG_MSG("CheckDBCSCP: Codepage %d true", codepage);
        return true;
    }
    else return false;
}

FILE* testLoadLangFile(const char* fname) {
    std::string exepath = GetDOSBoxXPath();
    std::string config_path, res_path;
    config_path = Cross::GetPlatformConfigDir();
    res_path = Cross::GetPlatformResDir();

    std::vector<std::string> base_paths = {
        "", exepath, config_path, res_path,
        "languages/", exepath + "languages/", config_path + "languages/", res_path + "languages/",
        "language/", exepath + "language/", config_path + "language/", res_path + "language/"
    };

    std::vector<std::string> suffixes = { "", ".lng" };

    for(const auto& base : base_paths) {
        for(const auto& suffix : suffixes) {
            std::string full_path = base + fname + suffix;
            FILE* mfile = fopen(full_path.c_str(), "rt");
            if(mfile) return mfile;
        }
    }

#if defined(WIN32) && defined(C_SDL2)
    std::string localname = fname;
    if(FileDirExistUTF8(localname, fname) == 1) {
        FILE* mfile = fopen(localname.c_str(), "rt");
        if(mfile) return mfile;
    }
#endif

    return nullptr;
}


static std::string loaded_fname;

void LoadMessageFile(const char* fname) {
    if(!fname || *fname == '\0') return;

    if(loaded_fname == fname) {
        return;
    }

    LOG(LOG_MISC, LOG_DEBUG)("Loading message file %s", fname);

    FILE* mfile = testLoadLangFile(fname);
 
    if(!mfile) {
        std::string message = formatString(MSG_Get("LANG_LOAD_ERROR"), fname);
        systemmessagebox("Warning", message.c_str(), "ok", "warning", 1);
        SetVal("dosbox", "language", "");
        LOG_MSG("MSG:Cannot load language file: %s", fname);
        control->opt_lang = "";
        return;
    }

    loaded_fname = fname;
    langname.clear();
    langnote.clear();
    std::string linein, name, menu_name, mapper_name, string;

    morelen = inmsg = true;
    bool res = true;
    bool loadlangcp = false;
    int cp = dos.loaded_codepage;
    if(!dos.loaded_codepage) res = InitCodePage();

    const size_t buffer_size = LINE_IN_MAXLEN + 1024;
    char* buffer = new char[buffer_size];

    while(fgets(buffer, buffer_size, mfile)) {
        linein = buffer;
        // Remove \r and \n
        linein.erase(std::remove(linein.begin(), linein.end(), '\r'), linein.end());
        linein.erase(std::remove(linein.begin(), linein.end(), '\n'), linein.end());

        if(linein.empty()) {
            string += "\n";
            continue;
        }

        std::string trimmed = linein.substr(1);
         trim(trimmed);

        if(linein[0] == ':') {
            string.clear();

            if(!strncasecmp(linein.c_str() + 1, "DOSBOX-X:", 9)) {
                std::string p = linein.substr(10);
                size_t colon_pos = p.find(':');
                if(colon_pos != std::string::npos && colon_pos > 0 && colon_pos + 1 < p.size()) {
                    std::string key = p.substr(0, colon_pos);
                    std::string val = p.substr(colon_pos + 1);
                    if(key == "CODEPAGE") {
                        int c = std::atoi(val.c_str());
                        if(!isSupportedCP(c)) {
                            LOG_MSG("Language file: Invalid codepage :DOSBOX-X:CODEPAGE:%d", c);
                            loadlangcp = false;
                        }
                        else if(((IS_PC98_ARCH || IS_JEGA_ARCH) && c != 437 && c != 932 &&
                            !systemmessagebox("DOSBox-X language file",
                                MSG_Get("LANG_JP_INCOMPATIBLE"),
                                "yesno", "question", 2)) ||
                            (((IS_JDOSV && c != 932) || (IS_PDOSV && c != 936) ||
                                (IS_KDOSV && c != 949) || (IS_TDOSV && c != 950 && c != 951)) &&
                                c != 437 &&
                                !systemmessagebox("DOSBox-X language file",
                                    MSG_Get("LANG_DOSV_INCOMPATIBLE"),
                                    "yesno", "question", 2))) {
                            fclose(mfile);
                            dos.loaded_codepage = cp;
                            return;
                        }
                        else {
                            std::string msg = formatString(MSG_Get("LANG_CHANGE_CP"), c);
                            if(c == dos.loaded_codepage) {
                                msgcodepage = c;
                                lastmsgcp = msgcodepage;
                            }
                            if(c != dos.loaded_codepage && (control->opt_langcp || uselangcp || !CHCP_changed || CheckDBCSCP(c) || !loadlang ||
                                (loadlang && systemmessagebox("DOSBox-X language file", msg.c_str(), "yesno", "question", 1)))) {
                                loadlangcp = true;
                                if(c == 950 && dos.loaded_codepage == 951) msgcodepage = 951;      // zh_tw defaults to CP950, but CP951 is acceptable as well so keep it
                                else if(c == 951 && dos.loaded_codepage == 950) msgcodepage = 950; // And vice versa for lang files requiring CP951
                                else msgcodepage = c;
                                dos.loaded_codepage = c;
                                if(c == 950 && !chinasea) makestdcp950table();
                                if(c == 951 && chinasea) makeseacp951table();
                                lastmsgcp = c;
                            }
                        }
                    }
                    else if(key == "LANGUAGE") {
                        langname = val;
                    }
                    else if(key == "REMARK") {
                        langnote = val;
                    }
                }
            }
            else if(!strncasecmp(linein.c_str() + 1, "MENU:", 5) && linein.size() > 6) {
                name.clear(); mapper_name.clear();
                menu_name = linein.substr(6);
            }
            else if(!strncasecmp(linein.c_str() + 1, "MAPPER:", 7) && linein.size() > 8) {
                name.clear(); menu_name.clear();
                mapper_name = linein.substr(8);
            }
            else {
                menu_name.clear(); mapper_name.clear();
                name = linein.substr(1);
            }

        }
        else if(linein[0] == '.' && trimmed.empty()) {
            if(!string.empty() && string.back() == '\n') string.pop_back();

            if(!name.empty()) {
                MSG_Replace(name.c_str(), string.c_str());
            }
            else if(menu_name.size() > 6 && menu_name.rfind("drive_", 0) == 0) {
                for(char c = 'A'; c <= 'Z'; ++c) {
                    std::string mname = "drive_" + std::string(1, c) + menu_name.substr(5);
                    if(mainMenu.item_exists(mname)) {
                        mainMenu.get_item(mname).set_text(string);
                    }
                }
            }
            else if(!menu_name.empty() && mainMenu.item_exists(menu_name)) {
                mainMenu.get_item(menu_name).set_text(string);
                if(menu_name.rfind("mapper_", 0) == 0 && menu_name.size() > 7) {
                    set_eventbutton_text(menu_name.c_str() + 7, string.c_str());
                }
            }
            else if(!mapper_name.empty()) {
                set_eventbutton_text(mapper_name.c_str(), string.c_str());
            }
        }
        else {
            size_t temp_size = linein.size() * 3 + 1; // Converting to UTF-8 will expand to max. 3-bytes / character + null terminator
            std::vector<char> temp(temp_size);
            if(!CodePageHostToGuestUTF8(temp.data(), linein.c_str())) {
                string += linein + "\n";
            }
            else {
                string += std::string(temp.data()) + "\n";
            }
        }
    }

    morelen = inmsg = false;
    delete[] buffer;
    fclose(mfile);
    menu_update_dynamic();
    menu_update_autocycle();
    update_bindbutton_text();
    dos.loaded_codepage = cp;

#if !defined(OSFREE)
    if(loadlangcp && msgcodepage > 0) {
        const char* layoutname = DOS_GetLoadedLayout();
        if(!IS_DOSV && !IS_JEGA_ARCH && !IS_PC98_ARCH && layoutname != nullptr) {
            toSetCodePage(nullptr, msgcodepage, -1);
        }
    }
#endif

    get_yesno_chars();
    refreshExtChar();
    LOG_MSG("LoadMessageFile: Loaded language file: %s", fname);
    loadlang = true;
}



bool MSG_Write(const char* location, const char* name) {
    std::unique_ptr<char[]> temp(new char[4096]);

    FILE* out = fopen(location, "w+t");
    if(out == nullptr) return false; // Failed to open file for writing

    if(name != nullptr) langname = std::string(name);

    if(!langname.empty())
        fprintf(out, ":DOSBOX-X:LANGUAGE:%s\n", langname.c_str());

    if(dos.loaded_codepage)
        fprintf(out, ":DOSBOX-X:CODEPAGE:%d\n", dos.loaded_codepage);

    fprintf(out, ":DOSBOX-X:VERSION:%s\n", VERSION);
    fprintf(out, ":DOSBOX-X:REMARK:%s\n", langnote.c_str());

    morelen = inmsg = true;

    // Output messages in insertion order using LangList
    for(const auto& msg : LangList) {
        const char* out_text = msg.val.c_str();
        if(!CodePageGuestToHostUTF8(temp.get(), out_text))
            fprintf(out, ":%s\n%s\n.\n", msg.name.c_str(), out_text);
        else
            fprintf(out, ":%s\n%s\n.\n", msg.name.c_str(), temp.get());
    }

    // Output menu items (menu filtering conditions remain unchanged)
    std::vector<DOSBoxMenu::item> master_list = mainMenu.get_master_list();
    for(auto& id : master_list) {
        if(!id.is_allocated() || id.get_type() == DOSBoxMenu::separator_type_id ||
            id.get_type() == DOSBoxMenu::vseparator_type_id ||
            (id.get_name().size() == 5 && id.get_name().substr(0, 4) == "slot") ||
            (id.get_name().size() > 9 && id.get_name().substr(0, 8) == "command_") ||
            (id.get_name().size() == 6 && id.get_name().substr(0, 5) == "Drive" &&
                id.get_name().back() >= 'A' && id.get_name().back() <= 'Z') ||
            (id.get_name().size() > 9 && id.get_name().substr(0, 6) == "drive_" &&
                id.get_name()[6] >= 'B' && id.get_name()[6] <= 'Z' && id.get_name()[7] == '_') ||
            id.get_name() == "mapper_cycauto")
            continue;

        std::string text = id.get_text();
        if(id.get_name() == "hostkey_mapper" || id.get_name() == "clipboard_device") {
            size_t found = text.find(":");
            if(found != std::string::npos) text = text.substr(0, found);
        }

        std::string idname = (id.get_name().size() > 9 && id.get_name().substr(0, 8) == "drive_A_")
            ? "drive_" + id.get_name().substr(8)
            : id.get_name();

        const char* out_text = text.c_str();
        if(!CodePageGuestToHostUTF8(temp.get(), out_text))
            fprintf(out, ":MENU:%s\n%s\n.\n", idname.c_str(), out_text);
        else
            fprintf(out, ":MENU:%s\n%s\n.\n", idname.c_str(), temp.get());
    }

    // Output MAPPER items that differ from mainMenu mappings
    std::map<std::string, std::string> event_map = get_event_map();
    for(const auto& it : event_map) {
        if(mainMenu.item_exists("mapper_" + it.first) &&
            mainMenu.get_item("mapper_" + it.first).get_text() == it.second)
            continue;

        const char* out_text = it.second.c_str();
        if(!CodePageGuestToHostUTF8(temp.get(), out_text))
            fprintf(out, ":MAPPER:%s\n%s\n.\n", it.first.c_str(), out_text);
        else
            fprintf(out, ":MAPPER:%s\n%s\n.\n", it.first.c_str(), temp.get());
    }

    morelen = inmsg = false;
    fclose(out);
    return true;
}


void ResolvePath(std::string& in);
void MSG_Init() {
	Section_prop *section=static_cast<Section_prop *>(control->GetSection("dosbox"));

	if (control->opt_lang != "") {
		LoadMessageFile(control->opt_lang.c_str());
		SetVal("dosbox", "language", control->opt_lang.c_str());
        if (control->opt_langcp && msgcodepage>0 && isSupportedCP(msgcodepage)) {
            Section_prop *sec = static_cast<Section_prop *>(control->GetSection("config"));
            char cstr[20];
            cstr[0] = 0;
            if (sec!=NULL) {
                char *countrystr = (char *)sec->Get_string("country"), *r=strchr(countrystr, ',');
                if (r!=NULL) *r=0;
                if (strlen(countrystr)>10) countrystr[0] = 0;
                sprintf(cstr, "%s,%d", countrystr, msgcodepage);
                SetVal("config", "country", cstr);
                const char *imestr = section->Get_string("ime");
                if (tonoime && !strcasecmp(imestr, "auto") && CheckDBCSCP(msgcodepage)) {
                    tonoime = false;
                    enableime = true;
                    SetIME();
                }
            }
        }
        if (tonoime) {
            tonoime = enableime = false;
#if defined(WIN32) && !defined(HX_DOS) && !defined(_WIN32_WINDOWS)
            ImmDisableIME((DWORD)(-1));
#endif
            SetIME();
        }
	}
	else {
		Prop_path* pathprop = section->Get_path("language");
        if (pathprop != NULL) {
            std::string path = pathprop->realpath;
            ResolvePath(path);

            FILE* f = testLoadLangFile(path.c_str());
            if (f) {
                fclose(f);
                LoadMessageFile(path.c_str());
            }
            else {
                std::string lang = section->Get_string("language");
                if (lang.size()) LoadMessageFile(lang.c_str());
            }
        }
    }
    std::string showdbcsstr = static_cast<Section_prop *>(control->GetSection("dosv"))->Get_string("showdbcsnodosv");
#if defined(USE_TTF)
    showdbcs = showdbcsstr=="true"||showdbcsstr=="1"||(showdbcsstr=="auto" && (loadlang || dbcs_sbcs));
#else
    showdbcs = showdbcsstr=="true"||showdbcsstr=="1"||(showdbcsstr=="auto" && loadlang);
#endif
    if (!IS_EGAVGA_ARCH) showdbcs = false;
}
