AGS: Add engine option widget to select language

AGS games can have multiple languages by providing
additional translations as .tra files. The language
selection is done by setting the config "translation"
 key to the tra file name. This means that we cannot
easily use the standard ScummVM language selection
for this purpose as the tra file name may not map
to language names easily.

The approach in this commit does something similar
to the original games by detecting at runtime the
available tra files.
This commit is contained in:
Thierry Crozat 2021-02-14 19:15:21 +00:00
parent 8e3376306a
commit 65fa2fa61b
2 changed files with 91 additions and 0 deletions

View file

@ -24,9 +24,15 @@
#include "common/config-manager.h" #include "common/config-manager.h"
#include "common/file.h" #include "common/file.h"
#include "common/md5.h" #include "common/md5.h"
#include "common/str-array.h"
#include "common/translation.h"
#include "ags/detection.h" #include "ags/detection.h"
#include "ags/detection_tables.h" #include "ags/detection_tables.h"
#include "gui/ThemeEval.h"
#include "gui/widget.h"
#include "gui/widgets/popup.h"
namespace AGS3 { namespace AGS3 {
static const char *const HEAD_SIG = "CLIB\x1a"; static const char *const HEAD_SIG = "CLIB\x1a";
@ -58,6 +64,85 @@ static bool isAGSFile(Common::File &f) {
return false; return false;
} }
class AGSOptionsWidget : public GUI::OptionsContainerWidget {
public:
explicit AGSOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain);
// OptionsContainerWidget API
void load() override;
bool save() override;
private:
// OptionsContainerWidget API
void defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const override;
GUI::PopUpWidget *_langPopUp;
Common::StringArray _traFileNames;
};
AGSOptionsWidget::AGSOptionsWidget(GuiObject *boss, const Common::String &name, const Common::String &domain) :
OptionsContainerWidget(boss, name, "AGSGameOptionsDialog", false, domain) {
GUI::StaticTextWidget *textWidget = new GUI::StaticTextWidget(widgetsBoss(), _dialogLayout + ".translation_desc", _("Game language:"), _("Language to use for multilingual games"));
textWidget->setAlign(Graphics::kTextAlignRight);
_langPopUp = new GUI::PopUpWidget(widgetsBoss(), _dialogLayout + ".translation");
_langPopUp->appendEntry(_("<default>"), (uint32)-1);
Common::String path = ConfMan.get("path", _domain);
Common::FSDirectory dir(path);
Common::ArchiveMemberList traFileList;
dir.listMatchingMembers(traFileList, "*.tra");
int i = 0;
for (Common::ArchiveMemberList::iterator iter = traFileList.begin(); iter != traFileList.end(); ++iter) {
Common::String traFileName = (*iter)->getName();
traFileName.erase(traFileName.size() - 4); // remove .tra extension
_traFileNames.push_back(traFileName);
_langPopUp->appendEntry(traFileName, i++);
}
}
void AGSOptionsWidget::defineLayout(GUI::ThemeEval &layouts, const Common::String &layoutName, const Common::String &overlayedLayout) const {
layouts.addDialog(layoutName, overlayedLayout);
layouts.addLayout(GUI::ThemeLayout::kLayoutVertical).addPadding(16, 16, 16, 16);
layouts.addLayout(GUI::ThemeLayout::kLayoutHorizontal).addPadding(0, 0, 0, 0);
layouts.addWidget("translation_desc", "OptionsLabel");
layouts.addWidget("translation", "PopUp").closeLayout();
layouts.closeLayout().closeDialog();
}
void AGSOptionsWidget::load() {
Common::ConfigManager::Domain *gameConfig = ConfMan.getDomain(_domain);
if (!gameConfig)
return;
uint32 curLangIndex = (uint32)-1;
Common::String curLang;
gameConfig->tryGetVal("translation", curLang);
if (!curLang.empty()) {
for (uint i = 0; i < _traFileNames.size(); ++i) {
if (_traFileNames[i].equalsIgnoreCase(curLang)) {
curLangIndex = i;
break;
}
}
}
_langPopUp->setSelectedTag(curLangIndex);
}
bool AGSOptionsWidget::save() {
uint langIndex = _langPopUp->getSelectedTag();
if (langIndex < _traFileNames.size())
ConfMan.set("translation", _traFileNames[langIndex], _domain);
else
ConfMan.removeKey("translation", _domain);
return true;
}
} // namespace AGS3 } // namespace AGS3
AGSMetaEngineDetection::AGSMetaEngineDetection() : AdvancedMetaEngineDetection(AGS::GAME_DESCRIPTIONS, AGSMetaEngineDetection::AGSMetaEngineDetection() : AdvancedMetaEngineDetection(AGS::GAME_DESCRIPTIONS,
@ -133,4 +218,8 @@ ADDetectedGame AGSMetaEngineDetection::fallbackDetect(const FileMap &allFiles, c
return ADDetectedGame(); return ADDetectedGame();
} }
GUI::OptionsContainerWidget *AGSMetaEngineDetection::buildEngineOptionsWidgetStatic(GUI::GuiObject *boss, const Common::String &name, const Common::String &target) const {
return new AGS3::AGSOptionsWidget(boss, name, target);
}
REGISTER_PLUGIN_STATIC(AGS_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, AGSMetaEngineDetection); REGISTER_PLUGIN_STATIC(AGS_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, AGSMetaEngineDetection);

View file

@ -69,6 +69,8 @@ public:
} }
ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override; ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const override;
GUI::OptionsContainerWidget *buildEngineOptionsWidgetStatic(GUI::GuiObject *boss, const Common::String &name, const Common::String &target) const override;
}; };
#endif #endif