mirror of
https://github.com/joncampbell123/dosbox-x.git
synced 2025-10-15 20:46:55 +08:00
616 lines
25 KiB
C++
616 lines
25 KiB
C++
/*
|
|
* 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();
|
|
extern const char * RunningProgram;
|
|
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);
|
|
|
|
#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
|
|
}
|
|
|
|
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("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 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?");
|
|
}
|
|
|
|
// 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;
|
|
Cross::GetPlatformConfigDir(config_path);
|
|
Cross::GetPlatformResDir(res_path);
|
|
|
|
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(loadlangcp && msgcodepage > 0) {
|
|
const char* layoutname = DOS_GetLoadedLayout();
|
|
if(!IS_DOSV && !IS_JEGA_ARCH && !IS_PC98_ARCH && layoutname != nullptr) {
|
|
toSetCodePage(nullptr, msgcodepage, -1);
|
|
}
|
|
}
|
|
|
|
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);
|
|
if (testLoadLangFile(path.c_str()))
|
|
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;
|
|
}
|