diff --git a/.github/SECURITY.md b/.github/SECURITY.md
new file mode 100644
index 00000000..a926715b
--- /dev/null
+++ b/.github/SECURITY.md
@@ -0,0 +1,13 @@
+# Reporting Security Issues
+
+EDMC takes security very seriously. Our users trust us to provide a secure and safe tool to support their experience in Elite.
+
+In general, the best way to report a major security issue with us that should not be publically discussed is to email our maintainer teams. 
+
+The best point of contact for this is edmc@hullseals.space. When contacting, be sure to include as much information in your report. 
+
+As soon as your report is processed, we'll get in touch to make sure we quickly move ahead with fixing the issue and will lay out a timeline for public disclosure and fixes.
+
+Another method of reporting vulnerabilities is to open a new Bug Report [here](https://github.com/EDCD/EDMarketConnector/issues/new?assignees=&labels=bug%2C+unconfirmed&projects=&template=bug_report.md&title=).
+
+If reporting a security issue here, do not include details as to the issue or steps to reproduce, simply indicate you have found a potential security bug and would like us to contact you directly.
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 00000000..0d1c9ac1
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,18 @@
+<!---
+Thank you for submitting a PR for EDMC! Please follow this template to ensure your PR is processed.
+In general, you should be submitting targeting the develop branch. Please make sure you have this selected.
+-->
+# Description
+<!-- What does this PR Do? -->
+
+# Example Images
+<!-- Only if relevant. Remove if irrelevant. -->
+
+# Type of Change
+<!-- What type of change is this? New Feature? Enhancement to Existing Feature? Bug Fix? Translation Update? -->
+
+# How Tested
+<!-- How have you tested this change to ensure it works and doesn't cause new issues -->
+
+# Notes
+<!-- Does this resolve any open issues? Was this PR discussed internally somewhere off GitHub? -->
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 00000000..a709c7dc
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,95 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+#
+# ******** NOTE ********
+# We have attempted to detect the languages in your repository. Please check
+# the `language` matrix defined below to confirm you have the correct set of
+# supported CodeQL languages.
+#
+name: "CodeQL"
+
+on:
+  push:
+    branches-ignore:
+      - 'stable'
+      - 'releases'
+      - 'beta'
+  pull_request:
+    branches: [ develop ]
+  schedule:
+    - cron: '38 5 * * 4'
+
+jobs:
+  analyze:
+    name: Analyze (${{ matrix.language }})
+    # Runner size impacts CodeQL analysis time. To learn more, please see:
+    #   - https://gh.io/recommended-hardware-resources-for-running-codeql
+    #   - https://gh.io/supported-runners-and-hardware-resources
+    #   - https://gh.io/using-larger-runners (GitHub.com only)
+    # Consider using larger runners or machines with greater resources for possible analysis time improvements.
+    runs-on: 'ubuntu-latest'
+    timeout-minutes: 360
+    permissions:
+      # required for all workflows
+      security-events: write
+
+      # required to fetch internal or private CodeQL packs
+      packages: read
+
+      # only required for workflows in private repositories
+      actions: read
+      contents: read
+
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+        - language: python
+          build-mode: none
+        # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
+        # Use `c-cpp` to analyze code written in C, C++ or both
+        # Use 'java-kotlin' to analyze code written in Java, Kotlin or both
+        # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
+        # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
+        # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
+        # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
+        # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v4
+
+    # Initializes the CodeQL tools for scanning.
+    - name: Initialize CodeQL
+      uses: github/codeql-action/init@v3
+      with:
+        languages: ${{ matrix.language }}
+        build-mode: ${{ matrix.build-mode }}
+        # If you wish to specify custom queries, you can do so here or in a config file.
+        # By default, queries listed here will override any specified in a config file.
+        # Prefix the list here with "+" to use these queries and those in the config file.
+
+        # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
+        # queries: security-extended,security-and-quality
+
+    # If the analyze step fails for one of the languages you are analyzing with
+    # "We were unable to automatically build your code", modify the matrix above
+    # to set the build mode to "manual" for that language. Then modify this step
+    # to build your code.
+    # ℹ️ Command-line programs to run using the OS shell.
+    # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
+    - if: matrix.build-mode == 'manual'
+      run: |
+        echo 'If you are using a "manual" build mode for one or more of the' \
+          'languages you are analyzing, replace this with the commands to build' \
+          'your code, for example:'
+        echo '  make bootstrap'
+        echo '  make release'
+        exit 1
+
+    - name: Perform CodeQL Analysis
+      uses: github/codeql-action/analyze@v3
+      with:
+        category: "/language:${{matrix.language}}"
diff --git a/ChangeLog.md b/ChangeLog.md
index ebaf4d71..7aa9871e 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -6,6 +6,29 @@ This is the master changelog for Elite Dangerous Market Connector.  Entries are
       in the source (not distributed with the Windows installer) for the
       currently used version.
 ---
+Release 5.10.5
+===
+This release contains a fix for a bug that could crash EDMC's console versions when reading outfitting information
+from the new SCO Frame Shift Drive modules. 
+
+Please note that this does not offer full support for the new SCO modules or the Python Mk II. More support will
+be added in a future update.
+
+**Changes and Enhancements**
+* Updated Translations
+* Added limited data regarding the Python Mk II
+* Added a few Coriolis module information entries
+
+**Bug Fixes**
+* Fixed a bug that could cause the new SCO modules to display improper ratings or sizes
+* Fixed a bug where the new SCO modules would display as a normal Frame Shift Drive
+* Fixed a bug which could crash EDMC if the exact details of a Frame Shift Drive were unknown
+
+**Plugin Developers**
+* modules.p and ships.p are deprecated, and slated for removal in 5.11+!
+* The `openurl()` function in ttkHyperlinkLabel has been deprecated,
+and slated for removal in 5.11+! Please migrate to `webbrowser.open()`.
+
 Release 5.10.4
 ===
 This release contains updated dependencies, modules files, translations, and adds two new EDDN schemas. It also 
diff --git a/FDevIDs b/FDevIDs
index 7cffab3d..9b3f4061 160000
--- a/FDevIDs
+++ b/FDevIDs
@@ -1 +1 @@
-Subproject commit 7cffab3d913b788f981923687203399c22cf358f
+Subproject commit 9b3f40612017b43a8b826017e1e2befebd9074f2
diff --git a/L10n/ja.strings b/L10n/ja.strings
index 82d9f8df..75acb252 100644
--- a/L10n/ja.strings
+++ b/L10n/ja.strings
@@ -231,6 +231,18 @@
 /* EDMarketConnector.py: Popup-text about 'broken' plugins that failed to load; In files: EDMarketConnector.py:2266; */
 "One or more of your enabled plugins failed to load. Please see the list on the '{PLUGINS}' tab of '{FILE}' > '{SETTINGS}'. This could be caused by a wrong folder structure. The load.py file should be located under plugins/PLUGIN_NAME/load.py.\r\n\r\nYou can disable a plugin by renaming its folder to have '{DISABLED}' on the end of the name." = "有効にしている1つ以上のプラグインがロードに失敗しました。'{FILE}' > '{SETTINGS}' メニューで表示される設定ダイアログの'{PLUGINS}' タブの一覧を確認してください。この問題は誤ったフォルダ構造によって引き起こされます。load.pyファイルはplugins/プラグイン名/plug-in.pyとして配置される必要があります。\n\nプラグインを無効にするにはフォルダ名の最後に'{DISABLED}'を追加してください。";
 
+/* EDMarketConnector.py: Popup-text about Reset Providers; In files: EDMarketConnector.py:2146; */
+"One or more of your URL Providers were invalid, and have been reset:\r\n\r\n" = "一つ以上のURLプロバイダが無効であったためリセットされました:\n\n";
+
+/* EDMarketConnector.py: Text About What Provider Was Reset; In files: EDMarketConnector.py:2148; */
+"{PROVIDER} was set to {OLDPROV}, and has been reset to {NEWPROV}\r\n" = "{PROVIDER} は {OLDPROV} に設定され、 {NEWPROV} にリセットされました\n";
+
+/* EDMarketConnector.py: Popup window title for Reset Providers; In files: EDMarketConnector.py:2161; */
+"EDMC: Default Providers Reset" = "EDMC: デフォルトプロバイダーのリセット";
+
+/* EDMarketConnector.py: Await Full CMDR Login to Game; In files: EDMarketConnector.py:813; */
+"Awaiting Full CMDR Login" = "完全なCMDRログインを待機しています";
+
 /* journal_lock.py: Title text on popup when Journal directory already locked; In files: journal_lock.py:208; */
 "Journal directory already locked" = "ジャーナルディレクトリは既にロックされています";
 
@@ -789,3 +801,5 @@
 /* stats.py: Status dialog title; In files: stats.py:418; */
 "Ships" = "所有船";
 
+/* update.py: Update Available Text; In files: update.py:229; */
+"{NEWVER} is available" = "{NEWVER} があります";
diff --git a/L10n/pl.strings b/L10n/pl.strings
index 547406b1..efde05be 100644
--- a/L10n/pl.strings
+++ b/L10n/pl.strings
@@ -213,12 +213,36 @@
 /* EDMarketConnector.py: Popup-text about 'active' plugins without Python 3.x support; In files: EDMarketConnector.py:2253:2259; */
 "One or more of your enabled plugins do not yet have support for Python 3.x. Please see the list on the '{PLUGINS}' tab of '{FILE}' > '{SETTINGS}'. You should check if there is an updated version available, else alert the developer that they need to update the code for Python 3.x.\r\n\r\nYou can disable a plugin by renaming its folder to have '{DISABLED}' on the end of the name." = "Co najmniej jeden z uruchomionych pluginów nie wspiera Python 3.x. Sprawdź listę w '{FILE}' > '{SETTINGS}', sekcja '{PLUGINS}'. Upewnij się, że dostępna jest aktualna wersja pluginu, w przeciwnym razie poinformuj twórcę, że należy zaktualizować kod do wersji Python 3.x.\n\nMożesz wyłączyć plugin, dodając '{DISABLED}' na koniec nazwy jego folderu.";
 
+/* EDMarketConnector.py: Popup-text about missing FDEVID Files; In files: EDMarketConnector.py:2329; */
+"FDevID Files not found! Some functionality regarding commodities may be disabled.\r\n\r\n Do you want to open the Wiki page on how to set up submodules?" = "Pliki FDevID nie znalezione. Część funkcjonalności związanej z materiałami może nie działać. \n\nChcesz otworzyć stronę Wiki dotyczacą konfiguracji podmodułów?";
+
+/* EDMarketConnector.py: Popup window title for missing FDEVID files; In files: EDMarketConnector.py:2340; */
+"FDevIDs: Missing Commodity Files" = "FDevIDs: Brakuje plików z towarami.";
+
 /* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2263; prefs.py:986; */
 "Plugins" = "Pluginy";
 
 /* EDMarketConnector.py: Popup window title for list of 'enabled' plugins that don't work with Python 3.x; In files: EDMarketConnector.py:2274; */
 "EDMC: Plugins Without Python 3.x Support" = "EDMC: Pluginy Nie Wspierające Python 3.x";
 
+/* EDMarketConnector.py: Popup window title for list of 'broken' plugins that failed to load; In files: EDMarketConnector.py:2285; */
+"EDMC: Broken Plugins" = "EDMC: Uszkodzone pluginy";
+
+/* EDMarketConnector.py: Popup-text about 'broken' plugins that failed to load; In files: EDMarketConnector.py:2266; */
+"One or more of your enabled plugins failed to load. Please see the list on the '{PLUGINS}' tab of '{FILE}' > '{SETTINGS}'. This could be caused by a wrong folder structure. The load.py file should be located under plugins/PLUGIN_NAME/load.py.\r\n\r\nYou can disable a plugin by renaming its folder to have '{DISABLED}' on the end of the name." = "Jeden lub więcej plugin nie został załadowany. Sprawdź listę na zakładce '{PLUGINS}' w menu '{FILE}' > '{SETTINGS}'. Może to być spowodowane błedną strukturą katalogów. Plik load.py powinien być umiezczony w plugins/NAZWA PLUGINA/load.py.\n\nMożesz wyłączyć plugin zmieniając nazwę jego folderu tak, aby zawierała '{DISABLED}' na końcu nazwy.";
+
+/* EDMarketConnector.py: Popup-text about Reset Providers; In files: EDMarketConnector.py:2146; */
+"One or more of your URL Providers were invalid, and have been reset:\r\n\r\n" = "Jeden lub więcej adres URL jest błędny i został zresetowany:\n";
+
+/* EDMarketConnector.py: Text About What Provider Was Reset; In files: EDMarketConnector.py:2148; */
+"{PROVIDER} was set to {OLDPROV}, and has been reset to {NEWPROV}\r\n" = "{PROVIDER} ustawiony na {OLDPROV} i został zresetowany do {NEWPROV}\n";
+
+/* EDMarketConnector.py: Popup window title for Reset Providers; In files: EDMarketConnector.py:2161; */
+"EDMC: Default Providers Reset" = "EDMC: Dostawca domyślny zresetowany";
+
+/* EDMarketConnector.py: Await Full CMDR Login to Game; In files: EDMarketConnector.py:813; */
+"Awaiting Full CMDR Login" = "Oczekiwanie na pełne zalogowanie do gry";
+
 /* journal_lock.py: Title text on popup when Journal directory already locked; In files: journal_lock.py:208; */
 "Journal directory already locked" = "Katalog dziennika zablokowany";
 
@@ -471,6 +495,9 @@
 /* prefs.py: Plugins - Label on URL to documentation about migrating plugins from Python 2.7; In files: prefs.py:962; */
 "Information on migrating plugins" = "Informacja o migracji pluginów";
 
+/* prefs.py: Plugins - Label for list of 'broken' plugins that failed to load; In files: prefs.py:1039; */
+"Broken Plugins" = "Niedziałające pluginy";
+
 /* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:977; */
 "Disabled Plugins" = "Pluginy wyłączone";
 
@@ -774,3 +801,5 @@
 /* stats.py: Status dialog title; In files: stats.py:418; */
 "Ships" = "Statki";
 
+/* update.py: Update Available Text; In files: update.py:229; */
+"{NEWVER} is available" = "Dostępna nowa wersja {NEWVER}";
diff --git a/L10n/pt-BR.strings b/L10n/pt-BR.strings
index 605e0559..b4d9e933 100644
--- a/L10n/pt-BR.strings
+++ b/L10n/pt-BR.strings
@@ -213,12 +213,36 @@
 /* EDMarketConnector.py: Popup-text about 'active' plugins without Python 3.x support; In files: EDMarketConnector.py:2253:2259; */
 "One or more of your enabled plugins do not yet have support for Python 3.x. Please see the list on the '{PLUGINS}' tab of '{FILE}' > '{SETTINGS}'. You should check if there is an updated version available, else alert the developer that they need to update the code for Python 3.x.\r\n\r\nYou can disable a plugin by renaming its folder to have '{DISABLED}' on the end of the name." = "Um ou mais dos plugins habilitados não possuem suporte ao Python 3.x. Você pode ver a lista na guia '{PLUGINS}' em '{FILE}' > '{SETTINGS}'. Você deve verificar se existe versão atualizada ou então avisar o desenvolvedor de que ele precisa atualizar o código para o Python 3.x.\n\nVocê pode desabilitar um plugin renomeando sua pasta para que seu nome termine com '{DISABLED}'.";
 
+/* EDMarketConnector.py: Popup-text about missing FDEVID Files; In files: EDMarketConnector.py:2329; */
+"FDevID Files not found! Some functionality regarding commodities may be disabled.\r\n\r\n Do you want to open the Wiki page on how to set up submodules?" = "Arquivo FDevID não encontrados! Algumas funções de mercadorias podem estar indisponíveis.\n\nGostaria de abrir a Wiki com instruções para configurar sub-módulos?";
+
+/* EDMarketConnector.py: Popup window title for missing FDEVID files; In files: EDMarketConnector.py:2340; */
+"FDevIDs: Missing Commodity Files" = "FDevIDs: Arquivos de Mercadorias não encontrados";
+
 /* EDMarketConnector.py: Settings > Plugins tab; prefs.py: Label on Settings > Plugins tab; In files: EDMarketConnector.py:2263; prefs.py:986; */
 "Plugins" = "Plugins";
 
 /* EDMarketConnector.py: Popup window title for list of 'enabled' plugins that don't work with Python 3.x; In files: EDMarketConnector.py:2274; */
 "EDMC: Plugins Without Python 3.x Support" = "EDMC: Plugins sem Suporte ao Python 3.x";
 
+/* EDMarketConnector.py: Popup window title for list of 'broken' plugins that failed to load; In files: EDMarketConnector.py:2285; */
+"EDMC: Broken Plugins" = "EDMC: Plugins Quebrados";
+
+/* EDMarketConnector.py: Popup-text about 'broken' plugins that failed to load; In files: EDMarketConnector.py:2266; */
+"One or more of your enabled plugins failed to load. Please see the list on the '{PLUGINS}' tab of '{FILE}' > '{SETTINGS}'. This could be caused by a wrong folder structure. The load.py file should be located under plugins/PLUGIN_NAME/load.py.\r\n\r\nYou can disable a plugin by renaming its folder to have '{DISABLED}' on the end of the name." = "Um ou mais plugins ativados falhou ao carregar. Verifique a lista na aba '{PLUGINS}', em '{FILE}' > '{SETTINGS}'. Isto pode ter ocorrido por um erro na estrutura de pastas. O arquivo load.py deve estar localizado em plugins/NOME_PLUGIN/load.py.\n\nVocê pode desativar plugins renomeando a pasta para ter '.{DISABLED}' ao final do nome.";
+
+/* EDMarketConnector.py: Popup-text about Reset Providers; In files: EDMarketConnector.py:2146; */
+"One or more of your URL Providers were invalid, and have been reset:\r\n\r\n" = "Um ou mais dos seus Provedores de URL eram inválidos e portanto foram reconfigurados:\n";
+
+/* EDMarketConnector.py: Text About What Provider Was Reset; In files: EDMarketConnector.py:2148; */
+"{PROVIDER} was set to {OLDPROV}, and has been reset to {NEWPROV}\r\n" = "{PROVIDER} estava como {OLDPROV}, e foi reconfigurado para {NEWPROV}\n";
+
+/* EDMarketConnector.py: Popup window title for Reset Providers; In files: EDMarketConnector.py:2161; */
+"EDMC: Default Providers Reset" = "EDMC: Provedores Padrão Reconfigurados";
+
+/* EDMarketConnector.py: Await Full CMDR Login to Game; In files: EDMarketConnector.py:813; */
+"Awaiting Full CMDR Login" = "Aguardando CMDT entrar no jogo";
+
 /* journal_lock.py: Title text on popup when Journal directory already locked; In files: journal_lock.py:208; */
 "Journal directory already locked" = "Diretório de Jornais já está bloqueado";
 
@@ -471,6 +495,9 @@
 /* prefs.py: Plugins - Label on URL to documentation about migrating plugins from Python 2.7; In files: prefs.py:962; */
 "Information on migrating plugins" = "Informações de migração de plugins";
 
+/* prefs.py: Plugins - Label for list of 'broken' plugins that failed to load; In files: prefs.py:1039; */
+"Broken Plugins" = "Plugins Quebrados";
+
 /* prefs.py: Lable on list of user-disabled plugins; In files: prefs.py:977; */
 "Disabled Plugins" = "Plugins desabilitados";
 
@@ -774,3 +801,5 @@
 /* stats.py: Status dialog title; In files: stats.py:418; */
 "Ships" = "Naves";
 
+/* update.py: Update Available Text; In files: update.py:229; */
+"{NEWVER} is available" = "{NEWVER} está disponível";
diff --git a/config/__init__.py b/config/__init__.py
index 1dfe26c3..92347c0b 100644
--- a/config/__init__.py
+++ b/config/__init__.py
@@ -54,7 +54,7 @@ appcmdname = 'EDMC'
 # <https://semver.org/#semantic-versioning-specification-semver>
 # Major.Minor.Patch(-prerelease)(+buildmetadata)
 # NB: Do *not* import this, use the functions appversion() and appversion_nobuild()
-_static_appversion = '5.10.4'
+_static_appversion = '5.10.5'
 _cached_version: semantic_version.Version | None = None
 copyright = '© 2015-2019 Jonathan Harris, 2020-2024 EDCD'
 
diff --git a/coriolis-data b/coriolis-data
index 05b16a4c..8adfd86b 160000
--- a/coriolis-data
+++ b/coriolis-data
@@ -1 +1 @@
-Subproject commit 05b16a4c716980ea95a46d29205f7d3b1f957fb4
+Subproject commit 8adfd86b64e8c14e873d2f5123d88ca6743420b9
diff --git a/edmc_data.py b/edmc_data.py
index b7ed4556..6c2e809a 100644
--- a/edmc_data.py
+++ b/edmc_data.py
@@ -357,7 +357,7 @@ outfitting_standard_map = {
     'guardianpowerdistributor':     'Guardian Hybrid Power Distributor',
     'guardianpowerplant':           'Guardian Hybrid Power Plant',
     'hyperdrive':                   'Frame Shift Drive',
-    ('hyperdrive', 'overcharge'):   'Frame Shift Drive',
+    ('hyperdrive', 'overcharge'):   'Frame Shift Drive (SCO)',
     'lifesupport':                  'Life Support',
     # 'planetapproachsuite':        handled separately
     'powerdistributor':             'Power Distributor',
@@ -501,6 +501,7 @@ ship_name_map = {
     'mamba':                        'Mamba',
     'orca':                         'Orca',
     'python':                       'Python',
+    'python_nx':                    'Python Mk II',
     'scout':                        'Taipan Fighter',
     'sidewinder':                   'Sidewinder',
     'testbuggy':                    'Scarab',
diff --git a/edshipyard.py b/edshipyard.py
index 1660ad7e..15936dfc 100644
--- a/edshipyard.py
+++ b/edshipyard.py
@@ -106,7 +106,7 @@ def export(data, filename=None) -> None:  # noqa: C901, CCR001
             else:
                 name = module['name']  # type: ignore
 
-            if name == 'Frame Shift Drive':
+            if name == 'Frame Shift Drive' or name == 'Frame Shift Drive (SCO)':
                 fsd = module  # save for range calculation
 
                 if mods.get('OutfittingFieldType_FSDOptimalMass'):
@@ -167,15 +167,19 @@ def export(data, filename=None) -> None:  # noqa: C901, CCR001
     try:
         mass += ships[ship_name_map[data['ship']['name'].lower()]]['hullMass']
         string += f'Mass  : {mass:.2f} T empty\n        {mass + fuel + cargo:.2f} T full\n'
+        maxfuel = fsd.get('maxfuel', 0)  # type: ignore
+        fuelmul = fsd.get('fuelmul', 0)  # type: ignore
 
-        multiplier = pow(min(fuel, fsd['maxfuel']) / fsd['fuelmul'], 1.0  # type: ignore
-                         / fsd['fuelpower']) * fsd['optmass']  # type: ignore
-
-        range_unladen = multiplier / (mass + fuel) + jumpboost
-        range_laden = multiplier / (mass + fuel + cargo) + jumpboost
-        # As of 2021-04-07 edsy.org says text import not yet implemented, so ignore the possible issue with
-        # a locale that uses comma for decimal separator.
-        string += f'Range : {range_unladen:.2f} LY unladen\n        {range_laden:.2f} LY laden\n'
+        try:
+            multiplier = pow(min(fuel, maxfuel) / fuelmul, 1.0 / fsd['fuelpower']) * fsd['optmass']  # type: ignore
+            range_unladen = multiplier / (mass + fuel) + jumpboost
+            range_laden = multiplier / (mass + fuel + cargo) + jumpboost
+            # As of 2021-04-07 edsy.org says text import not yet implemented, so ignore the possible issue with
+            # a locale that uses comma for decimal separator.
+        except ZeroDivisionError:
+            range_unladen = range_laden = 0.0
+        string += (f'Range : {range_unladen:.2f} LY current without cargo\n'
+                   f'        {range_laden:.2f} LY current with cargo\n')
 
     except Exception:
         if __debug__:
diff --git a/modules.json b/modules.json
index 27e54626..16e72f7f 100644
--- a/modules.json
+++ b/modules.json
@@ -326,6 +326,9 @@
     "hpt_antiunknownshutdown_tiny": {
         "mass": 1.3
     },
+    "hpt_antiunknownshutdown_tiny_v2": {
+        "mass": 3
+    },
     "hpt_atdumbfiremissile_fixed_large": {
         "mass": 8
     },
@@ -446,6 +449,9 @@
     "hpt_causticmissile_fixed_medium": {
         "mass": 4
     },
+    "hpt_causticsinklauncher_turret_tiny": {
+        "mass": 1.7
+    },
     "hpt_chafflauncher_tiny": {
         "mass": 1.3
     },
@@ -833,6 +839,9 @@
     "hpt_slugshot_turret_small": {
         "mass": 2
     },
+    "hpt_xenoscanner_advanced_tiny": {
+        "mass": 3
+    },
     "hpt_xenoscanner_basic_tiny": {
         "mass": 1.3
     },
@@ -1352,6 +1361,12 @@
     "int_engine_size8_class5": {
         "mass": 160
     },
+    "int_expmodulestabiliser_size3_class3": {
+        "mass": 8
+    },
+    "int_expmodulestabiliser_size5_class3": {
+        "mass": 20
+    },
     "int_fighterbay_size5_class1": {
         "mass": 20
     },
diff --git a/outfitting.py b/outfitting.py
index d290ad3b..6314d3da 100644
--- a/outfitting.py
+++ b/outfitting.py
@@ -224,7 +224,7 @@ def lookup(module, ship_map, entitled=False) -> dict | None:  # noqa: C901, CCR0
             (new['class'], new['rating']) = (str(name[2][4:]), 'H')
 
         elif len(name) > 4 and name[1] == 'hyperdrive':  # e.g. Int_Hyperdrive_Overcharge_Size6_Class3
-            (new['class'], new['rating']) = (str(name[4][-1:]), 'C')
+            (new['class'], new['rating']) = (str(name[3][-1:]), rating_map[name[4][-1:]])
 
         else:
             if len(name) < 3:
@@ -259,7 +259,7 @@ def lookup(module, ship_map, entitled=False) -> dict | None:  # noqa: C901, CCR0
         if not m:
             print(f'No data for module {key}')
 
-        elif new['name'] == 'Frame Shift Drive':
+        elif new['name'] == 'Frame Shift Drive' or new['name'] == 'Frame Shift Drive (SCO)':
             assert 'mass' in m and 'optmass' in m and 'maxfuel' in m and 'fuelmul' in m and 'fuelpower' in m, m
 
         else:
diff --git a/ships.json b/ships.json
index 4a40ba4f..65a2989e 100644
--- a/ships.json
+++ b/ships.json
@@ -89,6 +89,9 @@
     "Python": {
         "hullMass": 350
     },
+    "Python Mk II": {
+        "hullMass": 450
+    },
     "Sidewinder": {
         "hullMass": 25
     },