Changeset - r24213:10613c2a6ba0
[Not reviewed]
master
0 5 0
Michael Lutz - 5 years ago 2020-05-17 21:31:56
michi@icosahedron.de
Codechange: Store GS lang texts in std::strings.
5 files changed with 185 insertions and 94 deletions:
0 comments (0 inline, 0 general)
src/game/game_text.cpp
Show inline comments
 
@@ -59,69 +59,46 @@ void NORETURN CDECL strgen_fatal(const c
 
}
 

	
 
/**
 
 * Create a new container for language strings.
 
 * @param language The language name.
 
 * @param end If not nullptr, terminate \a language at this position.
 
 */
 
LanguageStrings::LanguageStrings(const char *language, const char *end)
 
{
 
	this->language = stredup(language, end != nullptr ? end - 1 : nullptr);
 
}
 

	
 
/** Free everything. */
 
LanguageStrings::~LanguageStrings()
 
{
 
	free(this->language);
 
}
 

	
 
/**
 
 * Read all the raw language strings from the given file.
 
 * @param file The file to read from.
 
 * @return The raw strings, or nullptr upon error.
 
 */
 
std::unique_ptr<LanguageStrings> ReadRawLanguageStrings(const char *file)
 
LanguageStrings ReadRawLanguageStrings(const std::string &file)
 
{
 
	try {
 
		size_t to_read;
 
		FILE *fh = FioFOpenFile(file, "rb", GAME_DIR, &to_read);
 
		if (fh == nullptr) return nullptr;
 
	size_t to_read;
 
	FILE *fh = FioFOpenFile(file.c_str(), "rb", GAME_DIR, &to_read);
 
	if (fh == nullptr) return LanguageStrings();
 

	
 
		FileCloser fhClose(fh);
 
	FileCloser fhClose(fh);
 

	
 
		const char *langname = strrchr(file, PATHSEPCHAR);
 
		if (langname == nullptr) {
 
			langname = file;
 
		} else {
 
			langname++;
 
		}
 
	auto pos = file.rfind(PATHSEPCHAR);
 
	if (pos == std::string::npos) return LanguageStrings();
 
	std::string langname = file.substr(pos + 1);
 

	
 
		/* Check for invalid empty filename */
 
		if (*langname == '.' || *langname == 0) return nullptr;
 
	/* Check for invalid empty filename */
 
	if (langname.empty() || langname.front() == '.') return LanguageStrings();
 

	
 
		std::unique_ptr<LanguageStrings> ret(new LanguageStrings(langname, strchr(langname, '.')));
 
	LanguageStrings ret(langname.substr(0, langname.find('.')));
 

	
 
		char buffer[2048];
 
		while (to_read != 0 && fgets(buffer, sizeof(buffer), fh) != nullptr) {
 
			size_t len = strlen(buffer);
 
	char buffer[2048];
 
	while (to_read != 0 && fgets(buffer, sizeof(buffer), fh) != nullptr) {
 
		size_t len = strlen(buffer);
 

	
 
			/* Remove trailing spaces/newlines from the string. */
 
			size_t i = len;
 
			while (i > 0 && (buffer[i - 1] == '\r' || buffer[i - 1] == '\n' || buffer[i - 1] == ' ')) i--;
 
			buffer[i] = '\0';
 

	
 
			ret->lines.emplace_back(buffer, i);
 
		/* Remove trailing spaces/newlines from the string. */
 
		size_t i = len;
 
		while (i > 0 && (buffer[i - 1] == '\r' || buffer[i - 1] == '\n' || buffer[i - 1] == ' ')) i--;
 
		buffer[i] = '\0';
 

	
 
			if (len > to_read) {
 
				to_read = 0;
 
			} else {
 
				to_read -= len;
 
			}
 
		ret.lines.emplace_back(buffer, i);
 

	
 
		if (len > to_read) {
 
			to_read = 0;
 
		} else {
 
			to_read -= len;
 
		}
 
	}
 

	
 
		return ret;
 
	} catch (...) {
 
		return nullptr;
 
	}
 
	return ret;
 
}
 

	
 

	
 
@@ -138,7 +115,7 @@ struct StringListReader : StringReader {
 
	 * @param translation Are we reading a translation?
 
	 */
 
	StringListReader(StringData &data, const LanguageStrings &strings, bool master, bool translation) :
 
			StringReader(data, strings.language, master, translation), p(strings.lines.begin()), end(strings.lines.end())
 
			StringReader(data, strings.language.c_str(), master, translation), p(strings.lines.begin()), end(strings.lines.end())
 
	{
 
	}
 

	
 
@@ -215,12 +192,11 @@ struct StringNameWriter : HeaderWriter {
 
class LanguageScanner : protected FileScanner {
 
private:
 
	GameStrings *gs;
 
	char *exclude;
 
	std::string exclude;
 

	
 
public:
 
	/** Initialise */
 
	LanguageScanner(GameStrings *gs, const char *exclude) : gs(gs), exclude(stredup(exclude)) {}
 
	~LanguageScanner() { free(exclude); }
 
	LanguageScanner(GameStrings *gs, const std::string &exclude) : gs(gs), exclude(exclude) {}
 

	
 
	/**
 
	 * Scan.
 
@@ -232,10 +208,10 @@ public:
 

	
 
	bool AddFile(const char *filename, size_t basepath_length, const char *tar_filename) override
 
	{
 
		if (strcmp(filename, exclude) == 0) return true;
 
		if (exclude == filename) return true;
 

	
 
		auto ls = ReadRawLanguageStrings(filename);
 
		if (ls == nullptr) return false;
 
		if (!ls.IsValid()) return false;
 

	
 
		gs->raw_strings.push_back(std::move(ls));
 
		return true;
 
@@ -249,17 +225,16 @@ public:
 
GameStrings *LoadTranslations()
 
{
 
	const GameInfo *info = Game::GetInfo();
 
	char filename[512];
 
	strecpy(filename, info->GetMainScript(), lastof(filename));
 
	char *e = strrchr(filename, PATHSEPCHAR);
 
	if (e == nullptr) return nullptr;
 
	e++; // Make 'e' point after the PATHSEPCHAR
 
	std::string basename(info->GetMainScript());
 
	auto e = basename.rfind(PATHSEPCHAR);
 
	if (e == std::string::npos) return nullptr;
 
	basename.erase(e + 1);
 

	
 
	strecpy(e, "lang" PATHSEP "english.txt", lastof(filename));
 
	if (!FioCheckFileExists(filename, GAME_DIR)) return nullptr;
 
	std::string filename = basename + "lang" PATHSEP "english.txt";
 
	if (!FioCheckFileExists(filename.c_str() , GAME_DIR)) return nullptr;
 

	
 
	auto ls = ReadRawLanguageStrings(filename);
 
	if (ls == nullptr) return nullptr;
 
	if (!ls.IsValid()) return nullptr;
 

	
 
	GameStrings *gs = new GameStrings();
 
	try {
 
@@ -267,8 +242,7 @@ GameStrings *LoadTranslations()
 

	
 
		/* Scan for other language files */
 
		LanguageScanner scanner(gs, filename);
 
		strecpy(e, "lang" PATHSEP, lastof(filename));
 
		size_t len = strlen(filename);
 
		std::string ldir = basename + "lang" PATHSEP;
 

	
 
		const char *tar_filename = info->GetTarFile();
 
		TarList::iterator iter;
 
@@ -281,14 +255,14 @@ GameStrings *LoadTranslations()
 
				if (tar->second.tar_filename != iter->first) continue;
 

	
 
				/* Check the path and extension. */
 
				if (tar->first.size() <= len || tar->first.compare(0, len, filename) != 0) continue;
 
				if (tar->first.size() <= ldir.size() || tar->first.compare(0, ldir.size(), ldir) != 0) continue;
 
				if (tar->first.compare(tar->first.size() - 4, 4, ".txt") != 0) continue;
 

	
 
				scanner.AddFile(tar->first.c_str(), 0, tar_filename);
 
			}
 
		} else {
 
			/* Scan filesystem */
 
			scanner.Scan(filename);
 
			scanner.Scan(ldir.c_str());
 
		}
 

	
 
		gs->Compile();
 
@@ -303,7 +277,7 @@ GameStrings *LoadTranslations()
 
void GameStrings::Compile()
 
{
 
	StringData data(32);
 
	StringListReader master_reader(data, *this->raw_strings[0], true, false);
 
	StringListReader master_reader(data, this->raw_strings[0], true, false);
 
	master_reader.ParseFile();
 
	if (_errors != 0) throw std::exception();
 

	
 
@@ -314,12 +288,12 @@ void GameStrings::Compile()
 

	
 
	for (const auto &p : this->raw_strings) {
 
		data.FreeTranslation();
 
		StringListReader translation_reader(data, *p, false, strcmp(p->language, "english") != 0);
 
		StringListReader translation_reader(data, p, false, p.language != "english");
 
		translation_reader.ParseFile();
 
		if (_errors != 0) throw std::exception();
 

	
 
		this->compiled_strings.emplace_back(new LanguageStrings(p->language));
 
		TranslationWriter writer(this->compiled_strings.back()->lines);
 
		this->compiled_strings.emplace_back(p.language);
 
		TranslationWriter writer(this->compiled_strings.back().lines);
 
		writer.WriteLang(data);
 
	}
 
}
 
@@ -387,11 +361,11 @@ void ReconsiderGameScriptLanguage()
 
	language++;
 

	
 
	for (auto &p : _current_data->compiled_strings) {
 
		if (strcmp(p->language, language) == 0) {
 
			_current_data->cur_language = p;
 
		if (p.language == language) {
 
			_current_data->cur_language = &p;
 
			return;
 
		}
 
	}
 

	
 
	_current_data->cur_language = _current_data->compiled_strings[0];
 
	_current_data->cur_language = &_current_data->compiled_strings[0];
 
}
src/game/game_text.hpp
Show inline comments
 
@@ -18,23 +18,34 @@ void ReconsiderGameScriptLanguage();
 

	
 
/** Container for the raw (unencoded) language strings of a language. */
 
struct LanguageStrings {
 
	const char *language; ///< Name of the language (base filename).
 
	StringList lines;     ///< The lines of the file to pass into the parser/encoder.
 
	std::string language; ///< Name of the language (base filename). Empty string if invalid.
 
	StringList  lines;    ///< The lines of the file to pass into the parser/encoder.
 

	
 
	LanguageStrings(const char *language, const char *end = nullptr);
 
	~LanguageStrings();
 
	LanguageStrings() {}
 
	LanguageStrings(const std::string &lang) : language(lang) {}
 
	LanguageStrings(const LanguageStrings &other) : language(other.language), lines(other.lines) {}
 
	LanguageStrings(LanguageStrings &&other) : language(std::move(other.language)), lines(std::move(other.lines)) {}
 

	
 
	bool IsValid() const { return !this->language.empty(); }
 
};
 

	
 
/** Container for all the game strings. */
 
struct GameStrings {
 
	uint version;                                  ///< The version of the language strings.
 
	std::shared_ptr<LanguageStrings> cur_language; ///< The current (compiled) language.
 
	uint version;                  ///< The version of the language strings.
 
	LanguageStrings *cur_language; ///< The current (compiled) language.
 

	
 
	std::vector<std::unique_ptr<LanguageStrings>> raw_strings;      ///< The raw strings per language, first must be English/the master language!.
 
	std::vector<std::shared_ptr<LanguageStrings>> compiled_strings; ///< The compiled strings per language, first must be English/the master language!.
 
	StringList string_names;                                        ///< The names of the compiled strings.
 
	std::vector<LanguageStrings> raw_strings;      ///< The raw strings per language, first must be English/the master language!.
 
	std::vector<LanguageStrings> compiled_strings; ///< The compiled strings per language, first must be English/the master language!.
 
	StringList string_names;                       ///< The names of the compiled strings.
 

	
 
	void Compile();
 

	
 
	GameStrings() = default;
 

	
 
	GameStrings(const GameStrings &) = delete;
 
	GameStrings(GameStrings &&) = delete;
 
	GameStrings &operator=(const GameStrings &) = delete;
 
	GameStrings &operator=(GameStrings &&) = delete;
 
};
 

	
 
#endif /* GAME_TEXT_HPP */
src/saveload/game_sl.cpp
Show inline comments
 
@@ -113,23 +113,23 @@ static void Save_GSDT()
 

	
 
extern GameStrings *_current_data;
 

	
 
static const char *_game_saveload_string;
 
static std::string _game_saveload_string;
 
static uint _game_saveload_strings;
 

	
 
static const SaveLoad _game_language_header[] = {
 
	SLEG_STR(_game_saveload_string, SLE_STR),
 
	SLEG_VAR(_game_saveload_strings, SLE_UINT32),
 
	 SLE_END()
 
	SLEG_SSTR(_game_saveload_string, SLE_STR),
 
	 SLEG_VAR(_game_saveload_strings, SLE_UINT32),
 
	  SLE_END()
 
};
 

	
 
static const SaveLoad _game_language_string[] = {
 
	SLEG_STR(_game_saveload_string, SLE_STR | SLF_ALLOW_CONTROL),
 
	 SLE_END()
 
	SLEG_SSTR(_game_saveload_string, SLE_STR | SLF_ALLOW_CONTROL),
 
	  SLE_END()
 
};
 

	
 
static void SaveReal_GSTR(const LanguageStrings *ls)
 
{
 
	_game_saveload_string  = ls->language;
 
	_game_saveload_string  = ls->language.c_str();
 
	_game_saveload_strings = (uint)ls->lines.size();
 

	
 
	SlObject(nullptr, _game_language_header);
 
@@ -145,13 +145,13 @@ static void Load_GSTR()
 
	_current_data = new GameStrings();
 

	
 
	while (SlIterateArray() != -1) {
 
		_game_saveload_string = nullptr;
 
		_game_saveload_string.clear();
 
		SlObject(nullptr, _game_language_header);
 

	
 
		std::unique_ptr<LanguageStrings> ls(new LanguageStrings(_game_saveload_string != nullptr ? _game_saveload_string : ""));
 
		LanguageStrings ls(_game_saveload_string);
 
		for (uint i = 0; i < _game_saveload_strings; i++) {
 
			SlObject(nullptr, _game_language_string);
 
			ls->lines.emplace_back(_game_saveload_string != nullptr ? _game_saveload_string : "");
 
			ls.lines.emplace_back(_game_saveload_string);
 
		}
 

	
 
		_current_data->raw_strings.push_back(std::move(ls));
 
@@ -174,7 +174,7 @@ static void Save_GSTR()
 

	
 
	for (uint i = 0; i < _current_data->raw_strings.size(); i++) {
 
		SlSetArrayIndex(i);
 
		SlAutolength((AutolengthProc *)SaveReal_GSTR, _current_data->raw_strings[i].get());
 
		SlAutolength((AutolengthProc *)SaveReal_GSTR, &_current_data->raw_strings[i]);
 
	}
 
}
 

	
src/saveload/saveload.cpp
Show inline comments
 
@@ -44,6 +44,7 @@
 
#include "../fios.h"
 
#include "../error.h"
 
#include <atomic>
 
#include <string>
 

	
 
#include "table/strings.h"
 

	
 
@@ -902,6 +903,21 @@ static inline size_t SlCalcStringLen(con
 
}
 

	
 
/**
 
 * Calculate the gross length of the string that it
 
 * will occupy in the savegame. This includes the real length, returned
 
 * by SlCalcNetStringLen and the length that the index will occupy.
 
 * @param ptr Pointer to the \c std::string.
 
 * @return The gross length of the string.
 
 */
 
static inline size_t SlCalcStdStringLen(const void *ptr)
 
{
 
	const std::string *str = reinterpret_cast<const std::string *>(ptr);
 

	
 
	size_t len = str->length();
 
	return len + SlGetArrayLength(len); // also include the length of the index
 
}
 

	
 
/**
 
 * Save/Load a string.
 
 * @param ptr the string being manipulated
 
 * @param length of the string (full length)
 
@@ -981,6 +997,53 @@ static void SlString(void *ptr, size_t l
 
}
 

	
 
/**
 
 * Save/Load a \c std::string.
 
 * @param ptr the string being manipulated
 
 * @param conv must be SLE_FILE_STRING
 
 */
 
static void SlStdString(void *ptr, VarType conv)
 
{
 
	std::string *str = reinterpret_cast<std::string *>(ptr);
 

	
 
	switch (_sl.action) {
 
		case SLA_SAVE: {
 
			size_t len = str->length();
 
			SlWriteArrayLength(len);
 
			SlCopyBytes(const_cast<void *>(static_cast<const void *>(str->c_str())), len);
 
			break;
 
		}
 

	
 
		case SLA_LOAD_CHECK:
 
		case SLA_LOAD: {
 
			size_t len = SlReadArrayLength();
 
			char *buf = AllocaM(char, len + 1);
 

	
 
			SlCopyBytes(buf, len);
 
			buf[len] = '\0'; // properly terminate the string
 

	
 
			StringValidationSettings settings = SVS_REPLACE_WITH_QUESTION_MARK;
 
			if ((conv & SLF_ALLOW_CONTROL) != 0) {
 
				settings = settings | SVS_ALLOW_CONTROL_CODE;
 
				if (IsSavegameVersionBefore(SLV_169)) {
 
					str_fix_scc_encoded(buf, buf + len);
 
				}
 
			}
 
			if ((conv & SLF_ALLOW_NEWLINE) != 0) {
 
				settings = settings | SVS_ALLOW_NEWLINE;
 
			}
 
			str_validate(buf, buf + len, settings);
 

	
 
			// Store sanitized string.
 
			str->assign(buf);
 
		}
 

	
 
		case SLA_PTRS: break;
 
		case SLA_NULL: break;
 
		default: NOT_REACHED();
 
	}
 
}
 

	
 
/**
 
 * Return the size in bytes of a certain type of atomic array
 
 * @param length The length of the array counted in elements
 
 * @param conv VarType type of the variable that is used in calculating the size
 
@@ -1403,6 +1466,7 @@ size_t SlCalcObjMemberLength(const void 
 
		case SL_STR:
 
		case SL_LST:
 
		case SL_DEQUE:
 
		case SL_STDSTR:
 
			/* CONDITIONAL saveload types depend on the savegame version */
 
			if (!SlIsObjectValidInSavegame(sld)) break;
 

	
 
@@ -1413,6 +1477,7 @@ size_t SlCalcObjMemberLength(const void 
 
				case SL_STR: return SlCalcStringLen(GetVariableAddress(object, sld), sld->length, sld->conv);
 
				case SL_LST: return SlCalcListLen(GetVariableAddress(object, sld));
 
				case SL_DEQUE: return SlCalcDequeLen(GetVariableAddress(object, sld), sld->conv);
 
				case SL_STDSTR: return SlCalcStdStringLen(GetVariableAddress(object, sld));
 
				default: NOT_REACHED();
 
			}
 
			break;
 
@@ -1461,6 +1526,10 @@ static bool IsVariableSizeRight(const Sa
 
			/* These should be pointer sized, or fixed array. */
 
			return sld->size == sizeof(void *) || sld->size == sld->length;
 

	
 
		case SL_STDSTR:
 
			/* These should be all pointers to std::string. */
 
			return sld->size == sizeof(std::string);
 

	
 
		default:
 
			return true;
 
	}
 
@@ -1482,6 +1551,7 @@ bool SlObjectMember(void *ptr, const Sav
 
		case SL_STR:
 
		case SL_LST:
 
		case SL_DEQUE:
 
		case SL_STDSTR:
 
			/* CONDITIONAL saveload types depend on the savegame version */
 
			if (!SlIsObjectValidInSavegame(sld)) return false;
 
			if (SlSkipVariableOnLoad(sld)) return false;
 
@@ -1510,6 +1580,7 @@ bool SlObjectMember(void *ptr, const Sav
 
				case SL_STR: SlString(ptr, sld->length, sld->conv); break;
 
				case SL_LST: SlList(ptr, (SLRefType)conv); break;
 
				case SL_DEQUE: SlDeque(ptr, conv); break;
 
				case SL_STDSTR: SlStdString(ptr, sld->conv); break;
 
				default: NOT_REACHED();
 
			}
 
			break;
src/saveload/saveload.h
Show inline comments
 
@@ -485,6 +485,7 @@ enum SaveLoadTypes {
 
	SL_STR         =  3, ///< Save/load a string.
 
	SL_LST         =  4, ///< Save/load a list.
 
	SL_DEQUE       =  5, ///< Save/load a deque.
 
	SL_STDSTR      =  6, ///< Save/load a \c std::string.
 
	/* non-normal save-load types */
 
	SL_WRITEBYTE   =  8,
 
	SL_VEH_INCLUDE =  9,
 
@@ -568,6 +569,16 @@ typedef SaveLoad SaveLoadGlobVarList;
 
#define SLE_CONDSTR(base, variable, type, length, from, to) SLE_GENERAL(SL_STR, base, variable, type, length, from, to)
 

	
 
/**
 
 * Storage of a \c std::string in some savegame versions.
 
 * @param base     Name of the class or struct containing the string.
 
 * @param variable Name of the variable in the class or struct referenced by \a base.
 
 * @param type     Storage of the data in memory and in the savegame.
 
 * @param from     First savegame version that has the string.
 
 * @param to       Last savegame version that has the string.
 
 */
 
#define SLE_CONDSSTR(base, variable, type, from, to) SLE_GENERAL(SL_STDSTR, base, variable, type, 0, from, to)
 

	
 
/**
 
 * Storage of a list in some savegame versions.
 
 * @param base     Name of the class or struct containing the list.
 
 * @param variable Name of the variable in the class or struct referenced by \a base.
 
@@ -622,6 +633,14 @@ typedef SaveLoad SaveLoadGlobVarList;
 
#define SLE_STR(base, variable, type, length) SLE_CONDSTR(base, variable, type, length, SL_MIN_VERSION, SL_MAX_VERSION)
 

	
 
/**
 
 * Storage of a \c std::string in every savegame version.
 
 * @param base     Name of the class or struct containing the string.
 
 * @param variable Name of the variable in the class or struct referenced by \a base.
 
 * @param type     Storage of the data in memory and in the savegame.
 
 */
 
#define SLE_SSTR(base, variable, type) SLE_CONDSSTR(base, variable, type, SL_MIN_VERSION, SL_MAX_VERSION)
 

	
 
/**
 
 * Storage of a list in every savegame version.
 
 * @param base     Name of the class or struct containing the list.
 
 * @param variable Name of the variable in the class or struct referenced by \a base.
 
@@ -702,6 +721,15 @@ typedef SaveLoad SaveLoadGlobVarList;
 
#define SLEG_CONDSTR(variable, type, length, from, to) SLEG_GENERAL(SL_STR, variable, type, length, from, to)
 

	
 
/**
 
 * Storage of a global \c std::string in some savegame versions.
 
 * @param variable Name of the global variable.
 
 * @param type     Storage of the data in memory and in the savegame.
 
 * @param from     First savegame version that has the string.
 
 * @param to       Last savegame version that has the string.
 
 */
 
#define SLEG_CONDSSTR(variable, type, from, to) SLEG_GENERAL(SL_STDSTR, variable, type, 0, from, to)
 

	
 
/**
 
 * Storage of a global list in some savegame versions.
 
 * @param variable Name of the global variable.
 
 * @param type     Storage of the data in memory and in the savegame.
 
@@ -739,6 +767,13 @@ typedef SaveLoad SaveLoadGlobVarList;
 
#define SLEG_STR(variable, type) SLEG_CONDSTR(variable, type, sizeof(variable), SL_MIN_VERSION, SL_MAX_VERSION)
 

	
 
/**
 
 * Storage of a global \c std::string in every savegame version.
 
 * @param variable Name of the global variable.
 
 * @param type     Storage of the data in memory and in the savegame.
 
 */
 
#define SLEG_SSTR(variable, type) SLEG_CONDSSTR(variable, type, SL_MIN_VERSION, SL_MAX_VERSION)
 

	
 
/**
 
 * Storage of a global list in every savegame version.
 
 * @param variable Name of the global variable.
 
 * @param type     Storage of the data in memory and in the savegame.
0 comments (0 inline, 0 general)