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();
}