1
0

3 Revīzijas a791a98dd9 ... 017cbea257

Autors SHA1 Ziņojums Datums
  Iñigo Valentin 017cbea257 Fixes in singleton handling. The installer now asks for an ISO image 1 mēnesi atpakaļ
  Iñigo Valentin 20c5ef3900 Upgraded minimun CMake version 1 mēnesi atpakaļ
  Iñigo Valentin 5c571b3389 Improved makefiles 7 mēneši atpakaļ

+ 2 - 0
.gitignore

@@ -12,6 +12,7 @@
 **/ogre.cfg
 **/CMakeCache.txt
 build/
+build-*/
 tools/
 _OLD/
 output/share
@@ -24,3 +25,4 @@ doc/FFVII/RAW/
 .cproject
 .directory
 v-gears-new*.png
+/.vscode/c_cpp_properties.json

+ 41 - 0
CMake/ProjectCommon.cmake

@@ -0,0 +1,41 @@
+# Centralized CMake settings used across the V-Gears project.
+
+# Build verbosity default (can be overridden by caller)
+if(NOT DEFINED CMAKE_VERBOSE_MAKEFILE)
+  set(CMAKE_VERBOSE_MAKEFILE OFF CACHE BOOL "Enable verbose makefile output" FORCE)
+endif()
+
+# C++ standard and basic compile settings
+if(NOT DEFINED CMAKE_CXX_STANDARD)
+  set(CMAKE_CXX_STANDARD 14)
+  set(CMAKE_CXX_STANDARD_REQUIRED ON)
+endif()
+
+# Produce position independent code by default where relevant
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+
+# Debug postfix for libraries
+if(NOT DEFINED CMAKE_DEBUG_POSTFIX)
+  set(CMAKE_DEBUG_POSTFIX "_d")
+endif()
+
+# Set output directories for binaries and libraries
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/lib)
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/lib)
+
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin_debug)
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin_debug/lib)
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin_debug/lib)
+
+# Enable folders in IDEs like Visual Studio
+set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+
+# Make custom CMake modules discoverable
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMake")
+
+# Create directory for generated headers
+file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/generated")
+
+## Helper: install default GNUInstallDirs if not available
+include(GNUInstallDirs OPTIONAL)

+ 1 - 1
CMake/cotire.cmake

@@ -37,7 +37,7 @@ set(__COTIRE_INCLUDED TRUE)
 if (NOT CMAKE_SCRIPT_MODE_FILE)
 	cmake_policy(PUSH)
 endif()
-cmake_minimum_required(VERSION 2.8.12)
+cmake_minimum_required(VERSION 3.5)
 if (NOT CMAKE_SCRIPT_MODE_FILE)
 	cmake_policy(POP)
 endif()

+ 39 - 54
CMakeLists.txt

@@ -1,64 +1,50 @@
-cmake_minimum_required(VERSION 3.0)
+# Minimum required CMake and project declaration
+cmake_minimum_required(VERSION 3.5)
+# Enable both C and CXX languages so C and C++ toolchains are configured.
+project(V-Gears VERSION 0.1.19 LANGUAGES C CXX)
 
+# Provide a central common include for project-wide policies and defaults.
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMake")
+include(ProjectCommon)
 
-# Build verbosity: on / off.
-set(CMAKE_VERBOSE_MAKEFILE off)
-
-
-# C++ Standard.
-set(CMAKE_CXX_STANDARD 14)
-set(CMAKE_CXX_STANDARD_REQUIRED True)
-
-
-# Add cotire
-set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMake")
-include(cotire)
-
-# Project Information.
-project(V-Gears)
-set(CMAKE_PACKAGE_ICON v-gears.png)
-set(VGEARS_VERSION_MAJOR 0)
-set(VGEARS_VERSION_MINOR 1)
-set(VGEARS_VERSION_PATCH 18)
-set(VGEARS_VERSION ${VGEARS_VERSION_MAJOR}.${VGEARS_VERSION_MINOR}.${VGEARS_VERSION_PATCH})
-
-
-# Project options.
+# Configure CMake options
+option(USE_COTIRE "Enable cotire precompiled header support" ON)
 option(BUILD_INSTALLER "Build the V-Gears-Installer" TRUE)
 option(BUILD_TESTS "Build the unit tests" FALSE)
-option(MULTITHREADING "Enable multithreading" FALSE)
-
-
-# Generate version header.
-configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/Version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/src/Version.h) 
-
-
-# Hanle build type
-if ("${CMAKE_BUILD_TYPE}" STREQUAL "")
-    # CMake defaults to leaving CMAKE_BUILD_TYPE empty. This screws up
-    # differentiation between debug and release builds.
-    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: None (CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel." FORCE)
+option(MULTITHREADING "Enable multithreading" TRUE)
+
+# Hint for Qt configuration dirs. Setting this early helps subprojects locate
+# Qt via config-mode packages when installs are in distro-specific locations.
+# Example: -DQT_DIR_HINT=/usr/lib/x86_64-linux-gnu/cmake
+set(QT_DIR_HINT "" CACHE PATH "Optional hint for Qt CMake config directory (parent of Qt5/Qt6)")
+if(QT_DIR_HINT)
+    list(APPEND CMAKE_PREFIX_PATH "${QT_DIR_HINT}")
+    if(EXISTS "${QT_DIR_HINT}/Qt5")
+        set(Qt5_DIR "${QT_DIR_HINT}/Qt5" CACHE PATH "Qt5 dir from hint" FORCE)
+    elseif(EXISTS "${QT_DIR_HINT}/Qt5Core")
+        set(Qt5_DIR "${QT_DIR_HINT}/Qt5Core" CACHE PATH "Qt5 dir from hint" FORCE)
+    endif()
+    if(EXISTS "${QT_DIR_HINT}/Qt6")
+        set(Qt6_DIR "${QT_DIR_HINT}/Qt6" CACHE PATH "Qt6 dir from hint" FORCE)
+    endif()
 endif()
 
+# Optionally enable cotire (precompiled header/speedup).
+if(USE_COTIRE)
+    include(cotire OPTIONAL RESULT_VARIABLE COTIRE_INCLUDE_RESULT)
+    if(NOT COTIRE_INCLUDE_RESULT)
+        message(WARNING "cotire requested but cotire.cmake not found in CMake modules; skipping")
+    endif()
+endif()
+set(CMAKE_PACKAGE_ICON v-gears.png)
 
-#Output directories
-set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin/lib)
-set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin/lib)
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin)
-set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin_debug/lib)
-set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin_debug/lib)
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin_debug)
-
-# Enable Visual Studio solution "folders".
-SET_PROPERTY(GLOBAL PROPERTY USE_FOLDERS ON)
-
+# Generate version header into the build tree so source tree is not modified.
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/Version.h.in ${CMAKE_BINARY_DIR}/generated/Version.h @ONLY)
+include_directories(${CMAKE_BINARY_DIR}/generated)
 
-# Global configurations.
-set(CMAKE_DEBUG_POSTFIX "_d")
-if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
-    set(CMAKE_CURRENT_SOURCE_DIR
-        "${VGEARS_SOURCE_DIR}/data" CACHE PATH "V-Gears install prefix" FORCE
-    )
+# Default build type when none is supplied by the caller.
+if(NOT CMAKE_BUILD_TYPE)
+    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build" FORCE)
 endif()
 
 # First, build external libraries (luabind and luajit).
@@ -72,5 +58,4 @@ if(BUILD_TESTS)
     add_subdirectory(test)
 endif()
 
-
 # TODO: Generate Installer (.msi, .deb, .appImage...)

+ 20 - 0
doc/BUILD.md

@@ -7,6 +7,26 @@ If you are runind Debian (or derivates), you can install the required dependenci
 apt install g++ cmake libqt5widgets5 qtbase5-dev zlib1g-dev libogre-1.12-dev libois-dev libvorbis-dev libboost-dev libboost-program-options-dev libboost-test-dev libboost-filesystem-dev libboost-thread-dev lua5.2 liblua5.2-dev libluabind-dev luajit libopenal-dev libtinyxml-dev
 ```
 
+## Deterministic fallbacks
+
+The CMake scripts include an option `USE_DETERMINISTIC_FALLBACKS` (default `ON`).
+- When `ON`, CMake will attempt pragmatic fallbacks to find libraries when
+	`find_package`/pkg-config do not succeed (SONAME `find_library` passes,
+	appending common `/usr/lib` paths, or injecting generic `-lboost_*` flags).
+- When `OFF`, CMake will not perform these fallbacks; instead require proper
+	dev packages or explicit hints (recommended for CI and packaging).
+
+Disable fallbacks example:
+
+```bash
+cmake .. -DUSE_DETERMINISTIC_FALLBACKS=OFF -DCMAKE_BUILD_TYPE=Release
+```
+
+If you see warnings about missing Boost or OgreBites, prefer installing the
+appropriate `-dev` packages listed above, or point CMake at your OGRE/Boost
+installation with `-DOGRE_ROOT=...` and `-DBoost_DIR=...`/`-DBoost_LIBRARY_DIRS`.
+
+
 ## Build using CMake
 
 From the project directory, run these commands:

+ 23 - 11
lib/CMakeLists.txt

@@ -1,15 +1,27 @@
-include_directories(
-    luabind
-    luajit/src
-    luabind/luabind
-    luabind/luabind/detail
+add_subdirectory(luajit) # LuaJIT has its own configuration.
+
+add_subdirectory(sfxdump) # SFXDump has its own configuration.
+
+# Collect luabind sources and headers explicitly and create a modern target.
+file(GLOB_RECURSE LUABIND_SRC_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+    "luabind/src/*.cpp"
+    "luabind/src/*.c"
+)
+file(GLOB_RECURSE LUABIND_HEADER_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+    "luabind/luabind/*.h"
+    "luabind/luabind/*.hpp"
+    "luabind/luabind/detail/*.h"
+    "luabind/luabind/detail/*.hpp"
 )
 
-add_subdirectory(luajit) # LuaJIT has it's own configuration.
+add_library(libluabind STATIC ${LUABIND_SRC_FILES} ${LUABIND_HEADER_FILES})
 
-add_subdirectory(sfxdump) # SFXDump has it's own configuration.
+target_include_directories(libluabind
+    PUBLIC
+        ${CMAKE_CURRENT_SOURCE_DIR}/luabind
+        ${CMAKE_CURRENT_SOURCE_DIR}/luajit/src
+        ${CMAKE_CURRENT_SOURCE_DIR}/luabind/luabind
+        ${CMAKE_CURRENT_SOURCE_DIR}/luabind/luabind/detail
+)
 
-aux_source_directory(luabind/luabind LUABIND_HEADER_FILES)
-aux_source_directory(luabind/luabind/details LUABIND_HEADER_DETAILS_FILES)
-aux_source_directory(luabind/src LUABIND_SRC_FILES)
-add_library(libluabind STATIC ${LUABIND_SRC_FILES} ${LUABIND_HEADER_FILES} ${LUABIND_HEADER_DETAILS_FILES})
+set_target_properties(libluabind PROPERTIES FOLDER "lib")

+ 5 - 3
lib/luabind/src/class_registry.cpp

@@ -147,9 +147,11 @@ namespace luabind { namespace detail {
 
     void class_registry::add_class(type_id const& info, class_rep* crep)
     {
-        // class is already registered
-        assert((m_classes.find(info) == m_classes.end()) 
-            && "you are trying to register a class twice");
+        // If the class is already registered, don't abort — a plugin or other
+        // component may attempt to register bindings for the same C++ type.
+        if (m_classes.find(info) != m_classes.end()){
+            return;
+        }
         m_classes[info] = crep;
     }
 

+ 1 - 1
lib/luajit/CMakeLists.txt

@@ -16,7 +16,7 @@ project ( luajit C ASM)
 
 set( CMAKE_VERBOSE_MAKEFILE on )
 
-cmake_minimum_required ( VERSION 3.0 )
+cmake_minimum_required ( VERSION 3.5 )
 include ( cmake/dist.cmake )
 include ( lua )
 

+ 795 - 47
src/CMakeLists.txt

@@ -1,4 +1,162 @@
 # Find Ogre module.
+# Auto-detect common OGRE install locations if the user didn't provide one.
+# TODO: Add candidates for other platforms.
+if(NOT DEFINED OGRE_ROOT)
+    set(_OGRE_CANDIDATES
+        "/usr/lib/x86_64-linux-gnu/OGRE"
+        "/usr/lib/OGRE"
+        "/usr/local/lib/OGRE"
+        "/usr/share/OGRE"
+    )
+    foreach(_cand IN LISTS _OGRE_CANDIDATES)
+        if(EXISTS "${_cand}")
+            # If the candidate points to an include directory like
+            # /usr/include/OGRE,set OGRE_ROOT to the system root (two
+            # directories up) so later searches for modules/libraries find it.
+            if(_cand MATCHES "/include/OGRE$" OR _cand MATCHES ".*/OGRE$")
+                get_filename_component(_p "${_cand}" DIRECTORY)
+                get_filename_component(_p2 "${_p}" DIRECTORY)
+                set(OGRE_ROOT "${_p2}")
+            else()
+                set(OGRE_ROOT "${_cand}")
+            endif()
+            message(STATUS "Auto-detected OGRE_ROOT=${OGRE_ROOT}")
+            break()
+        endif()
+    endforeach()
+endif()
+
+
+
+# Explicitly search for other common libraries if CMake find modules failed.
+if(NOT OIS_LIBRARIES)
+    find_library(_ois_lib NAMES OIS OIS-1.3.0 libOIS-1.3.0 libOIS HINTS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib)
+    if(_ois_lib)
+        set(OIS_LIBRARIES ${_ois_lib})
+        message(STATUS "Found OIS library: ${_ois_lib}")
+    endif()
+endif()
+
+# Option: control whether deterministic filesystem fallbacks are allowed.
+# When ON (default), CMake will try SONAME/`/usr/lib` fallbacks and inject
+# generic `-lboost_*` flags as a last resort to improve out-of-tree builds on
+# systems without proper dev packages. Set to OFF to require proper find_package
+# results or user-supplied hints (preferred for packaging/CI).
+option(USE_DETERMINISTIC_FALLBACKS "Enable deterministic filesystem fallbacks for library detection (SONAME and /usr/lib path appends). Toggle off to require proper dev packages or OGRE_ROOT." ON)
+
+
+if(NOT TinyXML_LIBRARIES)
+    find_library(_tinyxml_lib NAMES tinyxml libtinyxml TinyXML TiXml libtinyxml2 tinyxml2 libtinyxml2 HINTS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib)
+    if(_tinyxml_lib)
+        set(TinyXML_LIBRARIES ${_tinyxml_lib})
+        message(STATUS "Found TinyXML library: ${_tinyxml_lib}")
+    endif()
+endif()
+
+if(NOT OPENAL_LIBRARY)
+    find_library(_openal_lib NAMES openal libopenal OpenAL openal32 libopenal.so.1 HINTS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib)
+    if(_openal_lib)
+        set(OPENAL_LIBRARY ${_openal_lib})
+        message(STATUS "Fallback: Found OpenAL library: ${_openal_lib}")
+    endif()
+endif()
+
+if(NOT OGGVORBIS_LIBRARIES)
+    set(_ov)
+    find_library(_vorbisfile_lib NAMES vorbisfile libvorbisfile libvorbisfile.so vorbisfile.so HINTS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib)
+    find_library(_vorbis_lib NAMES vorbis libvorbis libvorbis.so vorbis.so HINTS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib)
+    find_library(_ogg_lib NAMES ogg libogg libogg.so ogg.so HINTS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib)
+    if(_vorbisfile_lib)
+        list(APPEND _ov ${_vorbisfile_lib})
+    endif()
+    if(_vorbis_lib)
+        list(APPEND _ov ${_vorbis_lib})
+    endif()
+    if(_ogg_lib)
+        list(APPEND _ov ${_ogg_lib})
+    endif()
+    if(_ov)
+        set(OGGVORBIS_LIBRARIES ${_ov})
+        message(STATUS "Found Ogg/Vorbis libraries: ${_ov}")
+    endif()
+endif()
+
+# Try to find additional OGRE components if they are present as system libs
+foreach(_comp IN ITEMS OgreOverlay OgreBites OgreRTShaderSystem OgreProperty OgrePaging OgreVolume)
+    find_library(_comp_lib NAMES ${_comp} lib${_comp} ${_comp}.so HINTS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib ${OGRE_ROOT}/lib)
+    if(_comp_lib)
+        list(APPEND OGRE_LIBRARIES ${_comp_lib})
+        message(STATUS "Found OGRE component ${_comp}: ${_comp_lib}")
+    endif()
+endforeach()
+
+# Remove any duplicate entries that may have been appended by fallbacks
+list(REMOVE_DUPLICATES OGRE_LIBRARIES)
+
+# Try pkg-config to find OGRE (if available on the system)
+find_package(PkgConfig QUIET)
+if(PKG_CONFIG_FOUND)
+    pkg_check_modules(_ogre_pkgs QUIET OGRE)
+    if(_ogre_pkgs_FOUND)
+        if(_ogre_pkgs.prefix)
+            if(NOT DEFINED OGRE_ROOT)
+                set(OGRE_ROOT "${_ogre_pkgs.prefix}")
+            endif()
+            message(STATUS "Found OGRE via pkg-config, OGRE_ROOT=${OGRE_ROOT}")
+            # If pkg-config gives a prefix, try to set CMake module path.
+            set(_candidate_cm_path "${_ogre_pkgs.prefix}/CMake")
+            if(EXISTS "${_candidate_cm_path}")
+                set(OGRE_CMAKE_MODULE_PATH "${_candidate_cm_path}")
+            else()
+                set(_candidate_cm_path "${_ogre_pkgs.prefix}/cmake")
+                if(EXISTS "${_candidate_cm_path}")
+                    set(OGRE_CMAKE_MODULE_PATH "${_candidate_cm_path}")
+                endif()
+            endif()
+        endif()
+        # Export include dirs and libraries from pkg-config for later usage
+        set(OGRE_INCLUDE_DIRS ${_ogre_pkgs_INCLUDE_DIRS})
+        # Convert pkg-config library flags (e.g. "-L.. -lOgreMain -lOgreBites")
+        # into a CMake list so target_link_libraries handles them correctly.
+        string(REPLACE " " ";" _pkg_libs_list "${_ogre_pkgs_LIBRARIES}")
+        list(APPEND OGRE_LIBRARIES ${_pkg_libs_list})
+        set(OGRE_PKG_FOUND TRUE)
+    endif()
+endif()
+
+# If OGRE_ROOT is not still yet, try locating Ogre headers or the OgreMain
+# library directly
+if(NOT DEFINED OGRE_ROOT)
+    find_path(_ogre_header Ogre.h HINTS /usr/include/OGRE /usr/include /usr/local/include)
+    if(_ogre_header)
+        # If header was found in /usr/include/OGRE, set OGRE_ROOT to /usr
+        get_filename_component(_hdr_dir "${_ogre_header}" DIRECTORY)
+        if(_hdr_dir MATCHES "/include/OGRE$" OR _hdr_dir MATCHES ".*/OGRE$")
+            get_filename_component(_p "${_hdr_dir}" DIRECTORY)
+            get_filename_component(_p2 "${_p}" DIRECTORY)
+            set(OGRE_ROOT "${_p2}")
+        else()
+            # header found in include root; set OGRE_ROOT to parent
+            get_filename_component(_p "${_hdr_dir}" DIRECTORY)
+            set(OGRE_ROOT "${_p}")
+        endif()
+        message(STATUS "Found Ogre header at ${_ogre_header}; setting OGRE_ROOT=${OGRE_ROOT}")
+    endif()
+endif()
+
+# If OGRE_ROOT is not still yet, try locating Ogre headers or the OgreMain
+# library directly
+if(NOT DEFINED OGRE_ROOT)
+    find_library(_ogre_lib OgreMain HINTS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib)
+    if(_ogre_lib)
+        get_filename_component(_lib_dir "${_ogre_lib}" DIRECTORY)
+        # set OGRE_ROOT two levels up if libs are in /usr/lib/x86_64-linux-gnu
+        get_filename_component(_p "${_lib_dir}" DIRECTORY)
+        get_filename_component(_p2 "${_p}" DIRECTORY)
+        set(OGRE_ROOT "${_p2}")
+        message(STATUS "Found Ogre library ${_ogre_lib}; setting OGRE_ROOT=${OGRE_ROOT}")
+    endif()
+endif()
 set(CMAKE_FRAMEWORK_PATH
     ${CMAKE_FRAMEWORK_PATH}
     ${CMAKE_SOURCE_DIR}/OgreSDK/lib/macosx/Release
@@ -8,34 +166,47 @@ set(OGRE_FRAMEWORK_INCLUDES ${CMAKE_SOURCE_DIR}/OgreSDK/include)
 find_path(OGRE_CMAKE_MODULE_PATH FindOGRE.cmake
     HINTS
     "$ENV{OGRE_HOME}/CMake/"
+    "/usr/lib/x86_64-linux-gnu/OGRE/cmake"
+    "/usr/lib/x86_64-linux-gnu/OGRE"
     "/usr/local/lib/OGRE/cmake"
     "/usr/lib/OGRE/cmake"
     "/usr/share/OGRE/cmake/modules"
     "${CMAKE_CURRENT_SOURCE_DIR}/OgreSDK/CMake"
 )
-if(OGRE_CMAKE_MODULE_PATH-NOTFOUND)
-    message(SEND_ERROR "Failed to find OGRE module path.")
+if(NOT OGRE_CMAKE_MODULE_PATH)
+    # Try additional conventional locations using OGRE_ROOT if set by user or
+    # auto-detection
+    if(DEFINED OGRE_ROOT)
+        find_path(OGRE_CMAKE_MODULE_PATH FindOGRE.cmake HINTS "${OGRE_ROOT}/CMake" "${OGRE_ROOT}/cmake")
+    endif()
+endif()
+if(NOT OGRE_CMAKE_MODULE_PATH)
+    message(WARNING "FindOGRE.cmake not found in expected locations; will try pkg-config or config-mode find_package(OGRE)")
 else()
-    set(CMAKE_MODULE_PATH "${OGRE_CMAKE_MODULE_PATH};${CMAKE_MODULE_PATH}")
+    list(APPEND CMAKE_MODULE_PATH "${OGRE_CMAKE_MODULE_PATH}" "${CMAKE_SOURCE_DIR}/CMake")
 endif()
-set(CMAKE_MODULE_PATH "${OGRE_CMAKE_MODULE_PATH};${CMAKE_MODULE_PATH} ")
-set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_SOURCE_DIR}/CMake")
 
 
 # Find Boost module.
 if (WIN32)
     set(Boost_USE_STATIC_LIBS TRUE)
 else()
-    # Statically linking boost to a dynamic Ogre build doesn't work on Linux 64bit.
+    # Statically linking boost to a dynamic Ogre build doesn't work on Linux
+    # 64bit.
     set(Boost_USE_STATIC_LIBS ${OGRE_STATIC})
 endif ()
 if (MINGW)
-    # This is probably a bug in CMake: the boost find module tries to look for boost libraries with
-    # name libboost_*, but CMake already prefixes library search names with "lib". This is the
-    # workaround.
+    # Ensure OgreBites is linked (provides WindowEventUtilities and related
+    # symbols).
+    find_library(_ogrebites_lib OgreBites HINTS ${OGRE_ROOT} /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib)
+    if(_ogrebites_lib)
+    # This is probably a bug in CMake: the boost find module tries to look for
+    # boost libraries with name libboost_*, but CMake already prefixes library
+    # search names with "lib". This is the workaround.
     set(CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_FIND_LIBRARY_PREFIXES} "")
 endif ()
-# Components that need linking (NB does not include header-only components like bind).
+# Components that need linking (NB does not include header-only components like
+# bind).
 set(OGRE_BOOST_COMPONENTS thread date_time)
 find_package(Boost COMPONENTS ${OGRE_BOOST_COMPONENTS} QUIET)
 if (NOT Boost_FOUND)
@@ -46,22 +217,369 @@ if(Boost_FOUND AND Boost_VERSION GREATER 104900)
     set(OGRE_BOOST_COMPONENTS thread date_time system chrono)
     find_package(Boost COMPONENTS ${OGRE_BOOST_COMPONENTS} QUIET)
 endif()
+
 # Set up referencing of Boost.
 if (NOT APPLE)
     add_definitions(-DBOOST_ALL_NO_LIB)
 endif()
 if (Boost_LIBRARIES STREQUAL "")
-    message(SEND_ERROR "Boost_LIBRARIES is not set")
+    message(STATUS "Boost_LIBRARIES is not set yet; will attempt robust fallbacks later")
+endif()
+
+#message(STATUS "DEBUG: entering Boost detection; BOOST_COMPONENTS=${BOOST_COMPONENTS} BOOST_LINK_LIBS=${BOOST_LINK_LIBS}")
+
+
+# Find packages required for V-Gears.
+# Prefer pkg-config discovery for OGRE (works with system packages). If
+# pkg-config finds OGRE, populate OGRE_INCLUDE_DIRS and OGRE_LIBRARIES and
+# skip find_package(OGRE). Otherwise fall back to the existing FindOGRE logic.
+find_package(PkgConfig QUIET)
+if(NOT DEFINED OGRE_PKG_FOUND)
+    if(PKG_CONFIG_FOUND)
+        # pkg-config already attempted above; if OGRE_PKG_FOUND is set, use it.
+        if(DEFINED _ogre_pkgs_FOUND AND _ogre_pkgs_FOUND)
+            set(OGRE_PKG_FOUND TRUE)
+            # OGRE_INCLUDE_DIRS/OGRE_LIBRARIES were set earlier when
+            # _ogre_pkgs was found.
+        endif()
+    endif()
+endif()
+if(NOT OGRE_PKG_FOUND)
+    find_package(OGRE REQUIRED QUIET)
+else()
+    message(STATUS "Using OGRE from pkg-config: includes=${OGRE_INCLUDE_DIRS}")
 endif()
 
+# If pkg-config was used, try to locate common OGRE component libraries
+if(OGRE_PKG_FOUND)
+    set(_extra_ogre_libs)
+    foreach(_comp IN ITEMS OgreOverlay OgreBites OgreRTShaderSystem)
+        find_library(_lib ${_comp} HINTS ${OGRE_ROOT} /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib PATHS ${OGRE_ROOT}/lib)
+        if(_lib)
+            list(APPEND _extra_ogre_libs ${_lib})
+            message(STATUS "Found OGRE component ${_comp}: ${_lib}")
+        endif()
+    endforeach()
+    if(_extra_ogre_libs)
+        list(APPEND OGRE_LIBRARIES ${_extra_ogre_libs})
+    endif()
+    # Display final OGRE libraries that will be linked
+    message(STATUS "Using OGRE_LIBRARIES: ${OGRE_LIBRARIES}")
+endif()
 
-# Find packacges required for V-Gears.
-find_package(OGRE REQUIRED QUIET)
-find_package(OIS REQUIRED)
-find_package(OpenAL REQUIRED)
-find_package(OggVorbis REQUIRED)
-find_package(TinyXML REQUIRED)
-find_package(Boost COMPONENTS program_options filesystem thread REQUIRED QUIET)
+# Find OIS.
+find_package(OIS QUIET)
+if(NOT OIS_FOUND)
+    find_library(_ois_lib NAMES OIS libOIS ois HINTS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib)
+    if(_ois_lib)
+        set(OIS_LIBRARIES ${_ois_lib})
+        message(STATUS "Found OIS library: ${_ois_lib}")
+    else()
+        message(STATUS "OIS not found by find_package; will attempt to continue and emit clearer diagnostics later.")
+    endif()
+endif()
+
+# Find OpenAL.
+find_package(OpenAL QUIET)
+if(NOT OPENAL_FOUND AND PKG_CONFIG_FOUND)
+    pkg_check_modules(_openal_pkgs QUIET openal)
+    if(_openal_pkgs_FOUND)
+        list(APPEND OPENAL_LIBRARY ${_openal_pkgs_LIBRARIES})
+        list(APPEND OPENAL_INCLUDE_DIR ${_openal_pkgs_INCLUDE_DIRS})
+        message(STATUS "Found OpenAL via pkg-config: ${_openal_pkgs_LIBRARIES}")
+    endif()
+endif()
+if(NOT OPENAL_FOUND AND NOT OPENAL_LIBRARY)
+    find_library(_openal_lib NAMES openal libopenal OpenAL openal32 HINTS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib)
+    if(_openal_lib)
+        set(OPENAL_LIBRARY ${_openal_lib})
+        message(STATUS "Found OpenAL library: ${_openal_lib}")
+    endif()
+endif()
+
+# Find OggVorbis.
+find_package(OggVorbis QUIET)
+if(NOT OGGVORBIS_FOUND)
+    find_library(_vorbisfile_lib NAMES vorbisfile libvorbisfile HINTS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib)
+    find_library(_vorbis_lib NAMES vorbis libvorbis HINTS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib)
+    find_library(_ogg_lib NAMES ogg libogg HINTS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib)
+    set(_ovlibs)
+    if(_vorbisfile_lib)
+        list(APPEND _ovlibs ${_vorbisfile_lib})
+    endif()
+    if(_vorbis_lib)
+        list(APPEND _ovlibs ${_vorbis_lib})
+    endif()
+    if(_ogg_lib)
+        list(APPEND _ovlibs ${_ogg_lib})
+    endif()
+    if(_ovlibs)
+        set(OGGVORBIS_LIBRARIES ${_ovlibs})
+        message(STATUS "Found Ogg/Vorbis libraries: ${_ovlibs}")
+    else()
+        message(STATUS "Ogg/Vorbis not found by find_package; will attempt to continue and emit clearer diagnostics later.")
+    endif()
+endif()
+
+# If the installer is requested, ensure Qt is available. If Qt is missing,
+# disable the installer to avoid compilation failures.
+if(BUILD_INSTALLER)
+    # Optional user-provided hint: allow passing a single path that contains
+    # the Qt CMake config directories (e.g. '/usr/lib/x86_64-linux-gnu/cmake').
+    set(QT_DIR_HINT "" CACHE PATH "Optional hint for Qt CMake config directory (parent of Qt5/Qt6)")
+    if(QT_DIR_HINT)
+        # If user passed a hint, set Qt5_DIR/Qt6_DIR to assist find_package.
+        if(EXISTS "${QT_DIR_HINT}/Qt5")
+            set(Qt5_DIR "${QT_DIR_HINT}/Qt5" CACHE PATH "Qt5 dir from hint" FORCE)
+        elseif(EXISTS "${QT_DIR_HINT}/Qt5Core")
+            set(Qt5_DIR "${QT_DIR_HINT}/Qt5Core" CACHE PATH "Qt5 dir from hint" FORCE)
+        endif()
+        if(EXISTS "${QT_DIR_HINT}/Qt6")
+            set(Qt6_DIR "${QT_DIR_HINT}/Qt6" CACHE PATH "Qt6 dir from hint" FORCE)
+        endif()
+    endif()
+    # Try Qt5 first, then fall back to Qt6. Prefer imported targets for linking.
+    find_package(Qt5 COMPONENTS Core Widgets Gui QUIET)
+    # Debugging: print detected Qt5 variables/targets when present
+    if(TARGET Qt5::Core)
+        message(STATUS "Qt5 imported: target Qt5::Core is available")
+    endif()
+    message(STATUS "Qt5Core_FOUND=${Qt5Core_FOUND} Qt5Widgets_FOUND=${Qt5Widgets_FOUND} Qt5Gui_FOUND=${Qt5Gui_FOUND}")
+    # Some distro setups place Qt config files in nonstandard cmake dirs that
+    # CMake may not probe. If the normal find_package didn't create imported
+    # targets or set FOUND variables, probe common Qt config locations and
+    # retry find_package with explicit hints.
+    if(NOT (TARGET Qt5::Core OR Qt5Core_FOUND))
+        set(_qt_candidate_dirs
+            /usr/lib/x86_64-linux-gnu/cmake/Qt5
+            /usr/lib/x86_64-linux-gnu/cmake/Qt5Core
+            /usr/lib/cmake/Qt5
+            /usr/lib64/cmake/Qt5
+            /usr/local/lib/cmake/Qt5
+            $ENV{HOME}/.local/lib/cmake/Qt5
+        )
+        foreach(_qd IN LISTS _qt_candidate_dirs)
+            if(EXISTS "${_qd}/Qt5Config.cmake" OR EXISTS "${_qd}/Qt5CoreConfig.cmake")
+                message(STATUS "Found Qt5 config file under ${_qd}; retrying find_package(Qt5) with PATHS hint")
+                set(Qt5_DIR "${_qd}" CACHE PATH "Detected Qt5 dir" FORCE)
+                find_package(Qt5 COMPONENTS Core Widgets Gui QUIET PATHS "${_qd}")
+                break()
+            endif()
+        endforeach()
+    endif()
+
+    if(TARGET Qt5::Core OR Qt5Core_FOUND)
+        set(QT_LINK_LIBS Qt5::Core Qt5::Widgets Qt5::Gui)
+        if(DEFINED Qt5Core_INCLUDE_DIRS)
+            set(QT_INCLUDE_DIRS ${Qt5Core_INCLUDE_DIRS})
+        endif()
+        message(STATUS "Found Qt5; installer will be built using Qt5.")
+    else()
+        find_package(Qt6 COMPONENTS Core Widgets Gui QUIET)
+        if(NOT (TARGET Qt6::Core OR Qt6Core_FOUND))
+            # Retry probing common Qt6 cmake locations as well
+            set(_qt6_candidate_dirs
+                /usr/lib/x86_64-linux-gnu/cmake/Qt6
+                /usr/lib/cmake/Qt6
+                /usr/lib64/cmake/Qt6
+                /usr/local/lib/cmake/Qt6
+                $ENV{HOME}/.local/lib/cmake/Qt6
+            )
+            foreach(_qd IN LISTS _qt6_candidate_dirs)
+                if(EXISTS "${_qd}/Qt6Config.cmake" OR EXISTS "${_qd}/Qt6CoreConfig.cmake")
+                    message(STATUS "Found Qt6 config file under ${_qd}; retrying find_package(Qt6) with PATHS hint")
+                    set(Qt6_DIR "${_qd}" CACHE PATH "Detected Qt6 dir" FORCE)
+                    find_package(Qt6 COMPONENTS Core Widgets Gui QUIET PATHS "${_qd}")
+                    break()
+                endif()
+            endforeach()
+        endif()
+        if(TARGET Qt6::Core OR Qt6Core_FOUND)
+            set(QT_LINK_LIBS Qt6::Core Qt6::Widgets Qt6::Gui)
+            set(QT_INCLUDE_DIRS)
+            message(STATUS "Found Qt6; installer will be built using Qt6.")
+        else()
+            # Attempt a pragmatic fallback: search for Qt headers/libs in common
+            # distro include/lib locations and link manually if found. This is
+            # less featureful than using the Qt CMake config (imported targets),
+            # but it helps the installer build on systems where Qt dev packages
+            # are installed but config-mode find_package didn't succeed.
+            set(_qt_fallback_includes)
+            set(_qt_fallback_libs)
+            set(_qt_inc_candidates /usr/include/qt5 /usr/include/x86_64-linux-gnu/qt5 /usr/include)
+            foreach(_inc IN LISTS _qt_inc_candidates)
+                if(EXISTS "${_inc}/QtCore/QtGlobal")
+                    list(APPEND _qt_fallback_includes ${_inc})
+                    message(STATUS "Pragmatic Qt fallback: found headers under ${_inc}")
+                    break()
+                endif()
+            endforeach()
+            # Try to find Qt5 libraries by SONAME/name
+            foreach(_libname IN ITEMS Qt5Core Qt5Widgets Qt5Gui)
+                find_library(_qtl NAMES ${_libname} ${_libname}.so lib${_libname}.so HINTS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib)
+                if(_qtl)
+                    list(APPEND _qt_fallback_libs ${_qtl})
+                    message(STATUS "Pragmatic Qt fallback: found ${_libname}: ${_qtl}")
+                endif()
+            endforeach()
+            if(_qt_fallback_libs AND _qt_fallback_includes)
+                set(QT_LINK_LIBS ${_qt_fallback_libs})
+                set(QT_INCLUDE_DIRS ${_qt_fallback_includes})
+                message(STATUS "Using pragmatic Qt fallback (manual include/libs). Installer will be built.")
+            else()
+                # As a last resort, and only when deterministic fallbacks are
+                # enabled, inject generic Qt5 linker flags and common include
+                # paths so the installer can still be built on Debian/Ubuntu-like
+                # systems where the config-mode package lookup didn't succeed.
+                if(USE_DETERMINISTIC_FALLBACKS)
+                    message(STATUS "Pragmatic Qt fallback failed; using generic Qt -l flags and include paths as last resort")
+                    set(QT_LINK_LIBS -lQt5Core -lQt5Widgets -lQt5Gui)
+                    set(QT_INCLUDE_DIRS /usr/include/qt5 /usr/include/x86_64-linux-gnu/qt5)
+                else()
+                    message(WARNING "BUILD_INSTALLER requested but Qt5/Qt6 not found and pragmatic fallback failed. Disabling installer build. Install Qt dev packages or set BUILD_INSTALLER=OFF.")
+                    set(BUILD_INSTALLER OFF CACHE BOOL "Build the V-Gears-Installer" FORCE)
+                endif()
+            endif()
+        endif()
+    endif()
+endif()
+
+# Find TinyXML
+find_package(TinyXML QUIET)
+if(NOT TinyXML_FOUND)
+    find_library(_tinyxml_lib NAMES tinyxml libtinyxml TiXml TiXmlDocument HINTS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib)
+    if(_tinyxml_lib)
+        set(TinyXML_LIBRARIES ${_tinyxml_lib})
+        message(STATUS "Found TinyXML library: ${_tinyxml_lib}")
+    else()
+        message(STATUS "TinyXML not found by find_package; will attempt to continue and emit clearer diagnostics later.")
+    endif()
+endif()
+
+# Find Boost module with common components and fall back to robust per-component search
+set(BOOST_COMPONENTS program_options filesystem thread system chrono)
+# First try to pick up modern imported targets (Boost::component)
+set(BOOST_LINK_LIBS)
+foreach(_comp IN LISTS BOOST_COMPONENTS)
+    if(TARGET Boost::${_comp})
+        list(APPEND BOOST_LINK_LIBS Boost::${_comp})
+    endif()
+endforeach()
+
+# Next try the classic find_package that populates Boost_LIBRARIES
+if(NOT BOOST_LINK_LIBS)
+    find_package(Boost COMPONENTS ${BOOST_COMPONENTS} QUIET)
+    if(NOT Boost_FOUND)
+        # Try toggling static preference once
+        set(Boost_USE_STATIC_LIBS NOT ${Boost_USE_STATIC_LIBS})
+        find_package(Boost COMPONENTS ${BOOST_COMPONENTS} QUIET)
+    endif()
+    if(Boost_FOUND AND Boost_LIBRARIES)
+        list(APPEND BOOST_LINK_LIBS ${Boost_LIBRARIES})
+        message(STATUS "Found Boost (find_package): ${Boost_VERSION}; libraries: ${Boost_LIBRARIES}")
+    endif()
+endif()
+
+# Final fallback: search for per-component shared objects in common lib dirs
+if(NOT BOOST_LINK_LIBS)
+    foreach(_comp IN LISTS BOOST_COMPONENTS)
+        # Look for any versioned libboost_${comp}*.so* file
+        file(GLOB _found_libs
+            /usr/lib*/libboost_${_comp}*.so*
+            /usr/local/lib*/libboost_${_comp}*.so*
+        )
+        if(_found_libs)
+            # Prefer the first (usually the SONAME symlink), but append all found to be safe
+            list(APPEND BOOST_LINK_LIBS ${_found_libs})
+            message(STATUS "Found boost ${_comp}: ${_found_libs}")
+        else()
+            message(STATUS "Not found: boost ${_comp} not found in standard lib dirs")
+        endif()
+    endforeach()
+    if(NOT BOOST_LINK_LIBS)
+        message(STATUS "No Boost libs found; linking will fail. Install libboost-*-dev packages.")
+    endif()
+endif()
+
+# Try a direct SONAME-style find_library pass for each component (preferred:
+# yields absolute paths like /usr/lib/x86_64-linux-gnu/libboost_program_options.so.1.83.0).
+if(NOT BOOST_LINK_LIBS)
+    foreach(_comp IN LISTS BOOST_COMPONENTS)
+        # try common SONAME/filename variants so find_library returns full paths
+        find_library(_boost_soname
+            NAMES libboost_${_comp}.so libboost_${_comp} boost_${_comp} boost_${_comp}.so
+            HINTS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib
+            PATH_SUFFIXES . lib lib64
+        )
+        if(_boost_soname)
+            list(APPEND BOOST_LINK_LIBS ${_boost_soname})
+            message(STATUS "Found Boost SONAME for ${_comp}: ${_boost_soname}")
+        endif()
+    endforeach()
+    if(NOT BOOST_LINK_LIBS)
+        message(STATUS "SONAME pass did not find Boost libs; falling back to -l flags next.")
+    endif()
+endif()
+
+# If previous attempts didn't produce usable lib paths, fall back to linker flags
+# using common Boost library names so the system linker searches standard paths.
+if(NOT BOOST_LINK_LIBS)
+    if(USE_DETERMINISTIC_FALLBACKS)
+        find_library(_boost_system_lib
+            NAMES boost_system libboost_system
+            HINTS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib
+            PATH_SUFFIXES . lib lib64
+        )
+        set(_generic_boost_flags -lboost_program_options -lboost_filesystem -lboost_thread -lboost_chrono)
+        if(_boost_system_lib)
+            list(APPEND _generic_boost_flags ${_boost_system_lib})
+        else()
+            message(STATUS "Boost.System library not found; omitting boost_system from generic Boost flags.")
+        endif()
+        set(BOOST_LINK_LIBS ${_generic_boost_flags})
+        message(STATUS "Using generic Boost flags: ${BOOST_LINK_LIBS}")
+    else()
+        message(WARNING "Boost libraries not found and USE_DETERMINISTIC_FALLBACKS is OFF. Install libboost-*-dev packages or provide Boost hints (Boost_DIR/Boost_INCLUDE_DIR).")
+    endif()
+endif()
+
+# Deduplicate and export a stable list for later linking
+list(REMOVE_DUPLICATES BOOST_LINK_LIBS)
+message(STATUS "Final BOOST_LINK_LIBS: ${BOOST_LINK_LIBS}")
+
+# Root source dir for V-Gears sources (used for include paths).
+if(NOT DEFINED VGEARS_SOURCE_DIR)
+    set(VGEARS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
+endif()
+
+# Ensure OgreBites (WindowEventUtilities) is explicitly found and linked when
+# available.
+if(NOT OGRE_LIBRARIES)
+    set(OGRE_LIBRARIES)
+endif()
+find_library(_ogrebites_lib NAMES OgreBites libOgreBites OgreBites.so HINTS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib ${OGRE_ROOT}/lib)
+if(_ogrebites_lib)
+    list(FIND OGRE_LIBRARIES ${_ogrebites_lib} _idx)
+    if(_idx EQUAL -1)
+        list(APPEND OGRE_LIBRARIES ${_ogrebites_lib})
+        message(STATUS "Explicitly found OgreBites: ${_ogrebites_lib}")
+    endif()
+else()
+    message(STATUS "OgreBites not found by explicit search; OgreBites symbols may be unresolved.")
+endif()
+
+# As a last-resort deterministic fallback, append the system path if the file
+# exists.
+if(NOT OGRE_LIBRARIES MATCHES ".*OgreBites.*")
+    if(USE_DETERMINISTIC_FALLBACKS)
+        if(EXISTS "/usr/lib/x86_64-linux-gnu/libOgreBites.so")
+            list(APPEND OGRE_LIBRARIES "/usr/lib/x86_64-linux-gnu/libOgreBites.so")
+            message(STATUS "Appended deterministic OgreBites path: /usr/lib/x86_64-linux-gnu/libOgreBites.so")
+        endif()
+    else()
+        message(WARNING "OgreBites not found and USE_DETERMINISTIC_FALLBACKS is OFF. Set OGRE_ROOT or install OGRE dev packages.")
+    endif()
+endif()
 
 
 # Source files for libvgears and v-gears.
@@ -218,23 +736,7 @@ set(VGEARS_SOURCE_FILES
     modules/worldmap/WorldmapModule.cpp
     #viewer/ViewerModule.cpp
 )
-
-
-# Directories for compiling libvgears and v-gears.
-include_directories(
-    ${VGEARS_SOURCE_DIR}
-    ${CMAKE_CURRENT_SOURCE_DIR}
-    ${Boost_INCLUDE_DIR}
-    ${OIS_INCLUDE_DIRS}
-    ${OGRE_INCLUDE_DIRS}
-    ${OGRE_INCLUDE_DIRS}/Overlay
-    ${OGRE_INCLUDE_DIRS}/Bites
-    ${CMAKE_SOURCE_DIR}/lib/luajit
-    ${CMAKE_SOURCE_DIR}/lib/luajit/src
-    ${CMAKE_SOURCE_DIR}/lib/luabind
-    ${CMAKE_SOURCE_DIR}/lib/luabind/luabind/detail
-)
-
+endif()
 
 # Compiler options.
 if(APPLE) # Apple specific options
@@ -252,7 +754,9 @@ if(WIN32) # Check usage and remove if uneccessary
     -Wl,--add-stdcall-alias
     )
 endif()
-if (MSVC) # Build cpp files on all cores, up virtual mem for PCH's as cotire really makes the compiler scream
+if (MSVC)
+    # Build cpp files on all cores, up virtual mem for PCH's as cotire really
+    # makes the compiler scream.
     SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /Zm193")
     add_definitions(-D_CRT_SECURE_NO_WARNINGS)
 endif()
@@ -260,20 +764,155 @@ if (UNIX AND $ENV{COVERAGE}==1) # Coverage options (disables optimization)
       SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0") # debug, no optimisation
       SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") # enabling coverage
 endif()
-SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") # Position Independent Code, required if QT is compiled with -reduce-relocations
-#if(CMAKE_BUILD_TYPE MATCHES "Debug") # Debug build
-    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -rdynamic -O0")
-#endif()
-add_definitions(-DTIXML_USE_STL)
+# Prefer target-level compile definitions and options below (kept conservative).
 
 
 # Generate libvgears.
+if(NOT VGEARS_SOURCE_FILES)
+    file(GLOB_RECURSE VGEARS_SOURCE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*.cpp")
+    list(LENGTH VGEARS_SOURCE_FILES _vgears_auto_count)
+    message(STATUS "Auto-collected VGEARS_SOURCE_FILES count: ${_vgears_auto_count}")
+endif()
+
+# If installer is disabled, remove installer sources from the collected list
+if(NOT BUILD_INSTALLER)
+    set(_removed_count 0)
+    foreach(_f IN LISTS VGEARS_SOURCE_FILES)
+        if(_f MATCHES "installer/")
+            list(REMOVE_ITEM VGEARS_SOURCE_FILES ${_f})
+            math(EXPR _removed_count "${_removed_count} + 1")
+        endif()
+    endforeach()
+    if(_removed_count GREATER 0)
+        message(STATUS "Removed ${_removed_count} installer source(s) because BUILD_INSTALLER=OFF")
+    endif()
+endif()
+
+# Conservative global Qt include probe (ensure headers visible when
+# generating libvgears target). Prefer distro qt5 include locations.
+set(_qt_global_candidates /usr/include/x86_64-linux-gnu/qt5 /usr/include/qt5 /usr/include)
+foreach(_qinc IN LISTS _qt_global_candidates)
+    if(EXISTS "${_qinc}/QtCore/QtGlobal")
+        message(STATUS "Global probe: adding Qt include directory ${_qinc}")
+        include_directories(BEFORE PRIVATE ${_qinc})
+        break()
+    endif()
+endforeach()
+# Ensure UI headers generated by uic are available before compiling
+# libvgears. Some build order setups (unity/PCH/cotire) may attempt to
+# compile sources that include generated `ui_*.h` before uic runs, so add
+# an explicit custom command/target to generate the header.
+set(_ui_src "${CMAKE_CURRENT_SOURCE_DIR}/installer/MainWindow.ui")
+# Place generated headers under the build tree parallel to the source dir
+# CMAKE_CURRENT_BINARY_DIR is <build>/src so append 'installer' (not
+# 'src/installer').
+set(_ui_out "${CMAKE_CURRENT_BINARY_DIR}/installer/ui_MainWindow.h")
+find_program(QT_UIC_EXECUTABLE NAMES uic uic-qt5 PATHS /usr/lib/qt5/bin /usr/bin /usr/local/bin)
+if(QT_UIC_EXECUTABLE AND EXISTS "${_ui_src}")
+    add_custom_command(
+        OUTPUT "${_ui_out}"
+        COMMAND ${QT_UIC_EXECUTABLE} -o "${_ui_out}" "${_ui_src}"
+        DEPENDS "${_ui_src}"
+        COMMENT "Generating ui_MainWindow.h via uic"
+        VERBATIM
+    )
+    add_custom_target(generate_ui_files DEPENDS "${_ui_out}")
+    set(_GENERATED_UI_TARGET generate_ui_files)
+endif()
+
+# Exclude unit tests under installer/decompiler/test from the main
+# library to avoid pulling test-only dependencies (gtest/gmock).
+set(_test_removed_count 0)
+foreach(_f IN LISTS VGEARS_SOURCE_FILES)
+    if(_f MATCHES "installer/decompiler/test/")
+        list(REMOVE_ITEM VGEARS_SOURCE_FILES ${_f})
+        math(EXPR _test_removed_count "${_test_removed_count} + 1")
+    endif()
+endforeach()
+if(_test_removed_count GREATER 0)
+    message(STATUS "Removed ${_test_removed_count} decompiler test source(s) from libvgears")
+endif()
+
 add_library(libvgears STATIC ${VGEARS_SOURCE_FILES})
-cotire(libvgears)
+# Ensure generated ui headers are created before compiling libvgears
+if(DEFINED _GENERATED_UI_TARGET)
+    add_dependencies(libvgears ${_GENERATED_UI_TARGET})
+    # Generated ui headers live in <build>/src/installer
+    target_include_directories(libvgears PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/installer")
+endif()
+
+# Ensure platform macro for scummsys.h selection on Unix
+if(UNIX)
+    target_compile_definitions(libvgears PRIVATE POSIX)
+endif()
+if(USE_COTIRE)
+    cotire(libvgears)
+endif()
 SET_PROPERTY(TARGET libvgears PROPERTY FOLDER "build/libvgears")
 
+# Attach include directories and compile definitions to `libvgears` target.
+target_include_directories(libvgears
+    PUBLIC
+        ${VGEARS_SOURCE_DIR}
+        ${CMAKE_CURRENT_SOURCE_DIR}
+        ${CMAKE_CURRENT_SOURCE_DIR}/installer
+        ${CMAKE_CURRENT_BINARY_DIR}/installer
+        ${Boost_INCLUDE_DIR}
+        ${OIS_INCLUDE_DIRS}
+        ${OGRE_INCLUDE_DIRS}
+        ${OGRE_INCLUDE_DIRS}/Overlay
+        ${OGRE_INCLUDE_DIRS}/Bites
+        ${CMAKE_SOURCE_DIR}/lib/luajit
+        ${CMAKE_SOURCE_DIR}/lib/luajit/src
+        ${CMAKE_SOURCE_DIR}/lib/luabind
+        ${CMAKE_SOURCE_DIR}/lib/luabind/luabind/detail
+        ${QT_INCLUDE_DIRS}
+)
+target_compile_definitions(libvgears PUBLIC TIXML_USE_STL)
+if(NOT MSVC)
+    target_compile_options(libvgears PRIVATE -fPIC)
+endif()
+
+# If Qt link libraries were detected (imported targets or fallback libs), link
+# them to libvgears so imported-target include directories are propagated to
+# installer/source compilation. This ensures Qt headers are available when
+# compiling installer sources into the static lib.
+if(QT_LINK_LIBS)
+    message(STATUS "Linking libvgears with Qt libraries: ${QT_LINK_LIBS}")
+    target_link_libraries(libvgears PUBLIC ${QT_LINK_LIBS})
+endif()
+
+# If QT_LINK_LIBS were provided but QT_INCLUDE_DIRS ended up empty (for example
+# when generic -l flags were injected), try a conservative probe for common Qt
+# include locations and attach them to the target so compiler `-I` flags are
+# present when building installer sources.
+if(QT_LINK_LIBS AND (NOT QT_INCLUDE_DIRS))
+    set(_qt_probe_candidates /usr/include/qt5 /usr/include/x86_64-linux-gnu/qt5 /usr/include)
+    foreach(_qi IN LISTS _qt_probe_candidates)
+        if(EXISTS "${_qi}/QtCore/QtGlobal")
+            message(STATUS "Detected Qt headers under ${_qi}; setting QT_INCLUDE_DIRS")
+            set(QT_INCLUDE_DIRS ${_qi} CACHE PATH "Detected Qt include dir" FORCE)
+            break()
+        endif()
+    endforeach()
+endif()
+
+if(QT_INCLUDE_DIRS)
+    message(STATUS "Adding Qt include dirs to libvgears: ${QT_INCLUDE_DIRS}")
+    target_include_directories(libvgears PUBLIC ${QT_INCLUDE_DIRS})
+endif()
+
 
 # Generate v-gears executable
+# Ensure installer sources are not compiled into the main executable; the
+# installer is built separately under `src/installer`. Remove any installer
+# sources from the source list used for the `v-gears` executable.
+foreach(_f IN LISTS VGEARS_SOURCE_FILES)
+    if(_f MATCHES "^installer/")
+        list(REMOVE_ITEM VGEARS_SOURCE_FILES ${_f})
+    endif()
+endforeach()
+
 add_executable(v-gears ${VGEARS_SOURCE_FILES})
 SET_PROPERTY(TARGET v-gears PROPERTY FOLDER "build/v-gears")
 set(CPACK_PACKAGE_EXECUTABLES v-gears "v-gears")
@@ -294,11 +933,43 @@ set(VGEARS_LIBRARIES
     ${OPENAL_LIBRARY}
     ${OGGVORBIS_LIBRARIES}
     ${OGRE_LIBRARIES}
-    ${Boost_LIBRARIES}
-    Boost::thread
-    ${OIS_LIBRARIES}
-    ${TinyXML_LIBRARIES}
 )
+
+# Ensure we have something for Boost; if BOOST_LINK_LIBS is empty, inject
+# generic `-lboost_*` flags directly so the system linker will attempt to
+# resolve them from standard library search paths.
+if(NOT BOOST_LINK_LIBS)
+    if(USE_DETERMINISTIC_FALLBACKS)
+        find_library(_boost_system_lib
+            NAMES boost_system libboost_system
+            HINTS /usr/lib /usr/lib/x86_64-linux-gnu /usr/local/lib
+            PATH_SUFFIXES . lib lib64
+        )
+        set(_generic_boost_flags -lboost_program_options -lboost_filesystem -lboost_thread -lboost_chrono)
+        if(_boost_system_lib)
+            list(APPEND _generic_boost_flags -lboost_system)
+        endif()
+        set(BOOST_LINK_LIBS ${_generic_boost_flags})
+        message(STATUS "Injecting generic Boost -l flags into link list: ${_generic_boost_flags}")
+    else()
+        message(WARNING "Boost not found and deterministic fallbacks disabled; build may fail. Install libboost-*-dev or provide Boost hints.")
+    endif()
+endif()
+
+# `BOOST_LINK_LIBS` should have been computed earlier (imported targets,
+# find_package, or the robust per-component fallback). Append whatever was found
+# into the final link list for the executable.
+list(APPEND VGEARS_LIBRARIES ${BOOST_LINK_LIBS} ${OIS_LIBRARIES} ${TinyXML_LIBRARIES})
+if(QT_LINK_LIBS)
+    list(APPEND VGEARS_LIBRARIES ${QT_LINK_LIBS})
+endif()
+message(STATUS "Resolved library variables:")
+message(STATUS "  OPENAL_LIBRARY=${OPENAL_LIBRARY}")
+message(STATUS "  OGGVORBIS_LIBRARIES=${OGGVORBIS_LIBRARIES}")
+message(STATUS "  OGRE_LIBRARIES=${OGRE_LIBRARIES}")
+message(STATUS "  Boost_LIBRARIES=${Boost_LIBRARIES}")
+message(STATUS "  OIS_LIBRARIES=${OIS_LIBRARIES}")
+message(STATUS "  TinyXML_LIBRARIES=${TinyXML_LIBRARIES}")
 if(MULTITHREADING)
     find_package (Threads REQUIRED) # Find Threads
     set(VGEARS_LIBRARIES
@@ -306,10 +977,67 @@ if(MULTITHREADING)
         ${CMAKE_THREAD_LIBS_INIT}
     )
 endif()
+message(STATUS "Final VGEARS_LIBRARIES list: ${VGEARS_LIBRARIES}")
+# If OgreBites SONAME exists on the system but wasn't picked up earlier, append
+# it directly to the final link list so WindowEventUtilities symbols are
+#resolved at link time.
+if(USE_DETERMINISTIC_FALLBACKS)
+    if(EXISTS "/usr/lib/x86_64-linux-gnu/libOgreBites.so")
+        list(FIND VGEARS_LIBRARIES "/usr/lib/x86_64-linux-gnu/libOgreBites.so" _found_bites)
+        if(_found_bites EQUAL -1)
+            list(APPEND VGEARS_LIBRARIES "/usr/lib/x86_64-linux-gnu/libOgreBites.so")
+            message(STATUS "Appending system OgreBites to VGEARS_LIBRARIES: /usr/lib/x86_64-linux-gnu/libOgreBites.so")
+        endif()
+    endif()
+else()
+    # When deterministic fallbacks are disabled, warn if OgreBites is likely
+    # missing.
+    if(NOT OGRE_LIBRARIES MATCHES ".*OgreBites.*")
+        message(WARNING "OgreBites not present in OGRE_LIBRARIES and deterministic fallbacks are disabled. Link errors may occur if OGRE dev packages are not installed.")
+    endif()
+endif()
+
 target_link_libraries(v-gears ${VGEARS_LIBRARIES})
 
+# Attach include dirs/defs/options to executable as well (uses same sources).
+target_include_directories(v-gears
+    PRIVATE
+        ${VGEARS_SOURCE_DIR}
+        ${CMAKE_CURRENT_SOURCE_DIR}
+        ${CMAKE_CURRENT_SOURCE_DIR}/installer
+        ${CMAKE_CURRENT_BINARY_DIR}/installer
+        ${Boost_INCLUDE_DIR}
+        ${OIS_INCLUDE_DIRS}
+        ${OGRE_INCLUDE_DIRS}
+        ${OGRE_INCLUDE_DIRS}/Overlay
+        ${OGRE_INCLUDE_DIRS}/Bites
+        ${CMAKE_SOURCE_DIR}/lib/luajit
+        ${CMAKE_SOURCE_DIR}/lib/luajit/src
+        ${CMAKE_SOURCE_DIR}/lib/luabind
+        ${CMAKE_SOURCE_DIR}/lib/luabind/luabind/detail
+    ${QT_INCLUDE_DIRS}
+)
+
+# If UI files are generated by the uic custom target, make sure the executable
+# depends on it and can see the generated headers.
+if(DEFINED _GENERATED_UI_TARGET)
+    add_dependencies(v-gears ${_GENERATED_UI_TARGET})
+    target_include_directories(v-gears PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/installer")
+endif()
+
+# Ensure platform macro for scummsys.h selection on Unix when building the main
+# executable as well (same as done for libvgears).
+if(UNIX)
+    target_compile_definitions(v-gears PRIVATE POSIX)
+endif()
+target_compile_definitions(v-gears PRIVATE TIXML_USE_STL)
+if(NOT MSVC)
+    target_compile_options(v-gears PRIVATE -fPIC)
+endif()
+
 
 # Install v-gears.
+# TODO: Fix routes for installation
 if(WIN32 OR APPLE)
     install(TARGETS v-gears DESTINATION .)
 else()
@@ -317,7 +1045,27 @@ else()
 endif()
 
 
-# If requested, build the installer.
+# If installer build is enabled, ensure Qt includes/links are available for the
+# `installer` subdirectory. Do this before adding the subdirectory so its
+# targets inherit the settings.
 if(BUILD_INSTALLER)
+    # If pragmatic detection earlier set `QT_INCLUDE_DIRS`, expose them as
+    # global include dirs so installer targets find headers.
+    if(QT_INCLUDE_DIRS)
+        include_directories(${QT_INCLUDE_DIRS})
+    else()
+        # Last-resort add common system Qt include paths.
+        if(EXISTS "/usr/include/qt5/QtCore/QDir")
+            include_directories(/usr/include/qt5 /usr/include/x86_64-linux-gnu/qt5)
+            message(STATUS "Added generic Qt include paths: /usr/include/qt5")
+        endif()
+    endif()
+    # Make sure any QT_LINK_LIBS are visible to subdirectories by appending
+    # them to the global link list variable used when creating targets.
+    if(QT_LINK_LIBS)
+        list(APPEND VGEARS_LIBRARIES ${QT_LINK_LIBS})
+    endif()
+
+    message(STATUS "Building installer as requested (BUILD_INSTALLER=ON)")
     add_subdirectory(installer)
 endif()

+ 13 - 0
src/common/VGearsApplication.h

@@ -93,6 +93,19 @@ namespace VGears{
              */
             Ogre::ResourceGroupManager* ResMgr() {return res_mgr_;}
 
+            /**
+             * Re-finalizes singleton registration after full object construction.
+             * 
+             * Call this method immediately after constructing the Application
+             * to ensure the singleton pointer is properly registered after the
+             * object's vptr is fully initialized (avoids UBSAN vptr warnings).
+             * 
+             * @param[in] instance Pointer to the Application instance.
+             */
+            static void FinalizeSingletonRegistration(Application* instance) {
+                msSingleton = instance;
+            }
+
         protected:
 
             /**

+ 8 - 6
src/core/AudioManager.cpp

@@ -279,8 +279,10 @@ const char* AudioManager::ALCError(const ALCdevice* device){
 
 AudioManager::Player::Player(boost::recursive_mutex* mutex):
   loop_(-1.0), vorbis_info_(nullptr), vorbis_section_(0),
-  stream_finished_(false), update_mutex_(mutex)
-{buffer_ = new char[1024 * 96];}
+  stream_finished_(false), update_mutex_(mutex), source_(0), file_()
+{
+    buffer_ = new char[1024 * 96];
+}
 
 AudioManager::Player::~Player(){Stop();}
 
@@ -369,7 +371,7 @@ void AudioManager::Player::SetLoop(const float loop){
 
 void AudioManager::Player::Update(){
     // Try to fill processed buffers
-    int processed;
+    int processed = 0;
     alGetSourcei(source_, AL_BUFFERS_PROCESSED, &processed);
     for(int i = 0; i < processed; ++i){
         // Try to fill buffer
@@ -398,11 +400,11 @@ void AudioManager::Player::Update(){
 
     // Manage source state
     alGetSourcei(source_, AL_BUFFERS_PROCESSED, &processed);
-    int queued;
+    int queued = 0;
     alGetSourcei(source_, AL_BUFFERS_QUEUED, &queued);
     if (stream_finished_ && processed == queued) Stop();
     else{
-        ALenum source_state;
+        ALenum source_state = AL_NONE;
         alGetSourcei(source_, AL_SOURCE_STATE, &source_state);
         if (source_state == AL_STOPPED) alSourcePlay(source_);
     }
@@ -444,7 +446,7 @@ ALsizei AudioManager::Player::FillBuffer(){
 
 float AudioManager::Player::GetPosition(){
     boost::recursive_mutex::scoped_lock lock(*update_mutex_);
-    int play_offset;
+    int play_offset = 0;
     alGetSourcei(source_, AL_SAMPLE_OFFSET, &play_offset);
     return (
       ov_pcm_tell(&vorbis_file_)

+ 15 - 0
src/core/Savemap.cpp

@@ -22,6 +22,21 @@
 #include "core/XmlFile.h"
 #include "common/TypeDefine.h"
 
+constexpr unsigned int Savemap::MAX_CHARACTERS;
+constexpr unsigned int Savemap::MAX_ITEM_SLOTS;
+constexpr unsigned int Savemap::MAX_KEY_ITEM_SLOTS;
+constexpr unsigned int Savemap::MAX_MATERIA_SLOTS;
+constexpr unsigned int Savemap::MAX_STASH_SLOTS;
+constexpr unsigned int Savemap::MAX_EQUIP_SLOTS;
+constexpr unsigned int Savemap::MAX_ENEMY_SKILLS;
+constexpr unsigned int Savemap::MAX_LIMIT_LEVELS;
+constexpr unsigned int Savemap::MAX_LIMIT_TECHNIQUES;
+constexpr unsigned int Savemap::MAX_LIMIT_BAR;
+constexpr unsigned int Savemap::MAX_PARTY_MEMBERS;
+constexpr unsigned int Savemap::BANK_COUNT;
+constexpr unsigned int Savemap::BANK_ADDRESS_COUNT;
+constexpr unsigned int Savemap::MAX_COLOUR;
+
 Savemap::Savemap():
   empty_(true), control_(""), money_(0), seconds_(0), countdown_(0), slot_(-1)
 {

+ 29 - 29
src/core/Savemap.h

@@ -780,42 +780,42 @@ class Savemap{
                 /**
                  * Strength stat.
                  */
-                static const unsigned int STR = 0;
+                static constexpr unsigned int STR = 0;
 
                 /**
                  * Vtatlity stat.
                  */
-                static const unsigned int VIT = 1;
+                static constexpr unsigned int VIT = 1;
 
                 /**
                  * Magic stat.
                  */
-                static const unsigned int MAG = 2;
+                static constexpr unsigned int MAG = 2;
 
                 /**
                  * Spirit stat.
                  */
-                static const unsigned int SPR = 3;
+                static constexpr unsigned int SPR = 3;
 
                 /**
                  * Dexterity stat.
                  */
-                static const unsigned int DEX = 4;
+                static constexpr unsigned int DEX = 4;
 
                 /**
                  * Luck stat.
                  */
-                static const unsigned int LCK = 5;
+                static constexpr unsigned int LCK = 5;
 
                 /**
                  * HP stat.
                  */
-                static const unsigned int HP = 6;
+                static constexpr unsigned int HP = 6;
 
                 /**
                  * MP stat.
                  */
-                static const unsigned int MP = 7;
+                static constexpr unsigned int MP = 7;
         };
 
         /**
@@ -828,22 +828,22 @@ class Savemap{
                 /**
                  * Top left window corner.
                  */
-                static const unsigned int T_L = 0;
+                static constexpr unsigned int T_L = 0;
 
                 /**
                  * Top right window corner.
                  */
-                static const unsigned int T_R = 1;
+                static constexpr unsigned int T_R = 1;
 
                 /**
                  * Bottom right window corner.
                  */
-                static const unsigned int B_R = 2;
+                static constexpr unsigned int B_R = 2;
 
                 /**
                  * Bottom left window corner.
                  */
-                static const unsigned int B_L = 3;
+                static constexpr unsigned int B_L = 3;
         };
 
         /**
@@ -856,17 +856,17 @@ class Savemap{
                 /**
                  * Red colour.
                  */
-                static const unsigned int R = 0;
+                static constexpr unsigned int R = 0;
 
                 /**
                  * Green colour.
                  */
-                static const unsigned int G = 1;
+                static constexpr unsigned int G = 1;
 
                 /**
                  * Blue colour.
                  */
-                static const unsigned int B = 2;
+                static constexpr unsigned int B = 2;
         };
 
     private:
@@ -874,72 +874,72 @@ class Savemap{
         /**
          * Maximum number of characters.
          */
-        static const unsigned int MAX_CHARACTERS = 11;
+        static constexpr unsigned int MAX_CHARACTERS = 11;
 
         /**
          * Maximum number of inventory slots.
          */
-        static const unsigned int MAX_ITEM_SLOTS = 500;
+        static constexpr unsigned int MAX_ITEM_SLOTS = 500;
 
         /**
          * Maximum number of inventory slots for key items.
          */
-        static const unsigned int MAX_KEY_ITEM_SLOTS = 100;
+        static constexpr unsigned int MAX_KEY_ITEM_SLOTS = 100;
 
         /**
          * Maximum number of materia slots.
          */
-        static const unsigned int MAX_MATERIA_SLOTS = 500;
+        static constexpr unsigned int MAX_MATERIA_SLOTS = 500;
 
         /**
          * Maximum number of materia slots in the stash.
          */
-        static const unsigned int MAX_STASH_SLOTS = 500;
+        static constexpr unsigned int MAX_STASH_SLOTS = 500;
 
         /**
          * Maximum number of materia slots in a weapon or armor.
          */
-        static const unsigned int MAX_EQUIP_SLOTS = 10;
+        static constexpr unsigned int MAX_EQUIP_SLOTS = 10;
 
         /**
          * Maximum number of skills in an Enemy Skill materia.
          */
-        static const unsigned int MAX_ENEMY_SKILLS = 32;
+        static constexpr unsigned int MAX_ENEMY_SKILLS = 32;
 
         /**
          * Maximum limit level.
          */
-        static const unsigned int MAX_LIMIT_LEVELS = 4;
+        static constexpr unsigned int MAX_LIMIT_LEVELS = 4;
 
         /**
          * Maximum limit techniques per level.
          */
-        static const unsigned int MAX_LIMIT_TECHNIQUES = 4;
+        static constexpr unsigned int MAX_LIMIT_TECHNIQUES = 4;
 
         /**
          * Level at which the limit level is full.
          */
-        static const unsigned int MAX_LIMIT_BAR = 254;
+        static constexpr unsigned int MAX_LIMIT_BAR = 254;
 
         /**
          * Maximum number of party member.
          */
-        static const unsigned int MAX_PARTY_MEMBERS = 3;
+        static constexpr unsigned int MAX_PARTY_MEMBERS = 3;
 
         /**
          * Number of data banks.
          */
-        static const unsigned int BANK_COUNT = 16;
+        static constexpr unsigned int BANK_COUNT = 16;
 
         /**
          * Number of addresses in each data bank.
          */
-        static const unsigned int BANK_ADDRESS_COUNT = 256;
+        static constexpr unsigned int BANK_ADDRESS_COUNT = 256;
 
         /**
          * MAx colour component value.
          */
-        static const unsigned int MAX_COLOUR = 254;
+        static constexpr unsigned int MAX_COLOUR = 254;
 
 
         /**

+ 5 - 0
src/core/ScriptManager.cpp

@@ -33,6 +33,11 @@ ConfigVar cv_debug_script(
  */
 template<>ScriptManager *Ogre::Singleton<ScriptManager>::msSingleton = nullptr;
 
+/**
+ * Global guard used by ScriptManagerBinds to prevent multiple luabind class registrations.
+ */
+bool g_luabind_binds_registered = false;
+
 Ogre::String script_entity_type[] = {"SYSTEM", "ENTITY", "UI", "BATTLE"};
 
 /**

+ 22 - 2
src/core/ScriptManagerBinds.h

@@ -32,6 +32,11 @@
 #include "XmlMapsFile.h"
 #include "DialogsManager.h"
 #include "TextHandler.h"
+#include <luabind/detail/class_registry.hpp>
+#include <luabind/typeid.hpp>
+
+// Global guard for luabind registrations (defined in ScriptManagerBinds.cpp)
+extern bool g_luabind_binds_registered;
 
 
 /*
@@ -86,8 +91,24 @@ void ScriptConsole(const char* text){
 }
 
 void ScriptManager::InitBinds(){
+  if (g_luabind_binds_registered) return;
+
+  // If luabind has already registered any class in this Lua state (for
+  // example a plugin performed bindings earlier), skip the whole binds
+  // registration to avoid the "register a class twice" assertion.
+  luabind::detail::class_registry* registry = luabind::detail::class_registry::get_registry(lua_state_);
+  if (registry) {
+    if (!registry->get_classes().empty()){
+      std::cerr << "[ScriptManager] InitBinds() skipped: luabind registry already contains classes" << std::endl;
+      g_luabind_binds_registered = true;
+      return;
+    }
+  }
 
-    // Global functions.
+  g_luabind_binds_registered = true;
+
+  std::cerr << "[ScriptManager] InitBinds() starting registration" << std::endl;
+  // Global functions.
     luabind::module(lua_state_)[
         luabind::def("print", (void(*)(const char*)) &ScriptPrint),
         luabind::def("map", (void(*)(const char*)) &ScriptMap),
@@ -101,7 +122,6 @@ void ScriptManager::InitBinds(){
             "set_position",
             (void(Entity::*)(const float, const float, const float)) &Entity::ScriptSetPosition
           )
-          // Internally returns 3 values:
           .def("get_position", (void(Entity::*)()) &Entity::ScriptGetPosition)
           .def("set_rotation", (void(Entity::*)(const float)) &Entity::ScriptSetRotation)
           .def("get_rotation", (float(Entity::*)()) &Entity::ScriptGetRotation)

+ 1 - 1
src/installer/BattleDataInstaller.h

@@ -288,7 +288,7 @@ class BattleDataInstaller{
             std::vector<std::string> a;
 
             /**
-             * List of .a files associated to the model
+             * List of .s files associated to the model
              */
             std::vector<std::string> s;
 

+ 18 - 1
src/installer/CMakeLists.txt

@@ -13,6 +13,7 @@ set(INSTALLER_SOURCE_FILES
     FieldTextWriter.cpp
     KernelDataInstaller.cpp
     MainWindow.cpp
+    Release.cpp
     MediaDataInstaller.cpp
     ModelsAndAnimationsDb.cpp
     ScopedLgp.cpp
@@ -133,13 +134,29 @@ target_link_libraries(v-gears-installer
     libvgears
     Qt5::Widgets
     Qt5::Core
+    archive
     ${OIS_LIBRARIES}
     ${TinyXML_LIBRARIES}
-    ${Boost_LIBRARIES}
+    ${BOOST_LINK_LIBS}
     ${OGRE_LIBRARIES}
     ${ZLIB_LIBRARIES}
 )
 
+# If OgreBites was found by the top-level CMake as a deterministic fallback
+# it may be present in OGRE_LIBRARIES already; ensure it's visible here too.
+if(NOT OGRE_LIBRARIES MATCHES ".*OgreBites.*")
+    if(EXISTS "/usr/lib/x86_64-linux-gnu/libOgreBites.so")
+        list(APPEND OGRE_LIBRARIES "/usr/lib/x86_64-linux-gnu/libOgreBites.so")
+        message(STATUS "Installer CMake: appended deterministic OgreBites path for linker: /usr/lib/x86_64-linux-gnu/libOgreBites.so")
+    endif()
+endif()
+
+# Ensure any newly appended OGRE libraries are linked into the installer target
+if(OGRE_LIBRARIES)
+    message(STATUS "Installer linking additional OGRE libraries: ${OGRE_LIBRARIES}")
+    target_link_libraries(v-gears-installer ${OGRE_LIBRARIES})
+endif()
+
 
 # Install v-gears-installer.
 if(WIN32 OR APPLE)

+ 1 - 1
src/installer/FieldDataInstaller.cpp

@@ -424,7 +424,7 @@ void FieldDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPt
                                         output_dir_ + FIELD_MODELS_DIR + "/" + base_name + ".png",
                                         output_dir_ + FIELD_MODELS_DIR + "/"
                                         + base_mesh_name + "_" + base_name + ".png",
-                                      boost::filesystem::copy_option::overwrite_if_exists
+                                      boost::filesystem::copy_options::overwrite_existing
                                     );
                                     textures.insert(unit->getTextureName());
                                 }

+ 27 - 4
src/installer/MainWindow.cpp

@@ -16,7 +16,6 @@
 #include <iostream>
 #include <sstream>
 #include <iomanip>
-#include <Qt>
 #include <QtCore/QProcess>
 #include <QtWidgets/QFileDialog>
 #include <QtCore/QDir>
@@ -27,12 +26,15 @@
 #include "DataInstaller.h"
 #include "MainWindow.h"
 #include "ui_MainWindow.h"
+#include "Release.h"
 
 /**
  * Indicates if an installer has already been created.
  */
 static bool installer_created = false;
 
+Release release;
+
 MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), main_window_(new Ui::MainWindow){
     main_window_->setupUi(this);
     // Goto the data tab by default.
@@ -134,10 +136,31 @@ void MainWindow::on_btn_vgears_run_clicked(){
 }
 
 void MainWindow::on_btn_data_src_clicked(){
-    QString temp = QFileDialog::getExistingDirectory(
-      this, tr("Location of extracted original game data"), QDir::homePath()
+    const QString filter = tr("ISO images (*.iso)");
+    QString temp = QFileDialog::getOpenFileName(
+      this,
+      tr("Select ISO image"),
+      settings_->value("DataDir").toString(),
+      filter
     );
-    main_window_->line_data_src->setText(temp);
+    std::cout << "Selected ISO: " << temp.toStdString() << std::endl;
+    if (!temp.isNull()){
+        main_window_->line_data_src->setText(temp);
+        release = Release(temp.toStdString());
+        main_window_->isoData->setText(QString::fromStdString(release.getId()));
+        if (!release.isValid()){
+            main_window_->isoError->setStyleSheet("QLabel { color : red; }");
+            main_window_->isoError->setText(QString::fromStdString(release.getErrorMessage()));
+        }
+        else if (!release.isSupported()){
+            main_window_->isoError->setStyleSheet("QLabel { color : orange; }");
+            main_window_->isoError->setText(QString::fromStdString(release.getWarningMessage()));
+        }
+        else{
+            main_window_->isoError->setStyleSheet("QLabel { color : green; }");
+            main_window_->isoError->setText(tr("ISO is valid and supported."));
+        }
+    }
 }
 
 void MainWindow::on_line_data_dst_editingFinished(){

+ 36 - 12
src/installer/MainWindow.ui

@@ -18,7 +18,7 @@
     <item>
      <widget class="QTabWidget" name="tabWidget">
       <property name="tabPosition">
-       <enum>QTabWidget::North</enum>
+       <enum>QTabWidget::TabPosition::North</enum>
       </property>
       <property name="currentIndex">
        <number>0</number>
@@ -29,7 +29,7 @@
        </attribute>
        <layout class="QVBoxLayout" name="verticalLayout_3">
         <item>
-         <layout class="QHBoxLayout" name="horizontalLayout_4">
+         <layout class="QHBoxLayout" name="isoLocator">
           <item>
            <widget class="QLabel" name="label_4">
             <property name="minimumSize">
@@ -45,10 +45,10 @@
              </size>
             </property>
             <property name="text">
-             <string>Original FFVII extracted data:</string>
+             <string>Final Fantasy VII ISO file: </string>
             </property>
             <property name="alignment">
-             <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+             <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
             </property>
            </widget>
           </item>
@@ -93,7 +93,31 @@
          </layout>
         </item>
         <item>
-         <layout class="QHBoxLayout" name="horizontalLayout_3">
+         <layout class="QVBoxLayout" name="isoInfo">
+          <property name="leftMargin">
+           <number>30</number>
+          </property>
+          <item>
+           <widget class="QLabel" name="isoData">
+            <property name="text">
+             <string>ISO Info: </string>
+            </property>
+            <property name="margin">
+             <number>0</number>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QLabel" name="isoError">
+            <property name="text">
+             <string>Message</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+        <item>
+         <layout class="QHBoxLayout" name="output">
           <item>
            <widget class="QLabel" name="label_3">
             <property name="minimumSize">
@@ -112,7 +136,7 @@
              <string>VGears installation directory:</string>
             </property>
             <property name="alignment">
-             <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+             <set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
             </property>
            </widget>
           </item>
@@ -351,10 +375,10 @@
         <item>
          <spacer name="verticalSpacer">
           <property name="orientation">
-           <enum>Qt::Vertical</enum>
+           <enum>Qt::Orientation::Vertical</enum>
           </property>
           <property name="sizeType">
-           <enum>QSizePolicy::Fixed</enum>
+           <enum>QSizePolicy::Policy::Fixed</enum>
           </property>
           <property name="sizeHint" stdset="0">
            <size>
@@ -377,7 +401,7 @@
            <number>0</number>
           </property>
           <property name="alignment">
-           <set>Qt::AlignCenter</set>
+           <set>Qt::AlignmentFlag::AlignCenter</set>
           </property>
           <property name="textVisible">
            <bool>false</bool>
@@ -414,7 +438,7 @@
            <string/>
           </property>
           <property name="alignment">
-           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
           </property>
          </widget>
         </item>
@@ -448,7 +472,7 @@
            <string/>
           </property>
           <property name="alignment">
-           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+           <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
           </property>
          </widget>
         </item>
@@ -483,7 +507,7 @@
            </font>
           </property>
           <property name="verticalScrollBarPolicy">
-           <enum>Qt::ScrollBarAlwaysOn</enum>
+           <enum>Qt::ScrollBarPolicy::ScrollBarAlwaysOn</enum>
           </property>
           <property name="undoRedoEnabled">
            <bool>false</bool>

+ 338 - 0
src/installer/Release.cpp

@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2022 The V-Gears Team
+ *
+ * This file is part of V-Gears
+ *
+ * V-Gears is free software: you can redistribute it and/or modify it under
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, version 3.0 (GPLv3) of the License.
+ *
+ * V-Gears is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <regex>
+#include <archive.h>
+#include <archive_entry.h>
+#include "Release.h"
+
+Release::Release(){
+    id = "";
+    platform = PLATFORM_UNKNOWN;
+    region = REGION_UNKNOWN;
+    language = LANGUAGE_UNKNOWN;
+    disk = DISK_UNKNOWN;
+    valid = false;
+    supported = false;
+    error_message = "";
+    warning_message = "";
+};
+
+
+
+Release::Release(std::string iso_path){
+    Release();
+    struct archive* a = archive_read_new();
+    struct archive_entry* entry;
+    archive_read_support_format_iso9660(a);
+    archive_read_support_format_all(a); // Fail-safe fallback
+
+    if (archive_read_open_filename(a, iso_path.c_str(), 10240) != ARCHIVE_OK) {
+        archive_read_free(a);
+        // Error readding iso
+        error_message = "Failed to read ISO file: " + std::string(archive_error_string(a));
+        return;
+    }
+    std::string game_id = "";
+
+    // Scan headers without extracting data to disk
+    while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
+        std::string path = archive_entry_pathname(entry);
+        
+        // Convert to uppercase to handle varied ISO naming conventions
+        for (char &c : path) c = std::toupper(c);
+
+        // We only care about SYSTEM.CNF at the root
+        if (path == "SYSTEM.CNF" || path == "/SYSTEM.CNF" || path.find("SYSTEM.CNF;") != std::string::npos) {
+            size_t size = archive_entry_size(entry);
+            if (size > 0) {
+                std::vector<char> buffer(size);
+                archive_read_data(a, buffer.data(), size);
+                
+                std::string cnf_content(buffer.begin(), buffer.end());
+                // Get the game ID from the SYSTEM.CNF content using regex
+                // Looks for patterns like SLUS_123.45, SCUS_944.44, SLES_001.23, etc.
+                std::regex code_regex(R"(([A-Z]{4})_(\d{3})\.(\d{2}))");
+                std::smatch match;
+
+                if (std::regex_search(cnf_content, match, code_regex)) {
+                    // match[1] = ABCD, match[2] = 123, match[3] = 45
+                    // Standardize it to the common "ABCD-12345" format
+                    game_id = match[1].str() + "-" + match[2].str() + match[3].str();
+                }
+                else game_id = "";
+            }
+            break; // Found a PC release
+            std::string path = archive_entry_pathname(entry);
+            if (path.find("diski.x") != std::string::npos) {
+                id = "Final Fantasy VII (PC) Disc 1";
+                platform = PLATFORM_PC;
+                region = REGION_UNKNOWN;
+                language = LANGUAGE_UNKNOWN;
+                disk = DISK_1;
+                supported = true;
+                valid = true;
+            }
+            else if (path.find("diskii.x") != std::string::npos) {
+                id = "Final Fantasy VII (PC) Disc 3";
+                platform = PLATFORM_PC;
+                region = REGION_UNKNOWN;
+                language = LANGUAGE_UNKNOWN;
+                disk = DISK_3;
+                supported = true;
+                valid = true;
+            }
+            else if (path.find("diskiii.x") != std::string::npos) {
+                id = "Final Fantasy VII (PC) Disc 3";
+                platform = PLATFORM_PC;
+                region = REGION_UNKNOWN;
+                language = LANGUAGE_UNKNOWN;
+                disk = DISK_3;
+                supported = true;
+                valid = true;
+            }
+            return;
+        }
+    }
+    archive_read_close(a);
+    archive_read_free(a);
+
+    // If it's a PC release, no need to check the game ID further
+    if (platform == PLATFORM_PC) {
+        return;
+    }
+
+    if (game_id.empty()) {
+        error_message = "Game ID not found in ISO file: " + iso_path;
+        return;
+    }
+    
+    if (game_id == "SCUS-94163") {
+        id = "Final Fantasy VII (USA) Disc 1";
+        platform = PLATFORM_PS1;
+        region = REGION_NORTH_AMERICA;
+        language = LANGUAGE_ENGLISH;
+        disk = DISK_1;
+        supported = true;
+        valid = true;
+    }
+    else if (game_id == "SCUS-94164") {
+        id = "Final Fantasy VII (USA) Disc 2";
+        platform = PLATFORM_PS1;
+        region = REGION_NORTH_AMERICA;
+        language = LANGUAGE_ENGLISH;
+        disk = DISK_2;
+        supported = true;
+        valid = true;
+    }
+    else if (game_id == "SCUS-94165") {
+        id = "Final Fantasy VII (USA) Disc 3";
+        platform = PLATFORM_PS1;
+        region = REGION_NORTH_AMERICA;
+        language = LANGUAGE_ENGLISH;
+        disk = DISK_3;
+        supported = true;
+        valid = true;
+    }
+    else if (game_id == "SCES-00867") {
+        id = "Final Fantasy VII (Europe, English) Disc 1";
+        platform = PLATFORM_PS1;
+        region = REGION_EUROPE;
+        language = LANGUAGE_ENGLISH;
+        disk = DISK_1;
+        supported = true;
+        valid = true;
+    }
+    else if (game_id == "SCES-10867") {
+        id = "Final Fantasy VII (Europe, English) Disc 2";
+        platform = PLATFORM_PS1;
+        region = REGION_EUROPE;
+        language = LANGUAGE_ENGLISH;
+        disk = DISK_2;
+        supported = true;
+        valid = true;
+    }
+    else if (game_id == "SCES-20867") {
+        id = "Final Fantasy VII (Europe, English) Disc 3";
+        platform = PLATFORM_PS1;
+        region = REGION_EUROPE;
+        language = LANGUAGE_ENGLISH;
+        disk = DISK_3;
+        supported = true;
+        valid = true;
+    }
+    else if (game_id == "SCES-00868") {
+        id = "Final Fantasy VII (Europe, French) Disc 1";
+        platform = PLATFORM_PS1;
+        region = REGION_EUROPE;
+        language = LANGUAGE_FRENCH;
+        disk = DISK_1;
+        supported = false;
+        warning_message = "Spanish language is not fully supported in V-Gears. Some features may not work as expected.";
+        valid = true;
+    }
+    else if (game_id == "SCES-10868") {
+        id = "Final Fantasy VII (Europe, French) Disc 2";
+        platform = PLATFORM_PS1;
+        region = REGION_EUROPE;
+        language = LANGUAGE_FRENCH;
+        disk = DISK_2;
+        supported = false;
+        warning_message = "Spanish language is not fully supported in V-Gears. Some features may not work as expected.";
+        valid = true;
+    }
+    else if (game_id == "SCES-20868") {
+        id = "Final Fantasy VII (Europe, French) Disc 3";
+        platform = PLATFORM_PS1;
+        region = REGION_EUROPE;
+        language = LANGUAGE_FRENCH;
+        disk = DISK_3;
+        supported = false;
+        warning_message = "Spanish language is not fully supported in V-Gears. Some features may not work as expected.";
+        valid = true;
+    }
+    else if (game_id == "SCES-00900") {
+        id = "Final Fantasy VII (Europe, Spanish) Disc 1";
+        platform = PLATFORM_PS1;
+        region = REGION_EUROPE;
+        language = LANGUAGE_SPANISH;
+        disk = DISK_1;
+        supported = true;supported = false;
+        warning_message = "Spanish language is not fully supported in V-Gears. Some features may not work as expected.";
+        valid = true;
+    }
+    else if (game_id == "SCES-10900") {
+        id = "Final Fantasy VII (Europe, Spanish) Disc 2";
+        platform = PLATFORM_PS1;
+        region = REGION_EUROPE;
+        language = LANGUAGE_SPANISH;
+        disk = DISK_2;
+        supported = false;
+        warning_message = "Spanish language is not fully supported in V-Gears. Some features may not work as expected.";
+        valid = true;
+    }
+    else if (game_id == "SCES-20900") {
+        id = "Final Fantasy VII (Europe, Spanish) Disc 3";
+        platform = PLATFORM_PS1;
+        region = REGION_EUROPE;
+        language = LANGUAGE_SPANISH;
+        disk = DISK_3;
+        supported = false;
+        warning_message = "Spanish language is not fully supported in V-Gears. Some features may not work as expected.";
+        valid = true;
+    }
+    else if (game_id == "SCES-00869") {
+        id = "Final Fantasy VII (Europe, German) Disc 1";
+        platform = PLATFORM_PS1;
+        region = REGION_EUROPE;
+        language = LANGUAGE_GERMAN;
+        disk = DISK_1;
+        supported = true;supported = false;
+        warning_message = "German language is not fully supported in V-Gears. Some features may not work as expected.";
+        valid = true;
+    }
+    else if (game_id == "SCES-10869") {
+        id = "Final Fantasy VII (Europe, German) Disc 2";
+        platform = PLATFORM_PS1;
+        region = REGION_EUROPE;
+        language = LANGUAGE_GERMAN;
+        disk = DISK_2;
+        supported = false;
+        warning_message = "German language is not fully supported in V-Gears. Some features may not work as expected.";
+        valid = true;
+    }
+    else if (game_id == "SCES-20869") {
+        id = "Final Fantasy VII (Europe, German) Disc 3";
+        platform = PLATFORM_PS1;
+        region = REGION_EUROPE;
+        language = LANGUAGE_GERMAN;
+        disk = DISK_3;
+        supported = false;
+        warning_message = "German language is not fully supported in V-Gears. Some features may not work as expected.";
+        valid = true;
+    }
+    else if (game_id == "SCES-00901") {
+        id = "Final Fantasy VII (Europe, Italian) Disc 1";
+        platform = PLATFORM_PS1;
+        region = REGION_EUROPE;
+        language = LANGUAGE_ITALIAN;
+        disk = DISK_1;
+        supported = true;supported = false;
+        warning_message = "Italian language is not fully supported in V-Gears. Some features may not work as expected.";
+        valid = true;
+    }
+    else if (game_id == "SCES-10901") {
+        id = "Final Fantasy VII (Europe, Italian) Disc 2";
+        platform = PLATFORM_PS1;
+        region = REGION_EUROPE;
+        language = LANGUAGE_ITALIAN;
+        disk = DISK_2;
+        supported = false;
+        warning_message = "Italian language is not fully supported in V-Gears. Some features may not work as expected.";
+        valid = true;
+    }
+    else if (game_id == "SCES-20901") {
+        id = "Final Fantasy VII (Europe, Italian) Disc 3";
+        platform = PLATFORM_PS1;
+        region = REGION_EUROPE;
+        language = LANGUAGE_ITALIAN;
+        disk = DISK_3;
+        supported = false;
+        warning_message = "Italian language is not fully supported in V-Gears. Some features may not work as expected.";
+        valid = true;
+    }
+    return;
+};
+
+Release::~Release(){};
+
+std::string Release::getId(){
+    return id;
+};
+
+Release::Platform Release::getPlatform(){
+    return platform;
+};
+
+Release::Region Release::getRegion(){
+    return region;
+};
+
+Release::Language Release::getLanguage(){
+    return language;
+};
+
+Release::Disk Release::getDisk(){
+    return disk;
+};
+
+bool Release::isValid(){
+    return valid;
+};
+
+bool Release::isSupported(){
+    return supported;
+};
+
+std::string Release::getErrorMessage(){
+    return error_message;
+};
+
+std::string Release::getWarningMessage(){
+    return warning_message;
+};

+ 201 - 0
src/installer/Release.h

@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2022 The V-Gears Team
+ *
+ * This file is part of V-Gears
+ *
+ * V-Gears is free software: you can redistribute it and/or modify it under
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, version 3.0 (GPLv3) of the License.
+ *
+ * V-Gears is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#pragma once
+#include <string>
+
+/**
+ * Information about Final Fantasy VII releases.
+ */
+struct Release{
+
+    public:
+
+        /**
+         * The platform for which the release is intended.
+         */
+        enum Platform{
+
+            /** The release is for PC. */
+            PLATFORM_PC,
+            /** The release is for PS1. */
+            PLATFORM_PS1,
+            /** The release is for an unknown platform. */
+            PLATFORM_UNKNOWN
+        };
+
+        /**
+         * The region for which the release is intended.
+         */
+        enum Region{
+            /** The release is for Japan. */
+            REGION_JAPAN,
+            /** The release is for North America. */
+            REGION_NORTH_AMERICA,
+            /** The release is for Europe. */
+            REGION_EUROPE,
+            /** The release is for an unknown region. */
+            REGION_UNKNOWN
+        };
+
+        /**
+         * The language for which the release is intended.
+         */
+        enum Language{
+            /** The release is in Japanese. */
+            LANGUAGE_JAPANESE,
+            /** The release is in English. */
+            LANGUAGE_ENGLISH,
+            /** The release is in French. */
+            LANGUAGE_FRENCH,
+            /** The release is in Spanish. */
+            LANGUAGE_SPANISH,
+            /** The release is in German. */
+            LANGUAGE_GERMAN,
+            /** The release is in Italian. */
+            LANGUAGE_ITALIAN,
+            /** The release is for an unknown language. */
+            LANGUAGE_UNKNOWN
+        };
+
+        /**
+         * The disk for which the release is intended.
+         */
+        enum Disk{
+            /** The release is for disk 1. */
+            DISK_1,
+            /** The release is for disk 2. */
+            DISK_2,
+            /** The release is for disk 3. */
+            DISK_3,
+            /** The release is for disk 4. */
+            DISK_4,
+            /** The release is for an unknown disk. */
+            DISK_UNKNOWN
+        };
+
+        /**
+         * Constructs a new release.
+         */
+        Release();
+
+        /**
+         * Constructs a new release from an ISO path.
+         *
+         * @param iso_path The path to the ISO file.
+         */
+        Release(std::string iso_path);
+
+        /**
+         * Destroys the release.
+         */
+        ~Release();
+
+        /**
+         * Gets the human readable ID of the release.
+         *
+         * @return The ID of the release.
+         */
+        std::string getId();
+
+        /**
+         * Gets the platform for which the release is intended.
+         *
+         * @return The platform of the release.
+         */
+        Platform getPlatform();
+
+        /**
+         * Gets the region for which the release is intended.
+         *
+         * @return The region of the release.
+         */
+        Region getRegion();
+
+        /**
+         * Gets the language for which the release is intended.
+         *
+         * @return The language of the release.
+         */
+        Language getLanguage();
+
+        /**
+         * Gets the disk for which the release is intended.
+         *
+         * @return The disk of the release.
+         */
+        Disk getDisk();
+
+        /**
+         * Checks if the release is a valid Final Fantasy VII release.
+         *
+         * @return True if the release is valid, false otherwise.
+         */
+        bool isValid();
+
+        /**
+         * Gets the error message for the release.
+         *
+         * @return The error message.
+         */
+        std::string getErrorMessage();
+
+        /**
+         * Checks if the release is supported and tested in V-Gears.
+         *
+         * @return True if the release is supported, false otherwise.
+         */
+        bool isSupported();
+
+        /**
+         * Gets a warning in case the release is not fully supported.
+         *
+         * @return The warning message.
+         */
+        std::string getWarningMessage();
+
+    private:
+
+        /** A human readable ID of the release. */
+        std::string id;
+
+        /** The platform for which the release is intended. */
+        Platform platform;
+
+        /** The region for which the release is intended. */
+        Region region;
+
+        /** The language for which the release is intended. */
+        Language language;
+
+        /** The disk for which the release is intended. */
+        Disk disk;
+
+        /** Whether the release is valid. */
+        bool valid;
+
+        /** Whether the release is supported. */
+        bool supported;
+
+        /**
+         * The error message for the release. Empty if no error.
+         */
+        std::string error_message;
+
+        /**
+         * The warning message for the release. Empty if no warning.
+         */
+        std::string warning_message;
+};

+ 1 - 1
src/installer/WorldInstaller.cpp

@@ -729,7 +729,7 @@ void WorldInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPtr &m
                                         output_dir_ + ELEMENT_MODELS_DIR + "/" + base_name + ".png",
                                         output_dir_ + ELEMENT_MODELS_DIR + "/"
                                         + base_mesh_name + "_" + base_name + ".png",
-                                      boost::filesystem::copy_option::overwrite_if_exists
+                                      boost::filesystem::copy_options::overwrite_existing
                                     );
                                     textures.insert(unit->getTextureName());
                                 }

+ 46 - 46
src/installer/decompiler/Function.h

@@ -25,60 +25,60 @@
  */
 struct Function {
 
-        /**
-         * Constructor.
-         *
-         * Required for use with STL, should not be called manually.
-         */
-        Function(): start_addr(0), end_addr(0), num_instructions(0), ret_val(true){}
+    /**
+     * Constructor.
+     *
+     * Required for use with STL, should not be called manually.
+     */
+    Function(): start_addr(0), end_addr(0), num_instructions(0), ret_val(true){}
 
-        /**
-         * Constructor.
-         *
-         * @param[in] start_addr Address of the first instruction in the function.
-         * @param[in] end_addr Address of the last instruction in the function
-         */
-        Function(uint32 start_addr, uint32 end_addr):
-          start_addr(start_addr), end_addr(end_addr), num_instructions(0), ret_val(true){}
+    /**
+     * Constructor.
+     *
+     * @param[in] start_addr Address of the first instruction in the function.
+     * @param[in] end_addr Address of the last instruction in the function
+     */
+    Function(uint32 start_addr, uint32 end_addr):
+      start_addr(start_addr), end_addr(end_addr), num_instructions(0), ret_val(true){}
 
-        /**
-         * The function starting address.
-         */
-        uint32 start_addr;
+    /**
+     * The function starting address.
+     */
+    uint32 start_addr;
 
-        /**
-         * The function ending address.
-         */
-        uint32 end_addr;
+    /**
+     * The function ending address.
+     */
+    uint32 end_addr;
 
-        /**
-         * Number of instructions in the function.
-         */
-        uint32 num_instructions;
+    /**
+     * Number of instructions in the function.
+     */
+    uint32 num_instructions;
 
-        /**
-         * The name of the function.
-         */
-        std::string name;
+    /**
+     * The name of the function.
+     */
+    std::string name;
 
-        /**
-         * The function vertex.
-         */
-        GraphVertex vertex;
+    /**
+     * The function vertex.
+     */
+    GraphVertex vertex;
 
-        /**
-         * Number of arguments in the function.
-         */
-        uint32 num_args;
+    /**
+     * Number of arguments in the function.
+     */
+    uint32 num_args;
 
-        /**
-         * Return value of the function.
-         */
-        bool ret_val;
+    /**
+     * Return value of the function.
+     */
+    bool ret_val;
 
-        /**
-         * Metadata for code generation.
-         */
-        std::string metadata;
+    /**
+     * Metadata for code generation.
+     */
+    std::string metadata;
 
 };

+ 4 - 0
src/installer/decompiler/scummv6/engine.h

@@ -0,0 +1,4 @@
+// Compatibility shim: some test sources include "decompiler/scummv6/engine.h"
+// Provide a simple wrapper that includes the existing Engine.h in this tree.
+#pragma once
+#include "../Engine.h"

+ 589 - 0
src/installer/ui_MainWindow.h

@@ -0,0 +1,589 @@
+/********************************************************************************
+** Form generated from reading UI file 'MainWindow.ui'
+**
+** Created by: Qt User Interface Compiler version 5.15.17
+**
+** WARNING! All changes made in this file will be lost when recompiling UI file!
+********************************************************************************/
+
+#ifndef UI_MAINWINDOW_H
+#define UI_MAINWINDOW_H
+
+#include <QtCore/QVariant>
+#include <QtGui/QIcon>
+#include <QtWidgets/QAction>
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QCheckBox>
+#include <QtWidgets/QGroupBox>
+#include <QtWidgets/QHBoxLayout>
+#include <QtWidgets/QLabel>
+#include <QtWidgets/QLineEdit>
+#include <QtWidgets/QMainWindow>
+#include <QtWidgets/QMenu>
+#include <QtWidgets/QMenuBar>
+#include <QtWidgets/QProgressBar>
+#include <QtWidgets/QPushButton>
+#include <QtWidgets/QSpacerItem>
+#include <QtWidgets/QTabWidget>
+#include <QtWidgets/QTextEdit>
+#include <QtWidgets/QVBoxLayout>
+#include <QtWidgets/QWidget>
+
+QT_BEGIN_NAMESPACE
+
+class Ui_MainWindow
+{
+public:
+    QAction *actionExit;
+    QWidget *centralWidget;
+    QVBoxLayout *verticalLayout;
+    QTabWidget *tabWidget;
+    QWidget *tab_installer;
+    QVBoxLayout *verticalLayout_3;
+    QHBoxLayout *isoLocator;
+    QLabel *label_4;
+    QLineEdit *line_data_src;
+    QPushButton *btn_data_src;
+    QPushButton *help_data_src;
+    QVBoxLayout *isoInfo;
+    QLabel *isoData;
+    QLabel *isoError;
+    QHBoxLayout *output;
+    QLabel *label_3;
+    QLineEdit *line_data_dst;
+    QPushButton *btn_data_dst;
+    QPushButton *help_data_dest;
+    QCheckBox *chk_advanced_options;
+    QGroupBox *advancedOptions;
+    QVBoxLayout *verticalLayout_5;
+    QHBoxLayout *horizontalLayout_6;
+    QCheckBox *chk_no_battle_data;
+    QCheckBox *chk_no_battle_models;
+    QHBoxLayout *horizontalLayout_61;
+    QCheckBox *chk_no_kernel;
+    QCheckBox *chk_no_images;
+    QHBoxLayout *horizontalLayout_7;
+    QCheckBox *chk_no_sounds;
+    QCheckBox *chk_no_music;
+    QHBoxLayout *horizontalLayout_8;
+    QCheckBox *chk_no_fields;
+    QCheckBox *chk_no_field_models;
+    QHBoxLayout *horizontalLayout_9;
+    QCheckBox *chk_no_wm;
+    QCheckBox *chk_no_wm_models;
+    QHBoxLayout *horizontalLayout_5;
+    QCheckBox *chk_no_ffmpeg;
+    QCheckBox *chk_no_timidity;
+    QHBoxLayout *horizontalLayout_91;
+    QCheckBox *chk_keep_original;
+    QSpacerItem *verticalSpacer;
+    QPushButton *btn_data_run;
+    QProgressBar *data_progress_bar;
+    QLabel *label_percent;
+    QLabel *label_progress;
+    QLabel *label_5;
+    QTextEdit *data_log;
+    QWidget *tab_vgears;
+    QVBoxLayout *verticalLayout_2;
+    QHBoxLayout *horizontalLayout;
+    QLabel *label_2;
+    QLineEdit *line_vgears_exe;
+    QPushButton *btn_vgears_exe;
+    QHBoxLayout *horizontalLayout_2;
+    QLabel *label;
+    QLineEdit *line_vgears_config;
+    QPushButton *btn_vgears_config;
+    QPushButton *btn_vgears_launch;
+    QMenuBar *menuBar;
+    QMenu *menuFile;
+
+    void setupUi(QMainWindow *MainWindow)
+    {
+        if (MainWindow->objectName().isEmpty())
+            MainWindow->setObjectName(QString::fromUtf8("MainWindow"));
+        MainWindow->resize(720, 703);
+        actionExit = new QAction(MainWindow);
+        actionExit->setObjectName(QString::fromUtf8("actionExit"));
+        centralWidget = new QWidget(MainWindow);
+        centralWidget->setObjectName(QString::fromUtf8("centralWidget"));
+        verticalLayout = new QVBoxLayout(centralWidget);
+        verticalLayout->setSpacing(6);
+        verticalLayout->setContentsMargins(11, 11, 11, 11);
+        verticalLayout->setObjectName(QString::fromUtf8("verticalLayout"));
+        tabWidget = new QTabWidget(centralWidget);
+        tabWidget->setObjectName(QString::fromUtf8("tabWidget"));
+        tabWidget->setTabPosition(QTabWidget::TabPosition::North);
+        tab_installer = new QWidget();
+        tab_installer->setObjectName(QString::fromUtf8("tab_installer"));
+        verticalLayout_3 = new QVBoxLayout(tab_installer);
+        verticalLayout_3->setSpacing(6);
+        verticalLayout_3->setContentsMargins(11, 11, 11, 11);
+        verticalLayout_3->setObjectName(QString::fromUtf8("verticalLayout_3"));
+        isoLocator = new QHBoxLayout();
+        isoLocator->setSpacing(6);
+        isoLocator->setObjectName(QString::fromUtf8("isoLocator"));
+        label_4 = new QLabel(tab_installer);
+        label_4->setObjectName(QString::fromUtf8("label_4"));
+        label_4->setMinimumSize(QSize(200, 0));
+        label_4->setMaximumSize(QSize(200, 50));
+        label_4->setAlignment(Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter);
+
+        isoLocator->addWidget(label_4);
+
+        line_data_src = new QLineEdit(tab_installer);
+        line_data_src->setObjectName(QString::fromUtf8("line_data_src"));
+
+        isoLocator->addWidget(line_data_src);
+
+        btn_data_src = new QPushButton(tab_installer);
+        btn_data_src->setObjectName(QString::fromUtf8("btn_data_src"));
+        btn_data_src->setMaximumSize(QSize(24, 24));
+
+        isoLocator->addWidget(btn_data_src);
+
+        help_data_src = new QPushButton(tab_installer);
+        help_data_src->setObjectName(QString::fromUtf8("help_data_src"));
+        QIcon icon;
+        QString iconThemeName = QString::fromUtf8("dialog-information");
+        if (QIcon::hasThemeIcon(iconThemeName)) {
+            icon = QIcon::fromTheme(iconThemeName);
+        } else {
+            icon.addFile(QString::fromUtf8("Workspace/Software/V-Gears/V-Gears-Installer/src"), QSize(), QIcon::Normal, QIcon::Off);
+        }
+        help_data_src->setIcon(icon);
+        help_data_src->setCheckable(false);
+        help_data_src->setFlat(true);
+
+        isoLocator->addWidget(help_data_src);
+
+
+        verticalLayout_3->addLayout(isoLocator);
+
+        isoInfo = new QVBoxLayout();
+        isoInfo->setSpacing(6);
+        isoInfo->setObjectName(QString::fromUtf8("isoInfo"));
+        isoData = new QLabel(tab_installer);
+        isoData->setObjectName(QString::fromUtf8("isoData"));
+
+        isoInfo->addWidget(isoData);
+
+        isoError = new QLabel(tab_installer);
+        isoError->setObjectName(QString::fromUtf8("isoError"));
+
+        isoInfo->addWidget(isoError);
+
+
+        verticalLayout_3->addLayout(isoInfo);
+
+        output = new QHBoxLayout();
+        output->setSpacing(6);
+        output->setObjectName(QString::fromUtf8("output"));
+        label_3 = new QLabel(tab_installer);
+        label_3->setObjectName(QString::fromUtf8("label_3"));
+        label_3->setMinimumSize(QSize(200, 0));
+        label_3->setMaximumSize(QSize(200, 50));
+        label_3->setAlignment(Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter);
+
+        output->addWidget(label_3);
+
+        line_data_dst = new QLineEdit(tab_installer);
+        line_data_dst->setObjectName(QString::fromUtf8("line_data_dst"));
+
+        output->addWidget(line_data_dst);
+
+        btn_data_dst = new QPushButton(tab_installer);
+        btn_data_dst->setObjectName(QString::fromUtf8("btn_data_dst"));
+        btn_data_dst->setMaximumSize(QSize(24, 24));
+
+        output->addWidget(btn_data_dst);
+
+        help_data_dest = new QPushButton(tab_installer);
+        help_data_dest->setObjectName(QString::fromUtf8("help_data_dest"));
+        help_data_dest->setIcon(icon);
+        help_data_dest->setCheckable(false);
+        help_data_dest->setFlat(true);
+
+        output->addWidget(help_data_dest);
+
+
+        verticalLayout_3->addLayout(output);
+
+        chk_advanced_options = new QCheckBox(tab_installer);
+        chk_advanced_options->setObjectName(QString::fromUtf8("chk_advanced_options"));
+
+        verticalLayout_3->addWidget(chk_advanced_options);
+
+        advancedOptions = new QGroupBox(tab_installer);
+        advancedOptions->setObjectName(QString::fromUtf8("advancedOptions"));
+        QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+        sizePolicy.setHorizontalStretch(0);
+        sizePolicy.setVerticalStretch(0);
+        sizePolicy.setHeightForWidth(advancedOptions->sizePolicy().hasHeightForWidth());
+        advancedOptions->setSizePolicy(sizePolicy);
+        advancedOptions->setMaximumSize(QSize(16777215, 0));
+        verticalLayout_5 = new QVBoxLayout(advancedOptions);
+        verticalLayout_5->setSpacing(5);
+        verticalLayout_5->setContentsMargins(11, 11, 11, 11);
+        verticalLayout_5->setObjectName(QString::fromUtf8("verticalLayout_5"));
+        verticalLayout_5->setContentsMargins(11, -1, -1, -1);
+        horizontalLayout_6 = new QHBoxLayout();
+        horizontalLayout_6->setSpacing(6);
+        horizontalLayout_6->setObjectName(QString::fromUtf8("horizontalLayout_6"));
+        chk_no_battle_data = new QCheckBox(advancedOptions);
+        chk_no_battle_data->setObjectName(QString::fromUtf8("chk_no_battle_data"));
+
+        horizontalLayout_6->addWidget(chk_no_battle_data);
+
+        chk_no_battle_models = new QCheckBox(advancedOptions);
+        chk_no_battle_models->setObjectName(QString::fromUtf8("chk_no_battle_models"));
+
+        horizontalLayout_6->addWidget(chk_no_battle_models);
+
+
+        verticalLayout_5->addLayout(horizontalLayout_6);
+
+        horizontalLayout_61 = new QHBoxLayout();
+        horizontalLayout_61->setSpacing(6);
+        horizontalLayout_61->setObjectName(QString::fromUtf8("horizontalLayout_61"));
+        chk_no_kernel = new QCheckBox(advancedOptions);
+        chk_no_kernel->setObjectName(QString::fromUtf8("chk_no_kernel"));
+
+        horizontalLayout_61->addWidget(chk_no_kernel);
+
+        chk_no_images = new QCheckBox(advancedOptions);
+        chk_no_images->setObjectName(QString::fromUtf8("chk_no_images"));
+
+        horizontalLayout_61->addWidget(chk_no_images);
+
+
+        verticalLayout_5->addLayout(horizontalLayout_61);
+
+        horizontalLayout_7 = new QHBoxLayout();
+        horizontalLayout_7->setSpacing(6);
+        horizontalLayout_7->setObjectName(QString::fromUtf8("horizontalLayout_7"));
+        chk_no_sounds = new QCheckBox(advancedOptions);
+        chk_no_sounds->setObjectName(QString::fromUtf8("chk_no_sounds"));
+
+        horizontalLayout_7->addWidget(chk_no_sounds);
+
+        chk_no_music = new QCheckBox(advancedOptions);
+        chk_no_music->setObjectName(QString::fromUtf8("chk_no_music"));
+
+        horizontalLayout_7->addWidget(chk_no_music);
+
+
+        verticalLayout_5->addLayout(horizontalLayout_7);
+
+        horizontalLayout_8 = new QHBoxLayout();
+        horizontalLayout_8->setSpacing(6);
+        horizontalLayout_8->setObjectName(QString::fromUtf8("horizontalLayout_8"));
+        chk_no_fields = new QCheckBox(advancedOptions);
+        chk_no_fields->setObjectName(QString::fromUtf8("chk_no_fields"));
+
+        horizontalLayout_8->addWidget(chk_no_fields);
+
+        chk_no_field_models = new QCheckBox(advancedOptions);
+        chk_no_field_models->setObjectName(QString::fromUtf8("chk_no_field_models"));
+
+        horizontalLayout_8->addWidget(chk_no_field_models);
+
+
+        verticalLayout_5->addLayout(horizontalLayout_8);
+
+        horizontalLayout_9 = new QHBoxLayout();
+        horizontalLayout_9->setSpacing(6);
+        horizontalLayout_9->setObjectName(QString::fromUtf8("horizontalLayout_9"));
+        chk_no_wm = new QCheckBox(advancedOptions);
+        chk_no_wm->setObjectName(QString::fromUtf8("chk_no_wm"));
+
+        horizontalLayout_9->addWidget(chk_no_wm);
+
+        chk_no_wm_models = new QCheckBox(advancedOptions);
+        chk_no_wm_models->setObjectName(QString::fromUtf8("chk_no_wm_models"));
+
+        horizontalLayout_9->addWidget(chk_no_wm_models);
+
+
+        verticalLayout_5->addLayout(horizontalLayout_9);
+
+        horizontalLayout_5 = new QHBoxLayout();
+        horizontalLayout_5->setSpacing(6);
+        horizontalLayout_5->setObjectName(QString::fromUtf8("horizontalLayout_5"));
+        chk_no_ffmpeg = new QCheckBox(advancedOptions);
+        chk_no_ffmpeg->setObjectName(QString::fromUtf8("chk_no_ffmpeg"));
+
+        horizontalLayout_5->addWidget(chk_no_ffmpeg);
+
+        chk_no_timidity = new QCheckBox(advancedOptions);
+        chk_no_timidity->setObjectName(QString::fromUtf8("chk_no_timidity"));
+
+        horizontalLayout_5->addWidget(chk_no_timidity);
+
+
+        verticalLayout_5->addLayout(horizontalLayout_5);
+
+        horizontalLayout_91 = new QHBoxLayout();
+        horizontalLayout_91->setSpacing(6);
+        horizontalLayout_91->setObjectName(QString::fromUtf8("horizontalLayout_91"));
+        chk_keep_original = new QCheckBox(advancedOptions);
+        chk_keep_original->setObjectName(QString::fromUtf8("chk_keep_original"));
+
+        horizontalLayout_91->addWidget(chk_keep_original);
+
+
+        verticalLayout_5->addLayout(horizontalLayout_91);
+
+
+        verticalLayout_3->addWidget(advancedOptions);
+
+        verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Policy::Fixed, QSizePolicy::Minimum);
+
+        verticalLayout_3->addItem(verticalSpacer);
+
+        btn_data_run = new QPushButton(tab_installer);
+        btn_data_run->setObjectName(QString::fromUtf8("btn_data_run"));
+
+        verticalLayout_3->addWidget(btn_data_run);
+
+        data_progress_bar = new QProgressBar(tab_installer);
+        data_progress_bar->setObjectName(QString::fromUtf8("data_progress_bar"));
+        data_progress_bar->setValue(0);
+        data_progress_bar->setAlignment(Qt::AlignmentFlag::AlignCenter);
+        data_progress_bar->setTextVisible(false);
+
+        verticalLayout_3->addWidget(data_progress_bar);
+
+        label_percent = new QLabel(tab_installer);
+        label_percent->setObjectName(QString::fromUtf8("label_percent"));
+        QSizePolicy sizePolicy1(QSizePolicy::Expanding, QSizePolicy::Fixed);
+        sizePolicy1.setHorizontalStretch(1);
+        sizePolicy1.setVerticalStretch(0);
+        sizePolicy1.setHeightForWidth(label_percent->sizePolicy().hasHeightForWidth());
+        label_percent->setSizePolicy(sizePolicy1);
+        label_percent->setMinimumSize(QSize(0, 0));
+        label_percent->setMaximumSize(QSize(16777215, 10));
+        QFont font;
+        font.setPointSize(8);
+        font.setItalic(true);
+        label_percent->setFont(font);
+        label_percent->setAlignment(Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter);
+
+        verticalLayout_3->addWidget(label_percent);
+
+        label_progress = new QLabel(tab_installer);
+        label_progress->setObjectName(QString::fromUtf8("label_progress"));
+        sizePolicy1.setHeightForWidth(label_progress->sizePolicy().hasHeightForWidth());
+        label_progress->setSizePolicy(sizePolicy1);
+        label_progress->setMinimumSize(QSize(0, 0));
+        label_progress->setMaximumSize(QSize(16777215, 10));
+        label_progress->setFont(font);
+        label_progress->setAlignment(Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter);
+
+        verticalLayout_3->addWidget(label_progress);
+
+        label_5 = new QLabel(tab_installer);
+        label_5->setObjectName(QString::fromUtf8("label_5"));
+        label_5->setMaximumSize(QSize(16777215, 15));
+        QFont font1;
+        font1.setFamily(QString::fromUtf8("Courier"));
+        font1.setPointSize(8);
+        label_5->setFont(font1);
+        label_5->setMargin(-1);
+
+        verticalLayout_3->addWidget(label_5);
+
+        data_log = new QTextEdit(tab_installer);
+        data_log->setObjectName(QString::fromUtf8("data_log"));
+        data_log->setFont(font1);
+        data_log->setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOn);
+        data_log->setUndoRedoEnabled(false);
+        data_log->setReadOnly(true);
+
+        verticalLayout_3->addWidget(data_log);
+
+        tabWidget->addTab(tab_installer, QString());
+        tab_vgears = new QWidget();
+        tab_vgears->setObjectName(QString::fromUtf8("tab_vgears"));
+        verticalLayout_2 = new QVBoxLayout(tab_vgears);
+        verticalLayout_2->setSpacing(6);
+        verticalLayout_2->setContentsMargins(11, 11, 11, 11);
+        verticalLayout_2->setObjectName(QString::fromUtf8("verticalLayout_2"));
+        horizontalLayout = new QHBoxLayout();
+        horizontalLayout->setSpacing(6);
+        horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout"));
+        label_2 = new QLabel(tab_vgears);
+        label_2->setObjectName(QString::fromUtf8("label_2"));
+
+        horizontalLayout->addWidget(label_2);
+
+        line_vgears_exe = new QLineEdit(tab_vgears);
+        line_vgears_exe->setObjectName(QString::fromUtf8("line_vgears_exe"));
+
+        horizontalLayout->addWidget(line_vgears_exe);
+
+        btn_vgears_exe = new QPushButton(tab_vgears);
+        btn_vgears_exe->setObjectName(QString::fromUtf8("btn_vgears_exe"));
+        QSizePolicy sizePolicy2(QSizePolicy::Fixed, QSizePolicy::Fixed);
+        sizePolicy2.setHorizontalStretch(0);
+        sizePolicy2.setVerticalStretch(0);
+        sizePolicy2.setHeightForWidth(btn_vgears_exe->sizePolicy().hasHeightForWidth());
+        btn_vgears_exe->setSizePolicy(sizePolicy2);
+        btn_vgears_exe->setMaximumSize(QSize(24, 24));
+
+        horizontalLayout->addWidget(btn_vgears_exe);
+
+
+        verticalLayout_2->addLayout(horizontalLayout);
+
+        horizontalLayout_2 = new QHBoxLayout();
+        horizontalLayout_2->setSpacing(6);
+        horizontalLayout_2->setObjectName(QString::fromUtf8("horizontalLayout_2"));
+        label = new QLabel(tab_vgears);
+        label->setObjectName(QString::fromUtf8("label"));
+
+        horizontalLayout_2->addWidget(label);
+
+        line_vgears_config = new QLineEdit(tab_vgears);
+        line_vgears_config->setObjectName(QString::fromUtf8("line_vgears_config"));
+
+        horizontalLayout_2->addWidget(line_vgears_config);
+
+        btn_vgears_config = new QPushButton(tab_vgears);
+        btn_vgears_config->setObjectName(QString::fromUtf8("btn_vgears_config"));
+        sizePolicy2.setHeightForWidth(btn_vgears_config->sizePolicy().hasHeightForWidth());
+        btn_vgears_config->setSizePolicy(sizePolicy2);
+        btn_vgears_config->setMaximumSize(QSize(24, 24));
+
+        horizontalLayout_2->addWidget(btn_vgears_config);
+
+
+        verticalLayout_2->addLayout(horizontalLayout_2);
+
+        btn_vgears_launch = new QPushButton(tab_vgears);
+        btn_vgears_launch->setObjectName(QString::fromUtf8("btn_vgears_launch"));
+
+        verticalLayout_2->addWidget(btn_vgears_launch);
+
+        tabWidget->addTab(tab_vgears, QString());
+
+        verticalLayout->addWidget(tabWidget);
+
+        MainWindow->setCentralWidget(centralWidget);
+        menuBar = new QMenuBar(MainWindow);
+        menuBar->setObjectName(QString::fromUtf8("menuBar"));
+        menuBar->setGeometry(QRect(0, 0, 720, 23));
+        menuFile = new QMenu(menuBar);
+        menuFile->setObjectName(QString::fromUtf8("menuFile"));
+        MainWindow->setMenuBar(menuBar);
+
+        menuBar->addAction(menuFile->menuAction());
+        menuFile->addAction(actionExit);
+
+        retranslateUi(MainWindow);
+        QObject::connect(actionExit, SIGNAL(triggered()), MainWindow, SLOT(close()));
+
+        tabWidget->setCurrentIndex(0);
+
+
+        QMetaObject::connectSlotsByName(MainWindow);
+    } // setupUi
+
+    void retranslateUi(QMainWindow *MainWindow)
+    {
+        MainWindow->setWindowTitle(QCoreApplication::translate("MainWindow", "VGears Installer", nullptr));
+        actionExit->setText(QCoreApplication::translate("MainWindow", "Exit", nullptr));
+        label_4->setText(QCoreApplication::translate("MainWindow", "Original FFVII extracted data:", nullptr));
+        btn_data_src->setText(QCoreApplication::translate("MainWindow", "...", nullptr));
+#if QT_CONFIG(tooltip)
+        help_data_src->setToolTip(QCoreApplication::translate("MainWindow", "\n"
+"              <html><head/><body><b>Path to the extracted FFVII (PC version) data.</b><br><br>It must contain (at least) the folders 'kernel' and 'field'.</body></html>\n"
+"             ", nullptr));
+#endif // QT_CONFIG(tooltip)
+        help_data_src->setText(QString());
+        isoData->setText(QCoreApplication::translate("MainWindow", "ISO Info: ", nullptr));
+        isoError->setText(QCoreApplication::translate("MainWindow", "Message", nullptr));
+        label_3->setText(QCoreApplication::translate("MainWindow", "VGears installation directory:", nullptr));
+        btn_data_dst->setText(QCoreApplication::translate("MainWindow", "...", nullptr));
+#if QT_CONFIG(tooltip)
+        help_data_dest->setToolTip(QCoreApplication::translate("MainWindow", "\n"
+"              <html><head/><body><b>V-Gears data installation directory.</b><br><br>The data will be installed to this location. Existing installations will be overwritten.</body></html>\n"
+"             ", nullptr));
+#endif // QT_CONFIG(tooltip)
+        help_data_dest->setText(QString());
+        chk_advanced_options->setText(QCoreApplication::translate("MainWindow", "Show advanced options", nullptr));
+        advancedOptions->setTitle(QString());
+#if QT_CONFIG(tooltip)
+        chk_no_battle_data->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract battle data.</b><br><br>If checked, battle data such as enemies, attacks or enemy formations will not be extracted. Data from previous installations will not be deleted.<br><br>This installation step is usually quite fast, so there is no reason to skip it unless you have manually changed data.</body></html>", nullptr));
+#endif // QT_CONFIG(tooltip)
+        chk_no_battle_data->setText(QCoreApplication::translate("MainWindow", "Don't extract battle data", nullptr));
+#if QT_CONFIG(tooltip)
+        chk_no_battle_models->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract battle models.</b><br><br>If checked, battle 3D models will not be extracted. This includes models for players, enemies, attacks, battle scenarios... Models from previous installations will not be deleted.<br><br>This installation step is usually quite fast, so there is no reason to skip it unless you have manually edited the models.</body></html>", nullptr));
+#endif // QT_CONFIG(tooltip)
+        chk_no_battle_models->setText(QCoreApplication::translate("MainWindow", "Don't extract battle models", nullptr));
+#if QT_CONFIG(tooltip)
+        chk_no_kernel->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract kernel data.</b><br><br>If checked, game data such as items, materia, character information or initial savemap will not be extracted. Data from previous installations will not be deleted.<br><br>This installation step is usually quite fast, so there is no reason to skip it unless you have manually changed data.</body></html>", nullptr));
+#endif // QT_CONFIG(tooltip)
+        chk_no_kernel->setText(QCoreApplication::translate("MainWindow", "Don't extract kernel data", nullptr));
+#if QT_CONFIG(tooltip)
+        chk_no_images->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract images.</b><br><br>If checked, images will not be extracted. This includes window decorations, menus icons, character portraits... It doesn' include backgrounds or textures. Images from previous installations will not be deleted.<br><br>This installation step is usually quite fast, so there is no reason to skip it unless you have manually edited the images.</body></html>", nullptr));
+#endif // QT_CONFIG(tooltip)
+        chk_no_images->setText(QCoreApplication::translate("MainWindow", "Don't extract images", nullptr));
+#if QT_CONFIG(tooltip)
+        chk_no_sounds->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract sounds.</b><br><br>If checked, sound effects will not be extracted. This doesn' include music tracks. Sounds from previous installations will not be deleted.<br><br>Installing sounds can take a few minutes, and it's OK to skip this step if they are already installed. You can also check this if you are getting errors installing sounds or if FFmpeg is not installed in your system.</body></html>", nullptr));
+#endif // QT_CONFIG(tooltip)
+        chk_no_sounds->setText(QCoreApplication::translate("MainWindow", "Don't extract sounds", nullptr));
+#if QT_CONFIG(tooltip)
+        chk_no_music->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract audio tracks.</b><br><br>If checked, audio tracks will not be extracted. This doesn' include sound effects. Tracks from previous installations will not be deleted.<br><br>Installing music can take a few minutes, and it's OK to skip this step if they are already installed. You can also check this if you are getting errors installing sounds or if FFmpeg or TiMidity are not installed in your system.</body></html>", nullptr));
+#endif // QT_CONFIG(tooltip)
+        chk_no_music->setText(QCoreApplication::translate("MainWindow", "Don't extract music tracks", nullptr));
+#if QT_CONFIG(tooltip)
+        chk_no_fields->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract field maps.</b><br><br>If checked, map fields will not be extracted. Field maps from previous installations will not be deleted.<br><br>This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the fields. Skipping field map installation will also skip the installation of field 3D models.</body></html>", nullptr));
+#endif // QT_CONFIG(tooltip)
+        chk_no_fields->setText(QCoreApplication::translate("MainWindow", "Don't extract field maps", nullptr));
+#if QT_CONFIG(tooltip)
+        chk_no_field_models->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract field 3D models.</b><br><br>If checked, field 3D models and textures will not be extracted. Models from previous installations will not be deleted.<br><br>This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the models. Skipping field map installation will also skip the installation of field 3D models.</body></html>", nullptr));
+#endif // QT_CONFIG(tooltip)
+        chk_no_field_models->setText(QCoreApplication::translate("MainWindow", "Dont't extract field 3D models", nullptr));
+#if QT_CONFIG(tooltip)
+        chk_no_wm->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract world map data.</b><br><br>If checked, world map models and data will not be extracted. Data from previous installations will not be deleted.<br><br>This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the world map data. Skipping world map data installation will also skip the installation of the world map 3D models.</body></html>", nullptr));
+#endif // QT_CONFIG(tooltip)
+        chk_no_wm->setText(QCoreApplication::translate("MainWindow", "Don't extract world map data", nullptr));
+#if QT_CONFIG(tooltip)
+        chk_no_wm_models->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't extract world map 3D models.</b><br><br>If checked, world map 3D models and textures will not be extracted. Models from previous installations will not be deleted.<br><br>This installation step is not usually very long, so there is no reason to skip it unless you have manually edited the models. Skipping world map data installation will also skip the installation of world map 3D models.</body></html>", nullptr));
+#endif // QT_CONFIG(tooltip)
+        chk_no_wm_models->setText(QCoreApplication::translate("MainWindow", "Dont't extract world 3D models", nullptr));
+#if QT_CONFIG(tooltip)
+        chk_no_ffmpeg->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't use the ffmpeg command.</b><br><br>If checked, sound effects and background music will not be available to V-Gears.<br><br>Sound effects and music indexes will still be built, but you will need to provide your own ogg files.</body></html>", nullptr));
+#endif // QT_CONFIG(tooltip)
+        chk_no_ffmpeg->setText(QCoreApplication::translate("MainWindow", "Prevent FFmpeg calls", nullptr));
+#if QT_CONFIG(tooltip)
+        chk_no_timidity->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't use the timidity command.</b><br><br>If checked, background music will not be available to V-Gears.<br><br>Music indexes will still be built, but you will need to provide your own ogg tracks.</body></html>", nullptr));
+#endif // QT_CONFIG(tooltip)
+        chk_no_timidity->setText(QCoreApplication::translate("MainWindow", "Prevent TiMidity calls", nullptr));
+#if QT_CONFIG(tooltip)
+        chk_keep_original->setToolTip(QCoreApplication::translate("MainWindow", "<html><head/><body><b>Don't delete original data after installation.</b><br><br>The installer extracts some original data from the installation disk, such as MIDI sounds, TEX images, and some LGP archives. If checked, this data will not be deleted when the installation is complete.<br><br>This data is never used by V-Gears, and there is usually no need to check this.</body></html>", nullptr));
+#endif // QT_CONFIG(tooltip)
+        chk_keep_original->setText(QCoreApplication::translate("MainWindow", "Preserve original data", nullptr));
+        btn_data_run->setText(QCoreApplication::translate("MainWindow", "Install data", nullptr));
+        label_percent->setText(QString());
+        label_progress->setText(QString());
+        label_5->setText(QCoreApplication::translate("MainWindow", "Installer log:", nullptr));
+        tabWidget->setTabText(tabWidget->indexOf(tab_installer), QCoreApplication::translate("MainWindow", "Data Installer", nullptr));
+        label_2->setText(QCoreApplication::translate("MainWindow", "VGears Path", nullptr));
+        btn_vgears_exe->setText(QCoreApplication::translate("MainWindow", "...", nullptr));
+        label->setText(QCoreApplication::translate("MainWindow", "Config Directory", nullptr));
+        btn_vgears_config->setText(QCoreApplication::translate("MainWindow", "...", nullptr));
+        btn_vgears_launch->setText(QCoreApplication::translate("MainWindow", "Run V-Gears", nullptr));
+        tabWidget->setTabText(tabWidget->indexOf(tab_vgears), QCoreApplication::translate("MainWindow", "V-Gears Config", nullptr));
+        menuFile->setTitle(QCoreApplication::translate("MainWindow", "File", nullptr));
+    } // retranslateUi
+
+};
+
+namespace Ui {
+    class MainWindow: public Ui_MainWindow {};
+} // namespace Ui
+
+QT_END_NAMESPACE
+
+#endif // UI_MAINWINDOW_H

+ 3 - 0
src/main.cpp

@@ -56,6 +56,9 @@
 int main(int argc, char *argv[]){
     try{
         VGears::Application app(argc, argv);
+        // Re-finalize singleton registration after object construction is complete
+        // to avoid UBSAN vptr initialization warnings during base class constructor
+        VGears::Application::FinalizeSingletonRegistration(&app);
         if (!app.initOgre()) return 0;
         Ogre::Root *root(app.getRoot());
         Ogre::RenderWindow *window(app.getRenderWindow());

+ 5 - 4
src/viewer/ViewerModule.cpp

@@ -13,6 +13,7 @@
 #include "core/XmlWalkmeshFile.h"
 #include "core/XmlMapsFile.h"
 #include "core/XmlMapFile.h"
+#include "core/EntityManager.h"
 
 
 
@@ -151,7 +152,7 @@ ViewerModule::~ViewerModule()
 void
 ViewerModule::Input( const VGears::Event& event )
 {
-    if (event.type == ET_KEY_PRESS && event.param1 == OIS::KC_DOWN)
+    if (event.type == VGears::ET_KEY_PRESS && event.param1 == OIS::KC_DOWN)
     {
         bool change = false;
         if (m_Entity != nullptr)
@@ -181,7 +182,7 @@ ViewerModule::Input( const VGears::Event& event )
             }
         }
     }
-    else if (event.type == ET_KEY_PRESS && event.param1 == OIS::KC_UP)
+    else if (event.type == VGears::ET_KEY_PRESS && event.param1 == OIS::KC_UP)
     {
         bool change = false;
         if (m_Entity != nullptr && m_Entity->getAllAnimationStates() != nullptr)
@@ -331,8 +332,8 @@ ViewerModule::SetWalkmeshToLoad( const Ogre::String& name )
     if( walkmeshfile_name_ != "" )
     {
         XmlWalkmeshFile walkmesh_file( "./data/" + walkmeshfile_name_ );
-        m_Walkmesh = new Walkmesh();
-        walkmesh_file.Load( m_Walkmesh );
+        walkmesh_file.Load();
+        m_Walkmesh = EntityManager::getSingleton().GetWalkmesh();
         //m_SceneNode->attachObject( m_Walkmesh );
         //m_Walkmesh->setVisible( true );
     }