|
| 1 | +#include <filesystem> |
| 2 | + |
| 3 | +#include "wwise_file_overrides.h" |
| 4 | +#include "archive_file_overrides.h" |
| 5 | + |
| 6 | +namespace modengine::ext { |
| 7 | + |
| 8 | +// This hook the IAkFileLocationResolver::open() method. It takes in a path |
| 9 | +// and a so-called openMode. This openMode parameter is of type AkOpenMode |
| 10 | +// which is an enum with 4 states: Read, Write, WriteOverwrite and ReadWrite. |
| 11 | +// Passing in any of these 4 invariants will cause this function to yield a |
| 12 | +// FileOperator used for sourcing the file bytes. FromSoftware added another |
| 13 | +// possible state to AKOpenMode, decimal 9. which will make this fn yield an |
| 14 | +// EBLFileOperator, this implementation does all its fetching from the BDTs. |
| 15 | +// EBLFileOperator does not use the usual virtual path lookup that ME2 already |
| 16 | +// hooks. Hence we need this hook here. |
| 17 | +// |
| 18 | +// In order to selectively make it read from disk again this hook checks if an |
| 19 | +// override file exists and sets the openMode back from 9 to Read and replaces |
| 20 | +// the virtual path parameter string with an absolute path. |
| 21 | +// |
| 22 | +// This is a messy one though: |
| 23 | +// Figuring out if there is an override isn't straight forward. The hooked |
| 24 | +// function gets invoked with a virtual path string ex: `sd:/50846376.wem`. |
| 25 | +// Wwise uses subdirectories for localized content, meaning that the "simple" |
| 26 | +// WEM example makes Wwise look in `/50846376.wem` but also in |
| 27 | +// `enus/50846376.wem` (or `ja/50846376.wem` for AC6 which has `ja` as an extra |
| 28 | +// locale for some audio and BNKs). To make matters even worse, Elden Ring |
| 29 | +// specifically sorts the WEMs into a subdir (`wem/`) and then another subdir |
| 30 | +// based on the first two digits of the WEM. So above example will spawn |
| 31 | +// lookups in `/wem/50/50846376.wem` and `enus/wem/50/50846376.wem`. In order |
| 32 | +// to figure out if there is an override we will need to look in multiple |
| 33 | +// directories per request. Luckily, aside from boot, this routine is called |
| 34 | +// quite infrequently. |
| 35 | +// |
| 36 | +// Also, worth pointing out that not all paths passed to this hook will have |
| 37 | +// the `sd:/` prefix. So we cannot get away with the usual prefix / rewrite |
| 38 | +// trick and will have to allocate a completely new string. |
| 39 | + |
| 40 | +namespace fs = std::filesystem; |
| 41 | + |
| 42 | +using namespace spdlog; |
| 43 | + |
| 44 | +const wchar_t* prefixes[3] = { |
| 45 | + L"sd/", |
| 46 | + L"sd/enus/", |
| 47 | + L"sd/ja/", |
| 48 | +}; |
| 49 | + |
| 50 | +std::optional<fs::path> check_paths(const std::wstring filename) { |
| 51 | + for (auto prefix: prefixes) { |
| 52 | + if (auto override = find_override_file(prefix+filename)) { |
| 53 | + return override; |
| 54 | + } |
| 55 | + } |
| 56 | + |
| 57 | + return {}; |
| 58 | +} |
| 59 | + |
| 60 | +std::optional<fs::path> find_override(const std::wstring filename) |
| 61 | +{ |
| 62 | + // Check wem/<first to digits of filename>/<filename> too since ER uses |
| 63 | + // this format |
| 64 | + if (filename.ends_with(L".wem")) { |
| 65 | + auto wem_path = L"wem/" + filename.substr(0,2) + L"/" + filename; |
| 66 | + auto wem_path_result = check_paths(wem_path); |
| 67 | + |
| 68 | + if (wem_path_result.has_value()) { |
| 69 | + return wem_path_result; |
| 70 | + } |
| 71 | + } |
| 72 | + |
| 73 | + return check_paths(filename); |
| 74 | +} |
| 75 | + |
| 76 | +std::optional<std::wstring> normalize_filename(const std::wstring path) { |
| 77 | + if (path.starts_with(L"sd:/")) { |
| 78 | + return std::wstring(path.substr(4)); |
| 79 | + } |
| 80 | + |
| 81 | + return {}; |
| 82 | +} |
| 83 | + |
| 84 | +ScannedHook<decltype(&ak_file_location_resolver_open)> hooked_ak_file_location_resolver_open; |
| 85 | + |
| 86 | +void* __cdecl ak_file_location_resolver_open(UINT64 p1, wchar_t* path, AKOpenMode openMode, UINT64 p4, UINT64 p5, UINT64 p6) |
| 87 | +{ |
| 88 | + std::wstring lookup(path); |
| 89 | + debug(L"sd.bhd entry requested: {}", lookup); |
| 90 | + |
| 91 | + auto normalized = normalize_filename(lookup); |
| 92 | + if (!normalized.has_value()) { |
| 93 | + return hooked_ak_file_location_resolver_open.original(p1, path, openMode, p4, p5, p6); |
| 94 | + } |
| 95 | + |
| 96 | + auto filename = normalized.value(); |
| 97 | + auto override = find_override(filename); |
| 98 | + if (!override.has_value()) { |
| 99 | + return hooked_ak_file_location_resolver_open.original(p1, path, openMode, p4, p5, p6); |
| 100 | + } |
| 101 | + |
| 102 | + auto override_path_string = override.value().wstring(); |
| 103 | + return hooked_ak_file_location_resolver_open.original(p1, override_path_string.data(), AKOpenMode::READ, p4, p5, p6); |
| 104 | +} |
| 105 | + |
| 106 | +} |
0 commit comments