diff --git a/.gitignore b/.gitignore
index 865b8c0..3ca4900 100644
--- a/.gitignore
+++ b/.gitignore
@@ -103,3 +103,5 @@ venv.bak/
 
 # mypy
 .mypy_cache/
+
+edl_config.json
\ No newline at end of file
diff --git a/Drivers/51-edl.rules b/Drivers/51-edl.rules
index 455af78..42d3e95 100755
--- a/Drivers/51-edl.rules
+++ b/Drivers/51-edl.rules
@@ -2,6 +2,10 @@
 # Qualcomm EDL
 SUBSYSTEMS=="usb", ATTRS{idVendor}=="05c6", ATTRS{idProduct}=="9008", MODE="0666", GROUP="plugdev"
 
+# Sony EDL
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="9dde", MODE="0666", GROUP="plugdev"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="ade5", MODE="0666", GROUP="plugdev"
+
 # Qualcomm Memory Debug
 SUBSYSTEMS=="usb", ATTRS{idVendor}=="05c6", ATTRS{idProduct}=="9006", MODE="0666", GROUP="plugdev"
 
diff --git a/Drivers/Windows/Oneplus_9008_Driver/QUSB__BULK.cat b/Drivers/Windows/Oneplus_9008_Driver/QUSB__BULK.cat
deleted file mode 100644
index 09a1a07..0000000
Binary files a/Drivers/Windows/Oneplus_9008_Driver/QUSB__BULK.cat and /dev/null differ
diff --git a/Drivers/Windows/Oneplus_9008_Driver/QUSB__BULK.inf b/Drivers/Windows/Oneplus_9008_Driver/QUSB__BULK.inf
deleted file mode 100644
index 71fe777..0000000
Binary files a/Drivers/Windows/Oneplus_9008_Driver/QUSB__BULK.inf and /dev/null differ
diff --git a/Drivers/Windows/Oneplus_9008_Driver/amd64/WdfCoInstaller01009.dll b/Drivers/Windows/Oneplus_9008_Driver/amd64/WdfCoInstaller01009.dll
deleted file mode 100644
index 1731b96..0000000
Binary files a/Drivers/Windows/Oneplus_9008_Driver/amd64/WdfCoInstaller01009.dll and /dev/null differ
diff --git a/Drivers/Windows/Oneplus_9008_Driver/amd64/libusb0.dll b/Drivers/Windows/Oneplus_9008_Driver/amd64/libusb0.dll
deleted file mode 100644
index f916b08..0000000
Binary files a/Drivers/Windows/Oneplus_9008_Driver/amd64/libusb0.dll and /dev/null differ
diff --git a/Drivers/Windows/Oneplus_9008_Driver/amd64/libusbK.dll b/Drivers/Windows/Oneplus_9008_Driver/amd64/libusbK.dll
deleted file mode 100644
index d8112b9..0000000
Binary files a/Drivers/Windows/Oneplus_9008_Driver/amd64/libusbK.dll and /dev/null differ
diff --git a/Drivers/Windows/Oneplus_9008_Driver/amd64/libusbK.sys b/Drivers/Windows/Oneplus_9008_Driver/amd64/libusbK.sys
deleted file mode 100644
index e09ee12..0000000
Binary files a/Drivers/Windows/Oneplus_9008_Driver/amd64/libusbK.sys and /dev/null differ
diff --git a/Drivers/Windows/Oneplus_9008_Driver/desktop.ini b/Drivers/Windows/Oneplus_9008_Driver/desktop.ini
deleted file mode 100644
index bb9f3d6..0000000
--- a/Drivers/Windows/Oneplus_9008_Driver/desktop.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[ViewState]
-Mode=
-Vid=
-FolderType=Generic
diff --git a/Drivers/Windows/Oneplus_9008_Driver/dpinst.xml b/Drivers/Windows/Oneplus_9008_Driver/dpinst.xml
deleted file mode 100644
index cc2988e..0000000
--- a/Drivers/Windows/Oneplus_9008_Driver/dpinst.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<!--
-Summary of the DPInst XML Elements:
-  http://msdn.microsoft.com/en-us/library/windows/hardware/ff553383%28v=vs.85%29.aspx
-  
-Enabling Language Customization:
-  http://msdn.microsoft.com/en-us/library/windows/hardware/ff544886%28v=vs.85%29.aspx
-
-Customizing the Items That Appear on the Wizard Pages:
-  http://msdn.microsoft.com/en-us/library/windows/hardware/ff540265%28v=vs.85%29.aspx
-  
-Dpinst.xml Example:
-  http://msdn.microsoft.com/en-us/library/windows/hardware/ff544778%28v=vs.85%29.aspx
--->
-
-<?xml version="1.0"?>
-<dpinst>
-	<forceIfDriverIsNotBetter>1</forceIfDriverIsNotBetter>
-	<installAllOrNone>1</installAllOrNone>
-</dpinst>
diff --git a/Drivers/Windows/Oneplus_9008_Driver/dpinst32.exe b/Drivers/Windows/Oneplus_9008_Driver/dpinst32.exe
deleted file mode 100644
index 410a135..0000000
Binary files a/Drivers/Windows/Oneplus_9008_Driver/dpinst32.exe and /dev/null differ
diff --git a/Drivers/Windows/Oneplus_9008_Driver/dpinst64.exe b/Drivers/Windows/Oneplus_9008_Driver/dpinst64.exe
deleted file mode 100644
index 0096441..0000000
Binary files a/Drivers/Windows/Oneplus_9008_Driver/dpinst64.exe and /dev/null differ
diff --git a/Drivers/Windows/Oneplus_9008_Driver/dpinst64.vbs b/Drivers/Windows/Oneplus_9008_Driver/dpinst64.vbs
deleted file mode 100644
index b8b2f94..0000000
--- a/Drivers/Windows/Oneplus_9008_Driver/dpinst64.vbs
+++ /dev/null
@@ -1,2 +0,0 @@
-Set oShell = CreateObject("WScript.Shell")
-oShell.Run("""dpinst64.exe """ & "/SW")
\ No newline at end of file
diff --git a/Drivers/Windows/Oneplus_9008_Driver/dpscat.exe b/Drivers/Windows/Oneplus_9008_Driver/dpscat.exe
deleted file mode 100644
index c45f430..0000000
Binary files a/Drivers/Windows/Oneplus_9008_Driver/dpscat.exe and /dev/null differ
diff --git a/Drivers/Windows/Oneplus_9008_Driver/dpscat.vbs b/Drivers/Windows/Oneplus_9008_Driver/dpscat.vbs
deleted file mode 100644
index ce1cc81..0000000
--- a/Drivers/Windows/Oneplus_9008_Driver/dpscat.vbs
+++ /dev/null
@@ -1,2 +0,0 @@
-Set oShell = CreateObject("WScript.Shell")
-oShell.Run("""dpscat.exe""")
diff --git a/Drivers/Windows/Oneplus_9008_Driver/test.vbs b/Drivers/Windows/Oneplus_9008_Driver/test.vbs
deleted file mode 100644
index b015768..0000000
--- a/Drivers/Windows/Oneplus_9008_Driver/test.vbs
+++ /dev/null
@@ -1,2 +0,0 @@
-Set oShell = CreateObject("WScript.Shell")
-oShell.Run("""dpinst64.exe /SW""")
\ No newline at end of file
diff --git a/Drivers/Windows/Oneplus_9008_Driver/x86/WdfCoInstaller01009.dll b/Drivers/Windows/Oneplus_9008_Driver/x86/WdfCoInstaller01009.dll
deleted file mode 100644
index 30e81af..0000000
Binary files a/Drivers/Windows/Oneplus_9008_Driver/x86/WdfCoInstaller01009.dll and /dev/null differ
diff --git a/Drivers/Windows/Oneplus_9008_Driver/x86/libusb0_x86.dll b/Drivers/Windows/Oneplus_9008_Driver/x86/libusb0_x86.dll
deleted file mode 100644
index 6e475b9..0000000
Binary files a/Drivers/Windows/Oneplus_9008_Driver/x86/libusb0_x86.dll and /dev/null differ
diff --git a/Drivers/Windows/Oneplus_9008_Driver/x86/libusbK.sys b/Drivers/Windows/Oneplus_9008_Driver/x86/libusbK.sys
deleted file mode 100644
index 67b8ff4..0000000
Binary files a/Drivers/Windows/Oneplus_9008_Driver/x86/libusbK.sys and /dev/null differ
diff --git a/Drivers/Windows/Oneplus_9008_Driver/x86/libusbK_x86.dll b/Drivers/Windows/Oneplus_9008_Driver/x86/libusbK_x86.dll
deleted file mode 100644
index 0d41062..0000000
Binary files a/Drivers/Windows/Oneplus_9008_Driver/x86/libusbK_x86.dll and /dev/null differ
diff --git a/Drivers/Windows/libusb-1.0.26-binaries.7z b/Drivers/Windows/libusb-1.0.26-binaries.7z
new file mode 100644
index 0000000..7b002a1
Binary files /dev/null and b/Drivers/Windows/libusb-1.0.26-binaries.7z differ
diff --git a/Drivers/Windows/zadig-2.8.exe b/Drivers/Windows/zadig-2.8.exe
new file mode 100644
index 0000000..1e530ad
Binary files /dev/null and b/Drivers/Windows/zadig-2.8.exe differ
diff --git a/LICENSE b/LICENSE
index b4b94db..782d5be 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,21 +1,674 @@
-MIT License
+					 GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
 
-Copyright (c) 2018 Bjoern Kerler
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
 
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
+                            Preamble
 
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
 
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
\ No newline at end of file
diff --git a/README.md b/README.md
index b95ec97..119aab6 100755
--- a/README.md
+++ b/README.md
@@ -1,5 +1,9 @@
 # Qualcomm Sahara / Firehose Attack Client / Diag Tools
-(c) B. Kerler 2018-2022
+(c) B. Kerler 2018-2024
+Licensed under GPLv3 license.
+
+# Be aware that if you use anything from this repository in any (including) compiled form, you need to opensource your code as well !
+# Violating against the GPLv3 license will enforce me to stop developing these opensource tools.
 
 ## Why
 
@@ -7,6 +11,10 @@
 - Because attacking firehose is kewl
 - Because memory dumping helps to find issues :)
 
+## QC Sahara V3 additional information for newer QC devices
+- For newer qc phones, loader autodetection doesn't work anymore as the sahara loader doesn't offer a way to read the pkhash anymore
+- Thus, for Sahara V3, you need to give a valid loader via --loader option !
+  
 ### Use LiveDVD (everything ready to go, based on Ubuntu):
 User: user, Password:user (based on Ubuntu 22.04 LTS)
 
@@ -16,6 +24,14 @@ User: user, Password:user (based on Ubuntu 22.04 LTS)
 
 ## Installation
 
+#### Grab files and install
+```
+git clone https://github.com/bkerler/edl
+cd edl
+git submodule update --init --recursive
+pip3 install -r requirements.txt
+```
+
 ### Linux (Debian/Ubuntu/Mint/etc): 
 ```bash
 # Debian/Ubuntu/Mint/etc
@@ -37,10 +53,12 @@ cd edl
 git submodule update --init --recursive
 sudo cp Drivers/51-edl.rules /etc/udev/rules.d
 sudo cp Drivers/50-android.rules /etc/udev/rules.d
-python setup.py build
-sudo python setup.py install
+python3 setup.py build
+sudo python3 setup.py install
 ```
 
+If you have SELinux enabled, you may need to set it to permissive mode temporarily to prevent permission issues. SELinux is commonly used by RedHat-like distros (for example, RHEL, Fedora, and CentOS). You can set it to permissive run-time until next boot with `sudo setenforce 0`.
+
 ### macOS:
 ```bash
 brew install libusb git
@@ -48,8 +66,8 @@ brew install libusb git
 git clone https://github.com/bkerler/edl.git
 cd edl
 git submodule update --init --recursive
-python setup.py build
-sudo python setup.py install
+python3 setup.py build
+sudo python3 setup.py install
 ```
 
 ### Windows:
@@ -58,13 +76,6 @@ sudo python setup.py install
 - If you install python from microsoft store, "python setup.py install" will fail, but that step isn't required.
 - WIN+R ```cmd```
 
-#### Grab files and install
-```
-git clone https://github.com/bkerler/edl
-cd edl
-git submodule update --init --recursive
-pip3 install -r requirements.txt
-```
 
 #### Get latest UsbDk 64-Bit
 - Install normal QC 9008 Serial Port driver (or use default Windows COM Port one, make sure no exclamation is seen)
@@ -246,7 +257,7 @@ For Oneplus 6T, enter *#801#* on dialpad, set Engineer Mode and Serial to on and
 - Oneplus 3T/5/6T/7T/8/8t/9/Nord CE/N10/N100 (Read-Only), BQ X, BQ X5, BQ X2, Gigaset ME Pure, ZTE MF210, ZTE MF920V, Sierra Wireless EM7455, Netgear MR1100-10EUS, Netgear MR5100
 - SIMCOM SIM8905E
 
-Published under MIT license
+Published under GPLv3 license
 Additional license limitations: No use in commercial products without prior permit.
 
 Enjoy !
diff --git a/boottodownload b/boottodownload
new file mode 120000
index 0000000..a6c3aed
--- /dev/null
+++ b/boottodownload
@@ -0,0 +1 @@
+edlclient/Tools/boottodwnload
\ No newline at end of file
diff --git a/edl b/edl
index d0b10aa..fd77ae8 100755
--- a/edl
+++ b/edl
@@ -1,6 +1,11 @@
 #!/usr/bin/env python3
-# Qualcomm Sahara / Firehose Client (c) B.Kerler 2018-2021
-# Licensed under MIT License
+# -*- coding: utf-8 -*-
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
+
 """
 Usage:
     edl -h | --help
@@ -9,14 +14,14 @@ Usage:
     edl [--debugmode] [--portname=portname] [--serial]
     edl [--gpt-num-part-entries=number] [--gpt-part-entry-size=number] [--gpt-part-entry-start-lba=number] [--portname=portname] [--serial]
     edl [--memory=memtype] [--skipstorageinit] [--maxpayload=bytes] [--sectorsize==bytes] [--portname=portname] [--serial]
-    edl server [--tcpport=portnumber] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial]
-    edl memorydump [--partitions=partnames] [--debugmode] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]
-    edl printgpt [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode]  [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
-    edl gpt <directory> [--memory=memtype] [--lun=lun] [--genxml] [--loader=filename]  [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
-    edl r <partitionname> <filename> [--memory=memtype] [--sectorsize==bytes] [--lun=lun] [--loader=filename]  [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
-    edl rl <directory> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skip=partnames] [--genxml]  [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
-    edl rf <filename> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode]  [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
-    edl rs <start_sector> <sectors> <filename> [--lun=lun] [--sectorsize==bytes] [--memory=memtype] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
+    edl server [--tcpport=portnumber] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]  [--devicemodel=value]
+    edl memorydump [--partitions=partnames] [--debugmode] [--vid=vid] [--pid=pid] [--portname=portname] [--serial] [--serial_number=serial_number]
+    edl printgpt [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode]  [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] [--devicemodel=value]
+    edl gpt <directory> [--memory=memtype] [--lun=lun] [--genxml] [--loader=filename]  [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] [--devicemodel=value]
+    edl r <partitionname> <filename> [--memory=memtype] [--sectorsize==bytes] [--lun=lun] [--loader=filename]  [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] [--devicemodel=value]
+    edl rl <directory> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skip=partnames] [--genxml]  [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] [--devicemodel=value]
+    edl rf <filename> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode]  [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] [--devicemodel=value]
+    edl rs <start_sector> <sectors> <filename> [--lun=lun] [--sectorsize==bytes] [--memory=memtype] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] [--devicemodel=value]
     edl w <partitionname> <filename> [--partitionfilename=filename] [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skipwrite] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial]
     edl wl <directory> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skip=partnames] [--skipresponse] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial]
     edl wf <filename> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial]
@@ -24,7 +29,7 @@ Usage:
     edl e <partitionname> [--memory=memtype] [--skipwrite] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial]
     edl es <start_sector> <sectors> [--memory=memtype] [--lun=lun] [--sectorsize==bytes] [--skipwrite] [--loader=filename] [--skipresponse] [--debugmode] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial]
     edl ep <partitionname> <sectors> [--memory=memtype] [--skipwrite] [--lun=lun] [--sectorsize==bytes] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial]
-    edl footer <filename> [--memory=memtype] [--lun=lun] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
+    edl footer <filename> [--memory=memtype] [--lun=lun] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]  [--devicemodel=value]
     edl peek <offset> <length> <filename> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]
     edl peekhex <offset> <length> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]
     edl peekdword <offset> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
@@ -38,17 +43,18 @@ Usage:
     edl secureboot [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
     edl pbl <filename> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
     edl qfp <filename> [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
-    edl getstorageinfo [--loader=filename] [--memory=memtype] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
-    edl setbootablestoragedrive <lun> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]
-    edl setactiveslot <slot> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]
-    edl send <command> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]
-    edl xml <xmlfile> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial]
-    edl rawxml <xmlstring> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--skipstorageinit] [--portname=portname] [--serial]
-    edl reset [--resetmode=mode] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]
-    edl nop [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]
+    edl getstorageinfo [--loader=filename] [--memory=memtype] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]  [--devicemodel=value]
+    edl setbootablestoragedrive <lun> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]  [--devicemodel=value]
+    edl getactiveslot [--memory=memtype] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]  [--devicemodel=value]
+    edl setactiveslot <slot> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]  [--devicemodel=value]
+    edl send <command> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]  [--devicemodel=value]
+    edl xml <xmlfile> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]  [--devicemodel=value]
+    edl rawxml <xmlstring> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial]  [--devicemodel=value]
+    edl reset [--resetmode=mode] [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--portname=portname] [--serial] [--devicemodel=value]
+    edl nop [--loader=filename] [--debugmode] [--vid=vid] [--pid=pid] [--skipstorageinit] [--portname=portname] [--serial] [--devicemodel=value]
     edl modules <command> <options> [--memory=memtype] [--lun=lun] [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--devicemodel=value] [--portname=portname] [--serial]
-    edl provision <xmlfile> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]
-    edl qfil <rawprogram> <patch> <imagedir> [--loader=filename] [--memory=memtype] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]
+    edl provision <xmlfile> [--loader=filename] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]  [--devicemodel=value]
+    edl qfil <rawprogram> <patch> <imagedir> [--loader=filename] [--memory=memtype] [--debugmode] [--skipresponse] [--vid=vid] [--pid=pid] [--portname=portname] [--serial]  [--devicemodel=value]
 
 Description:
     server                      # Run tcp/ip server
@@ -96,8 +102,8 @@ Description:
 
 Options:
     --loader=filename                  Use specific EDL loader, disable autodetection [default: None]
-    --vid=vid                          Set usb vendor image_id used for EDL [default: -1]
-    --pid=pid                          Set usb product image_id used for EDL [default: -1]
+    --vid=vid                          Set usb vendor id used for EDL [default: -1]
+    --pid=pid                          Set usb product id used for EDL [default: -1]
     --lun=lun                          Set lun to read/write from (UFS memory only)
     --maxpayload=bytes                 Set the maximum payload for EDL [default: 0x100000]
     --sectorsize=bytes                 Set default sector size
@@ -142,13 +148,13 @@ from binascii import hexlify
 
 args = docopt(__doc__, version='3')
 
-print("Qualcomm Sahara / Firehose Client V3.60 (c) B.Kerler 2018-2022.")
+print("Qualcomm Sahara / Firehose Client V3.62 (c) B.Kerler 2018-2023.")
 
 
 def parse_cmd(rargs):
     cmds = ["server", "printgpt", "gpt", "r", "rl", "rf", "rs", "w", "wl", "wf", "ws", "e", "es", "ep", "footer",
             "peek", "peekhex", "peekdword", "peekqword", "memtbl", "poke", "pokehex", "pokedword", "pokeqword",
-            "memcpy", "secureboot", "pbl", "qfp", "getstorageinfo", "setbootablestoragedrive", "setactiveslot",
+            "memcpy", "secureboot", "pbl", "qfp", "getstorageinfo", "setbootablestoragedrive", "getactiveslot", "setactiveslot",
             "send", "xml", "rawxml", "reset", "nop", "modules", "memorydump", "provision", "qfil"]
     for cmd in cmds:
         if rargs[cmd]:
@@ -187,8 +193,9 @@ class main(metaclass=LogBase):
         self.sahara = None
         self.vid = None
         self.pid = None
+        self.serial_number = None
 
-    def doconnect(self, loop):
+    def doconnect(self, loop) -> dict:
         while not self.cdc.connected:
             self.cdc.connected = self.cdc.connect(portname=self.portname)
             if not self.cdc.connected:
@@ -226,32 +233,13 @@ class main(metaclass=LogBase):
         self.cdc.close()
         sys.exit()
 
-    def handle_streaming(self, mode):
-        self.cdc.timeout = None
-        sahara_info = self.sahara.streaminginfo()
-        if sahara_info:
-            mode, resp = self.sahara.connect()
-            if mode == "sahara":
-                mode = self.sahara.upload_loader()
-                if "enprg" in self.sahara.programmer.lower():
-                    mode = "load_enandprg"
-                elif "nprg" in self.sahara.programmer.lower():
-                    mode = "load_nandprg"
-                elif mode != "":
-                    mode = "load_" + mode
-                if "load_" in mode:
-                    time.sleep(0.3)
-                else:
-                    print("Error, couldn't find suitable enprg/nprg loader :(")
-                    self.exit()
-        return mode
-
     def run(self):
         if is_windows():
             proper_driver = console_cmd(r'reg query HKLM\HARDWARE\DEVICEMAP\SERIALCOMM')
             if re.findall(r'QCUSB', str(proper_driver)):
                 self.warning(f'Please first install libusb_win32 driver from Zadig')
 
+        mode = ""
         loop = 0
         vid = int(args["--vid"], 16)
         pid = int(args["--pid"], 16)
@@ -282,7 +270,9 @@ class main(metaclass=LogBase):
         if self.serial:
             self.cdc = serial_class(loglevel=self.__logger.level, portconfig=portconfig)
         else:
-            self.cdc = usb_class(portconfig=portconfig, loglevel=self.__logger.level)
+            if args["--serial_number"]:
+                self.serial_number = args["--serial_number"]
+            self.cdc = usb_class(portconfig=portconfig, loglevel=self.__logger.level, serial_number=self.serial_number)
 
         self.sahara = sahara(self.cdc, loglevel=self.__logger.level)
 
@@ -295,58 +285,64 @@ class main(metaclass=LogBase):
             self.sahara.programmer = loader
 
         self.info("Waiting for the device")
+        resp = None
         self.cdc.timeout = 1500
         conninfo = self.doconnect(loop)
         mode = conninfo["mode"]
+        if not "data" in conninfo:
+            version = 2
+        else:
+            version = conninfo["data"].version
         if mode == "sahara":
             cmd = conninfo["cmd"]
-            data = conninfo["data"]
             if cmd == cmd_t.SAHARA_HELLO_REQ:
-                if data.mode == sahara_mode_t.SAHARA_MODE_MEMORY_DEBUG:
-                    if args["memorydump"] or self.cdc.pid==0x900E:
-                        time.sleep(0.5)
-                        print("Device is in memory dump mode, dumping memory")
-                        if args["--partitions"]:
-                            self.sahara.debug_mode(args["--partitions"].split(","))
-                        else:
-                            self.sahara.debug_mode()
-                        self.exit()
-                    else:
-                        print("Device is in streaming mode, uploading loader")
-                        self.cdc.timeout = None
-                        sahara_info = self.sahara.streaminginfo()
-                        if sahara_info:
-                            sahara_connect = self.sahara.connect()
-                            if len(sahara_connect) == 3:
-                                mode, cmd, resp = sahara_connect
+                if "data" in conninfo:
+                    data = conninfo["data"]
+                    if data.mode == sahara_mode_t.SAHARA_MODE_MEMORY_DEBUG:
+                        if args["memorydump"] or self.cdc.pid == 0x900E:
+                            time.sleep(0.5)
+                            print("Device is in memory dump mode, dumping memory")
+                            if args["--partitions"]:
+                                self.sahara.debug_mode(args["--partitions"].split(","),version=version)
                             else:
-                                mode, resp = sahara_connect
-                            if mode == "sahara":
-                                mode = self.sahara.upload_loader()
-                                if "enprg" in self.sahara.programmer.lower():
-                                    mode = "load_enandprg"
-                                elif "nprg" in self.sahara.programmer.lower():
-                                    mode = "load_nandprg"
-                                elif mode != "":
-                                    mode = "load_" + mode
-                                if "load_" in mode:
-                                    time.sleep(0.3)
+                                self.sahara.debug_mode(version=version)
+                            self.exit()
+                        else:
+                            print("Device is in streaming mode, uploading loader")
+                            self.cdc.timeout = None
+                            sahara_info = self.sahara.streaminginfo()
+                            if sahara_info:
+                                sahara_connect = self.sahara.connect()
+                                if len(sahara_connect) == 3:
+                                    mode, cmd, resp = sahara_connect
                                 else:
-                                    print("Error, couldn't find suitable enprg/nprg loader :(")
-                                    self.exit()
-                else:
-                    sahara_info = self.sahara.cmd_info()
-                    if sahara_info is not None:
-                        resp = self.sahara.connect()
-                        mode = resp["mode"]
-                        if "data" in resp:
-                            data = resp["data"]
-                        if mode == "sahara":
-                            mode = self.sahara.upload_loader()
+                                    mode, resp = sahara_connect
+                                if mode == "sahara":
+                                    mode = self.sahara.upload_loader(version=version)
+                                    if "enprg" in self.sahara.programmer.lower():
+                                        mode = "load_enandprg"
+                                    elif "nprg" in self.sahara.programmer.lower():
+                                        mode = "load_nandprg"
+                                    elif mode != "":
+                                        mode = "load_" + mode
+                                    if "load_" in mode:
+                                        time.sleep(0.3)
+                                    else:
+                                        print("Error, couldn't find suitable enprg/nprg loader :(")
+                                        self.exit()
                     else:
-                        print("Error on sahara handshake, resetting.")
-                        self.sahara.cmd_reset()
-                        sys.exit(1)
+                        sahara_info = self.sahara.cmd_info(version=version)
+                        if sahara_info is not None:
+                            resp = self.sahara.connect()
+                            mode = resp["mode"]
+                            if "data" in resp:
+                                data = resp["data"]
+                            if mode == "sahara":
+                                mode = self.sahara.upload_loader(version=version)
+                        else:
+                            print("Error on sahara handshake, resetting.")
+                            self.sahara.cmd_reset()
+                            sys.exit(1)
         if mode == "error":
             print("Connection detected, quiting.")
             sys.exit(1)
diff --git a/edlclient/Config/qualcomm_config.py b/edlclient/Config/qualcomm_config.py
index abb1072..b3e7a41 100644
--- a/edlclient/Config/qualcomm_config.py
+++ b/edlclient/Config/qualcomm_config.py
@@ -1,3 +1,11 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
+
 vendor = {
     0x0000: "Qualcomm     ",
     0x0001: "Foxconn/Sony ",
@@ -37,11 +45,11 @@ vendor = {
 }
 
 root_cert_hash = {
-    "secboot_sha2_pss_subca1" : "afca69d4235117e5bfc21467068b20df85e0115d7413d5821883a6d244961581",
-    "secboot_sha2_pss_subca2" : "d40eee56f3194665574109a39267724ae7944134cd53cb767e293d3c40497955bc8a4519ff992b031fadc6355015ac87",
-    "old" : "cc3153a80293939b90d02d3bf8b23e0292e452fef662c74998421adad42a380f",
-    "new" : "7be49b72f9e4337223ccb84d6eccca4e61ce16e3602ac2008cb18b75babe6d09",
-    "mdm9x60_tel" : "36c886068d9a6634e9c55185044344e9e756dcc3b5960874942c7a1a1550dee0"
+    "secboot_sha2_pss_subca1": "afca69d4235117e5bfc21467068b20df85e0115d7413d5821883a6d244961581",
+    "secboot_sha2_pss_subca2": "d40eee56f3194665574109a39267724ae7944134cd53cb767e293d3c40497955bc8a4519ff992b031fadc6355015ac87",
+    "old": "cc3153a80293939b90d02d3bf8b23e0292e452fef662c74998421adad42a380f",
+    "secboot_sha2_root": "7be49b72f9e4337223ccb84d6eccca4e61ce16e3602ac2008cb18b75babe6d09",
+    "mdm9x60_tel": "36c886068d9a6634e9c55185044344e9e756dcc3b5960874942c7a1a1550dee0"
 }
 
 msmids = {
@@ -51,45 +59,59 @@ msmids = {
     0x9780E1: "IPQ4018",
     0x9790E1: "IPQ4019",
     0x0160E1: "QCA4020",
-    0x9680E1: "APQ8009",
+    0x9680E1: "APQ8009", # Snapdragon 212
     0x7060E1: "APQ8016",
     0x8100E1: "APQ806x",
     0x9D00E1: "APQ8076",
     0x08A0E1: "APQ807x",
     0x9000E1: "APQ8084",
+    0x9010E1: "APQ8084",  # SnapDragon 805
     0x9630E1: "APQ8092",
+    0x9410E1: "APQ8094",  # Snapdragon 810
     0x0940E1: "MSM8905",
-    0x9600E1: "MSM8909", # SnapDragon 210
+    0x9600E1: "MSM8909",  # SnapDragon 210
     0x0510E1: "MSM8909W",
-    0x7050E1: "MSM8916", # SnapDragon 410
-    0x0560E1: "MSM8917",
+    0x0520E1: "APQ8009W",  # wear3100
+    0x0960E1: "SDX24",  # 0x60020100 soc_hw_version, 0x8fff7000 dbgpolicy 32Bit, 0x8FCFD000 sec.elf 64Bit
+    0x0970E1: "SDX24M",  # 0x60020100 soc_hw_version, 0x8fff7000 dbgpolicy 32Bit, 0x8FCFD000 sec.elf 64Bit
+    0x7050E1: "MSM8916",  # SnapDragon 410
+    0x0560E1: "MSM8917",  # Snapdragon 425
     0x0860E1: "MSM8920",
     0x91B0E1: "MSM8929",  # SnapDragon 415
     0x04F0E1: "MSM8937",
-    0x90B0E1: "MSM8939",  # SnapDragon 610
+    0x90B0E1: "MSM8939",  # SnapDragon 615
     0x90C0E1: "APQ8036",
-    0x90D0E1: "APQ8039",
+    0x0500E1: "APQ8037",
+    0x90D0E1: "APQ8039",  # Snapdragon 615
     0x06B0E1: "MSM8940",
     0x9720E1: "MSM8952",  # SnapDragon 652
-    0x0460E1: "MSM8953",  # 8053lat
-    0x0660E1: "APQ8053",
+    0x0460E1: "MSM8953",  # Snapdragon 636
+    0x0660E1: "APQ8053",  # SnapDragon 652
     0x9900E1: "MSM8976",  # SnapDragon 652
-    0x9690E1: "MSM8992",  # SnapDragon 82x
+    0x9690E1: "MSM8992",  # SnapDragon 808
     0x9400E1: "MSM8994",  # SnapDragon 808
     0x9470E1: "MSM8996",  # SnapDragon 820
     0x06F0E1: "MSM8996AU",
+    0x0630E1: "MSM8996AU",
     0x05E0E1: "MSM8998_SDM835",
     0x94B0E1: "MSM9055",
+    0x7F00E1: "MDM8225",
+    0x7F30E1: "MDM8225M",
     0x9730E1: "MDM9206_MDM9607tx",
+    0x9530E1: "MDM9245M",
+    0x9200E1: "MDM9635", #Snapdragon X7
     0x04A0E1: "MDM9607",
+    0x9670E1: "MDM9609",
     0x8090E1: "MDM9916",
     0x80B0E1: "MDM9955",
     0x9210E1: "MDM9x35",
     0x9500E1: "MDM9x40",
     0x9540E1: "MDM9x45",
-    0x03A0E1: "MDM9x50",
-    0x7F50E1: "MDM9x25",
-    0x0320E1: "MDM9250",  # MDM9x50
+    0x03A0E1: "MDM9x50",  # MDM8650
+    0x7F50E1: "MDM9x25",  # Snapdragon X5
+    0x7F40E1: "MDM9625",  # Snapdragon X5
+    0x7F10E1: "MSM9225_1", # Snapdragon X5
+    0x0320E1: "MDM9250",  # MDM9x50, Snapdragon X16
     0x0340E1: "MDM9255",  # MDM9x55
     0x0390E1: "MDM9350",  # MDM9x50
     0x03B0E1: "MDM9x55",
@@ -102,18 +124,19 @@ msmids = {
     0x16A0E1: "FSM10051",
     0x16B0E1: "FSM10056",
     0x1530E1: "ipq5018",
-    0x1610E1: "olympic",
-    0x1720E1: "olympic_hybrid",
+    0x0C50E1: "sda439",
+    0x1610E1: "olympic_v1", # snapdragon 439, 0x6016 soc_hw, 0x8FCFD000 sec 32Bit, 0x8fff7000 dbg 32bit
+    0x1720E1: "olympic_v1_hybrid",
     0x1060E1: "qm215",
     0x0BE0E1: "SDM429",
     0x0BF0E1: "SDM439",
     0x09A0E1: "SDM450",
-    0x0AC0E1: "SDM630",  #0x30070x00
+    0x0AC0E1: "SDM630",  # 0x30070x00
     0x0BA0E1: "SDM632",
     0x0BB0E1: "SDA632",
     0x08C0E1: "SDM660",  # 0x30060000 soc_hw_version
     0x07B0E1: "SDX50M",  # 0x soc_hw_version,
-    0x0E50E1: "SDX55:CD90-PG591", # 0x600b0100 soc_hw_version, 0x8fff7000 dbgpolicy 32Bit, 0x8FCFD000 sec.elf 64Bit
+    0x0E50E1: "SDX55:CD90-PG591",  # 0x600b0100 soc_hw_version, 0x8fff7000 dbgpolicy 32Bit, 0x8FCFD000 sec.elf 64Bit
     0x0CF0E1: "SDX55M:CD90-PH809", # 0x600b0100 soc_hw_version, 0x8fff7000 dbgpolicy 32Bit, 0x8FCFD000 sec.elf 64Bit, # Netgear MR5100, sdxprairie
     0x1250E1: "SA515M",
 
@@ -130,33 +153,43 @@ msmids = {
     0x0DB0E1: "SDM710",
     0x0AA0E1: "QCS605",
     0x0ED0E1: "SXR1120",
-    0x0EA0E1: "SXR1130",
+    0x0EA0E1: "SXR1130", # QC VR/AR
     0x08E0E1: "SDA845",
+    0x1A60E1: "WCN7850", # hamilton, soc_hw 0x40170000,
+    0x1A70E1: "WCN7851", # hamilton, soc_hw 0x40170000
 
     # d40eee56f3194665574109a39267724ae7944134cd53cb767e293d3c40497955
     # d40eee56f3194665574109a39267724ae7944134cd53cb767e293d3c40497955bc8a4519ff992b031fadc6355015ac87 pk-hash/root-cert
     0x1260E1: "IPQ6018",
-    0x1070E1: "MDM9205", # 0x20130100
-    0x1450E1: "agatti", # soc_vers 0x9003
-    0x13F0E1: "bitra_SDM", # soc_vers 0x6012 SDM690
-    0x1410E1: "bitra_SDA",
-    0x1590E1: "cedros", # soc_vers 0x6017
-    0x1360E1: "kamorta", # soc_vers 0x9002 SnapDragon 460 SM4350
-    0x1370E1: "kamorta_P", # soc_vers 0x9002 SnapDragon 460 SM4350
-    0x1730E1: "kamorta_IoT_modem", # soc_vers 0x9002 SnapDragon 460 SM4350
-    0x1740E1: "kamorta_IoT_APQ", # soc_vers 0x9002 SnapDragon 460 SM4350
-    0x1350E1: "lahaina", # soc_vers 0x600F sm8350, SDM875
+    0x1070E1: "MDM9205",  # 0x20130100
+    0x1450E1: "agatti_mdm",  # soc_vers 0x9003
+    0x14F0E1: "agatti",
+    0x1850E1: "agatti_mdm_iot",
+    0x1860E1: "qcs2290",  # qcs_agatti_apq
+    0x13F0E1: "bitra_SDM",  # soc_vers 0x6012 SDM690
+    0x1410E1: "bitra_SDA", # Snapdragon 690 5G, smp_bitra
+    0x1590E1: "cedros",  # soc_vers 0x6017
+    0x1360E1: "kamorta",  # soc_vers 0x9002 SnapDragon 460 SM4350
+    0x1370E1: "kamorta_P",  # soc_vers 0x9002 SnapDragon 460 SM4350
+    0x1730E1: "kamorta_IoT_modem",  # soc_vers 0x9002 SnapDragon 460 SM4350
+    0x1740E1: "kamorta_IoT_APQ",  # soc_vers 0x9002 SnapDragon 460 SM4350
+    0x1C70E1: "kamorta_qrb",
+    0x1B80E1: "divar",  # Snapdragon 680 4G SM6225 Codename Divar, soc_vers 0x9007, 0x45FFF000 sec.elf 64 bit, 0x10000000 dbgpolicy 64 bit
+    0x1350E1: "lahaina",  # sd888, soc_vers 0x600F sm8350, SDM875
+    0x1520E1: "lahaina",  # sd888
+    0x19E0E1: "lahaina",  # vordonisi 
+    0x1A40E1: "Vordonisi",
     0x1420E1: "lahaina_premier",
-    0x14A0E1: "SC8280X", # soc_vers 0x6014, makena
+    0x14A0E1: "SC8280X",  # soc_vers 0x6014, makena
     0x14B0E1: "SA8295P",
-    0x14C0E1: "SA8540P",
-    0x16F0E1: "mannar", # soc_vers 0x9004
-    0x16E0E1: "mannar_P", # soc_vers 0x9004
-    0x1470E1: "moselle", # soc_vers 0x4014
+    0x14C0E1: "SA8540P", # Makena Adas
+    0x16F0E1: "mannar",  # soc_vers 0x9004, SnapDragon 480 5G SM4350
+    0x16E0E1: "mannar_P",  # soc_vers 0x9004
+    0x1470E1: "moselle",  # soc_vers 0x4014
     0x10A0E1: "nicobar",  # 0x90010100 soc_hw_version, 0x45FFF000 sec.elf 64Bit, 0x101FF000 dbgpolicy, 64Bit
     0x1750E1: "nicobar_IoT_modem",  # 0x90010100 soc_hw_version, 0x45FFF000 sec.elf 64Bit, 0x101FF000 dbgpolicy, 64Bit
     0x1760E1: "nicobar_IoT_APQ",  # 0x90010100 soc_hw_version, 0x45FFF000 sec.elf 64Bit, 0x101FF000 dbgpolicy, 64Bit
-    0x10B0E1: "QCN9000", # soc_vers 0x400D
+    0x10B0E1: "QCN9000",  # soc_vers 0x400D
     0x10C0E1: "QCN9001",
     0x1150E1: "QCN9002",
     0x10D0E1: "QCN9003",
@@ -164,83 +197,134 @@ msmids = {
     0x10F0E1: "QCN9011",
     0x1110E1: "QCN9012",
     0x1140E1: "QCN9013",
-    0x0E30E1: "qcs401", # 0x20140000 soc_hw_version, 0x863DB000 sec.elf 64Bit, 0x863DE000 dbgpolicy, 64Bit
-    0x0E40E1: "qcs403", # 0x20140000 soc_hw_version, 0x863DB000 sec.elf 64Bit, 0x863DE000 dbgpolicy, 64Bit
-    0x1040E1: "qcs404", # 0x20140000 soc_hw_version, 0x863DB000 sec.elf 64Bit, 0x863DE000 dbgpolicy, 64Bit
+    0x0E30E1: "qcs401",  # 0x20140000 soc_hw_version, 0x863DB000 sec.elf 64Bit, 0x863DE000 dbgpolicy, 64Bit
+    0x0E40E1: "qcs403",  # 0x20140000 soc_hw_version, 0x863DB000 sec.elf 64Bit, 0x863DE000 dbgpolicy, 64Bit
+    0x1040E1: "qcs404",  # 0x20140000 soc_hw_version, 0x863DB000 sec.elf 64Bit, 0x863DE000 dbgpolicy, 64Bit
     0x0AF0E1: "qcs405",  # 0x20140000 soc_hw_version, 0x863DB000 sec.elf 64Bit, 0x863DE000 dbgpolicy, 64Bit
-    0x0EB0E1: "qcs407", # 0x20140000 soc_hw_version, 0x863DB000 sec.elf 64Bit, 0x863DE000 dbgpolicy, 64Bit
-    0x0400E1: "rennell_cb", # soc_vers 0x600E7T A11 CB
-    0x12A0E1: "rennell",
-    0x12B0E1: "rennell_premier",
-    0x1490E1: "rennell_v1.1",
-    0x1630E1: "sd7250",
-    0x11E0E1: "saipan", # 0x600D0100 soc_hw_version, 0x808FF000 sec.elf 64Bit, 0x1C000000 dbgpolicy, 64Bit, SM7250 Snapdragon 765G
-    0x0950E1: "SM6150",  # 0x60070100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
+    0x0EB0E1: "qcs407",  # 0x20140000 soc_hw_version, 0x863DB000 sec.elf 64Bit, 0x863DE000 dbgpolicy, 64Bit
+    0x0400E1: "rennell_cb",  # soc_vers 0x600E7T A11 CB
+    0x12A0E1: "rennell", # Snapdragon 720G, rennell_sm
+    0x12B0E1: "rennell_premier", # Snapdragon 720G, rennell_smp
+    0x1490E1: "rennell_v1.1", # Snapdragon 720G rennell_sm_ab
+    0x1630E1: "sd7250", # Snapdragon 750G, bitra_h
+    0x11E0E1: "saipan",  # 0x600D0100 soc_hw_version, 0x808FF000 sec.elf 64Bit, 0x1C000000 dbgpolicy, 64Bit, SM7250 Snapdragon 765G
+    0x1430E1: "saipan", # saipan module
+    0x0950E1: "SM6150",  # Snapdragon 675, 0x60070100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
     0x0EC0E1: "SM6150p",  # 0x60070100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
-    0x0F50E1: "SM6155",  # 0x60070100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
+    0x0F50E1: "SM6155",  # SA6155, 0x60070100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
     0x100EE0E1: "SM6155p",  # 0x60070100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
+    0x000EE0E1: "SA6155p",  # 0x60070100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
+    0x0011C0E1: "QCS610",  # Qualcomm Vision Intelligence
     0x1011C0E1: "SM6150_IoT_High",  # 0x60070100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
-    0x001290E1: "SM6150_IoT_Low",  # 0x60070100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
+    0x001290E1: "SM6150_IoT_Low",  # QCS410, 0x60070100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
     0x0E60E1: "SM7150",  # 0x600C0100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
-    0x0A50E1: "SM8150", # SDM855 Hana 0x60030100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
-    0x0A60E1: "SM8150p", # SDM855p Hana 0x60030100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
-    0x0C30E1: "SM8250:CD90-PH805-1A", # Kona, 0x60080100 soc_hw_version, 0x808FF000 sec.elf 64Bit, 0x1C000000 dbgpolicy, 64Bit
-    0x0CE0E1: "SM8250:CD90-PH806-1A", # Kona 0x60080100 soc_hw_version, 0x808FF000 sec.elf 64Bit, 0x1C000000 dbgpolicy, 64Bit
-    0x0B80E1: "sc8180x", # Snapdragon 8CX, soc_vers 0x6006
-    0x1560E1: "SM8250", # HDK 8250
+    0x0A50E1: "SM8150",  # SDM855 Hana 0x60030100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
+    0x0A60E1: "SM8150p",  # SDA855 Hana 0x60030100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
+    0x0CB0E1: "SDM855A",
+    0x0C30E1: "SM8250:CD90-PH805-1A",  # Snapdragon 865, Kona, 0x60080100 soc_hw_version, 0x808FF000 sec.elf 64Bit, 0x1C000000 dbgpolicy, 64Bit
+    0x0CE0E1: "SM8250:CD90-PH806-1A",  # Snapdragon 865, Kona 0x60080100 soc_hw_version, 0x808FF000 sec.elf 64Bit, 0x1C000000 dbgpolicy, 64Bit
+    0x0B80E1: "sc8180x",  # Snapdragon 8CX, soc_vers 0x6006, 0x85FFD000 sec.elf 64Bit, 0x1C000000 dbgpolicy, 64Bit
+    0x1230E1: "sa8189P",  # Snapdragon 8CX, automotive, soc_vers 0x6006, 0x85FFD000 sec.elf 64Bit, 0x1C000000 dbgpolicy, 64Bit
+    0x1560E1: "SM8250",  # HDK 8250 / QCS820
     0x1510E1: "SA2150p",
-    0x14D0E1: "SDM662", # sm6115, bengal
-    0x18A0E1: "fraser", #soc_vers 0x600D
-    0x1920E1: "sm7325", #soc_vers 0x6018
-    0x1930E1: "sc7280", #soc_vers 0x6018
-    0x1940E1: "sc7295", #soc_vers 0x6018
-    0x18B0E1: "qtang2", #soc_vers 0x7001
-    0x12C0E1: "sc7180", #soc_vers 0x600E
+    0x14D0E1: "SDM662",  # sm6115, bengal
+    0x18A0E1: "fraser",  # soc_vers 0x600D
+    0x1920E1: "sm7325",  # soc_vers 0x6018
+    0x1930E1: "sc7280",  # soc_vers 0x6018
+    0x1940E1: "sc7295",  # soc_vers 0x6018
+    0x18B0E1: "qtang2",  # soc_vers 0x7001
+    0x12C0E1: "sc7180",  # soc_vers 0x600E, 0x1C000000 dbgpolicy 32Bit
+    0x1A90E1: "strait",  # 0x90060100 soc_hw_version, 0x10000000 dbgpolicy 64Bit, 0x808FF000 sec.elf 64Bit
 
     # Unknown root hash
     0x0B70E1: "SDM850",
-    0x0960E1: "SDX24",  # 0x60020100 soc_hw_version, 0x8fff7000 dbgpolicy 32Bit, 0x8FCFD000 sec.elf 64Bit
-    0x0970E1: "SDX24M", # 0x60020100 soc_hw_version, 0x8fff7000 dbgpolicy 32Bit, 0x8FCFD000 sec.elf 64Bit
-    0x0E70E1: "SM7150p", # 0x600C0100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
-    0x0E80E1: "SA8155", # 0x60030100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
-    0x0E90E1: "SA8155p", # 0x60030100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
-    0x1440E1: "chitwan", # soc_vers 0x6013
+    0x0E70E1: "SM7150p",  # SnapDragon 730, 0x600C0100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
+    0x0E80E1: "SA8155",  # Snapdragon 855+, 0x60030100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
+    0x0E90E1: "SA8155p",  # SDA855A, 0x60030100 soc_hw_version, 0x85FFF000 sec.elf 64Bit, 0x1C1FF000 dbgpolicy, 64Bit
+    0x1440E1: "chitwan",  # soc_vers 0x6013
     0x6220E1: "MSM7227A",
     0x8040E1: "APQ8026",
     0x0550E1: "APQ8017",
     0x90F0E1: "APQ8037",
     0x9770E1: "APQ8052",
-    0x9F00E1: "APQ8056",
+    0x9F00E1: "APQ8056", # Snapdragon 650
+    0x9120E1: "APQ8062",
     0x7190E1: "APQ8064",
     0x9300E1: "APQ8092",
+    0x0640E1: "APQ8096SG",
     0x0620E1: "APQ8098",
     0x8110E1: "MSM8210",
     0x8140E1: "MSM8212",
-    0x8120E1: "MSM8610",
+    0x0590E1: "MSM8217",
+    0x7BE0E1: "MSM8274_AA",
+    0x8120E1: "MSM8610", # SnapDragon 200
+    0x8160E1: "MSM8112", # SnapDragon 200
+    0x8170E1: "MSM8510", # Snapdragon 200   
+    0x8100E1: "MSM8110", # Snapdragon 200
+    0x8130E1: "MSM8810", # Snapdragon 200 
+    0x8080E1: "MSM8512", # Snapdragon 200 
     0x8150E1: "MSM8612",
     0x8010E1: "MSM8626",
-    0x8050E1: "MSM8926", # SnapDragon 400
-    0x9180E1: "MSM8928", # SnapDragon 400
+    0x8050E1: "MSM8926",  # SnapDragon 400
+    0x9180E1: "MSM8928",  # SnapDragon 400
+    0x9170E1: "MSM8628",  # SnapDragon 400
     0x7210E1: "MSM8930",
     0x72C0E1: "MSM8960",
-    0x9B00E1: "MSM8956", # SnapDragon 652
+    0x9B00E1: "MSM8956",  # SnapDragon 652
     0x9100E1: "MSM8962",
-    0x7B00E1: "MSM8974", # Snapdragon 800
-    0x7B30E1: "MSM8974A",
+    0x7B00E1: "MSM8974",  # Snapdragon 800
+    0x7BD0E1: "MSM8674_AA",  # Snapdragon 800
+    0x7B30E1: "APQ8074", # APQ8074
     0x7B40E1: "MSM8974AB",
     0x7B80E1: "MSM8974Pro",
     0x7BC0E1: "MSM8974ABv3",
     0x6B10E1: "MSM8974AC",
-    0x05F0E1: "MSM8996Pro", # SnapDragon 821
+    0x05F0E1: "MSM8996Pro",  # SnapDragon 821, MSM8996SG
+    0x06C0E1: "MSM8997",
     0x0480E1: "MDM9207",
     0x0CC0E1: "SDM636",
     0x0930E1: "SDA670",  # 0x60040100 soc_hw_version
-    #0x0930E1: "SDA835", # 0x30020000 => HW_ID1 3002000000290022
+    # 0x0930E1: "SDA835", # 0x30020000 => HW_ID1 3002000000290022
     0x08B0E1: "SDM845",  # Napali 0x60000100 => HW_ID1 6000000000010000
-    #SDM840 NapaliQ ?
-    #SDM640 Talos ?
-    0x19E0E1: "lahaina",
-    0x0520e1: "Wear3100"
+    # SDM840 NapaliQ ?
+    # SDM640 Talos ?
+    # SM6375 ?
+    0x1970E1: "qcm6490",
+    0x1980E1: "qcs6490",
+    0x9820E1: "msm8976", # Snapdragon 652
+    0x8060E1: "msm8326", # Snapdragon S4
+    0x9640E1: "msm8992", # Snapdragon 808
+    0x7B50E1: "msm8674_pro", # Snapdragon 801
+    0x80D0E1: "fsm9915",
+    0x9110E1: "msm8262",
+    0x0BC0E1: "sda630",
+    0x0F20E1: "sa4155p",
+    0x0EF0E1: "sdm660",
+    0x8030E1: "msm8126",
+    0x9130E1: "apq8028", #Snapdragon 400
+    0x0B90E1: "sda450",
+    0x05A0E1: "msm8617", #Snapdragon 425
+    0x13D0E1: "qcm2150",
+    0x8020E1: "msm8526",
+    0x80A0E1: "fsm9965",
+    0x80F0E1: "fsm9900",
+    0x9140E1: "msm8128",
+    0x9160E1: "msm8528", # Snapdragon 400
+    0x08F0E1: "sdm830", # Snapdragon 830
+    0x09D0E1: "sda658", # Snapdragon 660/658
+    0x08D0E1: "sdm658",
+    0x9830E1: "apq8076", # Snapdragon 652
+    0x80C0E1: "fsm9950",
+    0x80E0E1: "fsm9910",
+    0x15A0E1: "qrb516",
+    0x8000E1: "msm8226",
+    0x0D90E1: "qca6390",
+    0x9D70E1: "msm8229",
+    0x90E0E1: "msm8236",
+    0x9660E1: "mdm9309",
+    0x04E0E1: "apq8096au",
+    0x9570E1: "msm8239", # Snapdragon 615
+
 }
 
 sochw = {
@@ -256,7 +340,7 @@ sochw = {
     0x400B: "QCN7605,QCA6595,QCN7606",
     0x400D: "QCN9000,QCN9001,QCN9002,QCN9003,QCN9010,QCN9011,QCN9012,QCN9013",
     0x4014: "moselle",
-
+    0x4017: "WCN7850,WCN7851",
     #: "SDM632",
     #: "SDA632",
     #: "SDM636",
@@ -278,28 +362,30 @@ sochw = {
     0x600F: "lahaina",
     0x6012: "bitra_SDM",
     0x6013: "chitwan",
-    0x6014: "SC8280X,SA8295P,SA8540P", #makena
-    0x6016: "olympic,olympic_hybrid",
+    0x6014: "SC8280X,SA8295P,SA8540P",  # makena
+    0x6016: "olympic_v1,olympic_v1_hybrid",
     0x6017: "cedros",
-    0x6018: "sm7325,sc7280,sc7295", #kodiak
+    0x6018: "sm7325,sc7280,sc7295",  # kodiak
     0x7001: "qtang2",
     0x7200: "SDM662",
     0x9001: "nicobar,nicobar_IoT_APQ,nicobar_IoT_modem",
-    0x9002: "kamorta,kamorta_P,kamorta_IoT_APQ,kamorta_IoT_modem",
-    0x9003: "agatti",
-    0x9004: "mannar,mannar_P"
+    0x9002: "kamorta,kamorta_P,kamorta_IoT_APQ,kamorta_IoT_modem,sm6225",
+    0x9003: "agatti,agatti_mdm_iot,agatti_mdm,qcs2290",
+    0x9004: "mannar,mannar_P",
+    0x9006: "strait",
+    0x9007: "divar"
 }
 
-secgen=[
+secgen = [
     # BOOT_ROM_BASE_PHYS, SECURITY_CONTROL_BASE_PHYS, MEMORY_MAP
     [[], [0x01900000, 0x100000], []],
-    [[],[0x01e20000,0x1000],[]],
-    [[0xFC010000, 0x18000], [0xFC4B8000, 0x60F0], [0x200000, 0x24000]],
-    [[0x100000, 0x1ffb0], [0x70000, 0x6158], [0x200000, 0x24000]],
-    [[0x100000, 0x1ffb0], [0x00058000, 0x1000], [0x200000, 0x24000]],
-    [[0x100000, 0x1ffb0], [0x000A0000, 0x6FFF], [0x200000, 0x24000]],
-    [[0x100000, 0x1ffb0], [0x00700000, 0x6158], [0x200000, 0x24000]],
-    [[0x300000, 0x3c000], [0x00780000, 0x10000], [0x14009003, 0x24000]],
+    [[], [0x01e20000, 0x1000], []],
+    [[0xFC010000, 0x18000], [0xFC4B8000, 0x60F0], [0x200000, 0x18000]],
+    [[0x100000, 0x1ffb0], [0x70000, 0x6158], [0x200000, 0x18000]],
+    [[0x100000, 0x1ffb0], [0x00058000, 0x1000], [0x200000, 0x18000]],
+    [[0x100000, 0x1ffb0], [0x000A0000, 0x6FFF], [0x200000, 0x18000]],
+    [[0x100000, 0x1ffb0], [0x00700000, 0x6158], [0x200000, 0x18000]],
+    [[0x300000, 0x3c000], [0x00780000, 0x10000], [0x14009003, 0x18000]],
     [[0x300000, 0x3c000], [0x01B40000, 0x10000], []],
 ]
 
@@ -320,6 +406,7 @@ infotbl = {
     "MSM8974AB": secgen[2],
     "MSM8974ABv3": secgen[2],
     "MSM8974AC": secgen[2],
+    "APQ8074": secgen[2],
     "MSM8992": secgen[2],
     "MSM8994": secgen[2],
     "MDM9x25": secgen[2],
@@ -408,9 +495,9 @@ infotbl = {
     "SM6150": secgen[7],
     "SM6150p": secgen[7],
     "SM6150_IoT_High": secgen[7],
-    "SM6150_IoT_Low" : secgen[7],
-    "SM6155" : secgen[7],
-    "SM6155p" : secgen[7],
+    "SM6150_IoT_Low": secgen[7],
+    "SM6155": secgen[7],
+    "SM6155p": secgen[7],
     "SM7150": secgen[7],
     "SM7150p": secgen[7],
     "SM8150": secgen[7],
@@ -434,15 +521,23 @@ infotbl = {
     "rennell_v1.1": secgen[7],
     "sc7180": secgen[7],
     "sd7250": secgen[7],
+    "olympic_v1": secgen[7],        # secgen 8 as well
+    "olympic_v1_hybrid": secgen[7], # secgen 8 as well
+    "WCN7850": secgen[7],
+    "WCN7851": secgen[7],
 
     "nicobar": secgen[8],
     "nicobar_IoT_APQ": secgen[8],
     "nicobar_IoT_modem": secgen[8],
     "agatti": secgen[8],
+    "agatti_mdm": secgen[8],
+    "agatti_mdm_iot": secgen[8],
+    "qcs2290": secgen[8],
     "kamorta": secgen[8],
     "kamorta_P": secgen[8],
     "kamorta_IoT_modem": secgen[8],
     "kamorta_IoT_APQ": secgen[8],
+    "divar": secgen[8],
     "SDM662": secgen[8],
     "sm7235": secgen[8],
     "sc7280": secgen[8],
@@ -450,10 +545,7 @@ infotbl = {
     "SC8280X": secgen[8],
     "SA8295P": secgen[8],
     "SA8540P": secgen[8],
-    "olympic": secgen[8],
-    "olympic_hybrid": secgen[8],
-    "lahaina": secgen[8]
-
+    "strait": secgen[8],
 
     # "MSM7227A": [[], [], []],
     # "MSM8210": [[], [0xFC4B8000,0x6FFF], []],
@@ -464,6 +556,7 @@ infotbl = {
     # "MSM8928": [[], [], []],
 }
 
+
 class memory_type:
     nand = 0
     emmc = 1
@@ -487,6 +580,7 @@ class memory_type:
         "MSM8974AB": emmc,
         "MSM8974ABv3": emmc,
         "MSM8974AC": emmc,
+        "APQ8074": emmc,
         "MSM8992": emmc,
         "MSM8994": emmc,
         "MDM9x25": emmc,
@@ -602,15 +696,20 @@ class memory_type:
         "sc7180": ufs,
         "sd7250": ufs,
         "SA2150p": emmc,
-
         "nicobar": ufs,
-        "agatti": ufs,
+        "agatti": emmc,
+        "agatti_mdm": emmc,
+        "agatti_mdm_iot": emmc,
+        "qcs2290": emmc,
         "kamorta": ufs,
         "kamorta_P": ufs,
         "kamorta_IoT_APQ": emmc,
         "kamorta_IoT_modem": emmc,
+        "divar": ufs,
         "SDM662": emmc,
-
+        "strait": ufs,
+        "WCN7850": emmc,
+        "WCN7851": emmc,
         # "MSM7227A": [[], [], []],
         # "MSM8210": [[], [0xFC4B8000,0x6FFF], []],
         # "MSM8212": [[], [], []],
@@ -638,6 +737,7 @@ secureboottbl = {
     "APQ8076": 0x000a01d0,
     "APQ8084": 0xFC4B83F8,
     "APQ8092": 0xFC4B83F8,
+    "APQ8094": 0xFC4B83F8,
     "APQ8098": 0x00780350,
     "MSM8226": 0xFC4B83E8,
     "MSM8610": 0xFC4B83E8,
@@ -657,6 +757,7 @@ secureboottbl = {
     "MSM8953": 0x000a01d0,
     "MSM8956": 0x000a01d0,
     "MSM8974": 0xFC4B83F8,
+    "APQ8074": 0xFC4B83F8,
     "MSM8974AB": 0xFC4B83F8,
     "MSM8974ABv3": 0xFC4B83F8,
     "MSM8974AC": 0xFC4B83F8,
@@ -691,7 +792,7 @@ secureboottbl = {
     "SDA632": 0x000a01d0,
     "SDM636": 0x00780350,
     "SDM660": 0x00780350,
-    "SDM670": 0x00780350, # Warlock
+    "SDM670": 0x00780350,  # Warlock
     "SDA670": 0x00780350,
     "SDM710": 0x00780350,
     "QCS605": 0x00780350,
@@ -699,13 +800,13 @@ secureboottbl = {
     "SXR1130": 0x00780350,
     "SDM845": 0x00780350,
     "SDA845": 0x00780350,
-    "SDX24" : 0x00780390,
+    "SDX24": 0x00780390,
     "SDX24M": 0x00780390,
     "SDX50M": 0x000a01e0,
-    "SDX55:CD90-PG591":  0x007805E8,
-    "SDX55:CD90-PH809":  0x007805E8,
-    "SDX55M" : 0x007804D0,
-    "SA515M" : 0x007804D0,
+    "SDX55:CD90-PG591": 0x007805E8,
+    "SDX55:CD90-PH809": 0x007805E8,
+    "SDX55M": 0x007804D0,
+    "SA515M": 0x007804D0,
     "SM6150": 0x00780360,
     "SM6150p": 0x00780360,
     "SM6155": 0x00780360,
@@ -720,6 +821,7 @@ secureboottbl = {
     "SM8250:CD90-PH805-1A": 0x007805E8,
     "SM8250:CD90-PH806-1A": 0x007805E8,
     "agatti": 0x01B40458,
+    "qcs2290": 0x01B40458,
     "bitra": 0x007804D8,
     "bitra_SDM": 0x007804D8,
     "bitra_SDA": 0x007804D8,
@@ -740,15 +842,16 @@ secureboottbl = {
     "kamorta_P": 0x01B40458,
     "kamorta_IoT_APQ": 0x01B40458,
     "kamorta_IoT_modem": 0x01B40458,
+    "divar": 0x01B40458,
     "SDM662": 0x01B40458,
     "lahaina": 0x780668,
     "lahaina_premier": 0x780668,
     "mannar": 0x01B40458,
     "mannar_P": 0x01B40458,
     "qm215": 0x000a01d0,
-    "rennell":0x000780498,
-    "rennell_premier":0x000780498,
-    "rennell_V1.1":0x000780498,
+    "rennell": 0x000780498,
+    "rennell_premier": 0x000780498,
+    "rennell_V1.1": 0x000780498,
     "sc7180": 0x000780498
     # "MSM7227A":[[], [], []],
     # "MSM8210": [[], [], []],
diff --git a/edlclient/Config/usb_ids.py b/edlclient/Config/usb_ids.py
index 174e2dd..eb1f5b3 100644
--- a/edlclient/Config/usb_ids.py
+++ b/edlclient/Config/usb_ids.py
@@ -1,5 +1,15 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
+
 default_ids = [
     [0x05c6, 0x9008, -1],
+    [0x0fce, 0x9dde, -1],
+    [0x0fce, 0xade5, -1],
     [0x05c6, 0x900e, -1],
     [0x05c6, 0x9025, -1],
     [0x1199, 0x9062, -1],
@@ -15,6 +25,8 @@ default_diag_vid_pid = [
     [0x1199, 0x9091, -1],  # Sierra Wireless
     [0x0846, 0x68e2,  2],  # Netgear
     [0x05C6, 0x9008, -1],  # QC EDL
+    [0x0fce, 0x9dde, -1],  # SONY EDL
+    [0x0fce, 0xade5, -1],  # SONY EDL
     [0x05C6, 0x676C, 0],   # QC Handset
     [0x05c6, 0x901d, 0],   # QC Android "setprop sys.usb.config diag,adb"
     [0x19d2, 0x0016, -1],  # ZTE Diag
diff --git a/edlclient/Library/Connection/devicehandler.py b/edlclient/Library/Connection/devicehandler.py
index 8c29ce6..ab6d452 100644
--- a/edlclient/Library/Connection/devicehandler.py
+++ b/edlclient/Library/Connection/devicehandler.py
@@ -1,12 +1,19 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2021
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 import serial
 import serial.tools.list_ports
 import inspect
 import traceback
 from binascii import hexlify
-from edlclient.Library.utils import *
+try:
+    from edlclient.Library.utils import *
+except:
+    from Library.utils import *
 
 class DeviceClass(metaclass=LogBase):
 
diff --git a/edlclient/Library/Connection/seriallib.py b/edlclient/Library/Connection/seriallib.py
index 18451a6..95bd65b 100755
--- a/edlclient/Library/Connection/seriallib.py
+++ b/edlclient/Library/Connection/seriallib.py
@@ -1,6 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2022
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 import os.path
 import time
 import sys
@@ -20,8 +24,13 @@ import serial.tools.list_ports
 import inspect
 import traceback
 from binascii import hexlify
-from edlclient.Library.utils import *
-from edlclient.Library.Connection.devicehandler import DeviceClass
+try:
+    from edlclient.Library.utils import *
+    from edlclient.Library.Connection.devicehandler import DeviceClass
+except:
+    from Library.utils import *
+    from Library.Connection.devicehandler import DeviceClass
+
 
 class serial_class(DeviceClass):
 
diff --git a/edlclient/Library/Connection/usblib.py b/edlclient/Library/Connection/usblib.py
index 5686b2a..6fe2de6 100755
--- a/edlclient/Library/Connection/usblib.py
+++ b/edlclient/Library/Connection/usblib.py
@@ -1,6 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2021
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 import io
 import logging
 
@@ -9,17 +13,22 @@ import usb.util
 import time
 import inspect
 import array
-from edlclient.Library.utils import is_windows
 import usb.backend.libusb0
-if not is_windows():
-    import usb.backend.libusb1
 from enum import Enum
 from binascii import hexlify
 from ctypes import c_void_p, c_int
-from edlclient.Library.utils import *
+try:
+    from edlclient.Library.utils import *
+except:
+    from Library.utils import *
+if not is_windows():
+    import usb.backend.libusb1
 from struct import pack, calcsize
 import traceback
-from edlclient.Library.Connection.devicehandler import DeviceClass
+try:
+    from edlclient.Library.Connection.devicehandler import DeviceClass
+except:
+    from Library.Connection.devicehandler import DeviceClass
 USB_DIR_OUT = 0  # to device
 USB_DIR_IN = 0x80  # to host
 
@@ -40,6 +49,8 @@ USB_RECIP_OTHER = 0x03
 USB_RECIP_PORT = 0x04
 USB_RECIP_RPIPE = 0x05
 
+MAX_USB_BULK_BUFFER_SIZE = 16384
+
 tag = 0
 
 CDC_CMDS = {
@@ -57,8 +68,9 @@ CDC_CMDS = {
 
 class usb_class(DeviceClass):
 
-    def __init__(self, loglevel=logging.INFO, portconfig=None, devclass=-1):
+    def __init__(self, loglevel=logging.INFO, portconfig=None, devclass=-1, serial_number=None):
         super().__init__(loglevel, portconfig, devclass)
+        self.serial_number = serial_number
         self.load_windows_dll()
         self.EP_IN = None
         self.EP_OUT = None
@@ -210,9 +222,13 @@ class usb_class(DeviceClass):
         for dev in devices:
             for usbid in self.portconfig:
                 if dev.idProduct == usbid[1] and dev.idVendor == usbid[0]:
+                    if self.serial_number is not None:
+                        if dev.serial_number != self.serial_number:
+                            continue
                     self.device = dev
                     self.vid = dev.idVendor
                     self.pid = dev.idProduct
+                    self.serial_number = dev.serial_number
                     self.interface = usbid[2]
                     break
             if self.device is not None:
@@ -322,7 +338,8 @@ class usb_class(DeviceClass):
 
     def write(self, command, pktsize=None):
         if pktsize is None:
-            pktsize = self.EP_OUT.wMaxPacketSize
+            #pktsize = self.EP_OUT.wMaxPacketSize
+            pktsize = MAX_USB_BULK_BUFFER_SIZE
         if isinstance(command, str):
             command = bytes(command, 'utf-8')
         pos = 0
diff --git a/edlclient/Library/Connection/usbscsi.py b/edlclient/Library/Connection/usbscsi.py
index 7caeeb8..72520cd 100755
--- a/edlclient/Library/Connection/usbscsi.py
+++ b/edlclient/Library/Connection/usbscsi.py
@@ -1,6 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2021
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 import argparse
 from edlclient.Library.Connection.usblib import *
 
diff --git a/edlclient/Library/Modules/generic.py b/edlclient/Library/Modules/generic.py
index c22618f..adfd756 100644
--- a/edlclient/Library/Modules/generic.py
+++ b/edlclient/Library/Modules/generic.py
@@ -1,6 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2021
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 
 import logging
 from edlclient.Library.utils import LogBase
@@ -23,9 +27,12 @@ class generic(metaclass=LogBase):
         if res[0]:
             lun = res[1]
             rpartition = res[2]
-            offsettopatch = 0x7FFFF
-            sector = rpartition.sector + (offsettopatch // self.fh.cfg.SECTOR_SIZE_IN_BYTES)
-            offset = offsettopatch % self.fh.cfg.SECTOR_SIZE_IN_BYTES
+            if rpartition.sectors <= (0x8000//self.fh.cfg.SECTOR_SIZE_IN_BYTES):
+                offsettopatch = 0x7FFF
+                sector, offset = self.fh.calc_offset(rpartition.sector, offsettopatch)
+            else:
+                offsettopatch = 0x7FFFF
+                sector, offset = self.fh.calc_offset(rpartition.sector, offsettopatch)
             if enable:
                 value = 0x1
             else:
diff --git a/edlclient/Library/Modules/init.py b/edlclient/Library/Modules/init.py
index 48d17bf..96089ed 100644
--- a/edlclient/Library/Modules/init.py
+++ b/edlclient/Library/Modules/init.py
@@ -1,6 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2021
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 
 import logging
 from edlclient.Library.utils import LogBase
@@ -8,24 +12,32 @@ from edlclient.Library.utils import LogBase
 try:
     from edlclient.Library.Modules.generic import generic
 except ImportError as e:
+    print(e)
     generic = None
     pass
 
 try:
     from edlclient.Library.Modules.oneplus import oneplus
 except ImportError as e:
+    print(e)
     oneplus = None
     pass
 
 try:
     from edlclient.Library.Modules.xiaomi import xiaomi
 except ImportError as e:
+    print(e)
     xiaomi = None
     pass
 
+try:
+    from edlclient.Library.Modules.nothing import nothing
+except ImportError as e:
+    nothing = None
+    pass
 
 class modules(metaclass=LogBase):
-    def __init__(self, fh, serial, supported_functions, loglevel, devicemodel, args):
+    def __init__(self, fh, serial:int, supported_functions, loglevel, devicemodel:str, args):
         self.fh = fh
         self.args = args
         self.serial = serial
@@ -43,17 +55,20 @@ class modules(metaclass=LogBase):
         try:
             self.generic = generic(fh=self.fh, serial=self.serial, args=self.args, loglevel=loglevel)
         except Exception as e:
+            self.error(e)
             pass
         self.ops = None
         try:
             self.ops = oneplus(fh=self.fh, projid=self.devicemodel, serial=self.serial,
                                supported_functions=self.supported_functions, args=self.args, loglevel=loglevel)
         except Exception as e:
+            self.error(e)
             pass
         self.xiaomi = None
         try:
             self.xiaomi = xiaomi(fh=self.fh)
         except Exception as e:
+            self.error(e)
             pass
 
     def addpatch(self):
@@ -87,7 +102,7 @@ class modules(metaclass=LogBase):
             else:
                 options[args[i]] = True
         if command == "":
-            print("Valid commands are:\noemunlock\n")
+            print("Valid commands are:\noemunlock, ops\n")
             return False
         if self.generic is not None and command == "oemunlock":
             if "enable" in options:
@@ -98,3 +113,46 @@ class modules(metaclass=LogBase):
                 self.error("Unknown mode given. Available are: enable, disable.")
                 return False
             return self.generic.oem_unlock(enable)
+        elif self.ops is not None and command == "ops":
+            if self.devicemodel is not None:
+                enable = False
+                partition = "param"
+                if "enable" in options:
+                    enable = True
+                elif "disable" in options:
+                    enable = False
+                else:
+                    self.error("Unknown mode given. Available are: enable, disable.")
+                    return False
+                res = self.fh.detect_partition(self.args, partition)
+                if res[0]:
+                    lun = res[1]
+                    rpartition = res[2]
+                    paramdata = self.fh.cmd_read_buffer(lun, rpartition.sector, rpartition.sectors, False)
+                    if paramdata.data == b"":
+                        self.error("Error on reading param partition.")
+                        return False
+                    wdata = self.ops.enable_ops(paramdata.data, enable,self.devicemodel,self.serial)
+                    if wdata is not None:
+                        self.ops.run()
+                        if self.fh.cmd_program_buffer(lun, rpartition.sector, wdata, False):
+                            self.info("Successfully set mode")
+                            return True
+                        else:
+                            self.error("Error on writing param partition")
+                            return False
+                    else:
+                        self.error("No param info generated, did you provide the devicemodel ?")
+                        return False
+                else:
+                    fpartitions = res[1]
+                    self.error(f"Error: Couldn't detect partition: {partition}\nAvailable partitions:")
+                    for lun in fpartitions:
+                        for rpartition in fpartitions[lun]:
+                            if self.args["--memory"].lower() == "emmc":
+                                self.error("\t" + rpartition)
+                            else:
+                                self.error(lun + ":\t" + rpartition)
+            else:
+                self.error("A devicemodel is needed for this command")
+        return False
diff --git a/edlclient/Library/Modules/nothing.py b/edlclient/Library/Modules/nothing.py
new file mode 100644
index 0000000..811a89c
--- /dev/null
+++ b/edlclient/Library/Modules/nothing.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
+import hashlib
+import logging
+import random
+
+from edlclient.Library.utils import LogBase
+
+
+class nothing(metaclass=LogBase):
+    def __init__(self, fh, projid="22111", serial=123456, ATOBuild=0, Flash_Mode=0, cf=0, supported_functions=None,
+                 loglevel=logging.INFO):
+        self.fh = fh
+        self.projid = projid
+        #self.projid == "22111":
+        self.hashverify = "16386b4035411a770b12507b2e30297c0c5471230b213e6a1e1e701c6a425150"
+        self.serial = serial
+        self.supported_functions = supported_functions
+        self.__logger.setLevel(loglevel)
+        if loglevel == logging.DEBUG:
+            logfilename = "log.txt"
+            fh = logging.FileHandler(logfilename)
+            self.__logger.addHandler(fh)
+
+    def generatetoken(self, token1: str = None):
+        if token1 is None:
+            token1 = random.randbytes(32).hex()
+        authresp = token1 + self.projid + ("%x" % self.serial) + self.hashverify
+        token2 = hashlib.sha256(bytes(authresp,'utf-8')).hexdigest()[:64]
+        token3 = self.hashverify
+        return bytes(f"<?xml version=\"1.0\" encoding=\"UTF-8\" ?><data>\n    <ntprojectverify  token1=\"{token1}\" token2=\"{token2}\" token3=\"{token3}\"/>\n</data>\n",'utf-8')
+
+
+    def ntprojectverify(self):
+        """
+        Nothing Phone 2
+        """
+        authcmd = b"<?xml version=\"1.0\" encoding=\"UTF-8\" ?><data>\n  <checkntfeature />\n</data>\n"
+        rsp = self.fh.xmlsend(authcmd)
+        if rsp.resp:
+            authresp = self.generatetoken()
+            rsp = self.fh.xmlsend(authresp)
+            if rsp.resp:
+                if b"ACK" in rsp.data:
+                    return True
+                if "value" in rsp.resp:
+                    if rsp.resp["value"] == "ACK":
+                        if 'authenticated' in rsp.log[0].lower() and 'true' in rsp.log[0].lower():
+                            return True
+        return False
+
+
+if __name__ == "__main__":
+    nt = nothing(fh=None, projid="22111", serial=1729931115)
+    res=nt.generatetoken(token1="512034500a07154561661e0f371f4a712a0b76074605724c640e301d632b3671")
+    org=b"<?xml version=\"1.0\" encoding=\"UTF-8\" ?><data>\n    <ntprojectverify  token1=\"512034500a07154561661e0f371f4a712a0b76074605724c640e301d632b3671\" token2=\"1ecd222465436eb8acc0cfc41e90d1e677165c184ea7d9631615014dac88c669\" token3=\"16386b4035411a770b12507b2e30297c0c5471230b213e6a1e1e701c6a425150\"/>\n</data>\n"
+    if res!=org:
+        print("Error !")
+    print(res)
+    print(nt.generatetoken())
diff --git a/edlclient/Library/Modules/oneplus.py b/edlclient/Library/Modules/oneplus.py
index a128cdb..c87daa7 100755
--- a/edlclient/Library/Modules/oneplus.py
+++ b/edlclient/Library/Modules/oneplus.py
@@ -1,6 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2021
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 
 """
 Usage:
@@ -20,7 +24,7 @@ import random
 from struct import pack
 import logging
 from edlclient.Library.utils import LogBase
-
+from edlclient.Library.Modules.oneplus_param import paramtools
 try:
     from edlclient.Library.cryptutils import cryptutils
 except Exception as e:
@@ -75,7 +79,7 @@ deviceconfig = {
 
     # OP Nord, avicii
     "20801": dict(version=2, cm="eacf50e7", param_mode=0),
-    
+
     # OP N10 5G Metro, billie8t
     "20885": dict(version=3, cm="3a403a71", param_mode=1),
     # OP N10 5G Global, billie8
@@ -87,7 +91,7 @@ deviceconfig = {
 
     # OP N100 Metro, billie2t
     "20880": dict(version=3, cm="6ccf5913", param_mode=1),
-    # OP N100 Global, billie2 
+    # OP N100 Global, billie2
     "20881": dict(version=3, cm="fa9ff378", param_mode=1),
     # OP N100 TMO, billie2t
     "20882": dict(version=3, cm="4ca1e84e", param_mode=1),
@@ -124,10 +128,10 @@ deviceconfig = {
 
 
 class oneplus(metaclass=LogBase):
-    def __init__(self, fh, projid="18825", serial=123456, ATOBuild=0, Flash_Mode=0, cf=0, supported_functions=None,
+    def __init__(self, fh, projid:str="18825", serial=123456, ATOBuild=0, Flash_Mode=0, cf=0, supported_functions=None,
                  args=None, loglevel=logging.INFO):
         self.fh = fh
-        self.__logger=self.__logger
+        self.__logger = self.__logger
         self.args = args
         self.ATOBuild = ATOBuild
         self.Flash_Mode = Flash_Mode
@@ -155,7 +159,15 @@ class oneplus(metaclass=LogBase):
             logfilename = "log.txt"
             filehandler = logging.FileHandler(logfilename)
             self.__logger.addHandler(filehandler)
-        self.ops_parm = None
+        try:
+            if projid in deviceconfig:
+                mode = deviceconfig[projid]["param_mode"]
+                self.ops_parm = paramtools(mode=mode, serial=serial)
+            else:
+                self.ops_parm = paramtools(mode=0, serial=serial)
+        except ImportError as e:
+            self.__logger.error(str(e))
+            self.ops_parm = None
         self.ops = self.convert_projid(fh, projid, serial)
 
     def getprodkey(self, projid):
@@ -187,10 +199,11 @@ class oneplus(metaclass=LogBase):
                     exit(0)
             elif version == 3:
                 if cm is not None:
-                    oneplus2(fh, cm, serial, pk, prodkey, self.ATOBuild, self.Flash_Mode, self.cf)
+                    return oneplus2(fh, cm, serial, pk, prodkey, self.ATOBuild, self.Flash_Mode, self.cf)
                 else:
                     assert "Device is not supported"
                     exit(0)
+        assert "Unknown projid:"+str(projid)
         return None
 
     def run(self):
@@ -212,21 +225,26 @@ class oneplus(metaclass=LogBase):
         if self.ops.setprojmodel_verify:
             return self.ops.setprojmodel_verify(pk, token)
 
-    def setswprojmodel_verify(self, pk, token, device_timestamp):
+    def setswprojmodel_verify(self, pk, token):
         if self.ops.setswprojmodel_verify:
-            return self.ops.setswprojmodel_verify(pk, token, device_timestamp)
+            return self.ops.setswprojmodel_verify(pk, token)
 
     def program_verify(self, pk, token, tokendata):
         if self.ops.program_verify:
             return self.ops.program_verify(pk, token, tokendata)
 
-    def generatetoken(self, program=False, device_timestamp="123456789"):
-        return self.ops.generatetoken(program=program, device_timestamp=device_timestamp)
+    def generatetoken(self, program=False):
+        return self.ops.generatetoken(program=program)
 
     def demacia(self):
         if self.ops.demacia():
             return self.ops.demacia()
 
+    def enable_ops(self, data, enable, projid, serial):
+        if self.ops_parm is not None:
+            return self.ops_parm.enable_ops(data, enable)
+        return None
+
     def addpatch(self):
         if "setprojmodel" in self.supported_functions or "setswprojmodel" in self.supported_functions:
             pk, token = self.ops.generatetoken(True)
@@ -292,7 +310,7 @@ class oneplus1:
         data = "<?xml version=\"1.0\" ?>\n<data>\n<demacia token=\"" + token + "\" pk=\"" + pk + "\" />\n</data>"
         return data
 
-    def generatetoken(self, program=False, device_timestamp=None):
+    def generatetoken(self, program=False):
         timestamp = str(int(time.time()))
         ha = cryptutils().hash()
         h1 = self.prodkey + self.ModelVerifyPrjName + self.random_postfix
@@ -411,6 +429,7 @@ class oneplus1:
 class oneplus2(metaclass=LogBase):
     def __init__(self, fh, ModelVerifyPrjName="20889", serial=123456, pk="", prodkey="", ATOBuild=0, Flash_Mode=0,
                  cf=0, loglevel=logging.INFO):
+        self.device_timestamp = None
         self.ModelVerifyPrjName = ModelVerifyPrjName
         self.pk = pk
         self.fh = fh
@@ -427,10 +446,10 @@ class oneplus2(metaclass=LogBase):
             fh = logging.FileHandler(logfilename)
             self.__logger.addHandler(fh)
 
-    def crypt_token(self, data, pk, device_timestamp, decrypt=False):
+    def crypt_token(self, data, pk, device_timestamp:int, decrypt=False):
         aes = cryptutils().aes()
         aeskey = b"\x46\xA5\x97\x30\xBB\x0D\x41\xE8" + bytes(pk, 'utf-8') + \
-                 pack("<Q", int(device_timestamp, 10))  # we get this using setprocstart
+                 pack("<Q", device_timestamp)  # we get this using setprocstart
         aesiv = b"\xDC\x91\x0D\x88\xE3\xC6\xEE\x65\xF0\xC7\x44\xB4\x02\x30\xCE\x40"
         if decrypt:
             cdata = unhexlify(data)
@@ -445,7 +464,7 @@ class oneplus2(metaclass=LogBase):
             rdata = hexlify(result)
             return rdata.upper().decode('utf-8')
 
-    def generatetoken(self, program=False, device_timestamp=None):  # setswprojmodel
+    def generatetoken(self, program=False):  # setswprojmodel
         timestamp = str(int(time.time()))
         ha = cryptutils().hash()
         h1 = self.prodkey + self.ModelVerifyPrjName + self.random_postfix
@@ -464,7 +483,7 @@ class oneplus2(metaclass=LogBase):
         for item in items:
             data += item + ","
         data = data[:-1]
-        token = self.crypt_token(data, self.pk, device_timestamp)
+        token = self.crypt_token(data, self.pk, self.device_timestamp)
         return self.pk, token
 
     def run(self, flag):
@@ -475,7 +494,9 @@ class oneplus2(metaclass=LogBase):
             return False
         data = res.decode('utf-8')
         device_timestamp = data[data.rfind("device_timestamp"):].split("\"")[1]
-        pk, token = self.generatetoken(False, device_timestamp)
+        self.device_timestamp = int(device_timestamp)
+        print(self.device_timestamp)
+        pk, token = self.generatetoken(False)
         res = self.fh.cmd_send(f"setswprojmodel token=\"{token}\" pk=\"{pk}\"")
         if not b"model_check=\"0\"" in res or not b"auth_token_verify=\"0\"" in res:
             print("Setswprojmodel failed.")
@@ -483,10 +504,10 @@ class oneplus2(metaclass=LogBase):
             return False
         return True
 
-    def setswprojmodel_verify(self, pk, token, device_timestamp):
+    def setswprojmodel_verify(self, pk, token):
         self.pk = pk
         ha = cryptutils().hash()
-        items = self.crypt_token(token, pk, device_timestamp, True)
+        items = self.crypt_token(token, pk, self.device_timestamp, True)
         info = ["ModelVerifyPrjName", "random_postfix", "ModelVerifyHashToken", "ato_build_state", "flash_mode",
                 "Version", "soc_sn", "cf", "timestamp", "secret"]
         i = 0
@@ -550,10 +571,11 @@ def main():
         serial = args["--serial"]
         device_timestamp = args["--ts"]
         op2 = oneplus(None, projid="20889", serial=serial, ATOBuild=0, Flash_Mode=0, cf=0)
+        op2.ops.device_timestamp = int(device_timestamp)
         # 20889 OP N10 5G Europe
         print(f"./edl.py rawxml \"<?xml version=\\\"1.0\\\" ?><data><setprocstart /></data>\"")
         # Response should be : <?xml version="1.0" ?><data><response value=1 device_timestamp="%llu" /></data>
-        pk, token = op2.generatetoken(False, device_timestamp)
+        pk, token = op2.generatetoken(False)
         print(
             f"./edl.py rawxml \"<?xml version=\\\"1.0\\\" ?><data><setswprojmodel " +
             f"token=\\\"{token}\\\" pk=\\\"{pk}\\\" /></data>\" --debugmode")
@@ -587,9 +609,10 @@ def main():
         projid = args["--projid"][0]
         device_timestamp = args["--ts"]
         op = oneplus(None, projid=projid, serial=123456)
+        op.ops.device_timestamp = int(device_timestamp)
         token = args["<token>"]
         pk = args["<pk>"]
-        op.setswprojmodel_verify(pk, token, device_timestamp)
+        op.setswprojmodel_verify(pk, token)
 
 
 def test_setswprojmodel_verify():
@@ -599,8 +622,9 @@ def test_setswprojmodel_verify():
     op = oneplus(None, projid=projid, serial=123456)
     data = deviceresp.decode('utf-8')
     device_timestamp = data[data.rfind("device_timestamp"):].split("\"")[1]
-    pk, token = op.generatetoken(False, device_timestamp)
-    if not op.setswprojmodel_verify(pk, token, device_timestamp):
+    op.ops.device_timestamp = int(device_timestamp)
+    pk, token = op.generatetoken(False)
+    if not op.setswprojmodel_verify(pk, token):
         assert "Setswprojmodel error"
 
 
diff --git a/edlclient/Library/Modules/oneplus_param.py b/edlclient/Library/Modules/oneplus_param.py
new file mode 100755
index 0000000..696328e
--- /dev/null
+++ b/edlclient/Library/Modules/oneplus_param.py
@@ -0,0 +1,989 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
+"""
+Usage:
+    oneplus_param.py param <filename> [--mode=mode] [--serial=serial]
+    oneplus_param.py ops <filename> [--mode=mode] [--serial=serial]
+    oneplus_param.py gencode <imei>
+    oneplus_param.py setparam <filename> <sid> <offset> <value> [--mode=mode] [--serial=serial]
+"""
+import hashlib
+import zlib
+from enum import Enum
+from struct import calcsize, pack, unpack
+
+try:
+    from edlclient.Library.cryptutils import cryptutils
+except ImportError as e:
+    import os, sys, inspect
+
+    current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
+    parent_dir = os.path.dirname(current_dir)
+    sys.path.insert(0, parent_dir)
+    try:
+        from cryptutils import cryptutils
+    except ImportError as e:
+        print(str(e))
+from binascii import unhexlify, hexlify
+
+
+class sid(Enum):
+    PARAM_SID_PRODUCT = 0,
+    PARAM_SID_CONFIG = 1,
+    PARAM_SID_LCD = 2,
+    PARAM_SID_TP = 3,
+    PARAM_SID_TP_KPD = 4,
+    PARAM_SID_CAMERA = 5,
+    PARAM_SID_SENSORS = 6,
+    PARAM_SID_BATTERY = 7,
+    PARAM_SID_RTC = 8,
+    PARAM_SID_CRASH_RECORD = 9,
+    PARAM_SID_SALEINFO = 0xA,
+    PARAM_SID_MISC = 0xB,
+    PARAM_SID_DOWNLOAD = 0xC,
+    PARAM_SID_PHONE_HISTORY = 0xD,
+    PARAM_SID_DL_USERINFO = 0xE,
+    PARAM_SID_ENC_SECRECY = 0x12C,
+    PARAM_SID_ENC_CARRIER = 0x130,
+    PARAM_SID_ENC_MOBID = 0x134,
+    PARAM_SID_ENC_CVE = 0x138,
+    PARAM_SID_INVALID = -1
+
+    ''' 
+                CUSTOM_TYPE.NONE = new CUSTOM_TYPE("NONE", 0);
+                CUSTOM_TYPE.JCC = new CUSTOM_TYPE("JCC", 1); French edition
+                CUSTOM_TYPE.SW = new CUSTOM_TYPE("SW", 2); Star Wars
+                CUSTOM_TYPE.AVG = new CUSTOM_TYPE("AVG", 3); Avengers
+                CUSTOM_TYPE.MCL = new CUSTOM_TYPE("MCL", 4); McLaren
+                CUSTOM_TYPE.OPR_RETAIL = new CUSTOM_TYPE("OPR_RETAIL", 5);
+                CUSTOM_TYPE.CYB = new CUSTOM_TYPE("CYB", 6); Cyberpunk
+                CUSTOM_TYPE v0 = new CUSTOM_TYPE("CMCC", 7); China Mobile
+    '''
+    '''
+                SW_TYPE.DEFAULT = new SW_TYPE("DEFAULT", 0);
+                SW_TYPE.O2 = new SW_TYPE("O2", 1);
+                SW_TYPE.H2 = new SW_TYPE("H2", 2);
+                SW_TYPE.IN = new SW_TYPE("IN", 3);
+                SW_TYPE.EU = new SW_TYPE("EU", 4);
+                SW_TYPE.TMO = new SW_TYPE("TMO", 5);
+                SW_TYPE.SPRINT = new SW_TYPE("SPRINT", 6);
+                SW_TYPE.VERIZON = new SW_TYPE("VERIZON", 7);
+                SW_TYPE.ATT = new SW_TYPE("ATT", 8);
+                SW_TYPE v0 = new SW_TYPE("C532", 9);
+
+    '''
+    '''
+                CUSTOM_BACK_COVER_TYPE.NONE = new CUSTOM_BACK_COVER_TYPE("NONE", 0);
+                CUSTOM_BACK_COVER_TYPE.LCH = new CUSTOM_BACK_COVER_TYPE("LCH", 1);
+                CUSTOM_BACK_COVER_TYPE.MYH = new CUSTOM_BACK_COVER_TYPE("MYH", 2);
+                CUSTOM_BACK_COVER_TYPE.YYB = new CUSTOM_BACK_COVER_TYPE("YYB", 3);
+                CUSTOM_BACK_COVER_TYPE.HPH = new CUSTOM_BACK_COVER_TYPE("HPH", 4);
+                CUSTOM_BACK_COVER_TYPE.DGZ = new CUSTOM_BACK_COVER_TYPE("DGZ", 5);
+                CUSTOM_BACK_COVER_TYPE.OPGY = new CUSTOM_BACK_COVER_TYPE("OPGY", 6);
+                CUSTOM_BACK_COVER_TYPE.OPBL = new CUSTOM_BACK_COVER_TYPE("OPBL", 7);
+                CUSTOM_BACK_COVER_TYPE.OPGL = new CUSTOM_BACK_COVER_TYPE("OPGL", 8);
+                CUSTOM_BACK_COVER_TYPE.OPRD = new CUSTOM_BACK_COVER_TYPE("OPRD", 9);
+                CUSTOM_BACK_COVER_TYPE.OPHDBL = new CUSTOM_BACK_COVER_TYPE("OPHDBL", 10);
+                CUSTOM_BACK_COVER_TYPE.OPHDSL = new CUSTOM_BACK_COVER_TYPE("OPHDSL", 11);
+                CUSTOM_BACK_COVER_TYPE.OPHDMCL = new CUSTOM_BACK_COVER_TYPE("OPHDMCL", 12);
+                CUSTOM_BACK_COVER_TYPE.OPHDAGBL = new CUSTOM_BACK_COVER_TYPE("OPHDAGBL", 13);
+                CUSTOM_BACK_COVER_TYPE.OPINBLK = new CUSTOM_BACK_COVER_TYPE("OPINBLK", 14);
+                CUSTOM_BACK_COVER_TYPE.OPINGRN = new CUSTOM_BACK_COVER_TYPE("OPINGRN", 15);
+                CUSTOM_BACK_COVER_TYPE.OPINBLU = new CUSTOM_BACK_COVER_TYPE("OPINBLU", 16);
+                CUSTOM_BACK_COVER_TYPE.OPINGRD = new CUSTOM_BACK_COVER_TYPE("OPINGRD", 17);
+                CUSTOM_BACK_COVER_TYPE.OPINIB = new CUSTOM_BACK_COVER_TYPE("OPINIB", 18);
+                CUSTOM_BACK_COVER_TYPE.OPAVICGRIR = new CUSTOM_BACK_COVER_TYPE("OPAVICGRIR", 19);
+                CUSTOM_BACK_COVER_TYPE.OPAVICBLIC = new CUSTOM_BACK_COVER_TYPE("OPAVICBLIC", 20);
+                CUSTOM_BACK_COVER_TYPE.OPAVICGRFR = new CUSTOM_BACK_COVER_TYPE("OPAVICGRFR", 21);
+                CUSTOM_BACK_COVER_TYPE.OPKEBABGD = new CUSTOM_BACK_COVER_TYPE("OPKEBABGD", 22);
+                CUSTOM_BACK_COVER_TYPE.OPKEBABBG = new CUSTOM_BACK_COVER_TYPE("OPKEBABBG", 23);
+                CUSTOM_BACK_COVER_TYPE.OPKEBABSG = new CUSTOM_BACK_COVER_TYPE("OPKEBABSG", 24);
+                CUSTOM_BACK_COVER_TYPE.OPN1MB = new CUSTOM_BACK_COVER_TYPE("OPN1MB", 25);
+                CUSTOM_BACK_COVER_TYPE v0 = new CUSTOM_BACK_COVER_TYPE("OPN2GB", 26);
+
+    '''
+
+
+class paramtools():
+    paramitems = {
+        sid.PARAM_SID_PRODUCT.value[0]: {
+            0x18: ["8c", "project_name"],
+            0x20: ["I", "hw_version"],
+            0x24: ["I", "rf_version"],
+            0x28: ["16c", "rf_config_str"],
+            0x38: ["I", "operator_num"],
+            0x3C: ["10c", "operator_str"],
+            0x4C: ["B", "Length PCBA_number"],
+            0x4D: ["27c", "pcba_number"],
+            0x68: ["I", "boot_aging_count"],
+            0x6C: ["48c", "ota_info"],
+            0x9C: ["80c", "firmware_info"],
+            0xEC: ["32c", "build_info"],
+            0x10C: ["I", "unknown_dword"],
+            0x1A0: ["I", "OemCheckResetDevInfo"]  # if 1, then reset dev info. if 2, then do below, wth is this ?
+        },
+        sid.PARAM_SID_CONFIG.value[0]: {
+            0x18: ["B", "dump_enable"],
+        },
+        sid.PARAM_SID_CRASH_RECORD.value[0]: {
+            0x18: ["I", "crash_record_count"],
+            0x1c: ["20c", "Crash log 1"],
+            0x30: ["20c", "Crash log 2"],
+            0x44: ["20c", "Crash log 3"],
+            0x58: ["20c", "Crash log 4"],
+            0x6c: ["20c", "Crash log 5"],
+            0x80: ["20c", "Crash log 6"],
+            0x94: ["20c", "Crash log 7"],
+            0xa8: ["20c", "Crash log 8"],
+            0xbc: ["20c", "Crash log 9"],
+            0xd0: ["20c", "Crash log 10"],
+            0xe4: ["20c", "Crash log 11"],
+            0xf8: ["20c", "Crash log 12"],
+            0x10c: ["20c", "Crash log 13"],
+            0x120: ["20c", "Crash log 14"],
+            0x134: ["20c", "Crash log 15"],
+            0x15c: ["I", "restart_08_count"],
+            0x160: ["I", "restart_other_count"]
+        },
+        sid.PARAM_SID_SALEINFO.value[0]: {  # 0xA, Param_saleinfo, PARAM_SID_SALEINFO
+            0x18: ["I", "is_rooted"],
+            0x1c: ["I", "root_time"],
+            0x20: ["16c", "flash_0"],
+            0x30: ["16c", "flash_1"],
+            0x40: ["16c", "flash_2"],
+            0x50: ["16c", "erase_0"],
+            0x60: ["16c", "erase_1"],
+            0x70: ["16c", "erase_2"],
+            0x80: ["I", "is_angela"],
+            # adb shell am start -n com.android.engineeringmode/.qualcomm.DiagEnabled --es "code" "angela"
+            # adb shell am start -n com.oneplus.factorymode/.qualcomm.DiagEnabled --es "code" "angela"
+            # disable: "setprop persist.sys.adb.engineermode 0" and "setprop persist.sys.adbroot 0" or call code *#8011#
+            0x88: ["I", "Unknown flag"],
+            0x8c: ["I", "Unknown value"],
+            0x90: ["I", "Unknown flag"],
+        },
+        sid.PARAM_SID_MISC.value[0]: {  # 0xB
+            0x20: ["I", "Misc flag 1"],
+            0x24: ["44c", "Misc log a_1"],
+            0x50: ["I", "Misc flag a_2"],
+            0x54: ["44c", "Misc log a_2"],
+            0x80: ["I", "Misc flag a_3"],
+            0x84: ["44c", "Misc log a_3"],
+            0xb0: ["I", "Misc flag a_4"],
+            0xb4: ["44c", "Misc log a_4"],
+            0xe4: ["I", "Misc flag b_1"],
+            0xe8: ["44c", "Misc log b_1"],
+            0x114: ["I", "Misc flag b_2"],
+            0x118: ["44c", "Misc log b_2"],
+            0x144: ["I", "Misc flag b_3"],
+            0x148: ["44c", "Misc log b_3"],
+            0x174: ["I", "Misc flag b_4"],
+            0x178: ["44c", "Misc log b_4"],
+            0x1a8: ["I", "Misc flag c_1"],
+            0x1ac: ["44c", "Misc log c_1"],
+            0x1d8: ["I", "Misc flag c_2"],
+            0x1dc: ["44c", "Misc log c_2"],
+            0x208: ["I", "Misc flag c_3"],
+            0x20c: ["44c", "Misc log c_3"],
+            0x238: ["I", "Misc flag c_4"],
+            0x23c: ["44c", "Misc log c_4"],
+        },
+        sid.PARAM_SID_DOWNLOAD.value[0]: {  # 0xC
+            0x18: ["24c", "Unknown date"],
+            0x30: ["B", "SMT_Download_Status"],
+            0x32: ["B", "Unknown flag"],
+            0x33: ["B", "Unknown flag"],
+            0x38: ["32c", "Unknown string_1"],
+            0xd8: ["24c", "Unknown date_1"],
+            0xF0: ["B", "Upgrade_Download_Status_1"],
+            0xF2: ["B", "Unknown flag_1"],
+
+            0xF8: ["32c", "Unknown string_2"],
+            0x118: ["24c", "Unknown date_2"],
+            0x130: ["B", "Upgrade_Download_Status_2"],
+            0x132: ["B", "Unknown flag_2"],
+
+            0x138: ["32c", "Unknown string_3"],
+            0x158: ["24c", "Unknown date_3"],
+            0x170: ["B", "Upgrade_Download_Status_3"],
+            0x172: ["B", "Unknown flag_3"],
+
+            0x178: ["32c", "Unknown string_4"],
+            0x188: ["24c", "Unknown date_4"],
+
+            0x198: ["I", "boot_stage"],
+            0x19C: ["I", "data_stage"],
+            0x1A0: ["B", "reset_devinfo"],  # OemCheckResetRevInfo
+            0x1A4: ["B", "intranet_3t"],
+            0x1A8: ["B", "bootmode_3t"]
+        },
+
+        sid.PARAM_SID_PHONE_HISTORY.value[0]: {  # 0xD
+            0x24: ["I", "Update_Count"],
+            0x28: ["I", "Unlock_Count"],
+            0x2c: ["I", "Unknown value"],
+            0x30: ["I", "param_poweroff_count"],
+            0x34: ["I", "abl_tamper"],
+        },
+
+        sid.PARAM_SID_DL_USERINFO.value[0]: {  # 0xE
+            0x20: ["32c", "Computername_1"],
+            0x40: ["32c", "Username_1"],
+            0x60: ["I", "Flag_1"],
+            0x68: ["20c", "IP_1"],
+            0x7C: ["I", "Chksum_1_0"],
+            0xA0: ["32c", "Computername_2"],
+            0xC0: ["32c", "Username_2"],
+            0xE0: ["I", "Flag_2"],
+            0xE8: ["20c", "IP_2"],
+            0xFC: ["I", "Chksum_2"],
+            0x120: ["32c", "Computername_2"],
+            0x140: ["32c", "Username_2"],
+            0x160: ["I", "Flag_2"],
+            0x168: ["20c", "IP_2"],
+            0x17C: ["I", "Chksum_2"],
+            0x1A0: ["32c", "Computername_3"],
+            0x1C0: ["32c", "Username_3"],
+            0x1E0: ["I", "Flag_3"],
+            0x1E8: ["20c", "IP_3"],
+            0x1FC: ["I", "Chksum_3"],
+        },
+
+        0xB0: {
+            0x2b4: ["128B", "Unknown"]
+        },
+
+        sid.PARAM_SID_ENC_SECRECY.value[0]: {  # 0x12C
+            0x80: ["I", "intranet"],  # Allows factory commands via fastboot ops, *#808# engineermode
+            # dumpsys secrecy dump (persist)
+            # fastboot ops 4F50040TR18FTR7FSTD5F01
+            # fastboot ops help
+            0x84: ["I", "boottype"],  # 0xA9E:"sdebug"; 0xB7:"debug";0xA0:"auto";0x0:"normal"
+            0x88: ["I", "ONLINE_CFG_TEST_ENV"],
+            0x8C: ["I", "TargetSWID"],
+            0x90: ["I", "AgingFlag"]
+        },
+
+        sid.PARAM_SID_ENC_CARRIER.value[0]: {  # 0x130
+            0xA0: ["I", "CustFlag"],
+            0xA4: ["I", "CustFlagMigrationPlaintext"],
+            0xA8: ["I", "carrier_id"],
+            0xAC: ["I", "carrier_init_flag"]
+        },
+        sid.PARAM_SID_ENC_MOBID.value[0]: {
+            0x80: ["16c", "mobid/imei/meid1"],
+            0x90: ["I", "mobid/imei_flag"],
+            0x94: ["I", "Recondition flag (RCF)"],
+            0x98: ["19c", "Unknown date1"],
+            0xB0: ["19c", "Unknown date2"],
+            0xC8: ["19c", "Unknown date3"],
+            0xE0: ["8c", "Unknown_value_000000"],
+            0xE8: ["16c", "mobid/imei/meid2"],
+            0xF0: ["I", "mobid/meid flag"],
+            0xF8: ["I", "Unknown flag"]
+        },
+        sid.PARAM_SID_ENC_CVE.value[0]: {
+            0x80: ["16c", "CVE_SystemBlob_A"],
+            0x90: ["16c", "CVE_VendorBlob_A"],
+            0xA0: ["16c", "CVE_SystemBlob_B"],
+            0xB0: ["16c", "CVE_VendorBlob_B"],
+            0xC0: ["16c", "CVE_Current_BootImg_A"],
+            0xD0: ["16c", "CVE_Current_BootImg_B"],
+            0xE0: ["I", "PWD Index 1"],
+            0xE4: ["40c", "PWD Hash 1"],
+            0x114: ["40c", "PWD Hash 2"],
+            0x144: ["40c", "PWD Hash 3"],
+            0x174: ["40c", "PWD Hash 4"],
+            0x1a4: ["I", "PWD Index 2"],
+            0x1a8: ["40c", "PWD Hash 1"],
+            0x1d8: ["40c", "PWD Hash 2"],
+            0x208: ["40c", "PWD Hash 3"],
+            0x238: ["40c", "PWD Hash 4"],
+            0x268: ["I", "PWD Index 3"],
+            0x26c: ["40c", "PWD Hash 1"],
+            0x29c: ["40c", "PWD Hash 2"],
+            0x2cc: ["40c", "PWD Hash 3"],
+            0x2fc: ["40c", "PWD Hash 4"],
+        }
+    }
+
+    def __init__(self, mode, serial):
+        self.aes_iv = unhexlify("562E17996D093D28DDB3BA695A2E6F58")
+        self.aes_key = unhexlify("3030304F6E65506C7573383138303030")
+        if mode==1:
+            derivedkey=bytes.fromhex("a9264fbf8a"+("%08x"%serial)+"6b4487ea")[:0x1A]
+            derivedkey=hashlib.sha256(derivedkey).digest()[:16]
+            self.aes_key = derivedkey
+
+    def getparam(self, offset, sidindex):
+        if sidindex & 0x1FF in self.paramitems:
+            siditems = self.paramitems[sidindex & 0x1FF]
+            if offset in siditems:
+                return siditems[offset]
+        return None
+
+    def decryptsid(self, data):
+        aes = cryptutils().aes()
+        hash = cryptutils().hash()
+        header = data[:4 + 1 + 1]
+        magic, hv, cv = unpack("<IBB", header)
+        updatecounter = data[0x10]
+        if magic != 0xA0AD646A:
+            return None, None, None, None
+
+        enchash = data[0x80:0x90]
+        encdata = data[0x400:0x400 + 0xC00]
+        genenchash = hash.md5(encdata)
+        if genenchash != enchash:
+            print(
+                f"Generated hash doesn't match encrypted hash:\n{hexlify(genenchash).decode('utf-8')}\n{hexlify(enchash).decode('utf-8')}")
+            return None, None, None, None
+        decdata = aes.aes_cbc(self.aes_key, self.aes_iv, encdata)
+        dechash = decdata[:16]
+        itemdata = decdata[-0xB80:]
+        gendechash = hash.md5(itemdata)
+        if gendechash != dechash:
+            print(
+                f"Generated hash doesn't match decrypted hash:\n{hexlify(gendechash).decode('utf-8')}\n{hexlify(dechash).decode('utf-8')}")
+            return None, None, None, None
+        return itemdata, hv, cv, updatecounter
+
+    def encryptsid(self, itemdata, hv, cv, updatecounter):
+        aes = cryptutils().aes()
+        hash = cryptutils().hash()
+        magic = 0xA0AD646A
+        siddata = bytearray(b"\x00" * 0x1000)
+        siddata[:4 + 1 + 1] = pack("<IBB", magic, hv, cv)
+        siddata[0x10] = updatecounter + 1
+
+        header = bytearray(b"\00" * 0x80)
+        gendechash = hash.md5(itemdata)
+        header[0:16] = gendechash
+        decdata = header + itemdata
+        encdata = aes.aes_cbc(self.aes_key, self.aes_iv, decdata, False)
+        genenchash = hash.md5(encdata)
+
+        siddata[0x80:0x90] = genenchash
+        siddata[0x400:0x400 + 0xC00] = encdata
+        return siddata
+
+    def parse_encrypted(self, rdata, sid):
+        data = rdata[(sid * 0x400):(sid * 0x400) + 0x1000]
+        itemdata, hv, cv, updatecounter = self.decryptsid(data)
+        if itemdata != None:
+            itemdata = bytearray(itemdata)
+            print(
+                f"Offset {hex(sid * 0x400)}: hv {hex(hv)}, cv {hex(cv)}, increase_enc_update_counter {hex(updatecounter)}.")
+            i = 0
+            while i < len(itemdata):
+                offset = i + 0x80
+                param = self.getparam(offset, sid)
+                if param is None:
+                    if i + 4 < len(itemdata):
+                        value = unpack("<I", itemdata[i:i + 4])[0]
+                        if value != 0x0:
+                            print(f"Encrypted SID_Index {hex(sid)}, Offset {hex(offset)}: {hex(value)}")
+
+                    i += 4
+                else:
+                    length = self.parse_data(i, itemdata, offset, param, sid, True)
+                    i += length
+                    if length % 4:
+                        i += 4 - (length % 4)
+            print()
+
+    def parse_encrypted_fields(self, rdata):
+        for sid in range(0x12c, 0x139):
+            self.parse_encrypted(rdata, sid)
+        # Backup
+        """
+        for sid in range(0x32c, 0x339):
+            self.parse_encrypted(rdata, sid)
+        """
+
+    def parse_decrypted_fields(self, rdata):
+        for pos in range(0x0, 0x40000, 0x400):
+            if pos >= len(rdata):
+                break
+            data = rdata[pos:pos + 0x18]
+            fm = data[0:0x10].replace(b'\x00', b'').decode('utf-8')
+            if fm != "":
+                print()
+                print(f"Offset {hex(pos)}: Field {fm}")
+            itemlength = unpack("<I", data[0x14:0x18])[0]
+            if itemlength == 0x0:
+                itemlength = 0x400
+            itemdata = rdata[pos + 0x18:pos + 0x18 + itemlength]
+            i = 0
+            while (i < len(itemdata) - 0x22):
+                sidindex = (pos // 0x400) & 0x1FF
+                offset = i + 0x18
+                # if sidindex==0x334 and offset==0x80:
+                #    print(hexlify(itemdata).decode('utf-8'))
+                param = self.getparam(offset, sidindex)
+                if param is None:
+                    if itemdata[i] != 0:
+                        if i + 4 < len(itemdata):
+                            value = unpack("<I", itemdata[i:i + 4])[0]
+                            if value != 0x0:
+                                print(f"SID_Index {hex(sidindex)}, Offset {hex(offset)}: {hex(value)}")
+                        i += 4
+                    else:
+                        i += 1
+                else:
+                    length = self.parse_data(i, itemdata, offset, param, sidindex)
+                    i += length
+                    if length > 4:
+                        if (length % 4):
+                            i += 4 - (length % 4)
+
+    def parse_data(self, i, itemdata, offset, param, sidindex, encrypted=False):
+        stype = param[0]
+        name = param[1]
+        length = calcsize(stype)
+        item = itemdata[i:i + length]
+        content = unpack(stype, item)
+        try:
+            content = "\"" + b"".join(content).replace(b'\x00', b'').decode('utf-8') + "\""
+        except:
+            if len(content) == 1:
+                content = hex(content[0])
+            else:
+                tm = ""
+                for item in content:
+                    if item == 0:
+                        break
+                    tm += hex(item)
+                content = tm
+        offsetstr = hex(offset)
+        while len(offsetstr) < 5:
+            offsetstr = offsetstr[:2] + "0" + offsetstr[2:]
+        while len(name) < 30:
+            name = name + " "
+        if "PWD Hash" in name:
+            items = content.split(" ")
+            pwdhash = items[0][1:9]+"00000000000000000000000000000000000000000000000000000000"
+            valid = "True" if items[1] != "-1" else "False"
+            flag = items[2]
+            date = items[3] + " "+ items[4][:-1]
+            content = f"{date} ({valid},{flag}): {pwdhash}"
+            ff = f"SID_Index {hex(sidindex)}, Offset {offsetstr}: {name}: {content}"
+            if encrypted:
+                ff = "Encrypted " + ff
+        else:
+            ff = f"SID_Index {hex(sidindex)}, Offset {offsetstr}: {name}: {content}"
+            if encrypted:
+                ff = "Encrypted " + ff
+        print(ff)
+        return length
+
+    def setparamvalue(self, data, sid, offset, value):
+        if sid > 0x100:
+            rdata = data[sid * 0x400:(sid * 0x400) + 0x1000]
+            itemdata, hv, cv, updatecounter = self.decryptsid(rdata)
+            if itemdata is not None:
+                itemdata = bytearray(itemdata)
+                if isinstance(value, int):
+                    itemdata[offset - 0x80:(offset + 4) - 0x80] = pack("<I", value)
+                elif isinstance(value, bytearray):
+                    itemdata[offset - 0x80:(offset + len(value)) - 0x80] = value
+                # itemdata[0x84-0x80:(0x84+4)-0x80]=pack("<I",0xB7) #devkmsg_enable
+                mdata = self.encryptsid(itemdata, hv, cv, updatecounter)
+            data = bytearray(data)
+            data[sid * 0x400:(sid * 0x400) + 0x1000] = mdata
+            # data[(sid+0x200) * 0x400:((sid+0x200) * 0x400) + 0x1000] = mdata
+        else:
+            rdata = data[sid * 0x400:(sid * 0x400) + 0x1000]
+            data = bytearray(data)
+            rdata = bytearray(rdata)
+            if isinstance(value, int):
+                rdata[offset:offset + 4] = pack("<I", value)
+            elif isinstance(value, bytearray):
+                rdata[offset:(offset + len(value))] = value
+            data[sid * 0x400:(sid * 0x400) + 0x1000] = rdata
+        return data
+
+    def gencode(self, inparray):
+        res = []
+        for input in inparray:
+            m = hashlib.sha1()
+            m.update(bytes("ONEPLUS_" + input, 'utf-8'))
+            hash = m.hexdigest().lower()
+            crcdata = hex(zlib.crc32(bytes(hash, 'utf-8')))[2:]
+            while len(crcdata) < 8:
+                crcdata = '0' + crcdata
+            res.append(crcdata)
+        return res
+
+    def enable_ops(self, data, enable):
+        sid = 0x12C
+        offset = 0x80  # intranet
+        if enable:
+            value = 0x3
+        else:
+            value = 0x0
+        return self.setparamvalue(data, sid, offset, value)
+
+
+'''
+ONEPLUS_358240051111110 => sha1 = 99e489e03107817f14ac06a1bb52de3455423542 => CRC32 -> 
+ecec6492
+ONEPLUS_YOU_CAN_PASS_NOW => sha1 = 9db4b7338e254669b8c703e77cbc9f119ade7fa6 => CRC32 -> 79707450 (wenn < 8, dann fill mit 0)
+'''
+'''
+com.android.engineeringmode.encrypt
+
+3439 = oem.cust.flag 1 = French custom edition
+9339 = oem.cust.flag 2 = Indian edition
+3392 = oem.cust.flag 0 = Normal edition
+7668 = com.android.engineeringmode.manualtest.CheckRootStatusActivity
+5646 = com.android.engineeringmode.manualtest.DecryptActivity
+838 =  com.android.engineeringmode.manualtest.CheckRootStatusActivity
+
+com.oneplus.factorymode.CommonCommands
+com.oem.engineermode.DoShellCommond
+if(!arg18.getAction().equals(v1.mAction) && !arg18.getAction().equals(v1.na)) {
+if((arg18.getAction().equals(v1.doshellcommond)) && ("get_config_params".equals(v0.getStringExtra("shell_cmd")))) {
+adb shell am start -n com.oem.engineermode.DoShellCommond --es "shell_cmd" "get_config_params"
+
+action android:name
+adb shell am start -n com.android.engineeringmode/.qualcomm.DiagEnabled --es "code" "Angela"
+79a6a933dfc9b1975e444d4e8481c64c771d8ab40b7ac72f8bc1a1bca1718bef
+
+android.provider.Telephony.SECRET_CODE
+*#66# com.android.engineeringmode.IMeiAndPcbCheck
+*#99# com.android.engineeringmode.KeepSrceenOn
+*#008# com.android.engineeringmode.LanguageSwitchToChineseActivity
+*#268# com.android.engineeringmode.qualcomm.QualCommActivity
+*#391# com.android.engineeringmode.SwitchSoftwareVersion
+*#789# com.android.engineeringmode.qualcomm.ClearTelcelnetlock
+*#800# com.android.engineeringmode.qualcomm.LogSwitch
+*#800# com.oem.oemlogkit.OEMLogKitMainActivity
+*#801# com.android.engineeringmode.qualcomm.DiagEnabled
+*#802# com.android.engineeringmode.gps.GpsActivity
+*#803# com.android.engineeringmode.wifitest.WifiSettings
+*#804# com.android.engineeringmode.NetworkSearch
+*#805# com.android.engineeringmode.bluetoothtest.BluetoothTest
+*#806# com.android.engineeringmode.autoaging.AutoAgingMainListActivity
+*#807# com.android.engineeringmode.autotest.AutoTest
+*#808# com.android.engineeringmode.EngineerModeMain
+*#808# com.android.engineeringmode.manualtest.ManualTest
+*#809# com.android.engineeringmode.echotest.EchoTest
+*#810# com.android.engineeringmode.SwitchSetupWizardActivity
+*#814# com.android.engineeringmode.TDSNetworkSearch
+*#818# com.android.engineeringmode.NetworkSearch_New
+*#820# com.android.engineeringmode.DeleteNoNeedFilesActivity
+*#824# com.android.engineeringmode.WCDMANetworkSearch
+*#834# com.android.engineeringmode.LTENetworkSearch
+*#838# com.android.engineeringmode.NetworkSearch_New
+*#845# com.android.engineeringmode.wifitest.WifiApSettings
+*#888# com.android.engineeringmode.PcbShow
+*#899# com.oneplus.factorymode.aftersale.ChooseBackCoverColor
+*#900# com.android.engineeringmode.BackCameraAdjusting
+*#911# com.android.engineeringmode.PowerOff (Warning: Does factory reset)
+*#912# com.android.engineeringmode.qualcomm.RecoverTelcelnetlock
+*#912# com.android.engineeringmode.RebootManager
+*#928# com.android.engineeringmode.wifitest.WifiFTMActivity
+*#1234# com.android.engineeringmode.SHOW_ONEPLUS_VERSION
+*#4321# com.android.engineeringmode.LanguageSwitchToZimbabweService
+*#6776# com.android.engineeringmode.CheckSoftwareInfo
+*#7327# com.oem.rftoolkit.RfToolkitCustomerService
+*#7328# com.oem.rftoolkit.RfToolkitAgingTest
+*#7332# com.oem.rftoolkit.RfToolkitFactory
+*#8011# com.android.engineeringmode.NoUI
+*#8017# com.android.engineeringmode.wifitest.WifiAdbHelper
+*#8019# com.android.engineeringmode.wifitest.WifiSocketHelper
+*#8020# com.android.engineeringmode.wifitest.WifiAdbHelper
+*##*8110# com.android.engineeringmode.qualcomm.OtaSwitch
+*#8668# com.oneplus.activation.action.STOP_ACTIVATION
+*#8669# com.oneplus.activation.action.START_ACTIVATION
+*#8778# com.android.engineeringmode.manualtest.MasterClear
+*#8888# com.android.engineeringmode.manualtest.MasterClear
+*#9886# com.oneplus.screensaver.off
+*#9889# com.oneplus.screensaver.on
+*#10000# com.android.engineeringmode.MarkResultShow
+*#12345# com.android.activation.action.STOP_SERVICE
+*#3954391# Switch activated
+*##*37847# com.android.engineeringmode.manualtest.DeviceListActivity
+*#2288379# com.android.engineeringmode.BatteryExtraInfo
+*#36446337# com.android.engineeringmode.EngineeringMode
+*#6776001# set_language en_US
+*#6776007# set_language ru_RU
+*#67760052# set_language es_MX
+*#67760055# set_language pt_BR
+*#67760066# set_language th_TH
+*#67760062# set_language in_IN
+*#67760084# set_language vi_VI
+*#67760086# set_language zh_CN
+*#67760886# set_language zh_TW
+*#67760044# com.android.engineeringmode.LanguageSwitchToEnglishActivity
+*#67766776# com.android.engineeringmode.oneplusConnectionADBActivity
+*#677667764482# com.android.engineeringmode.UserAgentSwitchService
+*#*#5646#*#* com.android.engineeringmode.manualtest.DecryptActivity
+*#*#7668#*#* com.android.engineeringmode.manualtest.CheckRootStatusActivity
+*#3439# = oem.cust.flag=1
+*#9339# = oem.cust.flag=2
+*#3392# = oem.cust.flag=0
+
+fastboot ops 4F50040TR18FTR7FSTD5F01
+fastboot ops help
+
+fastboot ops 4F50040TR18FTR7FSTD5F01
+fastboot ops devkmsg on
+fastboot ops boottype debug
+
+
+(bootloader) ops android_log_all
+(bootloader) ops kernel_log_all
+(bootloader) ops devkmsg
+(bootloader) ops boottype [normal, debug, sdebug]
+(bootloader) ops set_cust
+(bootloader) ops force_adb
+(bootloader) ops qeaging_data_img
+(bootloader) ops unmount tempfs
+(bootloader) ops mount tempfs
+(bootloader) ops enable_dm_verity
+(bootloader) ops disable_dm_verity
+(bootloader) ops boot_mode [rf,ftm,wlan,normal]
+(bootloader) ops selinux
+(bootloader) ops dump
+(bootloader) ops help
+(bootloader) ops datafs
+(bootloader) ops unforce_training
+(bootloader) ops force_training
+(bootloader) ops reboot-bootloader
+(bootloader) ops reboot-shutdown
+(bootloader) ops kmemleak undetect
+(bootloader) ops kmemleak detect
+(bootloader) ops unconsole
+(bootloader) ops console
+(bootloader) oem get_unlock_code
+(bootloader) oem lock
+(bootloader) oem unlock
+(bootloader) download:
+(bootloader) getvar:
+(bootloader) reboot-bootloader
+(bootloader) reboot
+(bootloader) continue
+(bootloader) oem device-info
+(bootloader) oem select-display-panel
+(bootloader) oem off-mode-charge
+(bootloader) oem disable-charger-screen
+(bootloader) oem enable-charger-screen
+(bootloader) boot
+(bootloader) flashing lock
+(bootloader) flashing unlock
+(bootloader) flashing get_unlock_ability
+(bootloader) set_active
+(bootloader) erase:
+(bootloader) flash:
+(bootloader) Varlist
+
+(bootloader) hw-revision:20001
+(bootloader) unlocked:no
+(bootloader) off-mode-charge:1
+(bootloader) charger-screen-enabled:1
+(bootloader) battery-soc-ok:yes
+(bootloader) battery-voltage:4360
+(bootloader) version-baseband:
+(bootloader) version-bootloader:
+(bootloader) erase-block-size: 0x1000
+(bootloader) logical-block-size: 0x1000
+(bootloader) variant:SDM UFS
+(bootloader) partition-type:fsc:raw
+(bootloader) partition-size:fsc: 0x20000
+(bootloader) partition-type:fsg:raw
+(bootloader) partition-size:fsg: 0x200000
+(bootloader) partition-type:modemst2:raw
+(bootloader) partition-size:modemst2: 0x200000
+(bootloader) partition-type:modemst1:raw
+(bootloader) partition-size:modemst1: 0x200000
+(bootloader) partition-type:ALIGN_TO_128K_2:raw
+(bootloader) partition-size:ALIGN_TO_128K_2: 0x1A000
+(bootloader) partition-type:ImageFv:raw
+(bootloader) partition-size:ImageFv: 0x200000
+(bootloader) partition-type:logdump:raw
+(bootloader) partition-size:logdump: 0x4000000
+(bootloader) partition-type:sti:raw
+(bootloader) partition-size:sti: 0x200000
+(bootloader) partition-type:logfs:raw
+(bootloader) partition-size:logfs: 0x800000
+(bootloader) partition-type:toolsfv:raw
+(bootloader) partition-size:toolsfv: 0x100000
+(bootloader) partition-type:limits:raw
+(bootloader) partition-size:limits: 0x1000
+(bootloader) partition-type:splash:raw
+(bootloader) partition-size:splash: 0x20A4000
+(bootloader) partition-type:spunvm:raw
+(bootloader) partition-size:spunvm: 0x800000
+(bootloader) partition-type:msadp:raw
+(bootloader) partition-size:msadp: 0x40000
+(bootloader) partition-type:apdp:raw
+(bootloader) partition-size:apdp: 0x40000
+(bootloader) partition-type:dip:raw
+(bootloader) partition-size:dip: 0x100000
+(bootloader) partition-type:devinfo:raw
+(bootloader) partition-size:devinfo: 0x1000
+(bootloader) partition-type:sec:raw
+(bootloader) partition-size:sec: 0x4000
+(bootloader) partition-type:op1:raw
+(bootloader) partition-size:op1: 0x6400000
+(bootloader) partition-type:aging:raw
+(bootloader) partition-size:aging: 0x4000000
+(bootloader) partition-type:minidump:raw
+(bootloader) partition-size:minidump: 0x6400000
+(bootloader) partition-type:fw_ufs8_b:raw
+(bootloader) partition-size:fw_ufs8_b: 0x200000
+(bootloader) partition-type:fw_ufs7_b:raw
+(bootloader) partition-size:fw_ufs7_b: 0x200000
+(bootloader) partition-type:fw_ufs6_b:raw
+(bootloader) partition-size:fw_ufs6_b: 0x200000
+(bootloader) partition-type:fw_ufs5_b:raw
+(bootloader) partition-size:fw_ufs5_b: 0x200000
+(bootloader) partition-type:fw_ufs4_b:raw
+(bootloader) partition-size:fw_ufs4_b: 0x200000
+(bootloader) partition-type:fw_ufs3_b:raw
+(bootloader) partition-size:fw_ufs3_b: 0x200000
+(bootloader) partition-type:fw_4u1ea_b:raw
+(bootloader) partition-size:fw_4u1ea_b: 0x200000
+(bootloader) partition-type:fw_4j1ed_b:raw
+(bootloader) partition-size:fw_4j1ed_b: 0x200000
+(bootloader) partition-type:LOGO_b:raw
+(bootloader) partition-size:LOGO_b: 0x1000000
+(bootloader) partition-type:storsec_b:raw
+(bootloader) partition-size:storsec_b: 0x20000
+(bootloader) partition-type:dtbo_b:raw
+(bootloader) partition-size:dtbo_b: 0x800000
+(bootloader) partition-type:vbmeta_b:raw
+(bootloader) partition-size:vbmeta_b: 0x10000
+(bootloader) partition-type:vendor_b:raw
+(bootloader) partition-size:vendor_b: 0x40000000
+(bootloader) partition-type:qupfw_b:raw
+(bootloader) partition-size:qupfw_b: 0x10000
+(bootloader) partition-type:devcfg_b:raw
+(bootloader) partition-size:devcfg_b: 0x20000
+(bootloader) partition-type:cmnlib64_b:raw
+(bootloader) partition-size:cmnlib64_b: 0x80000
+(bootloader) partition-type:cmnlib_b:raw
+(bootloader) partition-size:cmnlib_b: 0x80000
+(bootloader) partition-type:boot_b:raw
+(bootloader) partition-size:boot_b: 0x4000000
+(bootloader) partition-type:keymaster_b:raw
+(bootloader) partition-size:keymaster_b: 0x80000
+(bootloader) partition-type:dsp_b:raw
+(bootloader) partition-size:dsp_b: 0x2000000
+(bootloader) partition-type:abl_b:raw
+(bootloader) partition-size:abl_b: 0x800000
+(bootloader) partition-type:mdtp_b:raw
+(bootloader) partition-size:mdtp_b: 0x2000000
+(bootloader) partition-type:mdtpsecapp_b:raw
+(bootloader) partition-size:mdtpsecapp_b: 0x400000
+(bootloader) partition-type:bluetooth_b:raw
+(bootloader) partition-size:bluetooth_b: 0x100000
+(bootloader) partition-type:modem_b:raw
+(bootloader) partition-size:modem_b: 0x7800000
+(bootloader) partition-type:hyp_b:raw
+(bootloader) partition-size:hyp_b: 0x80000
+(bootloader) partition-type:tz_b:raw
+(bootloader) partition-size:tz_b: 0x200000
+(bootloader) partition-type:aop_b:raw
+(bootloader) partition-size:aop_b: 0x80000
+(bootloader) partition-type:fw_ufs8_a:raw
+(bootloader) partition-size:fw_ufs8_a: 0x200000
+(bootloader) partition-type:fw_ufs7_a:raw
+(bootloader) partition-size:fw_ufs7_a: 0x200000
+(bootloader) partition-type:fw_ufs6_a:raw
+(bootloader) partition-size:fw_ufs6_a: 0x200000
+(bootloader) partition-type:fw_ufs5_a:raw
+(bootloader) partition-size:fw_ufs5_a: 0x200000
+(bootloader) partition-type:fw_ufs4_a:raw
+(bootloader) partition-size:fw_ufs4_a: 0x200000
+(bootloader) partition-type:fw_ufs3_a:raw
+(bootloader) partition-size:fw_ufs3_a: 0x200000
+(bootloader) partition-type:fw_4u1ea_a:raw
+(bootloader) partition-size:fw_4u1ea_a: 0x200000
+(bootloader) partition-type:fw_4j1ed_a:raw
+(bootloader) partition-size:fw_4j1ed_a: 0x200000
+(bootloader) partition-type:LOGO_a:raw
+(bootloader) partition-size:LOGO_a: 0x1000000
+(bootloader) partition-type:storsec_a:raw
+(bootloader) partition-size:storsec_a: 0x20000
+(bootloader) partition-type:dtbo_a:raw
+(bootloader) partition-size:dtbo_a: 0x800000
+(bootloader) partition-type:vbmeta_a:raw
+(bootloader) partition-size:vbmeta_a: 0x10000
+(bootloader) partition-type:vendor_a:raw
+(bootloader) partition-size:vendor_a: 0x40000000
+(bootloader) partition-type:qupfw_a:raw
+(bootloader) partition-size:qupfw_a: 0x10000
+(bootloader) partition-type:devcfg_a:raw
+(bootloader) partition-size:devcfg_a: 0x20000
+(bootloader) partition-type:cmnlib64_a:raw
+(bootloader) partition-size:cmnlib64_a: 0x80000
+(bootloader) partition-type:cmnlib_a:raw
+(bootloader) partition-size:cmnlib_a: 0x80000
+(bootloader) partition-type:boot_a:raw
+(bootloader) partition-size:boot_a: 0x4000000
+(bootloader) partition-type:keymaster_a:raw
+(bootloader) partition-size:keymaster_a: 0x80000
+(bootloader) partition-type:dsp_a:raw
+(bootloader) partition-size:dsp_a: 0x2000000
+(bootloader) partition-type:abl_a:raw
+(bootloader) partition-size:abl_a: 0x800000
+(bootloader) partition-type:mdtp_a:raw
+(bootloader) partition-size:mdtp_a: 0x2000000
+(bootloader) partition-type:mdtpsecapp_a:raw
+(bootloader) partition-size:mdtpsecapp_a: 0x400000
+(bootloader) partition-type:bluetooth_a:raw
+(bootloader) partition-size:bluetooth_a: 0x100000
+(bootloader) partition-type:modem_a:raw
+(bootloader) partition-size:modem_a: 0x7800000
+(bootloader) partition-type:hyp_a:raw
+(bootloader) partition-size:hyp_a: 0x80000
+(bootloader) partition-type:tz_a:raw
+(bootloader) partition-size:tz_a: 0x200000
+(bootloader) partition-type:aop_a:raw
+(bootloader) partition-size:aop_a: 0x80000
+(bootloader) partition-type:ddr:raw
+(bootloader) partition-size:ddr: 0x100000
+(bootloader) partition-type:cdt:raw
+(bootloader) partition-size:cdt: 0x20000
+(bootloader) partition-type:ALIGN_TO_128K_1:raw
+(bootloader) partition-size:ALIGN_TO_128K_1: 0x1A000
+(bootloader) partition-type:xbl_config_b:raw
+(bootloader) partition-size:xbl_config_b: 0x20000
+(bootloader) partition-type:xbl_b:raw
+(bootloader) partition-size:xbl_b: 0x380000
+(bootloader) partition-type:xbl_config_a:raw
+(bootloader) partition-size:xbl_config_a: 0x20000
+(bootloader) partition-type:xbl_a:raw
+(bootloader) partition-size:xbl_a: 0x380000
+(bootloader) partition-type:userdata:ext4
+(bootloader) partition-size:userdata: 0x1B800BB000
+(bootloader) partition-type:odm_b:raw
+(bootloader) partition-size:odm_b: 0x6400000
+(bootloader) partition-type:odm_a:raw
+(bootloader) partition-size:odm_a: 0x6400000
+(bootloader) partition-type:system_b:ext4
+(bootloader) partition-size:system_b: 0xB2C00000
+(bootloader) partition-type:system_a:ext4
+(bootloader) partition-size:system_a: 0xB2C00000
+(bootloader) partition-type:config:raw
+(bootloader) partition-size:config: 0x80000
+(bootloader) partition-type:reserve2:raw
+(bootloader) partition-size:reserve2: 0xFD0000
+(bootloader) partition-type:reserve1:raw
+(bootloader) partition-size:reserve1: 0x7E8000
+(bootloader) partition-type:oem_stanvbk:raw
+(bootloader) partition-size:oem_stanvbk: 0xA00000
+(bootloader) partition-type:oem_dycnvbk:raw
+(bootloader) partition-size:oem_dycnvbk: 0xA00000
+(bootloader) partition-type:op2:raw
+(bootloader) partition-size:op2: 0x10000000
+(bootloader) partition-type:frp:raw
+(bootloader) partition-size:frp: 0x80000
+(bootloader) partition-type:keystore:raw
+(bootloader) partition-size:keystore: 0x80000
+(bootloader) partition-type:param:raw
+(bootloader) partition-size:param: 0x100000
+(bootloader) partition-type:misc:raw
+(bootloader) partition-size:misc: 0x100000
+(bootloader) partition-type:persist:raw
+(bootloader) partition-size:persist: 0x2000000
+(bootloader) partition-type:ssd:raw
+(bootloader) partition-size:ssd: 0x2000
+(bootloader) has-slot:modem:yes
+(bootloader) has-slot:system:yes
+(bootloader) current-slot:b
+(bootloader) has-slot:boot:yes
+(bootloader) slot-retry-count:b:6
+(bootloader) slot-unbootable:b:no
+(bootloader) slot-successful:b:yes
+(bootloader) slot-retry-count:a:6
+(bootloader) slot-unbootable:a:no
+(bootloader) slot-successful:a:yes
+(bootloader) slot-count:2
+(bootloader) secure:yes
+(bootloader) serialno:45751efa
+(bootloader) product:sdm845
+(bootloader) max-download-size:536870912
+(bootloader) kernel:uefi
+
+25d52959
+
+am broadcast -n com.oneplus.factorymode/.EngineerModeActionReceiver -a android.provider.Telephony.SECRET_CODE -d android_secret_code://5646 
+
+am broadcast -n com.oneplus.factorymode/.EngineerModeActionReceiver -a com.android.engineeringmode.encrypt
+
+am broadcast -n com.oneplus.factorymode/.EngineerModeActionReceiver -a
+com.oem.engineermode.StartOEMLogMain
+
+IMEI:866241047809937
+'''
+
+
+def main():
+    from docopt import docopt
+    args = docopt(__doc__, version='oneplus 1.1')
+    # filename="param_jacob_7pro.bin"
+    # filename="chris/param.bin"
+    if args["param"]:
+        filename = args["<filename>"]
+        mode = args["--mode"]
+        serial = args["--serial"]
+        param = paramtools(mode,serial)
+        with open(filename, 'rb') as rf:
+            data = rf.read()
+            param.parse_decrypted_fields(data)
+            print("\nEncrypted Values:\n-----------------\n")
+            param.parse_encrypted_fields(data)
+            # with open(filename + ".patched", 'wb') as wf:
+            #    wf.write(param.setfactoryflags(data))
+    elif args["ops"]:
+        filename = args["<filename>"]
+        mode = args["--mode"]
+        serial = args["--serial"]
+        param = paramtools(mode,serial)
+        with open(filename, 'rb') as rf:
+            data = rf.read()
+            with open(filename + ".patched", 'wb') as wf:
+                try:
+                    data = param.setparamvalue(data, 0x12C, 0x80, 0x3)  # >= Oneplus 5
+                except:
+                    pass
+                data = param.setparamvalue(data, 0xC, 0x1A4, 0x1)  # < Oneplus 5
+                wf.write(data)
+    elif args["setparam"]:
+        filename = args["<filename>"]
+        sid = int(args["<sid>"], 16)
+        offset = int(args["<offset>"], 16)
+        value = int(args["<value>"], 16)
+        mode = args["--mode"]
+        serial = args["--serial"]
+        param = paramtools(mode,serial)
+        with open(filename, 'rb') as rf:
+            data = rf.read()
+            with open(filename + ".patched", 'wb') as wf:
+                wf.write(param.setparamvalue(data, sid, offset, value))
+    elif args["gencode"]:
+        imei = args["<imei>"]
+        mode = 0
+        serial = None
+        param = paramtools(mode,serial)
+        print("oneplus Factory qr code generator (c) B. Kerler 2019\nGPLv3 License\n----------------------")
+        print("Code : *#*#5646#*#* , *#808#, *#36446337# = com.android.engineeringmode.manualtest.DecryptActivity")
+        results = param.gencode([imei, "YOU_CAN_PASS_NOW"])
+        import qrcode
+        img = qrcode.make("op_eng://" + results[0])
+        print("Code : " + results[0])
+        img.save(imei + ".png")
+        print("Image written as " + imei + ".png")
+
+
+if __name__ == "__main__":
+    main()
diff --git a/edlclient/Library/Modules/xiaomi.py b/edlclient/Library/Modules/xiaomi.py
index 6602d8a..53eadee 100644
--- a/edlclient/Library/Modules/xiaomi.py
+++ b/edlclient/Library/Modules/xiaomi.py
@@ -1,6 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2021
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 
 import logging
 from edlclient.Library.utils import LogBase
@@ -31,15 +35,15 @@ class xiaomi(metaclass=LogBase):
 
     def edl_auth(self):
         """
-        Poco F1, Redmi 5 Pro, 6 Pro, 7 Pro, 7A, 8, 8A, 8A Dual, 8A Pro, Y2, S2
+        Redmi A1, Poco F1, Redmi 5 Pro, 6 Pro, 7 Pro, 7A, 8, 8A, 8A Dual, 8A Pro, Y2, S2
         """
         authcmd = b"<?xml version=\"1.0\" ?><data> <sig TargetName=\"sig\" size_in_bytes=\"256\" verbose=\"1\"/></data>"
         rsp = self.fh.xmlsend(authcmd)
-        if rsp[0]:
+        if rsp.resp:
             rsp = self.fh.xmlsend(self.xiaomi_authdata)
-            if len(rsp) > 1:
-                if rsp[0]:
-                    if b"EDL Authenticated" in rsp[2] or b"ACK" in rsp[2]:
-                        return True
-            return True
+            if rsp.resp:
+                if "value" in rsp.resp:
+                    if rsp.resp["value"]=="ACK":
+                        if 'authenticated' in rsp.log[0].lower() and 'true' in rsp.log[0].lower():
+                            return True
         return False
diff --git a/edlclient/Library/asmtools.py b/edlclient/Library/asmtools.py
index 9bda711..4d5354a 100755
--- a/edlclient/Library/asmtools.py
+++ b/edlclient/Library/asmtools.py
@@ -1,7 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2021
-
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 from capstone import *
 from keystone import *
 from binascii import unhexlify
diff --git a/edlclient/Library/cryptutils.py b/edlclient/Library/cryptutils.py
index d05f06c..9f7ff41 100755
--- a/edlclient/Library/cryptutils.py
+++ b/edlclient/Library/cryptutils.py
@@ -1,12 +1,16 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2019
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 
 import hashlib
-from Crypto.Cipher import AES
-from Crypto.Util import Counter
-from Crypto.Hash import CMAC
-from Crypto.Util.number import long_to_bytes, bytes_to_long
+from Cryptodome.Cipher import AES
+from Cryptodome.Util import Counter
+from Cryptodome.Hash import CMAC
+from Cryptodome.Util.number import long_to_bytes, bytes_to_long
 from binascii import hexlify, unhexlify
 
 
diff --git a/edlclient/Library/firehose.py b/edlclient/Library/firehose.py
index 2e41a11..cfeb66c 100755
--- a/edlclient/Library/firehose.py
+++ b/edlclient/Library/firehose.py
@@ -1,9 +1,14 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2019
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 
 import binascii
 import io
+import os.path
 import platform
 import re
 import time
@@ -12,10 +17,28 @@ from struct import unpack
 from binascii import hexlify
 from queue import Queue
 from threading import Thread
+
+from edlclient.Library.Modules.nothing import nothing
 from edlclient.Library.utils import *
-from edlclient.Library.gpt import gpt
+from edlclient.Library.gpt import gpt, AB_FLAG_OFFSET, AB_PARTITION_ATTR_SLOT_ACTIVE, MAX_PRIORITY, PART_ATT_PRIORITY_BIT
+from edlclient.Library.gpt import PART_ATT_PRIORITY_VAL, PART_ATT_ACTIVE_VAL, PART_ATT_MAX_RETRY_COUNT_VAL, PART_ATT_SUCCESSFUL_VAL, PART_ATT_UNBOOTABLE_VAL
 from edlclient.Library.sparse import QCSparse
 from edlclient.Library.utils import progress
+from queue import Queue
+from threading import Thread
+
+rq = Queue()
+
+def writedata(filename, rq):
+    pos = 0
+    with open(filename, "wb") as wf:
+        while True:
+            data = rq.get()
+            if data is None:
+                break
+            pos += len(data)
+            wf.write(data)
+            rq.task_done()
 
 
 class response:
@@ -191,7 +214,13 @@ class firehose(metaclass=LogBase):
         self.nandparttbl = None
         self.nandpart = nand_partition(parent=self, printer=print)
 
-    def detect_partition(self, arguments, partitionname):
+    def detect_partition(self, arguments, partitionname, send_full=False):
+        if arguments is None:
+            arguments = {
+                "--gpt-num-part-entries"     : 0,
+                "--gpt-part-entry-size"      : 0,
+                "--gpt-part-entry-start-lba" : 0
+            }
         fpartitions = {}
         for lun in self.luns:
             lunname = "Lun" + str(lun)
@@ -203,7 +232,7 @@ class firehose(metaclass=LogBase):
                 break
             else:
                 if partitionname in guid_gpt.partentries:
-                    return [True, lun, guid_gpt.partentries[partitionname]]
+                    return [True, lun, data, guid_gpt] if send_full else [True, lun, guid_gpt.partentries[partitionname]]
             for part in guid_gpt.partentries:
                 fpartitions[lunname].append(part)
         return [False, fpartitions]
@@ -267,7 +296,7 @@ class firehose(metaclass=LogBase):
                         if resp["rawmode"] == "false":
                             if status:
                                 log = self.xml.getlog(rdata)
-                                return response(resp=status, data=resp, log=log)
+                                return response(resp=status, data=rdata, log=log)
                             else:
                                 error = self.xml.getlog(rdata)
                                 return response(resp=status, error=error, data=resp, log=error)
@@ -304,7 +333,7 @@ class firehose(metaclass=LogBase):
 
     def cmd_reset(self, mode="reset"):
         if mode is None:
-            mode = "poweroff"
+            mode = "reset"
         data = "<?xml version=\"1.0\" ?><data><power value=\"" + mode + "\"/></data>"
         val = self.xmlsend(data)
         try:
@@ -340,7 +369,7 @@ class firehose(metaclass=LogBase):
     def cmd_nop(self):
         data = "<?xml version=\"1.0\" ?><data><nop /></data>"
         resp = self.xmlsend(data, True)
-        self.debug(resp.hex())
+        self.debug(resp.data.hex())
         info = b""
         tmp = None
         while tmp != b"":
@@ -607,6 +636,7 @@ class firehose(metaclass=LogBase):
         return True
 
     def cmd_read(self, physical_partition_number, start_sector, num_partition_sectors, filename, display=True):
+        global rq
         self.lasterror = b""
         progbar = progress(self.cfg.SECTOR_SIZE_IN_BYTES)
         if display:
@@ -614,52 +644,56 @@ class firehose(metaclass=LogBase):
                 f"\nReading from physical partition {str(physical_partition_number)}, " +
                 f"sector {str(start_sector)}, sectors {str(num_partition_sectors)}")
 
-        with open(file=filename, mode="wb", buffering=self.cfg.MaxPayloadSizeFromTargetInBytes) as wr:
-            data = f"<?xml version=\"1.0\" ?><data><read SECTOR_SIZE_IN_BYTES=\"{self.cfg.SECTOR_SIZE_IN_BYTES}\"" + \
-                   f" num_partition_sectors=\"{num_partition_sectors}\"" + \
-                   f" physical_partition_number=\"{physical_partition_number}\"" + \
-                   f" start_sector=\"{start_sector}\"/>\n</data>"
+        data = f"<?xml version=\"1.0\" ?><data><read SECTOR_SIZE_IN_BYTES=\"{self.cfg.SECTOR_SIZE_IN_BYTES}\"" + \
+               f" num_partition_sectors=\"{num_partition_sectors}\"" + \
+               f" physical_partition_number=\"{physical_partition_number}\"" + \
+               f" start_sector=\"{start_sector}\"/>\n</data>"
 
-            rsp = self.xmlsend(data, self.skipresponse)
-            self.cdc.xmlread = False
-            time.sleep(0.01)
-            if not rsp.resp:
-                if display:
-                    self.error(rsp.error)
-                return b""
-            else:
-                bytestoread = self.cfg.SECTOR_SIZE_IN_BYTES * num_partition_sectors
-                total = bytestoread
-                show_progress = progbar.show_progress
-                usb_read = self.cdc.read
-                progbar.show_progress(prefix="Read", pos=0, total=total, display=display)
-                while bytestoread > 0:
-                    if self.cdc.is_serial:
-                        maxsize = self.cfg.MaxPayloadSizeFromTargetInBytes
-                    else:
-                        maxsize = 5 * 1024 * 1024
-                    size = min(maxsize, bytestoread)
-                    data = usb_read(size)
-                    if len(data) > 0:
-                        wr.write(data)
-                        bytestoread -= len(data)
-                        show_progress(prefix="Read", pos=total - bytestoread, total=total, display=display)
-                self.cdc.xmlread = True
-                wd = self.wait_for_data()
-                info = self.xml.getlog(wd)
-                rsp = self.xml.getresponse(wd)
-                if "value" in rsp:
-                    if rsp["value"] != "ACK":
+        rsp = self.xmlsend(data, self.skipresponse)
+        self.cdc.xmlread = False
+        time.sleep(0.01)
+        if not rsp.resp:
+            if display:
+                self.error(rsp.error)
+            return b""
+        else:
+            bytestoread = self.cfg.SECTOR_SIZE_IN_BYTES * num_partition_sectors
+            total = bytestoread
+            show_progress = progbar.show_progress
+            usb_read = self.cdc.read
+            progbar.show_progress(prefix="Read", pos=0, total=total, display=display)
+            worker = Thread(target=writedata, args=(filename, rq), daemon=True)
+            worker.start()
+            while bytestoread > 0:
+                if self.cdc.is_serial:
+                    maxsize = self.cfg.MaxPayloadSizeFromTargetInBytes
+                else:
+                    maxsize = 5 * 1024 * 1024
+                size = min(maxsize, bytestoread)
+                data = usb_read(size)
+                if len(data) > 0:
+                    rq.put(data)
+                    bytestoread -= len(data)
+                    show_progress(prefix="Read", pos=total - bytestoread, total=total, display=display)
+            rq.put(None)
+            worker.join(60)
+            self.cdc.xmlread = True
+            wd = self.wait_for_data()
+            info = self.xml.getlog(wd)
+            rsp = self.xml.getresponse(wd)
+            if "value" in rsp:
+                if rsp["value"] != "ACK":
+                    if bytestoread!=0:
                         self.error(f"Error:")
                         for line in info:
                             self.error(line)
                             self.lasterror += bytes(line + "\n", "utf-8")
-                        return False
-                else:
-                    if display:
-                        self.error(f"Error:{rsp[2]}")
-                        return False
-            return True
+                    return False
+            else:
+                if display:
+                    self.error(f"Error:{rsp[2]}")
+                    return False
+        return True
 
     def cmd_read_buffer(self, physical_partition_number, start_sector, num_partition_sectors, display=True):
         self.lasterror = b""
@@ -720,13 +754,13 @@ class firehose(metaclass=LogBase):
         resp = rsp["value"] == "ACK"
         return response(resp=resp, data=resData, error=rsp[2])  # Do not remove, needed for oneplus
 
-    def get_gpt(self, lun, gpt_num_part_entries, gpt_part_entry_size, gpt_part_entry_start_lba):
+    def get_gpt(self, lun, gpt_num_part_entries, gpt_part_entry_size, gpt_part_entry_start_lba, start_sector=1):
         try:
-            resp = self.cmd_read_buffer(lun, 0, 2, False)
+            resp = self.cmd_read_buffer(lun, 0, 1, False)
         except Exception as err:
             self.debug(str(err))
             self.skipresponse = True
-            resp = self.cmd_read_buffer(lun, 0, 2, False)
+            resp = self.cmd_read_buffer(lun, 0, 1, False)
 
         if not resp.resp:
             for line in resp.error:
@@ -734,6 +768,7 @@ class firehose(metaclass=LogBase):
             return None, None
         data = resp.data
         magic = unpack("<I", data[0:4])[0]
+        data += self.cmd_read_buffer(lun, start_sector, 1, False).data
         if magic == 0x844bdcd1:
             self.info("Nand storage detected.")
             self.info("Scanning for partition table ...")
@@ -768,22 +803,22 @@ class firehose(metaclass=LogBase):
                 loglevel=self.__logger.level
             )
             try:
-                header = guid_gpt.parseheader(data, self.cfg.SECTOR_SIZE_IN_BYTES)
+                sectorsize = self.cfg.SECTOR_SIZE_IN_BYTES
+                header = guid_gpt.parseheader(data, sectorsize)
                 if header.signature == b"EFI PART":
-                    gptsize = (header.part_entry_start_lba * self.cfg.SECTOR_SIZE_IN_BYTES) + (
-                            header.num_part_entries * header.part_entry_size)
-                    sectors = gptsize // self.cfg.SECTOR_SIZE_IN_BYTES
-                    if gptsize % self.cfg.SECTOR_SIZE_IN_BYTES != 0:
+                    part_table_size = header.num_part_entries * header.part_entry_size
+                    sectors = part_table_size // self.cfg.SECTOR_SIZE_IN_BYTES
+                    if part_table_size % self.cfg.SECTOR_SIZE_IN_BYTES != 0:
                         sectors += 1
                     if sectors == 0:
                         return None, None
                     if sectors > 64:
                         sectors = 64
-                    data = self.cmd_read_buffer(lun, 0, sectors, False)
+                    data += self.cmd_read_buffer(lun, header.part_entry_start_lba, sectors, False).data
                     if data == b"":
                         return None, None
-                    guid_gpt.parse(data.data, self.cfg.SECTOR_SIZE_IN_BYTES)
-                    return data.data, guid_gpt
+                    guid_gpt.parse(data, self.cfg.SECTOR_SIZE_IN_BYTES)
+                    return data, guid_gpt
                 else:
                     return None, None
             except Exception as err:
@@ -848,7 +883,7 @@ class firehose(metaclass=LogBase):
         '''
         "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><data><response value=\"ACK\" MinVersionSupported=\"1\"" \
         "MemoryName=\"eMMC\" MaxPayloadSizeFromTargetInBytes=\"4096\" MaxPayloadSizeToTargetInBytes=\"1048576\" " \
-        "MaxPayloadSizeToTargetInBytesSupported=\"1048576\" MaxXMLSizeInBytes=\"4096\" Version=\"1\" 
+        "MaxPayloadSizeToTargetInBytesSupported=\"1048576\" MaxXMLSizeInBytes=\"4096\" Version=\"1\"
         TargetName=\"8953\" />" \
         "</data>"
         '''
@@ -1008,6 +1043,15 @@ class firehose(metaclass=LogBase):
                         self.cfg.SECTOR_SIZE_IN_BYTES = 4096
                         return self.configure(0)
             self.parse_storage()
+            for function in self.supported_functions:
+                if function == "checkntfeature":
+                    if type(self.devicemodel)==list:
+                        self.devicemodel=self.devicemodel[0]
+                    self.nothing = nothing(fh=self, projid=self.devicemodel, serial=self.serial,
+                                           supported_functions=self.supported_functions,
+                                           loglevel=self.loglevel)
+                    if self.nothing is not None:
+                        self.nothing.ntprojectverify()
             self.luns = self.getluns(self.args)
             return True
 
@@ -1104,6 +1148,8 @@ class firehose(metaclass=LogBase):
                 if "chip serial num" in line.lower():
                     try:
                         serial = line.split("0x")[1][:-1]
+                        if ")" in serial:
+                            serial=serial[:serial.rfind(")")]
                         self.serial = int(serial, 16)
                     except Exception as err:  # pylint: disable=broad-except
                         self.debug(str(err))
@@ -1136,7 +1182,8 @@ class firehose(metaclass=LogBase):
             try:
                 if os.path.exists(self.cfg.programmer):
                     data = open(self.cfg.programmer, "rb").read()
-                    for cmd in [b"demacia", b"setprojmodel", b"setswprojmodel", b"setprocstart", b"SetNetType"]:
+                    for cmd in [b"demacia", b"setprojmodel", b"setswprojmodel", b"setprocstart", b"SetNetType",
+                                b"checkntfeature"]:
                         if cmd in data:
                             self.supported_functions.append(cmd.decode('utf-8'))
                 state = {
@@ -1144,7 +1191,19 @@ class firehose(metaclass=LogBase):
                     "programmer": self.cfg.programmer,
                     "serial": self.serial
                 }
-                open("edl_config.json", "w").write(json.dumps(state))
+                if os.path.exists("edl_config.json"):
+                    data = json.loads(open("edl_config.json","rb").read().decode('utf-8'))
+                    if "serial" in data and data["serial"]!=state["serial"]:
+                        open("edl_config.json", "w").write(json.dumps(state))
+                    else:
+                        self.supported_functions = data["supported_functions"]
+                        self.cfg.programmer = data["programmer"]
+                else:
+                    open("edl_config.json", "w").write(json.dumps(state))
+                if "001920e101cf0000_fa2836525c2aad8a_fhprg.bin" in self.cfg.programmer:
+                    self.devicemodel = '20111'
+                elif "000b80e100020000_467f3020c4cc788d_fhprg.bin" in self.cfg.programmer:
+                    self.devicemodel = '22111'
             except:
                 pass
 
@@ -1262,41 +1321,204 @@ class firehose(metaclass=LogBase):
             return None
 
     def cmd_setactiveslot(self, slot: str):
+        # flags: 0x3a for inactive and 0x6f for active boot partition
+        def set_flags(flags, active, is_boot):
+            new_flags = flags
+            if active:
+                if is_boot:
+                    #new_flags |= (PART_ATT_PRIORITY_VAL | PART_ATT_ACTIVE_VAL | PART_ATT_MAX_RETRY_COUNT_VAL)
+                    #new_flags &= (~PART_ATT_SUCCESSFUL_VAL & ~PART_ATT_UNBOOTABLE_VAL)
+                    new_flags = 0x6f << (AB_FLAG_OFFSET*8)
+                else:
+                    new_flags |= AB_PARTITION_ATTR_SLOT_ACTIVE << (AB_FLAG_OFFSET*8)
+            else:
+                if is_boot:
+                    #new_flags &= (~PART_ATT_PRIORITY_VAL & ~PART_ATT_ACTIVE_VAL)
+                    #new_flags |= ((MAX_PRIORITY-1) << PART_ATT_PRIORITY_BIT)
+                    new_flags = 0x3a << (AB_FLAG_OFFSET*8)
+                else:
+                    new_flags &= ~(AB_PARTITION_ATTR_SLOT_ACTIVE << (AB_FLAG_OFFSET*8))
+            return new_flags
+
+        def patch_helper(gpt_data_a, gpt_data_b, guid_gpt_a, guid_gpt_b, partition_a, partition_b, slot_a_status, slot_b_status, is_boot):
+            part_entry_size = guid_gpt_a.header.part_entry_size
+
+            rf_a = BytesIO(gpt_data_a)
+            rf_b = BytesIO(gpt_data_b)
+
+            entryoffset_a = partition_a.entryoffset - ((guid_gpt_a.header.part_entry_start_lba - 2) * guid_gpt_a.sectorsize)
+            entryoffset_b = partition_b.entryoffset - ((guid_gpt_b.header.part_entry_start_lba - 2) * guid_gpt_b.sectorsize)
+            rf_a.seek(entryoffset_a)
+            rf_b.seek(entryoffset_b)
+
+            sdata_a = rf_a.read(part_entry_size)
+            sdata_b = rf_b.read(part_entry_size)
+
+            partentry_a = gpt.gpt_partition(sdata_a)
+            partentry_b = gpt.gpt_partition(sdata_b)
+
+            partentry_a.flags = set_flags(partentry_a.flags, slot_a_status, is_boot)
+            partentry_b.flags = set_flags(partentry_b.flags, slot_b_status, is_boot)
+            partentry_a.type, partentry_b.type = partentry_b.type, partentry_a.type
+
+            pdata_a, pdata_b = partentry_a.create(), partentry_b.create()
+            return pdata_a, partition_a.entryoffset, pdata_b, partition_b.entryoffset
+
+        def cmd_patch_multiple(lun, start_sector, byte_offset, patch_data):
+            offset = 0
+            size_each_patch = 8 if len(patch_data) % 8 == 0 else 4
+            unpack_fmt = "<I" if size_each_patch == 4 else "<Q"
+            write_size = len(patch_data)
+            for i in range(0, write_size, size_each_patch):
+                pdata_subset = int(unpack(unpack_fmt, patch_data[offset:offset+size_each_patch])[0])
+                self.cmd_patch( lun, start_sector, byte_offset + offset, pdata_subset, size_each_patch, True)
+                offset += size_each_patch
+            return True
+
+        def update_gpt_info(guid_gpt_a, guid_gpt_b, partitionname_a, partitionname_b,
+                            gpt_data_a, gpt_data_b, slot_a_status, slot_b_status, lun_a, lun_b
+                            ):
+            part_a = guid_gpt_a.partentries[partitionname_a]
+            part_b = guid_gpt_b.partentries[partitionname_b]
+
+            is_boot = False
+            if partitionname_a == "boot_a":
+                is_boot = True
+            pdata_a, poffset_a, pdata_b, poffset_b = patch_helper(
+                gpt_data_a, gpt_data_b,
+                guid_gpt_a, guid_gpt_b,
+                part_a, part_b,
+                slot_a_status, slot_b_status,
+                is_boot
+            )
+
+            if gpt_data_a and gpt_data_b:
+                entryoffset_a = poffset_a - ((guid_gpt_a.header.part_entry_start_lba - 2) * guid_gpt_a.sectorsize)
+                gpt_data_a[entryoffset_a : entryoffset_a + len(pdata_a)] = pdata_a
+                new_gpt_data_a = guid_gpt_a.fix_gpt_crc(gpt_data_a)
+
+                entryoffset_b = poffset_b - ((guid_gpt_b.header.part_entry_start_lba - 2) * guid_gpt_b.sectorsize)
+                gpt_data_b[entryoffset_b : entryoffset_b + len(pdata_b)] = pdata_b
+                new_gpt_data_b = guid_gpt_b.fix_gpt_crc(gpt_data_b)
+
+                start_sector_patch_a = poffset_a // self.cfg.SECTOR_SIZE_IN_BYTES
+                byte_offset_patch_a = poffset_a % self.cfg.SECTOR_SIZE_IN_BYTES
+                cmd_patch_multiple(lun_a, start_sector_patch_a, byte_offset_patch_a, pdata_a)
+
+                if lun_a != lun_b:
+                    start_sector_hdr_a = guid_gpt_a.header.current_lba
+                    headeroffset_a = guid_gpt_a.sectorsize # gptData: mbr + gpt header + part array
+                    new_hdr_a = new_gpt_data_a[headeroffset_a : headeroffset_a+guid_gpt_a.header.header_size]
+                    cmd_patch_multiple(lun_a, start_sector_hdr_a, 0, new_hdr_a)
+
+                start_sector_patch_b = poffset_b // self.cfg.SECTOR_SIZE_IN_BYTES
+                byte_offset_patch_b = poffset_b % self.cfg.SECTOR_SIZE_IN_BYTES
+                cmd_patch_multiple(lun_b, start_sector_patch_b, byte_offset_patch_b, pdata_b)
+
+                start_sector_hdr_b = guid_gpt_b.header.current_lba
+                headeroffset_b = guid_gpt_b.sectorsize
+                new_hdr_b = new_gpt_data_b[headeroffset_b : headeroffset_b+guid_gpt_b.header.header_size]
+                cmd_patch_multiple(lun_b, start_sector_hdr_b, 0, new_hdr_b)
+                return True
+            return False
+
+        def ensure_gpt_hdr_consistency(guid_gpt, backup_guid_gpt, gpt_data, backup_gpt_data):
+            headeroffset = guid_gpt.sectorsize
+            prim_corrupted, backup_corrupted = False, False
+
+            prim_hdr = gpt_data[headeroffset : headeroffset + guid_gpt.header.header_size]
+            test_hdr = guid_gpt.fix_gpt_crc(gpt_data)[headeroffset : headeroffset + guid_gpt.header.header_size]
+            prim_hdr_crc, test_hdr_crc = prim_hdr[0x10 : 0x10 + 4], test_hdr[0x10 : 0x10 + 4]
+            prim_part_table_crc, test_part_table_crc = prim_hdr[0x58 : 0x58 + 4], test_hdr[0x58 : 0x58 + 4]
+            prim_corrupted = prim_hdr_crc != test_hdr_crc or prim_part_table_crc != test_part_table_crc
+
+            backup_hdr = backup_gpt_data[headeroffset : headeroffset + backup_guid_gpt.header.header_size]
+            test_hdr = backup_guid_gpt.fix_gpt_crc(backup_gpt_data)[headeroffset : headeroffset + backup_guid_gpt.header.header_size]
+            backup_hdr_crc, test_hdr_crc = backup_hdr[0x10 : 0x10 + 4], test_hdr[0x10 : 0x10 + 4]
+            backup_part_table_crc, test_part_table_crc = backup_hdr[0x58 : 0x58 + 4], test_hdr[0x58 : 0x58 + 4]
+            backup_corrupted = backup_hdr_crc != test_hdr_crc or backup_part_table_crc != test_part_table_crc
+
+            prim_backup_consistent = prim_part_table_crc == backup_part_table_crc
+            if prim_corrupted or not prim_backup_consistent:
+                if backup_corrupted:
+                    self.error("both are gpt headers are corrupted, cannot recover")
+                    return False, None, None
+                gpt_data[2*guid_gpt.sectorsize:] = backup_gpt_data[2*backup_guid_gpt.sectorsize:]
+                gpt_data = guid_gpt.fix_gpt_crc(gpt_data)
+            elif backup_corrupted or not prim_backup_consistent:
+                backup_gpt_data[2*backup_guid_gpt.sectorsize:] = gpt_data[2*guid_gpt.sectorsize:]
+                backup_gpt_data = backup_guid_gpt.fix_gpt_crc(backup_gpt_data)
+            return True, gpt_data, backup_gpt_data
+
         if slot.lower() not in ["a", "b"]:
             self.error("Only slots a or b are accepted. Aborting.")
             return False
-        partslots = {}
+        slot_a_status = None
         if slot == "a":
-            partslots["_a"] = True
-            partslots["_b"] = False
+            slot_a_status = True
         elif slot == "b":
-            partslots["_a"] = True
-            partslots["_b"] = False
+            slot_a_status = False
+        slot_b_status = not slot_a_status
         fpartitions = {}
-        for lun in self.luns:
-            lunname = "Lun" + str(lun)
-            fpartitions[lunname] = []
-            data, guid_gpt = self.get_gpt(lun, int(0), int(0), int(0))
-            if guid_gpt is None:
-                break
-            else:
-                for partitionname in guid_gpt.partentries:
-                    gp = gpt()
-                    slot = partitionname.lower()[-2:]
-                    if "_a" in slot or "_b" in slot:
-                        pdata, poffset = gp.patch(data, partitionname, active=partslots[slot])
-                        data[poffset:poffset + len(pdata)] = pdata
-                        wdata = gp.fix_gpt_crc(data)
-                        if wdata is not None:
-                            start_sector_patch = poffset // self.cfg.SECTOR_SIZE_IN_BYTES
-                            byte_offset_patch = poffset % self.cfg.SECTOR_SIZE_IN_BYTES
-                            headeroffset = gp.header.current_lba * gp.sectorsize
-                            start_sector_hdr = headeroffset // self.cfg.SECTOR_SIZE_IN_BYTES
-                            header = wdata[start_sector_hdr:start_sector_hdr + gp.header.header_size]
-                            self.cmd_patch(lun, start_sector_patch, byte_offset_patch, pdata, len(pdata), True)
-                            self.cmd_patch(lun, headeroffset, 0, header, len(pdata), True)
-                return True
-        return False
+        try:
+            for lun_a in self.luns:
+                lunname = "Lun" + str(lun_a)
+                fpartitions[lunname] = []
+                check_gpt_hdr = False
+                gpt_data_a, guid_gpt_a = self.get_gpt(lun_a, int(0), int(0), int(0))
+                backup_gpt_data_a, backup_guid_gpt_a = self.get_gpt(lun_a, 0, 0 , 0, guid_gpt_a.header.backup_lba)
+                if guid_gpt_a is None:
+                    break
+                else:
+                    for partitionname_a in guid_gpt_a.partentries:
+                        slot = partitionname_a.lower()[-2:]
+                        if slot == "_a":
+                            partitionname_b = partitionname_a[:-1] + "b"
+                            if partitionname_b in guid_gpt_a.partentries:
+                                lun_b = lun_a
+                                gpt_data_b = gpt_data_a
+                                guid_gpt_b = guid_gpt_a
+                                backup_gpt_data_b = backup_gpt_data_a
+                                backup_guid_gpt_b = backup_guid_gpt_a
+                            else:
+                                resp = self.detect_partition(arguments=None,
+                                                             partitionname=partitionname_b,
+                                                             send_full=True)
+                                if not resp[0]:
+                                    self.error(f"Cannot find partition {partitionname_b}")
+                                    return False
+                                _, lun_b, gpt_data_b, guid_gpt_b = resp
+                                backup_gpt_data_b, backup_guid_gpt_b = self.get_gpt(lun_b, 0, 0 , 0, guid_gpt_b.header.backup_lba)
+
+                            if not check_gpt_hdr and partitionname_a[:3] != "xbl": # xbl partition don't need check consistency
+                                sts, gpt_data_a, backup_gpt_data_a = ensure_gpt_hdr_consistency(guid_gpt_a, backup_guid_gpt_a, gpt_data_a, backup_gpt_data_a)
+                                if not sts:
+                                    return False
+                                if lun_a != lun_b:
+                                    sts, gpt_data_b, backup_gpt_data_b = ensure_gpt_hdr_consistency(guid_gpt_b, backup_guid_gpt_b, gpt_data_b, backup_gpt_data_b)
+                                    if not sts:
+                                        return False
+                                check_gpt_hdr = True
+
+                            update_gpt_info(guid_gpt_a, guid_gpt_b,
+                                            partitionname_a, partitionname_b,
+                                            gpt_data_a, gpt_data_b,
+                                            slot_a_status, slot_b_status,
+                                            lun_a, lun_b)
+
+                            # TODO: this updates the backup gpt header, but is it needed, since it is updated when xbl loads
+                            #update_gpt_info(backup_guid_gpt_a, backup_guid_gpt_b,
+                            #                partitionname_a, partitionname_b,
+                            #                backup_gpt_data_a, backup_gpt_data_b,
+                            #                slot_a_status, slot_b_status,
+                            #                lun_a, lun_b)
+
+        except Exception as err:
+            self.error(str(err))
+            return False
+        return True
+
+
 
     def cmd_test(self, cmd):
         token = "1234"
@@ -1415,12 +1637,12 @@ class firehose(metaclass=LogBase):
         data = f"<?xml version=\"1.0\" ?><data><peek address64=\"{address}\" " + \
                f"size_in_bytes=\"{SizeInBytes}\" /></data>\n"
         '''
-            <?xml version="1.0" encoding="UTF-8" ?><data><log value="Using address 00100000" /></data> 
-            <?xml version="1.0" encoding="UTF-8" ?><data><log value="0x22 0x00 0x00 0xEA 0x70 0x00 0x00 0xEA 0x74 0x00 
-            0x00 0xEA 0x78 0x00 0x00 0xEA 0x7C 0x00 0x00 0xEA 0x80 0x00 0x00 0xEA 0x84 0x00 0x00 0xEA 0x88 0x00 0x00 
-            0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 
-            0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 
-            0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 
+            <?xml version="1.0" encoding="UTF-8" ?><data><log value="Using address 00100000" /></data>
+            <?xml version="1.0" encoding="UTF-8" ?><data><log value="0x22 0x00 0x00 0xEA 0x70 0x00 0x00 0xEA 0x74 0x00
+            0x00 0xEA 0x78 0x00 0x00 0xEA 0x7C 0x00 0x00 0xEA 0x80 0x00 0x00 0xEA 0x84 0x00 0x00 0xEA 0x88 0x00 0x00
+            0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA
+            0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE
+            0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF
             0xFF 0xEA 0xFE 0xFF 0xFF 0xEA 0xFE 0xFF " /></data>
             '''
         try:
diff --git a/edlclient/Library/firehose_client.py b/edlclient/Library/firehose_client.py
index 4b24ac5..8214f3d 100644
--- a/edlclient/Library/firehose_client.py
+++ b/edlclient/Library/firehose_client.py
@@ -1,6 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2019
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 
 import os
 import sys
@@ -12,6 +16,7 @@ from edlclient.Library.firehose import firehose
 from edlclient.Library.xmlparser import xmlparser
 from edlclient.Library.utils import do_tcp_server
 from edlclient.Library.utils import LogBase, getint
+from edlclient.Library.gpt import AB_FLAG_OFFSET, AB_PARTITION_ATTR_SLOT_ACTIVE
 from edlclient.Config.qualcomm_config import memory_type
 from edlclient.Config.qualcomm_config import infotbl, msmids, secureboottbl, sochw
 import fnmatch
@@ -132,10 +137,11 @@ class firehose_client(metaclass=LogBase):
                 return True
         return False
 
-    def find_bootable_partition(self, rawprogram):
+    def find_bootable_partition(self, imagedir, rawprogram):
         part = -1
         for xml in rawprogram:
-            with open(xml, "r") as fl:
+            filename = os.path.join(imagedir, xml)
+            with open(filename, "r") as fl:
                 for evt, elem in ET.iterparse(fl, events=["end"]):
                     if elem.tag == "program":
                         label = elem.get("label")
@@ -201,13 +207,15 @@ class firehose_client(metaclass=LogBase):
                                                        int(options["--gpt-part-entry-start-lba"]))
                 if guid_gpt is None:
                     break
-                with open(sfilename, "wb") as write_handle:
-                    write_handle.write(data)
+                #with open(sfilename, "wb") as write_handle:
+                #    #write_handle.write(data)
+                #    pass
 
                 self.printer(f"Dumped GPT from Lun {str(lun)} to {sfilename}")
                 sfilename = os.path.join(directory, f"gpt_backup{str(lun)}.bin")
-                with open(sfilename, "wb") as write_handle:
-                    write_handle.write(data[self.firehose.cfg.SECTOR_SIZE_IN_BYTES * 2:])
+                #with open(sfilename, "wb") as write_handle:
+                #    #write_handle.write(data[self.firehose.cfg.SECTOR_SIZE_IN_BYTES * 2:])
+                #    pass
                 self.printer(f"Dumped Backup GPT from Lun {str(lun)} to {sfilename}")
                 if genxml:
                     guid_gpt.generate_rawprogram(lun, self.firehose.cfg.SECTOR_SIZE_IN_BYTES, directory)
@@ -634,6 +642,29 @@ class firehose_client(metaclass=LogBase):
                 return False
             else:
                 return self.firehose.cmd_setbootablestoragedrive(int(options["<lun>"]))
+        elif cmd == "getactiveslot":
+            res = self.firehose.detect_partition(options, "boot_a", send_full=True)
+            if res[0]:
+                lun = res[1]
+                prim_guid_gpt = res[3]
+                _, backup_guid_gpt = self.firehose.get_gpt(lun, 0, 0, 0, prim_guid_gpt.header.backup_lba)
+                partition = backup_guid_gpt.partentries["boot_a"]
+                active = ((partition.flags >> (AB_FLAG_OFFSET*8))&0xFF) & AB_PARTITION_ATTR_SLOT_ACTIVE == AB_PARTITION_ATTR_SLOT_ACTIVE
+                if active:
+                    self.printer("Current active slot: a")
+                    return True
+            res = self.firehose.detect_partition(options, "boot_b", send_full=True)
+            if res[0]:
+                lun = res[1]
+                prim_guid_gpt = res[3]
+                _, backup_guid_gpt = self.firehose.get_gpt(lun, 0, 0, 0, prim_guid_gpt.header.backup_lba)
+                partition = backup_guid_gpt.partentries["boot_b"]
+                active = ((partition.flags >> (AB_FLAG_OFFSET*8))&0xFF) & AB_PARTITION_ATTR_SLOT_ACTIVE == AB_PARTITION_ATTR_SLOT_ACTIVE
+                if active:
+                    self.printer("Current active slot: b")
+                    return True
+            self.error("Can't detect active slot. Please make sure your device has slot A/B")
+            return False
         elif cmd == "setactiveslot":
             if not self.check_param(["<slot>"]):
                 return False
@@ -724,17 +755,17 @@ class firehose_client(metaclass=LogBase):
             filenames = []
             if self.firehose.modules is not None:
                 self.firehose.modules.writeprepare()
-            for dirName, subdirList, fileList in os.walk(directory):
-                for fname in fileList:
-                    filenames.append(os.path.join(dirName, fname))
+            for fname in filter(os.path.isfile, [ os.path.join(directory, i) for i in os.listdir(directory) ]):
+                filenames.append(fname)
             for lun in luns:
                 data, guid_gpt = self.firehose.get_gpt(lun, int(options["--gpt-num-part-entries"]),
                                                        int(options["--gpt-part-entry-size"]),
                                                        int(options["--gpt-part-entry-start-lba"]))
                 if guid_gpt is None:
+                    self.error("Error: Can not fetch GPT table from device, you may need to use `edl w gpt` to write a partition table first.`")
                     break
                 for filename in filenames:
-                    partname = filename[filename.rfind("/") + 1:]
+                    partname = os.path.basename(filename)
                     if ".bin" in partname[-4:] or ".img" in partname[-4:] or ".mbn" in partname[-4:]:
                         partname = partname[:-4]
                     if partname in skip:
@@ -953,7 +984,8 @@ class firehose_client(metaclass=LogBase):
                 else:
                     self.warning(f"File : {filename} not found.")
             self.info("[qfil] patching ok")
-            bootable = self.find_bootable_partition(rawprogram)
+
+            bootable = self.find_bootable_partition(imagedir, rawprogram)
             if bootable != -1:
                 if self.firehose.cmd_setbootablestoragedrive(bootable):
                     self.info("[qfil] partition({partition}) is now bootable\n".format(partition=bootable))
diff --git a/edlclient/Library/gpt.py b/edlclient/Library/gpt.py
index 563a55b..a61a37e 100755
--- a/edlclient/Library/gpt.py
+++ b/edlclient/Library/gpt.py
@@ -1,6 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2021 MIT License
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 import os
 import sys
 import argparse
@@ -190,6 +194,20 @@ AB_SLOT_ACTIVE = 1
 AB_SLOT_INACTIVE = 0
 
 
+PART_ATT_PRIORITY_BIT = 48
+PART_ATT_ACTIVE_BIT = 50
+PART_ATT_MAX_RETRY_CNT_BIT = 51
+MAX_PRIORITY = 3
+PART_ATT_SUCCESS_BIT = 54
+PART_ATT_UNBOOTABLE_BIT = 55
+
+PART_ATT_PRIORITY_VAL = 0x3 << PART_ATT_PRIORITY_BIT
+PART_ATT_ACTIVE_VAL = 0x1 << PART_ATT_ACTIVE_BIT
+PART_ATT_MAX_RETRY_COUNT_VAL = 0x7 << PART_ATT_MAX_RETRY_CNT_BIT
+PART_ATT_SUCCESSFUL_VAL  = 0x1 << PART_ATT_SUCCESS_BIT
+PART_ATT_UNBOOTABLE_VAL = 0x1 << PART_ATT_UNBOOTABLE_BIT
+
+
 class gpt(metaclass=LogBase):
     class gpt_header:
         def __init__(self, data):
@@ -220,7 +238,7 @@ class gpt(metaclass=LogBase):
             self.name = sh.string(72)
 
         def create(self):
-            val = pack("16s16sQQQ72s", self.type, self.unique, self.first_lba, self.last_lba, self.flags, self.name)
+            val = pack("<16s16sQQQ72s", self.type, self.unique, self.first_lba, self.last_lba, self.flags, self.name)
             return val
 
     class efi_type(Enum):
@@ -343,7 +361,7 @@ class gpt(metaclass=LogBase):
         if self.part_entry_start_lba != 0:
             start = self.part_entry_start_lba
         else:
-            start = self.header.part_entry_start_lba * sectorsize
+            start = 2 * sectorsize # mbr + header + part_table
 
         entrysize = self.header.part_entry_size
         self.partentries = {}
@@ -376,7 +394,7 @@ class gpt(metaclass=LogBase):
             pa.sector = partentry.first_lba
             pa.sectors = partentry.last_lba - partentry.first_lba + 1
             pa.flags = partentry.flags
-            pa.entryoffset = start + (idx * entrysize)
+            pa.entryoffset = (self.header.part_entry_start_lba * sectorsize) + (idx * entrysize)
             type = int(unpack("<I", partentry.type[0:0x4])[0])
             try:
                 pa.type = self.efi_type(type).name
@@ -386,7 +404,7 @@ class gpt(metaclass=LogBase):
             if pa.type == "EFI_UNUSED":
                 continue
             self.partentries[pa.name]=pa
-        self.totalsectors = self.header.last_usable_lba + 34
+        self.totalsectors = self.header.first_usable_lba + self.header.last_usable_lba
         return True
 
     def print(self):
@@ -478,37 +496,11 @@ class gpt(metaclass=LogBase):
         res = self.print_gptfile(os.path.join("TestFiles", "gpt_sm8180x.bin"))
         assert res, "GPT Partition wasn't decoded properly"
 
-    def patch(self, data:bytes, partitionname="boot", active: bool = True):
-        try:
-            rf = BytesIO(data)
-            for sectorsize in [512, 4096]:
-                result = self.parse(data, sectorsize)
-                if result:
-                    for rname in self.partentries:
-                        if partitionname.lower() == rname.lower():
-                            partition = self.partentries[rname]
-                            rf.seek(partition.entryoffset)
-                            sdata = rf.read(self.header.part_entry_size)
-                            partentry = self.gpt_partition(sdata)
-                            flags = partentry.flags
-                            if active:
-                                flags |= AB_PARTITION_ATTR_SLOT_ACTIVE << (AB_FLAG_OFFSET*8)
-                            else:
-                                flags |= AB_PARTITION_ATTR_UNBOOTABLE << (AB_FLAG_OFFSET*8)
-                            partentry.flags = flags
-                            pdata = partentry.create()
-                            return pdata, partition.entryoffset
-                    break
-            return None, None
-        except Exception as e:
-            self.error(str(e))
-        return None, None
-
     def fix_gpt_crc(self, data):
         partentry_size = self.header.num_part_entries * self.header.part_entry_size
-        partentry_offset = self.header.part_entry_start_lba * self.sectorsize
+        partentry_offset = 2 * self.sectorsize
         partdata = data[partentry_offset:partentry_offset + partentry_size]
-        headeroffset = self.header.current_lba * self.sectorsize
+        headeroffset = self.sectorsize
         headerdata = bytearray(data[headeroffset:headeroffset + self.header.header_size])
         headerdata[0x58:0x58 + 4] = pack("<I", crc32(partdata))
         headerdata[0x10:0x10 + 4] = pack("<I", 0)
diff --git a/edlclient/Library/hdlc.py b/edlclient/Library/hdlc.py
index e48ac1d..b242717 100755
--- a/edlclient/Library/hdlc.py
+++ b/edlclient/Library/hdlc.py
@@ -1,6 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2019
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 
 import logging
 from binascii import hexlify
diff --git a/edlclient/Library/loader_db.py b/edlclient/Library/loader_db.py
index 5443bd6..a1f1430 100644
--- a/edlclient/Library/loader_db.py
+++ b/edlclient/Library/loader_db.py
@@ -1,6 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2022
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 import binascii
 import time
 import os
@@ -11,8 +15,12 @@ from struct import unpack, pack
 current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
 parent_dir = os.path.dirname(current_dir)
 sys.path.insert(0, parent_dir)
-from edlclient.Library.utils import read_object, print_progress, rmrf, LogBase
-from edlclient.Config.qualcomm_config import sochw, msmids, root_cert_hash
+try:
+    from edlclient.Library.utils import read_object, print_progress, rmrf, LogBase
+    from edlclient.Config.qualcomm_config import sochw, msmids, root_cert_hash
+except:
+    from Library.utils import read_object, print_progress, rmrf, LogBase
+    from Config.qualcomm_config import sochw, msmids, root_cert_hash
 
 class loader_utils(metaclass=LogBase):
     def __init__(self, loglevel=logging.INFO):
@@ -43,7 +51,15 @@ class loader_utils(metaclass=LogBase):
                 try:
                     hwid = filename.split("_")[0].lower()
                     msmid = hwid[:8]
+                    try:
+                        int(msmid,16)
+                    except:
+                        continue
                     devid = hwid[8:]
+                    if devid == '':
+                        continue
+                    if len(filename.split("_"))<2:
+                        continue
                     pkhash = filename.split("_")[1].lower()
                     for msmid in self.convertmsmid(msmid):
                         mhwid = msmid + devid
@@ -53,7 +69,7 @@ class loader_utils(metaclass=LogBase):
                         if pkhash not in self.loaderdb[mhwid]:
                             self.loaderdb[mhwid][pkhash] = fn
                 except Exception as e:  # pylint: disable=broad-except
-                    self.debug(str(e))
+                    self.debug(f"Filename:{filename} => {str(e)}")
                     continue
         return self.loaderdb
 
diff --git a/edlclient/Library/memparse.py b/edlclient/Library/memparse.py
index 0dd4ec5..493102c 100755
--- a/edlclient/Library/memparse.py
+++ b/edlclient/Library/memparse.py
@@ -1,6 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2021
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 
 import os
 import pt64
diff --git a/edlclient/Library/nand_config.py b/edlclient/Library/nand_config.py
index 2eb45dd..533d57c 100644
--- a/edlclient/Library/nand_config.py
+++ b/edlclient/Library/nand_config.py
@@ -1,6 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2019
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 import ctypes
 from enum import Enum
 from edlclient.Config.qualcomm_config import secgen, secureboottbl
diff --git a/edlclient/Library/sahara.py b/edlclient/Library/sahara.py
index a8c39c7..3a2871c 100755
--- a/edlclient/Library/sahara.py
+++ b/edlclient/Library/sahara.py
@@ -1,6 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2021
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 import binascii
 import time
 import os
@@ -94,11 +98,11 @@ class sahara(metaclass=LogBase):
             self.error(str(e))
             return {}
 
-    def cmd_hello(self, mode, version_min=1, max_cmd_len=0):  # CMD 0x1, RSP 0x2
+    def cmd_hello(self, mode, version_min=1, max_cmd_len=0, version=2):  # CMD 0x1, RSP 0x2
         cmd = cmd_t.SAHARA_HELLO_RSP
         length = 0x30
-        version = SAHARA_VERSION
-        responsedata = pack("<IIIIIIIIIIII", cmd, length, version, version_min, max_cmd_len, mode, 0, 0, 0, 0, 0, 0)
+        #version = SAHARA_VERSION
+        responsedata = pack("<IIIIIIIIIIII", cmd, length, version, version_min, max_cmd_len, mode, 1, 2, 3, 4, 5, 6)
         try:
             self.cdc.write(responsedata)
             return True
@@ -151,8 +155,8 @@ class sahara(metaclass=LogBase):
             self.error(str(e))
         return {"mode": "error"}
 
-    def enter_command_mode(self):
-        if not self.cmd_hello(sahara_mode_t.SAHARA_MODE_COMMAND):
+    def enter_command_mode(self, version=2):
+        if not self.cmd_hello(sahara_mode_t.SAHARA_MODE_COMMAND, version=version):
             return False
         res = self.get_rsp()
         if "cmd" in res:
@@ -175,11 +179,9 @@ class sahara(metaclass=LogBase):
 
     def cmdexec_get_msm_hwid(self):
         res = self.cmd_exec(exec_cmd_t.SAHARA_EXEC_CMD_MSM_HW_ID_READ)
-        try:
+        if res is not None:
             return int.from_bytes(res[:8],'little')
-        except Exception as e:  # pylint: disable=broad-except
-            self.debug(str(e))
-            return None
+        return None
 
     def cmdexec_get_pkhash(self):
         try:
@@ -208,106 +210,111 @@ class sahara(metaclass=LogBase):
         res = self.cmd_exec(exec_cmd_t.SAHARA_EXEC_CMD_READ_DEBUG_DATA)
         return res
 
-    def cmd_info(self):
-        if self.enter_command_mode():
+    def cmd_info(self, version):
+        if self.enter_command_mode(version=version):
             self.serial = self.cmdexec_get_serial_num()
             self.serials = "{:08x}".format(self.serial)
-            # if self.version>=2.4:
-            #    self.sblversion = "{:08x}".format(self.cmdexec_get_sbl_version())
-            if self.programmer == "":
+            if version < 3:
                 self.hwid = self.cmdexec_get_msm_hwid()
                 self.pkhash = self.cmdexec_get_pkhash()
-
-            if self.hwid is not None:
-                self.hwidstr = "{:016x}".format(self.hwid)
-                self.msm_id = int(self.hwidstr[2:8], 16)
-                self.oem_id = int(self.hwidstr[-8:-4], 16)
-                self.model_id = int(self.hwidstr[-4:], 16)
-                self.oem_str = "{:04x}".format(self.oem_id)
-                self.model_id = "{:04x}".format(self.model_id)
-                self.msm_str = "{:08x}".format(self.msm_id)
-                if self.msm_id in msmids:
-                    cpustr = f"CPU detected:      \"{msmids[self.msm_id]}\"\n"
-                else:
-                    cpustr = "Unknown CPU, please send log as issue to https://github.com/bkerler/edl\n"
-                """
-                if self.version >= 2.4:
-                    self.info(f"\n------------------------\n" +
-                                f"HWID:              0x{self.hwidstr} (MSM_ID:0x{self.msm_str}," +
-                                f"OEM_ID:0x{self.oem_str}," +
-                                f"MODEL_ID:0x{self.model_id})\n" +
-                                f"PK_HASH:           0x{self.pkhash}\n" +
-                                f"Serial:            0x{self.serials}\n" +
-                                f"SBL Version:       0x{self.sblversion}\n")
-                else:
-                """
-                self.info(f"\n------------------------\n" +
-                          f"HWID:              0x{self.hwidstr} (MSM_ID:0x{self.msm_str}," +
-                          f"OEM_ID:0x{self.oem_str}," +
-                          f"MODEL_ID:0x{self.model_id})\n" +
-                          cpustr +
-                          f"PK_HASH:           0x{self.pkhash}\n" +
-                          f"Serial:            0x{self.serials}\n")
-            if self.programmer == "":
-                if self.hwidstr in self.loaderdb:
-                    mt = self.loaderdb[self.hwidstr]
-                    unfused = False
-                    for rootcert in root_cert_hash:
-                        if self.pkhash[0:16] in root_cert_hash[rootcert]:
-                            unfused = True
-                            break
-                    if unfused:
-                        self.info("Possibly unfused device detected, so any loader should be fine...")
-                        if self.pkhash[0:16] in mt:
-                            self.programmer = mt[self.pkhash[0:16]]
-                            self.info(f"Trying loader: {self.programmer}")
+                # if self.version>=2.4:
+                #    self.sblversion = "{:08x}".format(self.cmdexec_get_sbl_version())
+                if self.hwid is not None:
+                    self.hwidstr = "{:016x}".format(self.hwid)
+                    self.msm_id = int(self.hwidstr[2:8], 16)
+                    self.oem_id = int(self.hwidstr[-8:-4], 16)
+                    self.model_id = int(self.hwidstr[-4:], 16)
+                    self.oem_str = "{:04x}".format(self.oem_id)
+                    self.model_id = "{:04x}".format(self.model_id)
+                    self.msm_str = "{:08x}".format(self.msm_id)
+                    if self.msm_id in msmids:
+                        cpustr = f"CPU detected:      \"{msmids[self.msm_id]}\"\n"
+                    else:
+                        cpustr = "Unknown CPU, please send log as issue to https://github.com/bkerler/edl\n"
+                    """
+                    if self.version >= 2.4:
+                        self.info(f"\nVersion {hex(version)}\n------------------------\n" +
+                                    f"HWID:              0x{self.hwidstr} (MSM_ID:0x{self.msm_str}," +
+                                    f"OEM_ID:0x{self.oem_str}," +
+                                    f"MODEL_ID:0x{self.model_id})\n" +
+                                    f"PK_HASH:           0x{self.pkhash}\n" +
+                                    f"Serial:            0x{self.serials}\n" +
+                                    f"SBL Version:       0x{self.sblversion}\n")
+                    else:
+                    """
+                    self.info(f"\nVersion {hex(version)}\n------------------------\n" +
+                              f"HWID:              0x{self.hwidstr} (MSM_ID:0x{self.msm_str}," +
+                              f"OEM_ID:0x{self.oem_str}," +
+                              f"MODEL_ID:0x{self.model_id})\n" +
+                              cpustr +
+                              f"PK_HASH:           0x{self.pkhash}\n" +
+                              f"Serial:            0x{self.serials}\n")
+                if self.programmer == "":
+                    if self.hwidstr in self.loaderdb:
+                        mt = self.loaderdb[self.hwidstr]
+                        unfused = False
+                        for rootcert in root_cert_hash:
+                            if self.pkhash[0:16] in root_cert_hash[rootcert]:
+                                unfused = True
+                                break
+                        if unfused:
+                            self.info("Possibly unfused device detected, so any loader should be fine...")
+                            if self.pkhash[0:16] in mt:
+                                self.programmer = mt[self.pkhash[0:16]]
+                                self.info(f"Trying loader: {self.programmer}")
+                            else:
+                                for loader in mt:
+                                    self.programmer = mt[loader]
+                                    self.info(f"Possible loader available: {self.programmer}")
+                                for loader in mt:
+                                    self.programmer = mt[loader]
+                                    self.info(f"Trying loader: {self.programmer}")
+                                    break
+                        elif self.pkhash[0:16] in mt:
+                            self.programmer = self.loaderdb[self.hwidstr][self.pkhash[0:16]]
+                            self.info(f"Detected loader: {self.programmer}")
                         else:
-                            for loader in mt:
-                                self.programmer = mt[loader]
-                                self.info(f"Possible loader available: {self.programmer}")
-                            for loader in mt:
-                                self.programmer = mt[loader]
+                            for loader in self.loaderdb[self.hwidstr]:
+                                self.programmer = self.loaderdb[self.hwidstr][loader]
                                 self.info(f"Trying loader: {self.programmer}")
                                 break
-                    elif self.pkhash[0:16] in mt:
-                        self.programmer = self.loaderdb[self.hwidstr][self.pkhash[0:16]]
-                        self.info(f"Detected loader: {self.programmer}")
-                    else:
-                        for loader in self.loaderdb[self.hwidstr]:
-                            self.programmer = self.loaderdb[self.hwidstr][loader]
-                            self.info(f"Trying loader: {self.programmer}")
-                            break
-                        # print("Couldn't find a loader for given hwid and pkhash :(")
-                        # exit(0)
-                elif self.hwidstr is not None and self.pkhash is not None:
-                    msmid = self.hwidstr[:8]
-                    found = False
-                    for hwidstr in self.loaderdb:
-                        if msmid == hwidstr[:8]:
-                            if self.pkhash[0:16] in self.loaderdb[hwidstr]:
-                                self.programmer = self.loaderdb[hwidstr][self.pkhash[0:16]]
-                                self.info(f"Found loader: {self.programmer}")
-                                self.cmd_modeswitch(sahara_mode_t.SAHARA_MODE_COMMAND)
-                                return True
+                            # print("Couldn't find a loader for given hwid and pkhash :(")
+                            # exit(0)
+                    elif self.hwidstr is not None and self.pkhash is not None:
+                        msmid = self.hwidstr[:8]
+                        found = False
+                        for hwidstr in self.loaderdb:
+                            if msmid == hwidstr[:8]:
+                                if self.pkhash[0:16] in self.loaderdb[hwidstr]:
+                                    self.programmer = self.loaderdb[hwidstr][self.pkhash[0:16]]
+                                    self.info(f"Found loader: {self.programmer}")
+                                    self.cmd_modeswitch(sahara_mode_t.SAHARA_MODE_COMMAND)
+                                    return True
+                            else:
+                                if self.pkhash[0:16] in self.loaderdb[hwidstr]:
+                                    self.programmer = self.loaderdb[hwidstr][self.pkhash[0:16]]
+                                    self.info(f"Found possible loader: {self.programmer}")
+                                    found = True
+                        if found:
+                            self.cmd_modeswitch(sahara_mode_t.SAHARA_MODE_COMMAND)
+                            return True
                         else:
-                            if self.pkhash[0:16] in self.loaderdb[hwidstr]:
-                                self.programmer = self.loaderdb[hwidstr][self.pkhash[0:16]]
-                                self.info(f"Found possible loader: {self.programmer}")
-                                found = True
-                    if found:
-                        self.cmd_modeswitch(sahara_mode_t.SAHARA_MODE_COMMAND)
-                        return True
+                            self.error(
+                                f"Couldn't find a loader for given hwid and pkhash ({self.hwidstr}_{self.pkhash[0:16]}" +
+                                "_[FHPRG/ENPRG].bin) :(")
+                        return False
                     else:
-                        self.error(
-                            f"Couldn't find a loader for given hwid and pkhash ({self.hwidstr}_{self.pkhash[0:16]}" +
-                            "_[FHPRG/ENPRG].bin) :(")
+                        self.error(f"Couldn't find a suitable loader :(")
+                        return False
+            else:
+                self.info(f"\nVersion {hex(version)}\n------------------------\n" +
+                          f"Serial:            0x{self.serials}\n")
+                if self.programmer=="":
+                    self.error("No autodetection of loader possible with sahara version 3 and above :( Aborting.")
                     return False
-                else:
-                    self.error(f"Couldn't find a suitable loader :(")
-                    return False
-
             self.cmd_modeswitch(sahara_mode_t.SAHARA_MODE_COMMAND)
             return True
+
         return False
 
     def streaminginfo(self):
@@ -430,8 +437,8 @@ class sahara(metaclass=LogBase):
         self.cmd_reset()
         return True
 
-    def debug_mode(self, dump_partitions=None):
-        if not self.cmd_hello(sahara_mode_t.SAHARA_MODE_MEMORY_DEBUG):
+    def debug_mode(self, dump_partitions=None, version=2):
+        if not self.cmd_hello(sahara_mode_t.SAHARA_MODE_MEMORY_DEBUG, version=version):
             return False
         if os.path.exists("memory"):
             rmrf("memory")
@@ -479,7 +486,7 @@ class sahara(metaclass=LogBase):
                         num_entries = len(ptbldata) // pktsize
                         partitions = []
                         for id_entry in range(0, num_entries):
-                            pd = self.parttbl(ptbldata[id_entry * pktsize:(id_entry * pktsize) + pktsize])
+                            pd = self.ch.parttbl(ptbldata[id_entry * pktsize:(id_entry * pktsize) + pktsize])
                             desc = pd.desc.replace(b"\x00", b"").decode('utf-8')
                             filename = pd.filename.replace(b"\x00", b"").decode('utf-8')
                             if dump_partitions and filename not in dump_partitions:
@@ -499,7 +506,7 @@ class sahara(metaclass=LogBase):
             return False
         return False
 
-    def upload_loader(self):
+    def upload_loader(self, version):
         if self.programmer == "":
             return ""
         try:
@@ -510,14 +517,14 @@ class sahara(metaclass=LogBase):
             self.error(str(e))
             sys.exit()
 
-        if not self.cmd_hello(sahara_mode_t.SAHARA_MODE_IMAGE_TX_PENDING):
+        if not self.cmd_hello(sahara_mode_t.SAHARA_MODE_IMAGE_TX_PENDING, version=version):
             return ""
 
         try:
             datalen = len(programmer)
             done = False
             loop = 0
-            while datalen > 0 or done:
+            while datalen >= 0 or done:
                 resp = self.get_rsp()
                 if "cmd" in resp:
                     cmd = resp["cmd"]
@@ -582,6 +589,7 @@ class sahara(metaclass=LogBase):
         except Exception as e:  # pylint: disable=broad-except
             self.error("Unexpected error on uploading, maybe signature of loader wasn't accepted ?\n" + str(e))
             return ""
+        return self.mode
 
     def cmd_modeswitch(self, mode):
         data = pack("<III", cmd_t.SAHARA_SWITCH_MODE, 0xC, mode)
diff --git a/edlclient/Library/sahara_defs.py b/edlclient/Library/sahara_defs.py
index 727e337..a5310f4 100644
--- a/edlclient/Library/sahara_defs.py
+++ b/edlclient/Library/sahara_defs.py
@@ -1,3 +1,11 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
+
 from edlclient.Library.utils import structhelper_io
 from io import BytesIO
 
diff --git a/edlclient/Library/sparse.py b/edlclient/Library/sparse.py
index b0ba3d1..310d7ea 100755
--- a/edlclient/Library/sparse.py
+++ b/edlclient/Library/sparse.py
@@ -1,6 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2021
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 import logging
 import sys
 import os
@@ -14,6 +18,9 @@ sys.path.insert(0, parent_dir)
 from edlclient.Library.utils import LogBase, print_progress
 
 
+MAX_STORE_SIZE = 1024 * 1024 * 1024 * 2 # 2 GBs
+
+
 class QCSparse(metaclass=LogBase):
     def __init__(self, filename, loglevel):
         self.rf = open(filename, 'rb')
@@ -32,6 +39,8 @@ class QCSparse(metaclass=LogBase):
         self.total_chunks = None
         self.image_checksum = None
 
+        self.tmp_offset = 0
+
         self.info = self.__logger.info
         self.debug = self.__logger.debug
         self.error = self.__logger.error
@@ -77,20 +86,22 @@ class QCSparse(metaclass=LogBase):
         chunk_sz = header[2]
         total_sz = header[3]
         data_sz = total_sz - 12
+
         if chunk_type == 0xCAC1:
             if data_sz != (chunk_sz * self.blk_sz):
                 self.error(
                     "Raw chunk input size (%u) does not match output size (%u)" % (data_sz, chunk_sz * self.blk_sz))
                 return -1
             else:
-                self.rf.seek(self.rf.tell() + chunk_sz * self.blk_sz)
+                self.rf.seek(self.rf.tell() + data_sz)
                 return chunk_sz * self.blk_sz
         elif chunk_type == 0xCAC2:
             if data_sz != 4:
                 self.error("Fill chunk should have 4 bytes of fill, but this has %u" % data_sz)
                 return -1
             else:
-                return chunk_sz * self.blk_sz // 4
+                self.rf.seek(self.rf.tell() + data_sz)
+                return chunk_sz * self.blk_sz
         elif chunk_type == 0xCAC3:
             return chunk_sz * self.blk_sz
         elif chunk_type == 0xCAC4:
@@ -98,7 +109,7 @@ class QCSparse(metaclass=LogBase):
                 self.error("CRC32 chunk should have 4 bytes of CRC, but this has %u" % data_sz)
                 return -1
             else:
-                self.rf.seek(self.rf.tell() + 4)
+                self.rf.seek(self.rf.tell() + data_sz)
                 return 0
         else:
             self.debug("Unknown chunk type 0x%04X" % chunk_type)
@@ -166,17 +177,20 @@ class QCSparse(metaclass=LogBase):
         return length
 
     def read(self, length=None):
+        if self.tmp_offset >= MAX_STORE_SIZE:
+            self.tmpdata = self.tmpdata[self.tmp_offset:]
+            self.tmp_offset = 0
         if length is None:
             return self.unsparse()
-        if length <= len(self.tmpdata):
-            tdata = self.tmpdata[:length]
-            self.tmpdata = self.tmpdata[length:]
+        if (self.tmp_offset + length) <= len(self.tmpdata):
+            tdata = self.tmpdata[self.tmp_offset : self.tmp_offset + length]
+            self.tmp_offset += length
             return tdata
-        while len(self.tmpdata) < length:
+        while (self.tmp_offset + length) > len(self.tmpdata):
             self.tmpdata.extend(self.unsparse())
-            if length <= len(self.tmpdata):
-                tdata = self.tmpdata[:length]
-                self.tmpdata = self.tmpdata[length:]
+            if (self.tmp_offset + length) <= len(self.tmpdata):
+                tdata = self.tmpdata[self.tmp_offset : self.tmp_offset + length]
+                self.tmp_offset += length
                 return tdata
 
 
diff --git a/edlclient/Library/streaming.py b/edlclient/Library/streaming.py
index c9c085d..b745f66 100755
--- a/edlclient/Library/streaming.py
+++ b/edlclient/Library/streaming.py
@@ -1,6 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2021
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 from struct import pack
 from binascii import unhexlify
 from edlclient.Library.utils import *
diff --git a/edlclient/Library/streaming_client.py b/edlclient/Library/streaming_client.py
index 07e99d4..ccd77ff 100644
--- a/edlclient/Library/streaming_client.py
+++ b/edlclient/Library/streaming_client.py
@@ -1,6 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2021
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 import sys
 import os
 import logging
diff --git a/edlclient/Library/streaming_defs.py b/edlclient/Library/streaming_defs.py
index fdafca5..420a643 100644
--- a/edlclient/Library/streaming_defs.py
+++ b/edlclient/Library/streaming_defs.py
@@ -1,3 +1,10 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 class open_mode_type:
   OPEN_MODE_NONE = 0x00     # Not opened yet
   OPEN_BOOTLOADER = 0x01    # Bootloader Image
diff --git a/edlclient/Library/utils.py b/edlclient/Library/utils.py
index aa0f75d..05bd7fd 100755
--- a/edlclient/Library/utils.py
+++ b/edlclient/Library/utils.py
@@ -1,6 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2021
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 import sys
 import logging
 import logging.config
@@ -145,7 +149,7 @@ class progress:
                                suffix=prefix + ' (Sector 0x%X of 0x%X) %0.2f MB/s' %
                                       (pos // self.pagesize,
                                        total // self.pagesize,
-                                       0), bar_length=50)
+                                       0), bar_length=10)
 
         if prog > self.prog or prog==100.0:
             if display:
@@ -179,7 +183,7 @@ class progress:
                                    suffix=prefix + f' (Sector 0x%X of 0x%X, {hinfo}) %0.2f MB/s' %
                                           (pos // self.pagesize,
                                            total // self.pagesize,
-                                           throughput), bar_length=50)
+                                           throughput), bar_length=10)
                 self.prog = prog
                 self.progpos = pos
                 self.progtime = t0
diff --git a/edlclient/Library/xmlparser.py b/edlclient/Library/xmlparser.py
index 5cc65d0..3c54f80 100755
--- a/edlclient/Library/xmlparser.py
+++ b/edlclient/Library/xmlparser.py
@@ -1,6 +1,10 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2018-2021
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 import xml.etree.ElementTree as ET
 
 
diff --git a/edlclient/Tools/Config b/edlclient/Tools/Config
new file mode 120000
index 0000000..d0fb958
--- /dev/null
+++ b/edlclient/Tools/Config
@@ -0,0 +1 @@
+../Config
\ No newline at end of file
diff --git a/edlclient/Tools/Library b/edlclient/Tools/Library
new file mode 120000
index 0000000..a1d2d99
--- /dev/null
+++ b/edlclient/Tools/Library
@@ -0,0 +1 @@
+../Library
\ No newline at end of file
diff --git a/edlclient/Tools/beagle_to_loader b/edlclient/Tools/beagle_to_loader
index 8e48045..5504a1e 100755
--- a/edlclient/Tools/beagle_to_loader
+++ b/edlclient/Tools/beagle_to_loader
@@ -1,5 +1,11 @@
 #!/usr/bin/env python3
-# Beagle to EDL Loader (c) B.Kerler 2021
+# -*- coding: utf-8 -*-
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
+# Beagle to EDL Loader
 
 import os,sys
 from struct import unpack
diff --git a/edlclient/Tools/boottodwnload b/edlclient/Tools/boottodwnload
index 884bbeb..6b80737 100755
--- a/edlclient/Tools/boottodwnload
+++ b/edlclient/Tools/boottodwnload
@@ -1,14 +1,16 @@
 #!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2020 under MIT license
+# (c) B.Kerler 2018-2023 under GPLv3 license
 # If you use my code, make sure you refer to my name
-# If you want to use in a commercial product, ask me before integrating it
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 import time
 import serial
 import serial.tools.list_ports
 import argparse
 import requests
-from telnetlib import Telnet
+from Exscript.protocols.telnetlib import Telnet
 import usb.core
 from enum import Enum
 
@@ -53,7 +55,7 @@ class connection(metaclass=LogBase):
             port = self.detect(port)
             if port == "":
                 try:
-                    self.tn = Telnet("192.168.1.1", 5510, 5)
+                    self.tn = Telnet("192.168.1.1", 5510)
                     self.connected = True
                 except:
                     self.connected = False
diff --git a/edlclient/Tools/enableadb b/edlclient/Tools/enableadb
index 9368a86..5544a75 100755
--- a/edlclient/Tools/enableadb
+++ b/edlclient/Tools/enableadb
@@ -1,10 +1,12 @@
 #!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2020-2021 under MIT license
+# (c) B.Kerler 2018-2023 under GPLv3 license
 # If you use my code, make sure you refer to my name
-# If you want to use in a commercial product, ask me before integrating it
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 import time
-from telnetlib import Telnet
+from Exscript.protocols.telnetlib import Telnet
 import serial
 import serial.tools.list_ports
 import argparse
@@ -18,7 +20,8 @@ except ImportError as e:
 
 import usb.core
 from enum import Enum
-import crypt
+
+from passlib.hash import md5_crypt
 
 try:
     from edlclient.Tools.sierrakeygen import SierraKeygen
@@ -30,15 +33,85 @@ except ImportError:
     sys.path.insert(0, parent_dir)
     from sierrakeygen import SierraKeygen
 
-try:
-    from edlclient.Library.utils import LogBase
-except Exception as e:
-    import os, sys, inspect
+import logging
+import logging.config
+import logging.handlers
+import colorama
 
-    current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
-    parent_dir = os.path.dirname(current_dir)
-    sys.path.insert(0, parent_dir)
-    from edlclient.Library.utils import LogBase
+
+itoa64 = bytearray(b"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
+def _crypt_to64(s, v, n):
+    out=bytearray()
+    while --n >= 0:
+        out.append(itoa64[v&0x3f])
+        v >>= 6
+
+
+class ColorFormatter(logging.Formatter):
+    LOG_COLORS = {
+        logging.ERROR: colorama.Fore.RED,
+        logging.DEBUG: colorama.Fore.LIGHTMAGENTA_EX,
+        logging.WARNING: colorama.Fore.YELLOW,
+    }
+
+    def format(self, record, *args, **kwargs):
+        # if the corresponding logger has children, they may receive modified
+        # record, so we want to keep it intact
+        new_record = copy.copy(record)
+        if new_record.levelno in self.LOG_COLORS:
+            pad = ""
+            if new_record.name != "root":
+                print(new_record.name)
+                pad = "[LIB]: "
+            # we want levelname to be in different color, so let's modify it
+            new_record.msg = "{pad}{color_begin}{msg}{color_end}".format(
+                pad=pad,
+                msg=new_record.msg,
+                color_begin=self.LOG_COLORS[new_record.levelno],
+                color_end=colorama.Style.RESET_ALL,
+            )
+        # now we can let standart formatting take care of the rest
+        return super(ColorFormatter, self).format(new_record, *args, **kwargs)
+
+
+class LogBase(type):
+    debuglevel = logging.root.level
+
+    def __init__(cls, *args):
+        super().__init__(*args)
+        logger_attribute_name = '_' + cls.__name__ + '__logger'
+        logger_debuglevel_name = '_' + cls.__name__ + '__debuglevel'
+        logger_name = '.'.join([c.__name__ for c in cls.mro()[-2::-1]])
+        LOG_CONFIG = {
+            "version": 1,
+            "disable_existing_loggers": False,
+            "formatters": {
+                "root": {
+                    "()": ColorFormatter,
+                    "format": "%(name)s - %(message)s",
+                }
+            },
+            "handlers": {
+                "root": {
+                    # "level": cls.__logger.level,
+                    "formatter": "root",
+                    "class": "logging.StreamHandler",
+                    "stream": "ext://sys.stdout",
+                }
+            },
+            "loggers": {
+                "": {
+                    "handlers": ["root"],
+                    # "level": cls.debuglevel,
+                    "propagate": False
+                }
+            },
+        }
+        logging.config.dictConfig(LOG_CONFIG)
+        logger = logging.getLogger(logger_name)
+
+        setattr(cls, logger_attribute_name, logger)
+        setattr(cls, logger_debuglevel_name, cls.debuglevel)
 
 
 class vendor(Enum):
@@ -110,24 +183,36 @@ class connection:
             0x04E8: ["Samsung", -1]
         }
         mode = "Unknown"
-        for device in self.detectusbdevices():
-            if device.vid == vendor.zte.value:
-                if device.pid == 0x0016:
-                    print(f"Detected a {atvendortable[device.vid][0]} device with pid {hex(device.pid)} in AT mode")
-                    mode = "AT"
-                    break
-                elif device.pid == 0x1403:
-                    print(f"Detected a {atvendortable[device.vid][0]} device with pid {hex(device.pid)} in Web mode")
-                    mode = "Web"
-                    self.ZTE_Web()
-                    break
-            elif device.vid == vendor.netgear.value:
-                try:
-                    # vid 0846, netgear mr1100, mr5100
-                    self.tn = Telnet("192.168.1.1", 5510, 5)
-                    self.connected = True
-                except:
-                    self.connected = False
+        try:
+            for device in self.detectusbdevices():
+                if device.vid == vendor.zte.value:
+                    if device.pid == 0x0016:
+                        print(f"Detected a {atvendortable[device.vid][0]} device with pid {hex(device.pid)} in AT mode")
+                        mode = "AT"
+                        break
+                    elif device.pid == 0x1403:
+                        print(f"Detected a {atvendortable[device.vid][0]} device with pid {hex(device.pid)} in Web mode")
+                        mode = "Web"
+                        self.ZTE_Web()
+                        break
+                elif device.vid == vendor.netgear.value:
+                    try:
+                        # vid 0846, netgear mr1100, mr5100
+                        self.tn = Telnet("192.168.1.1", 5510)
+                        self.connected = True
+                    except:
+                        self.connected = False
+        except:
+            print("No libusb driver found. Trying Telnet instead.")
+            try:
+                # vid 0846, netgear mr1100, mr5100
+                self.tn = Telnet("192.168.1.1", 5510)
+                self.connected = True
+            except:
+                self.connected = False
+                print("Failed to connect to Telnet.")
+                return
+            pass
         if mode in ["AT", "Unknown"]:
             for port in self.getserialports():
                 if port.vid in atvendortable:
@@ -168,8 +253,8 @@ class connection:
             data = ""
             while True:
                 tmp = self.tn.read_eager()
-                if tmp != b"":
-                    data += tmp.strip().decode('utf-8')
+                if tmp != "":
+                    data += tmp.strip()
                 else:
                     break
             if "ERROR" in data:
@@ -322,7 +407,8 @@ class adbtools(metaclass=LogBase):
 
     def SierraWireless(self, cn, info, enable):
         print("Sending at switch command")
-        kg = SierraKeygen(cn)
+        kg = SierraKeygen(cn=cn,devicegeneration=None)
+        kg.detectdevicegeneration()
         if kg.openlock():
             if enable:
                 if cn.send('AT!CUSTOM="ADBENABLE",1\r') != -1:
@@ -330,7 +416,7 @@ class adbtools(metaclass=LogBase):
                 kg.openlock()
                 if cn.send('AT!CUSTOM="TELNETENABLE",1\r') != -1:
                     time.sleep(5)
-                    tn = Telnet("192.168.1.1", 23, 15)
+                    tn = Telnet("192.168.1.1", 23)
                     tn.write(b"adbd &\r\n")
                     info = tn.read_eager()
                     print(info)
@@ -470,12 +556,13 @@ class adbtools(metaclass=LogBase):
         return False
 
     def Quectel(self, cn, enable: bool = True):
-        salt = cn.send("AT+QADBKEY?\r")
-        if salt != -1:
-            if len(salt) > 1:
-                salt = salt[1]
-            code = crypt.crypt("SH_adb_quectel", "$1$" + salt)
-            code = code[12:]
+        sn = cn.send("AT+QADBKEY?\r")
+        if sn != -1:
+            if len(sn) > 1:
+                sn = sn[1]
+            cc = md5_crypt(salt="")
+            code = cc.encrypt("SH_adb_quectel", salt=str(sn))
+            code = code[12:28]
             cn.send("AT+QADBKEY=\"%s\"\r" % code)
         if enable:
             if cn.send("AT+QCFG=\"usbcfg\",0x2C7C,0x125,1,1,1,1,1,1,0\r") == -1:
@@ -491,7 +578,7 @@ class adbtools(metaclass=LogBase):
 
 def main():
     version = "1.2"
-    info = '\nModem Gimme-ADB ' + version + ' (c) B. Kerler 2020-2021\n-------------------------------------------\n'
+    info = '\nModem Gimme-ADB ' + version + ' (c) B. Kerler 2020-2023\n-------------------------------------------\n'
     parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description=info)
     parser.add_argument(
         '-mode', help='Mode: enable or disable',
diff --git a/edlclient/Tools/fhloaderparse b/edlclient/Tools/fhloaderparse
index da9141c..9832f1c 100755
--- a/edlclient/Tools/fhloaderparse
+++ b/edlclient/Tools/fhloaderparse
@@ -1,4 +1,10 @@
 #!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 import os
 import sys
 from os import walk
@@ -7,12 +13,10 @@ from struct import unpack, pack
 from shutil import copyfile
 import os, sys, inspect
 from io import BytesIO
+from Library.utils import elf
+from Library.loader_db import loader_utils
+from Config.qualcomm_config import vendor
 current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
-parent_dir = os.path.dirname(os.path.dirname(current_dir))
-sys.path.insert(0, parent_dir)
-from edlclient.Library.utils import elf
-from edlclient.Library.loader_db import loader_utils
-from edlclient.Config.qualcomm_config import vendor
 
 lu=loader_utils()
 
@@ -102,9 +106,54 @@ def extract_hdr(memsection, version, sign_info, mem_section, code_size, signatur
         len1 = unpack(">H", mem_section[signatureoffset + 2:signatureoffset + 4])[0] + 4
         casignature2offset = signatureoffset + len1
         len2 = unpack(">H", mem_section[casignature2offset + 2:casignature2offset + 4])[0] + 4
-        rootsignature3offset = casignature2offset + len2
-        len3 = unpack(">H", mem_section[rootsignature3offset + 2:rootsignature3offset + 4])[0] + 4
-        sign_info.pk_hash = hashlib.sha384(mem_section[rootsignature3offset:rootsignature3offset + len3]).hexdigest()
+        rootsignature3 = mem_section[(casignature2offset + len2):(casignature2offset + len2) + 999999999].split(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff')[0]
+
+        idx = signatureoffset
+        signature = {}
+        while idx != -1:
+            if idx >= len(mem_section):
+                break
+            idx = mem_section.find('\x04\x0B'.encode(), idx)
+            if idx == -1:
+                break
+            length = mem_section[idx + 3]
+            if length > 60:
+                idx += 1
+                continue
+            try:
+                text = mem_section[idx + 4:idx + 4 + length].decode().split(' ')
+                signature[text[2]] = text[1]
+            except:
+                text = ""
+            idx += 1
+        idx = mem_section.find('QC_IMAGE_VERSION_STRING='.encode(), 0)
+        if idx != -1:
+            sign_info.qc_version = grabtext(mem_section[idx + len("QC_IMAGE_VERSION_STRING="):])
+        idx = mem_section.find('OEM_IMAGE_VERSION_STRING='.encode(), 0)
+        if idx != -1:
+            sign_info.oem_version = grabtext(mem_section[idx + len("OEM_IMAGE_VERSION_STRING="):])
+        idx = mem_section.find('IMAGE_VARIANT_STRING='.encode(), 0)
+        if idx != -1:
+            sign_info.image_variant = grabtext(mem_section[idx + len("IMAGE_VARIANT_STRING="):])
+        if "MODEL_ID" in signature:
+            sign_info.model_id = signature["MODEL_ID"]
+        if "OEM_ID" in signature:
+            sign_info.oem_id = signature["OEM_ID"]
+        if "HW_ID" in signature:
+            sign_info.hw_id = signature["HW_ID"]
+        if "SW_ID" in signature:
+            sign_info.sw_id = signature["SW_ID"]
+        if "SW_SIZE" in signature:
+            sign_info.sw_size = signature["SW_SIZE"]
+        if "SHA256" in signature:
+            sign_info.pk_hash = hashlib.sha256(rootsignature3).hexdigest()
+        elif "SHA384" in signature:
+            sign_info.pk_hash = hashlib.sha384(
+                rootsignature3).hexdigest()
+        else:
+            sign_info.pk_hash = hashlib.sha384(
+                rootsignature3).hexdigest()
+
     except:
         return None
     return sign_info
@@ -122,9 +171,8 @@ def extract_old_hdr(signatureoffset, sign_info, mem_section, code_size, signatur
         len1 = unpack(">H", mem_section[signatureoffset + 2:signatureoffset + 4])[0] + 4
         casignature2offset = signatureoffset + len1
         len2 = unpack(">H", mem_section[casignature2offset + 2:casignature2offset + 4])[0] + 4
-        rootsignature3offset = casignature2offset + len2
-        len3 = unpack(">H", mem_section[rootsignature3offset + 2:rootsignature3offset + 4])[0] + 4
-        sign_info.pk_hash = hashlib.sha256(mem_section[rootsignature3offset:rootsignature3offset + len3]).hexdigest()
+        rootsignature3 = mem_section[(casignature2offset + len2):(casignature2offset + len2) + 999999999].split(b'\xff\xff\xff\xff\xff\xff\xff\xff\xff')[0]
+        sign_info.pk_hash = hashlib.sha256(rootsignature3).hexdigest()
         idx = signatureoffset
 
         while idx != -1:
@@ -162,12 +210,24 @@ def extract_old_hdr(signatureoffset, sign_info, mem_section, code_size, signatur
             sign_info.sw_id = signature["SW_ID"]
         if "SW_SIZE" in signature:
             sign_info.sw_size = signature["SW_SIZE"]
+        if "SHA256" in signature:
+            sign_info.pk_hash = hashlib.sha256(rootsignature3).hexdigest()
+        elif "SHA384" in signature:
+            sign_info.pk_hash = hashlib.sha384(
+                rootsignature3).hexdigest()
     return sign_info
 
 
 def init_loader_db():
     loaderdb = {}
-    for (dirpath, dirnames, filenames) in os.walk(os.path.join(current_dir,"..","..","Loaders")):
+    loaders=os.path.join(current_dir,"..","..","Loaders")
+    if not os.path.exists(loaders):
+        loaders = os.path.join(current_dir, "Loaders")
+        if not os.path.exists(loaders):
+            print("Couldn't find Loaders directory")
+            return loaderdb
+
+    for (dirpath, dirnames, filenames) in os.walk(loaders):
         for filename in filenames:
             file_name = os.path.join(dirpath, filename)
             found = False
@@ -311,6 +371,7 @@ def main(argv):
                         print("%s has no signature." % filename)
                         copyfile(filename,
                                  os.path.join(outputdir, "Unknown", filename[filename.rfind("/") + 1:].lower()))
+                        elfpos += 0x30
                         continue
                     if version < 6:  # MSM,MDM
                         signatureoffset = memsection.file_start_addr + 0x28 + code_size + signature_size
diff --git a/edlclient/Tools/qc_diag.py b/edlclient/Tools/qc_diag.py
index 605516a..c04b1ac 100755
--- a/edlclient/Tools/qc_diag.py
+++ b/edlclient/Tools/qc_diag.py
@@ -1,7 +1,10 @@
 #!/usr/bin/env python3
-"""
-Licensed under MIT License, (c) B. Kerler 2018-2021
-"""
+# -*- coding: utf-8 -*-
+# (c) B.Kerler 2018-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
 import inspect
 import argparse
 import json
@@ -16,12 +19,18 @@ import os, sys
 current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
 parent_dir = os.path.dirname(os.path.dirname(current_dir))
 sys.path.insert(0, parent_dir)
-
-from edlclient.Library.utils import print_progress, read_object, write_object, LogBase
-from edlclient.Library.Connection.usblib import usb_class
-from edlclient.Library.Connection.seriallib import serial_class
-from edlclient.Library.hdlc import hdlc
-from edlclient.Config.usb_ids import default_diag_vid_pid
+try:
+    from Library.utils import print_progress, read_object, write_object, LogBase
+    from Library.Connection.usblib import usb_class
+    from Library.Connection.seriallib import serial_class
+    from Library.hdlc import hdlc
+    from Config.usb_ids import default_diag_vid_pid
+except:
+    from edlclient.Library.utils import print_progress, read_object, write_object, LogBase
+    from edlclient.Library.Connection.usblib import usb_class
+    from edlclient.Library.Connection.seriallib import serial_class
+    from edlclient.Library.hdlc import hdlc
+    from edlclient.Config.usb_ids import default_diag_vid_pid
 
 qcerror = {
     1: "None",
@@ -332,9 +341,14 @@ class qcdiag(metaclass=LogBase):
             self.__logger.addHandler(fh)
         import os, inspect
         current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
-        parent_dir = os.path.dirname(os.path.dirname(current_dir))
-        nvxml = os.path.join(parent_dir, "edlclient", "Config", "nvitems.xml")
-        e = ElementTree.parse(nvxml).getroot()
+        try:
+            parent_dir = os.path.dirname(os.path.dirname(current_dir))
+            nvxml = os.path.join(parent_dir, "edlclient", "Config", "nvitems.xml")
+            e = ElementTree.parse(nvxml).getroot()
+        except:
+            current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
+            nvxml = os.path.join(current_dir, "edlclient", "Config", "nvitems.xml")
+            e = ElementTree.parse(nvxml).getroot()
         for atype in e.findall("nv"):
             name = atype.get("name")
             identifier = int(atype.get("id"))
@@ -1141,22 +1155,42 @@ class DiagTools(metaclass=LogBase):
         self.interface = -1
         self.vid = None
         self.pid = None
-        self.serial = args.serial
-        if args.portname is not None:
+        try:
+            self.serial = args.serial
+        except:
+            self.serial = False
+        try:
             self.portname = args.portname
-            self.serial = True
-        else:
+        except:
             self.portname = ""
 
-        if args.vid != "":
+        if self.portname is not None and self.portname != "":
+            self.serial = True
+        try:
             self.vid = int(args.vid, 16)
-        if args.pid != "":
+        except:
+            pass
+        try:
             self.pid = int(args.pid, 16)
-        if args.interface != "":
+        except:
+            pass
+        try:
             self.interface = int(args.interface, 16)
+        except:
+            pass
+
+        try:
+            self.debugmode = args.debugmode
+        except:
+            self.debugmode = False
+
+        if self.vid is not None:
+            self.vid = int(args.vid, 16)
+        if self.pid is not None:
+            self.pid = int(args.pid, 16)
 
         logfilename = "diag.txt"
-        if args.debugmode:
+        if self.debugmode:
             if os.path.exists(logfilename):
                 os.remove(logfilename)
             fh = logging.FileHandler(logfilename)
diff --git a/edlclient/Tools/sierrakeygen.py b/edlclient/Tools/sierrakeygen.py
index 558a827..98e312a 100755
--- a/edlclient/Tools/sierrakeygen.py
+++ b/edlclient/Tools/sierrakeygen.py
@@ -1,24 +1,88 @@
 #!/usr/bin/env python3
 # -*- coding: utf-8 -*-
-# (c) B.Kerler 2019-2020 under MIT license
+# (c) B.Kerler 2019-2023 under GPLv3 license
 # If you use my code, make sure you refer to my name
-# If you want to use in a commercial product, ask me before integrating it
-
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
+import copy
 import sys
 import argparse
 import time
 import serial.tools.list_ports
-from telnetlib import Telnet
+from Exscript.protocols.telnetlib import Telnet
 from binascii import hexlify, unhexlify
+import logging
+import logging.config
+import logging.handlers
+import colorama
+
+class ColorFormatter(logging.Formatter):
+    LOG_COLORS = {
+        logging.ERROR: colorama.Fore.RED,
+        logging.DEBUG: colorama.Fore.LIGHTMAGENTA_EX,
+        logging.WARNING: colorama.Fore.YELLOW,
+    }
+
+    def format(self, record, *args, **kwargs):
+        # if the corresponding logger has children, they may receive modified
+        # record, so we want to keep it intact
+        new_record = copy.copy(record)
+        if new_record.levelno in self.LOG_COLORS:
+            pad = ""
+            if new_record.name != "root":
+                print(new_record.name)
+                pad = "[LIB]: "
+            # we want levelname to be in different color, so let's modify it
+            new_record.msg = "{pad}{color_begin}{msg}{color_end}".format(
+                pad=pad,
+                msg=new_record.msg,
+                color_begin=self.LOG_COLORS[new_record.levelno],
+                color_end=colorama.Style.RESET_ALL,
+            )
+        # now we can let standart formatting take care of the rest
+        return super(ColorFormatter, self).format(new_record, *args, **kwargs)
+
+
+class LogBase(type):
+    debuglevel = logging.root.level
+
+    def __init__(cls, *args):
+        super().__init__(*args)
+        logger_attribute_name = '_' + cls.__name__ + '__logger'
+        logger_debuglevel_name = '_' + cls.__name__ + '__debuglevel'
+        logger_name = '.'.join([c.__name__ for c in cls.mro()[-2::-1]])
+        LOG_CONFIG = {
+            "version": 1,
+            "disable_existing_loggers": False,
+            "formatters": {
+                "root": {
+                    "()": ColorFormatter,
+                    "format": "%(name)s - %(message)s",
+                }
+            },
+            "handlers": {
+                "root": {
+                    # "level": cls.__logger.level,
+                    "formatter": "root",
+                    "class": "logging.StreamHandler",
+                    "stream": "ext://sys.stdout",
+                }
+            },
+            "loggers": {
+                "": {
+                    "handlers": ["root"],
+                    # "level": cls.debuglevel,
+                    "propagate": False
+                }
+            },
+        }
+        logging.config.dictConfig(LOG_CONFIG)
+        logger = logging.getLogger(logger_name)
+
+        setattr(cls, logger_attribute_name, logger)
+        setattr(cls, logger_debuglevel_name, cls.debuglevel)
 
-try:
-    from edlclient.Library.utils import LogBase
-except Exception as e:
-    import os,inspect
-    current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
-    parent_dir = os.path.dirname(current_dir)
-    sys.path.insert(0, parent_dir)
-    from Library.utils import LogBase
 
 '''
 C7 = 7 0		0	2	7		5 0
@@ -58,10 +122,12 @@ prodtable = {
                     run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),  # EM7565
     "MDM9x06": dict(openlock=20, openmep=19, opencnd=20, clen=8, init=[7, 3, 0, 1, 5],
                     run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),  # WP77xx
-    "SDX55": dict(openlock=22, openmep=21, opencnd=22, clen=8, init=[7, 3, 0, 1, 5], #MR5100
-                       run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),
-    "MDM9x15A": dict(openlock=24, openmep=23, opencnd=24, clen=8, init=[7, 3, 0, 1, 5], #AC779S
-                       run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)")
+    "SDX55": dict(openlock=22, openmep=21, opencnd=22, clen=8, init=[7, 3, 0, 1, 5],  # MR5100, MR6400 old fw
+                  run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),
+    "MDM9x15A": dict(openlock=24, openmep=23, opencnd=24, clen=8, init=[7, 3, 0, 1, 5],  # AC779S
+                     run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)"),
+    "SDX65": dict(openlock=25, openmep=21, opencnd=26, clen=8, init=[7, 3, 0, 1, 5],  # MR6400 new fw
+                  run="resultbuffer[i]=self.SierraAlgo(challenge[i], 4, 2, 1, 0, 3, 2, 0, 0)")
 }
 
 infotable = {
@@ -81,68 +147,73 @@ infotable = {
     "MDM9x15A": ["AC779S"],
     "MDM9x30": ["EM7455", "MC7455", "EM7430", "MC7430"],
     "MDM9x30_V1": ["Netgear AC790/MDM9230"],
-    "MDM9x40": ["AC815s", "AC785s", "AC797S", "MR1100"],
+    "MDM9x40": ["MR1100", "AC815s", "AC785s", "AC797S"],
     "MDM9x50": ["EM7565", "EM7565-9", "EM7511", "EM7411"],
-    "SDX55" : ["MR5100","MR5200","ac797-100eus","MR6400"]
+    "SDX55": ["MR5100", "MR5200", "ac797-100eus", "MR6400"],
+    "SDX65": ["MR6400", "MR6500", "MR6110", "MR6150", "MR6450", "MR6550"]
 }
 
-keytable = bytearray([0xF0, 0x14, 0x55, 0x0D, 0x5E, 0xDA, 0x92, 0xB3, 0xA7, 0x6C, 0xCE, 0x84, 0x90, 0xBC, 0x7F, 0xED,
                       # 0 MC8775_H2.0.8.19 !OPENLOCK, !OPENCND .. MC8765V,MC8765,MC8755V,MC8775,MC8775V,MC8775,AC850,
                       #   AC860,AC875,AC881,AC881U,AC875, AC340U 1.13.12.14
-                      0x61, 0x94, 0xCE, 0xA7, 0xB0, 0xEA, 0x4F, 0x0A, 0x73, 0xC5, 0xC3, 0xA6, 0x5E, 0xEC, 0x1C, 0xE2,
+keytable = bytearray([0xF0, 0x14, 0x55, 0x0D, 0x5E, 0xDA, 0x92, 0xB3, 0xA7, 0x6C, 0xCE, 0x84, 0x90, 0xBC, 0x7F, 0xED,
                       # 1 MC8775_H2.0.8.19 AC340U, OPENMEP default
-                      0x39, 0xC6, 0x7B, 0x04, 0xCA, 0x50, 0x82, 0x1F, 0x19, 0x63, 0x36, 0xDE, 0x81, 0x49, 0xF0, 0xD7,
-                      # 2 AC750,AC710,AC7XX,SB750A,SB750,PC7000,AC313u OPENMEP
-                      0xDE, 0xA5, 0xAD, 0x2E, 0xBE, 0xE1, 0xC9, 0xEF, 0xCA, 0xF9, 0xFE, 0x1F, 0x17, 0xFE, 0xED, 0x3B,
-                      # 3 AC775,PC7200
-                      0xFE, 0xD4, 0x40, 0x52, 0x2D, 0x4B, 0x12, 0x5C, 0xE7, 0x0D, 0xF8, 0x79, 0xF8, 0xC0, 0xDD, 0x37,
-                      # 4 MC7455_02.30.01.01 OPENMEP
-                      0x3B, 0x18, 0x99, 0x6B, 0x57, 0x24, 0x0A, 0xD8, 0x94, 0x6F, 0x8E, 0xD9, 0x90, 0xBC, 0x67, 0x56,
-                      # 5 MC7455_02.30.01.01 OPENLOCK
-                      0x47, 0x4F, 0x4F, 0x44, 0x4A, 0x4F, 0x42, 0x44, 0x45, 0x43, 0x4F, 0x44, 0x49, 0x4E, 0x47, 0x2E,
-                      # 6 SWI9x50 Openmep Key SWI9X50C_01.08.04.00
-                      0x4F, 0x4D, 0x41, 0x52, 0x20, 0x44, 0x49, 0x44, 0x20, 0x54, 0x48, 0x49, 0x53, 0x2E, 0x2E, 0x2E,
-                      # 7 SWI9x50 Openlock Key SWI9X50C_01.08.04.00
-                      0x8F, 0xA5, 0x85, 0x05, 0x5E, 0xCF, 0x44, 0xA0, 0x98, 0x8B, 0x09, 0xE8, 0xBB, 0xC6, 0xF7, 0x65,
-                      # 8 MDM8200 Special
-                      0x4D, 0x42, 0xD8, 0xC1, 0x25, 0x44, 0xD8, 0xA0, 0x1D, 0x80, 0xC4, 0x52, 0x8E, 0xEC, 0x8B, 0xE3,
-                      # 9 SWI9x07 Openlock Key 02.25.02.01
-                      0xED, 0xA9, 0xB7, 0x0A, 0xDB, 0x85, 0x3D, 0xC0, 0x92, 0x49, 0x7D, 0x41, 0x9A, 0x91, 0x09, 0xEE,
-                      # 10 SWI9x07 Openmep Key 02.25.02.01
-                      0x8A, 0x56, 0x03, 0xF0, 0xBB, 0x9C, 0x13, 0xD2, 0x4E, 0xB2, 0x45, 0xAD, 0xC4, 0x0A, 0xE7, 0x52,
-                      # 11 NTG9X40C_11.14.08.11 / mdm9x40r11_core AC815s / SWI9x50 MR1100 Openlock Key
-                      0x2A, 0xEF, 0x07, 0x2B, 0x19, 0x60, 0xC9, 0x01, 0x8B, 0x87, 0xF2, 0x6E, 0xC1, 0x42, 0xA8, 0x3A,
-                      # 12 SWI9x50 MR1100 Openmep Key
-                      0x28, 0x55, 0x48, 0x52, 0x24, 0x72, 0x63, 0x37, 0x14, 0x26, 0x37, 0x50, 0xBE, 0xFE, 0x00, 0x00,
-                      # 13 SWI9x50 Unknown key
-                      0x22, 0x63, 0x48, 0x02, 0x24, 0x72, 0x27, 0x37, 0x19, 0x26, 0x37, 0x50, 0xBE, 0xEF, 0xCA, 0xFE,
-                      # 14 SWI9x50,SWI9X06Y IMEI nv key
-                      0x98, 0xE1, 0xC1, 0x93, 0xC3, 0xBF, 0xC3, 0x50, 0x8D, 0xA1, 0x35, 0xFE, 0x50, 0x47, 0xB3, 0xC4,
-                      # 15 NTG9X35C_02.08.29.00 Openmep Key AC791L/AC790S Old
                       0x61, 0x94, 0xCE, 0xA7, 0xB0, 0xEA, 0x4F, 0x0A, 0x73, 0xC5, 0xC3, 0xA6, 0x5E, 0xEC, 0x1C, 0xE2,
+                      # 2 AC750,AC710,AC7XX,SB750A,SB750,PC7000,AC313u OPENMEP
+                      0x39, 0xC6, 0x7B, 0x04, 0xCA, 0x50, 0x82, 0x1F, 0x19, 0x63, 0x36, 0xDE, 0x81, 0x49, 0xF0, 0xD7,
+                      # 3 AC775,PC7200
+                      0xDE, 0xA5, 0xAD, 0x2E, 0xBE, 0xE1, 0xC9, 0xEF, 0xCA, 0xF9, 0xFE, 0x1F, 0x17, 0xFE, 0xED, 0x3B,
+                      # 4 MC7455_02.30.01.01 OPENMEP
+                      0xFE, 0xD4, 0x40, 0x52, 0x2D, 0x4B, 0x12, 0x5C, 0xE7, 0x0D, 0xF8, 0x79, 0xF8, 0xC0, 0xDD, 0x37,
+                      # 5 MC7455_02.30.01.01 OPENLOCK
+                      0x3B, 0x18, 0x99, 0x6B, 0x57, 0x24, 0x0A, 0xD8, 0x94, 0x6F, 0x8E, 0xD9, 0x90, 0xBC, 0x67, 0x56,
+                      # 6 SWI9x50 Openmep Key SWI9X50C_01.08.04.00
+                      0x47, 0x4F, 0x4F, 0x44, 0x4A, 0x4F, 0x42, 0x44, 0x45, 0x43, 0x4F, 0x44, 0x49, 0x4E, 0x47, 0x2E,
+                      # 7 SWI9x50 Openlock Key SWI9X50C_01.08.04.00
+                      0x4F, 0x4D, 0x41, 0x52, 0x20, 0x44, 0x49, 0x44, 0x20, 0x54, 0x48, 0x49, 0x53, 0x2E, 0x2E, 0x2E,
+                      # 8 MDM8200 Special
+                      0x8F, 0xA5, 0x85, 0x05, 0x5E, 0xCF, 0x44, 0xA0, 0x98, 0x8B, 0x09, 0xE8, 0xBB, 0xC6, 0xF7, 0x65,
+                      # 9 SWI9x07 Openlock Key 02.25.02.01
+                      0x4D, 0x42, 0xD8, 0xC1, 0x25, 0x44, 0xD8, 0xA0, 0x1D, 0x80, 0xC4, 0x52, 0x8E, 0xEC, 0x8B, 0xE3,
+                      # 10 SWI9x07 Openmep Key 02.25.02.01
+                      0xED, 0xA9, 0xB7, 0x0A, 0xDB, 0x85, 0x3D, 0xC0, 0x92, 0x49, 0x7D, 0x41, 0x9A, 0x91, 0x09, 0xEE,
+                      # 11 NTG9X40C_11.14.08.11 / mdm9x40r11_core AC815s / SWI9x50 MR1100 Openlock Key
+                      0x8A, 0x56, 0x03, 0xF0, 0xBB, 0x9C, 0x13, 0xD2, 0x4E, 0xB2, 0x45, 0xAD, 0xC4, 0x0A, 0xE7, 0x52,
+                      # 12 SWI9x50 MR1100 Openmep Key
+                      0x2A, 0xEF, 0x07, 0x2B, 0x19, 0x60, 0xC9, 0x01, 0x8B, 0x87, 0xF2, 0x6E, 0xC1, 0x42, 0xA8, 0x3A,
+                      # 13 SWI9x50/SWI9x65 Unknown key
+                      0x28, 0x55, 0x48, 0x52, 0x24, 0x72, 0x63, 0x37, 0x14, 0x26, 0x37, 0x50, 0xBE, 0xFE, 0x00, 0x00,
+                      # 14 SWI9x50,SWI9X06Y,SWI9x65 IMEI nv key
+                      0x22, 0x63, 0x48, 0x02, 0x24, 0x72, 0x27, 0x37, 0x19, 0x26, 0x37, 0x50, 0xBE, 0xEF, 0xCA, 0xFE,
+                      # 15 NTG9X35C_02.08.29.00 Openmep Key AC791L/AC790S Old
+                      0x98, 0xE1, 0xC1, 0x93, 0xC3, 0xBF, 0xC3, 0x50, 0x8D, 0xA1, 0x35, 0xFE, 0x50, 0x47, 0xB3, 0xC4,
                       # 16 NTG9X35C_02.08.29.00 Openmep Key AC791/AC790S, NTGX55_10.25.15.02 MR5100 Alternative, NTG9X40C_30.00.12.00 Alternative
-                      0xC5, 0x50, 0x40, 0xDA, 0x23, 0xE8, 0xF4, 0x4C, 0x29, 0xE9, 0x07, 0xDE, 0x24, 0xE5, 0x2C, 0x1D,
+                      0x61, 0x94, 0xCE, 0xA7, 0xB0, 0xEA, 0x4F, 0x0A, 0x73, 0xC5, 0xC3, 0xA6, 0x5E, 0xEC, 0x1C, 0xE2,
                       # 17 NTG9X35C_02.08.29.00 Openlock Key AC791/AC790S Old
-                      0xF0, 0x14, 0x55, 0x0D, 0x5E, 0xDA, 0x92, 0xB3, 0xA7, 0x6C, 0xCE, 0x84, 0x90, 0xBC, 0x7F, 0xED,
+                      0xC5, 0x50, 0x40, 0xDA, 0x23, 0xE8, 0xF4, 0x4C, 0x29, 0xE9, 0x07, 0xDE, 0x24, 0xE5, 0x2C, 0x1D,
                       # 18 NTG9X35C_02.08.29.00 Openlock Key AC791/AC790S, NTGX55_10.25.15.02 MR5100 Alternative, NTG9X40C_30.00.12.00 Alternative
-                      0x78, 0x19, 0xC5, 0x6D, 0xC3, 0xD8, 0x25, 0x3E, 0x51, 0x60, 0x8C, 0xA7, 0x32, 0x83, 0x37, 0x9D,
+                      0xF0, 0x14, 0x55, 0x0D, 0x5E, 0xDA, 0x92, 0xB3, 0xA7, 0x6C, 0xCE, 0x84, 0x90, 0xBC, 0x7F, 0xED,
                       # 19 SWI9X06Y_02.14.04.00 Openmep Key WP77xx
-                      0x12, 0xF0, 0x79, 0x6B, 0x19, 0xC7, 0xF4, 0xEC, 0x50, 0xF3, 0x8C, 0x40, 0x02, 0xC9, 0x43, 0xC8,
+                      0x78, 0x19, 0xC5, 0x6D, 0xC3, 0xD8, 0x25, 0x3E, 0x51, 0x60, 0x8C, 0xA7, 0x32, 0x83, 0x37, 0x9D,
                       # 20 SWI9X06Y_02.14.04.00 Openlock Key WP77xx
-                      0x49, 0x42, 0xFF, 0x76, 0x8A, 0x95, 0xCF, 0x7B, 0xA3, 0x47, 0x5F, 0xF5, 0x8F, 0xD8, 0x45, 0xE4,
+                      0x12, 0xF0, 0x79, 0x6B, 0x19, 0xC7, 0xF4, 0xEC, 0x50, 0xF3, 0x8C, 0x40, 0x02, 0xC9, 0x43, 0xC8,
                       # 21 NTGX55 Openmep Key, NTGX55_10.25.15.02 MR5100, NTG9X40C_30.00.12.00
-                      0xF8, 0x1A, 0x3A, 0xCC, 0xAA, 0x2B, 0xA5, 0xE8, 0x8B, 0x53, 0x5A, 0x55, 0xB9, 0x65, 0x57, 0x98,
+                      0x49, 0x42, 0xFF, 0x76, 0x8A, 0x95, 0xCF, 0x7B, 0xA3, 0x47, 0x5F, 0xF5, 0x8F, 0xD8, 0x45, 0xE4,
                       # 22 NTGX55 Openlock Key, NTGX55_10.25.15.02 MR5100, NTG9X40C_30.00.12.00
-                      0x54, 0xC9, 0xC7, 0xA4, 0x02, 0x1C, 0xB0, 0x11, 0x05, 0x22, 0x39, 0xB7, 0x84, 0xEF, 0x16, 0xCA,
+                      0xF8, 0x1A, 0x3A, 0xCC, 0xAA, 0x2B, 0xA5, 0xE8, 0x8B, 0x53, 0x5A, 0x55, 0xB9, 0x65, 0x57, 0x98,
                       # 23 NTG9X15A Openlock Key, NTG9X15A_01.08.02.00
-                      0xC7, 0xE6, 0x39, 0xFE, 0x0A, 0xC7, 0xCA, 0x4D, 0x49, 0x8F, 0xD8, 0x55, 0xEB, 0x1A, 0xCD, 0x8A
+                      0x54, 0xC9, 0xC7, 0xA4, 0x02, 0x1C, 0xB0, 0x11, 0x05, 0x22, 0x39, 0xB7, 0x84, 0xEF, 0x16, 0xCA,
                       # 24 NTG9X15A Openlock Key, NTG9X15A_01.08.02.00
+                      0xC7, 0xE6, 0x39, 0xFE, 0x0A, 0xC7, 0xCA, 0x4D, 0x49, 0x8F, 0xD8, 0x55, 0xEB, 0x1A, 0xCD, 0x8A,
+                      # 25 NTGX65 Openlock Key, NTGX65_10.04.13.03
+                      0xF2, 0x4A, 0x9A, 0x2C, 0xDA, 0x3D, 0xA5, 0xE2, 0x6B, 0x56, 0x9A, 0x45, 0x29, 0x25, 0x77, 0x9A,
+                      # 26 NTGX65 Openadm Key, NTGX65_10.04.13.03
+                      0x46, 0x30, 0x33, 0x43, 0x44, 0x36, 0x42, 0x34, 0x41, 0x32, 0x31, 0x32, 0x30, 0x35, 0x39, 0x37
                       ])
 
-
 class SierraGenerator():
     tbl = bytearray()
     rtbl = bytearray()
+    devicegeneration = None
 
     def __init__(self):
         for _ in range(0, 0x14):
@@ -163,7 +234,7 @@ class SierraGenerator():
         lockid = prodtable[devicegeneration]["openlock"]
         clen = prodtable[devicegeneration]["clen"]
         if len(challenge) < clen:
-            challenge=[0 for _ in range(0, clen - len(challenge))]
+            challenge = [0 for _ in range(0, clen - len(challenge))]
 
         challengelen = len(challenge)
         if _type == 0:  # lockkey
@@ -179,7 +250,7 @@ class SierraGenerator():
         return resp
 
     def selftest(self):
-        test_table=[
+        test_table = [
             {"challenge": "8101A18AB3C3E66A", "devicegeneration": "MDM9x15", "response": "D1E128FCA8A963ED"},
             {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x40", "response": "1033773720F6EE66"},
             {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x30", "response": "1E02CE6A98B7DD2A"},
@@ -193,7 +264,8 @@ class SierraGenerator():
             {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9x30_V1", "response": "6A5E4C9CBCBDA7DC"},
             {"challenge": "BE96CBBEE0829BCA", "devicegeneration": "MDM9200", "response": "EEDBF8BFF8DAE346"},
             {"challenge": "20E253156762DACE", "devicegeneration": "SDX55", "response": "03940D7067145323"},
-            {"challenge": "2387885E7D290FEE", "devicegeneration": "MDM9x15A", "response": "DC3E51897BAA9C1E"}
+            {"challenge": "2387885E7D290FEE", "devicegeneration": "MDM9x15A", "response": "DC3E51897BAA9C1E"},
+            {"challenge": "4B1FEF9FD43C6DAA", "devicegeneration": "SDX65", "response":"1253C1B1E447B697"}
         ]
         for test in test_table:
             challenge = test["challenge"]
@@ -202,9 +274,9 @@ class SierraGenerator():
             openlock = self.run(devicegeneration, challenge, 0)
             padding = " " * (16 - len(devicegeneration))
             if openlock != response:
-                print(devicegeneration+padding+" FAILED!")
+                print(devicegeneration + padding + " FAILED!")
             else:
-                print(devicegeneration+padding+" PASSED :)")
+                print(devicegeneration + padding + " PASSED :)")
 
     def SierraPreInit(self, counter, key, keylen, challengelen, mcount):
         if counter != 0:
@@ -232,7 +304,7 @@ class SierraGenerator():
         if keylen == 0 or keylen > 0x20:
             retval = [0, keylen]
         elif 1 <= keylen <= 0x20:
-            self.tbl = [(i&0xFF) for i in range(0, 0x100)]
+            self.tbl = [(i & 0xFF) for i in range(0, 0x100)]
             mcount = 0
             cl = keylen & 0xffffff00
             i = 0xFF
@@ -306,13 +378,13 @@ class SierraGenerator():
         self.rtbl[4] = 0
         return 1
 
-    def SierraKeygen(self, challenge:bytearray, key: bytearray, challengelen:int, keylen:int):
+    def SierraKeygen(self, challenge: bytearray, key: bytearray, challengelen: int, keylen: int):
         challenge = challenge
-        resultbuffer=bytearray([0 for _ in range(0, 0x100 + 1)])
+        resultbuffer = bytearray([0 for _ in range(0, 0x100 + 1)])
         ret, keylen = self.SierraInit(key, keylen)
         if ret:
             for i in range(0, challengelen):
-                exec(prodtable[self.devicegeneration]["run"]) # uses challenge
+                exec(prodtable[self.devicegeneration]["run"])  # uses challenge
             self.SierraFinish()
         return resultbuffer
 
@@ -326,7 +398,10 @@ class connection:
             port = self.detect(port)
             if port == "":
                 self.tn = Telnet(ip, 5510)
-                self.connected = True
+                if self.tn:
+                    self.connected = True
+                else:
+                    self.connected = False
         if port != "":
             self.serial = serial.Serial(port=port, baudrate=115200, bytesize=8, parity='N', stopbits=1, timeout=1)
             self.connected = self.serial.is_open
@@ -366,8 +441,8 @@ class connection:
             data = ""
             while True:
                 tmp = self.tn.read_eager()
-                if tmp != b"":
-                    data += tmp.strip().decode('utf-8')
+                if tmp != "":
+                    data += tmp.strip()
                 else:
                     break
             return data.split("\r\n")
@@ -384,14 +459,15 @@ class connection:
             self.serial.close()
             self.connected = False
 
+
 class SierraKeygen(metaclass=LogBase):
-    def __init__(self,cn,devicegeneration=None):
-        self.cn=cn
+    def __init__(self, cn, devicegeneration=None):
+        self.cn = cn
         self.keygen = SierraGenerator()
-        if devicegeneration==None:
+        if devicegeneration == None:
             self.detectdevicegeneration()
         else:
-            self.devicegeneration=devicegeneration
+            self.devicegeneration = devicegeneration
 
     def run_selftest(self):
         print("Running self-test ...")
@@ -410,23 +486,23 @@ class SierraKeygen(metaclass=LogBase):
                         _model = line.split(":")[1].strip()
                 if revision != "":
                     if "9200" in revision:
-                        devicegeneration = "MDM9200" #AC762S NTG9200H2_03.05.14.12ap
+                        devicegeneration = "MDM9200"  # AC762S NTG9200H2_03.05.14.12ap
                     if "9X07" in revision:
                         devicegeneration = "MDM9x07"
                     elif "9X25" in revision:
                         if "NTG9X25C" in revision:
-                            devicegeneration = "MDM9200" #AC781S NTG9X25C_01.00.57.00
+                            devicegeneration = "MDM9200"  # AC781S NTG9X25C_01.00.57.00
                     elif "9X15" in revision:
                         if "NTG9X15A" in revision:
-                            devicegeneration = "MDM9x15A" #Aircard 779S
+                            devicegeneration = "MDM9x15A"  # Aircard 779S
                         elif "NTG9X15C" in revision:
-                            devicegeneration = "MDM9200" #AC770S NTG9X15C_01.18.02.00
+                            devicegeneration = "MDM9200"  # AC770S NTG9X15C_01.18.02.00
                         elif "9X15A" in revision:
                             devicegeneration = "MDM9x15A"
                         else:
                             devicegeneration = "MDM9x15"
                     elif "9X30" in revision:
-                        if "NTG9X35C" in revision: #790S NTG9X35C_11.11.15.03
+                        if "NTG9X35C" in revision:  # 790S NTG9X35C_11.11.15.03
                             devicegeneration = "MDM9x30_V1"
                         else:
                             devicegeneration = "MDM9x30"
@@ -434,26 +510,35 @@ class SierraKeygen(metaclass=LogBase):
                         devicegeneration = "MDM9x40"
                     elif "9X50" in revision:
                         if "NTG9X50" in revision:
-                            devicegeneration = "MDM9x40" #MR1100,AC797S NTG9X50C_12.06.03.00
+                            devicegeneration = "MDM9x40"  # MR1100,AC797S NTG9X50C_12.06.03.00
                         else:
                             devicegeneration = "MDM9x50"
                     elif "9X06" in revision:
                         devicegeneration = "MDM9x06"
                     elif "X55" in revision or "9X40C" in revision:
-                        if "NTGX55" in revision: #MR5100 NTGX55_10.25.15.02, MR5200 NTGX55_12.04.12.00
-                            devicegeneration = "SDX55"
-                        elif "NTGX65" in revision: #MR6400 NTGX65_10.01.41.02
+                        if "NTGX55" in revision:  # MR5100 NTGX55_10.25.15.02, MR5200 NTGX55_12.04.12.00
                             devicegeneration = "SDX55"
                         devicegeneration = "SDX55"
+                    elif "X65" in revision:
+                        if "NTGX65" in revision:
+                            version = revision[revision.find("_") + 1:revision.find(" ")].split(".")
+                            maj = int(version[0]) * 1000000 + int(version[1]) * 10000 + int(version[2]) * 100 + int(
+                                version[3])
+                            if maj < 10041303:
+                                devicegeneration = "SDX55"
+                            else:  # MR6400 NTGX65_10.04.13.03
+                                devicegeneration = "SDX65"
+                                   # MR6550 NTGX65_12.01.31.00
+                        devicegeneration = "SDX65"
                     else:
                         devicegeneration = ""
-                    #Missing:
+                    # Missing:
                     # SDX24 Sierra
                     # MR2100 NTGX24_10.17.03.00
                     # SDX55 Sierra
                     # AC810S NTG9X40C_11.14.08.16
                     # AC800S NTG9X40C_11.14.07.00
-                    self.devicegeneration=devicegeneration
+                    self.devicegeneration = devicegeneration
             else:
                 print("Error on getting ATI modem response. Wrong port? Aborting.")
                 self.cn.close()
@@ -461,9 +546,9 @@ class SierraKeygen(metaclass=LogBase):
 
     def openlock(self):
         print("Device generation detected: " + self.devicegeneration)
-        #print("Sending AT!ENTERCND=\"A710\" request.")
-        #info = self.cn.send("AT!ENTERCND=\"A710\"")
-        #if info == -1:
+        # print("Sending AT!ENTERCND=\"A710\" request.")
+        # info = self.cn.send("AT!ENTERCND=\"A710\"")
+        # if info == -1:
         #    print("Uhoh ... invalid entercnd password. Aborting ...")
         #    return
         print("Sending AT!OPENLOCK? request")
@@ -472,7 +557,7 @@ class SierraKeygen(metaclass=LogBase):
         if info != -1:
             if len(info) > 2:
                 challenge = info[1]
-                print("Received challenge: "+info[1])
+                print("Received challenge: " + info[1])
         else:
             print("Error on AT!OPENLOCK? request. Aborting.")
             return False
@@ -489,10 +574,11 @@ class SierraKeygen(metaclass=LogBase):
             return True
         return False
 
+
 def main(args):
     version = "1.5"
     info = 'Sierra Wireless Generator ' + version + ' (c) B. Kerler 2019-2021'
-    parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,description=info)
+    parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description=info)
 
     parser.add_argument(
         '-openlock', '-l',
@@ -546,10 +632,10 @@ def main(args):
             print(info)
             print("------------------------------------------------------------\n")
             print("Usage: ./sierrakeygen.py [-l,-m,-c] [challenge] -d [devicegeneration]")
-            print("Example: ./sierrakeygen.py.py -l BE96CBBEE0829BCA -d MDM9200")
-            print("or: ./sierrakeygen.py.py -u for auto unlock")
-            print("or: ./sierrakeygen.py.py -u -p [portname] for auto unlock with given portname")
-            print("or: ./sierrakeygen.py.py -s for self-test")
+            print("Example: ./sierrakeygen.py -l BE96CBBEE0829BCA -d MDM9200")
+            print("or: ./sierrakeygen.py -u for auto unlock")
+            print("or: ./sierrakeygen.py -u -p [portname] for auto unlock with given portname")
+            print("or: ./sierrakeygen.py -s for self-test")
             print("Supported devicegenerations :")
             for key in infotable:
                 info = f"\t{key}:\t\t"
@@ -569,14 +655,14 @@ def main(args):
             print("You need to specific a device generation as well. Option -d")
             exit(0)
     if devicegeneration == "":
-        devicegeneration=None
+        devicegeneration = None
     if args.selftest:
-        kg=SierraKeygen(None,"selftest")
+        kg = SierraKeygen(None, "selftest")
         kg.run_selftest()
     elif args.unlock:
         cn = connection(args.port, args.ip)
         if cn.connected:
-            kg=SierraKeygen(cn,devicegeneration)
+            kg = SierraKeygen(cn, devicegeneration)
             if kg.devicegeneration == "":
                 print("Unknown device generation. Please send me details :)")
             else:
diff --git a/edlclient/Tools/txt_to_loader.py b/edlclient/Tools/txt_to_loader.py
new file mode 100755
index 0000000..e469cd9
--- /dev/null
+++ b/edlclient/Tools/txt_to_loader.py
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+# (c) B.Kerler 2019-2023 under GPLv3 license
+# If you use my code, make sure you refer to my name
+#
+# !!!!! If you use this code in commercial products, your product is automatically
+# GPLv3 and has to be open sourced under GPLv3 as well. !!!!!
+# TXT to EDL Loader (c) B.Kerler 2023
+
+import os,sys
+from struct import unpack
+
+def main():
+	if len(sys.argv)<2:
+		print("Usage: ./txt_to_loader.py [log.txt] [loader.elf]")
+		sys.exit(0)
+	with open(sys.argv[1],"rb") as rf:
+		data=bytearray()
+		for line in rf.readlines():
+			if line[0]==0x20:
+				tt=line.split(b" ")[:-1]
+				tt=tt[1:17]
+				xx=b"".join(tt)
+				data.extend(bytes.fromhex(xx.decode('utf-8')))
+
+		outdata=bytearray()
+		i=0
+		seq=b"\x03\x00\x00\x00\x14\x00\x00\x00\x0D\x00\x00\x00"
+		with open(sys.argv[2], "wb") as wf:
+			while True:
+				idx=data.find(seq)
+				if idx==-1:
+					if i==0:
+						seq=b"\x12\x00\x00\x00\x20\x00\x00\x00\x0D\x00\x00\x00\x00\x00\x00\x00"
+						i+=1
+						continue
+					else:
+						break
+				else:
+					cmd=unpack("<I", data[idx:idx+4])[0]
+					if cmd==0x03:
+						cmd,tlen,slen,offset,length=unpack("<IIIII",data[idx:idx+0x14])
+					elif cmd==0x12:
+						cmd, tlen, slen, offset, length = unpack("<IIQQQ", data[idx:idx + 0x20])
+					data = data[idx + 0x20:]
+					print("Offset : %08X Length: %08X" %(offset,length))
+					while len(outdata)<offset+length:
+						outdata.append(0xFF)
+					outdata[offset:offset+length]=data[:length]
+					i+=1
+					data = data[length:]
+			wf.write(outdata)
+
+		print("Done.")
+		
+if __name__=="__main__":
+	main()
\ No newline at end of file
diff --git a/enableadb b/enableadb
new file mode 120000
index 0000000..f69c09d
--- /dev/null
+++ b/enableadb
@@ -0,0 +1 @@
+edlclient/Tools/enableadb
\ No newline at end of file
diff --git a/fastpwn b/fastpwn
index 8df06d1..c68a94f 100755
Binary files a/fastpwn and b/fastpwn differ
diff --git a/fastpwn.exe b/fastpwn.exe
new file mode 100755
index 0000000..506c4c4
Binary files /dev/null and b/fastpwn.exe differ
diff --git a/fhloaderparse b/fhloaderparse
new file mode 120000
index 0000000..87ce192
--- /dev/null
+++ b/fhloaderparse
@@ -0,0 +1 @@
+edlclient/Tools/fhloaderparse
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
index 63ce6c0..9222bec 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -8,6 +8,8 @@ requires = [
     "docopt",
     "pycryptodome",
     "colorama",
-    "usb"
+    "Exscript",
+    "requests",
+    "passlib"
 ]
 build-backend = "setuptools.build_meta"
diff --git a/qc_diag b/qc_diag
new file mode 120000
index 0000000..209370e
--- /dev/null
+++ b/qc_diag
@@ -0,0 +1 @@
+edlclient/Tools/qc_diag
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index c08275e..84192bb 100755
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,11 +2,13 @@ wheel
 pyusb>=1.1.0
 pyserial>=3.4
 docopt>=0.6.2
-pylzma>=0.5.0
 pycryptodome
+pycryptodomex
 lxml>=4.6.1
 colorama
-usb
 capstone
 keystone-engine
-
+qrcode
+requests
+passlib
+Exscript
diff --git a/setup.py b/setup.py
index e857bbb..78612b6 100755
--- a/setup.py
+++ b/setup.py
@@ -4,7 +4,7 @@ import os
 
 setup(
     name='edlclient',
-    version='3.60',
+    version='3.62',
     packages=find_packages(),
     long_description=open("README.md").read(),
     scripts=['edl','edlclient/Tools/qc_diag.py','edlclient/Tools/sierrakeygen.py','edlclient/Tools/boottodwnload','edlclient/Tools/enableadb','edlclient/Tools/fhloaderparse','edlclient/Tools/beagle_to_loader'],
@@ -16,10 +16,10 @@ setup(
     },
     classifiers=[
         "Programming Language :: Python :: 3",
-        "License :: OSI Approved :: MIT License",
+        "License :: OSI Approved :: GPlv3 License",
         "Operating System :: OS Independent",
     ],
-    license='MIT License',
+    license='GPLv3 License',
     install_requires=[
     'colorama',
     'docopt',
@@ -29,7 +29,10 @@ setup(
     'lxml',
     'pylzma',
     'pycryptodome',
-    'wheel'
+    'wheel',
+    'Exscript',
+    'requests',
+    'passlib'
     ],
     author='B. Kerler',
     author_email='info@revskills.de',
diff --git a/sierrakeygen b/sierrakeygen
new file mode 120000
index 0000000..7280d53
--- /dev/null
+++ b/sierrakeygen
@@ -0,0 +1 @@
+edlclient/Tools/sierrakeygen.py
\ No newline at end of file
diff --git a/sierrakeygen_README.md b/sierrakeygen_README.md
index 2d7b62a..8c0fdb8 100755
--- a/sierrakeygen_README.md
+++ b/sierrakeygen_README.md
@@ -1,6 +1,6 @@
 # Challenge/Response Generator for Sierra Wireless Cards V1.2
-(c) B. Kerler 2019-2020
-MIT License
+(c) B. Kerler 2019-2023
+GPLv3 License
 
 ## Why
 
@@ -20,11 +20,12 @@ MIT License
                 "WP75xx", "WP85xx", "WP8548", "WP8548G", "AC340U"],
     "MDM9x30": ["EM7455", "MC7455", "EM7430", "MC7430"],
     "MDM9x30_V1": ["Netgear AC790S/AC791L"],
-    "MDM9x40": ["AC815s", "AC785s","Netgear MR1100"],
+    "MDM9x40": ["MR1100", "AC815s", "AC785s"],
     "MDM9x50": ["EM7565", "EM7565-9", "EM7511"],
     "MDM9x06": ["WP77xx"],
-    "MDM9x07": ["SWI9X07Y", "WP76xx"]
-    
+    "MDM9x07": ["SWI9X07Y", "WP76xx"],
+    "SDX65": ["MR6400", "MR6500", "MR6110", "MR6150", "MR6450", "MR6550"]
+
 ## Installation
 
 - Get python >=3.6 64-Bit
@@ -346,7 +347,7 @@ MIT License
  
 ## License
 
-Published under MIT license
+Published under GPLv3 license
 Additional license limitations: No use in commercial products without prior permit by me.
 
 Enjoy !