(svn r10211) -Feature: [NewGRF] Add support for action 0F
authorglx
Mon, 18 Jun 2007 23:00:55 +0000
changeset 6956 3579bfc5157b
parent 6955 da3431cb25a9
child 6957 a9af2426bf77
(svn r10211) -Feature: [NewGRF] Add support for action 0F
projects/openttd.vcproj
projects/openttd_vs80.vcproj
source.list
src/network/network_gui.cpp
src/newgrf.cpp
src/newgrf_config.cpp
src/newgrf_config.h
src/newgrf_gui.cpp
src/newgrf_townname.cpp
src/newgrf_townname.h
src/saveload.cpp
src/settings.cpp
src/settings_gui.cpp
src/strings.cpp
src/town.h
src/town_cmd.cpp
--- a/projects/openttd.vcproj	Mon Jun 18 22:49:55 2007 +0000
+++ b/projects/openttd.vcproj	Mon Jun 18 23:00:55 2007 +0000
@@ -564,6 +564,9 @@
 				RelativePath=".\..\src\newgrf_town.h">
 			</File>
 			<File
+				RelativePath=".\..\src\newgrf_townname.h">
+			</File>
+			<File
 				RelativePath=".\..\src\news.h">
 			</File>
 			<File
@@ -1080,6 +1083,9 @@
 			<File
 				RelativePath=".\..\src\newgrf_town.cpp">
 			</File>
+			<File
+				RelativePath=".\..\src\newgrf_townname.cpp">
+			</File>
 		</Filter>
 		<Filter
 			Name="Map Accessors"
--- a/projects/openttd_vs80.vcproj	Mon Jun 18 22:49:55 2007 +0000
+++ b/projects/openttd_vs80.vcproj	Mon Jun 18 23:00:55 2007 +0000
@@ -980,6 +980,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\newgrf_townname.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\news.h"
 				>
 			</File>
@@ -1659,6 +1663,10 @@
 				RelativePath=".\..\src\newgrf_town.cpp"
 				>
 			</File>
+			<File
+				RelativePath=".\..\src\newgrf_townname.cpp"
+				>
+			</File>
 		</Filter>
 		<Filter
 			Name="Map Accessors"
--- a/source.list	Mon Jun 18 22:49:55 2007 +0000
+++ b/source.list	Mon Jun 18 23:00:55 2007 +0000
@@ -155,6 +155,7 @@
 newgrf_station.h
 newgrf_text.h
 newgrf_town.h
+newgrf_townname.h
 news.h
 npf.h
 music/null_m.h
@@ -334,6 +335,7 @@
 newgrf_station.cpp
 newgrf_text.cpp
 newgrf_town.cpp
+newgrf_townname.cpp
 
 # Map Accessors
 bridge_map.cpp
--- a/src/network/network_gui.cpp	Mon Jun 18 22:49:55 2007 +0000
+++ b/src/network/network_gui.cpp	Mon Jun 18 23:00:55 2007 +0000
@@ -1519,8 +1519,8 @@
 
 		FOR_ALL_TOWNS_FROM(t, *item - MAX_CLIENT_INFO) {
 			/* Get the town-name via the string-system */
-			SetDParam(0, t->townnameparts);
-			GetString(chat_tab_temp_buffer, t->townnametype, lastof(chat_tab_temp_buffer));
+			SetDParam(0, t->index);
+			GetString(chat_tab_temp_buffer, STR_TOWN, lastof(chat_tab_temp_buffer));
 			return &chat_tab_temp_buffer[0];
 		}
 	}
--- a/src/newgrf.cpp	Mon Jun 18 22:49:55 2007 +0000
+++ b/src/newgrf.cpp	Mon Jun 18 23:00:55 2007 +0000
@@ -42,6 +42,7 @@
 #include "industry.h"
 #include "newgrf_canal.h"
 #include "newgrf_commons.h"
+#include "newgrf_townname.h"
 
 /* TTDPatch extended GRF format codec
  * (c) Petr Baudis 2004 (GPL'd)
@@ -3898,6 +3899,106 @@
 	}
 }
 
+/* Action 0x0F */
+static void FeatureTownName(byte *buf, int len)
+{
+	/* <0F> <id> <style-name> <num-parts> <parts>
+	 *
+	 * B id          ID of this definition in bottom 7 bits (final definition if bit 7 set)
+	 * V style-name  Name of the style (only for final definition)
+	 * B num-parts   Number of parts in this definition
+	 * V parts       The parts */
+
+	if (!check_length(len, 1, "FeatureTownName: definition ID")) return;
+	buf++; len--;
+
+	uint32 grfid = _cur_grffile->grfid;
+
+	GRFTownName *townname = AddGRFTownName(grfid);
+
+	byte id = grf_load_byte(&buf);
+	len--;
+	grfmsg(6, "FeatureTownName: definition 0x%02X", id & 0x7F);
+
+	if (HASBIT(id, 7)) {
+		/* Final definition */
+		CLRBIT(id, 7);
+		bool new_scheme = _cur_grffile->grf_version >= 7;
+
+		if (!check_length(len, 1, "FeatureTownName: lang_id")) return;
+		byte lang = grf_load_byte(&buf);
+		len--;
+
+		byte nb_gen = townname->nb_gen;
+		do {
+			CLRBIT(lang, 7);
+
+			if (!check_length(len, 1, "FeatureTownName: style name")) return;
+			const char *name = grf_load_string(&buf, len);
+			len -= strlen(name) + 1;
+			grfmsg(6, "FeatureTownName: lang 0x%X -> '%s'", lang, TranslateTTDPatchCodes(name));
+
+			townname->name[nb_gen] = AddGRFString(grfid, id, lang, new_scheme, name, STR_UNDEFINED);
+
+			if (!check_length(len, 1, "FeatureTownName: lang_id")) return;
+			lang = grf_load_byte(&buf);
+			len--;
+		} while (lang != 0);
+		townname->id[nb_gen] = id;
+		townname->nb_gen++;
+	}
+
+	if (!check_length(len, 1, "FeatureTownName: number of parts")) return;
+	byte nb = grf_load_byte(&buf);
+	len--;
+	grfmsg(6, "FeatureTownName: %d parts", nb, nb);
+
+	townname->nbparts[id] = nb;
+	townname->partlist[id] = CallocT<NamePartList>(nb);
+
+	for (int i = 0; i < nb; i++) {
+		if (!check_length(len, 3, "FeatureTownName: parts header")) return;
+		byte nbtext =  grf_load_byte(&buf);
+		townname->partlist[id][i].bitstart  = grf_load_byte(&buf);
+		townname->partlist[id][i].bitcount  = grf_load_byte(&buf);
+		townname->partlist[id][i].maxprob   = 0;
+		townname->partlist[id][i].partcount = nbtext;
+		townname->partlist[id][i].parts     = CallocT<NamePart>(nbtext);
+		len -= 3;
+		grfmsg(6, "FeatureTownName: part %d contains %d texts and will use GB(seed, %d, %d)", i, nbtext, townname->partlist[id][i].bitstart, townname->partlist[id][i].bitcount);
+
+		for (int j = 0; j < nbtext; j++) {
+			if (!check_length(len, 2, "FeatureTownName: part")) return;
+			byte prob = grf_load_byte(&buf);
+			len--;
+
+			if (HASBIT(prob, 7)) {
+				byte ref_id = grf_load_byte(&buf);
+				len--;
+
+				if (townname->nbparts[ref_id] == 0) {
+					grfmsg(0, "FeatureTownName: definition 0x%02X doesn't exist, deactivating", ref_id);
+					DelGRFTownName(grfid);
+					_cur_grfconfig->status = GCS_DISABLED;
+					_skip_sprites = -1;
+					return;
+				}
+
+				grfmsg(6, "FeatureTownName: part %d, text %d, uses intermediate definition 0x%02X (with probability %d)", i, j, ref_id, prob & 0x7F);
+				townname->partlist[id][i].parts[j].data.id = ref_id;
+			} else {
+				const char *text = grf_load_string(&buf, len);
+				len -= strlen(text) + 1;
+				townname->partlist[id][i].parts[j].data.text = TranslateTTDPatchCodes(text);
+				grfmsg(6, "FeatureTownName: part %d, text %d, '%s' (with probability %d)", i, j, townname->partlist[id][i].parts[j].data.text, prob);
+			}
+			townname->partlist[id][i].parts[j].prob = prob;
+			townname->partlist[id][i].maxprob += GB(prob, 0, 7);
+		}
+		grfmsg(6, "FeatureTownName: part %d, total probability %d", i, townname->partlist[id][i].maxprob);
+	}
+}
+
 /* Action 0x10 */
 static void DefineGotoLabel(byte *buf, int len)
 {
@@ -4241,7 +4342,7 @@
 	                   |    ((_patches.freight_trains > 1 ? 1 : 0) << 0x18)  // freighttrains
 	                   |                                        (1 << 0x19)  // newhouses
 	                   |                                        (1 << 0x1A)  // newbridges
-	                   |                                        (0 << 0x1B)  // newtownnames
+	                   |                                        (1 << 0x1B)  // newtownnames
 	                   |                                        (0 << 0x1C)  // moreanimations
 	                   |    ((_patches.wagon_speed_limits ? 1 : 0) << 0x1D)  // wagonspeedlimits
 	                   |                                        (1 << 0x1E)  // newshistory
@@ -4418,6 +4519,7 @@
 static void ResetNewGRFData()
 {
 	CleanUpStrings();
+	CleanUpGRFTownNames();
 
 	/* Copy/reset original engine info data */
 	memcpy(&_engine_info, &orig_engine_info, sizeof(orig_engine_info));
@@ -4787,7 +4889,7 @@
 		/* 0x0C */ { NULL,     NULL,      NULL,            GRFComment,     NULL,              GRFComment, },
 		/* 0x0D */ { NULL,     SafeParamSet, NULL,         ParamSet,       ParamSet,          ParamSet, },
 		/* 0x0E */ { NULL,     SafeGRFInhibit, NULL,       GRFInhibit,     GRFInhibit,        GRFInhibit, },
-		/* 0x0F */ { NULL,     NULL,      NULL,            NULL,           NULL,              NULL, },
+		/* 0x0F */ { NULL,     GRFUnsafe, NULL,            FeatureTownName, NULL,             NULL, },
 		/* 0x10 */ { NULL,     NULL,      DefineGotoLabel, NULL,           NULL,              NULL, },
 		/* 0x11 */ { NULL,     GRFUnsafe, NULL,            NULL,           NULL,              GRFSound, },
 		/* 0x12 */ { NULL,     NULL,      NULL,            NULL,           NULL,              LoadFontGlyph, },
@@ -4917,6 +5019,8 @@
 
 void InitDepotWindowBlockSizes();
 
+extern void SortTownGeneratorNames();
+
 static void AfterLoadGRFs()
 {
 	/* Update the bitmasks for the vehicle lists */
@@ -4941,6 +5045,9 @@
 	/* Map cargo strings. This is a separate step because cargos are
 	 * loaded before strings... */
 	MapNewCargoStrings();
+
+	/* Update the townname generators list */
+	SortTownGeneratorNames();
 }
 
 void LoadNewGRF(uint load_index, uint file_index)
@@ -4959,6 +5066,7 @@
 		_cur_spriteid = load_index;
 		for (GRFConfig *c = _grfconfig; c != NULL; c = c->next) {
 			if (c->status == GCS_DISABLED || c->status == GCS_NOT_FOUND) continue;
+			if (stage > GLS_INIT && HASBIT(c->flags, GCF_INIT_ONLY)) continue;
 
 			/* @todo usererror() */
 			if (!FioCheckFileExists(c->full_path)) error("NewGRF file is missing '%s'", c->filename);
--- a/src/newgrf_config.cpp	Mon Jun 18 22:49:55 2007 +0000
+++ b/src/newgrf_config.cpp	Mon Jun 18 23:00:55 2007 +0000
@@ -124,8 +124,9 @@
 /** Copy a GRF Config list
  * @param dst pointer to destination list
  * @param src pointer to source list values
+ * @param init_only the copied GRF will be processed up to GLS_INIT
  * @return pointer to the last value added to the destination list */
-GRFConfig **CopyGRFConfigList(GRFConfig **dst, const GRFConfig *src)
+GRFConfig **CopyGRFConfigList(GRFConfig **dst, const GRFConfig *src, bool init_only)
 {
 	/* Clear destination as it will be overwritten */
 	ClearGRFConfigList(dst);
@@ -143,6 +144,8 @@
 			if (src->error->custom_message != NULL) c->error->custom_message = strdup(src->error->custom_message);
 		}
 
+		if (init_only) SETBIT(c->flags, GCF_INIT_ONLY);
+
 		*dst = c;
 		dst = &c->next;
 	}
@@ -190,7 +193,7 @@
 	GRFConfig **tail = dst;
 	while (*tail != NULL) tail = &(*tail)->next;
 
-	CopyGRFConfigList(tail, _grfconfig_static);
+	CopyGRFConfigList(tail, _grfconfig_static, false);
 	RemoveDuplicatesFromGRFConfigList(*dst);
 }
 
@@ -210,14 +213,7 @@
 /* Reset the current GRF Config to either blank or newgame settings */
 void ResetGRFConfig(bool defaults)
 {
-	GRFConfig **c = &_grfconfig;
-
-	if (defaults) {
-		c = CopyGRFConfigList(c, _grfconfig_newgame);
-	} else {
-		ClearGRFConfigList(c);
-	}
-
+	CopyGRFConfigList(&_grfconfig, _grfconfig_newgame, !defaults);
 	AppendStaticGRFConfigs(&_grfconfig);
 }
 
--- a/src/newgrf_config.h	Mon Jun 18 22:49:55 2007 +0000
+++ b/src/newgrf_config.h	Mon Jun 18 23:00:55 2007 +0000
@@ -14,6 +14,7 @@
 	GCF_STATIC,    ///< GRF file is used statically (can be used in any MP game)
 	GCF_COMPATIBLE,///< GRF file does not exactly match the requested GRF (different MD5SUM), but grfid matches)
 	GCF_COPY,      ///< The data is copied from a grf in _all_grfs
+	GCF_INIT_ONLY, ///< GRF file is processed up to GLS_INIT
 };
 
 enum GRFStatus {
@@ -74,7 +75,7 @@
 void ScanNewGRFFiles();
 const GRFConfig *FindGRFConfig(uint32 grfid, const uint8 *md5sum = NULL);
 GRFConfig *GetGRFConfig(uint32 grfid);
-GRFConfig **CopyGRFConfigList(GRFConfig **dst, const GRFConfig *src);
+GRFConfig **CopyGRFConfigList(GRFConfig **dst, const GRFConfig *src, bool init_only);
 void AppendStaticGRFConfigs(GRFConfig **dst);
 void AppendToGRFConfigList(GRFConfig **dst, GRFConfig *el);
 void ClearGRFConfig(GRFConfig **config);
--- a/src/newgrf_gui.cpp	Mon Jun 18 22:49:55 2007 +0000
+++ b/src/newgrf_gui.cpp	Mon Jun 18 23:00:55 2007 +0000
@@ -307,12 +307,12 @@
 		GRFConfig *c;
 		int i = 0;
 
-		CopyGRFConfigList(nd->orig_list, *nd->list);
+		CopyGRFConfigList(nd->orig_list, *nd->list, false);
 		ReloadNewGRFData();
 
 		/* Show new, updated list */
 		for (c = *nd->list; c != NULL && c != nd->sel; c = c->next, i++);
-		CopyGRFConfigList(nd->list, *nd->orig_list);
+		CopyGRFConfigList(nd->list, *nd->orig_list, false);
 		for (c = *nd->list; c != NULL && i > 0; c = c->next, i--);
 		nd->sel = c;
 
@@ -469,7 +469,9 @@
 							NewGRFConfirmationCallback
 						);
 					} else {
-						CopyGRFConfigList(WP(w, newgrf_d).orig_list, *WP(w, newgrf_d).list);
+						CopyGRFConfigList(WP(w, newgrf_d).orig_list, *WP(w, newgrf_d).list, true);
+						ResetGRFConfig(false);
+						ReloadNewGRFData();
 					}
 					break;
 
@@ -498,7 +500,9 @@
 
 		case WE_DESTROY:
 			if (!WP(w, newgrf_d).execute) {
-				CopyGRFConfigList(WP(w, newgrf_d).orig_list, *WP(w, newgrf_d).list);
+				CopyGRFConfigList(WP(w, newgrf_d).orig_list, *WP(w, newgrf_d).list, true);
+				ResetGRFConfig(false);
+				ReloadNewGRFData();
 			}
 			/* Remove the temporary copy of grf-list used in window */
 			ClearGRFConfigList(WP(w, newgrf_d).list);
@@ -565,7 +569,7 @@
 	if (w == NULL) return;
 
 	w->resize.step_height = 14;
-	CopyGRFConfigList(&local, *config);
+	CopyGRFConfigList(&local, *config, false);
 
 	/* Clear selections */
 	WP(w, newgrf_d).sel         = NULL;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/newgrf_townname.cpp	Mon Jun 18 23:00:55 2007 +0000
@@ -0,0 +1,132 @@
+/* $Id$ */
+
+/** @file newgrf_townname.cpp
+ * Implementation of  Action 0F "universal holder" structure and functions.
+ * This file implements a linked-lists of townname generators,
+ * holding everything that the newgrf action 0F will send over to OpenTTD.
+ */
+
+#include "stdafx.h"
+#include "openttd.h"
+#include "table/strings.h"
+#include "newgrf_townname.h"
+#include "string.h"
+
+static GRFTownName *_grf_townnames = NULL;
+
+GRFTownName *GetGRFTownName(uint32 grfid)
+{
+	GRFTownName *t = _grf_townnames;
+	for (; t != NULL; t = t->next) {
+		if (t->grfid == grfid) return t;
+	}
+	return NULL;
+}
+
+GRFTownName *AddGRFTownName(uint32 grfid)
+{
+	GRFTownName *t = GetGRFTownName(grfid);
+	if (t == NULL) {
+		t = CallocT<GRFTownName>(1);
+		t->grfid = grfid;
+		t->next = _grf_townnames;
+		_grf_townnames = t;
+	}
+	return t;
+}
+
+void DelGRFTownName(uint32 grfid)
+{
+	GRFTownName *t = _grf_townnames;
+	GRFTownName *p = NULL;
+	for (;t != NULL; p = t, t = t->next) if (t->grfid == grfid) break;
+	if (t != NULL) {
+		for (int i = 0; i < 128; i++) {
+			for (int j = 0; j < t->nbparts[i]; j++) {
+				for (int k = 0; k < t->partlist[i][j].partcount; k++) {
+					if (!HASBIT(t->partlist[i][j].parts[k].prob, 7)) free(t->partlist[i][j].parts[k].data.text);
+				}
+				free(t->partlist[i][j].parts);
+			}
+			free(t->partlist[i]);
+		}
+		if (p != NULL) {
+			p->next = t->next;
+		} else {
+			_grf_townnames = t->next;
+		}
+		free(t);
+	}
+}
+
+static char *RandomPart(char *buf, GRFTownName *t, uint32 seed, byte id, const char *last)
+{
+	assert(t != NULL);
+	for (int i = 0; i < t->nbparts[id]; i++) {
+		byte count = t->partlist[id][i].bitcount;
+		uint16 maxprob = t->partlist[id][i].maxprob;
+		uint32 r = (GB(seed, t->partlist[id][i].bitstart, count) * maxprob) >> count;
+		for (int j = 0; j < t->partlist[id][i].partcount; j++) {
+			byte prob = t->partlist[id][i].parts[j].prob;
+			maxprob -= GB(prob, 0, 7);
+			if (maxprob > r) continue;
+			if (HASBIT(prob, 7)) {
+				buf = RandomPart(buf, t, seed, t->partlist[id][i].parts[j].data.id, last);
+			} else {
+				buf = strecat(buf, t->partlist[id][i].parts[j].data.text, last);
+			}
+			break;
+		}
+	}
+	return buf;
+}
+
+char *GRFTownNameGenerate(char *buf, uint32 grfid, uint16 gen, uint32 seed, const char *last)
+{
+	strecpy(buf, "", last);
+	for (GRFTownName *t = _grf_townnames; t != NULL; t = t->next) {
+		if (t->grfid == grfid) {
+			assert(gen < t->nb_gen);
+			buf = RandomPart(buf, t, seed, t->id[gen], last);
+			break;
+		}
+	}
+	return buf;
+}
+
+StringID *GetGRFTownNameList()
+{
+	int nb_names = 0, n = 0;
+	for (GRFTownName *t = _grf_townnames; t != NULL; t = t->next) nb_names += t->nb_gen;
+	StringID *list = MallocT<StringID>(nb_names + 1);
+	for (GRFTownName *t = _grf_townnames; t != NULL; t = t->next) {
+		for (int j = 0; j < t->nb_gen; j++) list[n++] = t->name[j];
+	}
+	list[n] = INVALID_STRING_ID;
+	return list;
+}
+
+void CleanUpGRFTownNames()
+{
+	while (_grf_townnames != NULL) DelGRFTownName(_grf_townnames->grfid);
+}
+
+uint32 GetGRFTownNameId(int gen)
+{
+	for (GRFTownName *t = _grf_townnames; t != NULL; t = t->next) {
+		if (gen < t->nb_gen) return t->grfid;
+		gen -= t->nb_gen;
+	}
+	/* Fallback to no NewGRF */
+	return 0;
+}
+
+uint16 GetGRFTownNameType(int gen)
+{
+	for (GRFTownName *t = _grf_townnames; t != NULL; t = t->next) {
+		if (gen < t->nb_gen) return gen;
+		gen -= t->nb_gen;
+	}
+	/* Fallback to english original */
+	return SPECSTR_TOWNNAME_ENGLISH;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/newgrf_townname.h	Mon Jun 18 23:00:55 2007 +0000
@@ -0,0 +1,44 @@
+/* $Id$ */
+#ifndef NEWGRF_TOWNNAME_H
+#define NEWGRF_TOWNNAME_H
+
+/** @file newgrf_townname.h
+ * Header of Action 0F "universal holder" structure and functions
+ */
+
+struct NamePart {
+	byte prob;     ///< The relative probablity of the following name to appear in the bottom 7 bits.
+	union {
+		char *text;    ///< If probability bit 7 is clear
+		byte id;       ///< If probability bit 7 is set
+	} data;
+};
+
+struct NamePartList {
+	byte partcount;
+	byte bitstart;
+	byte bitcount;
+	uint16 maxprob;
+	NamePart *parts;
+};
+
+struct GRFTownName {
+	uint32 grfid;
+	byte nb_gen;
+	byte id[128];
+	StringID name[128];
+	byte nbparts[128];
+	NamePartList *partlist[128];
+	GRFTownName *next;
+};
+
+GRFTownName *AddGRFTownName(uint32 grfid);
+GRFTownName *GetGRFTownName(uint32 grfid);
+void DelGRFTownName(uint32 grfid);
+void CleanUpGRFTownNames();
+StringID *GetGRFTownNameList();
+char *GRFTownNameGenerate(char *buf, uint32 grfid, uint16 gen, uint32 seed, const char *last);
+uint32 GetGRFTownNameId(int gen);
+uint16 GetGRFTownNameType(int gen);
+
+#endif /* NEWGRF_TOWNNAME_H */
--- a/src/saveload.cpp	Mon Jun 18 22:49:55 2007 +0000
+++ b/src/saveload.cpp	Mon Jun 18 23:00:55 2007 +0000
@@ -29,7 +29,7 @@
 #include <setjmp.h>
 #include <list>
 
-extern const uint16 SAVEGAME_VERSION = 65;
+extern const uint16 SAVEGAME_VERSION = 66;
 uint16 _sl_version;       ///< the major savegame version identifier
 byte   _sl_minor_version; ///< the minor savegame version, DO NOT USE!
 
--- a/src/settings.cpp	Mon Jun 18 22:49:55 2007 +0000
+++ b/src/settings.cpp	Mon Jun 18 23:00:55 2007 +0000
@@ -1306,7 +1306,8 @@
 	    SDT_VAR(GameOptions, diff_level, SLE_UINT8, 0, 0, 0, 0,  3, 0, STR_NULL, NULL),
 	  SDT_OMANY(GameOptions, currency,  SLE_UINT8, N, 0, 0, CUSTOM_CURRENCY_ID, "GBP|USD|EUR|YEN|ATS|BEF|CHF|CZK|DEM|DKK|ESP|FIM|FRF|GRD|HUF|ISK|ITL|NLG|NOK|PLN|ROL|RUR|SIT|SEK|YTL|SKK|BRR|custom", STR_NULL, NULL, NULL),
 	  SDT_OMANY(GameOptions, units,     SLE_UINT8, N, 0, 1,     2, "imperial|metric|si", STR_NULL, NULL, NULL),
-	  SDT_OMANY(GameOptions, town_name, SLE_UINT8, 0, 0, 0,    20, "english|french|german|american|latin|silly|swedish|dutch|finnish|polish|slovakish|norwegian|hungarian|austrian|romanian|czech|swiss|danish|turkish|italian|catalan", STR_NULL, NULL, NULL),
+	/* There are only 21 predefined town_name values (0-20), but you can have more with newgrf action F so allow these bigger values (21-255). Invalid values will fallback to english on use and (undefined string) in GUI. */
+	  SDT_OMANY(GameOptions, town_name, SLE_UINT8, 0, 0, 0,   255, "english|french|german|american|latin|silly|swedish|dutch|finnish|polish|slovakish|norwegian|hungarian|austrian|romanian|czech|swiss|danish|turkish|italian|catalan", STR_NULL, NULL, NULL),
 	  SDT_OMANY(GameOptions, landscape, SLE_UINT8, 0, 0, 0,     3, "temperate|arctic|tropic|toyland", STR_NULL, NULL, ConvertLandscape),
 	    SDT_VAR(GameOptions, snow_line, SLE_UINT8, 0, 0, 7 * TILE_HEIGHT, 2 * TILE_HEIGHT, 13 * TILE_HEIGHT, 0, STR_NULL, NULL),
 	SDT_CONDOMANY(GameOptions,autosave, SLE_UINT8, 0, 22,             N, 0, 0, 0, "", STR_NULL, NULL, NULL),
--- a/src/settings_gui.cpp	Mon Jun 18 22:49:55 2007 +0000
+++ b/src/settings_gui.cpp	Mon Jun 18 23:00:55 2007 +0000
@@ -24,6 +24,7 @@
 #include "vehicle.h"
 #include "date.h"
 #include "helpers.hpp"
+#include "newgrf_townname.h"
 
 static uint32 _difficulty_click_a;
 static uint32 _difficulty_click_b;
@@ -66,19 +67,44 @@
 	return buf;
 }
 
-static StringID _town_names[SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 2] = {STR_NULL};
+int _nb_orig_names = SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1;
+static StringID *_town_names = NULL;
+static StringID *_grf_names = NULL;
+static int _nb_grf_names = 0;
 
 void SortTownGeneratorNames()
 {
-	int nb_town_names = SPECSTR_TOWNNAME_LAST - SPECSTR_TOWNNAME_START + 1;
-	/* Init the strings */
-	if (_town_names[0] == STR_NULL) {
-		for (int i = 0; i < nb_town_names; i++) _town_names[i] = STR_TOWNNAME_ORIGINAL_ENGLISH + i;
-		_town_names[nb_town_names] = INVALID_STRING_ID;
-	}
+	int n = 0;
+
+	/* Get Newgrf generators' names */
+	free(_grf_names);
+	_grf_names = GetGRFTownNameList();
+	_nb_grf_names = 0;
+	for (StringID *s = _grf_names; *s != INVALID_STRING_ID; s++) _nb_grf_names++;
+
+	/* Prepare the list */
+	free(_town_names);
+	_town_names = MallocT<StringID>(_nb_orig_names + _nb_grf_names + 1);
+
+	/* Put the original strings */
+	for (int i = 0; i < _nb_orig_names; i++) _town_names[n++] = STR_TOWNNAME_ORIGINAL_ENGLISH + i;
+
+	/* Put the grf strings */
+	for (int i = 0; i < _nb_grf_names; i++) _town_names[n++] = _grf_names[i];
+
+	/* Put the terminator */
+	_town_names[n] = INVALID_STRING_ID;
 
 	/* Sort the strings */
-	qsort(&_town_names[0], nb_town_names, sizeof(StringID), &StringIDSorter);
+	qsort(&_town_names[0], _nb_orig_names + _nb_grf_names, sizeof(StringID), &StringIDSorter);
+}
+
+static inline StringID TownName(int town_name)
+{
+	if (town_name < _nb_orig_names) return STR_TOWNNAME_ORIGINAL_ENGLISH + town_name;
+	town_name -= _nb_orig_names;
+	if (town_name < _nb_grf_names) return _grf_names[town_name];
+	return STR_UNDEFINED;
 }
 
 static int GetCurRes()
@@ -120,7 +146,7 @@
 		SetDParam(1, _currency_specs[_opt_ptr->currency].name);
 		SetDParam(2, STR_UNITS_IMPERIAL + _opt_ptr->units);
 		SetDParam(3, STR_02E9_DRIVE_ON_LEFT + _opt_ptr->road_side);
-		SetDParam(4, STR_TOWNNAME_ORIGINAL_ENGLISH + _opt_ptr->town_name);
+		SetDParam(4, TownName(_opt_ptr->town_name));
 		SetDParam(5, _autosave_dropdown[_opt_ptr->autosave]);
 		SetDParam(6, SPECSTR_LANGUAGE_START + _dynlang.curr);
 		i = GetCurRes();
@@ -152,8 +178,8 @@
 		} return;
 		case 13: case 14: { /* Setup townname dropdown */
 			uint sel = 0;
-			for (uint i = 0; i < lengthof(_town_names) - 1; i++) {
-				if (_town_names[i] == STR_TOWNNAME_ORIGINAL_ENGLISH + _opt_ptr->town_name) {
+			for (uint i = 0; _town_names[i] != INVALID_STRING_ID; i++) {
+				if (_town_names[i] == TownName(_opt_ptr->town_name)) {
 					sel = i;
 					break;
 				}
@@ -214,7 +240,12 @@
 			break;
 		case 14: /* Town names */
 			if (_game_mode == GM_MENU) {
-				_opt_ptr->town_name = _town_names[e->we.dropdown.index] - STR_TOWNNAME_ORIGINAL_ENGLISH;
+				for (uint i = 0; _town_names[i] != INVALID_STRING_ID; i++) {
+					if (_town_names[e->we.dropdown.index] == TownName(i)) {
+						_opt_ptr->town_name = i;
+						break;
+					}
+				}
 				InvalidateWindow(WC_GAME_OPTIONS, 0);
 			}
 			break;
--- a/src/strings.cpp	Mon Jun 18 22:49:55 2007 +0000
+++ b/src/strings.cpp	Mon Jun 18 23:00:55 2007 +0000
@@ -28,6 +28,7 @@
 #include "cargotype.h"
 #include "group.h"
 #include "debug.h"
+#include "newgrf_townname.h"
 
 /* for opendir/readdir/closedir */
 # include "fios.h"
@@ -825,8 +826,8 @@
 					buff = GetStringWithArgs(buff, STR_UNKNOWN_DESTINATION, NULL, last);
 				} else {
 					int32 temp[2];
-					temp[0] = st->town->townnametype;
-					temp[1] = st->town->townnameparts;
+					temp[0] = STR_TOWN;
+					temp[1] = st->town->index;
 					buff = GetStringWithArgs(buff, st->string_id, temp, last);
 				}
 				break;
@@ -839,7 +840,21 @@
 				assert(IsValidTown(t));
 
 				temp[0] = t->townnameparts;
-				buff = GetStringWithArgs(buff, t->townnametype, temp, last);
+				uint32 grfid = t->townnamegrfid;
+
+				if (grfid == 0) {
+					/* Original town name */
+					buff = GetStringWithArgs(buff, t->townnametype, temp, last);
+				} else {
+					/* Newgrf town name */
+					if (GetGRFTownName(grfid) != NULL) {
+						/* The grf is loaded */
+						buff = GRFTownNameGenerate(buff, t->townnamegrfid, t->townnametype, t->townnameparts, last);
+					} else {
+						/* Fallback to english original */
+						buff = GetStringWithArgs(buff, SPECSTR_TOWNNAME_ENGLISH, temp, last);
+					}
+				}
 				break;
 			}
 
--- a/src/town.h	Mon Jun 18 22:49:55 2007 +0000
+++ b/src/town.h	Mon Jun 18 23:00:55 2007 +0000
@@ -83,6 +83,7 @@
 	uint32 population;
 
 	/* Town name */
+	uint32 townnamegrfid;
 	uint16 townnametype;
 	uint32 townnameparts;
 
--- a/src/town_cmd.cpp	Mon Jun 18 22:49:55 2007 +0000
+++ b/src/town_cmd.cpp	Mon Jun 18 23:00:55 2007 +0000
@@ -39,6 +39,7 @@
 #include "newgrf_callbacks.h"
 #include "newgrf_house.h"
 #include "newgrf_commons.h"
+#include "newgrf_townname.h"
 
 /**
  * Called if a new block is added to the town-pool
@@ -1295,6 +1296,7 @@
 
 static bool CreateTownName(uint32 *townnameparts)
 {
+	extern int _nb_orig_names;
 	Town *t2;
 	char buf1[64];
 	char buf2[64];
@@ -1305,7 +1307,9 @@
 	 * the other towns may take considerable amount of time (10000 is
 	 * too much). */
 	int tries = 1000;
-	uint16 townnametype = SPECSTR_TOWNNAME_START + _opt.town_name;
+	bool grf = (_opt.town_name >= _nb_orig_names);
+	uint32 grfid = grf ? GetGRFTownNameId(_opt.town_name - _nb_orig_names) : 0;
+	uint16 townnametype = grf ? GetGRFTownNameType(_opt.town_name - _nb_orig_names) : SPECSTR_TOWNNAME_START + _opt.town_name;
 
 	assert(townnameparts);
 
@@ -1314,7 +1318,11 @@
 		r = Random();
 
 		SetDParam(0, r);
-		GetString(buf1, townnametype, lastof(buf1));
+		if (grf && grfid != 0) {
+			GRFTownNameGenerate(buf1, grfid, townnametype, r, lastof(buf1));
+		} else {
+			GetString(buf1, townnametype, lastof(buf1));
+		}
 
 		/* Check size and width */
 		if (strlen(buf1) >= 31 || GetStringBoundingBox(buf1).width > 130) continue;
@@ -1351,6 +1359,7 @@
  */
 static void DoCreateTown(Town *t, TileIndex tile, uint32 townnameparts, TownSizeMode size_mode, uint size)
 {
+	extern int _nb_orig_names;
 	int x, i;
 
 	/* clear the town struct */
@@ -1392,7 +1401,15 @@
 	t->exclusive_counter = 0;
 	t->statues = 0;
 
-	t->townnametype = SPECSTR_TOWNNAME_START + _opt.town_name;
+	if (_opt.town_name < _nb_orig_names) {
+		/* Original town name */
+		t->townnamegrfid = 0;
+		t->townnametype = SPECSTR_TOWNNAME_START + _opt.town_name;
+	} else {
+		/* Newgrf town name */
+		t->townnamegrfid = GetGRFTownNameId(_opt.town_name  - _nb_orig_names);
+		t->townnametype  = GetGRFTownNameType(_opt.town_name - _nb_orig_names);
+	}
 	t->townnameparts = townnameparts;
 
 	UpdateTownVirtCoord(t);
@@ -2338,6 +2355,7 @@
 
 
 	    SLE_VAR(Town, num_houses,            SLE_UINT16),
+	SLE_CONDVAR(Town, townnamegrfid,         SLE_UINT32, 66, SL_MAX_VERSION),
 	    SLE_VAR(Town, townnametype,          SLE_UINT16),
 	    SLE_VAR(Town, townnameparts,         SLE_UINT32),