Give it a start at GSUB
diff --git a/src/hb-common.h b/src/hb-common.h
index c60ad84..d404353 100644
--- a/src/hb-common.h
+++ b/src/hb-common.h
@@ -46,6 +46,9 @@
 				((const char *) s)[2], \
 				((const char *) s)[3]))
 
-typedef uint16_t hb_glyph_t;
+typedef uint32_t hb_codepoint_t;
+
+/* XXX */
+typedef struct HB_BufferRec_ hb_buffer_t;
 
 #endif /* HB_COMMON_H */
diff --git a/src/hb-ot-layout-gdef-private.h b/src/hb-ot-layout-gdef-private.h
index 447ee0e..5418d8b 100644
--- a/src/hb-ot-layout-gdef-private.h
+++ b/src/hb-ot-layout-gdef-private.h
@@ -33,18 +33,18 @@
 
 
 #define DEFINE_INDIRECT_GLYPH_ARRAY_LOOKUP(Type, name) \
-  inline const Type& name (hb_glyph_t glyph_id) { \
+  inline const Type& name (hb_codepoint_t glyph) { \
     const Coverage &c = get_coverage (); \
-    hb_ot_layout_coverage_t c_index = c.get_coverage (glyph_id); \
+    hb_ot_layout_coverage_t c_index = c.get_coverage (glyph); \
     return (*this)[c_index]; \
   }
 
 
 struct GlyphClassDef : ClassDef {
-  static const uint16_t BaseGlyph		= 0x0001u;
-  static const uint16_t LigatureGlyph		= 0x0002u;
-  static const uint16_t MarkGlyph		= 0x0003u;
-  static const uint16_t ComponentGlyph		= 0x0004u;
+  static const unsigned int BaseGlyph		= 0x0001u;
+  static const unsigned int LigatureGlyph	= 0x0002u;
+  static const unsigned int MarkGlyph		= 0x0003u;
+  static const unsigned int ComponentGlyph	= 0x0004u;
 };
 
 /*
@@ -72,7 +72,7 @@
   friend struct GDEF;
 
   private:
-  /* const AttachPoint& get_attach_points (hb_glyph_t glyph_id); */
+  /* const AttachPoint& get_attach_points (hb_codepoint_t glyph); */
   DEFINE_INDIRECT_GLYPH_ARRAY_LOOKUP (AttachPoint, get_attach_points);
 
   private:
@@ -205,7 +205,7 @@
   friend struct GDEF;
 
   private:
-  /* const LigGlyph& get_lig_glyph (hb_glyph_t glyph_id); */
+  /* const LigGlyph& get_lig_glyph (hb_codepoint_t glyph); */
   DEFINE_INDIRECT_GLYPH_ARRAY_LOOKUP (LigGlyph, get_lig_glyph);
 
   private:
@@ -245,12 +245,12 @@
   DEFINE_GET_HAS_ACCESSOR (LigCaretList, lig_caret_list, ligCaretList);
   DEFINE_GET_HAS_ACCESSOR (ClassDef, mark_attachment_types, markAttachClassDef);
 
-  inline hb_ot_layout_class_t get_glyph_class (hb_glyph_t glyph_id) const {
-    return get_glyph_classes ().get_class (glyph_id);
+  inline hb_ot_layout_class_t get_glyph_class (hb_codepoint_t glyph) const {
+    return get_glyph_classes ().get_class (glyph);
   }
 
-  inline hb_ot_layout_class_t get_mark_attachment_type (hb_glyph_t glyph_id) const {
-    return get_mark_attachment_types ().get_class (glyph_id);
+  inline hb_ot_layout_class_t get_mark_attachment_type (hb_codepoint_t glyph) const {
+    return get_mark_attachment_types ().get_class (glyph);
   }
 
   /* TODO get_attach and get_lig_caret */
diff --git a/src/hb-ot-layout-gsub-private.h b/src/hb-ot-layout-gsub-private.h
index 8ce7705..ae41090 100644
--- a/src/hb-ot-layout-gsub-private.h
+++ b/src/hb-ot-layout-gsub-private.h
@@ -34,7 +34,17 @@
 
 
 struct SingleSubstFormat1 {
-  /* TODO */
+
+  friend struct SingleSubst;
+
+  private:
+  inline bool substitute (hb_ot_layout_t *layout,
+			  hb_buffer_t    *buffer,
+			  unsigned int    context_length,
+			  unsigned int    nesting_level_left) const {
+//    if (get_coverage (IN_CURGLYPH()))
+//      return ;
+  }
 
   private:
   USHORT	substFormat;		/* Format identifier--format = 1 */
@@ -450,6 +460,108 @@
 ASSERT_SIZE (ReverseChainSingleSubstFormat1, 10);
 
 /*
+ * SubstLookup
+ */
+
+struct SubstLookupSubTable {
+  DEFINE_NON_INSTANTIABLE(SubstLookupSubTable);
+
+  friend struct SubstLookup;
+
+  unsigned int get_size (unsigned int lookup_type) const {
+    switch (lookup_type) {
+//    case 1: return u.format1.get_size ();
+//    case 2: return u.format2.get_size ();
+    /*
+    case Single:
+    case Multiple:
+    case Alternate:
+    case Ligature:
+    case Context:
+    case ChainingContext:
+    case Extension:
+    case ReverseChainingContextSingle:
+    */
+    default:return sizeof (LookupSubTable);
+    }
+  }
+
+  inline bool substitute (hb_ot_layout_t *layout,
+			  hb_buffer_t    *buffer,
+			  unsigned int    context_length,
+			  unsigned int    nesting_level_left,
+			  unsigned int    lookup_type) const {
+  }
+
+  private:
+  union {
+  USHORT		substFormat;
+  CoverageFormat1	format1;
+  CoverageFormat2	format2;
+  } u;
+};
+
+struct SubstLookup : Lookup {
+
+  DEFINE_NON_INSTANTIABLE(SubstLookup);
+
+  static const unsigned int Single				= 1;
+  static const unsigned int Multiple				= 2;
+  static const unsigned int Alternate				= 3;
+  static const unsigned int Ligature				= 4;
+  static const unsigned int Context				= 5;
+  static const unsigned int ChainingContext			= 6;
+  static const unsigned int Extension				= 7;
+  static const unsigned int ReverseChainingContextSingle	= 8;
+
+  inline const SubstLookupSubTable& get_subtable (unsigned int i) const {
+    return *(SubstLookupSubTable*)&(((Lookup *)this)->get_subtable (i));
+  }
+
+  /* Like get_type(), but looks through extension lookups.
+   * Never returns SubstLookup::Extension */
+  inline unsigned int get_effective_type (void) const {
+    unsigned int type = get_type ();
+
+    if (HB_UNLIKELY (type == Extension)) {
+      /* Return lookup type of first extension subtable.
+       * The spec says all of them should have the same type.
+       * XXX check for that somehow */
+//XXX      type = get_subtable(0).v.extension.get_type ();
+    }
+
+    return type;
+  }
+
+  inline bool is_reverse (void) const {
+    switch (get_effective_type ()) {
+    case ReverseChainingContextSingle:	return true;
+    default:				return false;
+    }
+  }
+
+  inline bool substitute (hb_ot_layout_t *layout,
+			  hb_buffer_t    *buffer,
+			  unsigned int    context_length,
+			  unsigned int    nesting_level_left) const {
+    unsigned int lookup_type = get_type ();
+
+    if (HB_UNLIKELY (nesting_level_left == 0))
+      return false;
+    nesting_level_left--;
+  
+    for (unsigned int i = 0; i < get_subtable_count (); i++)
+      if (get_subtable (i).substitute (layout, buffer,
+				       context_length, nesting_level_left,
+				       lookup_type))
+	return true;
+  
+    return false;
+  }
+};
+DEFINE_NULL_ALIAS (SubstLookup, Lookup);
+
+/*
  * GSUB
  */
 
@@ -458,6 +570,12 @@
 
   STATIC_DEFINE_GET_FOR_DATA (GSUB);
   /* XXX check version here? */
+
+  inline const SubstLookup& get_lookup (unsigned int i) const {
+    return *(SubstLookup*)&(((GSUBGPOS *)this)->get_lookup (i));
+  }
+
+
 };
 DEFINE_NULL_ALIAS (GSUB, GSUBGPOS);
 
diff --git a/src/hb-ot-layout-open-private.h b/src/hb-ot-layout-open-private.h
index 9d45f7c..66fd55e 100644
--- a/src/hb-ot-layout-open-private.h
+++ b/src/hb-ot-layout-open-private.h
@@ -35,6 +35,7 @@
 
 
 #define NO_INDEX		((unsigned int) 0xFFFF)
+#define NO_CONTEXT		((unsigned int) -1)
 
 /*
  * Int types
@@ -191,7 +192,7 @@
 /* makes class uninstantiable.  should be used for union classes that don't
  * contain any complete type */
 #define DEFINE_NON_INSTANTIABLE(Type) \
-  private: inline Type() {} /* cannot be instantiated */ \
+  protected: inline Type() {} /* cannot be instantiated */ \
   public:
 
 // TODO use a global nul-array for most Null's
@@ -236,6 +237,7 @@
 
 
 
+
 /*
  *
  * The OpenType Font File
@@ -642,12 +644,12 @@
 DEFINE_NULL_ASSERT_SIZE (FeatureList, 2);
 
 struct LookupFlag : USHORT {
-  static const uint16_t RightToLeft		= 0x0001u;
-  static const uint16_t IgnoreBaseGlyphs	= 0x0002u;
-  static const uint16_t IgnoreLigatures		= 0x0004u;
-  static const uint16_t IgnoreMarks		= 0x0008u;
-  static const uint16_t Reserved		= 0x00F0u;
-  static const uint16_t MarkAttachmentType	= 0xFF00u;
+  static const unsigned int RightToLeft		= 0x0001u;
+  static const unsigned int IgnoreBaseGlyphs	= 0x0002u;
+  static const unsigned int IgnoreLigatures	= 0x0004u;
+  static const unsigned int IgnoreMarks		= 0x0008u;
+  static const unsigned int Reserved		= 0x00F0u;
+  static const unsigned int MarkAttachmentType	= 0xFF00u;
 };
 DEFINE_NULL_ASSERT_SIZE (LookupFlag, 2);
 
@@ -661,8 +663,9 @@
 
 
 struct Lookup {
-  /* SubTables, in the desired order */
-  DEFINE_OFFSET_ARRAY_TYPE (LookupSubTable, subTableOffset, subTableCount);
+  DEFINE_NON_INSTANTIABLE(Lookup);
+
+  DEFINE_ARRAY_INTERFACE (LookupSubTable, subtable);	/* get_subtable_count(), get_subtable(i) */
 
   inline bool is_right_to_left	(void) const { return lookupFlag & LookupFlag::RightToLeft; }
   inline bool ignore_base_glyphs(void) const { return lookupFlag & LookupFlag::IgnoreBaseGlyphs; }
@@ -670,10 +673,14 @@
   inline bool ignore_marks	(void) const { return lookupFlag & LookupFlag::IgnoreMarks; }
   inline bool get_mark_attachment_type (void) const { return lookupFlag & LookupFlag::MarkAttachmentType; }
 
-  inline uint16_t get_type (void) const { return lookupType; }
-  inline uint16_t get_flag (void) const { return lookupFlag; }
+  inline unsigned int get_type (void) const { return lookupType; }
+  inline unsigned int get_flag (void) const { return lookupFlag; }
 
   private:
+  /* SubTables, in the desired order */
+  DEFINE_OFFSET_ARRAY_TYPE (LookupSubTable, subTableOffset, subTableCount);
+
+  protected:
   USHORT	lookupType;	/* Different enumerations for GSUB and GPOS */
   USHORT	lookupFlag;	/* Lookup qualifiers */
   USHORT	subTableCount;	/* Number of SubTables for this lookup */
@@ -710,8 +717,10 @@
   /* GlyphIDs, in sorted numerical order */
   DEFINE_ARRAY_TYPE (GlyphID, glyphArray, glyphCount);
 
-  inline hb_ot_layout_coverage_t get_coverage (hb_glyph_t glyph_id) const {
+  inline hb_ot_layout_coverage_t get_coverage (hb_codepoint_t glyph_id) const {
     GlyphID gid;
+    if (HB_UNLIKELY (glyph_id > 65535))
+      return -1;
     gid = glyph_id;
     // TODO: bsearch
     for (unsigned int i = 0; i < glyphCount; i++)
@@ -732,7 +741,7 @@
   friend struct CoverageFormat2;
 
   private:
-  inline hb_ot_layout_coverage_t get_coverage (hb_glyph_t glyph_id) const {
+  inline hb_ot_layout_coverage_t get_coverage (hb_codepoint_t glyph_id) const {
     if (glyph_id >= start && glyph_id <= end)
       return startCoverageIndex + (glyph_id - start);
     return -1;
@@ -754,7 +763,7 @@
   /* CoverageRangeRecords, in sorted numerical start order */
   DEFINE_ARRAY_TYPE (CoverageRangeRecord, rangeRecord, rangeCount);
 
-  inline hb_ot_layout_coverage_t get_coverage (hb_glyph_t glyph_id) const {
+  inline hb_ot_layout_coverage_t get_coverage (hb_codepoint_t glyph_id) const {
     // TODO: bsearch
     for (unsigned int i = 0; i < rangeCount; i++) {
       int coverage = rangeRecord[i].get_coverage (glyph_id);
@@ -784,7 +793,7 @@
     }
   }
 
-  hb_ot_layout_coverage_t get_coverage (hb_glyph_t glyph_id) const {
+  hb_ot_layout_coverage_t get_coverage (hb_codepoint_t glyph_id) const {
     switch (u.coverageFormat) {
     case 1: return u.format1.get_coverage(glyph_id);
     case 2: return u.format2.get_coverage(glyph_id);
@@ -813,7 +822,7 @@
   /* GlyphIDs, in sorted numerical order */
   DEFINE_ARRAY_TYPE (USHORT, classValueArray, glyphCount);
 
-  inline hb_ot_layout_class_t get_class (hb_glyph_t glyph_id) const {
+  inline hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const {
     if (glyph_id >= startGlyph && glyph_id - startGlyph < glyphCount)
       return classValueArray[glyph_id - startGlyph];
     return 0;
@@ -832,7 +841,7 @@
   friend struct ClassDefFormat2;
 
   private:
-  inline hb_ot_layout_class_t get_class (hb_glyph_t glyph_id) const {
+  inline hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const {
     if (glyph_id >= start && glyph_id <= end)
       return classValue;
     return 0;
@@ -853,9 +862,9 @@
   /* ClassRangeRecords, in sorted numerical start order */
   DEFINE_ARRAY_TYPE (ClassRangeRecord, rangeRecord, rangeCount);
 
-  inline hb_ot_layout_class_t get_class (hb_glyph_t glyph_id) const {
+  inline hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const {
     // TODO: bsearch
-    for (int i = 0; i < rangeCount; i++) {
+    for (unsigned int i = 0; i < rangeCount; i++) {
       int classValue = rangeRecord[i].get_class (glyph_id);
       if (classValue > 0)
         return classValue;
@@ -882,7 +891,7 @@
     }
   }
 
-  hb_ot_layout_class_t get_class (hb_glyph_t glyph_id) const {
+  hb_ot_layout_class_t get_class (hb_codepoint_t glyph_id) const {
     switch (u.classFormat) {
     case 1: return u.format1.get_class(glyph_id);
     case 2: return u.format2.get_class(glyph_id);
@@ -949,7 +958,7 @@
  * GSUB/GPOS Common
  */
 
-typedef struct GSUBGPOS {
+struct GSUBGPOS {
   static const hb_tag_t GSUBTag		= HB_TAG ('G','S','U','B');
   static const hb_tag_t GPOSTag		= HB_TAG ('G','P','O','S');
 
diff --git a/src/hb-ot-layout-private.h b/src/hb-ot-layout-private.h
index 8060e4f..a1be8aa 100644
--- a/src/hb-ot-layout-private.h
+++ b/src/hb-ot-layout-private.h
@@ -53,7 +53,7 @@
 
 HB_OT_LAYOUT_INTERNAL hb_ot_layout_glyph_properties_t
 _hb_ot_layout_get_glyph_properties (hb_ot_layout_t *layout,
-				    hb_glyph_t      glyph);
+				    hb_codepoint_t  glyph);
 
 HB_OT_LAYOUT_INTERNAL hb_bool_t
 _hb_ot_layout_check_glyph_properties (hb_ot_layout_t                  *layout,
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index 1172248..68fd3e5 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -35,6 +35,8 @@
 #include "hb-ot-layout-gdef-private.h"
 #include "hb-ot-layout-gsub-private.h"
 
+/* XXX */
+#include "harfbuzz-buffer-private.h"
 
 #include <stdlib.h>
 #include <string.h>
@@ -50,6 +52,7 @@
     unsigned int len;
   } new_gdef;
 
+  /* TODO add max-nesting-level here? */
 };
 
 hb_ot_layout_t *
@@ -109,7 +112,7 @@
 
 HB_OT_LAYOUT_INTERNAL hb_ot_layout_glyph_properties_t
 _hb_ot_layout_get_glyph_properties (hb_ot_layout_t *layout,
-				    hb_glyph_t      glyph)
+				    hb_codepoint_t  glyph)
 {
   hb_ot_layout_class_t klass;
 
@@ -186,7 +189,7 @@
 
 hb_ot_layout_glyph_class_t
 hb_ot_layout_get_glyph_class (hb_ot_layout_t *layout,
-			      hb_glyph_t      glyph)
+			      hb_codepoint_t  glyph)
 {
   hb_ot_layout_glyph_properties_t properties;
   hb_ot_layout_class_t klass;
@@ -201,7 +204,7 @@
 
 void
 hb_ot_layout_set_glyph_class (hb_ot_layout_t             *layout,
-			      hb_glyph_t                  glyph,
+			      hb_codepoint_t              glyph,
 			      hb_ot_layout_glyph_class_t  klass)
 {
   /* TODO optimize this, similar to old harfbuzz code for example */
@@ -243,7 +246,7 @@
 void
 hb_ot_layout_build_glyph_classes (hb_ot_layout_t *layout,
 				  uint16_t        num_total_glyphs,
-				  hb_glyph_t     *glyphs,
+				  hb_codepoint_t *glyphs,
 				  unsigned char  *klasses,
 				  uint16_t        count)
 {
@@ -508,3 +511,55 @@
 
   return f.get_lookup_index (num_lookup);
 }
+
+/*
+ * GSUB
+ */
+
+hb_bool_t
+hb_ot_layout_substitute_lookup (hb_ot_layout_t              *layout,
+				hb_buffer_t                 *buffer,
+			        unsigned int                 lookup_index,
+				hb_ot_layout_feature_mask_t  mask)
+{
+  const GSUB &gsub = *(layout->gsub);
+  const SubstLookup &l = gsub.get_lookup (lookup_index);
+  unsigned int lookup_type = l.get_type ();
+  unsigned int nesting_level_left = HB_OT_LAYOUT_MAX_NESTING_LEVEL;
+  unsigned int context_length = NO_CONTEXT;
+  bool handled, ret = false;
+
+  if (!l.is_reverse ()) {
+
+      /* in/out forward substitution */
+      _hb_buffer_clear_output (buffer);
+      buffer->in_pos = 0;
+      while (buffer->in_pos < buffer->in_length) {
+
+	if ((~IN_PROPERTIES (buffer->in_pos) & mask) &&
+	    l.substitute (layout, buffer, context_length, nesting_level_left))
+	  ret = true;
+	else
+	  _hb_buffer_copy_output_glyph (buffer);
+
+      }
+      _hb_buffer_swap (buffer);
+
+  } else {
+
+      /* in-place backward substitution */
+      buffer->in_pos = buffer->in_length - 1;
+      do {
+
+	if ((~IN_PROPERTIES (buffer->in_pos) & mask) &&
+	    l.substitute (layout, buffer, context_length, nesting_level_left))
+	  ret = true;
+	else
+	  buffer->in_pos--;
+
+      } while (buffer->in_pos);
+
+  }
+
+  return ret;
+}
diff --git a/src/hb-ot-layout.h b/src/hb-ot-layout.h
index 447dac2..c29485c 100644
--- a/src/hb-ot-layout.h
+++ b/src/hb-ot-layout.h
@@ -67,17 +67,17 @@
 
 hb_ot_layout_glyph_class_t
 hb_ot_layout_get_glyph_class (hb_ot_layout_t *layout,
-			      hb_glyph_t      glyph);
+			      hb_codepoint_t  glyph);
 
 void
 hb_ot_layout_set_glyph_class (hb_ot_layout_t            *layout,
-			      hb_glyph_t                 glyph,
+			      hb_codepoint_t             glyph,
 			      hb_ot_layout_glyph_class_t klass);
 
 void
 hb_ot_layout_build_glyph_classes (hb_ot_layout_t *layout,
 				  uint16_t        num_total_glyphs,
-				  hb_glyph_t     *glyphs,
+				  hb_codepoint_t *glyphs,
 				  unsigned char  *klasses,
 				  uint16_t        count);
 
@@ -91,6 +91,10 @@
   HB_OT_LAYOUT_TABLE_TYPE_NONE
 } hb_ot_layout_table_type_t;
 
+typedef uint16_t hb_ot_layout_feature_mask_t;
+
+#define HB_OT_LAYOUT_MAX_NESTING_LEVEL		100
+
 #define HB_OT_LAYOUT_NO_SCRIPT_INDEX		((unsigned int) 0xFFFF)
 #define HB_OT_LAYOUT_NO_FEATURE_INDEX		((unsigned int) 0xFFFF)
 #define HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX	((unsigned int) 0xFFFF)
@@ -195,6 +199,25 @@
 				       unsigned int               feature_index,
 				       unsigned int               num_lookup);
 
+/*
+ * GSUB
+ */
+
+hb_bool_t
+hb_ot_layout_substitute_lookup (hb_ot_layout_t              *layout,
+				hb_buffer_t                 *buffer,
+			        unsigned int                 lookup_index,
+				hb_ot_layout_feature_mask_t  mask);
+
+
+
+
+
+
+
+
+
+
 
 /*
 #define PANGO_OT_ALL_GLYPHS			((guint) 0xFFFF)
diff --git a/src/main.cc b/src/main.cc
index b149e11..4c24ff4 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -125,10 +125,10 @@
 		  feature.get_lookup_count());
 
 	  int num_lookups = feature.get_lookup_count ();
-	  printf ("        %d lookup(s) found in language system\n", num_lookups);
+	  printf ("        %d lookup(s) found in feature\n", num_lookups);
 	  for (int n_lookup = 0; n_lookup < num_lookups; n_lookup++) {
 	    unsigned int lookup_index = feature.get_lookup_index (n_lookup);
-	    printf ("        Feature index %2d of %2d: %d\n", n_lookup, num_lookups,
+	    printf ("        Lookup index %2d of %2d: %d\n", n_lookup, num_lookups,
 	            feature.get_lookup_index (n_lookup));
 	  }
 	}