Changeset - r16477:0f1fcd3c927b
[Not reviewed]
master
0 2 0
rubidium - 14 years ago 2010-11-16 16:58:19
rubidium@openttd.org
(svn r21211) -Feature: [NewGRF] Allow specifying a "choice list" for cases and genders
2 files changed with 199 insertions and 0 deletions:
0 comments (0 inline, 0 general)
src/newgrf_text.cpp
Show inline comments
 
@@ -25,6 +25,8 @@
 
#include "string_func.h"
 
#include "date_type.h"
 
#include "debug.h"
 
#include "core/alloc_type.hpp"
 
#include "core/smallmap_type.hpp"
 
#include "language.h"
 

	
 
#include "table/strings.h"
 
@@ -233,6 +235,150 @@ int LanguageMap::GetMapping(int newgrf_i
 
}
 

	
 
/**
 
 * Get the mapping from OpenTTD's internal ID to the NewGRF supplied ID.
 
 * @param openttd_id The OpenTTD ID to map.
 
 * @param gender     Whether to map genders or cases.
 
 * @return The, to the NewGRF supplied ID, mapped index, or -1 if there is no mapping.
 
 */
 
int LanguageMap::GetReverseMapping(int openttd_id, bool gender) const
 
{
 
	const SmallVector<Mapping, 1> &map = gender ? this->gender_map : this->case_map;
 
	for (const Mapping *m = map.Begin(); m != map.End(); m++) {
 
		if (m->openttd_id == openttd_id) return m->newgrf_id;
 
	}
 
	return -1;
 
}
 

	
 
/** Helper structure for mapping choice lists. */
 
struct UnmappedChoiceList : ZeroedMemoryAllocator {
 
	/** Clean everything up. */
 
	~UnmappedChoiceList()
 
	{
 
		for (SmallPair<byte, char *> *p = this->strings.Begin(); p < this->strings.End(); p++) {
 
			free(p->second);
 
		}
 
	}
 

	
 
	/**
 
	 * Initialise the mapping.
 
	 * @param type   The type of mapping.
 
	 * @param old_d  The old begin of the string, i.e. from where to start writing again.
 
	 * @param offset The offset to get the gender from.
 
	 */
 
	UnmappedChoiceList(StringControlCode type, char *old_d, int offset) :
 
		type(type), old_d(old_d), offset(offset)
 
	{
 
	}
 

	
 
	StringControlCode type; ///< The type of choice list.
 
	char *old_d;            ///< The old/original location of the "d" local variable.
 
	int offset;             ///< The offset for the gender form.
 

	
 
	/** Mapping of NewGRF supplied ID to the different strings in the choice list. */
 
	SmallMap<byte, char *> strings;
 

	
 
	/**
 
	 * Flush this choice list into the old d variable.
 
	 * @param lm  The current language mapping.
 
	 * @return The new location of the output string.
 
	 */
 
	char *Flush(const LanguageMap *lm)
 
	{
 
		if (!this->strings.Contains(0)) {
 
			/* In case of a (broken) NewGRF without a default,
 
			 * assume an empty string. */
 
			grfmsg(1, "choice list misses default value");
 
			this->strings[0] = strdup("");
 
		}
 

	
 
		char *d = old_d;
 
		if (lm == NULL) {
 
			NOT_REACHED();
 
			/* In case there is no mapping, just ignore everything but the default. */
 
			int len = strlen(this->strings[0]);
 
			memcpy(d, this->strings[0], len);
 
			return d + len;
 
		}
 

	
 
		d += Utf8Encode(d, this->type);
 

	
 
		if (this->type == SCC_SWITCH_CASE) {
 
			/*
 
			 * Format for case switch:
 
			 * <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
 
			 * Each LEN is printed using 2 bytes in big endian order.
 
			 */
 

	
 
			/* "<NUM CASES>" */
 
			int count = 0;
 
			for (uint8 i = 0; i < _current_language->num_cases; i++) {
 
				/* Count the ones we have a mapped string for. */
 
				if (this->strings.Contains(lm->GetReverseMapping(i, false))) count++;
 
			}
 
			*d++ = count;
 

	
 
			for (uint8 i = 0; i < _current_language->num_cases; i++) {
 
				/* Resolve the string we're looking for. */
 
				int idx = lm->GetReverseMapping(i, false);
 
				if (!this->strings.Contains(idx)) continue;
 
				char *str = this->strings[idx];
 

	
 
				/* "<CASEn>" */
 
				*d++ = i;
 

	
 
				/* "<LENn>" */
 
				int len = strlen(str);
 
				*d++ = GB(len, 8, 8);
 
				*d++ = GB(len, 0, 8);
 

	
 
				/* "<STRINGn>" */
 
				memcpy(d, str, len);
 
				d += len;
 
				*d++ = '\0';
 
			}
 

	
 
			/* "<STRINGDEFAULT>" */
 
			int len = strlen(this->strings[0]);
 
			memcpy(d, this->strings[0], len);
 
			d += len;
 
			*d++ = '\0';
 
		} else {
 
			/*
 
			 * Format for choice list:
 
			 * <OFFSET> <NUM CHOICES> <LENs> <STRINGs>
 
			 */
 

	
 
			/* "<OFFSET>" */
 
			*d++ = this->offset - 0x80;
 

	
 
			/* "<NUM CHOICES>" */
 
			int count = _current_language->num_genders;
 
			*d++ = count;
 

	
 
			/* "<LENs>" */
 
			for (int i = 0; i < count; i++) {
 
				int idx = lm->GetReverseMapping(i, true);
 
				const char *str = this->strings[this->strings.Contains(idx) ? idx : 0];
 
				int len = strlen(str) + 1;
 
				if (len > 0xFF) grfmsg(1, "choice list string is too long");
 
				*d++ = len;
 
			}
 

	
 
			/* "<STRINGs>" */
 
			for (int i = 0; i < count; i++) {
 
				int idx = lm->GetReverseMapping(i, true);
 
				const char *str = this->strings[this->strings.Contains(idx) ? idx : 0];
 
				int len = strlen(str);
 
				memcpy(d, str, len);
 
				d += len;
 
				*d++ = '\0';
 
			}
 
		}
 
		return d;
 
	}
 
};
 

	
 
/**
 
 * Translate TTDPatch string codes into something OpenTTD can handle (better).
 
 * @param grfid       The (NewGRF) ID associated with this string
 
 * @param language_id The (NewGRF) language ID associated with this string.
 
@@ -248,6 +394,9 @@ char *TranslateTTDPatchCodes(uint32 grfi
 
	WChar c;
 
	size_t len = Utf8Decode(&c, str);
 

	
 
	/* Helper variable for a possible (string) mapping. */
 
	UnmappedChoiceList *mapping = NULL;
 

	
 
	if (c == NFO_UTF8_IDENTIFIER) {
 
		unicode = true;
 
		str += len;
 
@@ -362,6 +511,50 @@ char *TranslateTTDPatchCodes(uint32 grfi
 
						break;
 
					}
 

	
 
					case 0x10:
 
					case 0x11:
 
						if (mapping == NULL) {
 
							if (code == 0x10) str++; // Skip the index
 
							grfmsg(1, "choice list %s marker found when not expected", code == 0x10 ? "next" : "default");
 
							break;
 
						} else {
 
							/* Terminate the previous string. */
 
							*d = '\0';
 
							int index = (code == 0x10 ? *str++ : 0);
 
							if (mapping->strings.Contains(index)) {
 
								grfmsg(1, "duplicate choice list string, ignoring");
 
								d++;
 
							} else {
 
								d = mapping->strings[index] = MallocT<char>(strlen(str) * 10 + 1);
 
							}
 
						}
 
						break;
 

	
 
					case 0x12:
 
						if (mapping == NULL) {
 
							grfmsg(1, "choice list end marker found when not expected");
 
						} else {
 
							/* Terminate the previous string. */
 
							*d = '\0';
 

	
 
							/* Now we can start flushing everything and clean everything up. */
 
							d = mapping->Flush(LanguageMap::GetLanguageMap(grfid, language_id));
 
							delete mapping;
 
							mapping = NULL;
 
						}
 
						break;
 

	
 
					case 0x13:
 
					case 0x14:
 
						if (mapping != NULL) {
 
							grfmsg(1, "choice lists can't be stacked, it's going to get messy now...");
 
							if (code != 0x14) str++;
 
						} else {
 
							static const StringControlCode mp[] = { SCC_GENDER_LIST, SCC_SWITCH_CASE };
 
							mapping = new UnmappedChoiceList(mp[code - 0x13], d, code == 0x14 ? 0 : *str++);
 
						}
 
						break;
 

	
 
					default:
 
						grfmsg(1, "missing handler for extended format code");
 
						break;
 
@@ -392,6 +585,11 @@ char *TranslateTTDPatchCodes(uint32 grfi
 
		}
 
	}
 

	
 
	if (mapping != NULL) {
 
		grfmsg(1, "choice list was incomplete, the whole list is ignored");
 
		delete mapping;
 
	}
 

	
 
	*d = '\0';
 
	if (olen != NULL) *olen = d - tmp + 1;
 
	tmp = ReallocT(tmp, d - tmp + 1);
src/newgrf_text.h
Show inline comments
 
@@ -61,6 +61,7 @@ struct LanguageMap {
 
	SmallVector<Mapping, 1> case_map;   ///< Mapping of NewGRF and OpenTTD IDs for cases.
 

	
 
	int GetMapping(int newgrf_id, bool gender) const;
 
	int GetReverseMapping(int openttd_id, bool gender) const;
 
	static const LanguageMap *GetLanguageMap(uint32 grfid, uint8 language_id);
 
};
 

	
0 comments (0 inline, 0 general)