Mercurial > hg > openttd
changeset 18727:6f49f2276a02 draft
(svn r23575) -Codechange: split class definition and implementation of base strgen classes
author | rubidium <rubidium@openttd.org> |
---|---|
date | Sat, 17 Dec 2011 17:25:50 +0000 |
parents | 9e987e309252 |
children | bc1d87f9a787 |
files | Makefile.lang.in src/strgen/strgen.cpp src/strgen/strgen.h |
diffstat | 3 files changed, 446 insertions(+), 409 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile.lang.in +++ b/Makefile.lang.in @@ -42,7 +42,7 @@ all: table/strings.h $(LANGS) -strgen.o: $(SRC_DIR)/strgen/strgen.cpp endian_host.h $(SRC_DIR)/table/control_codes.h $(SRC_DIR)/table/strgen_tables.h +strgen.o: $(SRC_DIR)/strgen/strgen.cpp $(SRC_DIR)/strgen/strgen.h endian_host.h $(SRC_DIR)/table/control_codes.h $(SRC_DIR)/table/strgen_tables.h $(E) '$(STAGE) Compiling $(<:$(SRC_DIR)/%.cpp=%.cpp)' $(Q)$(CXX_BUILD) $(CFLAGS_BUILD) -DSTRGEN -c -o $@ $<
--- a/src/strgen/strgen.cpp +++ b/src/strgen/strgen.cpp @@ -17,6 +17,8 @@ #include "../misc/getoptdata.h" #include "../table/control_codes.h" +#include "strgen.h" + #include <stdarg.h> #include <exception> @@ -49,218 +51,189 @@ static const ptrdiff_t MAX_COMMAND_PARAM_SIZE = 100; ///< Maximum size of every command block, not counting the name of the command itself static const CmdStruct *ParseCommandString(const char **str, char *param, int *argno, int *casei); -/** Container for the different cases of a string. */ -struct Case { - int caseidx; ///< The index of the case. - char *string; ///< The translation of the case. - Case *next; ///< The next, chained, case. +/** + * Create a new case. + * @param caseidx The index of the case. + * @param string The translation of the case. + * @param next The next chained case. + */ +Case::Case(int caseidx, const char *string, Case *next) : + caseidx(caseidx), string(strdup(string)), next(next) +{ +} + +/** Free everything we allocated. */ +Case::~Case() +{ + free(this->string); + delete this->next; +} + +/** + * Create a new string. + * @param name The name of the string. + * @param english The english "translation" of the string. + * @param index The index in the string table. + * @param line The line this string was found on. + */ +LangString::LangString(const char *name, const char *english, int index, int line) : + name(strdup(name)), english(strdup(english)), translated(NULL), + hash_next(0), index(index), line(line), translated_case(NULL) +{ +} - /** - * Create a new case. - * @param caseidx The index of the case. - * @param string The translation of the case. - * @param next The next chained case. - */ - Case(int caseidx, const char *string, Case *next) : - caseidx(caseidx), string(strdup(string)), next(next) - { - } +/** Free everything we allocated. */ +LangString::~LangString() +{ + free(this->name); + free(this->english); + free(this->translated); + delete this->translated_case; +} + +/** Free all data related to the translation. */ +void LangString::FreeTranslation() +{ + free(this->translated); + this->translated = NULL; + + delete this->translated_case; + this->translated_case = NULL; +} - /** Free everything we allocated. */ - ~Case() - { - free(this->string); - delete this->next; +/** + * Create a new string data container. + * @param max_strings The maximum number of strings. + */ +StringData::StringData(size_t tabs) : tabs(tabs), max_strings(tabs * STRINGS_IN_TAB) +{ + this->strings = CallocT<LangString *>(max_strings); + this->hash_heads = CallocT<uint16>(max_strings); + this->next_string_id = 0; +} + +/** Free everything we allocated. */ +StringData::~StringData() +{ + for (size_t i = 0; i < this->max_strings; i++) delete this->strings[i]; + free(this->strings); + free(this->hash_heads); +} + +/** Free all data related to the translation. */ +void StringData::FreeTranslation() +{ + for (size_t i = 0; i < this->max_strings; i++) { + LangString *ls = this->strings[i]; + if (ls != NULL) ls->FreeTranslation(); } -}; - -/** Information about a single string. */ -struct LangString { - char *name; ///< Name of the string. - char *english; ///< English text. - char *translated; ///< Translated text. - uint16 hash_next; ///< Next hash entry. - uint16 index; ///< The index in the language file. - int line; ///< Line of string in source-file. - Case *translated_case; ///< Cases of the translation. - - /** - * Create a new string. - * @param name The name of the string. - * @param english The english "translation" of the string. - * @param index The index in the string table. - * @param line The line this string was found on. - */ - LangString(const char *name, const char *english, int index, int line) : - name(strdup(name)), english(strdup(english)), translated(NULL), - hash_next(0), index(index), line(line), translated_case(NULL) - { - } +} - /** Free everything we allocated. */ - ~LangString() - { - free(this->name); - free(this->english); - free(this->translated); - delete this->translated_case; - } +/** + * Create a hash of the string for finding them back quickly. + * @param s The string to hash. + * @return The hashed string. + */ +uint StringData::HashStr(const char *s) const +{ + uint hash = 0; + for (; *s != '\0'; s++) hash = ROL(hash, 3) ^ *s; + return hash % this->max_strings; +} - /** Free all data related to the translation. */ - void FreeTranslation() - { - free(this->translated); - this->translated = NULL; +/** + * Add a newly created LangString. + * @param s The name of the string. + * @param ls The string to add. + */ +void StringData::Add(const char *s, LangString *ls) +{ + uint hash = this->HashStr(s); + ls->hash_next = this->hash_heads[hash]; + /* Off-by-one for hash find. */ + this->hash_heads[hash] = ls->index + 1; + this->strings[ls->index] = ls; +} - delete this->translated_case; - this->translated_case = NULL; +/** + * Find a LangString based on the string name. + * @param s The string name to search on. + * @return The LangString or NULL if it is not known. + */ +LangString *StringData::Find(const char *s) +{ + int idx = this->hash_heads[this->HashStr(s)]; + + while (--idx >= 0) { + LangString *ls = this->strings[idx]; + + if (strcmp(ls->name, s) == 0) return ls; + idx = ls->hash_next; } -}; - -/** Information about the currently known strings. */ -struct StringData { - static const uint STRINGS_IN_TAB = 2048; + return NULL; +} - LangString **strings; ///< Array of all known strings. - uint16 *hash_heads; ///< Hash table for the strings. - size_t tabs; ///< The number of 'tabs' of strings. - size_t max_strings; ///< The maxmimum number of strings. - int next_string_id; ///< The next string ID to allocate. - - /** - * Create a new string data container. - * @param max_strings The maximum number of strings. - */ - StringData(size_t tabs = 32) : tabs(tabs), max_strings(tabs * STRINGS_IN_TAB) - { - this->strings = CallocT<LangString *>(max_strings); - this->hash_heads = CallocT<uint16>(max_strings); - this->next_string_id = 0; +/** + * Create a compound hash. + * @param hash The hash to add the string hash to. + * @param s The string hash. + * @return The new hash. + */ +uint StringData::VersionHashStr(uint hash, const char *s) const +{ + for (; *s != '\0'; s++) { + hash = ROL(hash, 3) ^ *s; + hash = (hash & 1 ? hash >> 1 ^ 0xDEADBEEF : hash >> 1); } + return hash; +} - /** Free everything we allocated. */ - ~StringData() - { - for (size_t i = 0; i < this->max_strings; i++) delete this->strings[i]; - free(this->strings); - free(this->hash_heads); - } +/** + * Make a hash of the file to get a unique "version number" + * @return The version number. + */ +uint StringData::Version() const +{ + uint hash = 0; + + for (size_t i = 0; i < this->max_strings; i++) { + const LangString *ls = this->strings[i]; - /** Free all data related to the translation. */ - void FreeTranslation() - { - for (size_t i = 0; i < this->max_strings; i++) { - LangString *ls = this->strings[i]; - if (ls != NULL) ls->FreeTranslation(); + if (ls != NULL) { + const CmdStruct *cs; + const char *s; + char buf[MAX_COMMAND_PARAM_SIZE]; + int argno; + int casei; + + s = ls->name; + hash ^= i * 0x717239; + hash = (hash & 1 ? hash >> 1 ^ 0xDEADBEEF : hash >> 1); + hash = this->VersionHashStr(hash, s + 1); + + s = ls->english; + while ((cs = ParseCommandString(&s, buf, &argno, &casei)) != NULL) { + if (cs->flags & C_DONTCOUNT) continue; + + hash ^= (cs - _cmd_structs) * 0x1234567; + hash = (hash & 1 ? hash >> 1 ^ 0xF00BAA4 : hash >> 1); + } } } - /** - * Create a hash of the string for finding them back quickly. - * @param s The string to hash. - * @return The hashed string. - */ - uint HashStr(const char *s) const - { - uint hash = 0; - for (; *s != '\0'; s++) hash = ROL(hash, 3) ^ *s; - return hash % this->max_strings; - } - - /** - * Add a newly created LangString. - * @param s The name of the string. - * @param ls The string to add. - */ - void Add(const char *s, LangString *ls) - { - uint hash = this->HashStr(s); - ls->hash_next = this->hash_heads[hash]; - /* Off-by-one for hash find. */ - this->hash_heads[hash] = ls->index + 1; - this->strings[ls->index] = ls; - } - - /** - * Find a LangString based on the string name. - * @param s The string name to search on. - * @return The LangString or NULL if it is not known. - */ - LangString *Find(const char *s) - { - int idx = this->hash_heads[this->HashStr(s)]; - - while (--idx >= 0) { - LangString *ls = this->strings[idx]; - - if (strcmp(ls->name, s) == 0) return ls; - idx = ls->hash_next; - } - return NULL; - } + return hash; +} - /** - * Create a compound hash. - * @param hash The hash to add the string hash to. - * @param s The string hash. - * @return The new hash. - */ - uint VersionHashStr(uint hash, const char *s) const - { - for (; *s != '\0'; s++) { - hash = ROL(hash, 3) ^ *s; - hash = (hash & 1 ? hash >> 1 ^ 0xDEADBEEF : hash >> 1); - } - return hash; - } - - /** - * Make a hash of the file to get a unique "version number" - * @return The version number. - */ - uint Version() const - { - uint hash = 0; - - for (size_t i = 0; i < this->max_strings; i++) { - const LangString *ls = this->strings[i]; - - if (ls != NULL) { - const CmdStruct *cs; - const char *s; - char buf[MAX_COMMAND_PARAM_SIZE]; - int argno; - int casei; - - s = ls->name; - hash ^= i * 0x717239; - hash = (hash & 1 ? hash >> 1 ^ 0xDEADBEEF : hash >> 1); - hash = this->VersionHashStr(hash, s + 1); - - s = ls->english; - while ((cs = ParseCommandString(&s, buf, &argno, &casei)) != NULL) { - if (cs->flags & C_DONTCOUNT) continue; - - hash ^= (cs - _cmd_structs) * 0x1234567; - hash = (hash & 1 ? hash >> 1 ^ 0xF00BAA4 : hash >> 1); - } - } - } - - return hash; - } - - /** - * Count the number of tab elements that are in use. - * @param tab The tab to count the elements of. - */ - uint CountInUse(uint tab) const - { - int i; - for (i = STRINGS_IN_TAB; --i >= 0;) if (this->strings[(tab * STRINGS_IN_TAB) + i] != NULL) break; - return i + 1; - } -}; +/** + * Count the number of tab elements that are in use. + * @param tab The tab to count the elements of. + */ +uint StringData::CountInUse(uint tab) const +{ + int i; + for (i = STRINGS_IN_TAB; --i >= 0;) if (this->strings[(tab * STRINGS_IN_TAB) + i] != NULL) break; + return i + 1; +} static LanguagePackHeader _lang; ///< Header information about a language. @@ -661,56 +634,23 @@ return cmd; } -/** Helper for reading strings. */ -struct StringReader { - StringData &data; ///< The data to fill during reading. - const char *file; ///< The file we are reading. - bool master; ///< Are we reading the master file? - bool translation; ///< Are we reading a translation, implies !master. However, the base translation will have this false. - - /** - * Prepare reading. - * @param data The data to fill during reading. - * @param file The file we are reading. - * @param master Are we reading the master file? - * @param translation Are we reading a translation? - */ - StringReader(StringData &data, const char *file, bool master, bool translation) : - data(data), file(strdup(file)), master(master), translation(translation) - { - } - - /** Make sure the right reader gets freed. */ - virtual ~StringReader() - { - free(file); - } +/** + * Prepare reading. + * @param data The data to fill during reading. + * @param file The file we are reading. + * @param master Are we reading the master file? + * @param translation Are we reading a translation? + */ +StringReader::StringReader(StringData &data, const char *file, bool master, bool translation) : + data(data), file(strdup(file)), master(master), translation(translation) +{ +} - /** - * Read a single line from the source of strings. - * @param buffer The buffer to read the data in to. - * @param size The size of the buffer. - * @return The buffer, or NULL if at the end of the file. - */ - virtual char *ReadLine(char *buffer, size_t size) = 0; - - /** - * Handle the pragma of the file. - * @param str The pragma string to parse. - */ - virtual void HandlePragma(char *str) = 0; - - /** - * Handle reading a single string. - * @param str The string to handle. - */ - void HandleString(char *str); - - /** - * Start parsing the file. - */ - virtual void ParseFile(); -}; +/** Make sure the right reader gets freed. */ +StringReader::~StringReader() +{ + free(file); +} /** A reader that simply reads using fopen. */ struct FileStringReader : StringReader { @@ -1120,41 +1060,22 @@ } }; -/** Base class for writing the header. */ -struct HeaderWriter { - /** - * Write the string ID. - * @param name The name of the string. - * @param stringid The ID of the string. - */ - virtual void WriteStringID(const char *name, int stringid) = 0; - - /** - * Finalise writing the file. - * @param data The data about the string. - */ - virtual void Finalise(const StringData &data) = 0; - - /** Especially destroy the subclasses. */ - virtual ~HeaderWriter() {}; +/** + * Write the header information. + * @param data The data about the string. + */ +void HeaderWriter::WriteHeader(const StringData &data) +{ + int last = 0; + for (size_t i = 0; i < data.max_strings; i++) { + if (data.strings[i] != NULL) { + this->WriteStringID(data.strings[i]->name, i); + last = i; + } + } - /** - * Write the header information. - * @param data The data about the string. - */ - void WriteHeader(const StringData &data) - { - int last = 0; - for (size_t i = 0; i < data.max_strings; i++) { - if (data.strings[i] != NULL) { - this->WriteStringID(data.strings[i]->name, i); - last = i; - } - } - - this->WriteStringID("STR_LAST_STRINGID", last); - } -}; + this->WriteStringID("STR_LAST_STRINGID", last); +} struct HeaderFileWriter : HeaderWriter, FileWriter { /** The real file name we eventually want to write to. */ @@ -1287,154 +1208,129 @@ } } -/** Base class for all language writers. */ -struct LanguageWriter { - /** - * Write the header metadata. The multi-byte integers are already converted to - * the little endian format. - * @param header The header to write. - */ - virtual void WriteHeader(const LanguagePackHeader *header) = 0; - - /** - * Write a number of bytes. - * @param buffer The buffer to write. - * @param length The amount of byte to write. - */ - virtual void Write(const byte *buffer, size_t length) = 0; - - /** - * Finalise writing the file. - */ - virtual void Finalise() = 0; +/** + * Write the length as a simple gamma. + * @param length The number to write. + */ +void LanguageWriter::WriteLength(uint length) +{ + char buffer[2]; + int offs = 0; + if (length >= 0x4000) { + error("string too long"); + } - /** Especially destroy the subclasses. */ - virtual ~LanguageWriter() {} + if (length >= 0xC0) { + buffer[offs++] = (length >> 8) | 0xC0; + } + buffer[offs++] = length & 0xFF; + this->Write((byte*)buffer, offs); +} - /** - * Write the length as a simple gamma. - * @param length The number to write. - */ - void WriteLength(uint length) - { - char buffer[2]; - int offs = 0; - if (length >= 0x4000) { - error("string too long"); +/** + * Actually write the language. + * @param data The data about the string. + */ +void LanguageWriter::WriteLang(const StringData &data) +{ + uint *in_use = AllocaM(uint, data.tabs); + for (size_t tab = 0; tab < data.tabs; tab++) { + uint n = data.CountInUse(tab); + + in_use[tab] = n; + _lang.offsets[tab] = TO_LE16(n); + + for (uint j = 0; j != in_use[tab]; j++) { + const LangString *ls = data.strings[(tab * StringData::STRINGS_IN_TAB) + j]; + if (ls != NULL && ls->translated == NULL) _lang.missing++; } - - if (length >= 0xC0) { - buffer[offs++] = (length >> 8) | 0xC0; - } - buffer[offs++] = length & 0xFF; - this->Write((byte*)buffer, offs); } - /** - * Actually write the language. - * @param data The data about the string. - */ - void WriteLang(const StringData &data) - { - uint *in_use = AllocaM(uint, data.tabs); - for (size_t tab = 0; tab < data.tabs; tab++) { - uint n = data.CountInUse(tab); + _lang.ident = TO_LE32(LanguagePackHeader::IDENT); + _lang.version = TO_LE32(data.Version()); + _lang.missing = TO_LE16(_lang.missing); + _lang.winlangid = TO_LE16(_lang.winlangid); - in_use[tab] = n; - _lang.offsets[tab] = TO_LE16(n); + this->WriteHeader(&_lang); + Buffer buffer; - for (uint j = 0; j != in_use[tab]; j++) { - const LangString *ls = data.strings[(tab * StringData::STRINGS_IN_TAB) + j]; - if (ls != NULL && ls->translated == NULL) _lang.missing++; - } - } + for (size_t tab = 0; tab < data.tabs; tab++) { + for (uint j = 0; j != in_use[tab]; j++) { + const LangString *ls = data.strings[(tab * StringData::STRINGS_IN_TAB) + j]; + const Case *casep; + const char *cmdp; - _lang.ident = TO_LE32(LanguagePackHeader::IDENT); - _lang.version = TO_LE32(data.Version()); - _lang.missing = TO_LE16(_lang.missing); - _lang.winlangid = TO_LE16(_lang.winlangid); + /* For undefined strings, just set that it's an empty string */ + if (ls == NULL) { + this->WriteLength(0); + continue; + } - this->WriteHeader(&_lang); - Buffer buffer; + _cur_ident = ls->name; + _cur_line = ls->line; - for (size_t tab = 0; tab < data.tabs; tab++) { - for (uint j = 0; j != in_use[tab]; j++) { - const LangString *ls = data.strings[(tab * StringData::STRINGS_IN_TAB) + j]; - const Case *casep; - const char *cmdp; + /* Produce a message if a string doesn't have a translation. */ + if (_show_todo > 0 && ls->translated == NULL) { + if ((_show_todo & 2) != 0) { + strgen_warning("'%s' is untranslated", ls->name); + } + if ((_show_todo & 1) != 0) { + const char *s = "<TODO> "; + while (*s != '\0') buffer.AppendByte(*s++); + } + } - /* For undefined strings, just set that it's an empty string */ - if (ls == NULL) { - this->WriteLength(0); - continue; - } - - _cur_ident = ls->name; - _cur_line = ls->line; + /* Extract the strings and stuff from the english command string */ + ExtractCommandString(&_cur_pcs, ls->english, false); - /* Produce a message if a string doesn't have a translation. */ - if (_show_todo > 0 && ls->translated == NULL) { - if ((_show_todo & 2) != 0) { - strgen_warning("'%s' is untranslated", ls->name); - } - if ((_show_todo & 1) != 0) { - const char *s = "<TODO> "; - while (*s != '\0') buffer.AppendByte(*s++); - } - } + if (ls->translated_case != NULL || ls->translated != NULL) { + casep = ls->translated_case; + cmdp = ls->translated; + } else { + casep = NULL; + cmdp = ls->english; + } - /* Extract the strings and stuff from the english command string */ - ExtractCommandString(&_cur_pcs, ls->english, false); + _translated = cmdp != ls->english; - if (ls->translated_case != NULL || ls->translated != NULL) { - casep = ls->translated_case; - cmdp = ls->translated; - } else { - casep = NULL; - cmdp = ls->english; - } + if (casep != NULL) { + const Case *c; + uint num; - _translated = cmdp != ls->english; - - if (casep != NULL) { - const Case *c; - uint num; + /* Need to output a case-switch. + * It has this format + * <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT> + * Each LEN is printed using 2 bytes in big endian order. */ + buffer.AppendUtf8(SCC_SWITCH_CASE); + /* Count the number of cases */ + for (num = 0, c = casep; c; c = c->next) num++; + buffer.AppendByte(num); - /* Need to output a case-switch. - * It has this format - * <0x9E> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT> - * Each LEN is printed using 2 bytes in big endian order. */ - buffer.AppendUtf8(SCC_SWITCH_CASE); - /* Count the number of cases */ - for (num = 0, c = casep; c; c = c->next) num++; - buffer.AppendByte(num); + /* Write each case */ + for (c = casep; c != NULL; c = c->next) { + buffer.AppendByte(c->caseidx); + /* Make some space for the 16-bit length */ + size_t pos = buffer.Length(); + buffer.AppendByte(0); + buffer.AppendByte(0); + /* Write string */ + PutCommandString(&buffer, c->string); + buffer.AppendByte(0); // terminate with a zero + /* Fill in the length */ + size_t size = buffer.Length() - (pos + 2); + buffer[pos + 0] = GB(size, 8, 8); + buffer[pos + 1] = GB(size, 0, 8); + } + } - /* Write each case */ - for (c = casep; c != NULL; c = c->next) { - buffer.AppendByte(c->caseidx); - /* Make some space for the 16-bit length */ - size_t pos = buffer.Length(); - buffer.AppendByte(0); - buffer.AppendByte(0); - /* Write string */ - PutCommandString(&buffer, c->string); - buffer.AppendByte(0); // terminate with a zero - /* Fill in the length */ - size_t size = buffer.Length() - (pos + 2); - buffer[pos + 0] = GB(size, 8, 8); - buffer[pos + 1] = GB(size, 0, 8); - } - } + if (cmdp != NULL) PutCommandString(&buffer, cmdp); - if (cmdp != NULL) PutCommandString(&buffer, cmdp); - - this->WriteLength(buffer.Length()); - this->Write(buffer.Begin(), buffer.Length()); - buffer.Clear(); - } + this->WriteLength(buffer.Length()); + this->Write(buffer.Begin(), buffer.Length()); + buffer.Clear(); } } -}; +} /** Class for writing a language to disk. */ struct LanguageFileWriter : LanguageWriter, FileWriter {
new file mode 100644 --- /dev/null +++ b/src/strgen/strgen.h @@ -0,0 +1,141 @@ +/* $Id$ */ + +/* + * This file is part of OpenTTD. + * OpenTTD 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, version 2. + * OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>. + */ + +/** @file strgen.h Structures related to strgen. */ + +#ifndef STRGEN_H +#define STRGEN_H + +/** Container for the different cases of a string. */ +struct Case { + int caseidx; ///< The index of the case. + char *string; ///< The translation of the case. + Case *next; ///< The next, chained, case. + + Case(int caseidx, const char *string, Case *next); + ~Case(); +}; + +/** Information about a single string. */ +struct LangString { + char *name; ///< Name of the string. + char *english; ///< English text. + char *translated; ///< Translated text. + uint16 hash_next; ///< Next hash entry. + uint16 index; ///< The index in the language file. + int line; ///< Line of string in source-file. + Case *translated_case; ///< Cases of the translation. + + LangString(const char *name, const char *english, int index, int line); + ~LangString(); + void FreeTranslation(); +}; + +/** Information about the currently known strings. */ +struct StringData { + static const uint STRINGS_IN_TAB = 2048; + + LangString **strings; ///< Array of all known strings. + uint16 *hash_heads; ///< Hash table for the strings. + size_t tabs; ///< The number of 'tabs' of strings. + size_t max_strings; ///< The maxmimum number of strings. + int next_string_id; ///< The next string ID to allocate. + + StringData(size_t tabs = 32); + ~StringData(); + void FreeTranslation(); + uint HashStr(const char *s) const; + void Add(const char *s, LangString *ls); + LangString *Find(const char *s); + uint VersionHashStr(uint hash, const char *s) const; + uint Version() const; + uint CountInUse(uint tab) const; +}; + +/** Helper for reading strings. */ +struct StringReader { + StringData &data; ///< The data to fill during reading. + const char *file; ///< The file we are reading. + bool master; ///< Are we reading the master file? + bool translation; ///< Are we reading a translation, implies !master. However, the base translation will have this false. + + StringReader(StringData &data, const char *file, bool master, bool translation); + virtual ~StringReader(); + void HandleString(char *str); + + /** + * Read a single line from the source of strings. + * @param buffer The buffer to read the data in to. + * @param size The size of the buffer. + * @return The buffer, or NULL if at the end of the file. + */ + virtual char *ReadLine(char *buffer, size_t size) = 0; + + /** + * Handle the pragma of the file. + * @param str The pragma string to parse. + */ + virtual void HandlePragma(char *str) = 0; + + /** + * Start parsing the file. + */ + virtual void ParseFile(); +}; + +/** Base class for writing the header, i.e. the STR_XXX to numeric value. */ +struct HeaderWriter { + /** + * Write the string ID. + * @param name The name of the string. + * @param stringid The ID of the string. + */ + virtual void WriteStringID(const char *name, int stringid) = 0; + + /** + * Finalise writing the file. + * @param data The data about the string. + */ + virtual void Finalise(const StringData &data) = 0; + + /** Especially destroy the subclasses. */ + virtual ~HeaderWriter() {}; + + void WriteHeader(const StringData &data); +}; + +/** Base class for all language writers. */ +struct LanguageWriter { + /** + * Write the header metadata. The multi-byte integers are already converted to + * the little endian format. + * @param header The header to write. + */ + virtual void WriteHeader(const LanguagePackHeader *header) = 0; + + /** + * Write a number of bytes. + * @param buffer The buffer to write. + * @param length The amount of byte to write. + */ + virtual void Write(const byte *buffer, size_t length) = 0; + + /** + * Finalise writing the file. + */ + virtual void Finalise() = 0; + + /** Especially destroy the subclasses. */ + virtual ~LanguageWriter() {} + + void WriteLength(uint length); + void WriteLang(const StringData &data); +}; + +#endif /* STRGEN_H */