mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-10-03 16:58:13 -07:00
Compare commits
4 Commits
2b6e76730b
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 0684872013 | |||
| ed0ccdd6e3 | |||
| 7aba7da3b6 | |||
| 197d76755b |
@@ -199,11 +199,11 @@ namespace games::iidx {
|
|||||||
addr_camera_manager = (addr_camera_manager_ptr - (uint8_t*) iidx_module) + disp_camera_manager + 7;
|
addr_camera_manager = (addr_camera_manager_ptr - (uint8_t*) iidx_module) + disp_camera_manager + 7;
|
||||||
|
|
||||||
// addr_device_offset
|
// addr_device_offset
|
||||||
search_from = (addr_hook_a_ptr - (uint8_t*) iidx_module);
|
search_from = addr_hook_a;
|
||||||
uint8_t *addr_device_ptr = reinterpret_cast<uint8_t *>(find_pattern_from(
|
uint8_t *addr_device_ptr = reinterpret_cast<uint8_t *>(find_pattern_from(
|
||||||
iidx_module,
|
iidx_module,
|
||||||
"488B89",
|
"488B8F00000000488B014C8B870000000033D2FF9028010000",
|
||||||
"XXX",
|
"XXX????XXXXXX????XXXXXXXX",
|
||||||
3, 0, search_from));
|
3, 0, search_from));
|
||||||
|
|
||||||
if (addr_device_ptr == nullptr) {
|
if (addr_device_ptr == nullptr) {
|
||||||
@@ -211,7 +211,7 @@ namespace games::iidx {
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
addr_device_offset = *addr_device_ptr;
|
addr_device_offset = *reinterpret_cast<uint32_t*>(addr_device_ptr);
|
||||||
|
|
||||||
// --- addr_afp_texture_offset ---
|
// --- addr_afp_texture_offset ---
|
||||||
uint8_t *addr_afp_texture_ptr = reinterpret_cast<uint8_t *>(find_pattern(
|
uint8_t *addr_afp_texture_ptr = reinterpret_cast<uint8_t *>(find_pattern(
|
||||||
@@ -233,13 +233,27 @@ namespace games::iidx {
|
|||||||
static void **__fastcall camera_hook_a(PBYTE a1) {
|
static void **__fastcall camera_hook_a(PBYTE a1) {
|
||||||
std::call_once(hook_a_init, [&]{
|
std::call_once(hook_a_init, [&]{
|
||||||
device = *reinterpret_cast<LPDIRECT3DDEVICE9EX*>(a1 + addr_device_offset);
|
device = *reinterpret_cast<LPDIRECT3DDEVICE9EX*>(a1 + addr_device_offset);
|
||||||
auto const preview = *reinterpret_cast<LPDIRECT3DTEXTURE9**>((uint8_t*)iidx_module + addr_textures);
|
auto const registry = reinterpret_cast<Camera::TextureRegistry*>(
|
||||||
|
reinterpret_cast<uint8_t*>(iidx_module) + addr_textures - offsetof(Camera::TextureRegistry, begin));
|
||||||
auto const manager = reinterpret_cast<Camera::CCameraManager2*>((uint8_t*)iidx_module + addr_camera_manager);
|
auto const manager = reinterpret_cast<Camera::CCameraManager2*>((uint8_t*)iidx_module + addr_camera_manager);
|
||||||
|
|
||||||
camera_texture_a = manager->cameras->a->d3d9_texture(addr_afp_texture_offset);
|
auto const camera_count = manager->begin ? manager->end - manager->begin : 0;
|
||||||
camera_texture_b = manager->cameras->b->d3d9_texture(addr_afp_texture_offset);
|
auto const bind_camera = [&](size_t index, LPDIRECT3DTEXTURE9*& texture, LPDIRECT3DTEXTURE9*& preview) {
|
||||||
preview_texture_a = preview;
|
if (index >= static_cast<size_t>(camera_count)) {
|
||||||
preview_texture_b = preview + 2;
|
return; // IIDX 34 only has the TOP camera.
|
||||||
|
}
|
||||||
|
auto const camera = manager->begin[index];
|
||||||
|
auto const afp = camera ? camera->afp_texture(addr_afp_texture_offset) : nullptr;
|
||||||
|
if (!afp || !registry->begin || afp->handle < registry->handle_base ||
|
||||||
|
afp->handle - registry->handle_base >= registry->end - registry->begin) {
|
||||||
|
log_warning("iidx:camhook", "invalid texture destination for camera {}", index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
texture = &afp->texture;
|
||||||
|
preview = registry->begin + (afp->handle - registry->handle_base);
|
||||||
|
};
|
||||||
|
bind_camera(0, camera_texture_a, preview_texture_a);
|
||||||
|
bind_camera(1, camera_texture_b, preview_texture_b);
|
||||||
|
|
||||||
init_local_camera();
|
init_local_camera();
|
||||||
});
|
});
|
||||||
@@ -381,10 +395,10 @@ namespace games::iidx {
|
|||||||
log_misc("iidx:camhook", "Adding user-preferred cameras");
|
log_misc("iidx:camhook", "Adding user-preferred cameras");
|
||||||
for (size_t i = 0; i < numDevices && LOCAL_CAMERA_LIST.size() < 2; i++) {
|
for (size_t i = 0; i < numDevices && LOCAL_CAMERA_LIST.size() < 2; i++) {
|
||||||
auto pActivate = ppDevices[i];
|
auto pActivate = ppDevices[i];
|
||||||
if ((int)i == preferred_top_index && top_camera == nullptr) {
|
if ((int)i == preferred_top_index && top_camera == nullptr && camera_texture_a != nullptr) {
|
||||||
log_misc("iidx:camhook", "Adding user-preferred top camera {} / '{}'", i, top_camera_id);
|
log_misc("iidx:camhook", "Adding user-preferred top camera {} / '{}'", i, top_camera_id);
|
||||||
top_camera = add_top_camera(pActivate);
|
top_camera = add_top_camera(pActivate);
|
||||||
} else if ((int)i == preferred_front_index && front_camera == nullptr) {
|
} else if ((int)i == preferred_front_index && front_camera == nullptr && camera_texture_b != nullptr) {
|
||||||
log_misc("iidx:camhook", "Adding user-preferred front camera {} / '{}'", i, front_camera_id);
|
log_misc("iidx:camhook", "Adding user-preferred front camera {} / '{}'", i, front_camera_id);
|
||||||
front_camera = add_front_camera(pActivate);
|
front_camera = add_front_camera(pActivate);
|
||||||
}
|
}
|
||||||
@@ -402,16 +416,16 @@ namespace games::iidx {
|
|||||||
|
|
||||||
if (!FLIP_CAMS) {
|
if (!FLIP_CAMS) {
|
||||||
// top camera first, then front
|
// top camera first, then front
|
||||||
if (top_camera == nullptr) {
|
if (top_camera == nullptr && camera_texture_a != nullptr) {
|
||||||
top_camera = add_top_camera(pActivate);
|
top_camera = add_top_camera(pActivate);
|
||||||
} else if (front_camera == nullptr) {
|
} else if (front_camera == nullptr && camera_texture_b != nullptr) {
|
||||||
front_camera = add_front_camera(pActivate);
|
front_camera = add_front_camera(pActivate);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// front first, then top
|
// front first, then top
|
||||||
if (front_camera == nullptr) {
|
if (front_camera == nullptr && camera_texture_b != nullptr) {
|
||||||
front_camera = add_front_camera(pActivate);
|
front_camera = add_front_camera(pActivate);
|
||||||
} else if (top_camera == nullptr) {
|
} else if (top_camera == nullptr && camera_texture_a != nullptr) {
|
||||||
top_camera = add_top_camera(pActivate);
|
top_camera = add_top_camera(pActivate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,21 +70,31 @@ namespace games::iidx {
|
|||||||
class IIDXCameraSourceReaderCallback;
|
class IIDXCameraSourceReaderCallback;
|
||||||
|
|
||||||
namespace Camera {
|
namespace Camera {
|
||||||
|
struct AfpTexture {
|
||||||
|
void* vftbl;
|
||||||
|
IDirect3DTexture9* texture;
|
||||||
|
uint32_t handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TextureRegistry {
|
||||||
|
uint32_t handle_base;
|
||||||
|
uint32_t next_slot;
|
||||||
|
IDirect3DTexture9** begin;
|
||||||
|
IDirect3DTexture9** end;
|
||||||
|
IDirect3DTexture9** capacity_end;
|
||||||
|
};
|
||||||
|
|
||||||
struct PlayVideoCamera {
|
struct PlayVideoCamera {
|
||||||
IDirect3DTexture9** d3d9_texture(const uintptr_t offset) {
|
AfpTexture* afp_texture(const uintptr_t offset) {
|
||||||
auto const afp_texture = *reinterpret_cast<uint8_t**>
|
return *reinterpret_cast<AfpTexture**>(reinterpret_cast<uint8_t*>(this) + offset);
|
||||||
(reinterpret_cast<uint8_t*>(this) + offset);
|
|
||||||
return reinterpret_cast<IDirect3DTexture9**>(afp_texture + 0x8);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CCameraManager2 {
|
struct CCameraManager2 {
|
||||||
using camera_pointers = struct {
|
|
||||||
PlayVideoCamera* a;
|
|
||||||
PlayVideoCamera* b;
|
|
||||||
};
|
|
||||||
void* vftbl;
|
void* vftbl;
|
||||||
camera_pointers* cameras;
|
PlayVideoCamera** begin;
|
||||||
|
PlayVideoCamera** end;
|
||||||
|
PlayVideoCamera** capacity_end;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -172,8 +172,6 @@ static bool check_dll(const std::string &model) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_msvcrt_args(int argc, char *argv[]);
|
|
||||||
|
|
||||||
void dump_button_bindings(std::vector<Button> *buttons);
|
void dump_button_bindings(std::vector<Button> *buttons);
|
||||||
void dump_analog_bindings();
|
void dump_analog_bindings();
|
||||||
|
|
||||||
@@ -2528,7 +2526,23 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
// log some DLLs found in path (purely for troubleshooting purposes to detect
|
// log some DLLs found in path (purely for troubleshooting purposes to detect
|
||||||
// dxvk, ForceD3D9On12, ifs_layeredfs, etc)
|
// dxvk, ForceD3D9On12, ifs_layeredfs, etc)
|
||||||
libutils::warn_if_dll_exists("d3d8.dll");
|
libutils::warn_if_dll_exists("d3d8.dll");
|
||||||
libutils::warn_if_dll_exists("d3d9.dll");
|
if (libutils::warn_if_dll_exists("d3d9.dll")) {
|
||||||
|
|
||||||
|
#if SPICE64
|
||||||
|
// dx9 subscreen games
|
||||||
|
if (avs::game::is_model({"LDJ", "KFC", "M39", "M32"})) {
|
||||||
|
log_warning(
|
||||||
|
"launcher",
|
||||||
|
"custom d3d9.dll detected - may cause graphics and touch-emulation issues");
|
||||||
|
deferredlogs::defer_error_messages({
|
||||||
|
"found custom d3d9.dll",
|
||||||
|
" custom d3d9.dll wrappers may cause subscreen and touch-emulation issues",
|
||||||
|
" if you encounter problems, close the game, remove the custom d3d9.dll and retry"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
libutils::warn_if_dll_exists("d3d10core.dll");
|
libutils::warn_if_dll_exists("d3d10core.dll");
|
||||||
libutils::warn_if_dll_exists("d3d11.dll");
|
libutils::warn_if_dll_exists("d3d11.dll");
|
||||||
libutils::warn_if_dll_exists("d3d12.dll");
|
libutils::warn_if_dll_exists("d3d12.dll");
|
||||||
@@ -2745,7 +2759,11 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
networkhook_init();
|
networkhook_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
update_msvcrt_args(argc, argv);
|
#if defined(_UCRT) || (defined(_MSC_VER) && _MSC_VER >= 1900)
|
||||||
|
log_info("launcher", "C runtime: UCRT");
|
||||||
|
#else
|
||||||
|
log_info("launcher", "C runtime: MSVCRT");
|
||||||
|
#endif
|
||||||
|
|
||||||
// load hooks
|
// load hooks
|
||||||
for (auto &hook : game_hooks) {
|
for (auto &hook : game_hooks) {
|
||||||
@@ -2986,54 +3004,6 @@ int main_implementation(int argc, char *argv[]) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/spice2x/spice2x.github.io/issues/264
|
|
||||||
// huge ugly hack to work around things that broke when MinGW switched from msvcrt to ucrt
|
|
||||||
// this is done to ensure that any DLL hooks that rely on msvcrt continue to work
|
|
||||||
void update_msvcrt_args(int argc, char *argv[]) {
|
|
||||||
#if defined(_UCRT)
|
|
||||||
auto msvc = LoadLibraryA("msvcrt.dll");
|
|
||||||
if (!msvc) {
|
|
||||||
log_warning("launcher", "failed to load msvcrt.dll");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get __argc
|
|
||||||
PINT32 argc_addr = (PINT32)GetProcAddress(msvc, "__argc");
|
|
||||||
if (!argc_addr) {
|
|
||||||
log_warning("launcher", "failed to find msvcrt!__argc");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (*argc_addr == argc) {
|
|
||||||
log_warning("launcher", "msvcrt!__argc is already set");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (const std::exception &e) {
|
|
||||||
log_warning("launcher", "exception while reading msvcrt!_argc: {}", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
// get __argv
|
|
||||||
PCHAR **argv_addr = (PCHAR **)GetProcAddress(msvc, "__argv");
|
|
||||||
if (!argv_addr) {
|
|
||||||
log_warning("launcher", "failed to find msvcrt!__argv");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update them
|
|
||||||
try {
|
|
||||||
log_info("launcher", "msvcrt!__argc value before: {}", *argc_addr);
|
|
||||||
*argc_addr = argc;
|
|
||||||
log_info("launcher", "msvcrt!__argc value after: {}", *argc_addr);
|
|
||||||
*argv_addr = argv;
|
|
||||||
} catch (const std::exception &e) {
|
|
||||||
log_warning("launcher", "exception while messing with msvcrt!_argc and _argv: {}", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
log_misc("launcher", "not UCRT, skipping msvcrt!_argc / _argv hacks");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void dump_button_bindings(std::vector<Button> *buttons) {
|
void dump_button_bindings(std::vector<Button> *buttons) {
|
||||||
if (!buttons) {
|
if (!buttons) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -248,6 +248,27 @@ typedef SPICE_SDK_STATUS_CODE (__cdecl spice_sdk_insert_coin_func)(
|
|||||||
uint8_t amount
|
uint8_t amount
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// get_coin_blocker (v0.6 and up)
|
||||||
|
// gets the current shared coin blocker state
|
||||||
|
// insert_coin bypasses the blocker regardless of this state
|
||||||
|
//
|
||||||
|
// blocked: receives true when coins are blocked, false when allowed
|
||||||
|
|
||||||
|
typedef SPICE_SDK_STATUS_CODE (__cdecl spice_sdk_get_coin_blocker_func)(
|
||||||
|
bool *blocked
|
||||||
|
);
|
||||||
|
|
||||||
|
// set_coin_blocker (v0.6 and up)
|
||||||
|
// sets the shared coin blocker state used by normal coin input
|
||||||
|
// this is not a persistent override; the game can change the state again
|
||||||
|
// insert_coin bypasses the blocker regardless of this state
|
||||||
|
//
|
||||||
|
// blocked: true to block coins, false to allow them
|
||||||
|
|
||||||
|
typedef SPICE_SDK_STATUS_CODE (__cdecl spice_sdk_set_coin_blocker_func)(
|
||||||
|
bool blocked
|
||||||
|
);
|
||||||
|
|
||||||
typedef struct SPICE_SDK_MODULE_INFO {
|
typedef struct SPICE_SDK_MODULE_INFO {
|
||||||
uint32_t size; // initialize to sizeof(SPICE_SDK_MODULE_INFO)
|
uint32_t size; // initialize to sizeof(SPICE_SDK_MODULE_INFO)
|
||||||
uintptr_t base; // loaded address, not the preferred PE image base
|
uintptr_t base; // loaded address, not the preferred PE image base
|
||||||
@@ -383,6 +404,9 @@ typedef struct SPICE_SDK_V0 {
|
|||||||
|
|
||||||
spice_sdk_hook_library_func *hook_library;
|
spice_sdk_hook_library_func *hook_library;
|
||||||
|
|
||||||
|
spice_sdk_get_coin_blocker_func *get_coin_blocker;
|
||||||
|
spice_sdk_set_coin_blocker_func *set_coin_blocker;
|
||||||
|
|
||||||
} SPICE_SDK_V0;
|
} SPICE_SDK_V0;
|
||||||
|
|
||||||
typedef void (__cdecl spice_sdk_destroy_callback_func)(
|
typedef void (__cdecl spice_sdk_destroy_callback_func)(
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ static spice_sdk_insert_card_func sdk_insert_card;
|
|||||||
static spice_sdk_set_keypad_func sdk_set_keypad;
|
static spice_sdk_set_keypad_func sdk_set_keypad;
|
||||||
static spice_sdk_add_toast_func sdk_add_toast;
|
static spice_sdk_add_toast_func sdk_add_toast;
|
||||||
static spice_sdk_insert_coin_func sdk_insert_coin;
|
static spice_sdk_insert_coin_func sdk_insert_coin;
|
||||||
|
static spice_sdk_get_coin_blocker_func sdk_get_coin_blocker;
|
||||||
|
static spice_sdk_set_coin_blocker_func sdk_set_coin_blocker;
|
||||||
static spice_sdk_get_module_info_func sdk_get_module_info;
|
static spice_sdk_get_module_info_func sdk_get_module_info;
|
||||||
static spice_sdk_get_plugin_directory_func sdk_get_plugin_directory;
|
static spice_sdk_get_plugin_directory_func sdk_get_plugin_directory;
|
||||||
static spice_sdk_register_d3d9_func sdk_register_d3d9;
|
static spice_sdk_register_d3d9_func sdk_register_d3d9;
|
||||||
@@ -211,6 +213,14 @@ sdk_init(
|
|||||||
}
|
}
|
||||||
// end of 0.5
|
// end of 0.5
|
||||||
|
|
||||||
|
if (v0->size >= RTL_SIZEOF_THROUGH_FIELD(SPICE_SDK_V0, get_coin_blocker)) {
|
||||||
|
v0->get_coin_blocker = sdk_get_coin_blocker;
|
||||||
|
}
|
||||||
|
if (v0->size >= RTL_SIZEOF_THROUGH_FIELD(SPICE_SDK_V0, set_coin_blocker)) {
|
||||||
|
v0->set_coin_blocker = sdk_set_coin_blocker;
|
||||||
|
}
|
||||||
|
// end of 0.6
|
||||||
|
|
||||||
// any newer minor iterations will need to check the size
|
// any newer minor iterations will need to check the size
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -746,4 +756,37 @@ sdk_insert_coin(
|
|||||||
return SPICE_SDK_STATUS_SUCCESS;
|
return SPICE_SDK_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SPICE_SDK_STATUS_CODE
|
||||||
|
__cdecl
|
||||||
|
sdk_get_coin_blocker(
|
||||||
|
bool *blocked
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::shared_lock lock(sdk_global_mutex);
|
||||||
|
if (!sdk_initialized) {
|
||||||
|
return SPICE_SDK_STATUS_TOO_LATE;
|
||||||
|
}
|
||||||
|
if (!blocked) {
|
||||||
|
return SPICE_SDK_STATUS_INVALID_ARGUMENT_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*blocked = eamuse_coin_get_block();
|
||||||
|
return SPICE_SDK_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPICE_SDK_STATUS_CODE
|
||||||
|
__cdecl
|
||||||
|
sdk_set_coin_blocker(
|
||||||
|
bool blocked
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::shared_lock lock(sdk_global_mutex);
|
||||||
|
if (!sdk_initialized) {
|
||||||
|
return SPICE_SDK_STATUS_TOO_LATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
eamuse_coin_set_block(blocked);
|
||||||
|
return SPICE_SDK_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sdk
|
} // namespace sdk
|
||||||
@@ -369,61 +369,102 @@ void libutils::check_duplicate_dlls() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void libutils::warn_if_dll_exists(const std::string &file_name) {
|
bool libutils::warn_if_dll_exists(const std::string &file_name) {
|
||||||
if (fileutils::file_exists(MODULE_PATH / file_name)) {
|
|
||||||
log_info("libutils", "found user-supplied {} in modules directory", file_name);
|
|
||||||
libutils::print_dll_info(MODULE_PATH / file_name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto &spice_bin_path = libutils::module_file_name(nullptr).parent_path();
|
const auto &spice_bin_path = libutils::module_file_name(nullptr).parent_path();
|
||||||
|
bool found = false;
|
||||||
if (fileutils::file_exists(spice_bin_path / file_name)) {
|
if (fileutils::file_exists(spice_bin_path / file_name)) {
|
||||||
log_info("libutils", "found user-supplied {} next to spice executable path", file_name);
|
log_info("libutils", "found user-supplied {} next to spice executable path", file_name);
|
||||||
libutils::print_dll_info(spice_bin_path / file_name);
|
libutils::print_dll_info(spice_bin_path / file_name);
|
||||||
return;
|
found = true;
|
||||||
}
|
}
|
||||||
|
if (MODULE_PATH != spice_bin_path && fileutils::file_exists(MODULE_PATH / file_name)) {
|
||||||
|
log_info("libutils", "found user-supplied {} in modules directory", file_name);
|
||||||
|
libutils::print_dll_info(MODULE_PATH / file_name);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
void libutils::print_dll_info(std::filesystem::path filename) {
|
void libutils::print_dll_info(std::filesystem::path filename) {
|
||||||
DWORD handle;
|
std::string company_name;
|
||||||
|
std::string product_name;
|
||||||
|
std::string version_str;
|
||||||
|
auto print_summary = [&]() {
|
||||||
|
log_info(
|
||||||
|
"libutils",
|
||||||
|
"DLL info for {}: CompanyName = {}, ProductName = {}, Version = {}, Path = {}",
|
||||||
|
filename.filename(),
|
||||||
|
company_name.empty() ? "?" : company_name,
|
||||||
|
product_name.empty() ? "?" : product_name,
|
||||||
|
version_str.empty() ? "?" : version_str,
|
||||||
|
filename);
|
||||||
|
};
|
||||||
|
|
||||||
|
DWORD handle = 0;
|
||||||
const auto size = GetFileVersionInfoSizeW(filename.wstring().c_str(), &handle);
|
const auto size = GetFileVersionInfoSizeW(filename.wstring().c_str(), &handle);
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
log_debug(
|
const auto error = GetLastError();
|
||||||
|
log_misc(
|
||||||
"libutils",
|
"libutils",
|
||||||
"GetFileVersionInfoSizeA failed for {}: {}",
|
"GetFileVersionInfoSizeW failed for {}: Win32 error {}; "
|
||||||
filename.filename(),
|
"DLL may have no version resource",
|
||||||
get_last_error_string());
|
filename,
|
||||||
|
error);
|
||||||
|
print_summary();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto data = util::make_unique_plain<VOID>(size);
|
auto data = util::make_unique_plain<VOID>(size);
|
||||||
if (!GetFileVersionInfoW(filename.wstring().c_str(), handle, size, data.get())) {
|
if (!GetFileVersionInfoW(filename.wstring().c_str(), handle, size, data.get())) {
|
||||||
log_debug(
|
const auto error = GetLastError();
|
||||||
|
log_misc(
|
||||||
"libutils",
|
"libutils",
|
||||||
"GetFileVersionInfoA failed for {}: {}",
|
"GetFileVersionInfoW failed for {}: Win32 error {}",
|
||||||
filename.filename(),
|
filename,
|
||||||
get_last_error_string());
|
error);
|
||||||
|
print_summary();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VS_FIXEDFILEINFO *fixed_info = nullptr;
|
||||||
|
UINT fixed_size = 0;
|
||||||
|
if (VerQueryValueW(data.get(), L"\\", (LPVOID*)&fixed_info, &fixed_size) &&
|
||||||
|
fixed_info != nullptr && fixed_size >= sizeof(*fixed_info) &&
|
||||||
|
fixed_info->dwSignature == 0xFEEF04BD) {
|
||||||
|
version_str = fmt::format(
|
||||||
|
"{}.{}.{}.{}",
|
||||||
|
HIWORD(fixed_info->dwFileVersionMS),
|
||||||
|
LOWORD(fixed_info->dwFileVersionMS),
|
||||||
|
HIWORD(fixed_info->dwFileVersionLS),
|
||||||
|
LOWORD(fixed_info->dwFileVersionLS));
|
||||||
|
log_info("libutils", "Fixed file version for {}: {}", filename.filename(), version_str);
|
||||||
|
} else {
|
||||||
|
log_misc("libutils", "Missing or invalid fixed version info for {}", filename);
|
||||||
|
}
|
||||||
|
|
||||||
struct LANGANDCODEPAGE {
|
struct LANGANDCODEPAGE {
|
||||||
WORD wLanguage;
|
WORD wLanguage;
|
||||||
WORD wCodePage;
|
WORD wCodePage;
|
||||||
} *lpTranslate = nullptr;
|
} *lpTranslate = nullptr;
|
||||||
|
|
||||||
UINT cbTranslate = 0;
|
UINT cbTranslate = 0;
|
||||||
if (!VerQueryValueA(data.get(), "\\VarFileInfo\\Translation", (LPVOID*)&lpTranslate, &cbTranslate)) {
|
if (!VerQueryValueW(
|
||||||
log_debug(
|
data.get(), L"\\VarFileInfo\\Translation", (LPVOID*)&lpTranslate, &cbTranslate)) {
|
||||||
|
log_misc(
|
||||||
"libutils",
|
"libutils",
|
||||||
"VerQueryValueA failed for {}: {}",
|
"VerQueryValueW(\\VarFileInfo\\Translation) failed for {}",
|
||||||
filename.filename(),
|
filename);
|
||||||
get_last_error_string());
|
print_summary();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (cbTranslate == 0 || lpTranslate == nullptr) {
|
if (cbTranslate < sizeof(*lpTranslate) ||
|
||||||
log_debug(
|
cbTranslate % sizeof(*lpTranslate) != 0 || lpTranslate == nullptr) {
|
||||||
|
log_misc(
|
||||||
"libutils",
|
"libutils",
|
||||||
"VerQueryValueA returned invalid results for {}",
|
"Invalid version translation table for {} ({} bytes)",
|
||||||
filename.filename());
|
filename,
|
||||||
|
cbTranslate);
|
||||||
|
print_summary();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,36 +483,46 @@ void libutils::print_dll_info(std::filesystem::path filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// StringFileInfo helper
|
// StringFileInfo helper
|
||||||
auto query_string = [&](const char* key) -> std::string {
|
auto query_string = [&](const char* key, WORD language, WORD page) -> std::string {
|
||||||
std::string subBlock = fmt::format(
|
std::string subBlock = fmt::format(
|
||||||
"\\StringFileInfo\\{:04x}{:04x}\\{}",
|
"\\StringFileInfo\\{:04x}{:04x}\\{}",
|
||||||
lang, codepage, key
|
language, page, key
|
||||||
);
|
);
|
||||||
|
|
||||||
char* value = nullptr;
|
wchar_t *value = nullptr;
|
||||||
UINT size_out = 0;
|
UINT size_out = 0;
|
||||||
|
|
||||||
if (VerQueryValueA(data.get(), subBlock.c_str(), (LPVOID*)&value, &size_out) && value) {
|
const auto wide_sub_block = s2ws(subBlock);
|
||||||
return value;
|
if (VerQueryValueW(data.get(), wide_sub_block.c_str(), (LPVOID*)&value, &size_out) &&
|
||||||
|
value != nullptr && size_out > 0 && value[size_out - 1] == L'\0') {
|
||||||
|
return fmt::detail::to_utf8<wchar_t>(
|
||||||
|
std::wstring_view(value, size_out - 1),
|
||||||
|
fmt::detail::to_utf8_error_policy::replace).str();
|
||||||
}
|
}
|
||||||
log_debug(
|
log_misc(
|
||||||
"libutils",
|
"libutils",
|
||||||
"VerQueryValueA({}) failed for {}: {}",
|
"Missing or invalid version string {} for {}",
|
||||||
subBlock,
|
subBlock,
|
||||||
filename.filename(),
|
filename);
|
||||||
get_last_error_string());
|
|
||||||
return "";
|
return "";
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto company_name = query_string("CompanyName");
|
auto query_translations = [&](const char *key) {
|
||||||
const auto product_name = query_string("ProductName");
|
auto value = query_string(key, lang, codepage);
|
||||||
const auto version_str = query_string("FileVersion");
|
for (UINT i = 0; value.empty() && i < cbTranslate / sizeof(*lpTranslate); i++) {
|
||||||
|
const auto &translation = lpTranslate[i];
|
||||||
|
if (translation.wLanguage != lang || translation.wCodePage != codepage) {
|
||||||
|
value = query_string(key, translation.wLanguage, translation.wCodePage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
log_info(
|
company_name = query_translations("CompanyName");
|
||||||
"libutils",
|
product_name = query_translations("ProductName");
|
||||||
"DLL info for {}: CompanyName = {}, ProductName = {}, Version = {}",
|
const auto string_version = query_translations("FileVersion");
|
||||||
filename.filename(),
|
if (!string_version.empty()) {
|
||||||
company_name.empty() ? "?" : company_name,
|
version_str = string_version;
|
||||||
product_name.empty() ? "?" : product_name,
|
}
|
||||||
version_str.empty() ? "?" : version_str);
|
print_summary();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ namespace libutils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void check_duplicate_dlls();
|
void check_duplicate_dlls();
|
||||||
void warn_if_dll_exists(const std::string &file_name);
|
bool warn_if_dll_exists(const std::string &file_name);
|
||||||
|
|
||||||
// get module handle helpers
|
// get module handle helpers
|
||||||
HMODULE get_module(const char *module_name);
|
HMODULE get_module(const char *module_name);
|
||||||
|
|||||||
Reference in New Issue
Block a user