diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..ec370f5 --- /dev/null +++ b/.clang-format @@ -0,0 +1,100 @@ +AccessModifierOffset: -4 +AlignAfterOpenBracket: AlwaysBreak +AlignArrayOfStructures: Left +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: Consecutive +AlignConsecutiveDeclarations: None +AlignConsecutiveMacros: None +AlignEscapedNewlines: Right +AlignOperands: Align +AllowAllArgumentsOnNextLine: False +AllowAllParametersOfDeclarationOnNextLine: False +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: False +AllowShortEnumsOnASingleLine: False +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: Inline +AllowShortLoopsOnASingleLine: False +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: False +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: False +BinPackParameters: False +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: True + AfterClass: True + AfterControlStatement: Always + AfterEnum: True + AfterFunction: True + AfterNamespace: True + AfterStruct: True + AfterUnion: True + AfterExternBlock: True + BeforeCatch: True + BeforeElse: True + BeforeLambdaBody: True + BeforeWhile: True + IndentBraces: False + SplitEmptyFunction: False + SplitEmptyRecord: False + SplitEmptyNamespace: False +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: True +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon +BreakStringLiterals: False +ColumnLimit: 0 +CompactNamespaces: False +Cpp11BracedListStyle: False +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: Always +FixNamespaceComments: False +IncludeBlocks: Preserve +IndentAccessModifiers: False +IndentCaseBlocks: True +IndentCaseLabels: True +IndentExternBlock: AfterExternBlock +IndentGotoLabels: False +IndentPPDirectives: AfterHash +IndentWidth: 4 +IndentWrappedFunctionNames: True +KeepEmptyLinesAtTheStartOfBlocks: False +LambdaBodyIndentation: Signature +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: All +PPIndentWidth: 1 +PackConstructorInitializers: NextLine +PointerAlignment: Left +QualifierAlignment: Custom +QualifierOrder: ['inline', 'static', 'const', 'type'] +ReferenceAlignment: Left +ReflowComments: False +SeparateDefinitionBlocks: Always +SortIncludes: CaseInsensitive +SpaceAfterCStyleCast: False +SpaceAfterLogicalNot: False +SpaceAfterTemplateKeyword: False +SpaceBeforeAssignmentOperators: True +SpaceBeforeCaseColon: False +SpaceBeforeCpp11BracedList: False +SpaceBeforeCtorInitializerColon: True +SpaceBeforeInheritanceColon: True +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: True +SpaceBeforeSquareBrackets: False +SpaceInEmptyBlock: False +SpaceInEmptyParentheses: False +SpacesBeforeTrailingComments: 2 +SpacesInAngles: Never +SpacesInCStyleCastParentheses: False +SpacesInConditionalStatement: False +SpacesInContainerLiterals: True +SpacesInParentheses: False +SpacesInSquareBrackets: False +Standard: Latest +TabWidth: 4 +UseCRLF: True +UseTab: AlignWithSpaces diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..fb7e6e3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,15 @@ +[*] +charset = utf-8 +insert_final_newline = true + +[*.{hpp,cpp}] +indent_style = tab +indent_size = 4 + +[{CMakeLists.txt,*.cmake,*.cmake.in}] +indent_style = tab +indent_size = 4 + +[*.{json,yml}] +indent_style = space +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.github/workflows/maintenance.yml b/.github/workflows/maintenance.yml new file mode 100644 index 0000000..9fed75d --- /dev/null +++ b/.github/workflows/maintenance.yml @@ -0,0 +1,33 @@ +name: Scripted maintenance + +on: [ push, pull_request_target ] + +jobs: + maintenance: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.head_ref }} + + - name: Cache LLVM and Clang + id: cache-llvm + uses: actions/cache@v2 + with: + path: ${{ runner.temp }}/llvm + key: llvm-14 + + - name: Install LLVM and Clang + uses: KyleMayes/install-llvm-action@v1 + with: + version: "14" + directory: ${{ runner.temp }}/llvm + cached: ${{ steps.cache-llvm.outputs.cache-hit }} + + - name: Run clang-format + run: find -type f \( -name *.h -o -name *.cpp \) | xargs clang-format -style=file -i + + - uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: maintenance diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d163863 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b30655b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "extern/BethesdaCMakeModules"] + path = extern/BethesdaCMakeModules + url = https://github.com/shad0wshayd3/BethesdaCMakeModules.git +[submodule "extern/CommonLibF4"] + path = extern/CommonLibF4 + url = https://github.com/shad0wshayd3/CommonLibF4.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ff4483a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,74 @@ +cmake_minimum_required(VERSION 3.22) +set(NAME "BakaSurvivalSettings") +set(VERSION 1.0.0) + +# ---- Options ---- + +option(COPY_BUILD "Copy the build output to the Fallout 4 directory." OFF) + +# ---- Cache build vars ---- + +macro(set_from_environment VARIABLE) + if (NOT DEFINED ${VARIABLE} AND DEFINED ENV{${VARIABLE}}) + set(${VARIABLE} $ENV{${VARIABLE}}) + endif() +endmacro() + +macro(find_commonlib_path) + if (CommonLibName AND NOT ${CommonLibName} STREQUAL "") + # Check extern + find_path(CommonLibPath + NAMES + include/REL/Relocation.h + PATHS + extern/${CommonLibName} + extern/${CommonLibName}/${CommonLibName} + ) + if (${CommonLibName} STREQUAL "CommonLibName-NOTFOUND") + #Check path + set_from_environment(${CommonLibName}Path) + set(CommonLibName ${${CommonLibName}Path}) + endif() + endif() +endmacro() + +set_from_environment(VCPKG_ROOT) +if (NOT DEFINED VCPKG_ROOT) + message( + WARNING + "Variable VCPKG_ROOT is not set. Continuing without vcpkg." + ) +endif() + +set_from_environment(Fallout4Path) +set(CommonLibName "CommonLibF4") +find_commonlib_path("CommonLibF4") + +message( + STATUS + "Building for Fallout 4 at ${Fallout4Path} with ${CommonLibName} at ${CommonLibPath}." +) + +set(Boost_USE_STATIC_RUNTIME OFF CACHE BOOL "") +set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" CACHE STRING "") + +# ---- Project ---- + +project( + ${NAME} + VERSION ${VERSION} + LANGUAGES CXX +) + +# ---- Include guards ---- + +if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) + message( + FATAL_ERROR + "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there." + ) +endif() + +# ---- Subdirectories ---- + +add_subdirectory(src) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..00350b9 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,65 @@ +{ + "configurePresets": [ + { + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_BUILD_TYPE": { + "type": "STRING", + "value": "Release" + } + }, + "errors": { + "deprecated": true + }, + "hidden": true, + "name": "cmake-dev", + "warnings": { + "deprecated": true, + "dev": true + } + }, + { + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": { + "type": "STRING", + "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" + }, + "VCPKG_OVERLAY_PORTS": { + "type": "STRING", + "value": "${sourceDir}/cmake/ports/" + } + }, + "hidden": true, + "name": "vcpkg" + }, + { + "cacheVariables": { + "CMAKE_MSVC_RUNTIME_LIBRARY": { + "type": "STRING", + "value": "MultiThreaded$<$:Debug>" + }, + "VCPKG_TARGET_TRIPLET": { + "type": "STRING", + "value": "x64-windows-static" + } + }, + "hidden": true, + "name": "windows" + }, + { + "cacheVariables": { + "CMAKE_CXX_FLAGS": "/EHsc /MP /W4 /WX", + "CMAKE_CXX_FLAGS_RELEASE": "/O2 /DNDEBUG" + }, + "generator": "Visual Studio 17 2022", + "inherits": [ + "cmake-dev", + "vcpkg", + "windows" + ], + "name": "vs2022-windows-vcpkg", + "toolset": "v143" + } + ], + "version": 2 +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5337fdb --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 shad0wshayd3 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/cmake/Version.h.in b/cmake/Version.h.in new file mode 100644 index 0000000..0033cc5 --- /dev/null +++ b/cmake/Version.h.in @@ -0,0 +1,10 @@ +#pragma once + +namespace Version +{ + inline constexpr std::size_t MAJOR = @PROJECT_VERSION_MAJOR@; + inline constexpr std::size_t MINOR = @PROJECT_VERSION_MINOR@; + inline constexpr std::size_t PATCH = @PROJECT_VERSION_PATCH@; + inline constexpr auto NAME = "@PROJECT_VERSION@"sv; + inline constexpr auto PROJECT = "@PROJECT_NAME@"sv; +} diff --git a/cmake/ports/autotoml/portfile.cmake b/cmake/ports/autotoml/portfile.cmake new file mode 100644 index 0000000..894eb39 --- /dev/null +++ b/cmake/ports/autotoml/portfile.cmake @@ -0,0 +1,18 @@ +vcpkg_from_github( + OUT_SOURCE_PATH SOURCE_PATH + REPO shad0wshayd3/AutoTOML + REF fc0ba2364334503684c4d4dbda6bd10c35dde1fb + SHA512 36e8d2d7f042256723b28865067a7317b4783a419c16c62c02a0b5b4ed1e2256d4ed485a0a921bcd5b74ab74007f6d619e1cf06f63eccf386ea56b7d87785a25 + HEAD_REF master +) + +vcpkg_cmake_configure(SOURCE_PATH ${SOURCE_PATH}) +vcpkg_cmake_install() +vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/AutoTOML) + +file(REMOVE_RECURSE + ${CURRENT_PACKAGES_DIR}/debug + ${CURRENT_PACKAGES_DIR}/lib +) + +file(INSTALL ${SOURCE_PATH}/LICENSE DESTINATION ${CURRENT_PACKAGES_DIR}/share/${PORT} RENAME copyright) \ No newline at end of file diff --git a/cmake/ports/autotoml/vcpkg.json b/cmake/ports/autotoml/vcpkg.json new file mode 100644 index 0000000..b31a0ac --- /dev/null +++ b/cmake/ports/autotoml/vcpkg.json @@ -0,0 +1,17 @@ +{ + "name": "autotoml", + "version-string": "2021-12-18", + "homepage": "https://github.com/shad0wshayd3/AutoTOML", + "license": "MIT", + "dependencies": [ + "tomlplusplus", + { + "name": "vcpkg-cmake", + "host": true + }, + { + "name": "vcpkg-cmake-config", + "host": true + } + ] +} \ No newline at end of file diff --git a/cmake/version.rc.in b/cmake/version.rc.in new file mode 100644 index 0000000..efbb756 --- /dev/null +++ b/cmake/version.rc.in @@ -0,0 +1,32 @@ +#include + +1 VERSIONINFO + FILEVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0 + PRODUCTVERSION @PROJECT_VERSION_MAJOR@, @PROJECT_VERSION_MINOR@, @PROJECT_VERSION_PATCH@, 0 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "@PROJECT_NAME@" + VALUE "FileVersion", "@PROJECT_VERSION@.0" + VALUE "InternalName", "@PROJECT_NAME@" + VALUE "LegalCopyright", "MIT License" + VALUE "ProductName", "@PROJECT_NAME@" + VALUE "ProductVersion", "@PROJECT_VERSION@.0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/extern/BethesdaCMakeModules b/extern/BethesdaCMakeModules new file mode 160000 index 0000000..61a7cb9 --- /dev/null +++ b/extern/BethesdaCMakeModules @@ -0,0 +1 @@ +Subproject commit 61a7cb934ab04c227e08a0660079f86f6c3f956f diff --git a/extern/CommonLibF4 b/extern/CommonLibF4 new file mode 160000 index 0000000..3f27a5e --- /dev/null +++ b/extern/CommonLibF4 @@ -0,0 +1 @@ +Subproject commit 3f27a5e518170c6c7c2719a84a202e74b4124132 diff --git a/res/settings.toml b/res/settings.toml new file mode 100644 index 0000000..e13bac3 --- /dev/null +++ b/res/settings.toml @@ -0,0 +1,3 @@ +[General] +# Enable Debug message output +EnableDebugLogging = false diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..f3bb5c4 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(c++) +add_subdirectory(papyrus) diff --git a/src/c++/CMakeLists.txt b/src/c++/CMakeLists.txt new file mode 100644 index 0000000..86ecc3c --- /dev/null +++ b/src/c++/CMakeLists.txt @@ -0,0 +1,184 @@ +set(ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +# ---- Project ---- + +configure_file( + ${ROOT_DIR}/cmake/Version.h.in + ${CMAKE_CURRENT_BINARY_DIR}/include/Version.h + @ONLY +) + +configure_file( + ${ROOT_DIR}/cmake/version.rc.in + ${CMAKE_CURRENT_BINARY_DIR}/version.rc + @ONLY +) + +# ---- Globals ---- + +add_compile_definitions( + F4SE_SUPPORT_XBYAK +) + +if (MSVC) + if (NOT ${CMAKE_GENERATOR} STREQUAL "Ninja") + add_compile_options( + /MP # Build with Multiple Processes + ) + endif () +endif () + +set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) +set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_DEBUG OFF) + +set(Boost_USE_STATIC_LIBS ON) + +# ---- Dependencies ---- + +if (DEFINED CommonLibPath AND NOT ${CommonLibPath} STREQUAL "" AND IS_DIRECTORY ${CommonLibPath}) + add_subdirectory(${CommonLibPath} ${CommonLibName}) +else () + message( + FATAL_ERROR + "Variable ${CommonLibName}Path is not set or in extern/." + ) +endif() + +# ---- Add source files ---- + +file(GLOB_RECURSE SOURCE_FILES + CONFIGURE_DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/*.h +) + +source_group( + TREE + ${CMAKE_CURRENT_SOURCE_DIR} + PREFIX + "src" + FILES + ${SOURCE_FILES} +) + +source_group( + TREE + ${CMAKE_CURRENT_BINARY_DIR} + FILES + ${CMAKE_CURRENT_BINARY_DIR}/include/Version.h +) + +# ---- Create DLL ---- + +add_library( + ${PROJECT_NAME} + SHARED + ${SOURCE_FILES} + ${CMAKE_CURRENT_BINARY_DIR}/include/Version.h + ${CMAKE_CURRENT_BINARY_DIR}/version.rc + ${ROOT_DIR}/res/settings.toml + ${ROOT_DIR}/.clang-format + ${ROOT_DIR}/.editorconfig +) + +target_compile_features( + ${PROJECT_NAME} + PRIVATE + cxx_std_23 +) + +target_compile_definitions( + ${PROJECT_NAME} + PRIVATE + _UNICODE +) + +target_include_directories( + ${PROJECT_NAME} + PRIVATE + ${CMAKE_CURRENT_BINARY_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR} +) + +target_link_libraries( + ${PROJECT_NAME} + PRIVATE + ${CommonLibName}::${CommonLibName} +) + +target_precompile_headers( + ${PROJECT_NAME} + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/PCH.h +) + +if (MSVC) + target_compile_options( + ${PROJECT_NAME} + PRIVATE + /sdl # Enable Additional Security Checks + /utf-8 # Set Source and Executable character sets to UTF-8 + /Zi # Debug Information Format + + /permissive- # Standards conformance + + /Zc:alignedNew # C++17 over-aligned allocation + /Zc:auto # Deduce Variable Type + /Zc:char8_t + /Zc:__cplusplus # Enable updated __cplusplus macro + /Zc:externC + /Zc:externConstexpr # Enable extern constexpr variables + /Zc:forScope # Force Conformance in for Loop Scope + /Zc:hiddenFriend + /Zc:implicitNoexcept # Implicit Exception Specifiers + /Zc:lambda + /Zc:noexceptTypes # C++17 noexcept rules + /Zc:preprocessor # Enable preprocessor conformance mode + /Zc:referenceBinding # Enforce reference binding rules + /Zc:rvalueCast # Enforce type conversion rules + /Zc:sizedDealloc # Enable Global Sized Deallocation Functions + /Zc:strictStrings # Disable string literal type conversion + /Zc:ternary # Enforce conditional operator rules + /Zc:threadSafeInit # Thread-safe Local Static Initialization + /Zc:tlsGuards + /Zc:trigraphs # Trigraphs Substitution + /Zc:wchar_t # wchar_t Is Native Type + + /external:anglebrackets + /external:W0 + + /W4 # Warning level + /WX # Warning level (warnings are errors) + + "$<$:>" + "$<$:/Zc:inline;/JMC-;/Ob3>" + ) + + target_link_options( + ${PROJECT_NAME} + PRIVATE + /WX # Treat Linker Warnings as Errors + + "$<$:/INCREMENTAL;/OPT:NOREF;/OPT:NOICF>" + "$<$:/INCREMENTAL:NO;/OPT:REF;/OPT:ICF;/DEBUG:FULL>" + ) +endif() + +# ---- Post build ---- + +if (COPY_BUILD) + if (DEFINED Fallout4Path) + add_custom_command( + TARGET ${PROJECT_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ ${Fallout4Path}/Data/F4SE/Plugins/ + COMMAND ${CMAKE_COMMAND} -E copy $ ${Fallout4Path}/Data/F4SE/Plugins/ + COMMAND ${CMAKE_COMMAND} -E copy ${ROOT_DIR}/res/settings.toml ${Fallout4Path}/Data/F4SE/Plugins/${PROJECT_NAME}.toml + ) + else() + message( + WARNING + "Variable ${Fallout4Path} is not defined. Skipping post-build copy command." + ) + endif() +endif() diff --git a/src/c++/MCM/MCM.h b/src/c++/MCM/MCM.h new file mode 100644 index 0000000..bd8f063 --- /dev/null +++ b/src/c++/MCM/MCM.h @@ -0,0 +1,79 @@ +#pragma once + +namespace MCM +{ + class Settings + { + public: + class General + { + public: + inline static bool bEnable{ true }; + }; + + class Setting + { + public: + inline static bool bAidWeight{ true }; + inline static bool bAmmoWeight{ true }; + inline static bool bConsole{ true }; + inline static bool bEnemyMarkers{ true }; + inline static bool bFastTravel{ true }; + inline static bool bLocationMarkers{ true }; + inline static bool bMenuSaving{ true }; + inline static bool bQuickSaveLoad{ true }; + inline static bool bSaveOnLevel{ true }; + inline static bool bSaveOnPip{ true }; + inline static bool bSaveOnSleep{ true }; + inline static bool bSaveOnTravel{ true }; + inline static bool bSaveOnWorkshop{ true }; + inline static bool bSurvivalLock{ true }; + inline static bool bToggleGodMode{ true }; + }; + + static void Update() + { + if (m_FirstRun) + { + m_FirstRun = false; + } + + m_ini_base.LoadFile("Data/MCM/Config/BakaSurvivalSettings/settings.ini"); + m_ini_user.LoadFile("Data/MCM/Settings/BakaSurvivalSettings.ini"); + + GetModSettingBool("General", "bEnable", General::bEnable); + + GetModSettingBool("Setting", "bAidWeight", Setting::bAidWeight); + GetModSettingBool("Setting", "bAmmoWeight", Setting::bAmmoWeight); + GetModSettingBool("Setting", "bConsole", Setting::bConsole); + GetModSettingBool("Setting", "bEnemyMarkers", Setting::bEnemyMarkers); + GetModSettingBool("Setting", "bFastTravel", Setting::bFastTravel); + GetModSettingBool("Setting", "bLocationMarkers", Setting::bLocationMarkers); + GetModSettingBool("Setting", "bMenuSaving", Setting::bMenuSaving); + GetModSettingBool("Setting", "bQuickSaveLoad", Setting::bQuickSaveLoad); + GetModSettingBool("Setting", "bSaveOnLevel", Setting::bSaveOnLevel); + GetModSettingBool("Setting", "bSaveOnPip", Setting::bSaveOnPip); + GetModSettingBool("Setting", "bSaveOnSleep", Setting::bSaveOnSleep); + GetModSettingBool("Setting", "bSaveOnTravel", Setting::bSaveOnTravel); + GetModSettingBool("Setting", "bSaveOnWorkshop", Setting::bSaveOnWorkshop); + GetModSettingBool("Setting", "bSurvivalLock", Setting::bSurvivalLock); + GetModSettingBool("Setting", "bToggleGodMode", Setting::bToggleGodMode); + + m_ini_base.Reset(); + m_ini_user.Reset(); + } + + inline static bool m_FirstRun{ true }; + + private: + static void GetModSettingBool(const std::string& a_section, const std::string& a_setting, bool& a_value) + { + auto base = m_ini_base.GetBoolValue(a_section.c_str(), a_setting.c_str(), a_value); + auto user = m_ini_user.GetBoolValue(a_section.c_str(), a_setting.c_str(), base); + a_value = user; + } + + inline static CSimpleIniA m_ini_base{ true }; + inline static CSimpleIniA m_ini_user{ true }; + }; +} diff --git a/src/c++/PCH.cpp b/src/c++/PCH.cpp new file mode 100644 index 0000000..8607860 --- /dev/null +++ b/src/c++/PCH.cpp @@ -0,0 +1,37 @@ +#include "PCH.h" + +namespace stl +{ + namespace detail + { + struct asm_patch : + Xbyak::CodeGenerator + { + asm_patch(std::uintptr_t a_dst) + { + Xbyak::Label dst; + + jmp(ptr[rip + dst]); + + L(dst); + dq(a_dst); + } + }; + } + + void asm_jump(std::uintptr_t a_from, [[maybe_unused]] std::size_t a_size, std::uintptr_t a_to) + { + detail::asm_patch p{ a_to }; + p.ready(); + assert(p.getSize() <= a_size); + REL::safe_write( + a_from, + std::span{ p.getCode(), p.getSize() }); + } + + void asm_replace(std::uintptr_t a_from, std::size_t a_size, std::uintptr_t a_to) + { + REL::safe_fill(a_from, REL::INT3, a_size); + asm_jump(a_from, a_size, a_to); + } +} diff --git a/src/c++/PCH.h b/src/c++/PCH.h new file mode 100644 index 0000000..cbff35f --- /dev/null +++ b/src/c++/PCH.h @@ -0,0 +1,30 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX + +#include "F4SE/F4SE.h" +#include "RE/Fallout.h" + +#include +#include +#include +#include + +#define DLLEXPORT __declspec(dllexport) + +using namespace std::literals; + +namespace logger = F4SE::log; + +namespace stl +{ + using namespace F4SE::stl; + + void asm_replace(std::uintptr_t a_from, std::size_t a_size, std::uintptr_t a_to); +} + +// clang-format off +#include "Version.h" +#include "Settings.h" +// clang-format on diff --git a/src/c++/Settings.h b/src/c++/Settings.h new file mode 100644 index 0000000..ce29340 --- /dev/null +++ b/src/c++/Settings.h @@ -0,0 +1,46 @@ +#pragma once + +namespace Settings +{ + namespace + { + using bSetting = AutoTOML::bSetting; + using ISetting = AutoTOML::ISetting; + } + + namespace General + { + inline bSetting EnableDebugLogging{ "General"s, "EnableDebugLogging"s, false }; + } + + inline void Load() + { + try + { + const auto table = toml::parse_file( + fmt::format(FMT_STRING("Data/F4SE/Plugins/{:s}.toml"sv), Version::PROJECT)); + for (const auto& setting : ISetting::get_settings()) + { + setting->load(table); + } + } + catch (const toml::parse_error& e) + { + std::ostringstream ss; + ss + << "Error parsing file \'" << *e.source().path << "\':\n" + << '\t' << e.description() << '\n' + << "\t\t(" << e.source().begin << ')'; + logger::error(FMT_STRING("{:s}"sv), ss.str()); + stl::report_and_fail("Failed to load settings."sv); + } + catch (const std::exception& e) + { + stl::report_and_fail(e.what()); + } + catch (...) + { + stl::report_and_fail("Unknown failure."sv); + } + } +} diff --git a/src/c++/main.cpp b/src/c++/main.cpp new file mode 100644 index 0000000..c315e4b --- /dev/null +++ b/src/c++/main.cpp @@ -0,0 +1,238 @@ +#include "MCM/MCM.h" + +class Hooks +{ +public: + static void Install() + { + if (!MCM::Settings::General::bEnable) + { + return; + } + + if (!MCM::Settings::Setting::bAidWeight) + { + hkcmpEAX<1321341, 0x97>::Install(); // TESWeightForm::GetFormWeight + } + + if (!MCM::Settings::Setting::bAmmoWeight) + { + hkcmpEAX<1321341, 0x121>::Install(); // TESWeightForm::GetFormWeight + } + + if (!MCM::Settings::Setting::bConsole) + { + hkcmpEAX<927099, 0x20F>::Install(); // MenuOpenHandler::HandleEvent + } + + if (!MCM::Settings::Setting::bEnemyMarkers) + { + hkcmpEAX<1475119, 0x23>::Install(); // HUDMarkerUtils::GetHostileEnemyMaxDistance + } + + if (!MCM::Settings::Setting::bFastTravel) + { + hkcmpEAX<712982, 0x323>::Install(); // PipboyMenu::PipboyMenu + hkcmpEAX<1327120, 0x18>::Install(); // nsPipboyMenu::CheckHardcoreFastTravel + } + + if (!MCM::Settings::Setting::bLocationMarkers) + { + hkcmpEAX<1301956, 0x10>::Install(); // HUDMarkerUtils::GetLocationMaxDistance + hkcmpEAX<1153736, 0xA7>::Install(); // CalculateCompassMarkersFunctor::UpdateLocationMarkers + } + + if (!MCM::Settings::Setting::bMenuSaving) + { + hkcmpEAX<1330449, 0xC6>::Install(); // PauseMenu::InitMainList + hkcmpEAX<425422, 0x4C>::Install(); // PauseMenu::CheckIfSaveLoadPossible + hkcmpEAX<540706, 0x6FE>::Install(); // BGSSaveLoadManager::DoLoadGame + hkcmpEBX<1103363, 0x81A>::Install(); // StartMenuBase::SendGameplayOptions + } + + if (!MCM::Settings::Setting::bQuickSaveLoad) + { + hkcmpEAX<1470086, 0x71>::Install(); // QuickSaveLoadHandler::HandleEvent + } + + if (!MCM::Settings::Setting::bSaveOnLevel) + { + hkcmpEAX<1158548, 0x53>::Install(); // LevelUpMenu::~LevelUpMenu + } + + if (!MCM::Settings::Setting::bSaveOnPip) + { + hkcmpEAX<1231000, 0x18F>::Install(); // PipboyManager::OnPipboyCloseAnim + } + + if (!MCM::Settings::Setting::bSaveOnSleep) + { + hkcmpEAX<1551767, 0xCC>::Install(); // PlayerCharacter::WakeUp + hkcmpEAX<1551767, 0x14B>::Install(); // PlayerCharacter::WakeUp + } + + if (!MCM::Settings::Setting::bSaveOnTravel) + { + hkcmpEAX<146861, 0x67D>::Install(); // PlayerCharacter::HandlePositionPlayerRequest + hkcmpEAX<374033, 0x2B>::Install(); // PlayerCharacter::RequestQueueDoorAutosave + } + + if (!MCM::Settings::Setting::bSaveOnWorkshop) + { + hkcmpEAX<98443, 0x198>::Install(); // WorkshopMenu::~WorkshopMenu + } + + if (!MCM::Settings::Setting::bSurvivalLock) + { + // PauseMenu::CheckIfSaveLoadPossible + static REL::Relocation target{ REL::ID(425422), 0x14D }; + REL::safe_fill(target.address(), REL::NOP, 0x05); + + hkmovEDX<402595, 0x28>::Install(); // ExitSurvivalModeCallback::operator() + } + + if (!MCM::Settings::Setting::bToggleGodMode) + { + hkcmpEAX<1032309, 0x35>::Install(); // PlayerCharacter::IsGodMode + hkcmpEAX<500346, 0x35>::Install(); // PlayerCharacter::IsImmortal + hkcmpEAX<1111932, 0x3E>::Install(); // PlayerCharacter::IsInvulnerable + hkcmpEAX<1240293, 0x7A>::Install(); // PlayerCharacter::KillImpl + } + } + +private: + template + class hkcmpEAX + { + public: + static void Install() + { + static REL::Relocation target{ REL::ID(ID), OFF }; + REL::safe_fill(target.address(), REL::NOP, 0x03); + + auto code = cmpEAX(); + assert(code.getSize() <= 0x03); + REL::safe_write(target.address(), code.getCode(), code.getSize()); + } + + private: + struct cmpEAX : Xbyak::CodeGenerator + { + cmpEAX() + { + cmp(eax, 7); + } + }; + }; + + template + class hkcmpEBX + { + public: + static void Install() + { + static REL::Relocation target{ REL::ID(ID), OFF }; + REL::safe_fill(target.address(), REL::NOP, 0x03); + + auto code = cmpEBX(); + assert(code.getSize() <= 0x03); + REL::safe_write(target.address(), code.getCode(), code.getSize()); + } + + private: + struct cmpEBX : Xbyak::CodeGenerator + { + cmpEBX() + { + cmp(ebx, 7); + } + }; + }; + + template + class hkmovEDX + { + public: + static void Install() + { + static REL::Relocation target{ REL::ID(ID), OFF }; + REL::safe_fill(target.address(), REL::NOP, 0x05); + + auto code = movEDX(); + assert(code.getSize() <= 0x05); + REL::safe_write(target.address(), code.getCode(), code.getSize()); + } + + private: + struct movEDX : Xbyak::CodeGenerator + { + movEDX() + { + mov(edx, 0); + } + }; + }; +}; + +namespace +{ + void InitializeLog() + { + auto path = logger::log_directory(); + if (!path) + { + stl::report_and_fail("Failed to find standard logging directory"sv); + } + + *path /= fmt::format(FMT_STRING("{:s}.log"sv), Version::PROJECT); + auto sink = std::make_shared(path->string(), true); + + auto log = std::make_shared("global log"s, std::move(sink)); + auto lvl = *Settings::General::EnableDebugLogging + ? spdlog::level::trace + : spdlog::level::info; + + log->set_level(lvl); + log->flush_on(lvl); + + spdlog::set_default_logger(std::move(log)); + spdlog::set_pattern("[%m/%d/%Y - %T] [%^%l%$] %v"s); + + logger::info(FMT_STRING("{:s} v{:s}"sv), Version::PROJECT, Version::NAME); + } +} + +extern "C" DLLEXPORT bool F4SEAPI F4SEPlugin_Query(const F4SE::QueryInterface* a_F4SE, F4SE::PluginInfo* a_info) +{ + a_info->infoVersion = F4SE::PluginInfo::kVersion; + a_info->name = Version::PROJECT.data(); + a_info->version = Version::MAJOR; + + const auto rtv = a_F4SE->RuntimeVersion(); + if (rtv < F4SE::RUNTIME_LATEST) + { + stl::report_and_fail( + fmt::format( + FMT_STRING("{:s} does not support runtime v{:s}."sv), + Version::PROJECT, + rtv.string())); + } + + return true; +} + +extern "C" DLLEXPORT bool F4SEAPI F4SEPlugin_Load(const F4SE::LoadInterface* a_F4SE) +{ + Settings::Load(); + InitializeLog(); + + logger::info(FMT_STRING("{:s} loaded."sv), Version::PROJECT); + logger::debug("Debug logging enabled."sv); + + F4SE::Init(a_F4SE); + + MCM::Settings::Update(); + Hooks::Install(); + + return true; +} diff --git a/src/papyrus/CMakeLists.txt b/src/papyrus/CMakeLists.txt new file mode 100644 index 0000000..f4bf091 --- /dev/null +++ b/src/papyrus/CMakeLists.txt @@ -0,0 +1,60 @@ +set(ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../..) + +include(${ROOT_DIR}/extern/BethesdaCMakeModules/Modules/Papyrus.cmake) + +file(GLOB Papyrus_INPUT + LIST_DIRECTORIES false + CONFIGURE_DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/*.psc +) + +add_papyrus( + "Papyrus" + GAME + ${Fallout4Path} + MODE + "Fallout4" + IMPORTS + ${Fallout4Path}/Data/Scripts/Source/User + ${Fallout4Path}/Data/Scripts/Source/CreationClub + ${Fallout4Path}/Data/Scripts/Source/DLC06 + ${Fallout4Path}/Data/Scripts/Source/DLC05 + ${Fallout4Path}/Data/Scripts/Source/DLC04 + ${Fallout4Path}/Data/Scripts/Source/DLC03 + ${Fallout4Path}/Data/Scripts/Source/DLC02 + ${Fallout4Path}/Data/Scripts/Source/DLC01 + ${Fallout4Path}/Data/Scripts/Source/Base + SOURCES + ${Papyrus_INPUT} +) + +source_group( + "Scripts" + FILES + ${Papyrus_INPUT} +) + +if (COPY_BUILD) + if (DEFINED Fallout4Path) + foreach(FILE IN LISTS Papyrus_INPUT) + add_custom_command( + TARGET "Papyrus" + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${FILE} ${Fallout4Path}/Data/Scripts/Source/Baka/ + ) + endforeach() + + foreach(FILE IN LISTS Papyrus_OUTPUT) + add_custom_command( + TARGET "Papyrus" + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${FILE} ${Fallout4Path}/Data/Scripts/ + ) + endforeach() + else () + message( + WARNING + "Variable ${Fallout4Path} is not defined. Skipping post-build copy command." + ) + endif() +endif() diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000..67ee1cd --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,15 @@ +{ + "name": "bakasurvivalsettings", + "description": "F4SE plugin template using CommonLibF4.", + "homepage": "https://github.com/shad0wshayd3/BakaSurvivalSettings", + "license": "MIT", + "dependencies": [ + "autotoml", + "boost-stl-interfaces", + "fmt", + "rsm-mmio", + "simpleini", + "spdlog", + "xbyak" + ] +}