Skip to content
This repository was archived by the owner on Jul 19, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
include(GetGitRevisionDescription)
get_git_head_revision(GIT_REFSPEC GIT_SHA1)

set(version "2.0.1")
set(version "2.1.0")
set(VCPKG_TOOLS "${CMAKE_BINARY_DIR}/vcpkg_installed/x64-windows")

# Detours currently has no CMake manifests: https://github.com/microsoft/Detours/pull/48.
Expand Down
4 changes: 3 additions & 1 deletion cmake/ModEngineInstaller.cmake
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
set(CPACK_PACKAGE_NAME "ModEngine")
set(CPACK_PACKAGE_VENDOR "https://github.com/soulsmods/modengine2")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Mod Engine 2 - Mod companion framework for Souls games")
set(CPACK_PACKAGE_VERSION "2.0.0.1")
set(CPACK_PACKAGE_VERSION "2.1.0.0")
set(CPACK_PACKAGE_INSTALL_DIRECTORY "modengine2")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/installer/resources/LICENSE.txt")
set(CPACK_WIX_UPGRADE_GUID "1bf93396-5829-4275-8e34-01e443d54a98")
Expand Down Expand Up @@ -71,8 +71,10 @@ install(FILES tools/scylla/InjectorCLIx64.exe
install(FILES
installer/dist/config_darksouls3.toml
installer/dist/config_eldenring.toml
installer/dist/config_armoredcore6.toml
installer/dist/launchmod_darksouls3.bat
installer/dist/launchmod_eldenring.bat
installer/dist/launchmod_armoredcore6.bat
installer/dist/README.txt
DESTINATION .
COMPONENT application)
Expand Down
3 changes: 2 additions & 1 deletion include/modengine/extension.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,11 @@ class ModEngineExtension {
}

template <typename T>
void register_hook(GameType type, ScannedHook<T> *hook, const ScanPattern &signature, T detour, HookScanMode mode)
void register_hook(GameType type, ScannedHook<T> *hook, const ScanPattern &signature, int64_t offset, T detour, HookScanMode mode)
{
hook->mode = mode;
hook->pattern = signature;
hook->offset = offset;
hook->replacement = detour;

m_ext_connector->register_hook(type, (ScannedHook<GenericFunctionPointer>*) hook);
Expand Down
1 change: 1 addition & 0 deletions include/modengine/game_type.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ enum GameType : std::uint32_t {
DS3 = 1 << 2,
SEKIRO = 1 << 3,
ELDEN_RING = 1 << 4,
ARMORED_CORE_6 = 1 << 5,
ALL = 0xFFFFFFFF
};

Expand Down
6 changes: 5 additions & 1 deletion include/modengine/hook.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,18 @@ template <typename T>
struct ScannedHook {
ScannedHook()
: mode(SCAN_FUNCTION)
, pattern()
, offset(0)
, original(nullptr)
, replacement(nullptr)
, applied(false)
{
}

ScannedHook(HookScanMode _mode, ScanPattern _pattern, T _replacement)
ScannedHook(HookScanMode _mode, ScanPattern _pattern, int64_t _offset, T _replacement)
: mode(_mode)
, pattern(_pattern)
, offset(_offset)
, original(nullptr)
, replacement(_replacement)
, applied(false)
Expand All @@ -75,6 +78,7 @@ struct ScannedHook {
bool applied;
HookScanMode mode;
ScanPattern pattern;
int64_t offset;
T original;
T replacement;
};
Expand Down
10 changes: 5 additions & 5 deletions installer/dist/README.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
Mod Engine 2 Preview release:

This is a preview release of Mod Engine 2 for Elden Ring. Mod Engine 2 is a utility for injecting various code into the game
for the use of mods. Among other things, Mod Engine 2 allows the usage of mods that modify the game files without having to
go through the hassle of unpacking the game archives and patching the game exe. Mod Engine 2 does this by injecting code into
the game's asset system, and redirecting the game to load a modded file inside your mod folder if one exists instead of loading
the original file in the game's big archives.
This is a preview release of Mod Engine 2 for Elden Ring and Armored Core 6. Mod Engine 2 is a utility for injecting various
code into the game for the use of mods. Among other things, Mod Engine 2 allows the usage of mods that modify the game files
without having to go through the hassle of unpacking the game archives and patching the game exe. Mod Engine 2 does this
by injecting code into the game's asset system, and redirecting the game to load a modded file inside your mod folder if
one exists instead of loading the original file in the game's big archives.

Mod Engine 2 is a ground up rewrite of Mod Engine and is designed to improve over the existing user experience while also being
more reliable and having more infrastructure to diagnose issues. This public release currently only supports modded file injection,
Expand Down
47 changes: 47 additions & 0 deletions installer/dist/config_armoredcore6.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Global mod engine configuration
[modengine]
# If set to true the debug console will appear while the game is running
debug = false

# List of files that will be loaded into the game as DLL mods.
# Absolute paths to mods are supported but must use '\\' to separate path items. For example, if your mod is at E:\coolstuff\coolmod.dll, you must enter
# the path in the config as "E:\\coolstuff\\coolmod.dll".
# If there's no drive specifier (C:, D:, etc), the path is relative to where the launcher is located. For example, having the path as "mod.dll" will tell
# Mod Engine 2 to look for the directory mod inside the Mod Engine 2 directory with the launcher.
#
# Multiple mods must be separated with commas. For example if you have 3 mods, you will have something like the following:
# external_dlls = [ "coolmod.dll", "D:\\nicemods\\nicemod.dll", "sosofolder\sosomod.dll" ]
external_dlls = []

# Mod loader configuration
[extension.mod_loader]
enabled = true

# Not applicable for Armored Core 6
loose_params = false

# List of directories that contain modded files in order of prioritization. Inside each specified mod directory must have the game
# assets in Fromsoft's asset structure. I.e. if you mod parts/something.partsbnd.dcx, the modded version must be at mod/parts/something.partsbnd.dcx.
# Absolute paths to mods are supported but must use '\\' to separate path items. For example, if your mod is at E:\coolstuff\coolmod, you must enter
# the path in the config as "E:\\coolstuff\\coolmod".
# If there's no drive specifier (C:, D:, etc), the path is relative to where the launcher is located. For example, having the path as "mod" will tell
# Mod Engine 2 to look for the directory mod inside the Mod Engine 2 directory with the launcher.
#
# Multiple mods must be separated with commas. For example if you have 3 mods, you will have something like the following:
# mods = [
# { enabled = true, name = "coolmod", path = "mod1" },
# { enabled = true, name = "nicemod", path = "mod2" },
# { enabled = true, name = "sosomod", path = "mod3" }
# ]
# Note that modengine 2 currently has no way to resolve conflicting files including regulation.bin, and thus the mod with the highest priority
# will have the modded file be loaded in the case of conflict. Some support for merging of params and potentially other assets is considered for
# a future release.
mods = [
{ enabled = true, name = "default", path = "mod" }
]

# When enabled, scylla hide will be injected into the game. This allows for antidebug measures in the game to be bypassed so that you can attach
# debuggers such as Cheat Engine, x64dbg, windbg, etc to the game without as much trouble. If you're not reverse engineering the game, this option
# is probably not for you.
[extension.scylla_hide]
enabled = false
2 changes: 1 addition & 1 deletion installer/dist/config_eldenring.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ mods = [
{ enabled = true, name = "default", path = "mod" }
]

# When enabled, scylly hide will be injected into the game. This allows for antidebug measures in the game to be bypassed so that you can attach
# When enabled, scylla hide will be injected into the game. This allows for antidebug measures in the game to be bypassed so that you can attach
# debuggers such as Cheat Engine, x64dbg, windbg, etc to the game without as much trouble. If you're not reverse engineering the game, this option
# is probably not for you.
[extension.scylla_hide]
Expand Down
3 changes: 3 additions & 0 deletions installer/dist/launchmod_armoredcore6.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
chcp 65001
:: The above line is necessary in case you edit this file to lead to a path with Unicode characters.
.\modengine2_launcher.exe -t ac6 -c .\config_armoredcore6.toml
6 changes: 5 additions & 1 deletion launcher/launcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,25 @@ enum LaunchTarget {
AUTODETECT,
DS3,
ELDEN_RING,
ARMORED_CORE_6,
};

static std::map<LaunchTarget, LaunchTargetParams> launch_targets {
{ DS3, { L"374320", L"Game/DarkSoulsIII.exe", L"config_darksouls3.toml" } },
{ ELDEN_RING, { L"1245620", L"Game/eldenring.exe", L"config_eldenring.toml" } }
{ ELDEN_RING, { L"1245620", L"Game/eldenring.exe", L"config_eldenring.toml" } },
{ ARMORED_CORE_6, { L"1888160", L"Game/armoredcore6.exe", L"config_armoredcore6.toml" } }
};

static std::map<std::string, LaunchTarget> launch_target_names {
{ "ds3", DS3 },
{ "er", ELDEN_RING },
{ "ac6", ARMORED_CORE_6 },
};

static std::map<std::string, LaunchTarget> exe_names {
{ "DarkSoulsIII.exe", DS3 },
{ "eldenring.exe", ELDEN_RING },
{ "armoredcore6.exe", ARMORED_CORE_6 },
};

std::wstring GetCurrentDirectory()
Expand Down
12 changes: 12 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <optional>
#include <windows.h>
#include <iostream>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <modengine/settings_loader.h>

using namespace modengine;
Expand Down Expand Up @@ -53,6 +54,17 @@ int WINAPI modengine_entrypoint(void)
auto settings_status = settings_loader.load(settings);
auto config = settings.get_config_reader().read_config_object<ModEngineConfig>({ "modengine" });

if (config.debug && !is_debugger_enabled) {
// Create debug console
AllocConsole();
FILE* stream;
freopen_s(&stream, "CONOUT$", "w", stdout);
freopen_s(&stream, "CONIN$", "r", stdin);

logger->set_level(spdlog::level::trace);
logger->sinks().push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
}

const auto game_info = GameInfo::from_current_module();
if (!game_info) {
error("Unable to detect a supported game");
Expand Down
5 changes: 5 additions & 0 deletions src/modengine/ext/mod_loader/archive_file_overrides.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ void process_archive_path(wchar_t* raw_path, size_t raw_path_len)
for (auto i = 1; i < prefix_len && i < raw_path_len; i++) {
raw_path[i] = '/';
}

if (get_level() <= level::debug) {
std::wstring replaced(raw_path, raw_path_len);
debug(L"Replaced path with {}", replaced);
}
}

bool path_contains(const fs::path& root, const fs::path& filepath)
Expand Down
4 changes: 3 additions & 1 deletion src/modengine/ext/mod_loader/mod_loader_extension.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ auto loose_params_aob_2 = util::hex_string("0F 85 C5 00 00 00 48 8D 4C 24 28");
//auto loose_params_aob_3 = util::hex_string("E8 C8 F7 F7 FF 90 E9 73 E3 1F 04");

auto virtual_to_archive_path_er_aob = util::hex_aob("e8 ?? ?? ?? ?? 48 83 7b 20 08 48 8d 4b 08 72 03 48 8b 09 4c 8b 4b 18 41 b8 05 00 00 00 4d 3b c8");
auto virtual_to_archive_path_ac6_aob = util::hex_aob("cf e8 ?? ?? ?? ?? 48 83 7b 20 08 48 8d 4b 08 72 03 48 8b 09 4c 8b 4b 18 41 b8 05 00 00 00 4d 3b c8");

static fs::path primary_mod_path(const Settings& settings)
{
Expand Down Expand Up @@ -57,7 +58,8 @@ void ModLoaderExtension::on_attach()

register_hook(ALL, &hooked_CreateFileW, kernel32_path.wstring(), "CreateFileW", tCreateFileW);
register_hook(DS3, &hooked_virtual_to_archive_path_ds3, util::rva2addr(0x7d660), virtual_to_archive_path_ds3);
register_hook(ELDEN_RING, &hooked_virtual_to_archive_path_eldenring, virtual_to_archive_path_er_aob, virtual_to_archive_path_eldenring, SCAN_CALL_INST);
register_hook(ELDEN_RING, &hooked_virtual_to_archive_path_eldenring, virtual_to_archive_path_er_aob, 0x0, virtual_to_archive_path_eldenring, SCAN_CALL_INST);
register_hook(ARMORED_CORE_6, &hooked_virtual_to_archive_path_eldenring, virtual_to_archive_path_ac6_aob, 0x1, virtual_to_archive_path_eldenring, SCAN_CALL_INST);

auto config = get_config<ModLoaderConfig>();
for (const auto& mod : config.mods) {
Expand Down
6 changes: 4 additions & 2 deletions src/modengine/game_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@ static std::unordered_map<std::wstring, GameType> game_type_executables {
{ L"DarkSoulsII.exe", DS2 },
{ L"DarkSoulsIII.exe", DS3 },
{ L"sekiro.exe", SEKIRO },
{ L"eldenring.exe", ELDEN_RING }
{ L"eldenring.exe", ELDEN_RING },
{ L"armoredcore6.exe", ARMORED_CORE_6 }
};

static std::unordered_map<GameType, std::string> game_type_descriptions {
{ DS_REMASTERED, "Dark Souls Remastered" },
{ DS2, "Dark Souls 2: Scholar of The First Sin" },
{ DS3, "Dark Souls 3" },
{ SEKIRO, "Sekiro" },
{ ELDEN_RING, "Elden Ring" }
{ ELDEN_RING, "Elden Ring" },
{ ARMORED_CORE_6, "Armored Core 6" }
};

static std::optional<GameType> game_type_for_exe(const std::wstring& exe_name)
Expand Down
1 change: 1 addition & 0 deletions src/modengine/hook_set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ bool HookSet::hook_all()
error("Could not find pattern");
continue;
}
*addr = (uintptr_t)(((char*)*addr) + hook->offset);
debug("Found address at 0x{0:08x}", *addr);
if (hook->mode == SCAN_CALL_INST) {
// Assume near call x64 instruction for now
Expand Down
2 changes: 2 additions & 0 deletions src/modengine/mod_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ void ModEngine::attach()
}

auto lua = m_script_host.get_state();
#if 0
sol_ImGui::Init(lua);
#endif
modengine::scripting::bind_patch_api(this, lua);

m_script_host.load_scripts(m_config.script_roots);
Expand Down
7 changes: 6 additions & 1 deletion src/modengine/settings_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ SettingsLoadResult SettingsLoader::load(modengine::Settings& settings)

if (!result.inline_install) {
const auto global_settings_path = m_installation / "config.toml";

info("Attempting to load global config at {}", global_settings_path.string());
if (fs::exists(global_settings_path)) {
result.found_global_config = load_toml_into(settings, global_settings_path);
if (result.found_global_config)
info("Global config loaded");
}
}

Expand All @@ -44,9 +46,12 @@ SettingsLoadResult SettingsLoader::load(modengine::Settings& settings)
if (settings_path_env != nullptr) {
auto path = fs::path(settings_path_env);
auto local_modengine_path = path.parent_path();
info("Attempting to load mod settings config at {}", path.string());

result.found_local_config = load_toml_into(settings, path);
settings.m_modengine_local_path = local_modengine_path;
if (result.found_local_config)
info("Local config loaded");
}

return result;
Expand Down
6 changes: 3 additions & 3 deletions src/modengine/util/platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
#include <windows.h>

namespace modengine::util {
std::filesystem::path&& system_directory()
std::filesystem::path system_directory()
{
TCHAR system_folder[MAX_PATH];
TCHAR system_folder[MAX_PATH + 1];
if (!GetSystemDirectory(system_folder, MAX_PATH)) {
throw std::runtime_error("GetSystemDirectory(..., MAX_PATH) failed");
}

return std::move(std::filesystem::path(system_folder));
return { system_folder };
}

uintptr_t rva2addr(ptrdiff_t offset)
Expand Down
2 changes: 1 addition & 1 deletion src/modengine/util/platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace modengine::util {

std::filesystem::path&& system_directory();
std::filesystem::path system_directory();
uintptr_t rva2addr(ptrdiff_t offset);

}
2 changes: 1 addition & 1 deletion src/modengine/version.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#define VERSION "2.0.0-preview5-@GIT_SHA1@"
#define VERSION "2.1.0-@GIT_SHA1@"

namespace modengine {

Expand Down