diff --git a/coriolis-data b/coriolis-data
index 00f8c742..48477d80 160000
--- a/coriolis-data
+++ b/coriolis-data
@@ -1 +1 @@
-Subproject commit 00f8c7423fac9660884c0eecdea74846dc6c7d53
+Subproject commit 48477d8037af3dc219de0a100878453334d54ddc
diff --git a/coriolis.py b/coriolis.py
old mode 100644
new mode 100755
index 4a559c15..9dc6af02
--- a/coriolis.py
+++ b/coriolis.py
@@ -1,4 +1,7 @@
+#!/usr/bin/python
+#
 # Export ship loadout in Coriolis format
+#
 
 from collections import OrderedDict
 import cPickle
@@ -66,9 +69,10 @@ weaponmount_map = {
 
 # Modules that have a name as well as a group
 bulkheads       = outfitting.armour_map.values()
-scanners        = [x[0] for x in outfitting.misc_internal_map.values()]
-countermeasures = [x[0] for x in outfitting.countermeasure_map.values()]
-fixup_map = {
+fixup_map = {}
+fixup_map.update({ x[0] : ('Scanner', x[0]) for x in outfitting.misc_internal_map.values() })
+fixup_map.update({ x[0] : ('Countermeasure', x[0]) for x in outfitting.countermeasure_map.values() })
+fixup_map.update({
     'Advanced Plasma Accelerator'   : ('Plasma Accelerator', 'Advanced Plasma Accelerator'),
     'Cytoscrambler Burst Laser'     : ('Burst Laser', 'Cytoscrambler'),
     'Enforcer Cannon'               : ('Multi-cannon', 'Enforcer'),
@@ -77,10 +81,11 @@ fixup_map = {
     'Multi-Cannon'                  : ('Multi-cannon', None),
     'Pacifier Frag-Cannon'          : ('Fragment Cannon', 'Pacifier'),
     'Pack-Hound Missile Rack'       : ('Missile Rack', 'Pack-Hound'),
-    'Pulse Disruptor Laser'         : ('Pulse Laser', 'Distruptor'),	# Note sp
-    'Shock Mine Launcher'           : ('Mine Launcher', 'Shock Mine Launcher'),		# XXX
+    'Pulse Disruptor Laser'         : ('Pulse Laser', 'Disruptor'),
+    'Retributor Beam Laser'         : ('Beam Laser', 'Retributor'),
+    'Shock Mine Launcher'           : ('Mine Launcher', 'Shock Mine Launcher'),
     'Standard Docking Computer'     : ('Docking Computer', 'Standard Docking Computer'),
-}
+})
 
 
 # Ship masses
@@ -153,12 +158,6 @@ def export(data, filename=None):
                 if module['name'] in fixup_map:
                     thing['group'], name = fixup_map[module['name']]
                     if name: thing['name'] = name
-                elif module['name'] in scanners:
-                    thing['group'] = 'Scanner'
-                    thing['name'] = module['name']
-                elif module['name'] in countermeasures:
-                    thing['group'] = 'Countermeasure'
-                    thing['name'] = module['name']
                 else:
                     thing['group'] = module['name']
 
@@ -226,3 +225,106 @@ def export(data, filename=None):
     filename = join(config.get('outdir'), '%s.%s.json' % (ship, time.strftime('%Y-%m-%dT%H.%M.%S', time.localtime(querytime))))
     with open(filename, 'wt') as h:
         h.write(string)
+
+
+#
+# build ship and module databases from https://github.com/cmmcleod/coriolis-data
+#
+if __name__ == "__main__":
+    import json
+    data = json.load(open('coriolis-data/dist/index.json'))
+
+    # Map Coriolis's names to names displayed in the in-game shipyard
+    coriolis_ship_map = {
+        'Cobra Mk III' : 'Cobra MkIII',
+        'Cobra Mk IV'  : 'Cobra MkIV',
+        'Viper'        : 'Viper MkIII',
+        'Viper Mk IV'  : 'Viper MkIV',
+    }
+
+    # From https://github.com/cmmcleod/coriolis/blob/master/src/app/shipyard/Constants.js
+    ModuleGroupToName = {
+        # Standard
+        'pp'  : 'Power Plant',
+        't'   : 'Thrusters',
+        'fsd' : 'Frame Shift Drive',
+        'ls'  : 'Life Support',
+        'pd'  : 'Power Distributor',
+        's'   : 'Sensors',
+        'ft'  : 'Fuel Tank',
+        'pas' : 'Planetary Approach Suite',
+
+        # Internal
+        'fs'  : 'Fuel Scoop',
+        'sc'  : 'Scanner',
+        'am'  : 'Auto Field-Maintenance Unit',
+        'bsg' : 'Bi-Weave Shield Generator',
+        'cr'  : 'Cargo Rack',
+        'fi'  : 'Frame Shift Drive Interdictor',
+        'hb'  : 'Hatch Breaker Limpet Controller',
+        'hr'  : 'Hull Reinforcement Package',
+        'rf'  : 'Refinery',
+        'scb' : 'Shield Cell Bank',
+        'sg'  : 'Shield Generator',
+        'pv'  : 'Planetary Vehicle Hangar',
+        'psg' : 'Prismatic Shield Generator',
+        'dc'  : 'Docking Computer',
+        'fx'  : 'Fuel Transfer Limpet Controller',
+        'pc'  : 'Prospector Limpet Controller',
+        'cc'  : 'Collector Limpet Controller',
+
+        # Hard Points
+        'bl'  : 'Beam Laser',
+        'ul'  : 'Burst Laser',
+        'c'   : 'Cannon',
+        'cs'  : 'Cargo Scanner',
+        'cm'  : 'Countermeasure',
+        'fc'  : 'Fragment Cannon',
+        'ws'  : 'Frame Shift Wake Scanner',
+        'kw'  : 'Kill Warrant Scanner',
+        'nl'  : 'Mine Launcher',
+        'ml'  : 'Mining Laser',
+        'mr'  : 'Missile Rack',
+        'pa'  : 'Plasma Accelerator',
+        'mc'  : 'Multi-cannon',
+        'pl'  : 'Pulse Laser',
+        'rg'  : 'Rail Gun',
+        'sb'  : 'Shield Booster',
+        'tp'  : 'Torpedo Pylon'
+    };
+
+    specials = { v:k for k,v in fixup_map.items() }
+
+    ships = {}
+    modules = {}
+
+    # Ship and armour masses
+    for m in data['Ships'].values():
+        name = coriolis_ship_map.get(m['properties']['name'], str(m['properties']['name']))
+        ships[name] = { 'hullMass' : m['properties']['hullMass'] }
+        for i in range(len(bulkheads)):
+            modules[(bulkheads[i], name, '1', 'I')] = { 'mass': m['bulkheads'][i]['mass'] }
+    cPickle.dump(ships, open('ships.p', 'wb'), protocol = cPickle.HIGHEST_PROTOCOL)
+
+    # Module masses
+    for cat in data['Modules'].values():
+        for grp, mlist in cat.iteritems():
+            for m in mlist:
+                key = ('name' in m and specials[(ModuleGroupToName[grp], m['name'])] or specials.get((ModuleGroupToName[grp], None), ModuleGroupToName[grp]),
+                       None,
+                       str(m['class']),
+                       str(m['rating']))
+                if key in modules:
+                    # Test our assumption that mount and guidance don't affect mass
+                    assert modules[key]['mass'] == m.get('mass', 0), '%s !=\n%s' % (key, m)
+                elif grp == 'fsd':
+                    modules[key] = { 'mass'      : m['mass'],
+                                     'optmass'   : m['optmass'],
+                                     'maxfuel'   : m['maxfuel'],
+                                     'fuelmul'   : m['fuelmul'],
+                                     'fuelpower' : m['fuelpower'],
+                    }
+                else:
+                    modules[key] = { 'mass': m.get('mass', 0) }	# Some modules don't have mass
+    cPickle.dump(modules, open('modules.p', 'wb'), protocol = cPickle.HIGHEST_PROTOCOL)
+
diff --git a/eddb.py b/eddb.py
index cc233ed8..003aa67e 100755
--- a/eddb.py
+++ b/eddb.py
@@ -31,8 +31,9 @@ class EDDB:
         return (station_id, bool(flags & EDDB.HAS_MARKET), bool(flags & EDDB.HAS_OUTFITTING), bool(flags & EDDB.HAS_SHIPYARD))
 
 
-# build databases from files systems.json, stations.json and modules.json from http://eddb.io/api
-# and from https://github.com/cmmcleod/coriolis-data
+#
+# build databases from files systems.json and stations.json from http://eddb.io/api
+#
 if __name__ == "__main__":
     import json
 
@@ -64,72 +65,3 @@ if __name__ == "__main__":
          (EDDB.HAS_SHIPYARD   if x['has_shipyard']   else 0)))
                         for x in stations])
     cPickle.dump(station_ids, open('stations.p', 'wb'), protocol = cPickle.HIGHEST_PROTOCOL)
-
-
-    # Map eddb's names to names displayed in the in-game shipyard
-    eddb_ship_map = {
-        'Sidewinder Mk. I' : 'Sidewinder',
-        'Eagle Mk. II'     : 'Eagle',
-        'Cobra Mk. III'    : 'Cobra MkIII',
-        'Cobra MK IV'      : 'Cobra MkIV',
-        'Viper Mk III'     : 'Viper MkIII',
-        'Viper MK IV'      : 'Viper MkIV',
-    }
-
-    # PP modules (see weapon-map in outfitting.py)
-    specials = {
-        'Retributor'       : 'Retributor Beam Laser',
-        'Pack-Hound'       : 'Pack-Hound Missile Rack',
-        'Mining Lance'     : 'Mining Lance Beam Laser',
-        'Enforcer'         : 'Enforcer Cannon',
-        'Advanced'         : 'Advanced Plasma Accelerator',
-        'Distruptor'       : 'Pulse Disruptor Laser',
-        'Cytoscrambler'    : 'Cytoscrambler Burst Laser',
-        'Imperial Hammer'  : 'Imperial Hammer Rail Gun',
-        'Pacifier'         : 'Pacifier Frag-Cannon',
-        'Prismatic'        : 'Prismatic Shield Generator',
-    }
-
-    # Module masses
-    modules = {}
-    for m in json.load(open('modules.json')):
-        # ignore mount and guidance, and convert strings to ascii to save space
-        key = (specials.get(m['name'], str(m['name'] or m['group']['name'])),
-               m['ship'] and eddb_ship_map.get(m['ship'], str(m['ship'])),
-               str(m['class']),
-               str(m['rating']))
-        if key in modules:
-            # Test our assumption that mount and guidance don't affect mass
-            assert modules[key]['mass'] == m.get('mass', 0), '%s !=\n%s' % (key, m)
-        else:
-            modules[key] = { 'mass': m.get('mass', 0) }	# Some modules don't have mass
-
-    # Add FSD data from Coriolis
-    for m in json.load(open('coriolis-data/components/standard/frame_shift_drive.json')).values():
-        key = ('Frame Shift Drive', None, str(m['class']), str(m['rating']))
-        assert key in modules, key
-        modules[key].update({
-            'optmass'   : m['optmass'],
-            'maxfuel'   : m['maxfuel'],
-            'fuelmul'   : m['fuelmul'],
-            'fuelpower' : m['fuelpower'],
-        })
-    cPickle.dump(modules, open('modules.p', 'wb'), protocol = cPickle.HIGHEST_PROTOCOL)
-
-
-    # Map Coriolis's names to names displayed in the in-game shipyard
-    coriolis_ship_map = {
-        'Cobra Mk III' : 'Cobra MkIII',
-        'Cobra Mk IV'  : 'Cobra MkIV',
-        'Viper'        : 'Viper MkIII',
-        'Viper Mk IV'  : 'Viper MkIV',
-    }
-
-    # Ship masses
-    ships = {}
-    for f in os.listdir('coriolis-data/ships'):
-        if not f.endswith('.json'): continue
-        for m in json.load(open(join('coriolis-data/ships', f))).values():
-            ships[coriolis_ship_map.get(m['properties']['name'], str(m['properties']['name']))] = { 'hullMass' : m['properties']['hullMass'] }
-    cPickle.dump(ships, open('ships.p', 'wb'), protocol = cPickle.HIGHEST_PROTOCOL)
-
diff --git a/modules.p b/modules.p
index 49761e7b..c6358e9c 100644
Binary files a/modules.p and b/modules.p differ
diff --git a/outfitting.py b/outfitting.py
index b7bbb814..d1b3f90d 100644
--- a/outfitting.py
+++ b/outfitting.py
@@ -1,3 +1,4 @@
+from collections import OrderedDict
 import cPickle
 from os.path import join
 import time
@@ -8,13 +9,13 @@ from config import config
 
 # Map API module names to in-game names
 
-armour_map = {
-    'grade1'   : 'Lightweight Alloy',
-    'grade2'   : 'Reinforced Alloy',
-    'grade3'   : 'Military Grade Composite',
-    'mirrored' : 'Mirrored Surface Composite',
-    'reactive' : 'Reactive Surface Composite',
-}
+armour_map = OrderedDict([
+    ('grade1',   'Lightweight Alloy'),
+    ('grade2',   'Reinforced Alloy'),
+    ('grade3',   'Military Grade Composite'),
+    ('mirrored', 'Mirrored Surface Composite'),
+    ('reactive', 'Reactive Surface Composite'),
+])
 
 weapon_map = {
     'advancedtorppylon'              : 'Torpedo Pylon',
@@ -288,7 +289,7 @@ def lookup(module, ship_map):
         new['category'] = 'utility'
         new['name'] = utility_map[len(name)>4 and (name[1],name[4]) or name[1]]
         if not name[2].startswith('size') or not name[3].startswith('class'): raise AssertionError('%s: Unknown class/rating "%s/%s"' % (module['id'], name[2], name[3]))
-        new['class'] = name[2][4:]
+        new['class'] = str(name[2][4:])
         new['rating'] = rating_map[name[3][5:]]
 
     elif name[0]=='hpt':
@@ -319,7 +320,7 @@ def lookup(module, ship_map):
             raise AssertionError('%s: Unknown module "%s"' % (module['id'], name[1]))
 
         if not name[2].startswith('size') or not name[3].startswith('class'): raise AssertionError('%s: Unknown class/rating "%s/%s"' % (module['id'], name[2], name[3]))
-        new['class'] = name[2][4:]
+        new['class'] = str(name[2][4:])
         new['rating'] = (name[1]=='buggybay' and planet_rating_map or rating_map)[name[3][5:]]
 
     # Disposition of fitted modules
diff --git a/ships.p b/ships.p
index e1a1674b..02a270c1 100644
--- a/ships.p
+++ b/ships.p
@@ -1,5 +1,5 @@
-�}q(UFederal Assault Ship}qUhullMassqM�sU
-Viper MkIVq}qhK�sUFederal Gunship}qhMDsUViper MkIIIq}qhK<sUImperial Eagle}q	hK2sUImperial Cutter}q
-hMLsUEagle}qhK2sUPython}qhM^sUType-6 Transporter}q
hK�sUFer-de-Lance}qhK�sUVulture}qhK�sUAdder}qhK#sUType-7 Transporter}qhM�sUFederal Corvette}qhM�sUDiamondback Scout}qhK�sUHauler}qhKsUDiamondback Explorer}qhM*sUFederal Dropship}qhMDsUCobra MkIIIq}qhK�sUOrca}qhMDsU
-Sidewinder}qhKsUAsp Explorer}qhMsUType-9 Heavy}qhM�sU
-Cobra MkIVq}qhK�sUImperial Courier}qhK#sUAnaconda}q hM�sUImperial Clipper}q!hM�sUKeelback}q"hK�sU	Asp Scout}q#hK�su.
\ No newline at end of file
+�}q(UFederal Assault Shipq}qUhullMassqM�sU
+Viper MkIVq}qhK�sUFederal Gunshipq}qhMDsUViper MkIIIq	}q
+hK<sUImperial Eagleq}qhK2sUImperial Cutterq
}qhMLsUEagleq}qhK2sUPythonq}qhM^sUType-6 Transporterq}qhK�sUFer-de-Lanceq}qhK�sUVultureq}qhK�sUAdderq}qhK#sUType-7 Transporterq}qhM�sUFederal Corvetteq}qhM�sUDiamondback Scoutq}q hK�sUAsp Explorerq!}q"hMsUDiamondback Explorerq#}q$hM*sUFederal Dropshipq%}q&hMDsUCobra MkIIIq'}q(hK�sUOrcaq)}q*hMDsU
+Sidewinderq+}q,hKsUHaulerq-}q.hKsUType-9 Heavyq/}q0hM�sU
+Cobra MkIVq1}q2hK�sUImperial Courierq3}q4hK#sUAnacondaq5}q6hM�sUImperial Clipperq7}q8hM�sUKeelbackq9}q:hK�sU	Asp Scoutq;}q<hK�su.
\ No newline at end of file