diff --git a/.reuse/dep5 b/.reuse/dep5
index 69e066f5..283c680b 100644
--- a/.reuse/dep5
+++ b/.reuse/dep5
@@ -6,10 +6,10 @@ Files: CMakeSettings.json
        scripts/ps4_names.txt
        documents/changelog.txt
        documents/readme.txt
+	   documents/Screenshots/screenshot.png
        .github/shadps4.desktop
        .github/shadps4.png
        .gitmodules
-       documents/Screenshots/screenshot.png
        src/images/shadps4.ico
        src/images/controller_icon.png
        src/images/exit_icon.png
@@ -25,6 +25,12 @@ Files: CMakeSettings.json
        src/images/settings_icon.png
        src/images/stop_icon.png
        src/images/themes_icon.png
+	   src/images/flag_jp.png
+	   src/images/flag_eu.png
+	   src/images/flag_us.png
+	   src/images/flag_china.png
+	   src/images/flag_world.png
+	   src/images/flag_unk.png
        src/shadps4.rc
        src/shadps4.qrc
        externals/stb_image.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2e9b3a33..7bb549d2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -266,6 +266,8 @@ set(CORE src/core/aerolib/stubs.cpp
          src/core/file_format/pkg_type.h
          src/core/file_format/psf.cpp
          src/core/file_format/psf.h
+         src/core/file_format/trp.cpp
+         src/core/file_format/trp.h
          src/core/file_format/splash.h
          src/core/file_format/splash.cpp
          src/core/file_sys/fs.cpp
@@ -433,6 +435,12 @@ set(INPUT src/input/controller.cpp
           src/input/controller.h
 )
 
+set(EMULATOR src/emulator.cpp
+             src/emulator.h
+             src/sdl_window.h
+             src/sdl_window.cpp
+)
+
 # the above is shared in sdl and qt version (TODO share them all)
 
 if(ENABLE_QT_GUI)
@@ -442,9 +450,6 @@ set(QT_GUI
     src/qt_gui/main_window_ui.h
     src/qt_gui/main_window.cpp
     src/qt_gui/main_window.h
-    src/qt_gui/gui_settings.cpp
-    src/qt_gui/gui_settings.h
-    src/qt_gui/gui_save.h
     src/qt_gui/gui_context_menus.h
     src/qt_gui/game_list_utils.h
     src/qt_gui/game_info.cpp
@@ -457,11 +462,14 @@ set(QT_GUI
     src/qt_gui/game_install_dialog.h
     src/qt_gui/pkg_viewer.cpp
     src/qt_gui/pkg_viewer.h
-    src/qt_gui/settings.cpp
-    src/qt_gui/settings.h
+    src/qt_gui/trophy_viewer.cpp
+    src/qt_gui/trophy_viewer.h
+    src/qt_gui/elf_viewer.cpp
+    src/qt_gui/elf_viewer.h
     src/qt_gui/main_window_themes.cpp
     src/qt_gui/main_window_themes.h
     src/qt_gui/main.cpp
+    ${EMULATOR}
     ${RESOURCE_FILES}
     )
 endif()
@@ -475,8 +483,7 @@ if (ENABLE_QT_GUI)
         ${CORE}
         ${SHADER_RECOMPILER}
         ${VIDEO_CORE}
-        src/sdl_window.h
-        src/sdl_window.cpp
+        ${EMULATOR}
     )
 else()
     add_executable(shadps4
@@ -486,11 +493,8 @@ else()
         ${CORE}
         ${SHADER_RECOMPILER}
         ${VIDEO_CORE}
+        ${EMULATOR}
         src/main.cpp
-        src/emulator.cpp
-        src/emulator.h
-        src/sdl_window.h
-        src/sdl_window.cpp
     )
 endif()
 
@@ -548,7 +552,7 @@ target_include_directories(shadps4 PRIVATE ${HOST_SHADERS_INCLUDE})
 
 if (ENABLE_QT_GUI)
     set_target_properties(shadps4 PROPERTIES
-        WIN32_EXECUTABLE ON
+#        WIN32_EXECUTABLE ON
         MACOSX_BUNDLE ON)
 endif()
 
diff --git a/src/common/config.cpp b/src/common/config.cpp
index 63dd4c13..a577b143 100644
--- a/src/common/config.cpp
+++ b/src/common/config.cpp
@@ -24,6 +24,23 @@ static bool shouldDumpShaders = false;
 static bool shouldDumpPM4 = false;
 static bool vkValidation = false;
 static bool vkValidationSync = false;
+// Gui
+std::string settings_install_dir = "";
+u32 main_window_geometry_x = 400;
+u32 main_window_geometry_y = 400;
+u32 main_window_geometry_w = 1280;
+u32 main_window_geometry_h = 720;
+u32 mw_themes = 0;
+u32 m_icon_size = 36;
+u32 m_icon_size_grid = 69;
+u32 m_slider_pos = 0;
+u32 m_slider_pos_grid = 0;
+u32 m_table_mode = 0;
+u32 m_window_size_W = 1280;
+u32 m_window_size_H = 720;
+std::vector<std::string> m_pkg_viewer;
+std::vector<std::string> m_elf_viewer;
+std::vector<std::string> m_recent_files;
 
 bool isLleLibc() {
     return isLibc;
@@ -85,6 +102,101 @@ bool vkValidationSyncEnabled() {
     return vkValidationSync;
 }
 
+void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h) {
+    main_window_geometry_x = x;
+    main_window_geometry_y = y;
+    main_window_geometry_w = w;
+    main_window_geometry_h = h;
+}
+void setGameInstallDir(const std::string& dir) {
+    settings_install_dir = dir;
+}
+void setMainWindowTheme(u32 theme) {
+    mw_themes = theme;
+}
+void setIconSize(u32 size) {
+    m_icon_size = size;
+}
+void setIconSizeGrid(u32 size) {
+    m_icon_size_grid = size;
+}
+void setSliderPositon(u32 pos) {
+    m_slider_pos = pos;
+}
+void setSliderPositonGrid(u32 pos) {
+    m_slider_pos_grid = pos;
+}
+void setTableMode(u32 mode) {
+    m_table_mode = mode;
+}
+void setMainWindowWidth(u32 width) {
+    m_window_size_W = width;
+}
+void setMainWindowHeight(u32 height) {
+    m_window_size_H = height;
+}
+void setPkgViewer(std::vector<std::string> pkgList) {
+    m_pkg_viewer.resize(pkgList.size());
+    m_pkg_viewer = pkgList;
+}
+void setElfViewer(std::vector<std::string> elfList) {
+    m_elf_viewer.resize(elfList.size());
+    m_elf_viewer = elfList;
+}
+void setRecentFiles(std::vector<std::string> recentFiles) {
+    m_recent_files.resize(recentFiles.size());
+    m_recent_files = recentFiles;
+}
+
+u32 getMainWindowGeometryX() {
+    return main_window_geometry_x;
+}
+u32 getMainWindowGeometryY() {
+    return main_window_geometry_y;
+}
+u32 getMainWindowGeometryW() {
+    return main_window_geometry_w;
+}
+u32 getMainWindowGeometryH() {
+    return main_window_geometry_h;
+}
+std::string getGameInstallDir() {
+    return settings_install_dir;
+}
+u32 getMainWindowTheme() {
+    return mw_themes;
+}
+u32 getIconSize() {
+    return m_icon_size;
+}
+u32 getIconSizeGrid() {
+    return m_icon_size_grid;
+}
+u32 getSliderPositon() {
+    return m_slider_pos;
+}
+u32 getSliderPositonGrid() {
+    return m_slider_pos_grid;
+}
+u32 getTableMode() {
+    return m_table_mode;
+}
+u32 getMainWindowWidth() {
+    return m_window_size_W;
+}
+u32 getMainWindowHeight() {
+    return m_window_size_H;
+}
+std::vector<std::string> getPkgViewer() {
+    return m_pkg_viewer;
+}
+std::vector<std::string> getElfViewer() {
+    return m_elf_viewer;
+}
+std::vector<std::string> getRecentFiles() {
+    return m_recent_files;
+}
+
 void load(const std::filesystem::path& path) {
     // If the configuration file does not exist, create it and return
     std::error_code error;
@@ -152,6 +264,29 @@ void load(const std::filesystem::path& path) {
             isLibc = toml::find_or<toml::boolean>(lle, "libc", true);
         }
     }
+    if (data.contains("GUI")) {
+        auto guiResult = toml::expect<toml::value>(data.at("GUI"));
+        if (guiResult.is_ok()) {
+            auto gui = guiResult.unwrap();
+
+            m_icon_size = toml::find_or<toml::integer>(gui, "iconSize", 0);
+            m_icon_size_grid = toml::find_or<toml::integer>(gui, "iconSizeGrid", 0);
+            m_slider_pos = toml::find_or<toml::integer>(gui, "sliderPos", 0);
+            m_slider_pos_grid = toml::find_or<toml::integer>(gui, "sliderPosGrid", 0);
+            mw_themes = toml::find_or<toml::integer>(gui, "theme", 0);
+            m_window_size_W = toml::find_or<toml::integer>(gui, "mw_width", 0);
+            m_window_size_H = toml::find_or<toml::integer>(gui, "mw_height", 0);
+            settings_install_dir = toml::find_or<toml::string>(gui, "installDir", "");
+            main_window_geometry_x = toml::find_or<toml::integer>(gui, "geometry_x", 0);
+            main_window_geometry_y = toml::find_or<toml::integer>(gui, "geometry_y", 0);
+            main_window_geometry_w = toml::find_or<toml::integer>(gui, "geometry_w", 0);
+            main_window_geometry_h = toml::find_or<toml::integer>(gui, "geometry_h", 0);
+            m_pkg_viewer = toml::find_or<std::vector<std::string>>(gui, "pkgDirs", {});
+            m_elf_viewer = toml::find_or<std::vector<std::string>>(gui, "elfDirs", {});
+            m_recent_files = toml::find_or<std::vector<std::string>>(gui, "recentFiles", {});
+            m_table_mode = toml::find_or<toml::integer>(gui, "gameTableMode", 0);
+        }
+    }
 }
 void save(const std::filesystem::path& path) {
     toml::basic_value<toml::preserve_comments> data;
@@ -187,6 +322,22 @@ void save(const std::filesystem::path& path) {
     data["Vulkan"]["validation_sync"] = vkValidationSync;
     data["Debug"]["DebugDump"] = isDebugDump;
     data["LLE"]["libc"] = isLibc;
+    data["GUI"]["theme"] = mw_themes;
+    data["GUI"]["iconSize"] = m_icon_size;
+    data["GUI"]["sliderPos"] = m_slider_pos;
+    data["GUI"]["iconSizeGrid"] = m_icon_size_grid;
+    data["GUI"]["sliderPosGrid"] = m_slider_pos_grid;
+    data["GUI"]["gameTableMode"] = m_table_mode;
+    data["GUI"]["mw_width"] = m_window_size_W;
+    data["GUI"]["mw_height"] = m_window_size_H;
+    data["GUI"]["installDir"] = settings_install_dir;
+    data["GUI"]["geometry_x"] = main_window_geometry_x;
+    data["GUI"]["geometry_y"] = main_window_geometry_y;
+    data["GUI"]["geometry_w"] = main_window_geometry_w;
+    data["GUI"]["geometry_h"] = main_window_geometry_h;
+    data["GUI"]["pkgDirs"] = m_pkg_viewer;
+    data["GUI"]["elfDirs"] = m_elf_viewer;
+    data["GUI"]["recentFiles"] = m_recent_files;
 
     std::ofstream file(path, std::ios::out);
     file << data;
diff --git a/src/common/config.h b/src/common/config.h
index 7af028dc..c41c8c29 100644
--- a/src/common/config.h
+++ b/src/common/config.h
@@ -5,6 +5,7 @@
 
 #include <filesystem>
 #include "types.h"
+#include <vector>
 
 namespace Config {
 void load(const std::filesystem::path& path);
@@ -29,4 +30,36 @@ bool dumpPM4();
 bool vkValidationEnabled();
 bool vkValidationSyncEnabled();
 
+// Gui
+void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h);
+void setGameInstallDir(const std::string& dir);
+void setMainWindowTheme(u32 theme);
+void setIconSize(u32 size);
+void setIconSizeGrid(u32 size);
+void setSliderPositon(u32 pos);
+void setSliderPositonGrid(u32 pos);
+void setTableMode(u32 mode);
+void setMainWindowWidth(u32 width);
+void setMainWindowHeight(u32 height);
+void setPkgViewer(std::vector<std::string> pkgList);
+void setElfViewer(std::vector<std::string> elfList);
+void setRecentFiles(std::vector<std::string> recentFiles);
+
+u32 getMainWindowGeometryX();
+u32 getMainWindowGeometryY();
+u32 getMainWindowGeometryW();
+u32 getMainWindowGeometryH();
+std::string getGameInstallDir();
+u32 getMainWindowTheme();
+u32 getIconSize();
+u32 getIconSizeGrid();
+u32 getSliderPositon();
+u32 getSliderPositonGrid();
+u32 getTableMode();
+u32 getMainWindowWidth();
+u32 getMainWindowHeight();
+std::vector<std::string> getPkgViewer();
+std::vector<std::string> getElfViewer();
+std::vector<std::string> getRecentFiles();
+
 }; // namespace Config
diff --git a/src/common/io_file.h b/src/common/io_file.h
index 59cfcf7b..6beeb794 100644
--- a/src/common/io_file.h
+++ b/src/common/io_file.h
@@ -201,6 +201,11 @@ public:
         return WriteSpan(string);
     }
 
+    static void WriteBytes(const std::filesystem::path path, std::span<u8> vec) {
+        IOFile out(path, FileAccessMode::Write);
+        out.Write(vec);
+    }
+
 private:
     std::filesystem::path file_path;
     FileAccessMode file_access_mode{};
diff --git a/src/core/crypto/crypto.cpp b/src/core/crypto/crypto.cpp
index d45b5651..630faa34 100644
--- a/src/core/crypto/crypto.cpp
+++ b/src/core/crypto/crypto.cpp
@@ -4,67 +4,69 @@
 #include <array>
 #include "crypto.h"
 
-RSA::PrivateKey Crypto::key_pkg_derived_key3_keyset_init() {
-    InvertibleRSAFunction params;
-    params.SetPrime1(Integer(pkg_derived_key3_keyset.Prime1, 0x80));
-    params.SetPrime2(Integer(pkg_derived_key3_keyset.Prime2, 0x80));
+CryptoPP::RSA::PrivateKey Crypto::key_pkg_derived_key3_keyset_init() {
+    CryptoPP::InvertibleRSAFunction params;
+    params.SetPrime1(CryptoPP::Integer(pkg_derived_key3_keyset.Prime1, 0x80));
+    params.SetPrime2(CryptoPP::Integer(pkg_derived_key3_keyset.Prime2, 0x80));
 
-    params.SetPublicExponent(Integer(pkg_derived_key3_keyset.PublicExponent, 4));
-    params.SetPrivateExponent(Integer(pkg_derived_key3_keyset.PrivateExponent, 0x100));
+    params.SetPublicExponent(CryptoPP::Integer(pkg_derived_key3_keyset.PublicExponent, 4));
+    params.SetPrivateExponent(CryptoPP::Integer(pkg_derived_key3_keyset.PrivateExponent, 0x100));
 
-    params.SetModPrime1PrivateExponent(Integer(pkg_derived_key3_keyset.Exponent1, 0x80));
-    params.SetModPrime2PrivateExponent(Integer(pkg_derived_key3_keyset.Exponent2, 0x80));
+    params.SetModPrime1PrivateExponent(CryptoPP::Integer(pkg_derived_key3_keyset.Exponent1, 0x80));
+    params.SetModPrime2PrivateExponent(CryptoPP::Integer(pkg_derived_key3_keyset.Exponent2, 0x80));
 
-    params.SetModulus(Integer(pkg_derived_key3_keyset.Modulus, 0x100));
+    params.SetModulus(CryptoPP::Integer(pkg_derived_key3_keyset.Modulus, 0x100));
     params.SetMultiplicativeInverseOfPrime2ModPrime1(
-        Integer(pkg_derived_key3_keyset.Coefficient, 0x80));
+        CryptoPP::Integer(pkg_derived_key3_keyset.Coefficient, 0x80));
 
-    RSA::PrivateKey privateKey(params);
+    CryptoPP::RSA::PrivateKey privateKey(params);
 
     return privateKey;
 }
 
-RSA::PrivateKey Crypto::FakeKeyset_keyset_init() {
-    InvertibleRSAFunction params;
-    params.SetPrime1(Integer(FakeKeyset_keyset.Prime1, 0x80));
-    params.SetPrime2(Integer(FakeKeyset_keyset.Prime2, 0x80));
+CryptoPP::RSA::PrivateKey Crypto::FakeKeyset_keyset_init() {
+    CryptoPP::InvertibleRSAFunction params;
+    params.SetPrime1(CryptoPP::Integer(FakeKeyset_keyset.Prime1, 0x80));
+    params.SetPrime2(CryptoPP::Integer(FakeKeyset_keyset.Prime2, 0x80));
 
-    params.SetPublicExponent(Integer(FakeKeyset_keyset.PublicExponent, 4));
-    params.SetPrivateExponent(Integer(FakeKeyset_keyset.PrivateExponent, 0x100));
+    params.SetPublicExponent(CryptoPP::Integer(FakeKeyset_keyset.PublicExponent, 4));
+    params.SetPrivateExponent(CryptoPP::Integer(FakeKeyset_keyset.PrivateExponent, 0x100));
 
-    params.SetModPrime1PrivateExponent(Integer(FakeKeyset_keyset.Exponent1, 0x80));
-    params.SetModPrime2PrivateExponent(Integer(FakeKeyset_keyset.Exponent2, 0x80));
+    params.SetModPrime1PrivateExponent(CryptoPP::Integer(FakeKeyset_keyset.Exponent1, 0x80));
+    params.SetModPrime2PrivateExponent(CryptoPP::Integer(FakeKeyset_keyset.Exponent2, 0x80));
 
-    params.SetModulus(Integer(FakeKeyset_keyset.Modulus, 0x100));
-    params.SetMultiplicativeInverseOfPrime2ModPrime1(Integer(FakeKeyset_keyset.Coefficient, 0x80));
+    params.SetModulus(CryptoPP::Integer(FakeKeyset_keyset.Modulus, 0x100));
+    params.SetMultiplicativeInverseOfPrime2ModPrime1(
+        CryptoPP::Integer(FakeKeyset_keyset.Coefficient, 0x80));
 
-    RSA::PrivateKey privateKey(params);
+    CryptoPP::RSA::PrivateKey privateKey(params);
 
     return privateKey;
 }
 
-RSA::PrivateKey Crypto::DebugRifKeyset_init() {
-    AutoSeededRandomPool rng;
-    InvertibleRSAFunction params;
-    params.SetPrime1(Integer(DebugRifKeyset_keyset.Prime1, sizeof(DebugRifKeyset_keyset.Prime1)));
-    params.SetPrime2(Integer(DebugRifKeyset_keyset.Prime2, sizeof(DebugRifKeyset_keyset.Prime2)));
+CryptoPP::RSA::PrivateKey Crypto::DebugRifKeyset_init() {
+    CryptoPP::InvertibleRSAFunction params;
+    params.SetPrime1(
+        CryptoPP::Integer(DebugRifKeyset_keyset.Prime1, sizeof(DebugRifKeyset_keyset.Prime1)));
+    params.SetPrime2(
+        CryptoPP::Integer(DebugRifKeyset_keyset.Prime2, sizeof(DebugRifKeyset_keyset.Prime2)));
 
-    params.SetPublicExponent(Integer(DebugRifKeyset_keyset.PrivateExponent,
-                                     sizeof(DebugRifKeyset_keyset.PrivateExponent)));
-    params.SetPrivateExponent(Integer(DebugRifKeyset_keyset.PrivateExponent,
-                                      sizeof(DebugRifKeyset_keyset.PrivateExponent)));
+    params.SetPublicExponent(CryptoPP::Integer(DebugRifKeyset_keyset.PrivateExponent,
+                                               sizeof(DebugRifKeyset_keyset.PrivateExponent)));
+    params.SetPrivateExponent(CryptoPP::Integer(DebugRifKeyset_keyset.PrivateExponent,
+                                                sizeof(DebugRifKeyset_keyset.PrivateExponent)));
 
-    params.SetModPrime1PrivateExponent(
-        Integer(DebugRifKeyset_keyset.Exponent1, sizeof(DebugRifKeyset_keyset.Exponent1)));
-    params.SetModPrime2PrivateExponent(
-        Integer(DebugRifKeyset_keyset.Exponent2, sizeof(DebugRifKeyset_keyset.Exponent2)));
+    params.SetModPrime1PrivateExponent(CryptoPP::Integer(DebugRifKeyset_keyset.Exponent1,
+                                                         sizeof(DebugRifKeyset_keyset.Exponent1)));
+    params.SetModPrime2PrivateExponent(CryptoPP::Integer(DebugRifKeyset_keyset.Exponent2,
+                                                         sizeof(DebugRifKeyset_keyset.Exponent2)));
 
     params.SetModulus(
-        Integer(DebugRifKeyset_keyset.Modulus, sizeof(DebugRifKeyset_keyset.Modulus)));
-    params.SetMultiplicativeInverseOfPrime2ModPrime1(
-        Integer(DebugRifKeyset_keyset.Coefficient, sizeof(DebugRifKeyset_keyset.Coefficient)));
+        CryptoPP::Integer(DebugRifKeyset_keyset.Modulus, sizeof(DebugRifKeyset_keyset.Modulus)));
+    params.SetMultiplicativeInverseOfPrime2ModPrime1(CryptoPP::Integer(
+        DebugRifKeyset_keyset.Coefficient, sizeof(DebugRifKeyset_keyset.Coefficient)));
 
-    RSA::PrivateKey privateKey(params);
+    CryptoPP::RSA::PrivateKey privateKey(params);
 
     return privateKey;
 }
@@ -73,21 +75,21 @@ void Crypto::RSA2048Decrypt(std::span<CryptoPP::byte, 32> dec_key,
                             std::span<const CryptoPP::byte, 256> ciphertext,
                             bool is_dk3) { // RSAES_PKCS1v15_
     // Create an RSA decryptor
-    RSA::PrivateKey privateKey;
+    CryptoPP::RSA::PrivateKey privateKey;
     if (is_dk3) {
         privateKey = key_pkg_derived_key3_keyset_init();
     } else {
         privateKey = FakeKeyset_keyset_init();
     }
 
-    RSAES_PKCS1v15_Decryptor rsaDecryptor(privateKey);
+    CryptoPP::RSAES_PKCS1v15_Decryptor rsaDecryptor(privateKey);
 
     // Allocate memory for the decrypted data
     std::array<CryptoPP::byte, 256> decrypted;
 
     // Perform the decryption
-    AutoSeededRandomPool rng;
-    DecodingResult result =
+    CryptoPP::AutoSeededRandomPool rng;
+    CryptoPP::DecodingResult result =
         rsaDecryptor.Decrypt(rng, ciphertext.data(), decrypted.size(), decrypted.data());
     std::copy(decrypted.begin(), decrypted.begin() + dec_key.size(), dec_key.begin());
 }
@@ -120,6 +122,47 @@ void Crypto::aesCbcCfb128Decrypt(std::span<const CryptoPP::byte, 32> ivkey,
     }
 }
 
+void Crypto::aesCbcCfb128DecryptEntry(std::span<const CryptoPP::byte, 32> ivkey,
+                                      std::span<CryptoPP::byte> ciphertext,
+                                      std::span<CryptoPP::byte> decrypted) {
+    std::array<CryptoPP::byte, CryptoPP::AES::DEFAULT_KEYLENGTH> key;
+    std::array<CryptoPP::byte, CryptoPP::AES::DEFAULT_KEYLENGTH> iv;
+
+    std::copy(ivkey.begin() + 16, ivkey.begin() + 16 + key.size(), key.begin());
+    std::copy(ivkey.begin(), ivkey.begin() + iv.size(), iv.begin());
+
+    CryptoPP::AES::Decryption aesDecryption(key.data(), CryptoPP::AES::DEFAULT_KEYLENGTH);
+    CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, iv.data());
+
+    for (size_t i = 0; i < decrypted.size(); i += CryptoPP::AES::BLOCKSIZE) {
+        cbcDecryption.ProcessData(decrypted.data() + i, ciphertext.data() + i,
+                                  CryptoPP::AES::BLOCKSIZE);
+    }
+}
+
+void Crypto::decryptEFSM(std::span<CryptoPP::byte, 16> NPcommID,
+                         std::span<CryptoPP::byte, 16> efsmIv, std::span<CryptoPP::byte> ciphertext,
+                         std::span<CryptoPP::byte> decrypted) {
+
+    std::vector<CryptoPP::byte> TrophyKey = {0x21, 0xF4, 0x1A, 0x6B, 0xAD, 0x8A, 0x1D, 0x3E,
+                                             0xCA, 0x7A, 0xD5, 0x86, 0xC1, 0x01, 0xB7, 0xA9};
+    std::vector<CryptoPP::byte> TrophyIV = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    // step 1: Encrypt NPcommID
+    CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption encrypt;
+    encrypt.SetKeyWithIV(TrophyKey.data(), TrophyKey.size(), TrophyIV.data());
+
+    std::vector<CryptoPP::byte> trpKey(16);
+
+    encrypt.ProcessData(trpKey.data(), NPcommID.data(), 16);
+    // step 2: decrypt efsm.
+    CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decrypt;
+    decrypt.SetKeyWithIV(trpKey.data(), trpKey.size(), efsmIv.data());
+
+    for (size_t i = 0; i < decrypted.size(); i += CryptoPP::AES::BLOCKSIZE) {
+        decrypt.ProcessData(decrypted.data() + i, ciphertext.data() + i, CryptoPP::AES::BLOCKSIZE);
+    }
+}
+
 void Crypto::PfsGenCryptoKey(std::span<const CryptoPP::byte, 32> ekpfs,
                              std::span<const CryptoPP::byte, 16> seed,
                              std::span<CryptoPP::byte, 16> dataKey,
@@ -151,8 +194,8 @@ void Crypto::decryptPFS(std::span<const CryptoPP::byte, 16> dataKey,
     // Start at 0x10000 to keep the header when decrypting the whole pfs_image.
     for (int i = 0; i < src_image.size(); i += 0x1000) {
         const u64 current_sector = sector + (i / 0x1000);
-        CryptoPP::ECB_Mode<AES>::Encryption encrypt(tweakKey.data(), tweakKey.size());
-        CryptoPP::ECB_Mode<AES>::Decryption decrypt(dataKey.data(), dataKey.size());
+        CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption encrypt(tweakKey.data(), tweakKey.size());
+        CryptoPP::ECB_Mode<CryptoPP::AES>::Decryption decrypt(dataKey.data(), dataKey.size());
 
         std::array<CryptoPP::byte, 16> tweak{};
         std::array<CryptoPP::byte, 16> encryptedTweak;
diff --git a/src/core/crypto/crypto.h b/src/core/crypto/crypto.h
index 11edef84..2f926054 100644
--- a/src/core/crypto/crypto.h
+++ b/src/core/crypto/crypto.h
@@ -15,17 +15,15 @@
 #include "common/types.h"
 #include "keys.h"
 
-using namespace CryptoPP;
-
 class Crypto {
 public:
     PkgDerivedKey3Keyset pkg_derived_key3_keyset;
     FakeKeyset FakeKeyset_keyset;
     DebugRifKeyset DebugRifKeyset_keyset;
 
-    RSA::PrivateKey key_pkg_derived_key3_keyset_init();
-    RSA::PrivateKey FakeKeyset_keyset_init();
-    RSA::PrivateKey DebugRifKeyset_init();
+    CryptoPP::RSA::PrivateKey key_pkg_derived_key3_keyset_init();
+    CryptoPP::RSA::PrivateKey FakeKeyset_keyset_init();
+    CryptoPP::RSA::PrivateKey DebugRifKeyset_init();
 
     void RSA2048Decrypt(std::span<CryptoPP::byte, 32> dk3,
                         std::span<const CryptoPP::byte, 256> ciphertext,
@@ -35,6 +33,11 @@ public:
     void aesCbcCfb128Decrypt(std::span<const CryptoPP::byte, 32> ivkey,
                              std::span<const CryptoPP::byte, 256> ciphertext,
                              std::span<CryptoPP::byte, 256> decrypted);
+    void aesCbcCfb128DecryptEntry(std::span<const CryptoPP::byte, 32> ivkey,
+                                  std::span<CryptoPP::byte> ciphertext,
+                                  std::span<CryptoPP::byte> decrypted);
+    void decryptEFSM(std::span<CryptoPP::byte, 16>, std::span<CryptoPP::byte, 16> efsmIv,
+                     std::span<CryptoPP::byte> ciphertext, std::span<CryptoPP::byte> decrypted);
     void PfsGenCryptoKey(std::span<const CryptoPP::byte, 32> ekpfs,
                          std::span<const CryptoPP::byte, 16> seed,
                          std::span<CryptoPP::byte, 16> dataKey,
diff --git a/src/core/crypto/keys.h b/src/core/crypto/keys.h
index 5b8a8862..1c7ddfba 100644
--- a/src/core/crypto/keys.h
+++ b/src/core/crypto/keys.h
@@ -2,6 +2,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 #pragma once
+#include <rsa.h>
 
 class FakeKeyset {
 public:
diff --git a/src/core/file_format/pkg.cpp b/src/core/file_format/pkg.cpp
index 2251402d..5150e128 100644
--- a/src/core/file_format/pkg.cpp
+++ b/src/core/file_format/pkg.cpp
@@ -45,26 +45,54 @@ PKG::PKG() = default;
 
 PKG::~PKG() = default;
 
-bool PKG::Open(const std::string& filepath) {
+bool PKG::Open(const std::filesystem::path& filepath) {
     Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read);
     if (!file.IsOpen()) {
         return false;
     }
     pkgSize = file.GetSize();
 
-    PKGHeader pkgheader;
     file.Read(pkgheader);
+    if (pkgheader.magic != 0x7F434E54)
+        return false;
+
+    for (const auto& flag : flagNames) {
+        if (isFlagSet(pkgheader.pkg_content_flags, flag.first)) {
+            if (!pkgFlags.empty())
+                pkgFlags += (", ");
+            pkgFlags += (flag.second);
+        }
+    }
 
     // Find title id it is part of pkg_content_id starting at offset 0x40
     file.Seek(0x47); // skip first 7 characters of content_id
     file.Read(pkgTitleID);
 
+    file.Seek(0);
+    pkg.resize(pkgheader.pkg_promote_size);
+    file.Read(pkg);
+
+    u32 offset = pkgheader.pkg_table_entry_offset;
+    u32 n_files = pkgheader.pkg_table_entry_count;
+    for (int i = 0; i < n_files; i++) {
+        PKGEntry entry;
+        std::memcpy(&entry, &pkg[offset + i * 0x20], sizeof(entry));
+
+        // Try to figure out the name
+        const auto name = GetEntryNameByType(entry.id);
+        if (name == "param.sfo") {
+            sfo.clear();
+            file.Seek(entry.offset);
+            sfo.resize(entry.size);
+            file.ReadRaw<u8>(sfo.data(), entry.size);
+        }
+    }
     file.Close();
 
     return true;
 }
 
-bool PKG::Extract(const std::string& filepath, const std::filesystem::path& extract,
+bool PKG::Extract(const std::filesystem::path& filepath, const std::filesystem::path& extract,
                   std::string& failreason) {
     extract_path = extract;
     pkgpath = filepath;
@@ -75,6 +103,9 @@ bool PKG::Extract(const std::string& filepath, const std::filesystem::path& extr
     pkgSize = file.GetSize();
     file.ReadRaw<u8>(&pkgheader, sizeof(PKGHeader));
 
+    if (pkgheader.magic != 0x7F434E54)
+        return false;
+
     if (pkgheader.pkg_size > pkgSize) {
         failreason = "PKG file size is different";
         return false;
@@ -90,6 +121,7 @@ bool PKG::Extract(const std::string& filepath, const std::filesystem::path& extr
     u32 offset = pkgheader.pkg_table_entry_offset;
     u32 n_files = pkgheader.pkg_table_entry_count;
 
+    std::array<u8, 64> concatenated_ivkey_dk3;
     std::array<u8, 32> seed_digest;
     std::array<std::array<u8, 32>, 7> digest1;
     std::array<std::array<u8, 256>, 7> key1;
@@ -101,6 +133,9 @@ bool PKG::Extract(const std::string& filepath, const std::filesystem::path& extr
 
         // Try to figure out the name
         const auto name = GetEntryNameByType(entry.id);
+        const auto filepath = extract_path / "sce_sys" / name;
+        std::filesystem::create_directories(filepath.parent_path());
+
         if (name.empty()) {
             // Just print with id
             Common::FS::IOFile out(extract_path / "sce_sys" / std::to_string(entry.id),
@@ -110,9 +145,6 @@ bool PKG::Extract(const std::string& filepath, const std::filesystem::path& extr
             continue;
         }
 
-        const auto filepath = extract_path / "sce_sys" / name;
-        std::filesystem::create_directories(filepath.parent_path());
-
         if (entry.id == 0x1) {         // DIGESTS, seek;
                                        // file.Seek(entry.offset, fsSeekSet);
         } else if (entry.id == 0x10) { // ENTRY_KEYS, seek;
@@ -133,7 +165,6 @@ bool PKG::Extract(const std::string& filepath, const std::filesystem::path& extr
             file.Read(imgkeydata);
 
             // The Concatenated iv + dk3 imagekey for HASH256
-            std::array<CryptoPP::byte, 64> concatenated_ivkey_dk3;
             std::memcpy(concatenated_ivkey_dk3.data(), &entry, sizeof(entry));
             std::memcpy(concatenated_ivkey_dk3.data() + sizeof(entry), dk3_.data(), sizeof(dk3_));
 
@@ -150,10 +181,33 @@ bool PKG::Extract(const std::string& filepath, const std::filesystem::path& extr
         Common::FS::IOFile out(extract_path / "sce_sys" / name, Common::FS::FileAccessMode::Write);
         out.WriteRaw<u8>(pkg.data() + entry.offset, entry.size);
         out.Close();
+
+        // Decrypt Np stuff and overwrite.
+        if (entry.id == 0x400 || entry.id == 0x401 || entry.id == 0x402 ||
+            entry.id == 0x403) { // somehow 0x401 is not decrypting
+            decNp.resize(entry.size);
+            std::span<u8> cipherNp(pkg.data() + entry.offset, entry.size);
+            std::array<u8, 64> concatenated_ivkey_dk3_;
+            std::memcpy(concatenated_ivkey_dk3_.data(), &entry, sizeof(entry));
+            std::memcpy(concatenated_ivkey_dk3_.data() + sizeof(entry), dk3_.data(), sizeof(dk3_));
+            PKG::crypto.ivKeyHASH256(concatenated_ivkey_dk3_, ivKey);
+            PKG::crypto.aesCbcCfb128DecryptEntry(ivKey, cipherNp, decNp);
+
+            Common::FS::IOFile out(extract_path / "sce_sys" / name,
+                                   Common::FS::FileAccessMode::Write);
+            out.Write(decNp);
+            out.Close();
+        }
+    }
+
+    // Extract trophy files
+    if (!trp.Extract(extract_path)) {
+        // Do nothing some pkg come with no trp file.
+        // return false;
     }
 
     // Read the seed
-    std::array<CryptoPP::byte, 16> seed;
+    std::array<u8, 16> seed;
     file.Seek(pkgheader.pfs_image_offset + 0x370);
     file.Read(seed);
 
@@ -165,7 +219,7 @@ bool PKG::Extract(const std::string& filepath, const std::filesystem::path& extr
     std::vector<u8> pfs_encrypted(length);
     file.Seek(pkgheader.pfs_image_offset);
     file.Read(pfs_encrypted);
-
+    file.Close();
     // Decrypt the pfs_image.
     std::vector<u8> pfs_decrypted(length);
     PKG::crypto.decryptPFS(dataKey, tweakKey, pfs_encrypted, pfs_decrypted, 0);
diff --git a/src/core/file_format/pkg.h b/src/core/file_format/pkg.h
index bf2ce891..f77a7804 100644
--- a/src/core/file_format/pkg.h
+++ b/src/core/file_format/pkg.h
@@ -12,6 +12,7 @@
 #include "common/endian.h"
 #include "core/crypto/crypto.h"
 #include "pfs.h"
+#include "trp.h"
 
 struct PKGHeader {
     u32_be magic; // Magic
@@ -103,11 +104,13 @@ public:
     PKG();
     ~PKG();
 
-    bool Open(const std::string& filepath);
+    bool Open(const std::filesystem::path& filepath);
     void ExtractFiles(const int& index);
-    bool Extract(const std::string& filepath, const std::filesystem::path& extract,
+    bool Extract(const std::filesystem::path& filepath, const std::filesystem::path& extract,
                  std::string& failreason);
 
+    std::vector<u8> sfo;
+
     u32 GetNumberOfFiles() {
         return fsTable.size();
     }
@@ -116,16 +119,42 @@ public:
         return pkgSize;
     }
 
+    std::string GetPkgFlags() {
+        return pkgFlags;
+    }
+
     std::string_view GetTitleID() {
         return std::string_view(pkgTitleID, 9);
     }
 
+    PKGHeader GetPkgHeader() {
+        return pkgheader;
+    }
+
+    static bool isFlagSet(u32_be variable, PKGContentFlag flag) {
+        return (variable) & static_cast<u32>(flag);
+    }
+
+    static constexpr std::array<std::pair<PKGContentFlag, std::string_view>, 10> flagNames = {
+        {{PKGContentFlag::FIRST_PATCH, "FIRST_PATCH"},
+         {PKGContentFlag::PATCHGO, "PATCHGO"},
+         {PKGContentFlag::REMASTER, "REMASTER"},
+         {PKGContentFlag::PS_CLOUD, "PS_CLOUD"},
+         {PKGContentFlag::GD_AC, "GD_AC"},
+         {PKGContentFlag::NON_GAME, "NON_GAME"},
+         {PKGContentFlag::UNKNOWN_0x8000000, "UNKNOWN_0x8000000"},
+         {PKGContentFlag::SUBSEQUENT_PATCH, "SUBSEQUENT_PATCH"},
+         {PKGContentFlag::DELTA_PATCH, "DELTA_PATCH"},
+         {PKGContentFlag::CUMULATIVE_PATCH, "CUMULATIVE_PATCH"}}};
+
 private:
     Crypto crypto;
+    TRP trp;
     std::vector<u8> pkg;
     u64 pkgSize = 0;
     char pkgTitleID[9];
     PKGHeader pkgheader;
+    std::string pkgFlags;
 
     std::unordered_map<int, std::filesystem::path> extractPaths;
     std::vector<pfs_fs_table> fsTable;
@@ -133,12 +162,13 @@ private:
     std::vector<u64> sectorMap;
     u64 pfsc_offset;
 
-    std::array<CryptoPP::byte, 32> dk3_;
-    std::array<CryptoPP::byte, 32> ivKey;
-    std::array<CryptoPP::byte, 256> imgKey;
-    std::array<CryptoPP::byte, 32> ekpfsKey;
-    std::array<CryptoPP::byte, 16> dataKey;
-    std::array<CryptoPP::byte, 16> tweakKey;
+    std::array<u8, 32> dk3_;
+    std::array<u8, 32> ivKey;
+    std::array<u8, 256> imgKey;
+    std::array<u8, 32> ekpfsKey;
+    std::array<u8, 16> dataKey;
+    std::array<u8, 16> tweakKey;
+    std::vector<u8> decNp;
 
     std::filesystem::path pkgpath;
     std::filesystem::path current_dir;
diff --git a/src/core/file_format/psf.cpp b/src/core/file_format/psf.cpp
index fb808697..4a7f6215 100644
--- a/src/core/file_format/psf.cpp
+++ b/src/core/file_format/psf.cpp
@@ -12,16 +12,22 @@ PSF::PSF() = default;
 
 PSF::~PSF() = default;
 
-bool PSF::open(const std::string& filepath) {
-    Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read);
-    if (!file.IsOpen()) {
-        return false;
-    }
+bool PSF::open(const std::string& filepath, std::vector<u8> psfBuffer) {
+    if (!psfBuffer.empty()) {
+        psf.resize(psfBuffer.size());
+        psf = psfBuffer;
+    } else {
+        Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read);
+        if (!file.IsOpen()) {
+            return false;
+        }
 
-    const u64 psfSize = file.GetSize();
-    psf.resize(psfSize);
-    file.Seek(0);
-    file.Read(psf);
+        const u64 psfSize = file.GetSize();
+        psf.resize(psfSize);
+        file.Seek(0);
+        file.Read(psf);
+        file.Close();
+    }
 
     // Parse file contents
     PSFHeader header;
diff --git a/src/core/file_format/psf.h b/src/core/file_format/psf.h
index 31978630..17e515de 100644
--- a/src/core/file_format/psf.h
+++ b/src/core/file_format/psf.h
@@ -35,7 +35,7 @@ public:
     PSF();
     ~PSF();
 
-    bool open(const std::string& filepath);
+    bool open(const std::string& filepath, std::vector<u8> psfBuffer);
 
     std::string GetString(const std::string& key);
     u32 GetInteger(const std::string& key);
diff --git a/src/core/file_format/trp.cpp b/src/core/file_format/trp.cpp
new file mode 100644
index 00000000..cb55af2e
--- /dev/null
+++ b/src/core/file_format/trp.cpp
@@ -0,0 +1,91 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "trp.h"
+
+TRP::TRP() = default;
+TRP::~TRP() = default;
+
+void TRP::GetNPcommID(std::filesystem::path trophyPath, int index) {
+    std::filesystem::path trpPath = trophyPath / "sce_sys/npbind.dat";
+    Common::FS::IOFile npbindFile(trpPath, Common::FS::FileAccessMode::Read);
+    if (!npbindFile.IsOpen()) {
+        return;
+    }
+    npbindFile.Seek(0x84 + (index * 0x180));
+    npbindFile.ReadRaw<u8>(np_comm_id.data(), 12);
+    std::fill(np_comm_id.begin() + 12, np_comm_id.end(), 0); // fill with 0, we need 16 bytes.
+}
+
+static void removePadding(std::vector<u8>& vec) {
+    for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
+        if (*it == '>') {
+            size_t pos = std::distance(vec.begin(), it.base());
+            vec.resize(pos);
+            break;
+        }
+    }
+}
+
+bool TRP::Extract(std::filesystem::path trophyPath) {
+    std::string title = trophyPath.filename().string();
+    std::filesystem::path gameSysDir = trophyPath / "sce_sys/trophy/";
+    if (!std::filesystem::exists(gameSysDir)) {
+        return false;
+    }
+    for (int index = 0; const auto& it : std::filesystem::directory_iterator(gameSysDir)) {
+        if (it.is_regular_file()) {
+            GetNPcommID(trophyPath, index);
+
+            Common::FS::IOFile file(it.path(), Common::FS::FileAccessMode::Read);
+            if (!file.IsOpen()) {
+                return false;
+            }
+
+            TrpHeader header;
+            file.Read(header);
+            if (header.magic != 0xDCA24D00)
+                return false;
+
+            s64 seekPos = sizeof(TrpHeader);
+            std::filesystem::path trpFilesPath(std::filesystem::current_path() / "game_data" /
+                                               title / "TrophyFiles" / it.path().stem());
+            std::filesystem::create_directories(trpFilesPath / "Icons");
+            std::filesystem::create_directory(trpFilesPath / "Xml");
+
+            for (int i = 0; i < header.entry_num; i++) {
+                file.Seek(seekPos);
+                seekPos += (s64)header.entry_size;
+                TrpEntry entry;
+                file.Read(entry);
+                std::string_view name(entry.entry_name);
+                if (entry.flag == 0 && name.find("TROP") != std::string::npos) { // PNG
+                    file.Seek(entry.entry_pos);
+                    std::vector<u8> icon(entry.entry_len);
+                    file.Read(icon);
+                    Common::FS::IOFile::WriteBytes(trpFilesPath / "Icons" / name, icon);
+                }
+                if (entry.flag == 3 && np_comm_id[0] == 'N' &&
+                    np_comm_id[1] == 'P') { // ESFM, encrypted.
+                    file.Seek(entry.entry_pos);
+                    file.Read(esfmIv); // get iv key.
+                    // Skip the first 16 bytes which are the iv key on every entry as we want a
+                    // clean xml file.
+                    std::vector<u8> ESFM(entry.entry_len - iv_len);
+                    std::vector<u8> XML(entry.entry_len - iv_len);
+                    file.Seek(entry.entry_pos + iv_len);
+                    file.Read(ESFM);
+                    crypto.decryptEFSM(np_comm_id, esfmIv, ESFM, XML); // decrypt
+                    removePadding(XML);
+                    std::string xml_name = entry.entry_name;
+                    size_t pos = xml_name.find("ESFM");
+                    if (pos != std::string::npos)
+                        xml_name.replace(pos, xml_name.length(), "XML");
+                    Common::FS::IOFile::WriteBytes(trpFilesPath / "Xml" / xml_name, XML);
+                }
+            }
+        }
+        index++;
+    }
+    return true;
+}
\ No newline at end of file
diff --git a/src/core/file_format/trp.h b/src/core/file_format/trp.h
new file mode 100644
index 00000000..6d1f13bd
--- /dev/null
+++ b/src/core/file_format/trp.h
@@ -0,0 +1,46 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <vector>
+#include "common/endian.h"
+#include "common/io_file.h"
+#include "common/types.h"
+#include "core/crypto/crypto.h"
+
+struct TrpHeader {
+    u32_be magic; // (0xDCA24D00)
+    u32_be version;
+    u64_be file_size;         // size of full trp file
+    u32_be entry_num;         // num entries
+    u32_be entry_size;        // size of entry
+    u32_be dev_flag;          // 1: dev
+    unsigned char digest[20]; // sha1 hash
+    u32_be key_index;         // 3031300?
+    unsigned char padding[44];
+};
+
+struct TrpEntry {
+    char entry_name[32];
+    u64_be entry_pos;
+    u64_be entry_len;
+    u32_be flag; // 3 = CONFIG/ESFM , 0 = PNG
+    unsigned char padding[12];
+};
+
+class TRP {
+public:
+    TRP();
+    ~TRP();
+    bool Extract(std::filesystem::path trophyPath);
+    void GetNPcommID(std::filesystem::path trophyPath, int index);
+
+private:
+    Crypto crypto;
+    std::vector<u8> NPcommID = std::vector<u8>(12);
+    std::array<u8, 16> np_comm_id{};
+    std::array<u8, 16> esfmIv{};
+    std::filesystem::path trpFilesPath;
+    static constexpr int iv_len = 16;
+};
\ No newline at end of file
diff --git a/src/core/loader.cpp b/src/core/loader.cpp
index b12821c1..f80bfbb8 100644
--- a/src/core/loader.cpp
+++ b/src/core/loader.cpp
@@ -7,7 +7,7 @@
 
 namespace Loader {
 
-FileTypes DetectFileType(const std::string& filepath) {
+FileTypes DetectFileType(const std::filesystem::path& filepath) {
     // No file loaded
     if (filepath.empty()) {
         return FileTypes::Unknown;
diff --git a/src/core/loader.h b/src/core/loader.h
index 2f4d0651..608970dc 100644
--- a/src/core/loader.h
+++ b/src/core/loader.h
@@ -14,5 +14,5 @@ enum class FileTypes {
     Pkg,
 };
 
-FileTypes DetectFileType(const std::string& filepath);
+FileTypes DetectFileType(const std::filesystem::path& filepath);
 } // namespace Loader
diff --git a/src/emulator.cpp b/src/emulator.cpp
index dd8de3a7..793d996a 100644
--- a/src/emulator.cpp
+++ b/src/emulator.cpp
@@ -65,7 +65,7 @@ void Emulator::Run(const std::filesystem::path& file) {
         for (const auto& entry : std::filesystem::directory_iterator(sce_sys_folder)) {
             if (entry.path().filename() == "param.sfo") {
                 auto* param_sfo = Common::Singleton<PSF>::Instance();
-                param_sfo->open(sce_sys_folder.string() + "/param.sfo");
+                param_sfo->open(sce_sys_folder.string() + "/param.sfo", {});
                 std::string id(param_sfo->GetString("CONTENT_ID"), 7, 9);
                 std::string title(param_sfo->GetString("TITLE"));
                 LOG_INFO(Loader, "Game id: {} Title: {}", id, title);
diff --git a/src/images/flag_china.png b/src/images/flag_china.png
new file mode 100644
index 00000000..13bf221e
Binary files /dev/null and b/src/images/flag_china.png differ
diff --git a/src/images/flag_eu.png b/src/images/flag_eu.png
new file mode 100644
index 00000000..0922e11e
Binary files /dev/null and b/src/images/flag_eu.png differ
diff --git a/src/images/flag_jp.png b/src/images/flag_jp.png
new file mode 100644
index 00000000..6433eecf
Binary files /dev/null and b/src/images/flag_jp.png differ
diff --git a/src/images/flag_unk.png b/src/images/flag_unk.png
new file mode 100644
index 00000000..5c381028
Binary files /dev/null and b/src/images/flag_unk.png differ
diff --git a/src/images/flag_us.png b/src/images/flag_us.png
new file mode 100644
index 00000000..f4bf3a30
Binary files /dev/null and b/src/images/flag_us.png differ
diff --git a/src/images/flag_world.png b/src/images/flag_world.png
new file mode 100644
index 00000000..0dcccf80
Binary files /dev/null and b/src/images/flag_world.png differ
diff --git a/src/qt_gui/elf_viewer.cpp b/src/qt_gui/elf_viewer.cpp
new file mode 100644
index 00000000..1674e1ab
--- /dev/null
+++ b/src/qt_gui/elf_viewer.cpp
@@ -0,0 +1,96 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <QHeaderView>
+#include "elf_viewer.h"
+ElfViewer::ElfViewer(QWidget* parent) : QTableWidget(parent) {
+    dir_list_std = Config::getElfViewer();
+    for (const auto& str : dir_list_std) {
+        dir_list.append(QString::fromStdString(str));
+    }
+
+    CheckElfFolders();
+
+    this->setShowGrid(false);
+    this->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    this->setSelectionBehavior(QAbstractItemView::SelectRows);
+    this->setSelectionMode(QAbstractItemView::SingleSelection);
+    this->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
+    this->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
+    this->verticalScrollBar()->installEventFilter(this);
+    this->verticalScrollBar()->setSingleStep(20);
+    this->horizontalScrollBar()->setSingleStep(20);
+    this->verticalHeader()->setVisible(false);
+    this->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu);
+    this->horizontalHeader()->setHighlightSections(false);
+    this->horizontalHeader()->setSortIndicatorShown(true);
+    this->horizontalHeader()->setStretchLastSection(true);
+    this->setContextMenuPolicy(Qt::CustomContextMenu);
+    this->setColumnCount(2);
+    this->setColumnWidth(0, 250);
+    this->setColumnWidth(1, 400);
+    this->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
+    this->setStyleSheet("QTableWidget { background-color: #D3D3D3; }");
+    OpenElfFiles();
+    QStringList headers;
+    headers << "Name"
+            << "Path";
+    this->setHorizontalHeaderLabels(headers);
+    this->horizontalHeader()->setSortIndicatorShown(true);
+    this->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
+}
+
+void ElfViewer::OpenElfFolder() {
+    QString folderPath =
+        QFileDialog::getExistingDirectory(this, tr("Open Folder"), QDir::homePath());
+    if (!dir_list.contains(folderPath)) {
+        dir_list.append(folderPath);
+        QDir directory(folderPath);
+        QFileInfoList fileInfoList = directory.entryInfoList(QDir::Files);
+        for (const QFileInfo& fileInfo : fileInfoList) {
+            QString file_ext = fileInfo.suffix();
+            if (fileInfo.isFile() && (file_ext == "bin" || file_ext == "elf")) {
+                m_elf_list.append(fileInfo.absoluteFilePath());
+            }
+        }
+        std::ranges::sort(m_elf_list);
+        OpenElfFiles();
+        dir_list_std.clear();
+        for (auto dir : dir_list) {
+            dir_list_std.push_back(dir.toStdString());
+        }
+        Config::setElfViewer(dir_list_std);
+    } else {
+        // qDebug() << "Folder selection canceled.";
+    }
+}
+
+void ElfViewer::CheckElfFolders() {
+    m_elf_list.clear();
+    for (const QString& dir : dir_list) {
+        QDir directory(dir);
+        QFileInfoList fileInfoList = directory.entryInfoList(QDir::Files);
+        for (const QFileInfo& fileInfo : fileInfoList) {
+            QString file_ext = fileInfo.suffix();
+            if (fileInfo.isFile() && (file_ext == "bin" || file_ext == "elf")) {
+                m_elf_list.append(fileInfo.absoluteFilePath());
+            }
+        }
+    }
+    std::sort(m_elf_list.begin(), m_elf_list.end());
+}
+
+void ElfViewer::OpenElfFiles() {
+    this->clearContents();
+    this->setRowCount(m_elf_list.size());
+    for (int i = 0; auto elf : m_elf_list) {
+        QTableWidgetItem* item = new QTableWidgetItem();
+        QFileInfo fileInfo(m_elf_list[i]);
+        QString fileName = fileInfo.baseName();
+        SetTableItem(this, i, 0, fileName);
+        item = new QTableWidgetItem();
+        SetTableItem(this, i, 1, m_elf_list[i]);
+        i++;
+    }
+    this->resizeColumnsToContents();
+}
\ No newline at end of file
diff --git a/src/qt_gui/elf_viewer.h b/src/qt_gui/elf_viewer.h
new file mode 100644
index 00000000..15aeb55f
--- /dev/null
+++ b/src/qt_gui/elf_viewer.h
@@ -0,0 +1,61 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <ranges>
+#include <QApplication>
+#include <QFileDialog>
+#include <QMainWindow>
+#include <QMenuBar>
+#include <QScrollBar>
+#include <QTableWidget>
+#include <QTextEdit>
+#include <QTreeView>
+#include <QVBoxLayout>
+#include <QWidget>
+#include "game_list_frame.h"
+#include "src/core/loader/elf.h"
+
+class ElfViewer : public QTableWidget {
+    Q_OBJECT
+public:
+    explicit ElfViewer(QWidget* parent = nullptr);
+    QStringList m_elf_list;
+
+private:
+    void CheckElfFolders();
+    void OpenElfFiles();
+
+    Core::Loader::Elf m_elf_file;
+    QStringList dir_list;
+    QStringList elf_headers_list;
+    std::vector<std::string> dir_list_std;
+
+    void SetTableItem(QTableWidget* game_list, int row, int column, QString itemStr) {
+        QTableWidgetItem* item = new QTableWidgetItem();
+        QWidget* widget = new QWidget(this);
+        QVBoxLayout* layout = new QVBoxLayout(widget);
+        QLabel* label = new QLabel(itemStr, widget);
+
+        label->setStyleSheet("color: white; font-size: 15px; font-weight: bold;");
+
+        // Create shadow effect
+        QGraphicsDropShadowEffect* shadowEffect = new QGraphicsDropShadowEffect();
+        shadowEffect->setBlurRadius(5);               // Set the blur radius of the shadow
+        shadowEffect->setColor(QColor(0, 0, 0, 160)); // Set the color and opacity of the shadow
+        shadowEffect->setOffset(2, 2);                // Set the offset of the shadow
+
+        label->setGraphicsEffect(shadowEffect); // Apply shadow effect to the QLabel
+
+        layout->addWidget(label);
+        if (column != 8 && column != 1)
+            layout->setAlignment(Qt::AlignCenter);
+        widget->setLayout(layout);
+        game_list->setItem(row, column, item);
+        game_list->setCellWidget(row, column, widget);
+    }
+
+public slots:
+    void OpenElfFolder();
+};
diff --git a/src/qt_gui/game_grid_frame.cpp b/src/qt_gui/game_grid_frame.cpp
index 0b57a162..ca28e9ce 100644
--- a/src/qt_gui/game_grid_frame.cpp
+++ b/src/qt_gui/game_grid_frame.cpp
@@ -3,12 +3,9 @@
 
 #include "game_grid_frame.h"
 
-GameGridFrame::GameGridFrame(std::shared_ptr<GameInfoClass> game_info_get,
-                             std::shared_ptr<GuiSettings> m_gui_settings, QWidget* parent)
-    : QTableWidget(parent) {
-    m_game_info = game_info_get;
-    m_gui_settings_ = m_gui_settings;
-    icon_size = m_gui_settings->GetValue(gui::m_icon_size_grid).toInt();
+GameGridFrame::GameGridFrame(std::shared_ptr<GameInfoClass> game_info_get, QWidget* parent)
+    : QTableWidget(parent), m_game_info(game_info_get) {
+    icon_size = Config::getIconSizeGrid();
     windowWidth = parent->width();
     this->setShowGrid(false);
     this->setEditTriggers(QAbstractItemView::NoEditTriggers);
@@ -33,6 +30,12 @@ GameGridFrame::GameGridFrame(std::shared_ptr<GameInfoClass> game_info_get,
     connect(this, &QTableWidget::customContextMenuRequested, this, [=, this](const QPoint& pos) {
         m_gui_context_menus.RequestGameMenu(pos, m_game_info->m_games, this, false);
     });
+    connect(this, &QTableWidget::cellClicked, this, [&]() {
+        cellClicked = true;
+        crtRow = this->currentRow();
+        crtColumn = this->currentColumn();
+        columnCnt = this->columnCount();
+    });
 }
 
 void GameGridFrame::PopulateGameGrid(QVector<GameInfo> m_games_search, bool fromSearch) {
@@ -43,9 +46,7 @@ void GameGridFrame::PopulateGameGrid(QVector<GameInfo> m_games_search, bool from
     else
         m_games_ = m_game_info->m_games;
     m_games_shared = std::make_shared<QVector<GameInfo>>(m_games_);
-
-    icon_size = m_gui_settings_->GetValue(gui::m_icon_size_grid)
-                    .toInt(); // update icon size for resize event.
+    icon_size = Config::getIconSizeGrid(); // update icon size for resize event.
 
     int gamesPerRow = windowWidth / (icon_size + 20); // 2 x cell widget border size.
     int row = 0;
@@ -62,10 +63,10 @@ void GameGridFrame::PopulateGameGrid(QVector<GameInfo> m_games_search, bool from
         QWidget* widget = new QWidget();
         QVBoxLayout* layout = new QVBoxLayout();
         QLabel* image_label = new QLabel();
-        QPixmap icon = m_games_[gameCounter].icon.scaled(
+        QImage icon = m_games_[gameCounter].icon.scaled(
             QSize(icon_size, icon_size), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
         image_label->setFixedSize(icon.width(), icon.height());
-        image_label->setPixmap(icon);
+        image_label->setPixmap(QPixmap::fromImage(icon));
         QLabel* name_label = new QLabel(QString::fromStdString(m_games_[gameCounter].serial));
         name_label->setAlignment(Qt::AlignHCenter);
         layout->addWidget(image_label);
@@ -86,8 +87,7 @@ void GameGridFrame::PopulateGameGrid(QVector<GameInfo> m_games_search, bool from
                                        "color: #000000;"
                                        "border: 1px solid #000000;"
                                        "padding: 2px;"
-                                       "font-size: 12px; }")
-                                   .arg(tooltipText);
+                                       "font-size: 12px; }");
         widget->setStyleSheet(tooltipStyle);
         this->setCellWidget(row, column, widget);
 
@@ -134,10 +134,12 @@ void GameGridFrame::SetGridBackgroundImage(int row, int column) {
 }
 
 void GameGridFrame::RefreshGridBackgroundImage() {
-    QPixmap blurredPixmap = QPixmap::fromImage(backgroundImage);
-    QPalette palette;
-    palette.setBrush(QPalette::Base, QBrush(blurredPixmap.scaled(size(), Qt::IgnoreAspectRatio)));
-    QColor transparentColor = QColor(135, 206, 235, 40);
-    palette.setColor(QPalette::Highlight, transparentColor);
-    this->setPalette(palette);
+    if (!backgroundImage.isNull()) {
+        QPalette palette;
+        palette.setBrush(QPalette::Base,
+                         QBrush(backgroundImage.scaled(size(), Qt::IgnoreAspectRatio)));
+        QColor transparentColor = QColor(135, 206, 235, 40);
+        palette.setColor(QPalette::Highlight, transparentColor);
+        this->setPalette(palette);
+    }
 }
\ No newline at end of file
diff --git a/src/qt_gui/game_grid_frame.h b/src/qt_gui/game_grid_frame.h
index 751184ce..19ac531b 100644
--- a/src/qt_gui/game_grid_frame.h
+++ b/src/qt_gui/game_grid_frame.h
@@ -14,6 +14,7 @@
 #include <QVBoxLayout>
 #include <QWidget>
 #include <QtConcurrent/QtConcurrent>
+#include "common/config.h"
 #include "game_info.h"
 #include "game_list_utils.h"
 #include "gui_context_menus.h"
@@ -33,14 +34,16 @@ private:
     GameListUtils m_game_list_utils;
     GuiContextMenus m_gui_context_menus;
     std::shared_ptr<GameInfoClass> m_game_info;
-    std::shared_ptr<GuiSettings> m_gui_settings_;
     std::shared_ptr<QVector<GameInfo>> m_games_shared;
 
 public:
-    explicit GameGridFrame(std::shared_ptr<GameInfoClass> game_info_get,
-                           std::shared_ptr<GuiSettings> m_gui_settings, QWidget* parent = nullptr);
+    explicit GameGridFrame(std::shared_ptr<GameInfoClass> game_info_get, QWidget* parent = nullptr);
     void PopulateGameGrid(QVector<GameInfo> m_games, bool fromSearch);
 
+    bool cellClicked = false;
     int icon_size;
     int windowWidth;
+    int crtRow;
+    int crtColumn;
+    int columnCnt;
 };
diff --git a/src/qt_gui/game_info.cpp b/src/qt_gui/game_info.cpp
index f48ab327..39cdeb75 100644
--- a/src/qt_gui/game_info.cpp
+++ b/src/qt_gui/game_info.cpp
@@ -3,25 +3,43 @@
 
 #include <future>
 #include <thread>
+#include <QProgressDialog>
+#include <QtConcurrent/QtConcurrent>
 #include "game_info.h"
 
-void GameInfoClass::GetGameInfo() {
-    QString installDir = m_gui_settings->GetValue(gui::settings_install_dir).toString();
-    std::filesystem::path parent_folder(installDir.toStdString());
-    std::vector<std::string> filePaths;
-    for (const auto& dir : std::filesystem::directory_iterator(parent_folder)) {
-        if (dir.is_directory()) {
-            filePaths.push_back(dir.path().string());
+GameInfoClass::GameInfoClass() = default;
+GameInfoClass::~GameInfoClass() = default;
+
+void GameInfoClass::GetGameInfo(QWidget* parent) {
+    QString installDir = QString::fromStdString(Config::getGameInstallDir());
+    QStringList filePaths;
+    QDir parentFolder(installDir);
+    QFileInfoList fileList = parentFolder.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
+    for (const auto& fileInfo : fileList) {
+        if (fileInfo.isDir()) {
+            filePaths.append(fileInfo.absoluteFilePath());
         }
     }
-    std::vector<std::future<GameInfo>> futures;
+    m_games = QtConcurrent::mapped(filePaths, [&](const QString& path) {
+                  return readGameInfo(path.toStdString());
+              }).results();
 
-    for (const auto& filePath : filePaths) {
-        futures.emplace_back(std::async(std::launch::async, readGameInfo, filePath));
-    }
+    // Progress bar, please be patient :)
+    QProgressDialog dialog("Loading game list, please wait :3", "Cancel", 0, 0, parent);
+    dialog.setWindowTitle("Loading...");
 
-    for (auto& future : futures) {
-        m_games.push_back(future.get());
-    }
-    std::sort(m_games.begin(), m_games.end(), CompareStrings);
+    QFutureWatcher<void> futureWatcher;
+    GameListUtils game_util;
+    bool finished = false;
+    futureWatcher.setFuture(QtConcurrent::map(m_games, game_util.GetFolderSize));
+    connect(&futureWatcher, &QFutureWatcher<void>::finished, [&]() {
+        dialog.reset();
+        std::sort(m_games.begin(), m_games.end(), CompareStrings);
+    });
+    connect(&dialog, &QProgressDialog::canceled, &futureWatcher, &QFutureWatcher<void>::cancel);
+    dialog.setRange(0, m_games.size());
+    connect(&futureWatcher, &QFutureWatcher<void>::progressValueChanged, &dialog,
+            &QProgressDialog::setValue);
+
+    dialog.exec();
 }
\ No newline at end of file
diff --git a/src/qt_gui/game_info.h b/src/qt_gui/game_info.h
index 82477287..c137a5a6 100644
--- a/src/qt_gui/game_info.h
+++ b/src/qt_gui/game_info.h
@@ -3,34 +3,20 @@
 
 #pragma once
 
-#include <string>
 #include <QFuture>
+#include <QObject>
 #include <QPixmap>
 #include <QtConcurrent/QtConcurrent>
+#include "common/config.h"
 #include "core/file_format/psf.h"
 #include "game_list_utils.h"
-#include "gui_settings.h"
-
-struct GameInfo {
-    std::string path; // root path of game directory (normaly directory that contains eboot.bin)
-    std::string icon_path; // path of icon0.png
-    std::string pic_path;  // path of pic1.png
-    QPixmap icon;
-    std::string size;
-    // variables extracted from param.sfo
-    std::string name = "Unknown";
-    std::string serial = "Unknown";
-    std::string version = "Unknown";
-    std::string category = "Unknown";
-    std::string fw = "Unknown";
-};
-
-class GameInfoClass {
 
+class GameInfoClass : public QObject {
+    Q_OBJECT
 public:
-    void GetGameInfo();
-
-    std::shared_ptr<GuiSettings> m_gui_settings = std::make_shared<GuiSettings>();
+    GameInfoClass();
+    ~GameInfoClass();
+    void GetGameInfo(QWidget* parent = nullptr);
     QVector<GameInfo> m_games;
 
     static bool CompareStrings(GameInfo& a, GameInfo& b) {
@@ -39,19 +25,17 @@ public:
 
     static GameInfo readGameInfo(const std::string& filePath) {
         GameInfo game;
-        GameListUtils game_util;
-        game.size = game_util.GetFolderSize(QDir(QString::fromStdString(filePath))).toStdString();
         game.path = filePath;
 
         PSF psf;
-        if (psf.open(game.path + "/sce_sys/param.sfo")) {
-            QString iconpath(QString::fromStdString(game.path) + "/sce_sys/icon0.png");
-            QString picpath(QString::fromStdString(game.path) + "/sce_sys/pic1.png");
-            game.icon_path = iconpath.toStdString();
-            game.icon = QPixmap(iconpath);
-            game.pic_path = picpath.toStdString();
+        if (psf.open(game.path + "/sce_sys/param.sfo", {})) {
+            game.icon_path = game.path + "/sce_sys/icon0.png";
+            QString iconpath = QString::fromStdString(game.icon_path);
+            game.icon = QImage(iconpath);
+            game.pic_path = game.path + "/sce_sys/pic1.png";
             game.name = psf.GetString("TITLE");
             game.serial = psf.GetString("TITLE_ID");
+            game.region = GameListUtils::GetRegion(psf.GetString("CONTENT_ID").at(0)).toStdString();
             u32 fw_int = psf.GetInteger("SYSTEM_VER");
             QString fw = QString::number(fw_int, 16);
             QString fw_ = fw.length() > 7 ? QString::number(fw_int, 16).left(3).insert(2, '.')
diff --git a/src/qt_gui/game_install_dialog.cpp b/src/qt_gui/game_install_dialog.cpp
index 3ae78da9..ab4fc273 100644
--- a/src/qt_gui/game_install_dialog.cpp
+++ b/src/qt_gui/game_install_dialog.cpp
@@ -13,10 +13,8 @@
 #include <QMessageBox>
 #include <QPushButton>
 #include <QVBoxLayout>
-#include "gui_settings.h"
 
-GameInstallDialog::GameInstallDialog(std::shared_ptr<GuiSettings> gui_settings)
-    : m_gamesDirectory(nullptr), m_gui_settings(std::move(gui_settings)) {
+GameInstallDialog::GameInstallDialog() : m_gamesDirectory(nullptr) {
     auto layout = new QVBoxLayout(this);
 
     layout->addWidget(SetupGamesDirectory());
@@ -43,7 +41,7 @@ QWidget* GameInstallDialog::SetupGamesDirectory() {
 
     // Input.
     m_gamesDirectory = new QLineEdit();
-    m_gamesDirectory->setText(m_gui_settings->GetValue(gui::settings_install_dir).toString());
+    m_gamesDirectory->setText(QString::fromStdString(Config::getGameInstallDir()));
     m_gamesDirectory->setMinimumWidth(400);
 
     layout->addWidget(m_gamesDirectory);
@@ -78,7 +76,8 @@ void GameInstallDialog::Save() {
         return;
     }
 
-    m_gui_settings->SetValue(gui::settings_install_dir, QDir::toNativeSeparators(gamesDirectory));
-
+    Config::setGameInstallDir(gamesDirectory.toStdString());
+    const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
+    Config::save(config_dir / "config.toml");
     accept();
 }
diff --git a/src/qt_gui/game_install_dialog.h b/src/qt_gui/game_install_dialog.h
index b75aaaf6..abd447d9 100644
--- a/src/qt_gui/game_install_dialog.h
+++ b/src/qt_gui/game_install_dialog.h
@@ -4,13 +4,14 @@
 #pragma once
 
 #include <QDialog>
-#include "gui_settings.h"
+#include "common/config.h"
+#include "common/path_util.h"
 
 class QLineEdit;
 
 class GameInstallDialog final : public QDialog {
 public:
-    GameInstallDialog(std::shared_ptr<GuiSettings> gui_settings);
+    GameInstallDialog();
     ~GameInstallDialog();
 
 private slots:
@@ -23,5 +24,4 @@ private:
 
 private:
     QLineEdit* m_gamesDirectory;
-    std::shared_ptr<GuiSettings> m_gui_settings;
 };
\ No newline at end of file
diff --git a/src/qt_gui/game_list_frame.cpp b/src/qt_gui/game_list_frame.cpp
index 47e03789..f0ba113e 100644
--- a/src/qt_gui/game_list_frame.cpp
+++ b/src/qt_gui/game_list_frame.cpp
@@ -1,14 +1,12 @@
 // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 #include "game_list_frame.h"
 
-GameListFrame::GameListFrame(std::shared_ptr<GameInfoClass> game_info_get,
-                             std::shared_ptr<GuiSettings> m_gui_settings, QWidget* parent)
-    : QTableWidget(parent) {
-    m_game_info = game_info_get;
-    icon_size = m_gui_settings->GetValue(gui::m_icon_size).toInt();
-
+GameListFrame::GameListFrame(std::shared_ptr<GameInfoClass> game_info_get, QWidget* parent)
+    : QTableWidget(parent), m_game_info(game_info_get) {
+    icon_size = Config::getIconSize();
     this->setShowGrid(false);
     this->setEditTriggers(QAbstractItemView::NoEditTriggers);
     this->setSelectionBehavior(QAbstractItemView::SelectRows);
@@ -25,17 +23,19 @@ GameListFrame::GameListFrame(std::shared_ptr<GameInfoClass> game_info_get,
     this->horizontalHeader()->setSortIndicatorShown(true);
     this->horizontalHeader()->setStretchLastSection(true);
     this->setContextMenuPolicy(Qt::CustomContextMenu);
-    this->setColumnCount(8);
+    this->setColumnCount(9);
     this->setColumnWidth(1, 250);
     this->setColumnWidth(2, 110);
     this->setColumnWidth(3, 80);
     this->setColumnWidth(4, 90);
     this->setColumnWidth(5, 80);
     this->setColumnWidth(6, 80);
+    this->setColumnWidth(7, 80);
     QStringList headers;
     headers << "Icon"
             << "Name"
             << "Serial"
+            << "Region"
             << "Firmware"
             << "Size"
             << "Version"
@@ -81,13 +81,14 @@ void GameListFrame::PopulateGameList() {
     ResizeIcons(icon_size);
 
     for (int i = 0; i < m_game_info->m_games.size(); i++) {
-        SetTableItem(this, i, 1, QString::fromStdString(m_game_info->m_games[i].name));
-        SetTableItem(this, i, 2, QString::fromStdString(m_game_info->m_games[i].serial));
-        SetTableItem(this, i, 3, QString::fromStdString(m_game_info->m_games[i].fw));
-        SetTableItem(this, i, 4, QString::fromStdString(m_game_info->m_games[i].size));
-        SetTableItem(this, i, 5, QString::fromStdString(m_game_info->m_games[i].version));
-        SetTableItem(this, i, 6, QString::fromStdString(m_game_info->m_games[i].category));
-        SetTableItem(this, i, 7, QString::fromStdString(m_game_info->m_games[i].path));
+        SetTableItem(i, 1, QString::fromStdString(m_game_info->m_games[i].name));
+        SetTableItem(i, 2, QString::fromStdString(m_game_info->m_games[i].serial));
+        SetRegionFlag(i, 3, QString::fromStdString(m_game_info->m_games[i].region));
+        SetTableItem(i, 4, QString::fromStdString(m_game_info->m_games[i].fw));
+        SetTableItem(i, 5, QString::fromStdString(m_game_info->m_games[i].size));
+        SetTableItem(i, 6, QString::fromStdString(m_game_info->m_games[i].version));
+        SetTableItem(i, 7, QString::fromStdString(m_game_info->m_games[i].category));
+        SetTableItem(i, 8, QString::fromStdString(m_game_info->m_games[i].path));
     }
 }
 
@@ -119,12 +120,14 @@ void GameListFrame::SetListBackgroundImage(QTableWidgetItem* item) {
 }
 
 void GameListFrame::RefreshListBackgroundImage() {
-    QPixmap blurredPixmap = QPixmap::fromImage(backgroundImage);
-    QPalette palette;
-    palette.setBrush(QPalette::Base, QBrush(blurredPixmap.scaled(size(), Qt::IgnoreAspectRatio)));
-    QColor transparentColor = QColor(135, 206, 235, 40);
-    palette.setColor(QPalette::Highlight, transparentColor);
-    this->setPalette(palette);
+    if (!backgroundImage.isNull()) {
+        QPalette palette;
+        palette.setBrush(QPalette::Base,
+                         QBrush(backgroundImage.scaled(size(), Qt::IgnoreAspectRatio)));
+        QColor transparentColor = QColor(135, 206, 235, 40);
+        palette.setColor(QPalette::Highlight, transparentColor);
+        this->setPalette(palette);
+    }
 }
 
 void GameListFrame::SortNameAscending(int columnIndex) {
@@ -142,22 +145,66 @@ void GameListFrame::SortNameDescending(int columnIndex) {
 }
 
 void GameListFrame::ResizeIcons(int iconSize) {
-    QList<int> indices;
-    for (int i = 0; i < m_game_info->m_games.size(); i++) {
-        indices.append(i);
+    for (int index = 0; auto& game : m_game_info->m_games) {
+        QImage scaledPixmap = game.icon.scaled(QSize(iconSize, iconSize), Qt::KeepAspectRatio,
+                                               Qt::SmoothTransformation);
+        QTableWidgetItem* iconItem = new QTableWidgetItem();
+        this->verticalHeader()->resizeSection(index, scaledPixmap.height());
+        this->horizontalHeader()->resizeSection(0, scaledPixmap.width());
+        iconItem->setData(Qt::DecorationRole, scaledPixmap);
+        this->setItem(index, 0, iconItem);
+        index++;
     }
-    std::future<void> future = std::async(std::launch::async, [=, this]() {
-        for (int index : indices) {
-            QPixmap scaledPixmap = m_game_info->m_games[index].icon.scaled(
-                QSize(iconSize, iconSize), Qt::KeepAspectRatio, Qt::SmoothTransformation);
-
-            QTableWidgetItem* iconItem = new QTableWidgetItem();
-            this->verticalHeader()->resizeSection(index, scaledPixmap.height());
-            this->horizontalHeader()->resizeSection(0, scaledPixmap.width());
-            iconItem->setData(Qt::DecorationRole, scaledPixmap);
-            this->setItem(index, 0, iconItem);
-        }
-    });
-    future.wait();
     this->horizontalHeader()->setSectionResizeMode(7, QHeaderView::ResizeToContents);
+}
+
+void GameListFrame::SetTableItem(int row, int column, QString itemStr) {
+    QTableWidgetItem* item = new QTableWidgetItem();
+    QWidget* widget = new QWidget(this);
+    QVBoxLayout* layout = new QVBoxLayout(widget);
+    QLabel* label = new QLabel(itemStr, widget);
+
+    label->setStyleSheet("color: white; font-size: 15px; font-weight: bold;");
+
+    // Create shadow effect
+    QGraphicsDropShadowEffect* shadowEffect = new QGraphicsDropShadowEffect();
+    shadowEffect->setBlurRadius(5);               // Set the blur radius of the shadow
+    shadowEffect->setColor(QColor(0, 0, 0, 160)); // Set the color and opacity of the shadow
+    shadowEffect->setOffset(2, 2);                // Set the offset of the shadow
+
+    label->setGraphicsEffect(shadowEffect); // Apply shadow effect to the QLabel
+
+    layout->addWidget(label);
+    if (column != 8 && column != 1)
+        layout->setAlignment(Qt::AlignCenter);
+    widget->setLayout(layout);
+    this->setItem(row, column, item);
+    this->setCellWidget(row, column, widget);
+}
+
+void GameListFrame::SetRegionFlag(int row, int column, QString itemStr) {
+    QTableWidgetItem* item = new QTableWidgetItem();
+    QImage scaledPixmap;
+    if (itemStr == "Japan") {
+        scaledPixmap = QImage(":/images/flag_jp.png");
+    } else if (itemStr == "Europe") {
+        scaledPixmap = QImage(":/images/flag_eu.png");
+    } else if (itemStr == "USA") {
+        scaledPixmap = QImage(":/images/flag_us.png");
+    } else if (itemStr == "Asia") {
+        scaledPixmap = QImage(":/images/flag_china.png");
+    } else if (itemStr == "World") {
+        scaledPixmap = QImage(":/images/flag_world.png");
+    } else {
+        scaledPixmap = QImage(":/images/flag_unk.png");
+    }
+    QWidget* widget = new QWidget(this);
+    QVBoxLayout* layout = new QVBoxLayout(widget);
+    QLabel* label = new QLabel(widget);
+    label->setPixmap(QPixmap::fromImage(scaledPixmap));
+    layout->setAlignment(Qt::AlignCenter);
+    layout->addWidget(label);
+    widget->setLayout(layout);
+    this->setItem(row, column, item);
+    this->setCellWidget(row, column, widget);
 }
\ No newline at end of file
diff --git a/src/qt_gui/game_list_frame.h b/src/qt_gui/game_list_frame.h
index 24ef8081..e9f75afd 100644
--- a/src/qt_gui/game_list_frame.h
+++ b/src/qt_gui/game_list_frame.h
@@ -23,8 +23,7 @@
 class GameListFrame : public QTableWidget {
     Q_OBJECT
 public:
-    explicit GameListFrame(std::shared_ptr<GameInfoClass> game_info_get,
-                           std::shared_ptr<GuiSettings> m_gui_settings, QWidget* parent = nullptr);
+    explicit GameListFrame(std::shared_ptr<GameInfoClass> game_info_get, QWidget* parent = nullptr);
 Q_SIGNALS:
     void GameListFrameClosed();
 
@@ -35,6 +34,8 @@ public Q_SLOTS:
     void SortNameDescending(int columnIndex);
 
 private:
+    void SetTableItem(int row, int column, QString itemStr);
+    void SetRegionFlag(int row, int column, QString itemStr);
     QList<QAction*> m_columnActs;
     GameInfoClass* game_inf_get = nullptr;
     bool ListSortedAsc = true;
@@ -50,30 +51,6 @@ public:
 
     int icon_size;
 
-    void SetTableItem(QTableWidget* game_list, int row, int column, QString itemStr) {
-        QWidget* widget = new QWidget();
-        QVBoxLayout* layout = new QVBoxLayout();
-        QLabel* label = new QLabel(itemStr);
-        QTableWidgetItem* item = new QTableWidgetItem();
-
-        label->setStyleSheet("color: white; font-size: 15px; font-weight: bold;");
-
-        // Create shadow effect
-        QGraphicsDropShadowEffect* shadowEffect = new QGraphicsDropShadowEffect();
-        shadowEffect->setBlurRadius(5);               // Set the blur radius of the shadow
-        shadowEffect->setColor(QColor(0, 0, 0, 160)); // Set the color and opacity of the shadow
-        shadowEffect->setOffset(2, 2);                // Set the offset of the shadow
-
-        label->setGraphicsEffect(shadowEffect); // Apply shadow effect to the QLabel
-
-        layout->addWidget(label);
-        if (column != 7 && column != 1)
-            layout->setAlignment(Qt::AlignCenter);
-        widget->setLayout(layout);
-        game_list->setItem(row, column, item);
-        game_list->setCellWidget(row, column, widget);
-    }
-
     static bool CompareStringsAscending(GameInfo a, GameInfo b, int columnIndex) {
         if (columnIndex == 1) {
             return a.name < b.name;
diff --git a/src/qt_gui/game_list_utils.h b/src/qt_gui/game_list_utils.h
index 2561f2d7..2e25f122 100644
--- a/src/qt_gui/game_list_utils.h
+++ b/src/qt_gui/game_list_utils.h
@@ -8,6 +8,21 @@
 #include <QImage>
 #include <QString>
 
+struct GameInfo {
+    std::string path; // root path of game directory (normaly directory that contains eboot.bin)
+    std::string icon_path; // path of icon0.png
+    std::string pic_path;  // path of pic1.png
+    QImage icon;
+    std::string size;
+    // variables extracted from param.sfo
+    std::string name = "Unknown";
+    std::string serial = "Unknown";
+    std::string version = "Unknown";
+    std::string region = "Unknown";
+    std::string category = "Unknown";
+    std::string fw = "Unknown";
+};
+
 class GameListUtils {
 public:
     static QString FormatSize(qint64 size) {
@@ -33,25 +48,49 @@ public:
         return sizeString + " " + suffixes[suffixIndex];
     }
 
-    static QString GetFolderSize(const QDir& dir) {
-
+    static void GetFolderSize(GameInfo& game) {
+        QDir dir(QString::fromStdString(game.path));
         QDirIterator it(dir.absolutePath(), QDirIterator::Subdirectories);
         qint64 total = 0;
-
         while (it.hasNext()) {
-            // check if entry is file
-            if (it.fileInfo().isFile()) {
-                total += it.fileInfo().size();
-            }
-            it.next(); // this is heavy.
-        }
-
-        // if there is a file left "at the end" get it's size
-        if (it.fileInfo().isFile()) {
+            it.next();
             total += it.fileInfo().size();
         }
+        game.size = FormatSize(total).toStdString();
+    }
 
-        return FormatSize(total);
+    static QString GetRegion(char region) {
+        switch (region) {
+        case 'U':
+            return "USA";
+        case 'E':
+            return "Europe";
+        case 'J':
+            return "Japan";
+        case 'H':
+            return "Asia";
+        case 'I':
+            return "World";
+        default:
+            return "Unknown";
+        }
+    }
+
+    static QString GetAppType(int type) {
+        switch (type) {
+        case 0:
+            return "Not Specified";
+        case 1:
+            return "FULL APP";
+        case 2:
+            return "UPGRADABLE";
+        case 3:
+            return "DEMO";
+        case 4:
+            return "FREEMIUM";
+        default:
+            return "Unknown";
+        }
     }
 
     QImage BlurImage(const QImage& image, const QRect& rect, int radius) {
diff --git a/src/qt_gui/gui_context_menus.h b/src/qt_gui/gui_context_menus.h
index 06d4334a..c7349500 100644
--- a/src/qt_gui/gui_context_menus.h
+++ b/src/qt_gui/gui_context_menus.h
@@ -10,8 +10,10 @@
 #include <QTreeWidget>
 #include <QTreeWidgetItem>
 #include "game_info.h"
+#include "trophy_viewer.h"
 
-class GuiContextMenus {
+class GuiContextMenus : public QObject {
+    Q_OBJECT
 public:
     void RequestGameMenu(const QPoint& pos, QVector<GameInfo> m_games, QTableWidget* widget,
                          bool isList) {
@@ -27,9 +29,11 @@ public:
         QMenu menu(widget);
         QAction openFolder("Open Game Folder", widget);
         QAction openSfoViewer("SFO Viewer", widget);
+        QAction openTrophyViewer("Trophy Viewer", widget);
 
         menu.addAction(&openFolder);
         menu.addAction(&openSfoViewer);
+        menu.addAction(&openTrophyViewer);
         // Show menu.
         auto selected = menu.exec(global_pos);
         if (!selected) {
@@ -43,9 +47,13 @@ public:
 
         if (selected == &openSfoViewer) {
             PSF psf;
-            if (psf.open(m_games[itemID].path + "/sce_sys/param.sfo")) {
+            if (psf.open(m_games[itemID].path + "/sce_sys/param.sfo", {})) {
                 int rows = psf.map_strings.size() + psf.map_integers.size();
                 QTableWidget* tableWidget = new QTableWidget(rows, 2);
+                tableWidget->setAttribute(Qt::WA_DeleteOnClose);
+                connect(widget->parent(), &QWidget::destroyed, tableWidget,
+                        [widget, tableWidget]() { tableWidget->deleteLater(); });
+
                 tableWidget->verticalHeader()->setVisible(false); // Hide vertical header
                 int row = 0;
 
@@ -88,6 +96,15 @@ public:
                 tableWidget->show();
             }
         }
+
+        if (selected == &openTrophyViewer) {
+            QString trophyPath = QString::fromStdString(m_games[itemID].serial);
+            QString gameTrpPath = QString::fromStdString(m_games[itemID].path);
+            TrophyViewer* trophyViewer = new TrophyViewer(trophyPath, gameTrpPath);
+            trophyViewer->show();
+            connect(widget->parent(), &QWidget::destroyed, trophyViewer,
+                    [widget, trophyViewer]() { trophyViewer->deleteLater(); });
+        }
     }
 
     int GetRowIndex(QTreeWidget* treeWidget, QTreeWidgetItem* item) {
@@ -112,9 +129,9 @@ public:
         return -1;
     }
 
-    void RequestGameMenuPKGViewer(const QPoint& pos, QStringList m_pkg_app_list,
-                                  QTreeWidget* treeWidget,
-                                  std::function<void(std::string, int, int)> InstallDragDropPkg) {
+    void RequestGameMenuPKGViewer(
+        const QPoint& pos, QStringList m_pkg_app_list, QTreeWidget* treeWidget,
+        std::function<void(std::filesystem::path, int, int)> InstallDragDropPkg) {
         QPoint global_pos = treeWidget->viewport()->mapToGlobal(pos); // context menu position
         QTreeWidgetItem* currentItem = treeWidget->currentItem();     // current clicked item
         int itemIndex = GetRowIndex(treeWidget, currentItem);         // row
@@ -131,15 +148,11 @@ public:
 
         if (selected == &installPackage) {
             QStringList pkg_app_ = m_pkg_app_list[itemIndex].split(";;");
-            std::string pkg_to_install = pkg_app_[9].toStdString();
-            InstallDragDropPkg(pkg_to_install, 1, 1);
-
-            QFile file("log.txt");
-            if (!file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
-                return;
-
-            QTextStream stream(&file);
-            stream << QString::fromStdString(pkg_to_install) << Qt::endl;
+            std::filesystem::path path(pkg_app_[9].toStdString());
+#ifdef _WIN32
+            path = std::filesystem::path(pkg_app_[9].toStdWString());
+#endif
+            InstallDragDropPkg(path, 1, 1);
         }
     }
 };
diff --git a/src/qt_gui/gui_save.h b/src/qt_gui/gui_save.h
deleted file mode 100644
index e2434f75..00000000
--- a/src/qt_gui/gui_save.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <QString>
-#include <QVariant>
-
-struct GuiSave {
-    QString key;
-    QString name;
-    QVariant def;
-
-    GuiSave() {
-        key = "";
-        name = "";
-        def = QVariant();
-    }
-
-    GuiSave(const QString& k, const QString& n, const QVariant& d) {
-        key = k;
-        name = n;
-        def = d;
-    }
-
-    bool operator==(const GuiSave& rhs) const noexcept {
-        return key == rhs.key && name == rhs.name && def == rhs.def;
-    }
-};
diff --git a/src/qt_gui/gui_settings.cpp b/src/qt_gui/gui_settings.cpp
deleted file mode 100644
index d2fd3b3b..00000000
--- a/src/qt_gui/gui_settings.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "gui_settings.h"
-
-GuiSettings::GuiSettings(QObject* parent) {
-    m_settings.reset(new QSettings("shadps4qt.ini", QSettings::Format::IniFormat,
-                                   parent)); // TODO make the path configurable
-}
-
-void GuiSettings::SetGamelistColVisibility(int col, bool val) const {
-    SetValue(GetGuiSaveForColumn(col), val);
-}
-
-bool GuiSettings::GetGamelistColVisibility(int col) const {
-    return GetValue(GetGuiSaveForColumn(col)).toBool();
-}
-
-GuiSave GuiSettings::GetGuiSaveForColumn(int col) {
-    return GuiSave{gui::game_list,
-                   "visibility_" +
-                       gui::get_game_list_column_name(static_cast<gui::game_list_columns>(col)),
-                   true};
-}
\ No newline at end of file
diff --git a/src/qt_gui/gui_settings.h b/src/qt_gui/gui_settings.h
deleted file mode 100644
index d56fa54a..00000000
--- a/src/qt_gui/gui_settings.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <QColor>
-
-#include "settings.h"
-
-namespace gui {
-enum custom_roles {
-    game_role = Qt::UserRole + 1337,
-};
-
-enum game_list_columns {
-    column_icon,
-    column_name,
-    column_serial,
-    column_firmware,
-    column_size,
-    column_version,
-    column_category,
-    column_path,
-    column_count
-};
-
-inline QString get_game_list_column_name(game_list_columns col) {
-    switch (col) {
-    case column_icon:
-        return "column_icon";
-    case column_name:
-        return "column_name";
-    case column_serial:
-        return "column_serial";
-    case column_firmware:
-        return "column_firmware";
-    case column_size:
-        return "column_size";
-    case column_version:
-        return "column_version";
-    case column_category:
-        return "column_category";
-    case column_path:
-        return "column_path";
-    case column_count:
-        return "";
-    }
-
-    throw std::runtime_error("get_game_list_column_name: Invalid column");
-}
-
-const QString main_window = "main_window";
-const QString game_list = "GameList";
-const QString settings = "Settings";
-const QString themes = "Themes";
-
-const GuiSave main_window_gamelist_visible = GuiSave(main_window, "gamelistVisible", true);
-const GuiSave main_window_geometry = GuiSave(main_window, "geometry", QByteArray());
-const GuiSave main_window_windowState = GuiSave(main_window, "windowState", QByteArray());
-const GuiSave main_window_mwState = GuiSave(main_window, "mwState", QByteArray());
-
-const GuiSave game_list_state = GuiSave(game_list, "state", QByteArray());
-const GuiSave game_list_listMode = GuiSave(game_list, "listMode", true);
-const GuiSave settings_install_dir = GuiSave(settings, "installDirectory", "");
-const GuiSave mw_themes = GuiSave(themes, "Themes", 0);
-const GuiSave m_icon_size = GuiSave(game_list, "iconSize", 36);
-const GuiSave m_icon_size_grid = GuiSave(game_list, "iconSizeGrid", 69);
-const GuiSave m_slide_pos = GuiSave(game_list, "sliderPos", 0);
-const GuiSave m_slide_pos_grid = GuiSave(game_list, "sliderPosGrid", 0);
-const GuiSave m_table_mode = GuiSave(main_window, "tableMode", 0);
-const GuiSave m_window_size = GuiSave(main_window, "windowSize", QSize(1280, 720));
-const GuiSave m_pkg_viewer = GuiSave("pkg_viewer", "pkgDir", QStringList());
-const GuiSave m_pkg_viewer_pkg_list = GuiSave("pkg_viewer", "pkgList", QStringList());
-
-} // namespace gui
-
-class GuiSettings : public Settings {
-    Q_OBJECT
-
-public:
-    explicit GuiSettings(QObject* parent = nullptr);
-
-    bool GetGamelistColVisibility(int col) const;
-
-public Q_SLOTS:
-    void SetGamelistColVisibility(int col, bool val) const;
-    static GuiSave GetGuiSaveForColumn(int col);
-};
diff --git a/src/qt_gui/main.cpp b/src/qt_gui/main.cpp
index 5edefc01..3fc1452c 100644
--- a/src/qt_gui/main.cpp
+++ b/src/qt_gui/main.cpp
@@ -3,21 +3,33 @@
 
 #include <QtWidgets/QApplication>
 
+#include "common/config.h"
+#include "core/file_sys/fs.h"
 #include "qt_gui/game_install_dialog.h"
-#include "qt_gui/gui_settings.h"
 #include "qt_gui/main_window.h"
-#include "src/sdl_window.h"
 
-Frontend::WindowSDL* g_window;
+void customMessageHandler(QtMsgType, const QMessageLogContext&, const QString&) {}
 
 int main(int argc, char* argv[]) {
     QApplication a(argc, argv);
-    auto m_gui_settings = std::make_shared<GuiSettings>();
-    if (m_gui_settings->GetValue(gui::settings_install_dir) == "") {
-        GameInstallDialog dlg(m_gui_settings);
+    const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
+    Config::load(config_dir / "config.toml");
+    QString gameDataPath = qApp->applicationDirPath() + "/game_data/";
+    std::string stdStr = gameDataPath.toStdString();
+    std::filesystem::path path(stdStr);
+#ifdef _WIN64
+    std::wstring wstdStr = gameDataPath.toStdWString();
+    path = std::filesystem::path(wstdStr);
+#endif
+    std::filesystem::create_directory(path);
+
+    if (Config::getGameInstallDir() == "") {
+        GameInstallDialog dlg;
         dlg.exec();
     }
-    MainWindow* m_main_window = new MainWindow(m_gui_settings, nullptr);
+    qInstallMessageHandler(customMessageHandler); // ignore qt logs.
+
+    MainWindow* m_main_window = new MainWindow(nullptr);
     m_main_window->Init();
 
     return a.exec();
diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp
index 6f8dc148..ee7ab650 100644
--- a/src/qt_gui/main_window.cpp
+++ b/src/qt_gui/main_window.cpp
@@ -13,38 +13,41 @@
 #include "core/file_format/pkg.h"
 #include "core/loader.h"
 #include "game_install_dialog.h"
-#include "gui_settings.h"
 #include "main_window.h"
 
-MainWindow::MainWindow(std::shared_ptr<GuiSettings> gui_settings, QWidget* parent)
-    : QMainWindow(parent), ui(new Ui::MainWindow), m_gui_settings(std::move(gui_settings)) {
+MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
     ui->setupUi(this);
     setAttribute(Qt::WA_DeleteOnClose);
 }
 
 MainWindow::~MainWindow() {
     SaveWindowState();
+    const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
+    Config::save(config_dir / "config.toml");
 }
 
 bool MainWindow::Init() {
     auto start = std::chrono::steady_clock::now();
+    // setup ui
     AddUiWidgets();
     CreateActions();
+    CreateRecentGameActions();
+    ConfigureGuiFromSettings();
     CreateDockWindows();
     CreateConnects();
     SetLastUsedTheme();
     SetLastIconSizeBullet();
-    ConfigureGuiFromSettings();
-    LoadGameLists();
-
+    // show ui
     setMinimumSize(350, minimumSizeHint().height());
     setWindowTitle(QString::fromStdString("shadPS4 v" + std::string(Common::VERSION)));
-    show();
+    this->show();
+    // load game list
+    LoadGameLists();
 
     auto end = std::chrono::steady_clock::now();
     auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
-    statusBar = new QStatusBar(this);
-    m_main_window->setStatusBar(statusBar);
+    statusBar.reset(new QStatusBar);
+    this->setStatusBar(statusBar.data());
     // Update status bar
     int numGames = m_game_info->m_games.size();
     QString statusMessage = "Games: " + QString::number(numGames) + " (" +
@@ -93,45 +96,59 @@ void MainWindow::AddUiWidgets() {
 }
 
 void MainWindow::CreateDockWindows() {
-    m_main_window = new QMainWindow();
-    m_main_window->setContextMenuPolicy(Qt::PreventContextMenu);
+    // place holder widget is needed for good health they say :)
+    QWidget* phCentralWidget = new QWidget(this);
+    setCentralWidget(phCentralWidget);
 
-    // resize window to last W and H
-    QSize window_size = m_gui_settings->GetValue(gui::m_window_size).toSize();
-    m_main_window->resize(window_size.width(), window_size.height());
-
-    // Add the game table.
-    m_dock_widget = new QDockWidget("Game List", m_main_window);
-    m_game_list_frame = new GameListFrame(m_game_info, m_gui_settings, m_main_window);
+    m_dock_widget.reset(new QDockWidget("Game List", this));
+    m_game_list_frame.reset(new GameListFrame(m_game_info, this));
     m_game_list_frame->setObjectName("gamelist");
-    m_game_grid_frame = new GameGridFrame(m_game_info, m_gui_settings, m_main_window);
+    m_game_grid_frame.reset(new GameGridFrame(m_game_info, this));
     m_game_grid_frame->setObjectName("gamegridlist");
+    m_elf_viewer.reset(new ElfViewer(this));
+    m_elf_viewer->setObjectName("elflist");
 
-    int table_mode = m_gui_settings->GetValue(gui::m_table_mode).toInt();
+    int table_mode = Config::getTableMode();
     int slider_pos = 0;
     if (table_mode == 0) { // List
         m_game_grid_frame->hide();
-        m_dock_widget->setWidget(m_game_list_frame);
-        slider_pos = m_gui_settings->GetValue(gui::m_slide_pos).toInt();
+        m_elf_viewer->hide();
+        m_game_list_frame->show();
+        m_dock_widget->setWidget(m_game_list_frame.data());
+        slider_pos = Config::getSliderPositon();
         ui->sizeSlider->setSliderPosition(slider_pos); // set slider pos at start;
         isTableList = true;
-    } else { // Grid
+    } else if (table_mode == 1) { // Grid
         m_game_list_frame->hide();
-        m_dock_widget->setWidget(m_game_grid_frame);
-        slider_pos = m_gui_settings->GetValue(gui::m_slide_pos_grid).toInt();
+        m_elf_viewer->hide();
+        m_game_grid_frame->show();
+        m_dock_widget->setWidget(m_game_grid_frame.data());
+        slider_pos = Config::getSliderPositonGrid();
         ui->sizeSlider->setSliderPosition(slider_pos); // set slider pos at start;
         isTableList = false;
+    } else {
+        m_game_list_frame->hide();
+        m_game_grid_frame->hide();
+        m_elf_viewer->show();
+        m_dock_widget->setWidget(m_elf_viewer.data());
+        isTableList = false;
     }
 
-    m_main_window->addDockWidget(Qt::LeftDockWidgetArea, m_dock_widget);
-    m_main_window->setDockNestingEnabled(true);
+    m_dock_widget->setAllowedAreas(Qt::AllDockWidgetAreas);
+    m_dock_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+    m_dock_widget->resize(this->width(), this->height());
+    addDockWidget(Qt::LeftDockWidgetArea, m_dock_widget.data());
+    this->setDockNestingEnabled(true);
 
-    setCentralWidget(m_main_window);
+    // handle resize like this for now, we deal with it when we add more docks
+    connect(this, &MainWindow::WindowResized, this, [&]() {
+        this->resizeDocks({m_dock_widget.data()}, {this->width()}, Qt::Orientation::Horizontal);
+    });
 }
 
 void MainWindow::LoadGameLists() {
     // Get game info from game folders.
-    m_game_info->GetGameInfo();
+    m_game_info->GetGameInfo(this);
     if (isTableList) {
         m_game_list_frame->PopulateGameList();
     } else {
@@ -141,111 +158,150 @@ void MainWindow::LoadGameLists() {
 
 void MainWindow::CreateConnects() {
     connect(this, &MainWindow::WindowResized, this, &MainWindow::HandleResize);
-
     connect(ui->mw_searchbar, &QLineEdit::textChanged, this, &MainWindow::SearchGameTable);
-
     connect(ui->exitAct, &QAction::triggered, this, &QWidget::close);
-
     connect(ui->refreshGameListAct, &QAction::triggered, this, &MainWindow::RefreshGameTable);
+    connect(this, &MainWindow::ExtractionFinished, this, &MainWindow::RefreshGameTable);
 
     connect(ui->sizeSlider, &QSlider::valueChanged, this, [this](int value) {
         if (isTableList) {
             m_game_list_frame->icon_size =
                 36 + value; // 36 is the minimum icon size to use due to text disappearing.
             m_game_list_frame->ResizeIcons(36 + value);
-            m_gui_settings->SetValue(gui::m_icon_size, 36 + value);
-            m_gui_settings->SetValue(gui::m_slide_pos, value);
+            Config::setIconSize(36 + value);
+            Config::setSliderPositon(value);
         } else {
             m_game_grid_frame->icon_size = 69 + value;
             m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false);
-            m_gui_settings->SetValue(gui::m_icon_size_grid, 69 + value);
-            m_gui_settings->SetValue(gui::m_slide_pos_grid, value);
+            Config::setIconSizeGrid(69 + value);
+            Config::setSliderPositonGrid(value);
         }
     });
 
-    connect(ui->setIconSizeTinyAct, &QAction::triggered, this, [this](int value) {
+    connect(ui->playButton, &QPushButton::clicked, this, [this]() {
+        QString gamePath = "";
+        int table_mode = Config::getTableMode();
+        if (table_mode == 0) {
+            if (m_game_list_frame->currentItem()) {
+                int itemID = m_game_list_frame->currentItem()->row();
+                gamePath = QString::fromStdString(m_game_info->m_games[itemID].path + "/eboot.bin");
+            }
+        } else if (table_mode == 1) {
+            if (m_game_grid_frame->cellClicked) {
+                int itemID = (m_game_grid_frame->crtRow * m_game_grid_frame->columnCnt) +
+                             m_game_grid_frame->crtColumn;
+                gamePath = QString::fromStdString(m_game_info->m_games[itemID].path + "/eboot.bin");
+            }
+        } else {
+            if (m_elf_viewer->currentItem()) {
+                int itemID = m_elf_viewer->currentItem()->row();
+                gamePath = QString::fromStdString(m_elf_viewer->m_elf_list[itemID].toStdString());
+            }
+        }
+        if (gamePath != "") {
+            AddRecentFiles(gamePath);
+            Core::Emulator emulator;
+            emulator.Run(gamePath.toUtf8().constData());
+        }
+    });
+
+    connect(ui->setIconSizeTinyAct, &QAction::triggered, this, [this]() {
         if (isTableList) {
             m_game_list_frame->icon_size =
                 36; // 36 is the minimum icon size to use due to text disappearing.
-            m_gui_settings->SetValue(gui::m_icon_size, 36);
             ui->sizeSlider->setValue(0); // icone_size - 36
-            m_gui_settings->SetValue(gui::m_slide_pos, 0);
+            Config::setIconSize(36);
+            Config::setSliderPositon(0);
         } else {
-            m_gui_settings->SetValue(gui::m_icon_size_grid, 69); // nice :3
-            ui->sizeSlider->setValue(0);                         // icone_size - 36
-            m_gui_settings->SetValue(gui::m_slide_pos_grid, 0);
+            ui->sizeSlider->setValue(0); // icone_size - 36
+            Config::setIconSizeGrid(69);
+            Config::setSliderPositonGrid(0);
         }
     });
 
-    connect(ui->setIconSizeSmallAct, &QAction::triggered, this, [this](int value) {
+    connect(ui->setIconSizeSmallAct, &QAction::triggered, this, [this]() {
         if (isTableList) {
             m_game_list_frame->icon_size = 64;
-            m_gui_settings->SetValue(gui::m_icon_size, 64);
             ui->sizeSlider->setValue(28);
-            m_gui_settings->SetValue(gui::m_slide_pos, 28);
+            Config::setIconSize(64);
+            Config::setSliderPositon(28);
         } else {
-            m_gui_settings->SetValue(gui::m_icon_size_grid, 97);
             ui->sizeSlider->setValue(28);
-            m_gui_settings->SetValue(gui::m_slide_pos_grid, 28);
+            Config::setIconSizeGrid(97);
+            Config::setSliderPositonGrid(28);
         }
     });
 
-    connect(ui->setIconSizeMediumAct, &QAction::triggered, this, [this](int value) {
+    connect(ui->setIconSizeMediumAct, &QAction::triggered, this, [this]() {
         if (isTableList) {
             m_game_list_frame->icon_size = 128;
-            m_gui_settings->SetValue(gui::m_icon_size, 128);
             ui->sizeSlider->setValue(92);
-            m_gui_settings->SetValue(gui::m_slide_pos, 92);
+            Config::setIconSize(128);
+            Config::setSliderPositon(92);
         } else {
-            m_gui_settings->SetValue(gui::m_icon_size_grid, 160);
             ui->sizeSlider->setValue(92);
-            m_gui_settings->SetValue(gui::m_slide_pos_grid, 92);
+            Config::setIconSizeGrid(160);
+            Config::setSliderPositonGrid(91);
         }
     });
 
-    connect(ui->setIconSizeLargeAct, &QAction::triggered, this, [this](int value) {
+    connect(ui->setIconSizeLargeAct, &QAction::triggered, this, [this]() {
         if (isTableList) {
             m_game_list_frame->icon_size = 256;
-            m_gui_settings->SetValue(gui::m_icon_size, 256);
             ui->sizeSlider->setValue(220);
-            m_gui_settings->SetValue(gui::m_slide_pos, 220);
+            Config::setIconSize(256);
+            Config::setSliderPositon(220);
         } else {
-            m_gui_settings->SetValue(gui::m_icon_size_grid, 256);
             ui->sizeSlider->setValue(220);
-            m_gui_settings->SetValue(gui::m_slide_pos_grid, 220);
+            Config::setIconSizeGrid(256);
+            Config::setSliderPositonGrid(220);
         }
     });
-
-    connect(ui->setlistModeListAct, &QAction::triggered, m_dock_widget, [this]() {
-        m_dock_widget->setWidget(m_game_list_frame);
-        m_game_list_frame->show();
+    // List
+    connect(ui->setlistModeListAct, &QAction::triggered, m_dock_widget.data(), [this]() {
+        m_dock_widget->setWidget(m_game_list_frame.data());
         m_game_grid_frame->hide();
+        m_elf_viewer->hide();
+        m_game_list_frame->show();
         if (m_game_list_frame->item(0, 0) == nullptr) {
             m_game_list_frame->clearContents();
             m_game_list_frame->PopulateGameList();
         }
         isTableList = true;
-        m_gui_settings->SetValue(gui::m_table_mode, 0); // save table mode
-        int slider_pos = m_gui_settings->GetValue(gui::m_slide_pos).toInt();
+        Config::setTableMode(0);
+        int slider_pos = Config::getSliderPositon();
+        ui->sizeSlider->setEnabled(true);
         ui->sizeSlider->setSliderPosition(slider_pos);
     });
-
-    connect(ui->setlistModeGridAct, &QAction::triggered, m_dock_widget, [this]() {
-        m_dock_widget->setWidget(m_game_grid_frame);
+    // Grid
+    connect(ui->setlistModeGridAct, &QAction::triggered, m_dock_widget.data(), [this]() {
+        m_dock_widget->setWidget(m_game_grid_frame.data());
         m_game_grid_frame->show();
         m_game_list_frame->hide();
+        m_elf_viewer->hide();
         if (m_game_grid_frame->item(0, 0) == nullptr) {
             m_game_grid_frame->clearContents();
             m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false);
         }
         isTableList = false;
-        m_gui_settings->SetValue(gui::m_table_mode, 1); // save table mode
-        int slider_pos_grid = m_gui_settings->GetValue(gui::m_slide_pos_grid).toInt();
+        Config::setTableMode(1);
+        int slider_pos_grid = Config::getSliderPositonGrid();
+        ui->sizeSlider->setEnabled(true);
         ui->sizeSlider->setSliderPosition(slider_pos_grid);
     });
+    // Elf
+    connect(ui->setlistElfAct, &QAction::triggered, m_dock_widget.data(), [this]() {
+        m_dock_widget->setWidget(m_elf_viewer.data());
+        m_game_grid_frame->hide();
+        m_game_list_frame->hide();
+        m_elf_viewer->show();
+        isTableList = false;
+        ui->sizeSlider->setDisabled(true);
+        Config::setTableMode(2);
+    });
 
     // Dump game list.
-    connect(ui->dumpGameListAct, &QAction::triggered, this, [this] {
+    connect(ui->dumpGameListAct, &QAction::triggered, this, [&] {
         QString filePath = qApp->applicationDirPath().append("/GameList.txt");
         QFile file(filePath);
         QTextStream out(&file);
@@ -270,21 +326,26 @@ void MainWindow::CreateConnects() {
     });
 
     // Package install.
-    connect(ui->bootInstallPkgAct, &QAction::triggered, this, [this] { InstallPkg(); });
-    connect(ui->gameInstallPathAct, &QAction::triggered, this, [this] { InstallDirectory(); });
+    connect(ui->bootInstallPkgAct, &QAction::triggered, this, &MainWindow::InstallPkg);
+    connect(ui->gameInstallPathAct, &QAction::triggered, this, &MainWindow::InstallDirectory);
+
+    // elf viewer
+    connect(ui->addElfFolderAct, &QAction::triggered, m_elf_viewer.data(),
+            &ElfViewer::OpenElfFolder);
+
     // Package Viewer.
     connect(ui->pkgViewerAct, &QAction::triggered, this, [this]() {
-        PKGViewer* pkgViewer = new PKGViewer(m_game_info, m_gui_settings,
-                                             [this](std::string file, int pkgNum, int nPkg) {
-                                                 this->InstallDragDropPkg(file, pkgNum, nPkg);
-                                             });
+        PKGViewer* pkgViewer = new PKGViewer(
+            m_game_info, this, [this](std::filesystem::path file, int pkgNum, int nPkg) {
+                this->InstallDragDropPkg(file, pkgNum, nPkg);
+            });
         pkgViewer->show();
     });
 
     // Themes
     connect(ui->setThemeLight, &QAction::triggered, &m_window_themes, [this]() {
         m_window_themes.SetWindowTheme(Theme::Light, ui->mw_searchbar);
-        m_gui_settings->SetValue(gui::mw_themes, static_cast<int>(Theme::Light));
+        Config::setMainWindowTheme(static_cast<int>(Theme::Light));
         if (!isIconBlack) {
             SetUiIcons(true);
             isIconBlack = true;
@@ -292,7 +353,7 @@ void MainWindow::CreateConnects() {
     });
     connect(ui->setThemeDark, &QAction::triggered, &m_window_themes, [this]() {
         m_window_themes.SetWindowTheme(Theme::Dark, ui->mw_searchbar);
-        m_gui_settings->SetValue(gui::mw_themes, static_cast<int>(Theme::Dark));
+        Config::setMainWindowTheme(static_cast<int>(Theme::Dark));
         if (isIconBlack) {
             SetUiIcons(false);
             isIconBlack = false;
@@ -300,7 +361,7 @@ void MainWindow::CreateConnects() {
     });
     connect(ui->setThemeGreen, &QAction::triggered, &m_window_themes, [this]() {
         m_window_themes.SetWindowTheme(Theme::Green, ui->mw_searchbar);
-        m_gui_settings->SetValue(gui::mw_themes, static_cast<int>(Theme::Green));
+        Config::setMainWindowTheme(static_cast<int>(Theme::Green));
         if (isIconBlack) {
             SetUiIcons(false);
             isIconBlack = false;
@@ -308,7 +369,7 @@ void MainWindow::CreateConnects() {
     });
     connect(ui->setThemeBlue, &QAction::triggered, &m_window_themes, [this]() {
         m_window_themes.SetWindowTheme(Theme::Blue, ui->mw_searchbar);
-        m_gui_settings->SetValue(gui::mw_themes, static_cast<int>(Theme::Blue));
+        Config::setMainWindowTheme(static_cast<int>(Theme::Blue));
         if (isIconBlack) {
             SetUiIcons(false);
             isIconBlack = false;
@@ -316,7 +377,7 @@ void MainWindow::CreateConnects() {
     });
     connect(ui->setThemeViolet, &QAction::triggered, &m_window_themes, [this]() {
         m_window_themes.SetWindowTheme(Theme::Violet, ui->mw_searchbar);
-        m_gui_settings->SetValue(gui::mw_themes, static_cast<int>(Theme::Violet));
+        Config::setMainWindowTheme(static_cast<int>(Theme::Violet));
         if (isIconBlack) {
             SetUiIcons(false);
             isIconBlack = false;
@@ -345,8 +406,8 @@ void MainWindow::SearchGameTable(const QString& text) {
 }
 
 void MainWindow::RefreshGameTable() {
-    m_game_info->m_games.clear();
-    m_game_info->GetGameInfo();
+    // m_game_info->m_games.clear();
+    m_game_info->GetGameInfo(this);
     m_game_list_frame->clearContents();
     m_game_list_frame->PopulateGameList();
     m_game_grid_frame->clearContents();
@@ -358,16 +419,10 @@ void MainWindow::RefreshGameTable() {
 }
 
 void MainWindow::ConfigureGuiFromSettings() {
-    // Restore GUI state if needed. We need to if they exist.
-    if (!restoreGeometry(m_gui_settings->GetValue(gui::main_window_geometry).toByteArray())) {
-        resize(QGuiApplication::primaryScreen()->availableSize() * 0.7);
-    }
-
-    m_main_window->restoreState(m_gui_settings->GetValue(gui::main_window_mwState).toByteArray());
-
-    ui->showGameListAct->setChecked(
-        m_gui_settings->GetValue(gui::main_window_gamelist_visible).toBool());
+    setGeometry(Config::getMainWindowGeometryX(), Config::getMainWindowGeometryY(),
+                Config::getMainWindowGeometryW(), Config::getMainWindowGeometryH());
 
+    ui->showGameListAct->setChecked(true);
     if (isTableList) {
         ui->setlistModeListAct->setChecked(true);
     } else {
@@ -376,87 +431,155 @@ void MainWindow::ConfigureGuiFromSettings() {
 }
 
 void MainWindow::SaveWindowState() const {
-    // Save gui settings
-    m_gui_settings->SetValue(gui::main_window_geometry, saveGeometry());
-    m_gui_settings->SetValue(gui::main_window_windowState, saveState());
-    m_gui_settings->SetValue(gui::m_window_size,
-                             QSize(m_main_window->width(), m_main_window->height()));
-    m_gui_settings->SetValue(gui::main_window_mwState, m_main_window->saveState());
+    Config::setMainWindowWidth(this->width());
+    Config::setMainWindowHeight(this->height());
+    Config::setMainWindowGeometry(this->geometry().x(), this->geometry().y(),
+                                  this->geometry().width(), this->geometry().height());
 }
 
 void MainWindow::InstallPkg() {
-    QStringList fileNames = QFileDialog::getOpenFileNames(
-        this, tr("Install PKG Files"), QDir::currentPath(), tr("PKG File (*.PKG)"));
-    int nPkg = fileNames.size();
-    int pkgNum = 0;
-    for (const QString& file : fileNames) {
-        pkgNum++;
-        MainWindow::InstallDragDropPkg(file.toStdString(), pkgNum, nPkg);
+    QFileDialog dialog;
+    dialog.setFileMode(QFileDialog::ExistingFiles);
+    dialog.setNameFilter(tr("PKG File (*.PKG)"));
+    if (dialog.exec()) {
+        QStringList fileNames = dialog.selectedFiles();
+        int nPkg = fileNames.size();
+        int pkgNum = 0;
+        for (const QString& file : fileNames) {
+            ++pkgNum;
+            std::filesystem::path path(file.toStdString());
+#ifdef _WIN64
+            path = std::filesystem::path(file.toStdWString());
+#endif
+            MainWindow::InstallDragDropPkg(path, pkgNum, nPkg);
+        }
     }
 }
 
-void MainWindow::InstallDragDropPkg(std::string file, int pkgNum, int nPkg) {
+void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int nPkg) {
     if (Loader::DetectFileType(file) == Loader::FileTypes::Pkg) {
-        PKG pkg;
+        pkg = PKG();
         pkg.Open(file);
         std::string failreason;
         const auto extract_path =
-            std::filesystem::path(
-                m_gui_settings->GetValue(gui::settings_install_dir).toString().toStdString()) /
-            pkg.GetTitleID();
+            std::filesystem::path(Config::getGameInstallDir()) / pkg.GetTitleID();
+        QString pkgType = QString::fromStdString(pkg.GetPkgFlags());
+        QDir game_dir(QString::fromStdString(extract_path.string()));
+        if (game_dir.exists()) {
+            QMessageBox msgBox;
+            msgBox.setWindowTitle("PKG Extraction");
+            if (pkgType.contains("PATCH")) {
+                psf.open("", pkg.sfo);
+                QString pkg_app_version = QString::fromStdString(psf.GetString("APP_VER"));
+                psf.open(extract_path.string() + "/sce_sys/param.sfo", {});
+                QString game_app_version = QString::fromStdString(psf.GetString("APP_VER"));
+                double appD = game_app_version.toDouble();
+                double pkgD = pkg_app_version.toDouble();
+                if (pkgD == appD) {
+                    msgBox.setText(
+                        QString("Patch detected!\nPKG and Game versions match!: %1\nWould you like "
+                                "to overwrite?")
+                            .arg(pkg_app_version));
+                    msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+                    msgBox.setDefaultButton(QMessageBox::No);
+                } else if (pkgD < appD) {
+                    QMessageBox::information(this, "PKG Extraction",
+                                             QString("Patch detected!\nPKG Version %1 is older "
+                                                     "than installed version!: %2\nWould you like "
+                                                     "to overwrite?")
+                                                 .arg(pkg_app_version, game_app_version));
+                    return;
+                } else {
+                    msgBox.setText(QString("Patch detected!\nGame is installed: %1\nWould you like "
+                                           "to install Patch: %2?")
+                                       .arg(game_app_version, pkg_app_version));
+                    msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+                    msgBox.setDefaultButton(QMessageBox::No);
+                }
+                int result = msgBox.exec();
+                if (result == QMessageBox::Yes) {
+                    // Do nothing.
+                } else {
+                    return;
+                }
+            } else {
+                msgBox.setText(QString("Game already installed\n%1\nWould you like to overwrite?")
+                                   .arg(QString::fromStdString(extract_path.string())));
+                msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
+                msgBox.setDefaultButton(QMessageBox::No);
+                int result = msgBox.exec();
+                if (result == QMessageBox::Yes) {
+                    // Do nothing.
+                } else {
+                    return;
+                }
+            }
+        } else {
+            // Do nothing;
+            if (pkgType.contains("PATCH")) {
+                QMessageBox::information(this, "PKG Extraction",
+                                         "PKG is a patch, please install the game first!");
+                return;
+            }
+            // what else?
+        }
+
         if (!pkg.Extract(file, extract_path, failreason)) {
-            QMessageBox::critical(this, "PKG ERROR", QString::fromStdString(failreason),
-                                  QMessageBox::Ok);
+            QMessageBox::critical(this, "PKG ERROR", QString::fromStdString(failreason));
         } else {
             int nfiles = pkg.GetNumberOfFiles();
 
-            QList<int> indices;
+            QVector<int> indices;
             for (int i = 0; i < nfiles; i++) {
                 indices.append(i);
             }
 
             QProgressDialog dialog;
             dialog.setWindowTitle("PKG Extraction");
+            dialog.setWindowModality(Qt::WindowModal);
             QString extractmsg = QString("Extracting PKG %1/%2").arg(pkgNum).arg(nPkg);
             dialog.setLabelText(extractmsg);
+            dialog.setAutoClose(true);
+            dialog.setRange(0, nfiles);
 
-            // Create a QFutureWatcher and connect signals and slots.
             QFutureWatcher<void> futureWatcher;
-            QObject::connect(&futureWatcher, SIGNAL(finished()), &dialog, SLOT(reset()));
-            QObject::connect(&dialog, SIGNAL(canceled()), &futureWatcher, SLOT(cancel()));
-            QObject::connect(&futureWatcher, SIGNAL(progressRangeChanged(int, int)), &dialog,
-                             SLOT(setRange(int, int)));
-            QObject::connect(&futureWatcher, SIGNAL(progressValueChanged(int)), &dialog,
-                             SLOT(setValue(int)));
-
-            futureWatcher.setFuture(QtConcurrent::map(
-                indices, std::bind(&PKG::ExtractFiles, pkg, std::placeholders::_1)));
-
-            // Display the dialog and start the event loop.
+            connect(&futureWatcher, &QFutureWatcher<void>::finished, this, [=, this]() {
+                if (pkgNum == nPkg) {
+                    QString path = QString::fromStdString(Config::getGameInstallDir());
+                    QMessageBox extractMsgBox(this);
+                    extractMsgBox.setWindowTitle("Extraction Finished");
+                    extractMsgBox.setText(QString("Game successfully installed at %1").arg(path));
+                    extractMsgBox.addButton(QMessageBox::Ok);
+                    extractMsgBox.setDefaultButton(QMessageBox::Ok);
+                    connect(&extractMsgBox, &QMessageBox::buttonClicked, this,
+                            [&](QAbstractButton* button) {
+                                if (extractMsgBox.button(QMessageBox::Ok) == button) {
+                                    extractMsgBox.close();
+                                    emit ExtractionFinished();
+                                }
+                            });
+                    extractMsgBox.exec();
+                }
+            });
+            connect(&dialog, &QProgressDialog::canceled, [&]() { futureWatcher.cancel(); });
+            connect(&futureWatcher, &QFutureWatcher<void>::progressValueChanged, &dialog,
+                    &QProgressDialog::setValue);
+            futureWatcher.setFuture(
+                QtConcurrent::map(indices, [&](int index) { pkg.ExtractFiles(index); }));
             dialog.exec();
-            futureWatcher.waitForFinished();
-
-            auto path = m_gui_settings->GetValue(gui::settings_install_dir).toString();
-            if (pkgNum == nPkg) {
-                QMessageBox::information(this, "Extraction Finished",
-                                         "Game successfully installed at " + path, QMessageBox::Ok);
-                // Refresh game table after extraction.
-                RefreshGameTable();
-            }
         }
     } else {
-        QMessageBox::critical(this, "PKG ERROR", "File doesn't appear to be a valid PKG file",
-                              QMessageBox::Ok);
+        QMessageBox::critical(this, "PKG ERROR", "File doesn't appear to be a valid PKG file");
     }
 }
 
 void MainWindow::InstallDirectory() {
-    GameInstallDialog dlg(m_gui_settings);
+    GameInstallDialog dlg;
     dlg.exec();
 }
 
 void MainWindow::SetLastUsedTheme() {
-    Theme lastTheme = static_cast<Theme>(m_gui_settings->GetValue(gui::mw_themes).toInt());
+    Theme lastTheme = static_cast<Theme>(Config::getMainWindowTheme());
     m_window_themes.SetWindowTheme(lastTheme, ui->mw_searchbar);
 
     switch (lastTheme) {
@@ -489,7 +612,7 @@ void MainWindow::SetLastUsedTheme() {
 
 void MainWindow::SetLastIconSizeBullet() {
     // set QAction bullet point if applicable
-    int lastSize = m_gui_settings->GetValue(gui::m_icon_size).toInt();
+    int lastSize = Config::getIconSize();
     switch (lastSize) {
     case 36:
         ui->setIconSizeTinyAct->setChecked(true);
@@ -507,47 +630,30 @@ void MainWindow::SetLastIconSizeBullet() {
 }
 
 QIcon MainWindow::RecolorIcon(const QIcon& icon, bool isWhite) {
-    QPixmap pixmap(icon.pixmap(icon.actualSize(QSize(120, 120)), QIcon::Normal));
+    QPixmap pixmap(icon.pixmap(icon.actualSize(QSize(120, 120))));
     QColor clr(isWhite ? Qt::white : Qt::black);
     QBitmap mask = pixmap.createMaskFromColor(clr, Qt::MaskOutColor);
     pixmap.fill(QColor(isWhite ? Qt::black : Qt::white));
     pixmap.setMask(mask);
-    QIcon newIcon(pixmap);
-    return newIcon;
+    return QIcon(pixmap);
 }
 
 void MainWindow::SetUiIcons(bool isWhite) {
-    QIcon icon;
-    icon = RecolorIcon(ui->bootInstallPkgAct->icon(), isWhite);
-    ui->bootInstallPkgAct->setIcon(icon);
-    icon = RecolorIcon(ui->exitAct->icon(), isWhite);
-    ui->exitAct->setIcon(icon);
-    icon = RecolorIcon(ui->setlistModeListAct->icon(), isWhite);
-    ui->setlistModeListAct->setIcon(icon);
-    icon = RecolorIcon(ui->setlistModeGridAct->icon(), isWhite);
-    ui->setlistModeGridAct->setIcon(icon);
-    icon = RecolorIcon(ui->gameInstallPathAct->icon(), isWhite);
-    ui->gameInstallPathAct->setIcon(icon);
-    icon = RecolorIcon(ui->menuThemes->icon(), isWhite);
-    ui->menuThemes->setIcon(icon);
-    icon = RecolorIcon(ui->menuGame_List_Icons->icon(), isWhite);
-    ui->menuGame_List_Icons->setIcon(icon);
-    icon = RecolorIcon(ui->playButton->icon(), isWhite);
-    ui->playButton->setIcon(icon);
-    icon = RecolorIcon(ui->pauseButton->icon(), isWhite);
-    ui->pauseButton->setIcon(icon);
-    icon = RecolorIcon(ui->stopButton->icon(), isWhite);
-    ui->stopButton->setIcon(icon);
-    icon = RecolorIcon(ui->settingsButton->icon(), isWhite);
-    ui->settingsButton->setIcon(icon);
-    icon = RecolorIcon(ui->controllerButton->icon(), isWhite);
-    ui->controllerButton->setIcon(icon);
-    icon = RecolorIcon(ui->refreshGameListAct->icon(), isWhite);
-    ui->refreshGameListAct->setIcon(icon);
-    icon = RecolorIcon(ui->menuGame_List_Mode->icon(), isWhite);
-    ui->menuGame_List_Mode->setIcon(icon);
-    icon = RecolorIcon(ui->pkgViewerAct->icon(), isWhite);
-    ui->pkgViewerAct->setIcon(icon);
+    ui->bootInstallPkgAct->setIcon(RecolorIcon(ui->bootInstallPkgAct->icon(), isWhite));
+    ui->exitAct->setIcon(RecolorIcon(ui->exitAct->icon(), isWhite));
+    ui->setlistModeListAct->setIcon(RecolorIcon(ui->setlistModeListAct->icon(), isWhite));
+    ui->setlistModeGridAct->setIcon(RecolorIcon(ui->setlistModeGridAct->icon(), isWhite));
+    ui->gameInstallPathAct->setIcon(RecolorIcon(ui->gameInstallPathAct->icon(), isWhite));
+    ui->menuThemes->setIcon(RecolorIcon(ui->menuThemes->icon(), isWhite));
+    ui->menuGame_List_Icons->setIcon(RecolorIcon(ui->menuGame_List_Icons->icon(), isWhite));
+    ui->playButton->setIcon(RecolorIcon(ui->playButton->icon(), isWhite));
+    ui->pauseButton->setIcon(RecolorIcon(ui->pauseButton->icon(), isWhite));
+    ui->stopButton->setIcon(RecolorIcon(ui->stopButton->icon(), isWhite));
+    ui->settingsButton->setIcon(RecolorIcon(ui->settingsButton->icon(), isWhite));
+    ui->controllerButton->setIcon(RecolorIcon(ui->controllerButton->icon(), isWhite));
+    ui->refreshGameListAct->setIcon(RecolorIcon(ui->refreshGameListAct->icon(), isWhite));
+    ui->menuGame_List_Mode->setIcon(RecolorIcon(ui->menuGame_List_Mode->icon(), isWhite));
+    ui->pkgViewerAct->setIcon(RecolorIcon(ui->pkgViewerAct->icon(), isWhite));
 }
 
 void MainWindow::resizeEvent(QResizeEvent* event) {
@@ -563,4 +669,44 @@ void MainWindow::HandleResize(QResizeEvent* event) {
         m_game_grid_frame->PopulateGameGrid(m_game_info->m_games, false);
         m_game_grid_frame->RefreshGridBackgroundImage();
     }
+}
+
+void MainWindow::AddRecentFiles(QString filePath) {
+    std::vector<std::string> vec = Config::getRecentFiles();
+    if (!vec.empty()) {
+        if (filePath.toStdString() == vec.at(0)) {
+            return;
+        }
+        auto it = std::find(vec.begin(), vec.end(), filePath.toStdString());
+        if (it != vec.end()) {
+            vec.erase(it);
+        }
+    }
+    vec.insert(vec.begin(), filePath.toStdString());
+    if (vec.size() > 6) {
+        vec.pop_back();
+    }
+    Config::setRecentFiles(vec);
+    const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir);
+    Config::save(config_dir / "config.toml");
+    CreateRecentGameActions(); // Refresh the QActions.
+}
+
+void MainWindow::CreateRecentGameActions() {
+    m_recent_files_group = new QActionGroup(this);
+    ui->menuRecent->clear();
+    std::vector<std::string> vec = Config::getRecentFiles();
+    for (int i = 0; i < vec.size(); i++) {
+        QAction* recentFileAct = new QAction(this);
+        recentFileAct->setText(QString::fromStdString(vec.at(i)));
+        ui->menuRecent->addAction(recentFileAct);
+        m_recent_files_group->addAction(recentFileAct);
+    }
+
+    connect(m_recent_files_group, &QActionGroup::triggered, this, [this](QAction* action) {
+        QString gamePath = action->text();
+        AddRecentFiles(gamePath); // Update the list.
+        Core::Emulator emulator;
+        emulator.Run(gamePath.toUtf8().constData());
+    });
 }
\ No newline at end of file
diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h
index dfe448b9..27d14b93 100644
--- a/src/qt_gui/main_window.h
+++ b/src/qt_gui/main_window.h
@@ -3,10 +3,19 @@
 
 #pragma once
 
+#include <QAbstractButton>
 #include <QActionGroup>
 #include <QDragEnterEvent>
 #include <QMainWindow>
 #include <QMimeData>
+#include <QScopedPointer>
+#include <emulator.h>
+#include <fmt/core.h>
+#include "common/config.h"
+#include "common/path_util.h"
+#include "core/file_format/psf.h"
+#include "core/file_sys/fs.h"
+#include "elf_viewer.h"
 #include "game_grid_frame.h"
 #include "game_info.h"
 #include "game_list_frame.h"
@@ -15,26 +24,19 @@
 #include "main_window_ui.h"
 #include "pkg_viewer.h"
 
-class GuiSettings;
 class GameListFrame;
 
 class MainWindow : public QMainWindow {
     Q_OBJECT
-
-    std::unique_ptr<Ui_MainWindow> ui;
-
-    bool m_is_list_mode = true;
-    bool m_save_slider_pos = false;
-    int m_other_slider_pos = 0;
 signals:
     void WindowResized(QResizeEvent* event);
+    void ExtractionFinished();
 
 public:
-    explicit MainWindow(std::shared_ptr<GuiSettings> gui_settings, QWidget* parent = nullptr);
+    explicit MainWindow(QWidget* parent = nullptr);
     ~MainWindow();
     bool Init();
-    void InstallPkg();
-    void InstallDragDropPkg(std::string file, int pkgNum, int nPkg);
+    void InstallDragDropPkg(std::filesystem::path file, int pkgNum, int nPkg);
     void InstallDirectory();
 
 private Q_SLOTS:
@@ -45,38 +47,40 @@ private Q_SLOTS:
     void HandleResize(QResizeEvent* event);
 
 private:
+    Ui_MainWindow* ui;
     void AddUiWidgets();
     void CreateActions();
+    void CreateRecentGameActions();
     void CreateDockWindows();
     void LoadGameLists();
     void CreateConnects();
     void SetLastUsedTheme();
     void SetLastIconSizeBullet();
     void SetUiIcons(bool isWhite);
+    void InstallPkg();
+    void AddRecentFiles(QString filePath);
     QIcon RecolorIcon(const QIcon& icon, bool isWhite);
-
     bool isIconBlack = false;
     bool isTableList = true;
-
     QActionGroup* m_icon_size_act_group = nullptr;
     QActionGroup* m_list_mode_act_group = nullptr;
     QActionGroup* m_theme_act_group = nullptr;
-
+    QActionGroup* m_recent_files_group = nullptr;
+    PKG pkg;
     // Dockable widget frames
-    QMainWindow* m_main_window = nullptr;
     WindowThemes m_window_themes;
     GameListUtils m_game_list_utils;
-    QDockWidget* m_dock_widget = nullptr;
+    QScopedPointer<QDockWidget> m_dock_widget;
     // Game Lists
-    GameListFrame* m_game_list_frame = nullptr;
-    GameGridFrame* m_game_grid_frame = nullptr;
-    // Packge Viewer
-    PKGViewer* m_pkg_viewer = nullptr;
+    QScopedPointer<GameListFrame> m_game_list_frame;
+    QScopedPointer<GameGridFrame> m_game_grid_frame;
+    QScopedPointer<ElfViewer> m_elf_viewer;
     // Status Bar.
-    QStatusBar* statusBar = nullptr;
+    QScopedPointer<QStatusBar> statusBar;
+
+    PSF psf;
 
     std::shared_ptr<GameInfoClass> m_game_info = std::make_shared<GameInfoClass>();
-    std::shared_ptr<GuiSettings> m_gui_settings;
 
 protected:
     void dragEnterEvent(QDragEnterEvent* event1) override {
@@ -93,7 +97,11 @@ protected:
             int nPkg = urlList.size();
             for (const QUrl& url : urlList) {
                 pkgNum++;
-                InstallDragDropPkg(url.toLocalFile().toStdString(), pkgNum, nPkg);
+                std::filesystem::path path(url.toLocalFile().toStdString());
+#ifdef _WIN64
+                path = std::filesystem::path(url.toLocalFile().toStdWString());
+#endif
+                InstallDragDropPkg(path, pkgNum, nPkg);
             }
         }
     }
diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h
index 6d7b5260..7b5bf181 100644
--- a/src/qt_gui/main_window_ui.h
+++ b/src/qt_gui/main_window_ui.h
@@ -30,6 +30,7 @@ QT_BEGIN_NAMESPACE
 class Ui_MainWindow {
 public:
     QAction* bootInstallPkgAct;
+    QAction* addElfFolderAct;
     QAction* exitAct;
     QAction* showGameListAct;
     QAction* refreshGameListAct;
@@ -39,6 +40,7 @@ public:
     QAction* setIconSizeLargeAct;
     QAction* setlistModeListAct;
     QAction* setlistModeGridAct;
+    QAction* setlistElfAct;
     QAction* gameInstallPathAct;
     QAction* dumpGameListAct;
     QAction* pkgViewerAct;
@@ -54,14 +56,13 @@ public:
     QPushButton* stopButton;
     QPushButton* settingsButton;
     QPushButton* controllerButton;
-    QWidget* emuRunWidget;
-    QHBoxLayout* emuRunLayer;
 
     QWidget* sizeSliderContainer;
     QHBoxLayout* sizeSliderContainer_layout;
     QSlider* sizeSlider;
     QMenuBar* menuBar;
     QMenu* menuFile;
+    QMenu* menuRecent;
     QMenu* menuView;
     QMenu* menuGame_List_Icons;
     QMenu* menuGame_List_Mode;
@@ -91,6 +92,8 @@ public:
         bootInstallPkgAct = new QAction(MainWindow);
         bootInstallPkgAct->setObjectName("bootInstallPkgAct");
         bootInstallPkgAct->setIcon(QIcon(":images/file_icon.png"));
+        addElfFolderAct = new QAction(MainWindow);
+        addElfFolderAct->setObjectName("addElfFolderAct");
         exitAct = new QAction(MainWindow);
         exitAct->setObjectName("exitAct");
         exitAct->setIcon(QIcon(":images/exit_icon.png"));
@@ -99,7 +102,7 @@ public:
         showGameListAct->setCheckable(true);
         refreshGameListAct = new QAction(MainWindow);
         refreshGameListAct->setObjectName("refreshGameListAct");
-        refreshGameListAct->setIcon(QIcon(":/images/refresh_icon.png"));
+        refreshGameListAct->setIcon(QIcon(":images/refresh_icon.png"));
         setIconSizeTinyAct = new QAction(MainWindow);
         setIconSizeTinyAct->setObjectName("setIconSizeTinyAct");
         setIconSizeTinyAct->setCheckable(true);
@@ -121,6 +124,9 @@ public:
         setlistModeGridAct->setObjectName("setlistModeGridAct");
         setlistModeGridAct->setCheckable(true);
         setlistModeGridAct->setIcon(QIcon(":images/grid_icon.png"));
+        setlistElfAct = new QAction(MainWindow);
+        setlistElfAct->setObjectName("setlistModeGridAct");
+        setlistElfAct->setCheckable(true);
         gameInstallPathAct = new QAction(MainWindow);
         gameInstallPathAct->setObjectName("gameInstallPathAct");
         gameInstallPathAct->setIcon(QIcon(":images/folder_icon.png"));
@@ -219,6 +225,8 @@ public:
         menuBar->setContextMenuPolicy(Qt::PreventContextMenu);
         menuFile = new QMenu(menuBar);
         menuFile->setObjectName("menuFile");
+        menuRecent = new QMenu(menuFile);
+        menuRecent->setObjectName("menuRecent");
         menuView = new QMenu(menuBar);
         menuView->setObjectName("menuView");
         menuGame_List_Icons = new QMenu(menuView);
@@ -243,6 +251,9 @@ public:
         menuBar->addAction(menuView->menuAction());
         menuBar->addAction(menuSettings->menuAction());
         menuFile->addAction(bootInstallPkgAct);
+        menuFile->addAction(addElfFolderAct);
+        menuFile->addSeparator();
+        menuFile->addAction(menuRecent->menuAction());
         menuFile->addSeparator();
         menuFile->addAction(exitAct);
         menuView->addAction(showGameListAct);
@@ -262,6 +273,7 @@ public:
         menuGame_List_Icons->addAction(setIconSizeLargeAct);
         menuGame_List_Mode->addAction(setlistModeListAct);
         menuGame_List_Mode->addAction(setlistModeGridAct);
+        menuGame_List_Mode->addAction(setlistElfAct);
         menuSettings->addAction(gameInstallPathAct);
         menuSettings->addAction(menuUtils->menuAction());
         menuUtils->addAction(dumpGameListAct);
@@ -274,12 +286,15 @@ public:
 
     void retranslateUi(QMainWindow* MainWindow) {
         MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "Shadps4", nullptr));
+        addElfFolderAct->setText(
+            QCoreApplication::translate("MainWindow", "Open/Add Elf Folder", nullptr));
         bootInstallPkgAct->setText(
             QCoreApplication::translate("MainWindow", "Install Packages (PKG)", nullptr));
 #if QT_CONFIG(tooltip)
         bootInstallPkgAct->setToolTip(QCoreApplication::translate(
             "MainWindow", "Install application from a .pkg file", nullptr));
 #endif // QT_CONFIG(tooltip)
+        menuRecent->setTitle(QCoreApplication::translate("MainWindow", "Recent Games", nullptr));
         exitAct->setText(QCoreApplication::translate("MainWindow", "Exit", nullptr));
 #if QT_CONFIG(tooltip)
         exitAct->setToolTip(QCoreApplication::translate("MainWindow", "Exit Shadps4", nullptr));
@@ -300,6 +315,7 @@ public:
             QCoreApplication::translate("MainWindow", "List View", nullptr));
         setlistModeGridAct->setText(
             QCoreApplication::translate("MainWindow", "Grid View", nullptr));
+        setlistElfAct->setText(QCoreApplication::translate("MainWindow", "Elf Viewer", nullptr));
         gameInstallPathAct->setText(
             QCoreApplication::translate("MainWindow", "Game Install Directory", nullptr));
         dumpGameListAct->setText(
@@ -307,8 +323,6 @@ public:
         pkgViewerAct->setText(QCoreApplication::translate("MainWindow", "PKG Viewer", nullptr));
         mw_searchbar->setPlaceholderText(
             QCoreApplication::translate("MainWindow", "Search...", nullptr));
-        // darkModeSwitch->setText(
-        //    QCoreApplication::translate("MainWindow", "Game", nullptr));
         menuFile->setTitle(QCoreApplication::translate("MainWindow", "File", nullptr));
         menuView->setTitle(QCoreApplication::translate("MainWindow", "View", nullptr));
         menuGame_List_Icons->setTitle(
diff --git a/src/qt_gui/pkg_viewer.cpp b/src/qt_gui/pkg_viewer.cpp
index 9bccb482..cf0b2167 100644
--- a/src/qt_gui/pkg_viewer.cpp
+++ b/src/qt_gui/pkg_viewer.cpp
@@ -5,14 +5,16 @@
 #include <QWidget>
 #include "pkg_viewer.h"
 
-PKGViewer::PKGViewer(std::shared_ptr<GameInfoClass> game_info_get,
-                     std::shared_ptr<GuiSettings> m_gui_settings,
-                     std::function<void(std::string, int, int)> InstallDragDropPkg)
-    : QMainWindow() {
+PKGViewer::PKGViewer(std::shared_ptr<GameInfoClass> game_info_get, QWidget* parent,
+                     std::function<void(std::filesystem::path, int, int)> InstallDragDropPkg)
+    : QMainWindow(), m_game_info(game_info_get) {
     this->resize(1280, 720);
-    m_gui_settings_ = m_gui_settings;
-    m_game_info = game_info_get;
-    dir_list = m_gui_settings->GetValue(gui::m_pkg_viewer).toStringList();
+    this->setAttribute(Qt::WA_DeleteOnClose);
+    dir_list_std = Config::getPkgViewer();
+    dir_list.clear();
+    for (const auto& str : dir_list_std) {
+        dir_list.append(QString::fromStdString(str));
+    }
     statusBar = new QStatusBar(treeWidget);
     this->setStatusBar(statusBar);
     treeWidget = new QTreeWidget(this);
@@ -20,8 +22,8 @@ PKGViewer::PKGViewer(std::shared_ptr<GameInfoClass> game_info_get,
     QStringList headers;
     headers << "Name"
             << "Serial"
-            << "Size"
             << "Installed"
+            << "Size"
             << "Category"
             << "Type"
             << "App Ver"
@@ -50,6 +52,8 @@ PKGViewer::PKGViewer(std::shared_ptr<GameInfoClass> game_info_get,
                 m_gui_context_menus.RequestGameMenuPKGViewer(pos, m_full_pkg_list, treeWidget,
                                                              InstallDragDropPkg);
             });
+
+    connect(parent, &QWidget::destroyed, this, [parent, this]() { this->deleteLater(); });
 }
 
 PKGViewer::~PKGViewer() {}
@@ -59,18 +63,21 @@ void PKGViewer::OpenPKGFolder() {
         QFileDialog::getExistingDirectory(this, tr("Open Folder"), QDir::homePath());
     if (!dir_list.contains(folderPath)) {
         dir_list.append(folderPath);
-        if (!folderPath.isEmpty()) {
-            for (const auto& dir : std::filesystem::directory_iterator(folderPath.toStdString())) {
-                QString file_ext =
-                    QString::fromStdString(dir.path().extension().string()).toLower();
-                if (std::filesystem::is_regular_file(dir.path()) && file_ext == ".pkg") {
-                    m_pkg_list.append(QString::fromStdString(dir.path().string()));
-                }
+        QDir directory(folderPath);
+        QFileInfoList fileInfoList = directory.entryInfoList(QDir::Files);
+        for (const QFileInfo& fileInfo : fileInfoList) {
+            QString file_ext = fileInfo.suffix();
+            if (fileInfo.isFile() && file_ext == "pkg") {
+                m_pkg_list.append(fileInfo.absoluteFilePath());
             }
-            std::sort(m_pkg_list.begin(), m_pkg_list.end());
-            ProcessPKGInfo();
-            m_gui_settings_->SetValue(gui::m_pkg_viewer, dir_list);
         }
+        std::sort(m_pkg_list.begin(), m_pkg_list.end());
+        ProcessPKGInfo();
+        dir_list_std.clear();
+        for (auto dir : dir_list) {
+            dir_list_std.push_back(dir.toStdString());
+        }
+        Config::setPkgViewer(dir_list_std);
     } else {
         // qDebug() << "Folder selection canceled.";
     }
@@ -78,11 +85,13 @@ void PKGViewer::OpenPKGFolder() {
 
 void PKGViewer::CheckPKGFolders() { // Check for new PKG file additions.
     m_pkg_list.clear();
-    for (const QString& paths : dir_list) {
-        for (const auto& dir : std::filesystem::directory_iterator(paths.toStdString())) {
-            QString file_ext = QString::fromStdString(dir.path().extension().string()).toLower();
-            if (std::filesystem::is_regular_file(dir.path()) && file_ext == ".pkg") {
-                m_pkg_list.append(QString::fromStdString(dir.path().string()));
+    for (const QString& dir : dir_list) {
+        QDir directory(dir);
+        QFileInfoList fileInfoList = directory.entryInfoList(QDir::Files);
+        for (const QFileInfo& fileInfo : fileInfoList) {
+            QString file_ext = fileInfo.suffix();
+            if (fileInfo.isFile() && file_ext == "pkg") {
+                m_pkg_list.append(fileInfo.absoluteFilePath());
             }
         }
     }
@@ -97,87 +106,46 @@ void PKGViewer::ProcessPKGInfo() {
     m_pkg_patch_list.clear();
     m_full_pkg_list.clear();
     for (int i = 0; i < m_pkg_list.size(); i++) {
-        Common::FS::IOFile file(m_pkg_list[i].toStdString(), Common::FS::FileAccessMode::Read);
-        if (!file.IsOpen()) {
-            // return false;
-        }
-
-        file.ReadRaw<u8>(&pkgheader, sizeof(PKGHeader));
-        file.Seek(0);
-        pkgSize = file.GetSize();
-        pkg.resize(pkgheader.pkg_promote_size);
-        file.Read(pkg);
-
-        u32 offset = pkgheader.pkg_table_entry_offset;
-        u32 n_files = pkgheader.pkg_table_entry_count;
-
-        for (int i = 0; i < n_files; i++) {
-            std::memcpy(&entry, &pkg[offset + i * 0x20], sizeof(entry));
-            const auto name = GetEntryNameByType(entry.id);
-            if (name == "param.sfo") {
-                psf.resize(entry.size);
-                int seek = entry.offset;
-                file.Seek(seek);
-                file.Read(psf);
-                std::memcpy(&header, psf.data(), sizeof(header));
-                auto future = std::async(std::launch::async, [&]() {
-                    for (u32 i = 0; i < header.index_table_entries; i++) {
-                        PSFEntry psfentry;
-                        std::memcpy(&psfentry, &psf[sizeof(PSFHeader) + i * sizeof(PSFEntry)],
-                                    sizeof(psfentry));
-                        const std::string key =
-                            (char*)&psf[header.key_table_offset + psfentry.key_offset];
-                        if (psfentry.param_fmt == PSFEntry::Fmt::TextRaw ||
-                            psfentry.param_fmt == PSFEntry::Fmt::TextNormal) {
-                            map_strings[key] =
-                                (char*)&psf[header.data_table_offset + psfentry.data_offset];
-                        }
-                        if (psfentry.param_fmt == PSFEntry::Fmt::Integer) {
-                            u32 value;
-                            std::memcpy(&value,
-                                        &psf[header.data_table_offset + psfentry.data_offset],
-                                        sizeof(value));
-                            map_integers[key] = value;
-                        }
-                    }
-                });
-                future.wait();
-            }
-        }
-        QString title_name = GetString("TITLE");
-        QString title_id = GetString("TITLE_ID");
-        QString app_type = GetAppType(GetInteger("APP_TYPE"));
-        QString app_version = GetString("APP_VER");
-        QString title_category = GetString("CATEGORY");
-        QString pkg_size = game_list_util.FormatSize(pkgheader.pkg_size);
-        pkg_content_flag = pkgheader.pkg_content_flags;
+        std::filesystem::path path(m_pkg_list[i].toStdString());
+#ifdef _WIN32
+        path = std::filesystem::path(m_pkg_list[i].toStdWString());
+#endif
+        package.Open(path);
+        psf.open("", package.sfo);
+        QString title_name = QString::fromStdString(psf.GetString("TITLE"));
+        QString title_id = QString::fromStdString(psf.GetString("TITLE_ID"));
+        QString app_type = game_list_util.GetAppType(psf.GetInteger("APP_TYPE"));
+        QString app_version = QString::fromStdString(psf.GetString("APP_VER"));
+        QString title_category = QString::fromStdString(psf.GetString("CATEGORY"));
+        QString pkg_size = game_list_util.FormatSize(package.GetPkgHeader().pkg_size);
+        pkg_content_flag = package.GetPkgHeader().pkg_content_flags;
         QString flagss = "";
-        for (const auto& flag : flagNames) {
-            if (isFlagSet(pkg_content_flag, flag.first)) {
+        for (const auto& flag : package.flagNames) {
+            if (package.isFlagSet(pkg_content_flag, flag.first)) {
                 if (!flagss.isEmpty())
-                    flagss.append(", ");
-                flagss.append(QString::fromStdString(flag.second));
+                    flagss += (", ");
+                flagss += QString::fromStdString(flag.second.data());
             }
         }
 
-        u32 fw_int = GetInteger("SYSTEM_VER");
+        u32 fw_int = psf.GetInteger("SYSTEM_VER");
         QString fw = QString::number(fw_int, 16);
         QString fw_ = fw.length() > 7 ? QString::number(fw_int, 16).left(3).insert(2, '.')
                                       : fw.left(3).insert(1, '.');
         fw_ = (fw_int == 0) ? "0.00" : fw_;
-        char region = pkgheader.pkg_content_id[0];
+        char region = package.GetPkgHeader().pkg_content_id[0];
         QString pkg_info = "";
-        if (title_category == "gd") {
+        if (title_category == "gd" && !flagss.contains("PATCH")) {
             title_category = "App";
             pkg_info = title_name + ";;" + title_id + ";;" + pkg_size + ";;" + title_category +
                        ";;" + app_type + ";;" + app_version + ";;" + fw_ + ";;" +
-                       GetRegion(region) + ";;" + flagss + ";;" + m_pkg_list[i];
+                       game_list_util.GetRegion(region) + ";;" + flagss + ";;" + m_pkg_list[i];
             m_pkg_app_list.append(pkg_info);
         } else {
             title_category = "Patch";
             pkg_info = title_name + ";;" + title_id + ";;" + pkg_size + ";;" + title_category +
                        ";;" + app_type + ";;" + app_version + ";;" + fw_ + ";;" +
-                       GetRegion(region) + ";;" + flagss + ";;" + m_pkg_list[i];
+                       game_list_util.GetRegion(region) + ";;" + flagss + ";;" + m_pkg_list[i];
             m_pkg_patch_list.append(pkg_info);
         }
     }
@@ -204,7 +172,7 @@ void PKGViewer::ProcessPKGInfo() {
         treeItem->setText(9, pkg_app_[8]);
         treeItem->setText(10, pkg_app_[9]);
         for (const GameInfo& info : m_game_info->m_games) { // Check if game is installed.
-            if (info.name == pkg_app_[0].toStdString()) {
+            if (info.serial == pkg_app_[1].toStdString()) {
                 treeItem->setText(2, QChar(0x2713));
                 treeItem->setTextAlignment(2, Qt::AlignCenter);
             }
@@ -233,7 +201,6 @@ void PKGViewer::ProcessPKGInfo() {
             }
         }
     }
-    std::sort(m_full_pkg_list.begin(), m_full_pkg_list.end());
 
     for (int column = 0; column < treeWidget->columnCount() - 2; ++column) {
         // Resize the column to fit its contents
@@ -244,18 +211,4 @@ void PKGViewer::ProcessPKGInfo() {
     int numPkgs = m_pkg_list.size();
     QString statusMessage = QString::number(numPkgs) + " Package.";
     statusBar->showMessage(statusMessage);
-}
-
-QString PKGViewer::GetString(const std::string& key) {
-    if (map_strings.find(key) != map_strings.end()) {
-        return QString::fromStdString(map_strings.at(key));
-    }
-    return "";
-}
-
-u32 PKGViewer::GetInteger(const std::string& key) {
-    if (map_integers.find(key) != map_integers.end()) {
-        return map_integers.at(key);
-    }
-    return 0;
 }
\ No newline at end of file
diff --git a/src/qt_gui/pkg_viewer.h b/src/qt_gui/pkg_viewer.h
index 3be2c526..0e0a8706 100644
--- a/src/qt_gui/pkg_viewer.h
+++ b/src/qt_gui/pkg_viewer.h
@@ -22,33 +22,29 @@
 #include "game_info.h"
 #include "game_list_utils.h"
 #include "gui_context_menus.h"
-#include "gui_settings.h"
 
 class PKGViewer : public QMainWindow {
     Q_OBJECT
 public:
-    explicit PKGViewer(std::shared_ptr<GameInfoClass> game_info_get,
-                       std::shared_ptr<GuiSettings> m_gui_settings,
-                       std::function<void(std::string, int, int)> InstallDragDropPkg = nullptr);
+    explicit PKGViewer(
+        std::shared_ptr<GameInfoClass> game_info_get, QWidget* parent,
+        std::function<void(std::filesystem::path, int, int)> InstallDragDropPkg = nullptr);
     ~PKGViewer();
     void OpenPKGFolder();
     void CheckPKGFolders();
     void ProcessPKGInfo();
-    QString GetString(const std::string& key);
-    u32 GetInteger(const std::string& key);
 
 private:
     GuiContextMenus m_gui_context_menus;
-    PSF psf_;
+    PKG package;
+    PSF psf;
     PKGHeader pkgheader;
     PKGEntry entry;
     PSFHeader header;
     PSFEntry psfentry;
     char pkgTitleID[9];
     std::vector<u8> pkg;
-    std::vector<u8> psf;
     u64 pkgSize = 0;
-    std::shared_ptr<GuiSettings> m_gui_settings_;
     std::unordered_map<std::string, std::string> map_strings;
     std::unordered_map<std::string, u32> map_integers;
 
@@ -58,18 +54,6 @@ private:
     // Status bar
     QStatusBar* statusBar;
 
-    std::vector<std::pair<PKGContentFlag, std::string>> flagNames = {
-        {PKGContentFlag::FIRST_PATCH, "FIRST_PATCH"},
-        {PKGContentFlag::PATCHGO, "PATCHGO"},
-        {PKGContentFlag::REMASTER, "REMASTER"},
-        {PKGContentFlag::PS_CLOUD, "PS_CLOUD"},
-        {PKGContentFlag::GD_AC, "GD_AC"},
-        {PKGContentFlag::NON_GAME, "NON_GAME"},
-        {PKGContentFlag::UNKNOWN_0x8000000, "UNKNOWN_0x8000000"},
-        {PKGContentFlag::SUBSEQUENT_PATCH, "SUBSEQUENT_PATCH"},
-        {PKGContentFlag::DELTA_PATCH, "DELTA_PATCH"},
-        {PKGContentFlag::CUMULATIVE_PATCH, "CUMULATIVE_PATCH"}};
-
     std::vector<std::pair<int, QString>> appTypes = {
         {0, "FULL APP"},
         {1, "UPGRADABLE"},
@@ -77,47 +61,11 @@ private:
         {3, "FREEMIUM"},
     };
 
-    bool isFlagSet(u32_be variable, PKGContentFlag flag) {
-        return (variable) & static_cast<u32>(flag);
-    }
-
-    QString GetRegion(char region) {
-        switch (region) {
-        case 'U':
-            return "USA";
-        case 'E':
-            return "Europe";
-        case 'J':
-            return "Japan";
-        case 'H':
-            return "Asia";
-        case 'I':
-            return "World";
-        default:
-            return "Unknown";
-        }
-    }
-
-    QString GetAppType(int region) {
-        switch (region) {
-        case 0:
-            return "Not Specified";
-        case 1:
-            return "FULL APP";
-        case 2:
-            return "UPGRADABLE";
-        case 3:
-            return "DEMO";
-        case 4:
-            return "FREEMIUM";
-        default:
-            return "Unknown";
-        }
-    }
     QStringList m_full_pkg_list;
     QStringList m_pkg_app_list;
     QStringList m_pkg_patch_list;
     QStringList m_pkg_list;
     QStringList dir_list;
+    std::vector<std::string> dir_list_std;
     QTreeWidget* treeWidget = nullptr;
 };
\ No newline at end of file
diff --git a/src/qt_gui/settings.cpp b/src/qt_gui/settings.cpp
deleted file mode 100644
index b428bcda..00000000
--- a/src/qt_gui/settings.cpp
+++ /dev/null
@@ -1,77 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include "settings.h"
-
-Settings::Settings(QObject* parent) : QObject(parent), m_settings_dir(ComputeSettingsDir()) {}
-
-Settings::~Settings() {
-    if (m_settings) {
-        m_settings->sync();
-    }
-}
-
-QString Settings::GetSettingsDir() const {
-    return m_settings_dir.absolutePath();
-}
-
-QString Settings::ComputeSettingsDir() {
-    return ""; // TODO currently we configure same dir , make it configurable
-}
-
-void Settings::RemoveValue(const QString& key, const QString& name) const {
-    if (m_settings) {
-        m_settings->beginGroup(key);
-        m_settings->remove(name);
-        m_settings->endGroup();
-    }
-}
-
-void Settings::RemoveValue(const GuiSave& entry) const {
-    RemoveValue(entry.key, entry.name);
-}
-
-QVariant Settings::GetValue(const QString& key, const QString& name, const QVariant& def) const {
-    return m_settings ? m_settings->value(key + "/" + name, def) : def;
-}
-
-QVariant Settings::GetValue(const GuiSave& entry) const {
-    return GetValue(entry.key, entry.name, entry.def);
-}
-
-QVariant Settings::List2Var(const q_pair_list& list) {
-    QByteArray ba;
-    QDataStream stream(&ba, QIODevice::WriteOnly);
-    stream << list;
-    return QVariant(ba);
-}
-
-q_pair_list Settings::Var2List(const QVariant& var) {
-    q_pair_list list;
-    QByteArray ba = var.toByteArray();
-    QDataStream stream(&ba, QIODevice::ReadOnly);
-    stream >> list;
-    return list;
-}
-
-void Settings::SetValue(const GuiSave& entry, const QVariant& value) const {
-    if (m_settings) {
-        m_settings->beginGroup(entry.key);
-        m_settings->setValue(entry.name, value);
-        m_settings->endGroup();
-    }
-}
-
-void Settings::SetValue(const QString& key, const QVariant& value) const {
-    if (m_settings) {
-        m_settings->setValue(key, value);
-    }
-}
-
-void Settings::SetValue(const QString& key, const QString& name, const QVariant& value) const {
-    if (m_settings) {
-        m_settings->beginGroup(key);
-        m_settings->setValue(name, value);
-        m_settings->endGroup();
-    }
-}
diff --git a/src/qt_gui/settings.h b/src/qt_gui/settings.h
deleted file mode 100644
index 1e6d1a65..00000000
--- a/src/qt_gui/settings.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#pragma once
-
-#include <memory>
-
-#include <QDir>
-#include <QSettings>
-#include <QSize>
-#include <QVariant>
-
-#include "gui_save.h"
-
-typedef QPair<QString, QString> q_string_pair;
-typedef QPair<QString, QSize> q_size_pair;
-typedef QList<q_string_pair> q_pair_list;
-typedef QList<q_size_pair> q_size_list;
-
-// Parent Class for GUI settings
-class Settings : public QObject {
-    Q_OBJECT
-
-public:
-    explicit Settings(QObject* parent = nullptr);
-    ~Settings();
-
-    QString GetSettingsDir() const;
-
-    QVariant GetValue(const QString& key, const QString& name, const QVariant& def) const;
-    QVariant GetValue(const GuiSave& entry) const;
-    static QVariant List2Var(const q_pair_list& list);
-    static q_pair_list Var2List(const QVariant& var);
-
-public Q_SLOTS:
-    /** Remove entry */
-    void RemoveValue(const QString& key, const QString& name) const;
-    void RemoveValue(const GuiSave& entry) const;
-
-    /** Write value to entry */
-    void SetValue(const GuiSave& entry, const QVariant& value) const;
-    void SetValue(const QString& key, const QVariant& value) const;
-    void SetValue(const QString& key, const QString& name, const QVariant& value) const;
-
-protected:
-    static QString ComputeSettingsDir();
-
-    std::unique_ptr<QSettings> m_settings;
-    QDir m_settings_dir;
-};
\ No newline at end of file
diff --git a/src/qt_gui/trophy_viewer.cpp b/src/qt_gui/trophy_viewer.cpp
new file mode 100644
index 00000000..8c28019e
--- /dev/null
+++ b/src/qt_gui/trophy_viewer.cpp
@@ -0,0 +1,145 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "trophy_viewer.h"
+
+TrophyViewer::TrophyViewer(QString trophyPath, QString gameTrpPath) : QMainWindow() {
+    this->setWindowTitle("Trophy Viewer");
+    this->setAttribute(Qt::WA_DeleteOnClose);
+    tabWidget = new QTabWidget(this);
+    gameTrpPath_ = gameTrpPath;
+    headers << "Trophy"
+            << "Name"
+            << "Description"
+            << "ID"
+            << "Hidden"
+            << "Type"
+            << "PID";
+    PopulateTrophyWidget(trophyPath);
+}
+
+void TrophyViewer::PopulateTrophyWidget(QString title) {
+    QString trophyDir = qApp->applicationDirPath() + "/game_data/" + title + "/TrophyFiles";
+    QDir dir(trophyDir);
+    if (!dir.exists()) {
+        std::filesystem::path path(gameTrpPath_.toStdString());
+#ifdef _WIN64
+        path = std::filesystem::path(gameTrpPath_.toStdWString());
+#endif
+        if (!trp.Extract(path))
+            return;
+    }
+    QFileInfoList dirList = dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot);
+    if (dirList.isEmpty())
+        return;
+
+    for (const QFileInfo& dirInfo : dirList) {
+        QString tabName = dirInfo.fileName();
+        QString trpDir = trophyDir + "/" + tabName;
+
+        QString iconsPath = trpDir + "/Icons";
+        QDir iconsDir(iconsPath);
+        QFileInfoList iconDirList = iconsDir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
+        std::vector<QImage> icons;
+
+        for (const QFileInfo& iconInfo : iconDirList) {
+            QImage icon =
+                QImage(iconInfo.absoluteFilePath())
+                    .scaled(QSize(128, 128), Qt::KeepAspectRatio, Qt::SmoothTransformation);
+            icons.push_back(icon);
+        }
+
+        QStringList trpId;
+        QStringList trpHidden;
+        QStringList trpType;
+        QStringList trpPid;
+        QStringList trophyNames;
+        QStringList trophyDetails;
+
+        QString xmlPath = trpDir + "/Xml/TROP.XML";
+        QFile file(xmlPath);
+        if (!file.open(QFile::ReadOnly | QFile::Text)) {
+            return;
+        }
+
+        QXmlStreamReader reader(&file);
+
+        while (!reader.atEnd() && !reader.hasError()) {
+            reader.readNext();
+            if (reader.isStartElement() && reader.name().toString() == "trophy") {
+                trpId.append(reader.attributes().value("id").toString());
+                trpHidden.append(reader.attributes().value("hidden").toString());
+                trpType.append(reader.attributes().value("ttype").toString());
+                trpPid.append(reader.attributes().value("pid").toString());
+            }
+
+            if (reader.name().toString() == "name" && !trpId.isEmpty()) {
+                trophyNames.append(reader.readElementText());
+            }
+
+            if (reader.name().toString() == "detail" && !trpId.isEmpty()) {
+                trophyDetails.append(reader.readElementText());
+            }
+        }
+        QTableWidget* tableWidget = new QTableWidget(this);
+        tableWidget->setShowGrid(false);
+        tableWidget->setColumnCount(7);
+        tableWidget->setHorizontalHeaderLabels(headers);
+        tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
+        tableWidget->setSelectionMode(QAbstractItemView::SingleSelection);
+        tableWidget->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
+        tableWidget->horizontalHeader()->setStretchLastSection(true);
+        tableWidget->verticalHeader()->setVisible(false);
+        tableWidget->setRowCount(icons.size());
+        for (int row = 0; auto& icon : icons) {
+            QTableWidgetItem* item = new QTableWidgetItem();
+            item->setData(Qt::DecorationRole, icon);
+            item->setFlags(item->flags() & ~Qt::ItemIsEditable);
+            tableWidget->setItem(row, 0, item);
+            if (!trophyNames.isEmpty() && !trophyDetails.isEmpty()) {
+                SetTableItem(tableWidget, row, 1, trophyNames[row]);
+                SetTableItem(tableWidget, row, 2, trophyDetails[row]);
+                SetTableItem(tableWidget, row, 3, trpId[row]);
+                SetTableItem(tableWidget, row, 4, trpHidden[row]);
+                SetTableItem(tableWidget, row, 5, GetTrpType(trpType[row].at(0)));
+                SetTableItem(tableWidget, row, 6, trpPid[row]);
+            }
+            tableWidget->verticalHeader()->resizeSection(row, icon.height());
+            row++;
+        }
+        tableWidget->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
+        int width = 16;
+        for (int i = 0; i < 7; i++) {
+            width += tableWidget->horizontalHeader()->sectionSize(i);
+        }
+        tableWidget->resize(width, 720);
+        tabWidget->addTab(tableWidget,
+                          tabName.insert(6, " ").replace(0, 1, tabName.at(0).toUpper()));
+        this->resize(width + 20, 720);
+    }
+    this->setCentralWidget(tabWidget);
+}
+
+void TrophyViewer::SetTableItem(QTableWidget* parent, int row, int column, QString str) {
+    QWidget* widget = new QWidget();
+    QVBoxLayout* layout = new QVBoxLayout();
+    QLabel* label = new QLabel(str);
+    QTableWidgetItem* item = new QTableWidgetItem();
+    label->setWordWrap(true);
+    label->setStyleSheet("color: white; font-size: 15px; font-weight: bold;");
+
+    // Create shadow effect
+    QGraphicsDropShadowEffect* shadowEffect = new QGraphicsDropShadowEffect();
+    shadowEffect->setBlurRadius(5);               // Set the blur radius of the shadow
+    shadowEffect->setColor(QColor(0, 0, 0, 160)); // Set the color and opacity of the shadow
+    shadowEffect->setOffset(2, 2);                // Set the offset of the shadow
+
+    label->setGraphicsEffect(shadowEffect); // Apply shadow effect to the QLabel
+
+    layout->addWidget(label);
+    if (column != 1 && column != 2)
+        layout->setAlignment(Qt::AlignCenter);
+    widget->setLayout(layout);
+    parent->setItem(row, column, item);
+    parent->setCellWidget(row, column, widget);
+}
\ No newline at end of file
diff --git a/src/qt_gui/trophy_viewer.h b/src/qt_gui/trophy_viewer.h
new file mode 100644
index 00000000..ab79ac50
--- /dev/null
+++ b/src/qt_gui/trophy_viewer.h
@@ -0,0 +1,49 @@
+// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <QApplication>
+#include <QDir>
+#include <QFileInfoList>
+#include <QGraphicsBlurEffect>
+#include <QHeaderView>
+#include <QLabel>
+#include <QMainWindow>
+#include <QStyleOptionViewItem>
+#include <QTableWidget>
+#include <QTableWidgetItem>
+#include <QVBoxLayout>
+#include <QWidget>
+#include <QXmlStreamReader>
+#include "common/types.h"
+#include "core/file_format/trp.h"
+
+class TrophyViewer : public QMainWindow {
+    Q_OBJECT
+public:
+    explicit TrophyViewer(QString trophyPath, QString gameTrpPath);
+
+private:
+    void PopulateTrophyWidget(QString title);
+    void SetTableItem(QTableWidget* parent, int row, int column, QString str);
+
+    QTabWidget* tabWidget = nullptr;
+    QStringList headers;
+    QString gameTrpPath_;
+    TRP trp;
+
+    QString GetTrpType(const QChar trp_) {
+        switch (trp_.toLatin1()) {
+        case 'B':
+            return "Bronze";
+        case 'S':
+            return "Silver";
+        case 'G':
+            return "Gold";
+        case 'P':
+            return "Platinum";
+        }
+        return "Unknown";
+    }
+};
\ No newline at end of file
diff --git a/src/shadps4.qrc b/src/shadps4.qrc
index ac5b2dd6..cdbae786 100644
--- a/src/shadps4.qrc
+++ b/src/shadps4.qrc
@@ -15,5 +15,11 @@
 		<file>images/controller_icon.png</file>
 		<file>images/refresh_icon.png</file>
 		<file>images/list_mode_icon.png</file>
+		<file>images/flag_jp.png</file>
+		<file>images/flag_eu.png</file>
+		<file>images/flag_unk.png</file>
+		<file>images/flag_us.png</file>
+		<file>images/flag_world.png</file>
+		<file>images/flag_china.png</file>
 	</qresource>
 </RCC>