diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 1d862e0..1ff1f09 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -19,19 +19,6 @@ jobs: with: python-version: '3.x' # cache: 'pip' - - uses: actions/cache@v4 - id: cache-venv - with: - path: venv - key: setup-venv2-${{ runner.os }}-py-${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('requirements.txt') }} - restore-keys: | - setup-venv2-${{ runner.os }}-py-${{ steps.setup-python.outputs.python-version }}- - - name: Setup venv - run: | - python -m venv venv - venv/Scripts/python.exe -m pip install --upgrade pip setuptools wheel - venv/Scripts/python.exe -m pip install -r requirements.txt - if: steps.cache-venv.outputs.cache-hit != 'true' - uses: ilammy/msvc-dev-cmd@v1 with: arch: win32 diff --git a/.gitignore b/.gitignore index 07df22d..c90b71a 100644 --- a/.gitignore +++ b/.gitignore @@ -87,7 +87,6 @@ venv.bak/ # Flame /build/ -/src/ /tools/obj_research/ /libs/dkii_exe/api/ /install/ diff --git a/.idea/vcs.xml b/.idea/vcs.xml index c3e3123..94a25f7 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index de00a08..9429041 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,18 +64,6 @@ add_subdirectory(mapping) add_subdirectory(tools) add_subdirectory(libs) -if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/src/CMakeLists.txt") - # apply patches - execute_process( - COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${CMAKE_HOME_DIRECTORY}/mapping - ${Python3_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/apply_patches.py - OUTPUT_VARIABLE GIT_COMMIT_SUBJECT - ) -endif () -if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/src/CMakeLists.txt") - message(FATAL_ERROR "src/CMakeLists.txt is not exist.\nPlease run apply_patches.py") -endif () - if(USE_CONFIGURE_DATE) string(TIMESTAMP CONFIGURE_DATE "%y%m%d") endif () diff --git a/apply_patches.py b/apply_patches.py deleted file mode 100644 index 54da123..0000000 --- a/apply_patches.py +++ /dev/null @@ -1,72 +0,0 @@ -import pathlib -import os -import shutil - -import git -import git.exc - - -def read_message(file: pathlib.Path): - with open(file, 'r') as f: - for i in range(8): - line = f.readline() - if line.startswith('Subject: [PATCH] '): - return line.rstrip()[len('Subject: [PATCH] '):] - raise Exception("message not found") - - -def failed_changed_repo(message): - print(f"repo already exists in src and {message}") - print("delete src directory to apply existing patches or rebuild patches from your changes") - exit(-1) - - -def assert_no_new_changes(repo: git.Repo, patches: list[pathlib.Path]): - commits = [commit.message.rstrip() for commit in repo.iter_commits()] - patches = [read_message(file) for file in reversed(patches)] - if commits != patches: - return failed_changed_repo("some commit changes are made") - if repo.untracked_files: - return failed_changed_repo("there new files in repo") - if repo.index.diff(None): - return failed_changed_repo("there unstaged changes") - if repo.index.diff("HEAD"): - return failed_changed_repo("there staged changes") - - -def main(): - patches_dir = pathlib.Path(__file__).parent / 'patches' - patches = [file for file in patches_dir.iterdir() if file.name.endswith('.patch')] - - repo_dir = pathlib.Path(__file__).parent / 'src' - - if not repo_dir.exists(): - repo_dir.mkdir() - - try: - repo = git.Repo(repo_dir) - if repo.branches: # not empty repo - try: - repo.git.am("--abort") - except git.exc.GitError: - pass - assert_no_new_changes(repo, patches) - print("reapply patches") - shutil.rmtree(repo_dir, ignore_errors=True) - repo = git.Repo.init(repo_dir, initial_branch='main') - except git.exc.GitError: - repo = git.Repo.init(repo_dir, initial_branch='main') - with repo.config_writer() as w: - w.set_value("user", "name", "ember") - w.set_value("user", "email", "ember@users.noreply.github.com") - os.chdir(repo.working_dir) - - # repo.git.am("--3way", *[f"../patches/{file.name}" for file in patches]) - for file in patches: - print(f"apply {file}") - repo.git.am("--3way", f"../patches/{file.name}") - - -if __name__ == '__main__': - main() - diff --git a/git_notes.md b/git_notes.md deleted file mode 100644 index 875f337..0000000 --- a/git_notes.md +++ /dev/null @@ -1,27 +0,0 @@ - -### add new patch -```bat -cd src -:: make changes -git add . -git commit -m"fix_improve_whatever" -python ../rebuild_patches.py -``` - -### edit existing patch -```bat -cd src -gitk --all & :: locate hash of commit -git checkout -:: make changes -git add . -git commit --amend --no-edit -git rebase --onto HEAD main -git checkout main -python ../rebuild_patches.py -``` - -### get first commit hash -```bat -git rev-list --max-parents=0 HEAD -``` diff --git a/mapping/DKII_EXE_v170.sgmap b/mapping/DKII_EXE_v170.sgmap index c377f81..9cd618f 100644 --- a/mapping/DKII_EXE_v170.sgmap +++ b/mapping/DKII_EXE_v170.sgmap @@ -4783,20 +4783,11 @@ struct: id=vtbl_0066EF3C,name=CFrontEndComponent,size=201210,vtable=instance_006 type: kind=int,size=1 field: name=field_670D type: kind=int,size=1 - field: name=gap_670E - type: kind=array,count=19 - type: kind=int,size=1 - field: name=field_6721 - type: kind=int,size=1,signed=True,winapi=char - field: name=gap_6722 - type: kind=array,count=15 + field: name=f670e_gap + type: kind=array,count=3 type: kind=int,size=1 - field: name=field_6731 - type: kind=int,size=4,signed=True - field: name=field_6735 - type: kind=int,size=4,signed=True - field: name=surf63 - type: kind=struct,id=constructor_005B5270 + field: name=f6711_titleScreen + type: kind=struct,id=instance_0079D200 field: name=aabb16_x16x30x6 type: kind=array,count=16 type: kind=array,count=30 @@ -4893,8 +4884,9 @@ struct: id=vtbl_0066EF3C,name=CFrontEndComponent,size=201210,vtable=instance_006 type: kind=int,size=1,signed=True,winapi=char field: name=field_3033C type: kind=int,size=4,signed=True - field: name=field_30340 - type: kind=int,size=4,signed=True + field: name=f30340_pMyDdSurfaceEx + type: kind=ptr + type: kind=struct,id=instance_0079D200 field: name=field_30344 type: kind=int,size=1,signed=True,winapi=char field: name=field_30345 @@ -48082,7 +48074,7 @@ global: va=00553FE0,name=TbTQILoader::fun_553FE0,size=30 arg: kind=ptr type: kind=int,size=4 arg: kind=int,size=1,signed=True,winapi=char -global: va=00554000,name=DiscFileBase_554000,size=164 +global: va=00554000,name=DiscFileBase_open,size=164 type: kind=function,declspec=cdecl ret: kind=int,size=4,signed=True arg: kind=ptr @@ -49380,7 +49372,7 @@ global: va=0055D340,name=sub_55D340,size=486 arg: kind=ptr type: kind=ptr,winapi=HKEY type: kind=winapi,name=HKEY__,size=4 -global: va=0055D530,name=sub_55D530,size=6 +global: va=0055D530,name=getResourceExtensionFlags,size=6 type: kind=function,declspec=stdcall ret: kind=int,size=4,signed=True global: va=0055D540,name=sub_55D540,size=16 diff --git a/readme.md b/readme.md index 8a6a15b..5d544c4 100644 --- a/readme.md +++ b/readme.md @@ -6,11 +6,7 @@ Difference from the previous implementation(https://github.com/DiaLight/Ember): For a more detailed description of how Flame works, read `how_it_works.md` -Partially decompiled code is stored as git patches in the `patches` folder. -To build them to a local git repository, use the `apply_patches.py` script. -After changing any commit in the repository, do not forget to pack it back into patches using the `rebuild_patches.py` script. - -The latest version can be taken from the automatic build +The latest build can be taken from the github actions Requirements: - CMake 3.25 or higher https://cmake.org/download/ diff --git a/rebuild_patches.py b/rebuild_patches.py deleted file mode 100644 index 4bc85aa..0000000 --- a/rebuild_patches.py +++ /dev/null @@ -1,79 +0,0 @@ -import pathlib -import os -import re -import shutil - -import git.exc -from git import Repo - - -HASH1_RE = re.compile("From ([a-z0-9]{32,}) (.*)") - - -def hash1_re(m: re.Match): - hash = '0' * len(m.group(1)) - return f"From {hash} {m.group(2)}" - - -HASH2_RE = re.compile("index ([a-z0-9]{7,})\\.\\.([a-z0-9]{7,})(.*)") - - -def hash2_re(m: re.Match): - hash = '0' * len(m.group(1)) - return f"index {hash}..{hash}{m.group(3)}" - - -def cleanup_line(line: str): - line = HASH1_RE.sub(hash1_re, line) - line = HASH2_RE.sub(hash2_re, line) - return line - - -def cleanup_patch(file: pathlib.Path): - print('cleanup', file) - with open(file, 'r') as f: - lines = f.readlines() - - lines = [cleanup_line(line) for line in lines] - assert lines[-3].rstrip() == '--' - del lines[-3] - del lines[-2] - del lines[-1] - - with open(file, 'w') as f: - f.writelines(lines) - - -def main(): - patches_dir = pathlib.Path(__file__).parent / 'patches' - repo_dir = pathlib.Path(__file__).parent / 'src' - - if patches_dir.exists(): - shutil.rmtree(patches_dir, ignore_errors=True) - patches_dir.mkdir() - - repo_dir.mkdir(exist_ok=True) - - try: - repo = Repo(repo_dir) - except git.exc.GitError: - print("no repo to build patches from") - exit(-1) - os.chdir(repo.working_dir) - - repo.git.format_patch( - "--no-stat", # Generate plain patches without any diffstats - "--minimal", # Spend extra time to make sure the smallest possible diff is produced - "-N", # Name output in [PATCH] format - "-o", "../patches", # output dir - "-10000", "main" # from to - ) - patches = [file for file in patches_dir.iterdir() if file.name.endswith('.patch')] - for file in patches: - cleanup_patch(file) - - print("patches rebuilt") - - -if __name__ == '__main__': - main() diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index dba1edf..0000000 --- a/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -gitdb==4.0.11 -GitPython==3.1.43 -smmap==5.0.1 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..2f0cb9e --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,56 @@ +set(TARGET dkii_flame) + +add_executable(${TARGET} + main.cpp + dkii_exe_functions.cpp + dk2/MyResources.cpp + dk2/MyGame.cpp + dk2/MyDxMouse.cpp + dk2/CFrontEndComponent.cpp + + patches/replace_mouse_dinput_to_user32.cpp + patches/use_wheel_to_zoom.cpp + patches/micro_patches.cpp + + ${DKII_RESOURCES_FILE} + ) +target_include_directories(${TARGET} PRIVATE ${CMAKE_CURRENT_LIST_DIR}) +target_compile_definitions(${TARGET} PRIVATE + DIRECTINPUT_VERSION=0x0500 + DIRECT3D_VERSION=0x0600 + ) +target_link_libraries(${TARGET} PRIVATE + dkii_delinked dkii_exe_api +# gog_patch_dll + + # dk2 specific libs + qmixer_genlib # generated in qmixer_dll/genlib + weanetr_genlib # generated in weanetr_dll/genlib + + # the others libs dk2 depends on + winmm + dinput_genlib # generated in dinput_dll/genlib + ddraw # patch - dont link against gog's PATCH.dll because patch addresses are change after recompilation + # also i fully decompiled gog's PATCH.dll and bundled in project + + imm32 + # wsock32 is old link target + ws2_32 # should be better + dsound + ) + +target_compile_options(${TARGET} PRIVATE + /Gy # Enable Function-Level Linking + /Gw # Optimize Global Data + /GS- + /Gz # use __stdcall by default + ) +target_link_options(${TARGET} PRIVATE /OPT:NOREF) +target_link_options(${TARGET} PRIVATE /MAP) # generate msvc mapping file for exe +set_property(TARGET ${TARGET} PROPERTY + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") +set_target_properties(${TARGET} PROPERTIES OUTPUT_NAME + "DKII-Flame${OUTPUT_SUFFIX}" +) + + diff --git a/src/dk2/CFrontEndComponent.cpp b/src/dk2/CFrontEndComponent.cpp new file mode 100644 index 0000000..eb021cd --- /dev/null +++ b/src/dk2/CFrontEndComponent.cpp @@ -0,0 +1,37 @@ +// +// Created by DiaLight on 21.07.2024. +// +#include +#include "dk2_globals.h" +#include "dk2_functions.h" +#include "patches/micro_patches.h" + + +void dk2::CFrontEndComponent::showTitleScreen() { + char Buffer[260]; + if (MyResources_instance.myKeyboard.f109 == 17 ) { + sprintf(Buffer, "TitleScreen\\TitleScreen-Japanese"); + } else { + char *LayoutName = MyResources_instance.myKeyboard.getLayoutName(); + sprintf(Buffer, "TitleScreen\\TitleScreen"); + if ( _strcmpi(LayoutName, "english") ) + sprintf(Buffer, "TitleScreen\\TitleScreen-%s", LayoutName); + } + unsigned __int16 extensionFlags = getResourceExtensionFlags(); + uint32_t status; + loadArtToSurface( + &status, + &this->titleScreen, + &MyResources_instance.frontEndFileMan, + Buffer, + extensionFlags); + if ( status >= 0 ) { + static_MyDdSurfaceEx_BltWait(&status, this->pMyDdSurfaceEx, 0, 0, &this->titleScreen, 0, 0); + MyGame_instance.prepareScreen(); + if ( MyDdSurface_addRef(&this->titleScreen.dd_surf, 0) ) + MyDdSurface_release(&status, &this->titleScreen.dd_surf); + DWORD waitEnd = getTimeMs() + 10000; + while ( getTimeMs() <= waitEnd && !skippable_title_screen::skipKeyPressed() ) ; + MyGame_instance.prepareScreen(); + } +} diff --git a/src/dk2/MyDxMouse.cpp b/src/dk2/MyDxMouse.cpp new file mode 100644 index 0000000..b50f6c0 --- /dev/null +++ b/src/dk2/MyDxMouse.cpp @@ -0,0 +1,122 @@ +// +// Created by DiaLight on 09.07.2024. +// +#include + +#define UMDF_USING_NTSTATUS + +#include "dk2/MyDxMouse.h" +#include "dk2/MyDxInputManagerCb.h" +#include "dk2/MouseXyzDxAction.h" +#include "dk2/MouseRgbDxAction.h" +#include "dk2/ControlKeysUpdater.h" +#include "dk2_functions.h" +#include "patches/replace_mouse_dinput_to_user32.h" +#include "patches/use_wheel_to_zoom.h" + +int *dk2::MyDxInputManagerCb::initMouse(int *pstatus) { + if (this->f58_pdxmouse) { + *pstatus = 0xCFFE0102; + return pstatus; + } + if (replace_mouse_dinput_to_user32::enabled) { + *pstatus = STATUS_SUCCESS; + return pstatus; + } + int status; + MyDxMouse_create(&status, &this->f58_pdxmouse); + if (FAILED(status)) { + *pstatus = status; + return pstatus; + } + this->fC_async.setObjAndSignal(&status, (uint32_t *) this->f58_pdxmouse); + if (FAILED(status)) { + *pstatus = status; + return pstatus; + } + this->f58_pdxmouse->setControlKeysUpdater(this->f5C_controlKeys); + this->f58_pdxmouse->dx_device.updateCoopLevel_acquire(&status); + *pstatus = STATUS_SUCCESS; + return pstatus; +} + +int *__cdecl dk2::MyDxMouse_create(int *pstatus, MyDxMouse **pObj) { + if (pObj == nullptr) { + *pstatus = 0x80004003; + return pstatus; + } + MyDxMouse *obj = (MyDxMouse *) operator new(0x5Cu); + if (obj == nullptr) { + *pstatus = 0xCFFE0100; + return pstatus; + } + obj->constructor(); + HRESULT result; + obj->initDevice_0(&result); + if (FAILED(result)) { + obj->v_scalar_destructor(1); + *pstatus = result; + return pstatus; + } + *pObj = obj; + *pstatus = STATUS_SUCCESS; + return pstatus; +} + +void dk2::MyDxMouse::handleData(int count) { + if (!count) return; + for (int i = 0; i < count; ++i) { + DIDEVICEOBJECTDATA &pdevObj = this->f2C_pdevObjArr[i]; + switch (pdevObj.dwOfs) { + case 0: // x y z + case 4: + case 8: { + MouseXyzDxAction *xyz = this->listXYZ.getOrCreateUnhandled(); + int f8_dwTimeStamp = pdevObj.dwTimeStamp; + int f0_dwOfs = pdevObj.dwOfs; + xyz->value = pdevObj.dwData; // relative or absolute motion + xyz->actedAxe = f0_dwOfs; + xyz->timestamp = f8_dwTimeStamp; + xyz->isNotHandled = 1; + this->f8_pcontrolkeys->v_fun3(xyz); + } break; + case 0xC: // rgbButtons + case 0xD: + case 0xE: + case 0xF: { + MouseRgbDxAction *action = this->listRGB.getOrCreateUnhandled(); + switch (pdevObj.dwOfs) { + case 0xC: + count = 0xF0; // left mouse + break; + case 0xD: + count = 0xF1; // right mouse + break; + case 0xE: + count = 0xF2; // middle mouse + break; + case 0xF: + count = 0xF3; // unk mouse + break; + default: + break; + } + char f4_dwData = pdevObj.dwData; + int dwTimeStamp = pdevObj.dwTimeStamp; + int isPressed = ((unsigned int) pdevObj.dwData >> 4) & 8; + action->KeyCode_F0toF3 = count; + action->btnPressFlags = isPressed; + action->pos.x = 0; + action->pos.y = 0; + action->data = f4_dwData; + action->timestamp = dwTimeStamp; + action->isNotHandled = 1; + this->f8_pcontrolkeys->v_fun4(action); + } break; + default: + break; + } + use_wheel_to_zoom::dinput_proc(&pdevObj); + } +} + diff --git a/src/dk2/MyGame.cpp b/src/dk2/MyGame.cpp new file mode 100644 index 0000000..bff656b --- /dev/null +++ b/src/dk2/MyGame.cpp @@ -0,0 +1,182 @@ +// +// Created by DiaLight on 08.07.2024. +// +#include "dk2/MyGame.h" +#include "dk2/utils/Pos2i.h" +#include "dk2/utils/AABB.h" +#include "dk2/DxDeviceInfo.h" +#include "dk2/DxModeInfo.h" +#include "dk2_globals.h" +#include "dk2_functions.h" +#include "patches/micro_patches.h" + +int dk2::MyGame::prepareScreenEx( + uint32_t dwWidth, + uint32_t dwHeight, + uint32_t dwRGBBitCount, + int isWindowed, + int screenSwap, + int screenHardware3D) { + if(control_windowed_mode::enabled) { + printf("prepareScreen %p %dx%d %d %d %d %d\n", this, dwWidth, dwHeight, dwRGBBitCount, isWindowed, screenSwap, screenHardware3D); + isWindowed = true; // todo: control + } + int sel_dd_idx = this->selected_dd_idx; + if (sel_dd_idx != this->last_selected_dd_idx) { + MyResources_instance.video_settings.sub_566E40(sel_dd_idx); + MyResources_instance.video_settings.sub_566F40(0); + MyResources_instance.video_settings.sub_566EC0(0); + if (isGameWindowCreated == 1) { + setDebugStringFun(debugMsgBox); + this->zbufferSurf = 0; + this->c_window_test.recreate(); + IDirect3D2 *f6D_pIDirect3D2 = this->pIDirect3D2; + if (f6D_pIDirect3D2) { + f6D_pIDirect3D2->Release(); + this->pIDirect3D2 = 0; + } + uint32_t status; + dk2wnd_cleanup(&status); + BullfrogWindow_destroy(); + isGameWindowCreated = 0; + } + int result = this->createWindow(0); + if (!result) + return result; + } + int last_selected_dd_idx = this->last_selected_dd_idx; + int ddraw_idx; + DxDeviceInfo *v13; + int f1FE_modeListCount; + if (last_selected_dd_idx >= ddraw_device_count + || (ddraw_idx = 0, + v13 = &ddraw_devices[last_selected_dd_idx], + f1FE_modeListCount = v13->modeListCount, + f1FE_modeListCount <= 0)) { + LABEL_14: + MyGame_debugMsg(this, "Screen Mode %d*%d (%d bpp) is not available\n", dwWidth, dwHeight, dwRGBBitCount); + return 0; + } + DxModeInfo *f206_modeList = v13->modeList; + while (f206_modeList->dwWidth != dwWidth + || f206_modeList->dwHeight != dwHeight + || f206_modeList->dwRGBBitCount != dwRGBBitCount) { + ++ddraw_idx; + ++f206_modeList; + if (ddraw_idx >= f1FE_modeListCount) + goto LABEL_14; + } + void (__cdecl **fE89_WM_ACTIVATE_callbacks)(int, uint32_t, uint32_t, void *); + fE89_WM_ACTIVATE_callbacks = this->WM_ACTIVATE_callbacks; + int left = 8; + void (__cdecl **callbacks)(int, uint32_t, uint32_t, void *); + callbacks = this->WM_ACTIVATE_callbacks; + do { + if (*fE89_WM_ACTIVATE_callbacks) + (*fE89_WM_ACTIVATE_callbacks)(2, 0, 0, fE89_WM_ACTIVATE_callbacks[8]); + ++fE89_WM_ACTIVATE_callbacks; + --left; + } while (left); + setDebugStringFun(debugMsgBox); + int screenHardware3D_ = screenHardware3D; + bool isFullscreen = isWindowed == 0; + int screenSwap_ = screenSwap; + this->zbufferSurf = 0; + int initFlags; + if (isFullscreen) { + if (screenSwap_) { + initFlags = 1; + if (screenHardware3D_) + initFlags = 0x49; + } else { + initFlags = 2; + if (screenHardware3D_) + initFlags = 0x4A; + } + } else if (screenSwap_) { + initFlags = 0x11; + } else { + initFlags = 0x10; + if (screenHardware3D_) + initFlags = 0x58; + } + if (!cmd_flag_NOSOUND && MySound_ptr->v_sub_567210()) + MySound_ptr->v_fun_5677D0(); + this->c_window_test.recreate(); + int dwHeight_; + int dwRGBBitCount_; + if (isWindowed) { + dwHeight_ = dwHeight; + AABB aabb; + aabb.minX = 50; + aabb.minY = 50; + aabb.maxX = dwWidth + 50; + aabb.maxY = dwHeight + 50; + if (*this->c_window_test.probably_do_show_window_ev0_7(&dwHeight, &aabb) < 0) + return 0; + dwRGBBitCount_ = dwRGBBitCount; + } else { + dwRGBBitCount_ = dwRGBBitCount; + int status; + if (*dk2dd_init(&status, dwWidth, dwHeight, dwRGBBitCount, initFlags, 0) < 0) { + process_win_inputs(); + if (*dk2dd_init(&status, dwWidth, dwHeight, dwRGBBitCount_, initFlags, 0) < 0) + return 0; + } + dwHeight_ = dwHeight; + } + if (!cmd_flag_NOSOUND) { + if (MySound_ptr->v_sub_567210()) + MySound_ptr->v_fun_5677E0(); + else + MySound_ptr->v_set_number_of_channels( + MyResources_instance.obj_29CB.numberOfChannels); + MyResources_instance.obj_29CB.resolveValues(); + } + int screenSwap__1 = screenSwap; + this->isWindowed = isWindowed; + this->dwWidth = dwWidth; + this->dwHeight = dwHeight_; + this->dwRGBBitCount = dwRGBBitCount_; + this->_prepareScreen_a6 = screenSwap__1; + this->_prepareScreen_a7 = screenHardware3D_; + this->f18 = 0; + this->collect3dDevices(); + this->f4C_.fun_559820(0); + setDebugStringFun(MyGame_static_559050_parse); + if (MyResources_instance.video_settings.zbuffer_bitnes == 16) { + if (!this->createZBufferSurf(0x10u) && !this->createZBufferSurf(0x20u)) + this->createZBufferSurf(0x18u); + } else if (MyResources_instance.video_settings.zbuffer_bitnes == 32 + && !this->createZBufferSurf(0x20u) + && !this->createZBufferSurf(0x18u)) { + this->createZBufferSurf(0x10u); + } + // move mouse to center + int mousePos_y = (unsigned int) this->dwHeight >> 1; + Pos2i mousePos; + mousePos.x = (unsigned int) this->dwWidth >> 1; + mousePos.y = mousePos_y; + MyInputManagerCb_static_setMousePos(&mousePos); + // direct invoke mouse updater + AABB updateMousePos; + updateMousePos.minX = 0; + updateMousePos.minY = 0; + updateMousePos.maxX = dwWidth; + updateMousePos.maxY = dwHeight_; + MyInputManagerCb_static_updateMouse(&updateMousePos); + void (__cdecl **callbacks_)(int, uint32_t, uint32_t, void *); // esi + callbacks_ = callbacks; + int left2 = 8; + do { + if (*callbacks_) + (*callbacks_)(3, 0, 0, callbacks_[8]); + ++callbacks_; + --left2; + } while (left2); + MyResources_instance.video_settings.sub_566F40(0); + MyResources_instance.video_settings.sub_566EC0(1); + HWND HWindow = getHWindow(); + ij_ImmAssociateContext(HWindow, 0); + return 1; +} diff --git a/src/dk2/MyResources.cpp b/src/dk2/MyResources.cpp new file mode 100644 index 0000000..a9378a8 --- /dev/null +++ b/src/dk2/MyResources.cpp @@ -0,0 +1,77 @@ +// +// Created by DiaLight on 08.07.2024. +// +#include "dk2/MyResources.h" +#include "dk2_functions.h" +#include "dk2_globals.h" +#include "patches/micro_patches.h" + + +dk2::MyResources *dk2::MyResources::init_resources() { + this->meshesFileMan.constructor(); + int v9 = '\b'; + this->devMeshesFileMan.constructor(); + this->engineTexturesFileMan.constructor(); + this->textureFileMan.constructor(); + this->editorFileMan.constructor(); + this->paletteFileMan.constructor(); + this->spriteFileMan.constructor(); + this->textsFileMan.constructor(); + this->pathsFileMan.constructor(); + this->frontEndFileMan.constructor(); + this->f0 = '\0'; + v9 = (uint8_t) 9; + char exeDir[260]; + _strcpy(exeDir, "D:\\DEV\\DK2\\"); + char *CommandLineA = GetCommandLineA(); + char *cmdl = CommandLineA; + char *str_end; + if (*CommandLineA == '"') { + cmdl = CommandLineA + 1; + str_end = strchr(CommandLineA + 1, '"'); + goto LABEL_5; + } + str_end = strchr(CommandLineA + 1, ' '); + if (!str_end) { + str_end = &cmdl[strlen(cmdl)]; + LABEL_5: + if ( !str_end ) goto LABEL_12; + } + if (str_end > cmdl) { + do { + if ( *str_end == '\\' ) break; + --str_end; + } while( str_end > cmdl ); + if (str_end > cmdl) str_end[1] = '\0'; + } + _strcpy(exeDir, cmdl); + LABEL_12: + if(use_cwd_as_dk2_home_dir::enabled) { + GetCurrentDirectoryA(MAX_PATH, exeDir); + strcat(exeDir, "\\"); + printf("replace exe dir path2: %s -> %s\n", cmdl, exeDir); + } + MyGame_debugMsg(&MyGame_instance, "HD Path: %s\n", exeDir); + _strcpy(this->executableDir, exeDir); + this->resolveMovies(); + sprintf(this->editorDir, "%sdata\\editor\\", this->executableDir); + sprintf(this->savesDir, "%sdata\\Save\\", this->executableDir); + sprintf(this->settingsDir, "%sdata\\Settings\\", this->executableDir); + sprintf(this->globalDir, "GLOBAL\\"); + sprintf(this->textsDir, "%sdata\\Text\\", this->executableDir); + sprintf(this->textureCacheDir, "%s\\Dk2TextureCache", this->executableDir); + sprintf(this->soundSfxDir, "%sdata\\sound\\SFX\\", this->executableDir); + sprintf(this->soundMusicDir, "%sdata\\sound\\Music\\", this->executableDir); + uint32_t status; + CFileManager_readAndParseWad(&status, &this->meshesFileMan, "%sdata\\Meshes.Wad", this->executableDir); + CFileManager_readAndParseWad(&status, &this->devMeshesFileMan, "K:\\DK2\\Dev\\Data\\Meshes.Wad", this->executableDir); + CFileManager_readAndParseWad(&status, &this->engineTexturesFileMan, "%sdata\\EngineTextures.wad", this->executableDir); + CFileManager_readAndParseWad(&status, &this->spriteFileMan, "%sdata\\Sprite.Wad", this->executableDir); + CFileManager_readAndParseWad(&status, &this->frontEndFileMan, "%sdata\\FrontEnd.wad", this->executableDir); + CFileManager_readAndParseWad(&status, &this->pathsFileMan, "%sdata\\Paths.wad", this->executableDir); + CFileManager_setPathFormat(&status, &this->editorFileMan, "%sdata\\editor", this->executableDir); + CFileManager_setPathFormat(&status, &this->textsFileMan, "%sdata\\text\\", this->executableDir); + CFileManager_setPathFormat(&status, &this->textureFileMan, "%sdata\\Texture", this->executableDir); + CFileManager_setPathFormat(&status, &this->paletteFileMan, "%sdata\\palette", this->executableDir); + return this; +} diff --git a/src/dkii_exe_functions.cpp b/src/dkii_exe_functions.cpp new file mode 100644 index 0000000..0903507 --- /dev/null +++ b/src/dkii_exe_functions.cpp @@ -0,0 +1,107 @@ +// +// Created by DiaLight on 01.07.2024. +// +#include "dk2/MyGame.h" +#include "dk2/MyDxInputState.h" +#include "dk2/MyMouseUpdater.h" +#include "dk2_functions.h" +#include "dk2_globals.h" +#include "patches/replace_mouse_dinput_to_user32.h" +#include "patches/micro_patches.h" +#include "patches/use_wheel_to_zoom.h" +#include + +int32_t dk2::MyGame::isOsCompatible() { + if(add_win10_support::enabled) { + return !dk2::isOsVersionGE(11, 0, 0); + } + return !isOsVersionGE(6, 0, 0); +} + +void dk2::resolveDk2HomeDir() { + if(use_cwd_as_dk2_home_dir::enabled) { + char tmp[MAX_PATH]; + DWORD len = GetCurrentDirectoryA(MAX_PATH, tmp); + strcpy(tmp + len, "\\"); + printf("replace exe dir path1: %s -> %s\n", dk2::dk2HomeDir, tmp); + strcpy(dk2::dk2HomeDir, tmp); + return; + } + const char *CommandLineA = GetCommandLineA(); + _strncpy(pathBuf, CommandLineA, 259u); + char firstChar = pathBuf[0]; + pathBuf[259] = 0; + char sepChar = ' '; + if ( pathBuf[0] == '"' ) { + signed int idx = 0; + sepChar = '"'; + unsigned int len = strlen(pathBuf) + 1; + if ( (int)(len - 1) > 0 ) { + do { + pathBuf[idx] = pathBuf[idx + 1]; + ++idx; + } while ( idx < (int)(len - 1) ); + firstChar = pathBuf[0]; + } + } + char *pos = pathBuf; + if ( firstChar ) { + char curChar = firstChar; + do + { + if ( curChar == sepChar ) + break; + curChar = *++pos; + } + while ( curChar ); + } + *pos = 0; + char *sep1Pos = strrchr(pathBuf, '/'); + char *sep2Pos = strrchr(pathBuf, '\\'); + char **pSepPos = &sep2Pos; + if ( sep2Pos <= sep1Pos ) pSepPos = &sep1Pos; + char *sepPos = *pSepPos; + if ( sepPos ) { + sepPos[1] = 0; + setExeDirPath(pathBuf); + } +} + +LRESULT dk2::CWindowTest_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { + replace_mouse_dinput_to_user32::emulate_dinput_from_user32(hWnd, Msg, wParam, lParam); + fix_mouse_pos_on_resized_window::window_proc(hWnd, Msg, wParam, lParam); + use_wheel_to_zoom::window_proc(hWnd, Msg, wParam, lParam); + fix_keyboard_state_on_alt_tab::window_proc(hWnd, Msg, wParam, lParam); + bring_to_foreground::window_proc(hWnd, Msg, wParam, lParam); + fix_close_window::window_proc(hWnd, Msg, wParam, lParam); + switch(Msg) { + case WM_ACTIVATE: + g_isNeedBlt_fullscr = wParam != 0; + break; + case WM_SYSCOMMAND: { + switch ( wParam ) { + case 0xF090u: + case 0xF093u: + case 0xF100u: + case 0xF160u: + case 0xF163u: + return 0; + default: + break; + } + break; + } + case WM_MOUSEMOVE: { + Pos2i pos; + pos.x = LOWORD(lParam); + pos.y = HIWORD(lParam); + MyInputManagerCb_static_setMousePos(&pos); + break; + } + } + if ( !getCustomDefWindowProcA() ) + return DefWindowProcA(hWnd, Msg, wParam, lParam); + typedef LRESULT (__stdcall *CustomDefWindowProcA_t)(HWND, UINT, WPARAM, LPARAM); + auto CustomDefWindowProcA = (CustomDefWindowProcA_t) getCustomDefWindowProcA(); + return CustomDefWindowProcA(hWnd, Msg, wParam, lParam); +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..c74d59d --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,209 @@ +// +// Created by DiaLight on 19.06.2024. +// +#include "dk2_functions.h" +#include "dk2_globals.h" +#include "dk2/MyMutex.h" +#include "patches/micro_patches.h" + +namespace dk2 { + + bool dk2_main1(int argc, LPCSTR *argv); + bool dk2_main2(); + +} + +bool dk2::dk2_main2() { + MyGame_instance.f28D = cmd_flag_NOERRORS; + if ( MyResources_instance.video_settings.f9C ) + MyResources_instance.video_settings.sub_566DA0(); + if ( !cmd_flag_NOSOUND + && !MySound_ptr->v_sub_567210() + && !MySound_ptr->v_set_number_of_channels( + MyResources_instance.obj_29CB.numberOfChannels) ) { + MySound_ptr->v_fun_567410(); + cmd_flag_NOSOUND = 1; + } + if ( cmd_flag_NOSOUND || CSpeechSystem_instance.sub_567F90() ) { + if ( !WeaNetR_instance.init() ) { + WeaNetR_instance.sub_559CB0(); + if ( !cmd_flag_NOSOUND ) { + MySound_ptr->v_fun_567410(); + CSpeechSystem_instance.sub_568020(); + } + return false; + } + if ( !MyGame_instance.isOsCompatible() ) { + WeaNetR_instance.sub_559CB0(); + if ( !cmd_flag_NOSOUND ) MySound_ptr->v_fun_567410(); + return false; + } + if ( !all_components_fillStaticListeners() ) { + WeaNetR_instance.sub_559CB0(); + if ( !cmd_flag_NOSOUND ) MySound_ptr->v_fun_567410(); + return false; + } + if ( MyResources_instance.f2B2B ) { + MyResources_instance.useFe = 1; + MyResources_instance.f2A13 = 3; + } else if ( MyResources_instance.f2B2F == 1 ) { + MyResources_instance.useFe = 4; + MyResources_instance.f2A13 = 3; + } else if ( !cmd_flag_FrontEnd3D_unk8 ) { + if ( MyGame_instance.sub_559790() < 240.0 && getDevIdxSupportsLinearPerspective() != -1 + || MyGame_instance.sub_559790() < 290.0 && getDevIdxSupportsLinearPerspective() == -1 ) + { + MyResources_instance.f2B77 = 1; + } + MyResources_instance.useFe3d = 1; + MyResources_instance.useFe = 5; + wcsncpy(MyResources_instance.f2A17, L"FrontEnd3DLevel", 0x40u); + MyResources_instance.f2A17[63] = 0; + MyResources_instance.f2B1B = 0; + MyResources_instance.f2A13 = 3; + cmd_flag_FrontEnd3D_unk7 = 1; + } + CGameComponent *cur = &CGameComponent_instance; + while (cur != nullptr) { + if (!cur->v_handle()) break; + CGameComponent *next = cur->v_mainGuiLoop(); + cur->v_f10_(); + cur = next; + } + all_components_clearStaticListeners(); + WeaNetR_instance.sub_559CB0(); + CSpeechSystem_instance.sub_568020(); + if ( !cmd_flag_NOSOUND ) + MySound_ptr->v_fun_567410(); + } + return true; +} +bool dk2::dk2_main1(int argc, LPCSTR *argv) { + CoInitialize(0); + int status_2; + setLibIconName(&status_2, 1000); + struct _MEMORYSTATUS memoryStatus; + memset(&memoryStatus, 0, sizeof(memoryStatus)); + cmd_flag_NOSOUND = 0; + memoryStatus.dwLength = 32; + GlobalMemoryStatus(&memoryStatus); + if ( memoryStatus.dwAvailPhys + memoryStatus.dwAvailPageFile >= 0x12C00000 ) { + if ( memoryStatus.dwAvailPhys + memoryStatus.dwAvailPageFile < 0x15E00000 ) + g_fontType = 1; + } else { + g_fontType = 2; + } + int status; + struct _WIN32_FIND_DATAA FindFileData; + findFile(&status, *argv, &FindFileData, -1); + if (status >= 0) { + g_fileChecksum = FindFileData.ftLastWriteTime.dwLowDateTime + FindFileData.ftLastWriteTime.dwHighDateTime; + char *exeFilePath = (char *) argv[0]; + uint32_t hashsum_ = 0x5041554C; + uint32_t hashsum = 0x5041554C; + int status2; + TbDiscFile *pTbDiscFile; + if (*MyDiscFile_create(&status2, &pTbDiscFile, exeFilePath, 0x80000001) >= 0 ) { + int sizeLeft_ = TbDiscFile_getSize(pTbDiscFile); + int sizeLeft = sizeLeft_; + if ( sizeLeft_ > 0 ) { + char buf[8192]; + while (true) { + int blockSize = sizeLeft_; + if ( sizeLeft_ <= 0x2000 ) + memset(buf, 0, sizeof(buf)); + else + blockSize = 0x2000; + if ( (int)*TbDiscFile_readBytes(&status_2, pTbDiscFile, buf, blockSize, 0) < 0 ) { + hashsum_ = 0; + hashsum = 0; + sizeLeft = 0; + } else { + DWORD *pos = (DWORD *) buf; + sizeLeft -= blockSize; + int dwordsCount = (blockSize + 3) / 4; + if (dwordsCount > 0 ) { + do { + hashsum = _rotl(hashsum, 1); + hashsum_ = *pos++ ^ hashsum; + --dwordsCount; + hashsum = hashsum_; + } while(dwordsCount); + } + } + if (sizeLeft <= 0) break; + sizeLeft_ = sizeLeft; + } + } + int status_; + TbDiscFile_delete(&status_, pTbDiscFile); + } + g_fileHashsum = hashsum_; + closeFindFile(&status_2, (int)&FindFileData); + } + MyResources_instance.sub_55B120(); + if ( !parse_command_line(argc, argv) || !loadResources() ) return false; + bool useDefaultWindowName = true; + unsigned __int8 *MbString = MyMbStringList_idx1091_getMbString(42u); // "Dungeon Keeper II" + if ( MBToUni_convert(MbString, g_wchar_buf, 512) && unicodeToUtf8(g_wchar_buf, temp_string, 512) ) { + int status_; + setWindowName(&status_, temp_string); + useDefaultWindowName = false; + } + if (useDefaultWindowName) { + int status_; + setWindowName(&status_, "Bullfrog Productions Ltd"); + } + int DevIdxSupportsLinearPerspective = getDevIdxSupportsLinearPerspective(); + if ( (MyGame_instance.f50D & 0x800000) == 0 && DevIdxSupportsLinearPerspective == -1 ) { + unsigned __int8 *mbString1 = MyMbStringList_idx1091_getMbString(2u); + wchar_t wString1[512]; + if (MBToUni_convert(mbString1, wString1, 512)) { + unsigned __int8 *mbString2 = MyMbStringList_idx1091_getMbString(0xB60u); + wchar_t wString2[512]; + if (MBToUni_convert(mbString2, (wchar_t *)wString2, 512)) { + WCHAR Text[512]; + dk2::_swprintf(Text, L"%s\n\n%s", wString1, wString2); + HWND HWindow = getHWindow(); + MessageBoxW(HWindow, Text, g_wchar_buf, 0x10u); + return false; + } + } + } + bool success = MyGame_instance.init() && dk2_main2(); + MyGame_instance.release(); + releaseResources(); + CoUninitialize(); + return success; +} + +int __cdecl dk2::dk2_main(int argc, LPCSTR *argv) { + uint32_t finalStatus = 0; + MyMutex mutex; + mutex.constructor("DKII MUTEX"); + if (!mutex.alredyExists ) { + if(!dk2_main1(argc, argv)) { + finalStatus = -1; + mutex.destroy(); + return 0; + } + } else if(notify_another_instance_is_running::enabled) { + printf("[ERROR]: another instance of DK2 is running"); + } + + finalStatus = -1; + mutex.destroy(); + return 0; +} + +int dk2::WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, CHAR *lpCmdLine, int nShowCmd) { + setHInstance(hInstance); + return dk2_main(g_argc, g_argv); +} + +int main() { + // call entry point of DKII.EXE, + // initialize its runtime and call dk2::WinMain + dk2::dk2_start(); +} + diff --git a/src/patches/micro_patches.cpp b/src/patches/micro_patches.cpp new file mode 100644 index 0000000..4bbcfd5 --- /dev/null +++ b/src/patches/micro_patches.cpp @@ -0,0 +1,96 @@ +// +// Created by DiaLight on 20.07.2024. +// + +#include "micro_patches.h" +#include "dk2/utils/Pos2i.h" +#include "dk2/utils/AABB.h" +#include "dk2/MyMouseUpdater.h" +#include "dk2/MyDxInputState.h" +#include "dk2_globals.h" +#include "dk2_functions.h" +#include + + +bool add_win10_support::enabled = true; +bool use_cwd_as_dk2_home_dir::enabled = true; +bool notify_another_instance_is_running::enabled = true; +bool control_windowed_mode::enabled = true; + + +namespace { + dk2::Pos2i clientSize; +} +void fix_mouse_pos_on_resized_window::window_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { + switch (Msg) { + case WM_SIZE: { + clientSize = {LOWORD(lParam), HIWORD(lParam)}; + break; + } + case WM_MOUSEMOVE: { + dk2::Pos2i pos = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; + dk2::AABB renderRect = dk2::MyInputManagerCb_instance.f60_mouse->f30_aabb; + dk2::Pos2i renderSize = {renderRect.maxX - renderRect.minX, renderRect.maxY - renderRect.minY}; + pos.x = (int) ((float) pos.x * (float) renderSize.x / (float) clientSize.x); + pos.y = (int) ((float) pos.y * (float) renderSize.y / (float) clientSize.y); + lParam = (pos.x & 0xFFFF) | ((pos.y & 0xFFFF) << 16); + break; + } + } +} + +void fix_keyboard_state_on_alt_tab::window_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { + switch(Msg) { + case WM_ACTIVATEAPP: + if (wParam) { // activated + // clear buttons state + memset(dk2::MyInputManagerCb_instance.pdxInputState->keyboardState, 0, 256); + } + break; + } +} + +void bring_to_foreground::window_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { + switch(Msg) { + case WM_CREATE: { + SetForegroundWindow(hWnd); + break; + } + } +} + +namespace dk2 { + enum GameActionKind : DWORD { + GA_ExitToWindows = 0x7D + }; +} + +void fix_close_window::window_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { + switch(Msg) { + case WM_CLOSE: { + dk2::CDefaultPlayerInterface *playetIf = &dk2::CDefaultPlayerInterface_instance; + if (playetIf->profiler != nullptr) { // game is running + dk2::GameAction action; + ZeroMemory(&action, sizeof(action)); + action.actionKind = dk2::GA_ExitToWindows; + action._cpyFrF8 = playetIf->_cpyToF10; + playetIf->pushAction(&action); + } else { + dk2::setAppExitStatus(true); + } + break; + } + } +} + +bool skippable_title_screen::enabled = true; +bool skippable_title_screen::skipKeyPressed() { + if(!skippable_title_screen::enabled) return false; + if(GetAsyncKeyState(VK_SPACE) & 0x8000) return true; + if(GetAsyncKeyState(VK_ESCAPE) & 0x8000) return true; + if(GetAsyncKeyState(VK_LBUTTON) & 0x8000) return true; + if(GetAsyncKeyState(VK_RETURN) & 0x8000) return true; + SleepEx(50, TRUE); + return false; +} + diff --git a/src/patches/micro_patches.h b/src/patches/micro_patches.h new file mode 100644 index 0000000..32ff532 --- /dev/null +++ b/src/patches/micro_patches.h @@ -0,0 +1,48 @@ +// +// Created by DiaLight on 20.07.2024. +// + +#ifndef FLAME_MICRO_PATCHES_H +#define FLAME_MICRO_PATCHES_H + +#include + +namespace add_win10_support { + extern bool enabled; +} + +namespace use_cwd_as_dk2_home_dir { + extern bool enabled; +} + +namespace notify_another_instance_is_running { + extern bool enabled; +} + +namespace control_windowed_mode { + extern bool enabled; +} + +namespace fix_mouse_pos_on_resized_window { + void window_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +} + +namespace fix_keyboard_state_on_alt_tab { + void window_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +} + +namespace bring_to_foreground { + void window_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +} + +namespace fix_close_window { + void window_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +} + +namespace skippable_title_screen { + extern bool enabled; + bool skipKeyPressed(); +} + + +#endif //FLAME_MICRO_PATCHES_H diff --git a/src/patches/replace_mouse_dinput_to_user32.cpp b/src/patches/replace_mouse_dinput_to_user32.cpp new file mode 100644 index 0000000..3b2f2c4 --- /dev/null +++ b/src/patches/replace_mouse_dinput_to_user32.cpp @@ -0,0 +1,118 @@ +// +// Created by DiaLight on 20.07.2024. +// + +#include "replace_mouse_dinput_to_user32.h" +#include "dk2_globals.h" +#include "dk2/MouseRgbDxAction.h" +#include "dk2/ControlKeysUpdater.h" +#include + +// click flags +#define DK2_Shift 0x01 +#define DK2_Ctrl 0x02 +#define DK2_Alt 0x04 +#define DK2_IsPressed 0x08 +#define DK2_IsDblClick 0x10 + +// dk2 extends dinput scancodes for keyboard to add mouse keys in keyboard state array +#define DIK_DK2_LEFTMOUSE 0xF0 +#define DIK_DK2_RIGHTMOUSE 0xF1 +#define DIK_DK2_MIDDLEMOUSE 0xF2 +#define DIK_DK2_UNKMOUSE 0xF3 + +bool replace_mouse_dinput_to_user32::enabled = true; + +namespace { + std::vector actionsInProgress; +} + +void click_mouse(DWORD dik_scancode, DWORD flags) { + auto *updater = dk2::MyInputManagerCb_instance.f5C_controlKeys; + // do not try to call constructor/destructor + auto *action = (dk2::MouseRgbDxAction *) new char[sizeof(dk2::MouseRgbDxAction)]; + *(void **) action = &dk2::MouseRgbDxAction_vftable; +// action.f10_KeyCode_F0toF3 = click_dinput_to_dk2(dinput_dwOffs); + action->KeyCode_F0toF3 = dik_scancode; + action->btnPressFlags = flags; + action->pos.x = 0; + action->pos.y = 0; + // action->f1C_data = LOBYTE(DIDEVICEOBJECTDATA.dwData) + action->data = flags & DK2_IsPressed ? 0x80 : 0x00; + action->timestamp = GetTickCount(); + action->isNotHandled = 1; + actionsInProgress.push_back(action); + updater->v_fun4(action); +} + +namespace { + DWORD controlFlags = 0; + dk2::Pos2i clientSize; +} + +void replace_mouse_dinput_to_user32::emulate_dinput_from_user32(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { + switch (Msg) { + case WM_LBUTTONDOWN: + click_mouse(DIK_DK2_LEFTMOUSE, DK2_IsPressed | controlFlags); + break; + case WM_LBUTTONUP: + click_mouse(DIK_DK2_LEFTMOUSE, 0 | controlFlags); + break; + case WM_LBUTTONDBLCLK: + click_mouse(DIK_DK2_LEFTMOUSE, DK2_IsDblClick | controlFlags); + break; + case WM_RBUTTONDOWN: + click_mouse(DIK_DK2_RIGHTMOUSE, DK2_IsPressed | controlFlags); + break; + case WM_RBUTTONUP: + click_mouse(DIK_DK2_RIGHTMOUSE, 0 | controlFlags); + break; + case WM_RBUTTONDBLCLK: + click_mouse(DIK_DK2_RIGHTMOUSE, DK2_IsDblClick | controlFlags); + break; + case WM_MBUTTONDOWN: + click_mouse(DIK_DK2_MIDDLEMOUSE, DK2_IsPressed | controlFlags); + break; + case WM_MBUTTONUP: + click_mouse(DIK_DK2_MIDDLEMOUSE, 0 | controlFlags); + break; + case WM_MBUTTONDBLCLK: + click_mouse(DIK_DK2_MIDDLEMOUSE, DK2_IsDblClick | controlFlags); + break; + case WM_XBUTTONDOWN: + click_mouse(DIK_DK2_UNKMOUSE, DK2_IsPressed | controlFlags); + break; + case WM_XBUTTONUP: + click_mouse(DIK_DK2_UNKMOUSE, 0 | controlFlags); + break; + case WM_XBUTTONDBLCLK: + click_mouse(DIK_DK2_UNKMOUSE, DK2_IsDblClick | controlFlags); + break; + case WM_KEYDOWN: { + switch (wParam) { + case VK_SHIFT: + controlFlags |= DK2_Shift; + case VK_CONTROL: + controlFlags |= DK2_Ctrl; + case VK_MENU: + controlFlags |= DK2_Alt; + } + break; + } + case WM_KEYUP: { + switch (wParam) { + case VK_SHIFT: + controlFlags &= ~DK2_Shift; + case VK_CONTROL: + controlFlags &= ~DK2_Ctrl; + case VK_MENU: + controlFlags &= ~DK2_Alt; + } + break; + } + case WM_SIZE: { + clientSize = {LOWORD(lParam), HIWORD(lParam)}; + break; + } + } +} diff --git a/src/patches/replace_mouse_dinput_to_user32.h b/src/patches/replace_mouse_dinput_to_user32.h new file mode 100644 index 0000000..5cdf428 --- /dev/null +++ b/src/patches/replace_mouse_dinput_to_user32.h @@ -0,0 +1,18 @@ +// +// Created by DiaLight on 20.07.2024. +// + +#ifndef FLAME_REPLACE_MOUSE_DINPUT_TO_USER32_H +#define FLAME_REPLACE_MOUSE_DINPUT_TO_USER32_H + +#include + +namespace replace_mouse_dinput_to_user32 { + + extern bool enabled; + void emulate_dinput_from_user32(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); + +} + + +#endif //FLAME_REPLACE_MOUSE_DINPUT_TO_USER32_H diff --git a/src/patches/use_wheel_to_zoom.cpp b/src/patches/use_wheel_to_zoom.cpp new file mode 100644 index 0000000..2b5050c --- /dev/null +++ b/src/patches/use_wheel_to_zoom.cpp @@ -0,0 +1,43 @@ +// +// Created by DiaLight on 20.07.2024. +// + +#include "use_wheel_to_zoom.h" +#include "dk2_globals.h" +#include + +bool use_wheel_to_zoom::enabled = true; +void use_wheel_to_zoom::window_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { + switch (Msg) { + case WM_MOUSEWHEEL: { + DWORD fwKeys = GET_KEYSTATE_WPARAM(wParam); + DWORD zDelta = GET_WHEEL_DELTA_WPARAM(wParam); // +-120*speed + DWORD xPos = GET_X_LPARAM(lParam); + DWORD yPos = GET_Y_LPARAM(lParam); +// printf("k=%08X d=%d {%d %d}\n", fwKeys, zDelta, xPos, yPos); + dk2::CBridge_instance.camera.zoomRel_449CA0(-zDelta * 50); + break; + } + } +// + hook::DIRECT_INPUT_MOUSE_DATA.emplace_back([](DIDEVICEOBJECTDATA *data) { +// }); +} + +namespace { + DWORD g_lastTimestamp = 0; +} +void use_wheel_to_zoom::dinput_proc(DIDEVICEOBJECTDATA *data) { + switch (data->dwOfs) { + case DIMOFS_Z: { // mouse wheel + int zDelta = data->dwData; // +-150 with timestamp + int tsDelta = data->dwTimeStamp - g_lastTimestamp; + g_lastTimestamp = data->dwTimeStamp; +// printf("wheel: %d, ts: %d\n", data->dwData, tsDelta); + int mult = 80; + if (tsDelta > 100) mult = 40; + if (tsDelta > 500) mult = 20; + dk2::CBridge_instance.camera.zoomRel_449CA0(-zDelta * mult); + break; + } + } +} diff --git a/src/patches/use_wheel_to_zoom.h b/src/patches/use_wheel_to_zoom.h new file mode 100644 index 0000000..68f99ad --- /dev/null +++ b/src/patches/use_wheel_to_zoom.h @@ -0,0 +1,21 @@ +// +// Created by DiaLight on 20.07.2024. +// + +#ifndef FLAME_USE_WHEEL_TO_ZOOM_H +#define FLAME_USE_WHEEL_TO_ZOOM_H + +#include + +typedef struct DIDEVICEOBJECTDATA DIDEVICEOBJECTDATA; + +namespace use_wheel_to_zoom { + + extern bool enabled; + void window_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); + void dinput_proc(DIDEVICEOBJECTDATA *data); + +} + + +#endif //FLAME_USE_WHEEL_TO_ZOOM_H diff --git a/src/replace_globals.map b/src/replace_globals.map new file mode 100644 index 0000000..2f07299 --- /dev/null +++ b/src/replace_globals.map @@ -0,0 +1,33 @@ +# va comment + +006477F0 int WinMain(HINSTANCE, HINSTANCE, CHAR *, int); /* auto */ +005A5DA0 int __cdecl dk2_main(int, const CHAR **); /* auto */ +005B74A0 void resolveDk2HomeDir(); // --------------- /* auto */ + +# CFrontEndComponent.h +005340F0 void showTitleScreen(); // ----------------- /* auto */ + +# MyDxMouse.h +005BC760 int *__cdecl MyDxMouse_create(int *, MyDxMouse **); /* auto */ +005DDA90 void handleData(int); // ------------------- /* auto */ +005DDB88 extern void *jpt_5DDAC1[5]; // ------------- /* auto */ +005DDBAC extern void *jpt_5DDB05[4]; // ------------- /* auto */ + +# CWindowTest.h +00556650 LRESULT CWindowTest_proc(HWND, uint32_t, WPARAM, LPARAM); /* auto */ +00556704 extern void *jpt_5566AF[6]; // ------------- /* auto */ +0055671C extern uint8_t idt_5566A9[212]; // --------- /* auto */ + +# MyGame.h +00557FB0 int isOsCompatible(); // ------------------- /* auto */ +005581B0 int prepareScreenEx(uint32_t, uint32_t, uint32_t, int, int, int); /* auto */ + +# MyResources.h +0055C020 MyResources *init_resources(); // ---------- /* auto */ + +# MyInputManagerCb.h +# 005BB1C0 int *initMouse(int *); // ------------------ /* auto */ + +# MyDxInputManagerCb.h +005BC060 int *initMouse(int *); // ------------------ /* auto */ + diff --git a/src/replace_structs.map b/src/replace_structs.map new file mode 100644 index 0000000..6e4f0a2 --- /dev/null +++ b/src/replace_structs.map @@ -0,0 +1,2 @@ +# not implemented + diff --git a/tools/genapi/gen_struct_h.py b/tools/genapi/gen_struct_h.py index 1c03f86..8dc8eb1 100644 --- a/tools/genapi/gen_struct_h.py +++ b/tools/genapi/gen_struct_h.py @@ -69,7 +69,7 @@ def format_h_struct_head(): if struct.vtable is not None: vtable_glob = vtable_map.get(struct.vtable.id, None) if vtable_glob is not None: - yield f"/*{vtable_glob.va:08X}*/ static void **vftable();" + yield f"/*{vtable_glob.va:08X} vftable*/" def format_vtable(struct: sgmap.Struct, vtable_values, is_super=False): if len(struct.vtable.fields) == 0: diff --git a/tools/msvc_mangler/mangler_tests.cpp b/tools/msvc_mangler/mangler_tests.cpp index 9fb62d4..4450187 100644 --- a/tools/msvc_mangler/mangler_tests.cpp +++ b/tools/msvc_mangler/mangler_tests.cpp @@ -187,9 +187,6 @@ global: va=00556650,name=CWindowTest_proc,size=180 arg: kind=int,size=4 )", "?CWindowTest_proc@dk2@@YGJPAUHWND__@@III@Z", "long __stdcall dk2::CWindowTest_proc(struct HWND__ *,unsigned int,unsigned int,unsigned int)"); // HRESULT -} - -void lasty_test() { assert_mangle(R"( struct: id=vtbl_00673048,name=MyDirectInput,size=4 global: va=005DDBC0,name=initDevice_0,size=25,member_of=vtbl_00673048 @@ -203,10 +200,28 @@ global: va=005DDBC0,name=initDevice_0,size=25,member_of=vtbl_00673048 )", "?initDevice_0@MyDirectInput@dk2@@QAEPAJPAJ@Z", "public: long * __thiscall dk2::MyDirectInput::initDevice_0(long *)"); } +void last_test() { + // vftable + assert_mangle(R"( +struct: id=call_f0_at_005DA01F,name=DxAction_vtbl,size=4 + field: name=applyToState + type: kind=ptr + type: kind=function,declspec=thiscall + ret: kind=int,size=4,signed=True + arg: kind=ptr + type: kind=void + arg: kind=ptr + type: kind=void +struct: id=instance_006728F8,name=MouseRgbDxAction_vtbl,size=4,super=call_f0_at_005DA01F +global: va=006728F8,name=MouseRgbDxAction_vftable,size=4 + type: kind=struct,id=instance_006728F8 +)", "?MouseRgbDxAction_vftable@dk2@@3PAPAXA", "void * * dk2::MouseRgbDxAction_vftable"); +} + int main() { try { // tests(); - lasty_test(); + last_test(); } catch (std::exception &e) { printf("[e] %s\n", e.what()); } diff --git a/tools/msvc_mangler/msvc_mangler.cpp b/tools/msvc_mangler/msvc_mangler.cpp index de725bc..02d7c91 100644 --- a/tools/msvc_mangler/msvc_mangler.cpp +++ b/tools/msvc_mangler/msvc_mangler.cpp @@ -303,6 +303,31 @@ void mangleFunction(std::stringstream &ss, Struct *member_of, FunctionType *fun, } ss << 'Z'; } +void mangleGlobal(std::stringstream &ss, Global *global, std::map &backReference) { + // https://clang.llvm.org/doxygen/MicrosoftMangle_8cpp_source.html#l00622 + ss << "3"; // # global + // result: ?g_wchar_buf@dk2@@3[A + // expect: ?g_wchar_buf@dk2@@3PA_WA + // ?g_wchar_buf@dk2@ + // @ + // 3 + // PA + // _W + // A + + if(global->name.ends_with("_vftable")) { + // assume all vftable as void *[?] + ss << 'P'; + ss << 'A'; + ss << 'P'; + ss << 'A'; + ss << 'X'; + } else { + mangleType(ss, global->type, backReference); + } + + mangleQualifiers(ss, global->type, false); +} std::string msvcMangleName(Global *global) { std::vector names; names.emplace_back("dk2"); // namespace @@ -329,18 +354,7 @@ std::string msvcMangleName(Global *global) { mangleCConv(ss, fun->cconv); mangleFunction(ss, global->member_of, fun, backReference); } else { - https://clang.llvm.org/doxygen/MicrosoftMangle_8cpp_source.html#l00622 - ss << "3"; // # global - // result: ?g_wchar_buf@dk2@@3[A - // expect: ?g_wchar_buf@dk2@@3PA_WA - // ?g_wchar_buf@dk2@ - // @ - // 3 - // PA - // _W - // A - mangleType(ss, global->type, backReference); - mangleQualifiers(ss, global->type, false); + mangleGlobal(ss, global, backReference); } return ss.str(); }