diff --git a/src/Scripts/MageMesh.py b/src/Scripts/MageMesh.py
new file mode 100644
index 0000000000000000000000000000000000000000..065c12340b1b8a4ac2a6481d251f464acc6858b1
--- /dev/null
+++ b/src/Scripts/MageMesh.py
@@ -0,0 +1,289 @@
+# MageMesh.py -- Pamhyr
+# Copyright (C) 2023  INRAE
+#
+# 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 3 of the License, 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.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# -*- coding: utf-8 -*-
+
+import os
+import logging
+
+from ctypes import (
+    cdll,
+    byref, Structure,
+    c_char_p, c_wchar_p,
+    create_string_buffer,
+    POINTER, c_void_p,
+    c_int, c_double, c_bool
+)
+
+from Scripts.AScript import AScript
+
+logger = logging.getLogger()
+
+bief_lib = None
+c_init_bief_from_geo_file = None
+c_get_nb_sections = None
+c_get_nb_points_section = None
+c_set_bief_name = None
+c_st_to_m_compl = None
+c_interpolate_profils_pas_transversal = None
+c_update_pk = None
+c_output_bief = None
+
+##########################
+# Binding initialisation #
+##########################
+
+
+def init_clib(lib_path):
+    global bief_lib
+    bief_lib = cdll.LoadLibrary(lib_path)
+
+    init_c_init_bief_from_geo_file(bief_lib)
+    init_c_get_nb_sections(bief_lib)
+    init_c_get_nb_points_section(bief_lib)
+    init_c_set_bief_name(bief_lib)
+    init_c_st_to_m_compl(bief_lib)
+    init_c_interpolate_profils_pas_transversal(bief_lib)
+    init_c_update_pk(bief_lib)
+    init_c_output_bief(bief_lib)
+
+
+def init_c_init_bief_from_geo_file(bief_lib):
+    global c_init_bief_from_geo_file
+
+    c_init_bief_from_geo_file = getattr(
+        bief_lib, 'c_init_bief_from_geo_file'
+    )
+    c_init_bief_from_geo_file.argtypes = [
+        c_char_p, POINTER(c_int), POINTER(c_int)
+    ]
+    c_init_bief_from_geo_file.restype = None
+
+
+def init_c_get_nb_sections(bief_lib):
+    global c_get_nb_sections
+
+    c_get_nb_sections = getattr(bief_lib, 'c_get_nb_sections')
+    c_get_nb_sections.argtypes = [POINTER(c_int)]
+    c_get_nb_sections.restype = None
+
+
+def init_c_get_nb_points_section(bief_lib):
+    global c_get_nb_points_section
+
+    c_get_nb_points_section = getattr(bief_lib, 'c_get_nb_points_section')
+    c_get_nb_points_section.argtypes = [POINTER(c_int), POINTER(c_int)]
+    c_get_nb_points_section.restype = None
+
+
+def init_c_set_bief_name(bief_lib):
+    global c_set_bief_name
+
+    c_set_bief_name = getattr(bief_lib, 'c_set_bief_name')
+    c_set_bief_name.argtypes = [c_char_p]
+    c_set_bief_name.restype = None
+
+
+def init_c_st_to_m_compl(bief_lib):
+    global c_st_to_m_compl
+
+    c_st_to_m_compl = getattr(bief_lib, 'c_st_to_m_compl')
+    c_st_to_m_compl.argtypes = [POINTER(c_int), c_char_p, c_char_p]
+    c_st_to_m_compl.restype = None
+
+
+def init_c_interpolate_profils_pas_transversal(bief_lib):
+    global c_interpolate_profils_pas_transversal
+
+    c_interpolate_profils_pas_transversal = getattr(
+        bief_lib, 'c_interpolate_profils_pas_transversal'
+    )
+    c_interpolate_profils_pas_transversal.argtypes = [
+        POINTER(c_int), POINTER(c_int),
+        c_char_p, c_char_p,
+        POINTER(c_double), POINTER(c_bool),
+        POINTER(c_int), POINTER(c_bool)
+    ]
+    c_interpolate_profils_pas_transversal.restype = None
+
+
+def init_c_update_pk(bief_lib):
+    global c_update_pk
+
+    c_update_pk = getattr(bief_lib, 'c_update_pk')
+    c_update_pk.argtypes = [c_char_p]
+    c_update_pk.restype = None
+
+
+def init_c_output_bief(bief_lib):
+    global c_output_bief
+
+    c_output_bief = getattr(bief_lib, 'c_output_bief')
+    c_output_bief.argtypes = [c_char_p]
+    c_output_bief.restype = None
+
+
+def init_bief_from_geo_file(name, with_charriage, with_water):
+    logger.info("! call init_bief_from_geo_file:")
+    cname = create_string_buffer(name.encode())
+    c_init_bief_from_geo_file(
+        cname,
+        byref(c_int(with_charriage)),
+        byref(c_int(with_water))
+    )
+
+#####################
+# Binding functions #
+#####################
+
+
+def get_nb_sections():
+    nb_sections = c_int(0)
+    c_get_nb_sections(byref(nb_sections))
+    return nb_sections.value
+
+
+def get_nb_points_section(section):
+    nb_points = c_int(0)
+    c_get_nb_points_section(byref(c_int(section)), byref(nb_points))
+    return nb_points.value
+
+
+def set_bief_name(name):
+    logger.info(f"! call set_bief_name: {repr(name)}")
+    cname = create_string_buffer(name.encode())
+    c_set_bief_name(cname)
+
+
+def st_to_m_compl(npoints, tag1='   ', tag2='   '):
+    logger.info(f"! call st_to_m_compl: {npoints}")
+    cnpoints = c_int(npoints)
+    ctag1 = create_string_buffer(tag1.encode())
+    ctag2 = create_string_buffer(tag2.encode())
+
+    c_st_to_m_compl(byref(cnpoints), ctag1, ctag2)
+
+
+def interpolate_profils_pas_transversal(limite1, limite2,
+                                        directrice1, directrice2,
+                                        pas, lplan=False,
+                                        lm=3, lineaire=False):
+    logger.info("! call interpolate_profils_pas_transversal:")
+    climite1 = c_int(limite1)
+    climite2 = c_int(limite2)
+    cpas = c_double(pas)
+    clplan = c_bool(lplan)
+    clm = c_int(lm)
+    clineaire = c_bool(lineaire)
+    cdirectrice1 = create_string_buffer(directrice1.encode())
+    cdirectrice2 = create_string_buffer(directrice2.encode())
+
+    c_interpolate_profils_pas_transversal(
+        byref(climite1), byref(climite2),
+        cdirectrice1, cdirectrice2,
+        byref(cpas), byref(clplan),
+        byref(clm), byref(clineaire)
+    )
+
+
+def update_pk(directrice):
+    logger.info("! call update_pk:")
+    cdirectrice = create_string_buffer(directrice.encode())
+    c_update_pk(cdirectrice)
+
+
+def output_bief(name):
+    logger.info("! call output_bief:")
+    cname = create_string_buffer(name.encode())
+    c_output_bief(cname)
+
+
+##########
+# Script #
+##########
+
+
+class MageMesh(AScript):
+    name = "MageMesh"
+    description = "Mesh ST file to M"
+
+    def usage(self):
+        logger.info(
+            f"Usage : {self._args[0]} {self._args[1]} " +
+            "<SO_FILE> <ST_FILE> <STEP>"
+        )
+
+    def run(self):
+        if len(self._args) < 5:
+            return 1
+
+        try:
+            so = self._args[2]
+            st = self._args[3]
+            step = float(self._args[4])
+        except Exception as e:
+            logger.error(f"Argument format error: {e}")
+            return 2
+
+        try:
+            init_clib(so)
+        except Exception as e:
+            logger.error(f"Bindings failed: {e}")
+            return 3
+
+        self.meshing(st, step)
+
+        return 0
+
+    def meshing(self, st_file: str, step: float):
+        workdir = self.get_workdir(st_file)
+        file_name = st_file.rsplit(".", 1)[0]
+        reach_name = os.path.basename(file_name)
+
+        # Open
+        init_bief_from_geo_file(st_file, 0, 0)
+        set_bief_name(reach_name)
+
+        ns = get_nb_sections()
+        npts_max = max(
+            map(
+                lambda i: get_nb_points_section(i),
+                range(1, ns)
+            )
+        )
+
+        # Transform
+        st_to_m_compl(npts_max, 'rg', 'rd')
+        interpolate_profils_pas_transversal(
+            1, ns,
+            'un', 'np',
+            step
+        )
+        update_pk("un")
+
+        # Save
+        logger.info(f"Saved meshing geometry to {file_name}.M")
+        output_bief(f"{file_name}.M")
+
+    def get_workdir(self, file):
+        workdir = os.path.abspath(
+            os.path.dirname(file)
+        )
+
+        os.makedirs(workdir, exist_ok=True)
+        logger.info(f"Set working dir to {workdir}")
+
+        return workdir
diff --git a/src/View/MainWindow.py b/src/View/MainWindow.py
index ac1bef1299b96168bebc1b1f60608476dc0fe32d..e48b4803ab86d0aeac5419f84a9d3c934d2b3032 100644
--- a/src/View/MainWindow.py
+++ b/src/View/MainWindow.py
@@ -435,7 +435,7 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
             "", "Pamhyr(*.pamhyr)"
         )
 
-        if  file_name.rsplit(".", 1)[-1] == ".pamhyr":
+        if file_name.rsplit(".", 1)[-1] == ".pamhyr":
             self._study.filename = file_name
         else:
             self._study.filename = file_name + ".pamhyr"