Skip to content
Permalink
Browse files

Extend the image localization system to sounds and music (#3935)

Fixes #2987
  • Loading branch information...
CelticMinstrel committed Mar 3, 2019
1 parent a95403b commit 9cccfd73b0128e467912fca3470d52265622bd15
Showing with 112 additions and 92 deletions.
  1. +89 −0 src/filesystem.cpp
  2. +4 −0 src/filesystem.hpp
  3. +2 −0 src/game_config_manager.cpp
  4. +1 −0 src/gui/dialogs/title_screen.cpp
  5. +2 −0 src/menu_events.cpp
  6. +2 −90 src/picture.cpp
  7. +10 −2 src/sound.cpp
  8. +2 −0 src/sound.hpp
@@ -16,11 +16,13 @@
* @file
* File-IO
*/
#define GETTEXT_DOMAIN "wesnoth-lib"

#include "filesystem.hpp"

#include "config.hpp"
#include "game_config.hpp"
#include "gettext.hpp"
#include "log.hpp"
#include "serialization/unicode.hpp"
#include "serialization/unicode_cast.hpp"
@@ -1513,4 +1515,91 @@ std::string sanitize_path(const std::string& path)
return canonicalized;
}

namespace {
// Check if localized file is up-to-date according to l10n track index.
// Make sure only that the image is not explicitly recorded as fuzzy,
// in order to be able to use non-tracked images (e.g. from UMC).
std::set<std::string> fuzzy_localized_files;
bool localized_file_uptodate(const std::string& loc_file)
{
if(fuzzy_localized_files.empty()) {
// First call, parse track index to collect fuzzy files by path.
std::string fsep = "\xC2\xA6"; // UTF-8 for "broken bar"
std::string trackpath = filesystem::get_binary_file_location("", "l10n-track");

// l10n-track file not present. Assume image is up-to-date.
if(trackpath.empty()) {
return true;
}

std::string contents = filesystem::read_file(trackpath);

for(const std::string& line : utils::split(contents, '\n')) {
std::size_t p1 = line.find(fsep);
if(p1 == std::string::npos) {
continue;
}

std::string state = line.substr(0, p1);
boost::trim(state);
if(state == "fuzzy") {
std::size_t p2 = line.find(fsep, p1 + fsep.length());
if(p2 == std::string::npos) {
continue;
}

std::string relpath = line.substr(p1 + fsep.length(), p2 - p1 - fsep.length());
fuzzy_localized_files.insert(game_config::path + '/' + relpath);
}
}

fuzzy_localized_files.insert(""); // make sure not empty any more
}

return fuzzy_localized_files.count(loc_file) == 0;
}
}

// Return path to localized counterpart of the given file, if any, or empty string.
// Localized counterpart may also be requested to have a suffix to base name.
std::string get_localized_path(const std::string& file, const std::string& suff)
{
std::string dir = filesystem::directory_name(file);
std::string base = filesystem::base_name(file);

const std::size_t pos_ext = base.rfind(".");

std::string loc_base;
if(pos_ext != std::string::npos) {
loc_base = base.substr(0, pos_ext) + suff + base.substr(pos_ext);
} else {
loc_base = base + suff;
}

// TRANSLATORS: This is the language code which will be used
// to store and fetch localized non-textual resources, such as images,
// when they exist. Normally it is just the code of the PO file itself,
// e.g. "de" of de.po for German. But it can also be a comma-separated
// list of language codes by priority, when the localized resource
// found for first of those languages will be used. This is useful when
// two languages share sufficient commonality, that they can use each
// other's resources rather than duplicating them. For example,
// Swedish (sv) and Danish (da) are such, so Swedish translator could
// translate this message as "sv,da", while Danish as "da,sv".
std::vector<std::string> langs = utils::split(_("language code for localized resources^en_US"));

// In case even the original image is split into base and overlay,
// add en_US with lowest priority, since the message above will
// not have it when translated.
langs.push_back("en_US");
for(const std::string& lang : langs) {
std::string loc_file = dir + "/" + "l10n" + "/" + lang + "/" + loc_base;
if(filesystem::file_exists(loc_file) && localized_file_uptodate(loc_file)) {
return loc_file;
}
}

return "";
}

} // namespace filesystem
@@ -424,5 +424,9 @@ std::string get_independent_image_path(const std::string &filename);
*/
std::string get_program_invocation(const std::string &program_name);

/**
* Returns the localized version of the given filename, if it exists.
*/
std::string get_localized_path(const std::string& file, const std::string& suff = "");

}
@@ -36,6 +36,7 @@
#include "game_version.hpp"
#include "theme.hpp"
#include "picture.hpp"
#include "sound.hpp"
#include "serialization/schema_validator.hpp"

static lg::log_domain log_config("config");
@@ -525,6 +526,7 @@ void game_config_manager::reload_changed_game_config()
init_game_config(FORCE_RELOAD);

image::flush_cache();
sound::flush_cache();
}

void game_config_manager::load_game_config_for_editor()
@@ -386,6 +386,7 @@ void title_screen::pre_show(window& win)
if(game_.change_language()) {
t_string::reset_translations();
::image::flush_cache();
sound::flush_cache();
on_resize(win);
}
} catch(const std::runtime_error& e) {
@@ -81,6 +81,7 @@
#include "units/udisplay.hpp"
#include "units/unit.hpp"
#include "whiteboard/manager.hpp"
#include "sound.hpp"

static lg::log_domain log_engine("engine");
#define ERR_NG LOG_STREAM(err, log_engine)
@@ -1401,6 +1402,7 @@ std::vector<std::string> menu_handler::get_commands_list()
void console_handler::do_refresh()
{
image::flush_cache();
sound::flush_cache();

menu_handler_.gui_->create_buttons();
menu_handler_.gui_->redraw_everything();
@@ -17,15 +17,12 @@
* Routines for images: load, scale, re-color, etc.
*/

#define GETTEXT_DOMAIN "wesnoth-lib"

#include "picture.hpp"

#include "config.hpp"
#include "display.hpp"
#include "filesystem.hpp"
#include "game_config.hpp"
#include "gettext.hpp"
#include "image_modifications.hpp"
#include "log.hpp"
#include "preferences/general.hpp"
@@ -463,91 +460,6 @@ bool locator::value::operator<(const value& a) const
return false;
}

// Check if localized file is up-to-date according to l10n track index.
// Make sure only that the image is not explicitly recorded as fuzzy,
// in order to be able to use non-tracked images (e.g. from UMC).
static std::set<std::string> fuzzy_localized_files;
static bool localized_file_uptodate(const std::string& loc_file)
{
if(fuzzy_localized_files.empty()) {
// First call, parse track index to collect fuzzy files by path.
std::string fsep = "\xC2\xA6"; // UTF-8 for "broken bar"
std::string trackpath = filesystem::get_binary_file_location("", "l10n-track");

// l10n-track file not present. Assume image is up-to-date.
if(trackpath.empty()) {
return true;
}

std::string contents = filesystem::read_file(trackpath);

for(const std::string& line : utils::split(contents, '\n')) {
std::size_t p1 = line.find(fsep);
if(p1 == std::string::npos) {
continue;
}

std::string state = line.substr(0, p1);
boost::trim(state);
if(state == "fuzzy") {
std::size_t p2 = line.find(fsep, p1 + fsep.length());
if(p2 == std::string::npos) {
continue;
}

std::string relpath = line.substr(p1 + fsep.length(), p2 - p1 - fsep.length());
fuzzy_localized_files.insert(game_config::path + '/' + relpath);
}
}

fuzzy_localized_files.insert(""); // make sure not empty any more
}

return fuzzy_localized_files.count(loc_file) == 0;
}

// Return path to localized counterpart of the given file, if any, or empty string.
// Localized counterpart may also be requested to have a suffix to base name.
static std::string get_localized_path(const std::string& file, const std::string& suff = "")
{
std::string dir = filesystem::directory_name(file);
std::string base = filesystem::base_name(file);

const std::size_t pos_ext = base.rfind(".");

std::string loc_base;
if(pos_ext != std::string::npos) {
loc_base = base.substr(0, pos_ext) + suff + base.substr(pos_ext);
} else {
loc_base = base + suff;
}

// TRANSLATORS: This is the language code which will be used
// to store and fetch localized non-textual resources, such as images,
// when they exist. Normally it is just the code of the PO file itself,
// e.g. "de" of de.po for German. But it can also be a comma-separated
// list of language codes by priority, when the localized resource
// found for first of those languages will be used. This is useful when
// two languages share sufficient commonality, that they can use each
// other's resources rather than duplicating them. For example,
// Swedish (sv) and Danish (da) are such, so Swedish translator could
// translate this message as "sv,da", while Danish as "da,sv".
std::vector<std::string> langs = utils::split(_("language code for localized resources^en_US"));

// In case even the original image is split into base and overlay,
// add en_US with lowest priority, since the message above will
// not have it when translated.
langs.push_back("en_US");
for(const std::string& lang : langs) {
std::string loc_file = dir + "/" + "l10n" + "/" + lang + "/" + loc_base;
if(filesystem::file_exists(loc_file) && localized_file_uptodate(loc_file)) {
return loc_file;
}
}

return "";
}

// Ensure PNG images with an indexed palette are converted to 32-bit RGBA.
static void standardize_surface_format(surface& surf)
{
@@ -582,7 +494,7 @@ static surface load_image_file(const image::locator& loc)
{
if(!location.empty()) {
// Check if there is a localized image.
const std::string loc_location = get_localized_path(location);
const std::string loc_location = filesystem::get_localized_path(location);
if(!loc_location.empty()) {
location = loc_location;
}
@@ -594,7 +506,7 @@ static surface load_image_file(const image::locator& loc)

// If there was no standalone localized image, check if there is an overlay.
if(!res.null() && loc_location.empty()) {
const std::string ovr_location = get_localized_path(location, "--overlay");
const std::string ovr_location = filesystem::get_localized_path(location, "--overlay");
if(!ovr_location.empty()) {
add_localized_overlay(ovr_location, res);
}
@@ -191,6 +191,12 @@ std::vector<std::shared_ptr<sound::music_track>>::const_iterator find_track(cons

namespace sound
{
void flush_cache()
{
sound_cache.clear();
music_cache.clear();
}

boost::optional<unsigned int> get_current_track_index()
{
if(current_track_index >= current_track_list.size()){
@@ -609,7 +615,8 @@ static void play_new_music()
return;
}

const std::string& filename = current_track->file_path();
const std::string localized = filesystem::get_localized_path(current_track->file_path());
const std::string& filename = localized.empty() ? current_track->file_path() : localized;

auto itor = music_cache.find(filename);
if(itor == music_cache.end()) {
@@ -887,9 +894,10 @@ static Mix_Chunk* load_chunk(const std::string& file, channel_group group)

temp_chunk.group = group;
const std::string& filename = filesystem::get_binary_file_location("sounds", file);
const std::string localized = filesystem::get_localized_path(filename);

if(!filename.empty()) {
filesystem::rwops_ptr rwops = filesystem::make_read_RWops(filename);
filesystem::rwops_ptr rwops = filesystem::make_read_RWops(localized.empty() ? filename : localized);
temp_chunk.set_data(Mix_LoadWAV_RW(rwops.release(), true)); // SDL takes ownership of rwops
} else {
ERR_AUDIO << "Could not load sound file '" << file << "'." << std::endl;
@@ -113,4 +113,6 @@ unsigned int get_num_tracks();
void remove_track(unsigned int i);
void play_track(unsigned int i);

void flush_cache();

}

0 comments on commit 9cccfd7

Please sign in to comment.
You can’t perform that action at this time.