 common/Makefile.am           |    4 +
 common/effects.c             |    2 +-
 common/effects.h             |  209 ++++++-----
 common/fc_types.h            |   52 ++--
 common/improvement.h         |   30 +-
 common/requirements.h        |   18 +-
 common/rulesets.c            |  815 ++++++++++++++++++++++++++++++++++++++++++
 common/rulesets.h            |   60 +++
 common/tech.h                |   27 +-
 common/temp.c                |  378 +++++++++++++++++++
 common/temp.h                |  118 ++++++
 server/Makefile.am           |    2 +
 server/ruleset.c             |    7 +-
 server/rulesetdir.c          |  796 +++++++++++++++++++++++++++++++++++++++++
 server/rulesetdir.h          |   26 ++
 utility/generate_specenum.py |   58 +++
 utility/genlist.c            |   57 +++-
 utility/genlist.h            |    2 +
 utility/speclist.h           |   16 +-
 19 files changed, 2511 insertions(+), 166 deletions(-)

diff --git a/common/Makefile.am b/common/Makefile.am
index c43680d..3cccf68 100644
--- a/common/Makefile.am
+++ b/common/Makefile.am
@@ -57,6 +57,8 @@ libfreeciv_la_SOURCES = \
 		player.h	\
 		requirements.c	\
 		requirements.h	\
+		rulesets.c	\
+		rulesets.h	\
 		spaceship.c	\
 		spaceship.h	\
 		specialist.c	\
@@ -65,6 +67,8 @@ libfreeciv_la_SOURCES = \
 		team.h		\
 		tech.c		\
 		tech.h		\
+		temp.c		\
+		temp.h		\
 		terrain.c	\
 		terrain.h	\
 		tile.c		\
diff --git a/common/effects.c b/common/effects.c
index c54c8f8..7ecdecc 100644
--- a/common/effects.c
+++ b/common/effects.c
@@ -156,7 +156,7 @@ enum effect_type effect_type_from_str(const char *str)
   Return the name for an effect type enumeration.  Returns NULL if the
   effect_type is invalid.  This is the inverse of effect_type_from_str.
 **************************************************************************/
-const char *effect_type_name(enum effect_type effect_type)
+const char *effect_type_ruleset_name(enum effect_type effect_type)
 {
   fc_assert_ret_val(ARRAY_SIZE(effect_type_names) == EFT_LAST, NULL);
   if (effect_type >= 0 && effect_type < EFT_LAST) {
diff --git a/common/effects.h b/common/effects.h
index 089101e..90cf05b 100644
--- a/common/effects.h
+++ b/common/effects.h
@@ -22,105 +22,122 @@ struct requirement;
 
 /* Type of effects. (Used in effect.type field)
  * These must correspond to effect_type_names[] in effects.c. */
-enum effect_type {
-  EFT_TECH_PARASITE,
-  EFT_AIRLIFT,
-  EFT_ANY_GOVERNMENT,
-  EFT_CAPITAL_CITY,
-  EFT_ENABLE_NUKE,
-  EFT_ENABLE_SPACE,
-  EFT_SPECIALIST_OUTPUT,
-  EFT_OUTPUT_BONUS,
-  EFT_OUTPUT_BONUS_2,
-  EFT_OUTPUT_ADD_TILE,  /* add to each worked tile */
-  EFT_OUTPUT_INC_TILE,  /* add to each worked tile that already has output */
-  EFT_OUTPUT_PER_TILE,  /* increase tile output by given % */
-  EFT_OUTPUT_WASTE_PCT,
-  EFT_FORCE_CONTENT,
-  /* TODO: EFT_FORCE_CONTENT_PCT, */
-  EFT_GIVE_IMM_TECH,
-  EFT_GROWTH_FOOD,
-  EFT_HEALTH_PCT, /* reduced illness due to buildings, ... */
-  EFT_HAVE_EMBASSIES,
-  EFT_MAKE_CONTENT,
-  EFT_MAKE_CONTENT_MIL,
-  EFT_MAKE_CONTENT_MIL_PER,
-  /* TODO: EFT_MAKE_CONTENT_PCT, */
-  EFT_MAKE_HAPPY,
-  EFT_NO_ANARCHY,
-  EFT_NUKE_PROOF,
-  /* TODO: EFT_POLLU_ADJ, */
-  /* TODO: EFT_POLLU_PCT, */
-  /* TODO: EFT_POLLU_POP_ADJ, */
-  EFT_POLLU_POP_PCT,
-  /* TODO: EFT_POLLU_PROD_ADJ, */
-  EFT_POLLU_PROD_PCT,
-  /* TODO: EFT_PROD_PCT, */
-  EFT_REVEAL_CITIES,
-  EFT_REVEAL_MAP,
-  /* TODO: EFT_INCITE_DIST_ADJ, */
-  EFT_INCITE_COST_PCT,
-  EFT_SIZE_ADJ,
-  EFT_SIZE_UNLIMIT,
-  EFT_SS_STRUCTURAL,
-  EFT_SS_COMPONENT,
-  EFT_SS_MODULE,
-  EFT_SPY_RESISTANT,
-  EFT_MOVE_BONUS,
-  EFT_UNIT_NO_LOSE_POP,
-  EFT_UNIT_RECOVER,
-  EFT_UPGRADE_UNIT,
-  EFT_UPKEEP_FREE,
-  EFT_TECH_UPKEEP_FREE,
-  EFT_NO_UNHAPPY,
-  EFT_VETERAN_BUILD,
-  EFT_VETERAN_COMBAT,
-  EFT_HP_REGEN,
-  EFT_CITY_VISION_RADIUS_SQ,
-  EFT_UNIT_VISION_RADIUS_SQ,
-  EFT_DEFEND_BONUS,
-  EFT_NO_INCITE,
-  EFT_GAIN_AI_LOVE,
-  EFT_TURN_YEARS,
-  EFT_SLOW_DOWN_TIMELINE, /* Space module tech slowdown */
-  EFT_CIVIL_WAR_CHANCE,
-  EFT_MIGRATION_PCT, /* change of the migration score */
-  EFT_EMPIRE_SIZE_BASE, /* +1 unhappy when more than this cities */
-  EFT_EMPIRE_SIZE_STEP, /* adds additional +1 unhappy steps to above */
-  EFT_MAX_RATES,
-  EFT_MARTIAL_LAW_EACH,
-  EFT_MARTIAL_LAW_MAX,
-  EFT_RAPTURE_GROW,
-  EFT_UNBRIBABLE_UNITS,
-  EFT_REVOLUTION_WHEN_UNHAPPY,
-  EFT_HAS_SENATE,
-  EFT_INSPIRE_PARTISANS,
-  EFT_HAPPINESS_TO_GOLD,
-  EFT_FANATICS, /* stupid special case, we hatess it */
-  EFT_NO_DIPLOMACY,
-  EFT_TRADE_REVENUE_BONUS,
-  EFT_UNHAPPY_FACTOR, /* multiply unhappy upkeep by this effect */
-  EFT_UPKEEP_FACTOR,  /* multiply upkeep by this effect */
-  EFT_UNIT_UPKEEP_FREE_PER_CITY, /* this many units are free from upkeep */
-  EFT_OUTPUT_WASTE,
-  EFT_OUTPUT_WASTE_BY_DISTANCE,
-  EFT_OUTPUT_PENALTY_TILE, /* -1 penalty to tiles producing more than this */
-  EFT_OUTPUT_INC_TILE_CELEBRATE,
-  EFT_CITY_UNHAPPY_SIZE, /* all citizens after this are unhappy */
-  EFT_CITY_RADIUS_SQ, /* add to default squared city radius */
-  EFT_CITY_BUILD_SLOTS, /* slots to build units */
-  EFT_UPGRADE_PRICE_PCT,
-  EFT_VISIBLE_WALLS,     /* City should use walls gfx */
-  EFT_TECH_COST_FACTOR,
-  EFT_SHIELD2GOLD_FACTOR, /* [x%] gold upkeep instead of [1] shield upkeep
-                           * for units */
-  EFT_TILE_WORKABLE,
-  EFT_LAST	/* keep this last */
-};
+#define SPECENUM_NAME effect_type
+#define SPECENUM_VALUE0 EFT_TECH_PARASITE
+#define SPECENUM_VALUE1 EFT_AIRLIFT
+#define SPECENUM_VALUE2 EFT_ANY_GOVERNMENT
+#define SPECENUM_VALUE3 EFT_CAPITAL_CITY
+#define SPECENUM_VALUE4 EFT_ENABLE_NUKE
+#define SPECENUM_VALUE5 EFT_ENABLE_SPACE
+#define SPECENUM_VALUE6 EFT_SPECIALIST_OUTPUT
+#define SPECENUM_VALUE7 EFT_OUTPUT_BONUS
+#define SPECENUM_VALUE8 EFT_OUTPUT_BONUS_2
+/* add to each worked tile */
+#define SPECENUM_VALUE9 EFT_OUTPUT_ADD_TILE
+/* add to each worked tile that already has output */
+#define SPECENUM_VALUE10 EFT_OUTPUT_INC_TILE
+/* increase tile output by given % */
+#define SPECENUM_VALUE11 EFT_OUTPUT_PER_TILE
+#define SPECENUM_VALUE12 EFT_OUTPUT_WASTE_PCT
+#define SPECENUM_VALUE13 EFT_FORCE_CONTENT
+/* TODO: EFT_FORCE_CONTENT_PCT */
+#define SPECENUM_VALUE14 EFT_GIVE_IMM_TECH
+#define SPECENUM_VALUE15 EFT_GROWTH_FOOD
+/* reduced illness due to buildings ... */
+#define SPECENUM_VALUE16 EFT_HEALTH_PCT
+#define SPECENUM_VALUE17 EFT_HAVE_EMBASSIES
+#define SPECENUM_VALUE18 EFT_MAKE_CONTENT
+#define SPECENUM_VALUE19 EFT_MAKE_CONTENT_MIL
+#define SPECENUM_VALUE20 EFT_MAKE_CONTENT_MIL_PER
+/* TODO: EFT_MAKE_CONTENT_PCT */
+#define SPECENUM_VALUE21 EFT_MAKE_HAPPY
+#define SPECENUM_VALUE22 EFT_NO_ANARCHY
+#define SPECENUM_VALUE23 EFT_NUKE_PROOF
+/* TODO: EFT_POLLU_ADJ */
+/* TODO: EFT_POLLU_PCT */
+/* TODO: EFT_POLLU_POP_ADJ */
+#define SPECENUM_VALUE24 EFT_POLLU_POP_PCT
+/* TODO: EFT_POLLU_PROD_ADJ */
+#define SPECENUM_VALUE25 EFT_POLLU_PROD_PCT
+/* TODO: EFT_PROD_PCT */
+#define SPECENUM_VALUE26 EFT_REVEAL_CITIES
+#define SPECENUM_VALUE27 EFT_REVEAL_MAP
+/* TODO: EFT_INCITE_DIST_ADJ */
+#define SPECENUM_VALUE28 EFT_INCITE_COST_PCT
+#define SPECENUM_VALUE29 EFT_SIZE_ADJ
+#define SPECENUM_VALUE30 EFT_SIZE_UNLIMIT
+#define SPECENUM_VALUE31 EFT_SS_STRUCTURAL
+#define SPECENUM_VALUE32 EFT_SS_COMPONENT
+#define SPECENUM_VALUE33 EFT_SS_MODULE
+#define SPECENUM_VALUE34 EFT_SPY_RESISTANT
+#define SPECENUM_VALUE35 EFT_MOVE_BONUS
+#define SPECENUM_VALUE36 EFT_UNIT_NO_LOSE_POP
+#define SPECENUM_VALUE37 EFT_UNIT_RECOVER
+#define SPECENUM_VALUE38 EFT_UPGRADE_UNIT
+#define SPECENUM_VALUE39 EFT_UPKEEP_FREE
+#define SPECENUM_VALUE40 EFT_TECH_UPKEEP_FREE
+#define SPECENUM_VALUE41 EFT_NO_UNHAPPY
+#define SPECENUM_VALUE42 EFT_VETERAN_BUILD
+#define SPECENUM_VALUE43 EFT_VETERAN_COMBAT
+#define SPECENUM_VALUE44 EFT_HP_REGEN
+#define SPECENUM_VALUE45 EFT_CITY_VISION_RADIUS_SQ
+#define SPECENUM_VALUE46 EFT_UNIT_VISION_RADIUS_SQ
+#define SPECENUM_VALUE47 EFT_DEFEND_BONUS
+#define SPECENUM_VALUE48 EFT_NO_INCITE
+#define SPECENUM_VALUE49 EFT_GAIN_AI_LOVE
+#define SPECENUM_VALUE50 EFT_TURN_YEARS
+#define SPECENUM_VALUE51 EFT_SLOW_DOWN_TIMELINE
+#define SPECENUM_VALUE52 EFT_CIVIL_WAR_CHANCE
+/* change of the migration score */
+#define SPECENUM_VALUE53 EFT_MIGRATION_PCT
+/* +1 unhappy when more than this cities */
+#define SPECENUM_VALUE54 EFT_EMPIRE_SIZE_BASE
+/* adds additional +1 unhappy steps to above */
+#define SPECENUM_VALUE55 EFT_EMPIRE_SIZE_STEP
+#define SPECENUM_VALUE56 EFT_MAX_RATES
+#define SPECENUM_VALUE57 EFT_MARTIAL_LAW_EACH
+#define SPECENUM_VALUE58 EFT_MARTIAL_LAW_MAX
+#define SPECENUM_VALUE59 EFT_RAPTURE_GROW
+#define SPECENUM_VALUE60 EFT_UNBRIBABLE_UNITS
+#define SPECENUM_VALUE61 EFT_REVOLUTION_WHEN_UNHAPPY
+#define SPECENUM_VALUE62 EFT_HAS_SENATE
+#define SPECENUM_VALUE63 EFT_INSPIRE_PARTISANS
+#define SPECENUM_VALUE64 EFT_HAPPINESS_TO_GOLD
+/* stupid special case we hatess it */
+#define SPECENUM_VALUE65 EFT_FANATICS
+#define SPECENUM_VALUE66 EFT_NO_DIPLOMACY
+#define SPECENUM_VALUE67 EFT_TRADE_REVENUE_BONUS
+/* multiply unhappy upkeep by this effect */
+#define SPECENUM_VALUE68 EFT_UNHAPPY_FACTOR
+/* multiply upkeep by this effect */
+#define SPECENUM_VALUE69 EFT_UPKEEP_FACTOR
+/* this many units are free from upkeep */
+#define SPECENUM_VALUE70 EFT_UNIT_UPKEEP_FREE_PER_CITY
+#define SPECENUM_VALUE71 EFT_OUTPUT_WASTE
+#define SPECENUM_VALUE72 EFT_OUTPUT_WASTE_BY_DISTANCE
+/* -1 penalty to tiles producing more than this */
+#define SPECENUM_VALUE73 EFT_OUTPUT_PENALTY_TILE
+#define SPECENUM_VALUE74 EFT_OUTPUT_INC_TILE_CELEBRATE
+/* all citizens after this are unhappy */
+#define SPECENUM_VALUE75 EFT_CITY_UNHAPPY_SIZE
+/* add to default squared city radius */
+#define SPECENUM_VALUE76 EFT_CITY_RADIUS_SQ
+/* slots to build units */
+#define SPECENUM_VALUE77 EFT_CITY_BUILD_SLOTS
+#define SPECENUM_VALUE78 EFT_UPGRADE_PRICE_PCT
+/* City should use walls gfx */
+#define SPECENUM_VALUE79 EFT_VISIBLE_WALLS
+#define SPECENUM_VALUE80 EFT_TECH_COST_FACTOR
+/* [x%] gold upkeep instead of [1] shield upkeep for units */
+#define SPECENUM_VALUE81 EFT_SHIELD2GOLD_FACTOR
+#define SPECENUM_VALUE82 EFT_TILE_WORKABLE
+/* keep this last */
+#define SPECENUM_VALUE83 EFT_LAST
+#include "specenum_gen.h"
 
 /* lookups */
 enum effect_type effect_type_from_str(const char *str);
-const char *effect_type_name(enum effect_type effect_type);
+const char *effect_type_ruleset_name(enum effect_type effect_type);
 
 /* An effect is provided by a source.  If the source is present, and the
  * other conditions (described below) are met, the effect will be active.
diff --git a/common/fc_types.h b/common/fc_types.h
index 2890d64..9106c59 100644
--- a/common/fc_types.h
+++ b/common/fc_types.h
@@ -225,29 +225,35 @@ typedef union {
 /* The kind of universals_u (value_union_type was req_source_type).
  * Note: order must correspond to universal_names[] in requirements.c.
  */
-enum universals_n {
-  VUT_NONE,
-  VUT_ADVANCE,
-  VUT_GOVERNMENT,
-  VUT_IMPROVEMENT,
-  VUT_SPECIAL,
-  VUT_TERRAIN,
-  VUT_NATION,
-  VUT_UTYPE,
-  VUT_UTFLAG,
-  VUT_UCLASS,
-  VUT_UCFLAG,
-  VUT_OTYPE,
-  VUT_SPECIALIST,
-  VUT_MINSIZE,		/* Minimum size: at city range means city size */
-  VUT_AI_LEVEL,		/* AI level of the player */
-  VUT_TERRAINCLASS,	/* More generic terrain type, currently "Land" or "Ocean" */
-  VUT_BASE,
-  VUT_MINYEAR,
-  VUT_TERRAINALTER,     /* Terrain alterations that are possible */
-  VUT_CITYTILE,         /* Target tile is used by city */
-  VUT_LAST
-};
+#define SPECENUM_NAME universals_n
+#define SPECENUM_VALUE0 VUT_NONE
+#define SPECENUM_VALUE1 VUT_ADVANCE
+#define SPECENUM_VALUE2 VUT_GOVERNMENT
+#define SPECENUM_VALUE3 VUT_IMPROVEMENT
+#define SPECENUM_VALUE4 VUT_SPECIAL
+#define SPECENUM_VALUE5 VUT_TERRAIN
+#define SPECENUM_VALUE6 VUT_NATION
+#define SPECENUM_VALUE7 VUT_UTYPE
+#define SPECENUM_VALUE8 VUT_UTFLAG
+#define SPECENUM_VALUE9 VUT_UCLASS
+#define SPECENUM_VALUE10 VUT_UCFLAG
+#define SPECENUM_VALUE11 VUT_OTYPE
+#define SPECENUM_VALUE12 VUT_SPECIALIST
+/* Minimum size: at city range means city size */
+#define SPECENUM_VALUE13 VUT_MINSIZE
+/* AI level of the player */
+#define SPECENUM_VALUE14 VUT_AI_LEVEL
+/* More generic terrain type currently "Land" or "Ocean" */
+#define SPECENUM_VALUE15 VUT_TERRAINCLASS
+#define SPECENUM_VALUE16 VUT_BASE
+#define SPECENUM_VALUE17 VUT_MINYEAR
+/* Terrain alterations that are possible */
+#define SPECENUM_VALUE18 VUT_TERRAINALTER
+/* Target tile is used by city. */
+#define SPECENUM_VALUE19 VUT_CITYTILE
+/* Keep this last. */
+#define SPECENUM_VALUE20 VUT_LAST
+#include "specenum_gen.h"
 
 struct universal {
   universals_u value;
diff --git a/common/improvement.h b/common/improvement.h
index 2530bb2..32a4277 100644
--- a/common/improvement.h
+++ b/common/improvement.h
@@ -34,22 +34,24 @@
 
 #define B_NEVER (NULL)
 
-
 /* Changing these breaks network compatibility. */
-enum impr_flag_id {
-  IF_VISIBLE_BY_OTHERS,  /* improvement should be visible to others without spying */
-  IF_SAVE_SMALL_WONDER,  /* this small wonder is moved to another city if game.savepalace is on. */
-  IF_GOLD,		 /* when built, gives gold */
-  IF_LAST
-};
-
-enum impr_genus_id {
-  IG_GREAT_WONDER,
-  IG_SMALL_WONDER,
-  IG_IMPROVEMENT,
-  IG_SPECIAL,
-  IG_LAST
-};
+#define SPECENUM_NAME impr_flag_id
+/* improvement should be visible to others without spying */
+#define SPECENUM_VALUE0 IF_VISIBLE_BY_OTHERS
+/* this small wonder is moved to another city if game.savepalace is on. */
+#define SPECENUM_VALUE1 IF_SAVE_SMALL_WONDER
+/* when built, gives gold */
+#define SPECENUM_VALUE2 IF_GOLD
+#define SPECENUM_VALUE3 IF_LAST
+#include "specenum_gen.h"
+
+#define SPECENUM_NAME impr_genus_id
+#define SPECENUM_VALUE0 IG_GREAT_WONDER
+#define SPECENUM_VALUE1 IG_SMALL_WONDER
+#define SPECENUM_VALUE2 IG_IMPROVEMENT
+#define SPECENUM_VALUE3 IG_SPECIAL
+#define SPECENUM_VALUE4 IG_LAST
+#include "specenum_gen.h"
 
 BV_DEFINE(bv_imprs, B_LAST);
 
diff --git a/common/requirements.h b/common/requirements.h
index e3c9937..82e34fa 100644
--- a/common/requirements.h
+++ b/common/requirements.h
@@ -22,15 +22,15 @@
 
 /* Range of requirements.  This must correspond to req_range_names[]
  * in requirements.c. */
-enum req_range {
-  REQ_RANGE_LOCAL,
-  REQ_RANGE_ADJACENT,
-  REQ_RANGE_CITY,
-  REQ_RANGE_CONTINENT,
-  REQ_RANGE_PLAYER,
-  REQ_RANGE_WORLD,
-  REQ_RANGE_LAST   /* keep this last */
-};
+#define SPECENUM_NAME req_range
+#define SPECENUM_VALUE0 REQ_RANGE_LOCAL
+#define SPECENUM_VALUE1 REQ_RANGE_ADJACENT
+#define SPECENUM_VALUE2 REQ_RANGE_CITY
+#define SPECENUM_VALUE3 REQ_RANGE_CONTINENT
+#define SPECENUM_VALUE4 REQ_RANGE_PLAYER
+#define SPECENUM_VALUE5 REQ_RANGE_WORLD
+#define SPECENUM_VALUE6 REQ_RANGE_LAST /* keep this last */
+#include "specenum_gen.h"
 
 /* A requirement. This requirement is basically a conditional; it may or
  * may not be active on a target.  If it is active then something happens.
diff --git a/common/rulesets.c b/common/rulesets.c
new file mode 100644
index 0000000..5a864a9
--- /dev/null
+++ b/common/rulesets.c
@@ -0,0 +1,815 @@
+/**********************************************************************
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+   This program 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; either version 2, or (at your option)
+   any later version.
+
+   This program 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.
+***********************************************************************/
+
+/**********************************************************************
+  TODO:
+    - check all requirements
+    - advances: use reqs instead of advances for lists
+    - something like secfile_error()
+    - check all loaded values! (explicit check or use
+      secfile_lookup_*default())
+    - merge MAX_LEN_BUF, MAX_LEN_BUFFER (registry.c), ...
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* utility */
+#include "shared.h" /* bool */
+
+/* common */
+#include "improvement.h"
+#include "requirements.h"
+#include "temp.h"
+
+#include "rulesets.h"
+
+/* Define this to get additional output */
+#define DEBUG_RULESET
+
+#define SPECENUM_NAME ruleset_section
+#define SPECENUM_VALUE0 RSEC_MAIN
+#define SPECENUM_VALUE1 RSEC_ORDER
+#define SPECENUM_VALUE2 RSEC_ADVANCES
+#define SPECENUM_VALUE3 RSEC_IMPROVEMENTS
+#define SPECENUM_VALUE4 RSEC_GOVERMENTS
+#define SPECENUM_VALUE5 RSEC_UNITS
+#define SPECENUM_VALUE6 RSEC_TERRAIN
+#define SPECENUM_VALUE7 RSEC_CITIES
+#define SPECENUM_VALUE8 RSEC_NATIONS
+#define SPECENUM_VALUE9 RSEC_EFFECTS
+#define SPECENUM_VALUE10 RSEC_LAST
+#include "specenum_gen.h"
+
+/* Should correspond to enum ruleset_section. */
+static const char * const ruleset_section_str[] = {
+  "ruleset_main",
+  "ruleset_order",
+  "ruleset_advances",
+  "ruleset_improvements",
+  "ruleset_governments",
+  "ruleset_units",
+  "ruleset_terrain",
+  "ruleset_cities",
+  "ruleset_nations",
+  "ruleset_effects",
+  NULL
+};
+
+struct ruleset *game_ruleset = NULL;
+
+
+static bool ruleset_load_advances(struct section_file *file);
+static bool ruleset_load_improvements(struct section_file *file);
+static bool ruleset_load_governments(struct section_file *file);
+static bool ruleset_load_units(struct section_file *file);
+static bool ruleset_load_terrain(struct section_file *file);
+static bool ruleset_load_cities(struct section_file *file);
+static bool ruleset_load_nations(struct section_file *file);
+static bool ruleset_load_effects(struct section_file *file);
+
+static void ruleset_save_advances(struct section_file *file);
+static void ruleset_save_improvements(struct section_file *file);
+static void ruleset_save_governments(struct section_file *file);
+static void ruleset_save_units(struct section_file *file);
+static void ruleset_save_terrain(struct section_file *file);
+static void ruleset_save_cities(struct section_file *file);
+static void ruleset_save_nations(struct section_file *file);
+static void ruleset_save_effects(struct section_file *file);
+
+static void Xadvance_check_root_reqs(struct Xdvance *pXdvance,
+                                     struct Xdvance_list *Xdv_list,
+                                     struct Xdvance *pXdv_now);
+static bool Xadvance_check_dependency(struct Xdvance *pXdvance,
+                                      struct Xdvance_list *Xdv_list,
+                                      struct Xdvance *pXdv_now);
+
+#define MAX_LEN_BUF 1024
+
+static void ruleset_log_real(const char *file, const char *function,
+                             int line, const char *format, ...)
+                             fc__attribute((__format__(__printf__, 4, 5)));
+#define ruleset_log(format, ...)                                            \
+  ruleset_log_real(__FILE__, __FUNCTION__, __LINE__,                        \
+                   format, ## __VA_ARGS__);
+#define ruleset_log_ret_val(condition, value, format, ...)                  \
+  if (!(condition)) {                                                       \
+    ruleset_log(format, ## __VA_ARGS__);                                    \
+    return value;                                                           \
+  }
+#define ruleset_log_ret(condition, format, ...)                             \
+  ruleset_log_ret(condition, , format, ...);
+
+static char error_buffer[MAX_LEN_BUF] = "\0";
+
+/**************************************************************************
+  Returns the last error which occured in a string.  It never returns NULL.
+**************************************************************************/
+const char *ruleset_error(void)
+{
+  return error_buffer;
+}
+
+/**************************************************************************
+  Edit the error_buffer.
+**************************************************************************/
+static void ruleset_log_real(const char *file, const char *function, int line,
+                             const char *format, ...)
+{
+  char message[MAX_LEN_BUF];
+  va_list args;
+
+  va_start(args, format);
+  fc_vsnprintf(message, sizeof(message), format, args);
+  va_end(args);
+
+#ifdef DEBUG
+  fc_snprintf(error_buffer, sizeof(error_buffer), "In %s() [%s:%d]: %s",
+              function, file, line, message);
+#else
+  fc_snprintf(error_buffer, sizeof(error_buffer), "%s", message);
+#endif
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+void ruleset_init(void)
+{
+  enum advance_special asp;
+
+  fc_assert_ret(game_ruleset == NULL);
+
+  game_ruleset = fc_calloc(1, sizeof(*game_ruleset));
+
+  /* Requirements */
+  game_ruleset->reqs = requirement_list_new();
+
+  /* Advances */
+  game_ruleset->advances = Xdvance_list_new();
+  game_ruleset->advances_special = Xdvance_list_new();
+
+  /* Define dummy structs for the special advances. */
+  for (asp = advance_special_begin(); asp != advance_special_end();
+       asp = advance_special_next(asp)) {
+    struct Xdvance *pXdvance = Xdvance_new();
+
+    pXdvance->atype = A_TYPE_SPECIAL;
+    pXdvance->aspecial = asp;
+    name_set(&pXdvance->name, advance_special_name(asp));
+
+    Xdvance_list_append(game_ruleset->advances_special, pXdvance);
+  }
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+void ruleset_free(void)
+{
+  fc_assert_ret(game_ruleset != NULL);
+
+  /* Requirements */
+  requirement_list_iterate(game_ruleset->reqs, preq) {
+    requirement_list_remove(game_ruleset->reqs, preq);
+    requirement_destroy(preq);
+  } requirement_list_iterate_end;
+  requirement_list_destroy(game_ruleset->reqs);
+
+  /* Advances */
+  Xdvance_list_iterate(game_ruleset->advances, pXdvance) {
+    Xdvance_list_remove(game_ruleset->advances, pXdvance);
+    Xdvance_destroy(pXdvance);
+  } Xdvance_list_iterate_end;
+  Xdvance_list_destroy(game_ruleset->advances);
+
+  Xdvance_list_iterate(game_ruleset->advances_special, pXdvance) {
+    Xdvance_list_remove(game_ruleset->advances_special, pXdvance);
+    Xdvance_destroy(pXdvance);
+  } Xdvance_list_iterate_end;
+  Xdvance_list_destroy(game_ruleset->advances_special);
+
+  free(game_ruleset);
+  game_ruleset = NULL;
+}
+
+/**************************************************************************
+  Lookup optional string, returning allocated memory or NULL.
+**************************************************************************/
+char *ruleset_lookup_string(struct section_file *file,
+                            const char *format, ...)
+{
+  char buf[MAX_LEN_BUF];
+  const char *sval;
+  va_list args;
+
+  va_start(args, format);
+  fc_vsnprintf(buf, sizeof(buf), format, args);
+  va_end(args);
+
+  sval = secfile_lookup_str(file, "%s", buf);
+  if (sval) {
+    /* FIXME: The cast should be safe, but code style is ugly. */
+    sval = skip_leading_spaces((char *) sval);
+    if (strlen(sval) > 0) {
+      return fc_strdup(sval);
+    }
+  }
+
+  return NULL;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+bool ruleset_load(struct section_file *file)
+{
+  ruleset_log_ret_val(game_ruleset != NULL, FALSE,
+                      _("Ruleset not initialised"));
+
+  if (!ruleset_load_advances(file) || !ruleset_validate_advances()
+      || !ruleset_load_improvements(file) || !ruleset_validate_improvements()
+      || !ruleset_load_governments(file) || !ruleset_validate_governments()
+      || !ruleset_load_units(file) || !ruleset_validate_units()
+      || !ruleset_load_terrain(file) || !ruleset_validate_terrain()
+      || !ruleset_load_cities(file) || !ruleset_validate_cities()
+      || !ruleset_load_nations(file) || !ruleset_validate_nations()
+      || !ruleset_load_effects(file) || !ruleset_validate_effects()) {
+    /* The error message is available via ruleset_error(). */;
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static bool ruleset_load_advances(struct section_file *file)
+{
+  const char **advance_flags_order = NULL;
+  size_t advance_flags_size = 0;
+  int num_advances, i;
+
+  fc_assert_ret_val(game_ruleset != NULL, FALSE);
+
+  num_advances
+     = secfile_lookup_int_default(file, -1, "%s.count",
+                                  ruleset_section_str[RSEC_ADVANCES]);
+  ruleset_log_ret_val(num_advances != 0, FALSE, _("No Advances defined."));
+
+  advance_flags_order
+    = secfile_lookup_str_vec(file, &advance_flags_size, "%s.advance_flags",
+                             ruleset_section_str[RSEC_ORDER]);
+
+  /* Load basic data. */
+  for (i = 0; i < num_advances; i++) {
+    struct Xdvance *pXdvance = Xdvance_new();
+    const char *str;
+    char buf[32];
+
+    fc_snprintf(buf, sizeof(buf), "%s.a%d",
+                ruleset_section_str[RSEC_ADVANCES], i);
+
+    pXdvance->atype = A_TYPE_TECH;
+    pXdvance->index = i;
+
+    str = secfile_lookup_str_default(file, NULL, "%s.name", buf);
+    ruleset_log_ret_val(str != NULL, FALSE, _("No name for advance %d."), i);
+    name_set(&pXdvance->name, str);
+
+    sz_strlcpy(pXdvance->graphic_str,
+               secfile_lookup_str_default(file, "-", "%s.graphic_str", buf));
+    sz_strlcpy(pXdvance->graphic_alt,
+               secfile_lookup_str_default(file, "-", "%s.graphic_alt", buf));
+    pXdvance->helptext = ruleset_lookup_string(file, buf, "helptext");
+    pXdvance->bonus_message
+      = ruleset_lookup_string(file, buf, "bonus_message");
+
+    pXdvance->bv_flags
+      = tech_flag_id_str_to_bv(secfile_lookup_str_default(file, NULL,
+                                                          "%s.aflags", buf),
+                               advance_flags_order, advance_flags_size);
+
+    pXdvance->preset_cost
+      = secfile_lookup_int_default(file, -1, "%s.preset_cost", buf);
+
+    Xdvance_list_append(game_ruleset->advances, pXdvance);
+  }
+
+  /* Load Requirements. Must be after loading all advances for
+   * Xdvance_to_index() to work. */
+  Xdvance_list_iterate(game_ruleset->advances, pXdvance) {
+    char buf[32];
+
+    fc_snprintf(buf, sizeof(buf), "%s.a%d",
+                ruleset_section_str[RSEC_ADVANCES],
+                Xdvance_to_index(pXdvance));
+
+    pXdvance->adv_reqs
+      = Xdvance_str_to_list(secfile_lookup_str_default(file, "", "%s.reqs",
+                                                       buf));
+    /* pXdvance->reqs_all is set by ruleset_validate_advances(). */
+    pXdvance->adv_allows
+      = Xdvance_str_to_list(secfile_lookup_str_default(file, "", "%s.allows",
+                                                       buf));
+    pXdvance->adv_roots
+      = Xdvance_str_to_list(secfile_lookup_str_default(file, "", "%s.roots",
+                                                       buf));
+  } Xdvance_list_iterate_end;
+
+  return TRUE;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static bool ruleset_load_improvements(struct section_file *file)
+{
+  (void) secfile_lookup_str_default(file, "", "%s.tmp",
+                                    ruleset_section_str[RSEC_IMPROVEMENTS]);
+  return TRUE;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static bool ruleset_load_governments(struct section_file *file)
+{
+  (void) secfile_lookup_str_default(file, "", "%s.tmp",
+                                    ruleset_section_str[RSEC_GOVERMENTS]);
+  return TRUE;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static bool ruleset_load_units(struct section_file *file)
+{
+  (void) secfile_lookup_str_default(file, "", "%s.tmp",
+                                    ruleset_section_str[RSEC_UNITS]);
+  return TRUE;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static bool ruleset_load_terrain(struct section_file *file)
+{
+  (void) secfile_lookup_str_default(file, "", "%s.tmp",
+                                    ruleset_section_str[RSEC_TERRAIN]);
+  return TRUE;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static bool ruleset_load_cities(struct section_file *file)
+{
+  (void) secfile_lookup_str_default(file, "", "%s.tmp",
+                                    ruleset_section_str[RSEC_CITIES]);
+  return TRUE;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static bool ruleset_load_nations(struct section_file *file)
+{
+  (void) secfile_lookup_str_default(file, "", "%s.tmp",
+                                    ruleset_section_str[RSEC_NATIONS]);
+  return TRUE;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static bool ruleset_load_effects(struct section_file *file)
+{
+  (void) secfile_lookup_str_default(file, "", "%s.tmp",
+                                    ruleset_section_str[RSEC_EFFECTS]);
+  return TRUE;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+void ruleset_save(struct section_file *file)
+{
+  fc_assert_ret(game_ruleset != NULL);
+
+  secfile_insert_int(file, 1, "%s.version", ruleset_section_str[RSEC_MAIN]);
+
+  ruleset_save_advances(file);
+  ruleset_save_improvements(file);
+  ruleset_save_governments(file);
+  ruleset_save_units(file);
+  ruleset_save_terrain(file);
+  ruleset_save_cities(file);
+  ruleset_save_nations(file);
+  ruleset_save_effects(file);
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static void ruleset_save_advances(struct section_file *file)
+{
+  int num_advances;
+
+  fc_assert_ret(game_ruleset != NULL);
+
+  num_advances = Xdvance_list_size(game_ruleset->advances);
+  if (num_advances == 0) {
+    return;
+  }
+
+  {
+    enum tech_flag_id flag;
+    const char* names[TF_LAST];
+
+    for (flag = tech_flag_id_begin(); flag != tech_flag_id_end();
+         flag = tech_flag_id_next(flag)) {
+      names[flag] = tech_flag_id_name(flag);
+    }
+
+    secfile_insert_str_vec(file, names, TF_LAST, "%s.advance_flags",
+                           ruleset_section_str[RSEC_ORDER]);
+  }
+
+  Xdvance_list_iterate(game_ruleset->advances, pXdvance) {
+    char buf[32];
+
+    fc_snprintf(buf, sizeof(buf), "%s.a%d",
+                ruleset_section_str[RSEC_ADVANCES],
+                Xdvance_to_index(pXdvance));
+
+    secfile_insert_str(file, Xdvance_rule_name(pXdvance), "%s.name", buf);
+    secfile_insert_str(file, pXdvance->graphic_str,
+                       "%s.graphic_str", buf);
+    secfile_insert_str(file, pXdvance->graphic_alt,
+                       "%s.graphic_alt", buf);
+    secfile_insert_str(file, pXdvance->helptext, "%s.helptext", buf);
+    secfile_insert_str(file, pXdvance->bonus_message, "%s.bonus_message",
+                       buf);
+
+    secfile_insert_str(file, Xdvance_list_to_str(pXdvance->adv_reqs),
+                       "%s.reqs", buf);
+    secfile_insert_str(file, Xdvance_list_to_str(pXdvance->adv_allows),
+                       "%s.allows", buf);
+    secfile_insert_str(file, Xdvance_list_to_str(pXdvance->adv_roots),
+                       "%s.roots", buf);
+
+    secfile_insert_str(file, tech_flag_id_bv_to_str(pXdvance->bv_flags),
+                       "%s.aflags", buf);
+
+    secfile_insert_int(file, pXdvance->preset_cost, "%s.preset_cost", buf);
+  } Xdvance_list_iterate_end;
+  secfile_insert_int(file, num_advances, "%s.count",
+                     ruleset_section_str[RSEC_ADVANCES]);
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static void ruleset_save_improvements(struct section_file *file)
+{
+  secfile_insert_str(file, "tmp", "%s.tmp",
+                     ruleset_section_str[RSEC_IMPROVEMENTS]);
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static void ruleset_save_governments(struct section_file *file)
+{
+  secfile_insert_str(file, "tmp", "%s.tmp",
+                     ruleset_section_str[RSEC_GOVERMENTS]);
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static void ruleset_save_units(struct section_file *file)
+{
+  secfile_insert_str(file, "tmp", "%s.tmp",
+                     ruleset_section_str[RSEC_UNITS]);
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static void ruleset_save_terrain(struct section_file *file)
+{
+  secfile_insert_str(file, "tmp", "%s.tmp",
+                     ruleset_section_str[RSEC_TERRAIN]);
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static void ruleset_save_cities(struct section_file *file)
+{
+  secfile_insert_str(file, "tmp", "%s.tmp",
+                     ruleset_section_str[RSEC_CITIES]);
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static void ruleset_save_nations(struct section_file *file)
+{
+  secfile_insert_str(file, "tmp", "%s.tmp",
+                     ruleset_section_str[RSEC_NATIONS]);
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static void ruleset_save_effects(struct section_file *file)
+{
+  secfile_insert_str(file, "tmp", "%s.tmp",
+                     ruleset_section_str[RSEC_EFFECTS]);
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+bool ruleset_validate_advances(void)
+{
+  bool restart = TRUE;
+  int i;
+
+  fc_assert_ret_val(game_ruleset != NULL, FALSE);
+
+  /* Check for invalid entries (A_SPECIAL_NEVER). */
+  restart = TRUE;
+  while (restart && Xdvance_list_size(game_ruleset->advances)) {
+    restart = FALSE;
+    Xdvance_list_iterate(game_ruleset->advances, pXdvance) {
+      struct Xdvance *never = Xdvance_special(A_SPECIAL_NEVER);
+
+      if (Xdvance_list_search(pXdvance->adv_reqs, never)) {
+        /* This Advance is invalid - remove it but ... */
+        Xdvance_list_iterate(game_ruleset->advances, pXdv) {
+          /* .. first, search for advantages which have it as root_req ... */
+          if (Xdvance_list_search(pXdv->adv_roots, pXdvance)) {
+            Xdvance_list_search(pXdv->adv_reqs, never);
+            Xdvance_list_remove(pXdv->adv_roots, pXdvance);
+          }
+        } Xdvance_list_iterate_end;
+        Xdvance_list_iterate(pXdvance->adv_reqs, pXdv) {
+          /* .. second, remove it from its requirements ... */
+          Xdvance_list_remove(pXdv->adv_allows, pXdvance);
+        } Xdvance_list_iterate_end;
+        Xdvance_list_iterate(pXdvance->adv_allows, pXdv) {
+          /* .. third, mark the dependend advances as invalid. */
+          Xdvance_list_append(pXdv->adv_reqs, never);
+        } Xdvance_list_iterate_end;
+
+        /* Last, remove the advance and restart the process. */
+        Xdvance_list_remove(game_ruleset->advances, pXdvance);
+        log_normal("Remove advance %s as it is marked as not reachable.",
+                   rule_name(&pXdvance->name));
+        restart = TRUE;
+        break;
+      }
+    } Xdvance_list_iterate_end;
+  }
+
+  /* Some checks ... */
+  Xdvance_list_iterate(game_ruleset->advances, pXdvance) {
+    /* Check for circular dependencies */
+    Xdvance_list_clear(pXdvance->adv_reqs_all);
+    ruleset_log_ret_val(
+      Xadvance_check_dependency(pXdvance, pXdvance->adv_reqs_all, pXdvance),
+      FALSE, _("Circular dependency detected for advance '%s'."),
+      rule_name(&pXdvance->name));
+
+    /* Check requirements. */
+    Xdvance_list_iterate(pXdvance->adv_reqs, pXdv) {
+      ruleset_log_ret_val(Xdvance_list_search(pXdv->adv_allows, pXdvance),
+                          FALSE, _("Invalid dependency: [%s] => %s."),
+                          rule_name(&pXdv->name),
+                          rule_name(&pXdvance->name));
+    } Xdvance_list_iterate_end;
+
+    /* Check possibilities. */
+    Xdvance_list_iterate(pXdvance->adv_allows, pXdv) {
+      ruleset_log_ret_val(Xdvance_list_search(pXdv->adv_reqs, pXdvance),
+                          FALSE, _("Invalid dependency: %s => [%s]."),
+                          rule_name(&pXdvance->name),
+                          rule_name(&pXdv->name));
+    } Xdvance_list_iterate_end;
+
+    /* Propagate a root tech up into the tech tree. Thus, if a technology
+     * X has Y has a root tech, then any technology requiring X also has
+     * Y as a root tech. */
+    {
+      struct Xdvance_list *Xdv_list = Xdvance_list_new();
+      Xadvance_check_root_reqs(pXdvance, Xdv_list, pXdvance);
+      Xdvance_list_destroy(Xdv_list);
+    }
+  } Xdvance_list_iterate_end;
+
+  /* Add index to each tech. */
+  i = 0;
+  Xdvance_list_iterate(game_ruleset->advances, pXdvance) {
+    pXdvance->atype = A_TYPE_TECH;
+    pXdvance->index = i;
+
+    i++;
+  } Xdvance_list_iterate_end;
+
+#ifdef DEBUG_RULESET
+  /* Debugging information. */
+  Xdvance_list_iterate(game_ruleset->advances, pXdvance) {
+    char buf[256];
+    enum tech_flag_id aflag;
+
+    log_verbose("[%-20s(ID: %2d)] reqs: %d (%d); allows: %d; root: %d",
+                rule_name(&pXdvance->name), Xdvance_to_index(pXdvance),
+                Xdvance_list_size(pXdvance->adv_reqs),
+                Xdvance_list_size(pXdvance->adv_reqs_all),
+                Xdvance_list_size(pXdvance->adv_allows),
+                Xdvance_list_size(pXdvance->adv_roots));
+
+    Xdvance_list_iterate(pXdvance->adv_reqs, pXdv) {
+      log_verbose("[%-20s(ID: %2d)] reqs: %s", rule_name(&pXdvance->name),
+                  Xdvance_to_index(pXdvance), rule_name(&pXdv->name));
+    } Xdvance_list_iterate_end;
+
+    Xdvance_list_iterate(pXdvance->adv_reqs_all, pXdv) {
+      log_verbose("[%-20s(ID: %2d)] reqs_all: %s",
+                  rule_name(&pXdvance->name), Xdvance_to_index(pXdvance),
+                  rule_name(&pXdv->name));
+    } Xdvance_list_iterate_end;
+
+    Xdvance_list_iterate(pXdvance->adv_allows, pXdv) {
+      log_verbose("[%-20s(ID: %2d)] allows: %s", rule_name(&pXdvance->name),
+                 Xdvance_to_index(pXdvance), rule_name(&pXdv->name));
+    } Xdvance_list_iterate_end;
+
+    Xdvance_list_iterate(pXdvance->adv_roots, pXdv) {
+      log_verbose("[%-20s(ID: %2d)] roots: %s", rule_name(&pXdvance->name),
+                  Xdvance_to_index(pXdvance), rule_name(&pXdv->name));
+    } Xdvance_list_iterate_end;
+
+    buf[0] = '\0';
+    for (aflag = tech_flag_id_begin(); aflag != tech_flag_id_end();
+         aflag = tech_flag_id_next(aflag)) {
+      if (BV_ISSET(pXdvance->bv_flags, aflag)) {
+        if (strlen(buf) != 0) {
+          strcat(buf, ", ");
+        }
+        strcat(buf, tech_flag_id_name(aflag));
+      }
+    }
+    if (strlen(buf) > 0) {
+      log_verbose("[%-20s(ID: %2d)] flags: %s", rule_name(&pXdvance->name),
+                  Xdvance_to_index(pXdvance), buf);
+    }
+  } Xdvance_list_iterate_end;
+#endif
+
+  return TRUE;
+}
+
+/**************************************************************************
+  ...
+
+  This function should be called after Xadvance_check_dependency().
+**************************************************************************/
+static void Xadvance_check_root_reqs(struct Xdvance *pXdvance,
+                                     struct Xdvance_list *Xdv_list,
+                                     struct Xdvance *pXdv_now)
+{
+  fc_assert_ret(game_ruleset != NULL);
+
+  Xdvance_list_iterate(pXdv_now->adv_allows, pXdv) {
+    if (!Xdvance_list_search(Xdv_list, pXdv)) {
+      Xdvance_list_iterate(pXdvance->adv_roots, pXdv_root) {
+        if (!Xdvance_list_search(pXdv->adv_roots, pXdv_root)) {
+          Xdvance_list_append(pXdv->adv_roots, pXdv_root);
+        }
+      } Xdvance_list_iterate_end;
+
+      Xdvance_list_append(Xdv_list, pXdv);
+      Xadvance_check_root_reqs(pXdvance, Xdv_list, pXdv);
+    }
+  } Xdvance_list_iterate_end;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static bool Xadvance_check_dependency(struct Xdvance *pXdvance,
+                                      struct Xdvance_list *Xdv_list,
+                                      struct Xdvance *pXdv_now)
+{
+  fc_assert_ret_val(game_ruleset != NULL, FALSE);
+
+  if (Xdvance_list_search(Xdv_list, pXdv_now)) {
+    /* This advance was checked before. */
+    return TRUE;
+  }
+
+  Xdvance_list_append(Xdv_list, pXdv_now);
+
+  Xdvance_list_iterate(pXdv_now->adv_reqs, pXdv) {
+    if (pXdv == pXdvance
+        || !Xadvance_check_dependency(pXdvance, Xdv_list, pXdv)) {
+      /* This advance depends on itself. */
+      return FALSE;
+    }
+  } Xdvance_list_iterate_end;
+
+  return TRUE;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+bool ruleset_validate_improvements(void)
+{
+  return TRUE;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+bool ruleset_validate_governments(void)
+{
+  return TRUE;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+bool ruleset_validate_units(void)
+{
+  return TRUE;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+bool ruleset_validate_terrain(void)
+{
+  return TRUE;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+bool ruleset_validate_cities(void)
+{
+  return TRUE;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+bool ruleset_validate_nations(void)
+{
+  return TRUE;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+bool ruleset_validate_effects(void)
+{
+  return TRUE;
+}
+
+
+/**************************************************************************
+  ...
+**************************************************************************/
+int ruleset_add_req(struct requirement *req)
+{
+  fc_assert_ret_val(game_ruleset != NULL, -1);
+
+  requirement_list_iterate(game_ruleset->reqs, preq) {
+    if (are_requirements_equal(preq, req)) {
+      requirement_destroy(req);
+      return requirement_list_index(game_ruleset->reqs, preq);
+    }
+  } requirement_list_iterate_end;
+
+  requirement_list_append(game_ruleset->reqs, req);
+
+  return requirement_list_size(game_ruleset->reqs) - 1;
+}
diff --git a/common/rulesets.h b/common/rulesets.h
new file mode 100644
index 0000000..ec11ffc
--- /dev/null
+++ b/common/rulesets.h
@@ -0,0 +1,60 @@
+/**********************************************************************
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+   This program 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; either version 2, or (at your option)
+   any later version.
+
+   This program 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.
+***********************************************************************/
+#ifndef FC__RULESETS_H
+#define FC__RULESETS_H
+
+/* utility */
+#include "registry.h"
+#include "shared.h"
+
+/* common */
+#include "connection.h"
+#include "requirements.h"
+
+struct Xdvance_list;
+
+struct ruleset {
+  /* Requirements. */
+  struct requirement_list *reqs;
+
+  /* Advance. */
+  struct Xdvance_list *advances;
+  struct Xdvance_list *advances_special;
+};
+
+extern struct ruleset *game_ruleset;
+
+void ruleset_init(void);
+void ruleset_free(void);
+const char *ruleset_error(void);
+
+char *ruleset_lookup_string(struct section_file *file,
+                            const char *format, ...)
+                            fc__attribute((__format__(__printf__, 2, 3)));
+
+bool ruleset_load(struct section_file *file);
+void ruleset_save(struct section_file *file);
+
+bool ruleset_validate_advances(void);
+bool ruleset_validate_improvements(void);
+bool ruleset_validate_governments(void);
+bool ruleset_validate_units(void);
+bool ruleset_validate_terrain(void);
+bool ruleset_validate_improvements(void);
+bool ruleset_validate_cities(void);
+bool ruleset_validate_nations(void);
+bool ruleset_validate_effects(void);
+
+int ruleset_add_req(struct requirement *req);
+
+#endif  /* FC__RULESETS_H */
diff --git a/common/tech.h b/common/tech.h
index 089c2b9..46d7c31 100644
--- a/common/tech.h
+++ b/common/tech.h
@@ -64,17 +64,24 @@ typedef int Tech_type_id;
 
 /* Changing these breaks network compatibility. */
 /* If a new flag is added techtools.c:player_tech_lost() should be checked */
-enum tech_flag_id {
-  TF_BONUS_TECH, /* player gets extra tech if rearched first */
-  TF_BRIDGE,    /* "Settler" unit types can build bridges over rivers */
-  TF_RAILROAD,  /* "Settler" unit types can build rail roads */
-  TF_POPULATION_POLLUTION_INC,  /* Increase the pollution factor created by population by one */
-  TF_FARMLAND,  /* "Settler" unit types can build farmland */
-  TF_BUILD_AIRBORNE, /* Player can build air units */
-  TF_LAST
-};
-
-/* TECH_KNOWN is self-explanatory, TECH_PREREQS_KNOWN are those for which all 
+#define SPECENUM_NAME tech_flag_id
+/* player gets extra tech if rearched first */
+#define SPECENUM_VALUE0 TF_BONUS_TECH
+/* "Settler" unit types can build bridges over rivers */
+#define SPECENUM_VALUE1 TF_BRIDGE
+/* "Settler" unit types can build rail roads */
+#define SPECENUM_VALUE2 TF_RAILROAD
+/* Increase the pollution factor created by population by one */
+#define SPECENUM_VALUE3 TF_POPULATION_POLLUTION_INC
+/* "Settler" unit types can build farmland */
+#define SPECENUM_VALUE4 TF_FARMLAND
+/* Player can build air units */
+#define SPECENUM_VALUE5 TF_BUILD_AIRBORNE
+/* Keep this last. */
+#define SPECENUM_VALUE6 TF_LAST
+#include "specenum_gen.h"
+
+/* TECH_KNOWN is self-explana6ory, TECH_PREREQS_KNOWN are those for which all 
  * requirements are fulfilled; all others (including those which can never 
  * be reached) are TECH_UNKNOWN */
 enum tech_state {
diff --git a/common/temp.c b/common/temp.c
new file mode 100644
index 0000000..966fc7a
--- /dev/null
+++ b/common/temp.c
@@ -0,0 +1,378 @@
+/**********************************************************************
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+   This program 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; either version 2, or (at your option)
+   any later version.
+
+   This program 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.
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* utility */
+#include "shared.h" /* bool */
+
+/* common */
+#include "improvement.h"
+#include "requirements.h"
+#include "rulesets.h"
+
+#include "temp.h"
+
+/* == see common/requirements.(c|h)======================================= */
+
+/**************************************************************************
+  ...
+**************************************************************************/
+struct requirement *requirement_new(const char *type, const char *value,
+                                    const char *range, bool survives,
+                                    bool negated)
+{
+  struct requirement *req = fc_calloc(1, sizeof(*req));
+  bool invalid = TRUE;
+
+  req->source = universal_by_rule_name(type, value);
+
+  /* Scan the range string to find the range. If no range is given a
+   * default fallback is used rather than giving an error. */
+  req->range = req_range_from_str(range);
+  if (req->range == REQ_RANGE_LAST) {
+    switch (req->source.kind) {
+    case VUT_NONE:
+    case VUT_LAST:
+      break;
+    case VUT_IMPROVEMENT:
+    case VUT_SPECIAL:
+    case VUT_TERRAIN:
+    case VUT_UTYPE:
+    case VUT_UTFLAG:
+    case VUT_UCLASS:
+    case VUT_UCFLAG:
+    case VUT_OTYPE:
+    case VUT_SPECIALIST:
+    case VUT_TERRAINCLASS:
+    case VUT_BASE:
+    case VUT_TERRAINALTER:
+    case VUT_CITYTILE:
+      req->range = REQ_RANGE_LOCAL;
+      break;
+    case VUT_MINSIZE:
+      req->range = REQ_RANGE_CITY;
+      break;
+    case VUT_GOVERNMENT:
+    case VUT_ADVANCE:
+    case VUT_NATION:
+    case VUT_AI_LEVEL:
+      req->range = REQ_RANGE_PLAYER;
+      break;
+    case VUT_MINYEAR:
+      req->range = REQ_RANGE_WORLD;
+      break;
+    }
+  }
+
+  req->survives = survives;
+  req->negated = negated;
+
+  /* These checks match what combinations are supported inside
+   * is_req_active(). */
+  switch (req->source.kind) {
+  case VUT_SPECIAL:
+  case VUT_TERRAIN:
+  case VUT_TERRAINCLASS:
+  case VUT_BASE:
+    invalid = (req->range != REQ_RANGE_LOCAL
+               && req->range != REQ_RANGE_ADJACENT);
+    break;
+  case VUT_ADVANCE:
+    invalid = (req->range < REQ_RANGE_PLAYER);
+    break;
+  case VUT_GOVERNMENT:
+  case VUT_AI_LEVEL:
+    invalid = (req->range != REQ_RANGE_PLAYER);
+    break;
+  case VUT_IMPROVEMENT:
+    invalid = ((req->range == REQ_RANGE_WORLD
+                && !is_great_wonder(req->source.value.building))
+               || (req->range > REQ_RANGE_CITY
+                   && !is_wonder(req->source.value.building)));
+    break;
+  case VUT_MINSIZE:
+    invalid = (req->range != REQ_RANGE_CITY);
+    break;
+  case VUT_NATION:
+    invalid = (req->range != REQ_RANGE_PLAYER
+               && req->range != REQ_RANGE_WORLD);
+    break;
+  case VUT_UTYPE:
+  case VUT_UTFLAG:
+  case VUT_UCLASS:
+  case VUT_UCFLAG:
+  case VUT_OTYPE:
+  case VUT_SPECIALIST:
+  case VUT_TERRAINALTER: /* XXX could in principle support ADJACENT */
+  case VUT_CITYTILE:
+    invalid = (req->range != REQ_RANGE_LOCAL);
+    break;
+  case VUT_MINYEAR:
+    invalid = (req->range != REQ_RANGE_WORLD);
+    break;
+  case VUT_NONE:
+    invalid = FALSE;
+    break;
+  case VUT_LAST:
+    break;
+  }
+
+  if (invalid) {
+    log_error("Invalid requirement %s | %s | %s | %s | %s",
+              type, range, survives ? "survives" : "",
+              negated ? "negated" : "", value);
+    req->source.kind = VUT_LAST;
+  }
+
+  return req;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+void requirement_destroy(struct requirement *req)
+{
+  if (req != NULL) {
+    free(req);
+  }
+}
+
+/* == see common/requirements.(c|h)======================================= */
+
+
+/* == see common/tech.(c|h) ============================================== */
+
+#define TOKEN_SIZE 10
+
+/**************************************************************************
+  ...
+**************************************************************************/
+struct Xdvance *Xdvance_new(void)
+{
+  struct Xdvance *pXdvance = fc_calloc(1, sizeof(*pXdvance));
+
+  pXdvance->atype = A_TYPE_UNSET;
+  /* for A_TYPE_UNSET no value for the enum 'aspecial'/'index' is set. */
+  pXdvance->graphic_str[0] = '\0';
+  pXdvance->graphic_alt[0] = '\0';
+  pXdvance->helptext = NULL;
+  pXdvance->bonus_message = NULL;
+
+  pXdvance->adv_reqs = Xdvance_list_new();
+  pXdvance->adv_reqs_all = Xdvance_list_new();
+  pXdvance->adv_allows = Xdvance_list_new();
+  pXdvance->adv_roots = Xdvance_list_new();
+
+  BV_CLR_ALL(pXdvance->bv_flags);
+  pXdvance->preset_cost = -1;
+
+  pXdvance->sec_name = NULL;
+
+  return pXdvance;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+void Xdvance_destroy(struct Xdvance *pXdvance)
+{
+  if (pXdvance == NULL) {
+    return;
+  }
+
+  if (pXdvance->helptext != NULL) {
+    free(pXdvance->helptext);
+  }
+  if (pXdvance->bonus_message != NULL) {
+    free(pXdvance->bonus_message);
+  }
+
+  Xdvance_list_iterate(pXdvance->adv_reqs, pXdv) {
+    Xdvance_list_remove(pXdvance->adv_reqs, pXdv);
+  } Xdvance_list_iterate_end;
+  Xdvance_list_destroy(pXdvance->adv_reqs);
+
+  Xdvance_list_iterate(pXdvance->adv_reqs_all, pXdv) {
+    Xdvance_list_remove(pXdvance->adv_reqs_all, pXdv);
+  } Xdvance_list_iterate_end;
+  Xdvance_list_destroy(pXdvance->adv_reqs_all);
+
+  Xdvance_list_iterate(pXdvance->adv_allows, pXdv) {
+    Xdvance_list_remove(pXdvance->adv_allows, pXdv);
+  } Xdvance_list_iterate_end;
+  Xdvance_list_destroy(pXdvance->adv_allows);
+
+  Xdvance_list_iterate(pXdvance->adv_roots, pXdv) {
+    Xdvance_list_remove(pXdvance->adv_roots, pXdv);
+  } Xdvance_list_iterate_end;
+  Xdvance_list_destroy(pXdvance->adv_roots);
+
+  free(pXdvance);
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+char *Xdvance_list_to_str(const struct Xdvance_list *pXdv_list)
+{
+  static char buf[256];
+  char token[TOKEN_SIZE];
+  bool first = TRUE;
+
+  fc_assert_ret_val(pXdv_list != NULL, NULL);
+
+  buf[0] = '\0';
+  Xdvance_list_iterate(pXdv_list, pXdvance) {
+    if (!first) {
+      strcat(buf, ",");
+    }
+
+    fc_snprintf(token, sizeof(token), "%d", Xdvance_to_index(pXdvance));
+    strcat(buf, token);
+
+    first = FALSE;
+  } Xdvance_list_iterate_end;
+
+  return buf;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+struct Xdvance_list *Xdvance_str_to_list(const char *str)
+{
+  struct Xdvance_list *pXdv_list = Xdvance_list_new();
+  const char *ptr = str;
+  int i, index;
+
+  if (strlen(str) == 0) {
+    return pXdv_list;
+  }
+
+  for (i = 0; i < strlen(str); i++) {
+    char token[TOKEN_SIZE];
+    struct Xdvance *pXdvance;
+
+    scanin(&ptr, ",", token, sizeof(token));
+    if (token[0] == '\0' || sscanf(token, "%d", &index) != 1) {
+      break;
+    }
+
+    pXdvance = Xdvance_by_index(index);
+    if (pXdvance != NULL) {
+      Xdvance_list_append(pXdv_list, pXdvance);
+    } else {
+      log_error("Invalid advance: '%s'.", str);
+    }
+  }
+
+  return pXdv_list;
+}
+
+/**************************************************************************
+  Returns if the advance is equal to the special advance 'aspecial'.
+  If 'aspecial' == A_SPECIAL_LAST any special advance returns TRUE.
+**************************************************************************/
+bool Xdvance_check_special(struct Xdvance *pXdvance,
+                           enum advance_special aspecial)
+{
+  if (pXdvance->atype == A_TYPE_SPECIAL
+      && (aspecial == A_SPECIAL_ALL || pXdvance->aspecial == aspecial)) {
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+struct Xdvance *Xdvance_special(enum advance_special aspecial)
+{
+  fc_assert_ret_val(game_ruleset != NULL, NULL);
+
+  return Xdvance_list_get(game_ruleset->advances_special, aspecial);
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+int Xdvance_to_index(struct Xdvance *pXdvance)
+{
+  fc_assert_ret_val(game_ruleset != NULL, -1);
+
+  switch (pXdvance->atype) {
+  case A_TYPE_SPECIAL:
+    /* It is a 'special' advance. */
+    return -1;
+  case A_TYPE_TECH:
+    fc_assert_ret_val(Xdvance_list_index(game_ruleset->advances, pXdvance)
+                      == pXdvance->index, A_NONE);
+    return pXdvance->index;
+  case A_TYPE_UNSET:
+    return Xdvance_list_index(game_ruleset->advances, pXdvance);
+  }
+
+  /* Should never happen. */
+  fc_assert_ret_val(pXdvance->atype != A_TYPE_SPECIAL
+                    && pXdvance->atype != A_TYPE_TECH
+                    && pXdvance->atype != A_TYPE_UNSET, -1);
+  return -1;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+struct Xdvance *Xdvance_by_index(int index)
+{
+  fc_assert_ret_val(game_ruleset != NULL, NULL);
+
+  fc_assert_ret_val(Xdvance_list_get(game_ruleset->advances, index)->index
+                    == index, NULL);
+
+  return Xdvance_list_get(game_ruleset->advances, index);
+}
+
+/**************************************************************************
+  Does a linear search of advances[].name.vernacular
+  Returns NULL when none match.
+**************************************************************************/
+struct Xdvance *find_Xdvance_by_rule_name(const char *name)
+{
+  const char *qname = Qn_(name);
+
+  fc_assert_ret_val(ruleset_load != NULL, NULL);
+
+  Xdvance_list_iterate(game_ruleset->advances, pXdvance) {
+    if (0 == fc_strcasecmp(Xdvance_rule_name(pXdvance), qname)) {
+      return pXdvance;
+    }
+  } Xdvance_list_iterate_end;
+
+  return NULL;
+}
+
+/****************************************************************************
+  Return the (untranslated) rule name of the advance/technology.
+  You don't have to free the return pointer.
+****************************************************************************/
+const char *Xdvance_rule_name(const struct Xdvance *pXdvance)
+{
+  return rule_name(&pXdvance->name);
+}
+
+
+/* == see common/tech.(c|h) ============================================== */
diff --git a/common/temp.h b/common/temp.h
new file mode 100644
index 0000000..bb57066
--- /dev/null
+++ b/common/temp.h
@@ -0,0 +1,118 @@
+/**********************************************************************
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+   This program 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; either version 2, or (at your option)
+   any later version.
+
+   This program 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.
+***********************************************************************/
+#ifndef FC__TEMP_H
+#define FC__TEMP_H
+
+/* utility */
+#include "registry.h"
+#include "shared.h"
+
+/* common */
+#include "connection.h"
+#include "requirements.h"
+
+/* == see common/requirements.(c|h)======================================= */
+
+struct requirement *requirement_new(const char *type, const char *name,
+                                    const char *range, bool survives,
+                                    bool negated);
+void requirement_destroy(struct requirement *req);
+
+/* == see common/requirements.(c|h)======================================= */
+
+/* == see common/tech.c ================================================== */
+
+#define SPECENUM_NAME advance_special
+#define SPECENUM_VALUE0 A_SPECIAL_NONE
+#define SPECENUM_VALUE1 A_SPECIAL_UNSET
+#define SPECENUM_VALUE2 A_SPECIAL_UNKNOWN
+#define SPECENUM_VALUE3 A_SPECIAL_FUTURE
+#define SPECENUM_VALUE4 A_SPECIAL_NEVER
+#define SPECENUM_VALUE5 A_SPECIAL_LAST
+#include "specenum_gen.h"
+
+#define A_SPECIAL_ALL A_SPECIAL_LAST
+
+#define SPECENUM_NAME advance_type
+#define SPECENUM_VALUE0 A_TYPE_SPECIAL
+#define SPECENUM_VALUE1 A_TYPE_TECH
+#define SPECENUM_VALUE2 A_TYPE_UNSET
+#include "specenum_gen.h"
+
+struct Xdvance {
+  /* The following values are valid for 'atype':
+   * - A_TYPE_SPECIAL: it is a special advance (see advance_special); in the
+   *   union 'aspecial' is used.
+   * - A_TYPE_TECH: a real advance; the index of teh advance in the main list
+   *   (game_ruleset->advances) is saved in 'index'.
+   * - A_TYPE_UNSET: used while loading the ruleset; at this time, the order
+   *   of the advances is not fixed and therefore 'index' can't be used. */
+  enum advance_type atype;
+  union {
+    enum advance_special aspecial;
+    int index;
+  };
+
+  struct name_translation name;
+  char graphic_str[MAX_LEN_NAME]; /* which named sprite to use */
+  char graphic_alt[MAX_LEN_NAME]; /* alternate icon name */
+  char *helptext;
+  /* Message displayed to the first player to get a bonus tech */
+  char *bonus_message;
+
+  struct Xdvance_list *adv_reqs;     /* Required advances. */
+  struct Xdvance_list *adv_reqs_all; /* All required advances including this
+                                      * one. */
+  struct Xdvance_list *adv_allows;   /* List of advances which need this one
+                                      * as requirement. */
+  struct Xdvance_list *adv_roots;    /* Root advances needed. */
+
+  bv_tech_flag_id bv_flags;
+
+  /* Cost of advance in bulbs as specified in ruleset. -1 means that
+   * no value was set in ruleset. Server send this to client. */
+  int preset_cost;
+
+  /* Used only while loading the rulesetdir. */
+  const char *sec_name;
+};
+
+/* Define 'struct Xdvances_list' and related functions: */
+#define SPECLIST_TAG Xdvance
+#define SPECLIST_TYPE struct Xdvance
+#include "speclist.h"
+
+/* Iterator for advances. */
+#define Xdvance_list_iterate(Xdvance_list, pXdvance)                       \
+  TYPED_LIST_ITERATE(struct Xdvance, Xdvance_list, pXdvance)
+#define Xdvance_list_iterate_end                                            \
+  LIST_ITERATE_END
+
+char *Xdvance_list_to_str(const struct Xdvance_list *pXdv_list);
+struct Xdvance_list *Xdvance_str_to_list(const char *str);
+
+struct Xdvance *Xdvance_new(void);
+void Xdvance_destroy(struct Xdvance *pXdvance);
+
+bool Xdvance_check_special(struct Xdvance *pXdvance,
+                           enum advance_special aspecial);
+struct Xdvance *Xdvance_special(enum advance_special aspecial);
+
+int Xdvance_to_index(struct Xdvance *pXdvance);
+struct Xdvance *Xdvance_by_index(int index);
+struct Xdvance *find_Xdvance_by_rule_name(const char *name);
+const char *Xdvance_rule_name(const struct Xdvance *pXdvance);
+
+/* == see common/tech.c ================================================== */
+
+#endif  /* FC__TEMP_H */
diff --git a/server/Makefile.am b/server/Makefile.am
index 4a4db9f..ec30311 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -65,6 +65,8 @@ libfreeciv_srv_la_SOURCES = \
 		report.h	\
 		ruleset.c	\
 		ruleset.h	\
+		rulesetdir.c	\
+		rulesetdir.h	\
 		sanitycheck.c	\
 		sanitycheck.h	\
 		savegame.c	\
diff --git a/server/ruleset.c b/server/ruleset.c
index 8e5c6cf..f5688a7 100644
--- a/server/ruleset.c
+++ b/server/ruleset.c
@@ -48,6 +48,7 @@
 /* server */
 #include "citytools.h"
 #include "plrhand.h"
+#include "rulesetdir.h"
 #include "script.h"
 #include "settings.h"
 
@@ -3954,6 +3955,10 @@ void load_rulesets(void)
   struct section_file *techfile, *unitfile, *buildfile, *govfile, *terrfile;
   struct section_file *cityfile, *nationfile, *effectfile;
 
+log_error("-----");
+    ruleset_test();
+log_error("-----");
+
   log_normal(_("Loading rulesets"));
 
   game_ruleset_free();
@@ -4271,7 +4276,7 @@ static bool effect_list_sanity_cb(const struct effect *peffect)
                       *       -1 disables checking */
 
   return sanity_check_req_nreq_list(peffect->reqs, peffect->nreqs, one_tile,
-                                    effect_type_name(peffect->type));
+                                    effect_type_ruleset_name(peffect->type));
 }
 
 /**************************************************************************
diff --git a/server/rulesetdir.c b/server/rulesetdir.c
new file mode 100644
index 0000000..45b3d75
--- /dev/null
+++ b/server/rulesetdir.c
@@ -0,0 +1,796 @@
+/**********************************************************************
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+   This program 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; either version 2, or (at your option)
+   any later version.
+
+   This program 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.
+***********************************************************************/
+
+/**********************************************************************
+  TODO:
+    - change ruleset to use requirements for techs (as for improvements)
+    - remove game.control.num_tech_types
+      use advance_list_size(game_ruleset.advances)
+    - remove game.control.num_impr_types
+      use improvement_list_size(game_ruleset.improvements)
+    - cleanup logging / error reporting (do not exit but return without
+      loading the ruleset)
+      * exit only if error loading the first ruleset!
+***********************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* utility */
+#include "fcintl.h"
+#include "log.h"
+#include "mem.h"
+#include "registry.h"
+#include "shared.h"
+#include "support.h"
+
+/* common */
+#include "capability.h"
+#include "game.h"
+#include "rulesets.h"
+#include "temp.h"
+
+/* server */
+
+/* ai */
+
+#include "rulesetdir.h"
+
+#define RS_SUFFIX "ruleset"
+
+#define RS_SECTION_PREFIX_ADVANCE "advance_"
+
+static const char *rulesetdir_check_capabilities(struct section_file *file,
+                                                 const char *us_capstr,
+                                                 const char *filename);
+static const char *rulesetdir_valid_filename(const char *subdir,
+                                             const char *name,
+                                             const char *extension);
+static struct section_file *rulesetdir_openload_file(const char *rulesetdir,
+                                                     const char *whichset);
+
+static bool rulesetdir_load_advances(const char *rulesetdir);
+static bool rulesetdir_load_improvements(const char *rulesetdir);
+static bool rulesetdir_load_governments(const char *rulesetdir);
+static bool rulesetdir_load_units(const char *rulesetdir);
+static bool rulesetdir_load_terrain(const char *rulesetdir);
+static bool rulesetdir_load_cities(const char *rulesetdir);
+static bool rulesetdir_load_nations(const char *rulesetdir);
+static bool rulesetdir_load_effects(const char *rulesetdir);
+
+static struct Xdvance *lookup_tech(struct section_file *file,
+                                   const char *prefix, const char *entry,
+                                   int loglevel, const char *filename,
+                                   const char *description);
+
+static void rulesetdir_error_real(const char *file, const char *function,
+                                  int line, enum log_level level,
+                                  const char *format, ...)
+    fc__attribute((__format__ (__printf__, 5, 6)));
+#define rulesetdir_error(level, format, ...)                                \
+  if (log_do_output_for_level(level)) {                                     \
+    rulesetdir_error_real(__FILE__, __FUNCTION__, __LINE__,                 \
+                          level, format, ## __VA_ARGS__);                   \
+  }
+
+/**************************************************************************
+  Test the ruleset.
+**************************************************************************/
+#define FILE_RULES1 "./test1.rules"
+#define FILE_RULES2 "./test2.rules"
+void ruleset_test(void)
+{
+  struct section_file *sf = NULL;
+
+  ruleset_init();
+
+  /* [1] load ruleset */
+  log_normal("[%s:%d] rulesetdir_load (1)", __FILE__, __LINE__);
+  if (!rulesetdir_load(game.server.rulesetdir)) {
+    log_error("ERROR")
+    goto CLEANUP;
+  }
+
+  /* [2] save parsed ruleset */
+  log_normal("[%s:%d] ruleset_save (2)", __FILE__, __LINE__);
+  sf = secfile_new(FALSE);
+  ruleset_save(sf);
+  log_normal("[%s:%d] secfile_save (2)", __FILE__, __LINE__);
+  if (!secfile_save(sf, FILE_RULES1, 0, FZ_PLAIN)) {
+    log_error("ERROR: %s", secfile_error());
+    goto CLEANUP;
+  }
+  secfile_destroy(sf);
+  ruleset_free();
+
+  /* [3] load parsed ruleset */
+  ruleset_init();
+  log_normal("[%s:%d] secfile_load (3)", __FILE__, __LINE__);
+  sf = secfile_load(FILE_RULES1, FALSE);
+  if (sf == NULL) {
+    log_error("ERROR: %s", secfile_error());
+    goto CLEANUP;
+  }
+  log_normal("[%s:%d] ruleset_load (3)", __FILE__, __LINE__);
+  if (!ruleset_load(sf)) {
+    log_error("ERROR: %s", ruleset_error())
+    goto CLEANUP;
+  }
+  secfile_destroy(sf);
+
+  /* [4] save parsed ruleset */
+  log_normal("[%s:%d] ruleset_save (4)", __FILE__, __LINE__);
+  sf = secfile_new(FALSE);
+  ruleset_save(sf);
+  log_normal("[%s:%d] secfile_save (4)", __FILE__, __LINE__);
+  if (!secfile_save(sf, FILE_RULES2, 0, FZ_PLAIN)) {
+    log_error("ERROR: %s", secfile_error());
+    goto CLEANUP;
+  }
+
+ CLEANUP:
+  if (sf != NULL) {
+    secfile_destroy(sf);
+  }
+  ruleset_free();
+}
+
+/**************************************************************************
+  Load a ruleset defined by the files in 'rulesetdir'.
+**************************************************************************/
+bool rulesetdir_load(const char *rulesetdir)
+{
+  if (!rulesetdir_load_advances(rulesetdir)
+      || !rulesetdir_load_improvements(rulesetdir)
+      || !rulesetdir_load_governments(rulesetdir)
+      || !rulesetdir_load_units(rulesetdir)
+      || !rulesetdir_load_terrain(rulesetdir)
+      || !rulesetdir_load_cities(rulesetdir)
+      || !rulesetdir_load_nations(rulesetdir)
+      || !rulesetdir_load_effects(rulesetdir)) {
+    /* Error messages are output by the functions. */
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/**************************************************************************
+  Restore ruleset settings.
+**************************************************************************/
+void rulesets_settings_restore(void)
+{
+}
+
+/**************************************************************************
+  Send the ruleset to the clint(s).
+**************************************************************************/
+void rulesets_send(struct conn_list *dest)
+{
+}
+
+/**************************************************************************
+ Lookup a string prefix.entry in the file and return the corresponding
+ advances pointer. If (!required), return A_NEVER for match "Never" or
+ can't match. If (required), die when can't match. Note the first tech
+ should have name "None" so that will always match.
+ If description is not NULL, it is used in the warning message
+ instead of prefix (eg pass unit->name instead of prefix="units2.u27")
+
+  FIXME: This function needs a new comment. Furthermore the logging
+         chould be made flexible.
+**************************************************************************/
+static struct Xdvance *lookup_tech(struct section_file *file,
+                                   const char *prefix, const char *entry,
+                                   int loglevel, const char *filename,
+                                   const char *description)
+{
+  const char *sval;
+  struct Xdvance *pXdvance;
+
+  sval = secfile_lookup_str_default(file, NULL, "%s.%s", prefix, entry);
+  if (!sval || (LOG_FATAL < loglevel && strcmp(sval, "Never") == 0)) {
+    pXdvance = Xdvance_special(A_SPECIAL_NEVER);
+  } else if (strcmp(sval, "None") == 0) {
+    pXdvance = Xdvance_special(A_SPECIAL_NONE);
+  } else {
+    pXdvance = find_Xdvance_by_rule_name(sval);
+
+    if (pXdvance == NULL) {
+      rulesetdir_error(loglevel,
+                       "\"%s\" %s %s: couldn't match \"%s\".",
+                       filename, (description ? description : prefix),
+                       entry, sval);
+
+      /* ruleset_error returned only if error was not fatal. */
+      pXdvance = Xdvance_special(A_SPECIAL_NEVER);
+    }
+  }
+
+  return pXdvance;
+}
+
+/**************************************************************************
+  Notifications about ruleset errors to clients. Especially important in
+  case of internal server crashing.
+**************************************************************************/
+static void rulesetdir_error_real(const char *file, const char *function,
+                                  int line, enum log_level level,
+                                  const char *format, ...)
+{
+  va_list args;
+
+  va_start(args, format);
+  vdo_log(file, function, line, FALSE, level, format, args);
+  va_end(args);
+
+  if (LOG_FATAL >= level) {
+    exit(EXIT_FAILURE);
+  }
+}
+
+/**************************************************************************
+  Ruleset files should have a capabilities string datafile.options
+  This gets and returns that string, and checks that the required
+  capabilities specified are satisified.
+**************************************************************************/
+static const char *rulesetdir_check_capabilities(struct section_file *file,
+                                                 const char *us_capstr,
+                                                 const char *filename)
+{
+  const char *datafile_options;
+
+  if (!(datafile_options = secfile_lookup_str(file, "datafile.options"))) {
+    log_fatal("\"%s\": ruleset capability problem:", filename);
+    rulesetdir_error(LOG_FATAL, "%s", secfile_error());
+  }
+  if (!has_capabilities(us_capstr, datafile_options)) {
+    log_fatal("\"%s\": ruleset datafile appears incompatible:", filename);
+    log_fatal("  datafile options: %s", datafile_options);
+    log_fatal("  supported options: %s", us_capstr);
+    rulesetdir_error(LOG_FATAL, "Capability problem");
+  }
+  if (!has_capabilities(datafile_options, us_capstr)) {
+    log_fatal("\"%s\": ruleset datafile claims required option(s)"
+              " that we don't support:", filename);
+    log_fatal("  datafile options: %s", datafile_options);
+    log_fatal("  supported options: %s", us_capstr);
+    rulesetdir_error(LOG_FATAL, "Capability problem");
+  }
+  return datafile_options;
+}
+
+/**************************************************************************
+  datafilename() wrapper: tries to match in two ways.
+  Returns NULL on failure, the (statically allocated) filename on success.
+**************************************************************************/
+static const char *rulesetdir_valid_filename(const char *subdir,
+                                             const char *name,
+                                             const char *extension)
+{
+  char filename[512];
+  const char *dfilename;
+
+  fc_assert_ret_val(subdir && name && extension, NULL);
+
+  fc_snprintf(filename, sizeof(filename), "%s/%s.%s",
+              subdir, name, extension);
+  log_verbose("Trying \"%s\".", filename);
+  dfilename = fileinfoname(get_data_dirs(), filename);
+  if (dfilename) {
+    return dfilename;
+  }
+
+  fc_snprintf(filename, sizeof(filename), "default/%s.%s", name, extension);
+  log_verbose("Trying \"%s\": default ruleset directory.", filename);
+  dfilename = fileinfoname(get_data_dirs(), filename);
+  if (dfilename) {
+    return dfilename;
+  }
+
+  fc_snprintf(filename, sizeof(filename), "%s_%s.%s",
+              subdir, name, extension);
+  log_verbose("Trying \"%s\": alternative ruleset filename syntax.",
+              filename);
+  dfilename = fileinfoname(get_data_dirs(), filename);
+  if (dfilename) {
+    return dfilename;
+  } else {
+    rulesetdir_error(LOG_FATAL,
+                     /* TRANS: message about an installation error. */
+                     _("Could not find a readable \"%s.%s\" ruleset file."),
+                     name, extension);
+  }
+
+  return(NULL);
+}
+
+/**************************************************************************
+  Do initial section_file_load on a ruleset file.
+  "whichset" = "techs", "units", "buildings", "terrain", ...
+**************************************************************************/
+static struct section_file *rulesetdir_openload_file(const char *rulesetdir,
+                                                     const char *whichset)
+{
+  char sfilename[512];
+  const char *dfilename
+    = rulesetdir_valid_filename(rulesetdir, whichset, RS_SUFFIX);
+  struct section_file *secfile;
+
+  /* Need to save a copy of the filename for following message, since
+     section_file_load() may call datafilename() for includes. */
+  sz_strlcpy(sfilename, dfilename);
+
+  if (!(secfile = secfile_load(sfilename, FALSE))) {
+    rulesetdir_error(LOG_FATAL, "Could not load ruleset '%s':\n%s",
+                     sfilename, secfile_error());
+  }
+  return secfile;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static bool rulesetdir_load_advances(const char *rulesetdir)
+{
+  struct section_file *file;
+  struct section_list *sec;
+  const char *filename;
+  int i, num_techs;
+  bool success = TRUE;
+
+  fc_assert_ret_val(game_ruleset != NULL, FALSE);
+
+  /* Load all advances.*/
+  file = rulesetdir_openload_file(rulesetdir, "techs");
+  filename = secfile_name(file);
+
+  /* Prevent log message due to unused entry. */
+  (void) secfile_entry_by_path(file, "datafile.description");
+
+  (void) rulesetdir_check_capabilities(file, "+1.9", filename);
+
+  /* Load the section names. */
+  sec = secfile_sections_by_name_prefix(file, RS_SECTION_PREFIX_ADVANCE);
+  if (NULL == sec || 0 == (num_techs = section_list_size(sec))) {
+    rulesetdir_error(LOG_ERROR, "\"%s\": No Advances?!?", filename);
+    success = FALSE;
+    goto CLEANUP;
+  }
+  log_verbose("%d advances (including possibly unused)", num_techs);
+
+  /* Load basic data. */
+  for (i = 0; i < num_techs; i++) {
+    struct Xdvance *pXdvance = Xdvance_new();
+    const char *sec_name = section_name(section_list_get(sec, i));
+    const char *sval, **slist, *name;
+    size_t nval;
+    int j, ival;
+
+    /* 'atype'/'index' will be set by ruleset_validate_advances(). */
+    name = secfile_lookup_str(file, "%s.name", sec_name);
+    if (!name) {
+      rulesetdir_error(LOG_ERROR, "\"%s\": No name in section '%s'. (%s)",
+                       filename, sec_name, secfile_error());
+      Xdvance_destroy(pXdvance);
+      continue;
+    }
+    name_set(&pXdvance->name, name);
+
+    sz_strlcpy(pXdvance->graphic_str,
+               secfile_lookup_str_default(file, "-",
+                                          "%s.graphic", sec_name));
+    sz_strlcpy(pXdvance->graphic_alt,
+               secfile_lookup_str_default(file, "-",
+                                          "%s.graphic_alt", sec_name));
+    pXdvance->helptext = ruleset_lookup_string(file, sec_name, "helptext");
+    pXdvance->bonus_message
+      = ruleset_lookup_string(file, sec_name, "bonus_message");
+
+    /* Advance flags. */
+    slist = secfile_lookup_str_vec(file, &nval, "%s.flags", sec_name);
+    for(j = 0; j < nval; j++) {
+      sval = slist[j];
+      if(strcmp(sval, "") == 0) {
+        continue;
+      }
+      ival = find_advance_flag_by_rule_name(sval);
+      if (ival == TF_LAST) {
+        log_error("\"%s\" [%-25s] \"%s\": bad advance flag name \"%s\".",
+                  filename, sec_name, name, sval);
+      } else {
+        BV_SET(pXdvance->bv_flags, ival);
+      }
+    }
+    free(slist);
+
+    pXdvance->preset_cost
+      = secfile_lookup_int_default(file, -1, "%s.%s", sec_name, "cost");
+
+    /* Needed in the iterator below. */
+    pXdvance->sec_name = sec_name;
+
+    Xdvance_list_append(game_ruleset->advances, pXdvance);
+  }
+
+  /* Load requirements. */
+  Xdvance_list_iterate(game_ruleset->advances, pXdvance) {
+    struct Xdvance *pXdv, *never = Xdvance_special(A_SPECIAL_NEVER);
+
+    pXdv = lookup_tech(file, pXdvance->sec_name, "req1", LOG_ERROR,
+                       filename, rule_name(&pXdvance->name));
+    if (!Xdvance_check_special(pXdv, A_SPECIAL_ALL)) {
+      if (!Xdvance_list_search(pXdvance->adv_reqs, pXdv)) {
+        Xdvance_list_append(pXdvance->adv_reqs, pXdv);
+      }
+      if (!Xdvance_list_search(pXdv->adv_allows, pXdvance)) {
+        Xdvance_list_append(pXdv->adv_allows, pXdvance);
+      }
+    } else if (pXdv == never
+               && !Xdvance_list_search(pXdvance->adv_reqs, pXdv)) {
+      /* This advance is not valid in the ruleset. */
+      Xdvance_list_append(pXdvance->adv_reqs, pXdv);
+    } /* A_SPECIAL_NONE is not considered as it has _no_ influence. */
+
+    pXdv = lookup_tech(file, pXdvance->sec_name, "req2", LOG_ERROR,
+                       filename, rule_name(&pXdvance->name));
+    if (!Xdvance_check_special(pXdv, A_SPECIAL_ALL)) {
+      if (!Xdvance_list_search(pXdvance->adv_reqs, pXdv)) {
+        Xdvance_list_append(pXdvance->adv_reqs, pXdv);
+      }
+      if (!Xdvance_list_search(pXdv->adv_allows, pXdvance)) {
+        Xdvance_list_append(pXdv->adv_allows, pXdvance);
+      }
+    } else if (pXdv == never
+               && !Xdvance_list_search(pXdvance->adv_reqs, pXdv)) {
+      /* This advance is not valid in the ruleset. */
+      Xdvance_list_append(pXdvance->adv_reqs, pXdv);
+    } /* A_SPECIAL_NONE is not considered as it has _no_ influence. */
+
+    pXdv = lookup_tech(file, pXdvance->sec_name, "root_req", LOG_ERROR,
+                       filename, rule_name(&pXdvance->name));
+    if (!Xdvance_check_special(pXdv, A_SPECIAL_ALL)) {
+      Xdvance_list_append(pXdvance->adv_roots, pXdv);
+    }
+
+    /* Not needed anymore. Set it to NULL so that there are no invalid
+     * pointers after the secfile is freed. */
+    pXdvance->sec_name = NULL;
+  } Xdvance_list_iterate_end;
+
+  if (!ruleset_validate_advances()) {
+    rulesetdir_error(LOG_ERROR, "\"%s\": %s", filename, ruleset_error());
+    success = FALSE;
+    goto CLEANUP;
+  }
+
+ CLEANUP:
+  if (sec) {
+    section_list_destroy(sec);
+  }
+  if (file) {
+    secfile_check_unused(file);
+    secfile_destroy(file);
+  }
+
+  return success;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static bool rulesetdir_load_improvements(const char *rulesetdir)
+{
+  struct section_file *file;
+  struct section_list *sec;
+  const char *filename;
+  bool success = TRUE;
+
+  /* TODO */
+  fc_assert_ret_val(game_ruleset != NULL, FALSE);
+
+  /* Load all improvements.*/
+  file = rulesetdir_openload_file(rulesetdir, "buildings");
+  filename = secfile_name(file);
+
+  /* Prevent log message due to unused entry. */
+  (void) secfile_entry_by_path(file, "datafile.description");
+
+  (void) rulesetdir_check_capabilities(file, "+1.9", filename);
+
+  /* Load the section names. */
+  sec = secfile_sections_by_name_prefix(file, RS_SECTION_PREFIX_ADVANCE);
+  /* TODO */
+
+  if (!ruleset_validate_improvements()) {
+    rulesetdir_error(LOG_ERROR, "\"%s\": %s", filename, ruleset_error());
+    success = FALSE;
+    goto CLEANUP;
+  }
+
+ CLEANUP:
+  if (sec) {
+    section_list_destroy(sec);
+  }
+  if (file) {
+    secfile_check_unused(file);
+    secfile_destroy(file);
+  }
+
+  return success;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static bool rulesetdir_load_governments(const char *rulesetdir)
+{
+  struct section_file *file;
+  struct section_list *sec;
+  const char *filename;
+  bool success = TRUE;
+
+  /* TODO */
+  fc_assert_ret_val(game_ruleset != NULL, FALSE);
+
+  /* Load all advances.*/
+  file = rulesetdir_openload_file(rulesetdir, "techs");
+  filename = secfile_name(file);
+
+  /* Prevent log message due to unused entry. */
+  (void) secfile_entry_by_path(file, "datafile.description");
+
+  (void) rulesetdir_check_capabilities(file, "+1.9", filename);
+
+  /* Load the section names. */
+  sec = secfile_sections_by_name_prefix(file, RS_SECTION_PREFIX_ADVANCE);
+  /* TODO */
+
+  if (!ruleset_validate_governments()) {
+    rulesetdir_error(LOG_ERROR, "\"%s\": %s", filename, ruleset_error());
+    success = FALSE;
+    goto CLEANUP;
+  }
+
+ CLEANUP:
+  if (sec) {
+    section_list_destroy(sec);
+  }
+  if (file) {
+    secfile_check_unused(file);
+    secfile_destroy(file);
+  }
+
+  return success;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static bool rulesetdir_load_units(const char *rulesetdir)
+{
+  struct section_file *file;
+  struct section_list *sec;
+  const char *filename;
+  bool success = TRUE;
+
+  /* TODO */
+  fc_assert_ret_val(game_ruleset != NULL, FALSE);
+
+  /* Load all advances.*/
+  file = rulesetdir_openload_file(rulesetdir, "techs");
+  filename = secfile_name(file);
+
+  /* Prevent log message due to unused entry. */
+  (void) secfile_entry_by_path(file, "datafile.description");
+
+  (void) rulesetdir_check_capabilities(file, "+1.9", filename);
+
+  /* Load the section names. */
+  sec = secfile_sections_by_name_prefix(file, RS_SECTION_PREFIX_ADVANCE);
+  /* TODO */
+
+  if (!ruleset_validate_units()) {
+    rulesetdir_error(LOG_ERROR, "\"%s\": %s", filename, ruleset_error());
+    success = FALSE;
+    goto CLEANUP;
+  }
+
+ CLEANUP:
+  if (sec) {
+    section_list_destroy(sec);
+  }
+  if (file) {
+    secfile_check_unused(file);
+    secfile_destroy(file);
+  }
+
+  return success;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static bool rulesetdir_load_terrain(const char *rulesetdir)
+{
+  struct section_file *file;
+  struct section_list *sec;
+  const char *filename;
+  bool success = TRUE;
+
+  /* TODO */
+  fc_assert_ret_val(game_ruleset != NULL, FALSE);
+
+  /* Load all advances.*/
+  file = rulesetdir_openload_file(rulesetdir, "techs");
+  filename = secfile_name(file);
+
+  /* Prevent log message due to unused entry. */
+  (void) secfile_entry_by_path(file, "datafile.description");
+
+  (void) rulesetdir_check_capabilities(file, "+1.9", filename);
+
+  /* Load the section names. */
+  sec = secfile_sections_by_name_prefix(file, RS_SECTION_PREFIX_ADVANCE);
+  /* TODO */
+
+  if (!ruleset_validate_terrain()) {
+    rulesetdir_error(LOG_ERROR, "\"%s\": %s", filename, ruleset_error());
+    success = FALSE;
+    goto CLEANUP;
+  }
+
+ CLEANUP:
+  if (sec) {
+    section_list_destroy(sec);
+  }
+  if (file) {
+    secfile_check_unused(file);
+    secfile_destroy(file);
+  }
+
+  return success;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static bool rulesetdir_load_nations(const char *rulesetdir)
+{
+  struct section_file *file;
+  struct section_list *sec;
+  const char *filename;
+  bool success = TRUE;
+
+  /* TODO */
+  fc_assert_ret_val(game_ruleset != NULL, FALSE);
+
+  /* Load all advances.*/
+  file = rulesetdir_openload_file(rulesetdir, "techs");
+  filename = secfile_name(file);
+
+  /* Prevent log message due to unused entry. */
+  (void) secfile_entry_by_path(file, "datafile.description");
+
+  (void) rulesetdir_check_capabilities(file, "+1.9", filename);
+
+  /* Load the section names. */
+  sec = secfile_sections_by_name_prefix(file, RS_SECTION_PREFIX_ADVANCE);
+  /* TODO */
+
+  if (!ruleset_validate_nations()) {
+    rulesetdir_error(LOG_ERROR, "\"%s\": %s", filename, ruleset_error());
+    success = FALSE;
+    goto CLEANUP;
+  }
+
+ CLEANUP:
+  if (sec) {
+    section_list_destroy(sec);
+  }
+  if (file) {
+    secfile_check_unused(file);
+    secfile_destroy(file);
+  }
+
+  return success;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static bool rulesetdir_load_cities(const char *rulesetdir)
+{
+  struct section_file *file;
+  struct section_list *sec;
+  const char *filename;
+  bool success = TRUE;
+
+  /* TODO */
+  fc_assert_ret_val(game_ruleset != NULL, FALSE);
+
+  /* Load all advances.*/
+  file = rulesetdir_openload_file(rulesetdir, "techs");
+  filename = secfile_name(file);
+
+  /* Prevent log message due to unused entry. */
+  (void) secfile_entry_by_path(file, "datafile.description");
+
+  (void) rulesetdir_check_capabilities(file, "+1.9", filename);
+
+  /* Load the section names. */
+  sec = secfile_sections_by_name_prefix(file, RS_SECTION_PREFIX_ADVANCE);
+  /* TODO */
+
+  if (!ruleset_validate_cities()) {
+    rulesetdir_error(LOG_ERROR, "\"%s\": %s", filename, ruleset_error());
+    success = FALSE;
+    goto CLEANUP;
+  }
+
+ CLEANUP:
+  if (sec) {
+    section_list_destroy(sec);
+  }
+  if (file) {
+    secfile_check_unused(file);
+    secfile_destroy(file);
+  }
+
+  return success;
+}
+
+/**************************************************************************
+  ...
+**************************************************************************/
+static bool rulesetdir_load_effects(const char *rulesetdir)
+{
+  struct section_file *file;
+  struct section_list *sec;
+  const char *filename;
+  bool success = TRUE;
+
+  /* TODO */
+  fc_assert_ret_val(game_ruleset != NULL, FALSE);
+
+  /* Load all advances.*/
+  file = rulesetdir_openload_file(rulesetdir, "techs");
+  filename = secfile_name(file);
+
+  /* Prevent log message due to unused entry. */
+  (void) secfile_entry_by_path(file, "datafile.description");
+
+  (void) rulesetdir_check_capabilities(file, "+1.9", filename);
+
+  /* Load the section names. */
+  sec = secfile_sections_by_name_prefix(file, RS_SECTION_PREFIX_ADVANCE);
+  /* TODO */
+
+  if (!ruleset_validate_effects()) {
+    rulesetdir_error(LOG_ERROR, "\"%s\": %s", filename, ruleset_error());
+    success = FALSE;
+    goto CLEANUP;
+  }
+
+ CLEANUP:
+  if (sec) {
+    section_list_destroy(sec);
+  }
+  if (file) {
+    secfile_check_unused(file);
+    secfile_destroy(file);
+  }
+
+  return success;
+}
diff --git a/server/rulesetdir.h b/server/rulesetdir.h
new file mode 100644
index 0000000..80dffa7
--- /dev/null
+++ b/server/rulesetdir.h
@@ -0,0 +1,26 @@
+/**********************************************************************
+ Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
+   This program 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; either version 2, or (at your option)
+   any later version.
+
+   This program 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.
+***********************************************************************/
+#ifndef FC__RULESETDIR_H
+#define FC__RULESETDIR_H
+
+struct conn_list;
+
+void ruleset_test(void);
+
+
+bool rulesetdir_load(const char *rulesetdir);
+
+void rulesets_settings_restore(void);
+void rulesets_send(struct conn_list *dest);
+
+#endif  /* FC__RULESETDIR_H */
diff --git a/utility/generate_specenum.py b/utility/generate_specenum.py
index 583b045..fbb10ad 100755
--- a/utility/generate_specenum.py
+++ b/utility/generate_specenum.py
@@ -388,6 +388,61 @@ static inline enum SPECENUM_NAME SPECENUM_FOO(_by_name)
 }
 ''')
 
+def make_bv_definition(file):
+    file.write('''
+#define SPECENUM_BV SPECENUM_PASTE(bv_, SPECENUM_NAME)
+
+BV_DEFINE(SPECENUM_BV, SPECENUM_MAX_VALUE);
+''')
+    macros.append("SPECENUM_BV")
+
+def make_bv_to_str(file):
+    file.write('''
+/**************************************************************************
+  Returns the string representation of the bitvector for the enum.
+**************************************************************************/
+static inline char *SPECENUM_FOO(_bv_to_str) (SPECENUM_BV bv)
+{
+  enum SPECENUM_NAME e;
+  static char str[SPECENUM_MAX_VALUE + 1];
+
+  for (e = SPECENUM_FOO(_begin)(); e != SPECENUM_FOO(_end)();
+       e = SPECENUM_FOO(_next)(e)) {
+    if (BV_ISSET(bv, e)) {
+      str[e] = '1';
+    } else {
+      str[e] = '0';
+    }
+  }
+  str[SPECENUM_MAX_VALUE] = '\\0';
+
+  return str;
+}
+''')
+
+def make_str_to_bv(file):
+    file.write('''
+/**************************************************************************
+  Transforms the string representation into the bitvector for the enum.
+**************************************************************************/
+static inline SPECENUM_BV SPECENUM_FOO(_str_to_bv)
+    (const char *str, const char **names, size_t names_len)
+{
+  SPECENUM_BV bv;
+  int i;
+
+  BV_CLR_ALL(bv);
+  for (i = 0; i < strlen(str) && i < names_len; i++) {
+    enum SPECENUM_NAME e = SPECENUM_FOO(_by_name)(names[i], strcmp);
+    if (SPECENUM_FOO(_is_valid)(e) && str[i] == '1') {
+      BV_SET(bv, e);
+    }
+  }
+
+  return bv;
+}
+''')
+
 def make_undef(file):
     for macro in macros:
         file.write('''
@@ -418,6 +473,9 @@ def main():
     make_next(output)
     make_name(output)
     make_by_name(output)
+    make_bv_definition(output)
+    make_bv_to_str(output)
+    make_str_to_bv(output)
     make_undef(output)
 
     output.close()
diff --git a/utility/genlist.c b/utility/genlist.c
index 013b0bf..39e5497 100644
--- a/utility/genlist.c
+++ b/utility/genlist.c
@@ -148,6 +148,28 @@ int genlist_size(const struct genlist *pgenlist)
 }
 
 /****************************************************************************
+  Returns the index of the elements.
+
+  This is an O(n) operation.
+****************************************************************************/
+int genlist_index(const struct genlist *pgenlist, const void *data)
+{
+  const struct genlist_link *plink;
+  int index = 0;
+
+  fc_assert_ret_val(NULL != pgenlist, FALSE);
+
+  for (plink = pgenlist->head_link; plink; plink = plink->next) {
+    if (plink->dataptr == data) {
+      return index;
+    }
+    index++;
+  }
+
+  return -1;
+}
+
+/****************************************************************************
   Returns the user-data pointer stored in the genlist at the position
   given by 'idx'.  For idx out of range (including an empty list),
   returns NULL.
@@ -287,7 +309,7 @@ void genlist_insert(struct genlist *pgenlist, void *data, int pos)
   fc_assert_ret(NULL != pgenlist);
 
   if (pgenlist->nelements == 0) { /*list is empty, ignore pos */
-    
+
     struct genlist_link *plink = fc_malloc(sizeof(*plink));
 
     plink->dataptr = data;
@@ -326,6 +348,27 @@ void genlist_insert(struct genlist *pgenlist, void *data, int pos)
 }
 
 /****************************************************************************
+  Update the element at the list position 'pos' with the specified
+  user-data pointer 'data'. The pointer to the previous value is returned.
+
+  'pos' must be a valid position.
+****************************************************************************/
+void *genlist_update(struct genlist *pgenlist, void *data, int pos)
+{
+  void *old;
+  struct genlist_link *link;
+
+  fc_assert_ret_val(NULL != pgenlist, NULL);
+  fc_assert_ret_val(0 <= pos && pos < genlist_size(pgenlist), NULL);
+
+  link = find_genlist_position(pgenlist, pos);
+  old = link->dataptr;
+  link->dataptr = data;
+
+  return old;
+}
+
+/****************************************************************************
   Insert an item at the start of the list.
 ****************************************************************************/
 void genlist_prepend(struct genlist *pgenlist, void *data)
@@ -377,21 +420,13 @@ static struct genlist_link *find_genlist_position(const struct genlist *pgenlist
 /****************************************************************************
   Return TRUE iff this element is in the list.
 
-  This is an O(n) operation.  Hence, "search".
+  This is an O(n) operation (see genlist_index()).  Hence, "search".
 ****************************************************************************/
 bool genlist_search(const struct genlist *pgenlist, const void *data)
 {
-  const struct genlist_link *plink;
-
   fc_assert_ret_val(NULL != pgenlist, FALSE);
 
-  for (plink = pgenlist->head_link; plink; plink = plink->next) {
-    if (plink->dataptr == data) {
-      return TRUE;
-    }
-  }
-
-  return FALSE;
+  return (genlist_index(pgenlist, data) != -1);
 }
 
 /****************************************************************************
diff --git a/utility/genlist.h b/utility/genlist.h
index 0339671..fbf8107 100644
--- a/utility/genlist.h
+++ b/utility/genlist.h
@@ -78,9 +78,11 @@ void genlist_unique_full(struct genlist *pgenlist,
 void genlist_append(struct genlist *pgenlist, void *data);
 void genlist_prepend(struct genlist *pgenlist, void *data);
 void genlist_insert(struct genlist *pgenlist, void *data, int idx);
+void *genlist_update(struct genlist *pgenlist, void *data, int idx);
 bool genlist_remove(struct genlist *pgenlist, void *data);
 
 int genlist_size(const struct genlist *pgenlist);
+int genlist_index(const struct genlist *pgenlist, const void *data);
 void *genlist_get(const struct genlist *pgenlist, int idx);
 
 bool genlist_search(const struct genlist *pgenlist, const void *data);
diff --git a/utility/speclist.h b/utility/speclist.h
index 9c82293..bfff801 100644
--- a/utility/speclist.h
+++ b/utility/speclist.h
@@ -37,11 +37,13 @@
       struct foolist *foo_list_copy_full(const struct foolist *plist,
                                   foo_t * (*copy_data_func) (const foo_t *)),
                                   void (*free_data_func) (foo_t *));
-      int  foo_list_size(const struct foo_list *This);
+      int foo_list_size(const struct foo_list *This);
+      int foo_list_index(struct foo_list *This, foo_t *pfoo);
       foo_t *foo_list_get(const struct foo_list *This, int index);
       void foo_list_prepend(struct foo_list *This, foo_t *pfoo);
       void foo_list_append(struct foo_list *This, foo_t *pfoo);
       void foo_list_insert(struct foo_list *This, foo_t *pfoo, int idx);
+      void foo_list_update(struct foo_list *This, foo_t *pfoo, int idx);
       bool foo_list_remove(struct foo_list *This, foo_t *pfoo);
       void foo_list_clear(struct foo_list *This);
       void foo_list_unique(struct foo_list *This);
@@ -127,6 +129,12 @@ static inline int SPECLIST_FOO(_list_size) (const SPECLIST_LIST *tthis)
   return genlist_size(GENLIST(tthis));
 }
 
+static inline int SPECLIST_FOO(_list_index)
+    (const SPECLIST_LIST *tthis, SPECLIST_TYPE *pfoo)
+{
+  return genlist_index(GENLIST(tthis), pfoo);
+}
+
 static inline SPECLIST_TYPE *SPECLIST_FOO(_list_get) (const SPECLIST_LIST *tthis, int index)
 {
   return (SPECLIST_TYPE *) genlist_get(GENLIST(tthis), index);
@@ -147,6 +155,12 @@ static inline void SPECLIST_FOO(_list_insert) (SPECLIST_LIST *tthis, SPECLIST_TY
   genlist_insert(GENLIST(tthis), pfoo, idx);
 }
 
+static inline SPECLIST_TYPE *SPECLIST_FOO(_list_update)
+    (SPECLIST_LIST *tthis, SPECLIST_TYPE *pfoo, int idx)
+{
+  return (SPECLIST_TYPE *) genlist_update(GENLIST(tthis), pfoo, idx);
+}
+
 static inline bool SPECLIST_FOO(_list_remove) (SPECLIST_LIST *tthis, SPECLIST_TYPE *pfoo)
 {
   return genlist_remove(GENLIST(tthis), pfoo);
