mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-02 14:50:41 -07:00
b487cf2bcf
## Description of change When the game library fails to load, recursively iterate its import table to narrow down the missing library. If the dependency is a commonly missing one, give the user a hint on where to find it. ## Testing System with an AMD GPU and LDJ-010 IIDX: ``` [2025/10/01 00:32:41] M:dependencies: bm2dx.dll [2025/10/01 00:32:41] M:dependencies: |-- nvEncodeAPI64.dll [2025/10/01 00:32:41] W:dependencies: | |-- [!] NVIDIA Graphics Driver [2025/10/01 00:32:41] W:dependencies: | |-- For non-NVIDIA GPUs, copy the stub file from the spice2x release .zip. [2025/10/01 00:32:41] F:libutils: DLL failed to load - this is a common error. Please carefully read ALL of the following steps for a fix: ``` Same system, but with SDVX: ``` [2025/10/01 00:33:53] M:dependencies: soundvoltex.dll [2025/10/01 00:33:53] M:dependencies: |-- nvcuda.dll [2025/10/01 00:33:53] W:dependencies: | |-- [!] NVIDIA Graphics Driver [2025/10/01 00:33:53] W:dependencies: | |-- For non-NVIDIA GPUs, copy the stub file from the spice2x release .zip. [2025/10/01 00:33:53] M:dependencies: |-- nvcuvid.dll [2025/10/01 00:33:53] W:dependencies: | |-- [!] NVIDIA Graphics Driver [2025/10/01 00:33:53] W:dependencies: | |-- For non-NVIDIA GPUs, copy the stub file from the spice2x release .zip. [2025/10/01 00:33:53] F:libutils: DLL failed to load - this is a common error. Please carefully read ALL of the following steps for a fix: ``` Contrived example of a missing dependency of a dependency: ``` [2025/10/01 00:30:59] M:dependencies: bm2dx.dll [2025/10/01 00:30:59] M:dependencies: |-- libaio.dll [2025/10/01 00:30:59] W:dependencies: | |-- [!] The library could not be loaded. (126) [2025/10/01 00:30:59] M:dependencies: |-- libaio-iob.dll [2025/10/01 00:30:59] M:dependencies: | |-- libaio.dll [2025/10/01 00:30:59] M:dependencies: |-- libaio-iob2_video.dll [2025/10/01 00:30:59] M:dependencies: | |-- libaio.dll [2025/10/01 00:30:59] M:dependencies: | |-- libaio-iob.dll [2025/10/01 00:30:59] F:libutils: DLL failed to load - this is a common error. Please carefully read ALL of the following steps for a fix: ``` Another example of a missing file, but with 32-bit IIDX 24: ``` [2025/10/01 00:42:13] M:dependencies: bm2dx.dll [2025/10/01 00:42:13] M:dependencies: |-- libafp-win32.dll [2025/10/01 00:42:13] W:dependencies: | |-- [!] The library could not be loaded. (126) [2025/10/01 00:42:13] M:dependencies: |-- libafputils-win32.dll [2025/10/01 00:42:13] M:dependencies: | |-- libafp-win32.dll [2025/10/01 00:42:13] F:libutils: DLL failed to load - this is a common error. Please carefully read ALL of the following steps for a fix: ``` --------- Co-authored-by: aixxe <me@aixxe.net>
151 lines
4.9 KiB
C++
151 lines
4.9 KiB
C++
#include <set>
|
|
#include "logging.h"
|
|
#include "libutils.h"
|
|
#include "scope_guard.h"
|
|
#include "dependencies.h"
|
|
|
|
using loader_hint = std::tuple<std::string, std::string, std::string>;
|
|
|
|
namespace {
|
|
// list of commonly missing dependencies and tips on where to get them
|
|
std::vector<loader_hint> hints = {
|
|
{
|
|
"msvcr100.dll",
|
|
"Visual Studio 2010 (VC++ 10.0) SP1 Redistributable",
|
|
"Download and install from https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist",
|
|
},
|
|
{
|
|
"d3dx9_43.dll",
|
|
"DirectX End-User Runtimes",
|
|
"Download and install from https://www.microsoft.com/en-us/download/details.aspx?id=35"
|
|
},
|
|
{
|
|
"nvEncodeAPI64.dll",
|
|
"NVIDIA Graphics Driver",
|
|
"For non-NVIDIA GPUs, copy the stub file from the spice2x release .zip.",
|
|
},
|
|
{
|
|
"nvcuda.dll",
|
|
"NVIDIA Graphics Driver",
|
|
"For non-NVIDIA GPUs, copy the stub file from the spice2x release .zip.",
|
|
},
|
|
{
|
|
"nvcuvid.dll",
|
|
"NVIDIA Graphics Driver",
|
|
"For non-NVIDIA GPUs, copy the stub file from the spice2x release .zip.",
|
|
},
|
|
{
|
|
"cpusbxpkm.dll",
|
|
"LovePlus Printer DLL",
|
|
"Copy the stub file from the spice2x release .zip.",
|
|
},
|
|
};
|
|
|
|
std::set<std::wstring> failed = {};
|
|
|
|
auto read_imports(const std::filesystem::path& path) -> std::vector<std::filesystem::path> {
|
|
auto result = std::vector<std::filesystem::path> {};
|
|
auto const file = CreateFileW(path.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
|
|
|
|
if (file == INVALID_HANDLE_VALUE) {
|
|
return result;
|
|
}
|
|
|
|
auto const file_ = scope_guard { [file] { CloseHandle(file); } };
|
|
auto const mapping = CreateFileMapping(file, nullptr, PAGE_READONLY, 0, 0, nullptr);
|
|
|
|
if (!mapping) {
|
|
return result;
|
|
}
|
|
|
|
auto const mapping_ = scope_guard { [mapping] { CloseHandle(mapping); } };
|
|
auto const view = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0);
|
|
|
|
if (!view) {
|
|
return result;
|
|
}
|
|
|
|
auto const view_ = scope_guard { [view] { UnmapViewOfFile(view); } };
|
|
auto const dos = static_cast<PIMAGE_DOS_HEADER>(view);
|
|
|
|
if (dos->e_magic != IMAGE_DOS_SIGNATURE) {
|
|
return result;
|
|
}
|
|
|
|
auto const base = static_cast<std::uint8_t*>(view);
|
|
auto const nt = reinterpret_cast<PIMAGE_NT_HEADERS>(base + dos->e_lfanew);
|
|
|
|
if (nt->Signature != IMAGE_NT_SIGNATURE) {
|
|
return result;
|
|
}
|
|
|
|
auto const imports = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
|
|
|
if (imports.VirtualAddress == 0) {
|
|
return result;
|
|
}
|
|
|
|
auto desc = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(base + libutils::rva2offset(nt, imports.VirtualAddress));
|
|
|
|
while (desc->Name != 0) {
|
|
auto const filename = reinterpret_cast<char*>(base + libutils::rva2offset(nt, desc->Name));
|
|
auto entry_path = path.parent_path().append(filename);
|
|
|
|
// try to use library in the same directory if one exists
|
|
// otherwise, assume system library and use filename only
|
|
if (!std::filesystem::exists(entry_path)) {
|
|
entry_path = filename;
|
|
}
|
|
|
|
result.emplace_back(entry_path);
|
|
desc++;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
namespace dependencies {
|
|
auto walk(const std::filesystem::path& path, const std::string& prefix) -> bool {
|
|
// try to load the library -- skip walking if it loads successfully
|
|
if (auto const module = LoadLibraryW(path.c_str())) {
|
|
FreeLibrary(module);
|
|
return true;
|
|
}
|
|
|
|
auto const error = GetLastError();
|
|
auto const filename = path.filename().string();
|
|
|
|
log_misc("dependencies", "{}{}", prefix + (prefix.empty() ? "": "|-- "), filename);
|
|
|
|
if (failed.contains(path)) {
|
|
return false;
|
|
}
|
|
|
|
failed.insert(path);
|
|
|
|
auto const dependencies = read_imports(path);
|
|
auto const next_prefix = prefix + (prefix.empty() ? " ": "| ");
|
|
|
|
if (!dependencies.empty()) {
|
|
for (auto const& item : dependencies) {
|
|
walk(item, next_prefix);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
for (auto const& [dll, name, hint] : hints) {
|
|
if (_stricmp(dll.c_str(), filename.c_str()) != 0) {
|
|
continue;
|
|
}
|
|
|
|
log_warning("dependencies", "{}|-- [!] {}", next_prefix, name.c_str());
|
|
log_warning("dependencies", "{}|-- {}", next_prefix, hint.c_str());
|
|
return false;
|
|
}
|
|
|
|
log_warning("dependencies", "{}|-- [!] The library could not be loaded. ({})", next_prefix, error);
|
|
return false;
|
|
}
|
|
} |