From 46f249b82c5aee69b30b7bfc8bba432f14fd3e93 Mon Sep 17 00:00:00 2001 From: Perchik71 Date: Wed, 21 Oct 2020 12:11:19 +0300 Subject: [PATCH 001/257] UI modules Adding modules responsible for working on the UI from another project. --- .../src/patches/CKF4/UIBaseWindow.cpp | 573 ++++++++++++++++++ fallout4_test/src/patches/CKF4/UIBaseWindow.h | 309 ++++++++++ .../src/patches/CKF4/UICheckboxControl.cpp | 54 ++ .../src/patches/CKF4/UICheckboxControl.h | 25 + fallout4_test/src/patches/CKF4/UIMenus.cpp | 249 ++++++++ fallout4_test/src/patches/CKF4/UIMenus.h | 88 +++ 6 files changed, 1298 insertions(+) create mode 100644 fallout4_test/src/patches/CKF4/UIBaseWindow.cpp create mode 100644 fallout4_test/src/patches/CKF4/UIBaseWindow.h create mode 100644 fallout4_test/src/patches/CKF4/UICheckboxControl.cpp create mode 100644 fallout4_test/src/patches/CKF4/UICheckboxControl.h create mode 100644 fallout4_test/src/patches/CKF4/UIMenus.cpp create mode 100644 fallout4_test/src/patches/CKF4/UIMenus.h diff --git a/fallout4_test/src/patches/CKF4/UIBaseWindow.cpp b/fallout4_test/src/patches/CKF4/UIBaseWindow.cpp new file mode 100644 index 00000000..7e81b070 --- /dev/null +++ b/fallout4_test/src/patches/CKF4/UIBaseWindow.cpp @@ -0,0 +1,573 @@ +#include "..\..\Common.h" +#include "UIBaseWindow.h" + +#include + +namespace Core +{ + namespace Classes + { + namespace UI + { + // CFont + + CFont::CFont(const HDC hDC) : m_Cnt(nullptr), m_Handle(NULL) + { + Recreate(hDC); + } + + CFont::CFont(CUIBaseWindow* Cnt) : m_Cnt(Cnt), m_Handle(NULL) + { + Assert(m_Cnt); + HDC hDC = GetDC(m_Cnt->Handle); + Recreate(hDC); + ReleaseDC(m_Cnt->Handle, hDC); + } + + CFont::CFont(const std::string &name, const LONG size, const CFontStyles &styles, const CFontQuality quality, + const CFontPitch pitch) : + m_Name(name), m_Cnt(nullptr), m_Quality(quality), m_FontStyles(styles), m_Pitch(pitch), m_Handle(NULL) + { + Size = size; + } + + void CFont::Recreate(const HDC hDC) + { + TEXTMETRICA metric; + GetTextMetricsA(hDC, &metric); + m_Name.resize(MAX_PATH); + m_Name.resize(GetTextFaceA(hDC, MAX_PATH, &m_Name[0]) + 1); + + m_Quality = fqClearTypeNatural; + + if (metric.tmItalic) + m_FontStyles.insert(fsItalic); + if (metric.tmUnderlined) + m_FontStyles.insert(fsUnderline); + if (metric.tmWeight >= FW_BOLD) + m_FontStyles.insert(fsBold); + + m_Pitch = fpDefault; + m_Height = metric.tmHeight; + + Recreate(); + } + + void CFont::SetStyles(const CFontStyles &styles) + { + m_FontStyles = styles; + Change(); + } + + void CFont::SetName(const std::string &name) + { + m_Name = name; + Change(); + } + + LONG CFont::GetSize(void) const + { + HDC hDC = GetDC(0); + LONG res = -MulDiv(m_Height, 72, GetDeviceCaps(hDC, LOGPIXELSY)); + ReleaseDC(0, hDC); + return res; + } + + void CFont::SetSize(const LONG value) + { + HDC hDC = GetDC(0); + m_Height = -MulDiv(value, GetDeviceCaps(hDC, LOGPIXELSY), 72); + ReleaseDC(0, hDC); + Change(); + } + + void CFont::SetHeight(const LONG value) + { + m_Height = value; + Change(); + } + + void CFont::Apply(HWND window) + { + SendMessageA(window, WM_SETFONT, (WPARAM)m_Handle, 0); + } + + void CFont::Apply(CUIBaseWindow* window) + { + Apply(window->Handle); + } + + void CFont::SetQuality(const CFontQuality quality) + { + m_Quality = quality; + Change(); + } + + void CFont::SetPitch(const CFontPitch pitch) + { + m_Pitch = pitch; + Change(); + } + + void CFont::Recreate(void) + { + DWORD _Quality = DEFAULT_QUALITY; + DWORD _Pitch = DEFAULT_PITCH; + + switch (m_Quality) + { + case fqClearType: + _Quality = CLEARTYPE_QUALITY; + break; + case fqClearTypeNatural: + _Quality = CLEARTYPE_NATURAL_QUALITY; + break; + case fqProof: + _Quality = PROOF_QUALITY; + break; + case fqAntialiased: + _Quality = NONANTIALIASED_QUALITY; + break; + case fqNoAntialiased: + _Quality = ANTIALIASED_QUALITY; + break; + } + + switch (m_Pitch) + { + case fpFixed: + _Pitch = FIXED_PITCH; + break; + case fpVariable: + _Pitch = VARIABLE_PITCH; + break; + case fpMono: + _Pitch = MONO_FONT; + break; + } + + if (m_Handle) + DeleteObject(m_Handle); + + m_Handle = CreateFontA(Height, 0, 0, 0, + m_FontStyles.count(fsBold) ? FW_BOLD : FW_NORMAL, + m_FontStyles.count(fsItalic), + m_FontStyles.count(fsUnderline), + m_FontStyles.count(fsStrikeOut), + DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, + _Quality, + _Pitch, + m_Name.c_str()); + } + + void CFont::Change(void) + { + Recreate(); + + if (m_Cnt) + m_Cnt->__ChangeFont(this); + } + + // CUIBaseWindow + + void CUIBaseWindow::__ChangeFont(const CFont* font) + { + SendMessageA(m_hWnd, WM_SETFONT, (WPARAM)(font->Handle), TRUE); + } + + void CUIBaseWindow::LockUpdate(void) + { + if (m_LockUpdate) + return; + + m_LockUpdate = TRUE; + SendMessageA(m_hWnd, WM_SETREDRAW, FALSE, 0); + } + + void CUIBaseWindow::UnlockUpdate(void) + { + if (!m_LockUpdate) + return; + + m_LockUpdate = FALSE; + SendMessageA(m_hWnd, WM_SETREDRAW, TRUE, 0); + } + + void CUIBaseWindow::Move(const LONG x, const LONG y) + { + SetWindowPos(m_hWnd, NULL, x, y, 0, 0, SWP_NOOWNERZORDER | SWP_NOSIZE); + } + + void CUIBaseWindow::Invalidate(void) + { + InvalidateRect(m_hWnd, NULL, FALSE); + } + + void CUIBaseWindow::Repaint(void) + { + RedrawWindow(m_hWnd, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW); + } + + BOOL CUIBaseWindow::GetVisible(void) + { + return IsWindowVisible(m_hWnd); + } + + BOOL CUIBaseWindow::GetConstVisible(void) const + { + return IsWindowVisible(m_hWnd); + } + + void CUIBaseWindow::SetSize(const LONG cx, const LONG cy) + { + CRECT r = BoundsRect; + SetWindowPos(m_hWnd, NULL, 0, 0, cx + r.Left, cy + r.Top, SWP_NOOWNERZORDER | SWP_NOMOVE); + } + + void CUIBaseWindow::SetFocus(void) + { + ::SetFocus(m_hWnd); + } + + void CUIBaseWindow::SetVisible(const BOOL value) + { + WindowState = (value) ? wsNormal : wsHide; + } + + std::string CUIBaseWindow::GetCaption(void) const + { + std::string s; + s.resize(MAX_PATH); + GetWindowTextA(m_hWnd, &s[0], MAX_PATH); + return s; + } + + void CUIBaseWindow::SetCaption(const std::string &value) + { + SetWindowTextA(m_hWnd, value.c_str()); + } + + std::wstring CUIBaseWindow::GetWideCaption(void) const + { + std::wstring s; + s.resize(MAX_PATH); + GetWindowTextW(m_hWnd, &s[0], MAX_PATH); + return s; + } + + void CUIBaseWindow::SetWideCaption(const std::wstring &value) + { + SetWindowTextW(m_hWnd, value.c_str()); + } + + ULONG_PTR CUIBaseWindow::GetStyle(void) const + { + return GetWindowLongA(m_hWnd, GWL_STYLE); + } + + void CUIBaseWindow::SetStyle(const ULONG_PTR value) + { + SetWindowLongA(m_hWnd, GWL_STYLE, value); + } + + ULONG_PTR CUIBaseWindow::GetStyleEx(void) const + { + return GetWindowLongA(m_hWnd, GWL_EXSTYLE); + } + + void CUIBaseWindow::SetStyleEx(const ULONG_PTR value) + { + SetWindowLongA(m_hWnd, GWL_EXSTYLE, value); + } + + BOOL CUIBaseWindow::GetEnabled(void) const + { + return IsWindowEnabled(m_hWnd); + } + + void CUIBaseWindow::SetEnabled(const BOOL value) + { + EnableWindow(m_hWnd, value); + } + + std::string CUIBaseWindow::GetName(void) const + { + std::string s; + s.resize(MAX_PATH); + GetClassNameA(m_hWnd, &s[0], MAX_PATH); + return s; + } + + std::wstring CUIBaseWindow::GetWideName(void) const + { + std::wstring s; + s.resize(MAX_PATH); + GetClassNameW(m_hWnd, &s[0], MAX_PATH); + return s; + } + + void CUIBaseWindow::SetWindowState(const WindowState_t state) + { + if (state == m_WindowState) + return; + + int flag = SW_NORMAL; + m_WindowState = state; + + switch (state) + { + case wsMaximized: + { + flag = SW_MAXIMIZE; + break; + } + case wsMinimized: + { + flag = SW_MINIMIZE; + break; + } + case wsHide: + { + flag = SW_HIDE; + break; + } + } + + ShowWindow(m_hWnd, flag); + } + + CRECT CUIBaseWindow::WindowRect(void) const + { + RECT r; + GetWindowRect(m_hWnd, &r); + return r; + } + + CRECT CUIBaseWindow::ClientRect(void) const + { + RECT r; + GetClientRect(m_hWnd, &r); + return r; + } + + LONG CUIBaseWindow::ClientWidth(void) const + { + return ClientRect().Right; + } + + LONG CUIBaseWindow::ClientHeight(void) const + { + return ClientRect().Bottom; + } + + BOOL CUIBaseWindow::Is(void) const + { + return m_hWnd && IsWindow(m_hWnd); + } + + HWND CUIBaseWindow::Parent(void) const + { + return GetParent(m_hWnd); + } + + CUIBaseWindow CUIBaseWindow::ParentWindow(void) const + { + return CUIBaseWindow(Parent()); + } + + POINT CUIBaseWindow::ScreenToControl(const POINT &p) const + { + if (!Is()) + return POINT{ 0 }; + + POINT pnt = p; + + return (ScreenToClient(m_hWnd, &pnt)) ? pnt : POINT{ 0 }; + } + + POINT CUIBaseWindow::ControlToScreen(const POINT &p) const + { + if (!Is()) + return POINT{ 0 }; + + POINT pnt = p; + + return (ClientToScreen(m_hWnd, &pnt)) ? pnt : POINT{ 0 }; + } + + CRECT CUIBaseWindow::GetBoundsRect(void) const + { + CUIBaseWindow parent = ParentWindow(); + + if (!parent.Is()) + return WindowRect(); + + CRECT wrect = WindowRect(); + POINT Off = parent.ScreenToControl({ wrect.Left, wrect.Top }); + + wrect.Left = Off.x; + wrect.Top = Off.y; + + return wrect; + } + + void CUIBaseWindow::SetBoundsRect(const CRECT &bounds) + { + MoveWindow(m_hWnd, bounds.Left, bounds.Top, bounds.Width, bounds.Height, TRUE); + } + + LONG CUIBaseWindow::GetLeft(void) const + { + return BoundsRect.Left; + } + + void CUIBaseWindow::SetLeft(const LONG value) + { + CRECT bounds = BoundsRect; + LONG w = bounds.Width; + bounds.Left = value; + bounds.Width = w; + BoundsRect = bounds; + } + + LONG CUIBaseWindow::GetTop(void) const + { + return BoundsRect.Top; + } + + void CUIBaseWindow::SetTop(const LONG value) + { + CRECT bounds = BoundsRect; + LONG h = bounds.Height; + bounds.Top = value; + bounds.Height = h; + BoundsRect = bounds; + } + + LONG CUIBaseWindow::GetWidth(void) const + { + return BoundsRect.Width; + } + + void CUIBaseWindow::SetWidth(const LONG value) + { + CRECT bounds = BoundsRect; + bounds.Width = value; + BoundsRect = bounds; + } + + LONG CUIBaseWindow::GetHeight(void) const + { + return BoundsRect.Height; + } + + void CUIBaseWindow::SetHeight(const LONG value) + { + CRECT bounds = BoundsRect; + bounds.Height = value; + BoundsRect = bounds; + } + + LRESULT CUIBaseWindow::Perform(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + return Perform(m_hWnd, uMsg, wParam, lParam); + } + + LRESULT CUIBaseWindow::Perform(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + return SendMessageA(hWnd, uMsg, wParam, lParam); + } + + LRESULT CUIBaseWindow::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + return DefWindowProcA(hWnd, uMsg, wParam, lParam); + } + + // CUICustomWindow + + CUIBaseControl CUICustomWindow::GetControl(const uint32_t index) + { + HWND hWnd = GetDlgItem(Handle, index); + Assert(hWnd); + + return CUIBaseControl(hWnd); + } + + void CUICustomWindow::Foreground(void) + { + SetForegroundWindow(m_hWnd); + } + + // CUIDialog + + CUICustomDialog::CUICustomDialog(CUICustomWindow* parent, const UINT res_id, DLGPROC dlg_proc) : CUICustomWindow() + { + m_ResId = res_id; + m_DlgProc = dlg_proc; + m_Parent = parent; + } + + void CUICustomDialog::Create(void) + { + CreateDialogParamA(GetModuleHandle(NULL), MAKEINTRESOURCEA(m_ResId), m_Parent->Handle, m_DlgProc, (LONG_PTR)this); + Assert(m_hWnd); + ShowWindow(m_hWnd, SW_SHOW); + UpdateWindow(m_hWnd); + } + + void CUICustomDialog::FreeRelease(void) + { + if (!m_hWnd) return; + DestroyWindow(m_hWnd); + m_hWnd = NULL; + }; + + CUICustomDialog::~CUICustomDialog(void) + { + FreeRelease(); + } + + // CUIMainWindow + + void CUIMainWindow::FindToolWindow(void) + { + EnumChildWindows(Handle, [](HWND hwnd, LPARAM lParam) -> BOOL { + CUIMainWindow* main = (CUIMainWindow*)lParam; + CUIToolWindow Tool(hwnd); + if (!Tool.Name.compare(TOOLBARCLASSNAMEA)) + main->Toolbar = Tool; + else if (!Tool.Name.compare(STATUSCLASSNAMEA)) + main->Statusbar = Tool; + return TRUE; + }, (LPARAM)this); + } + + void CUIMainWindow::ProcessMessages(void) + { + MSG msg; + while (true) + { + if (PeekMessageA(&msg, NULL, 0, 0, PM_REMOVE)) + { + DispatchMessageA(&msg); + TranslateMessage(&msg); + } + else + break; + } + } + + void CUIMainWindow::SetTextToStatusBar(const uint32_t index, const std::string text) + { + Statusbar.Perform(SB_SETTEXTA, index, (LPARAM)text.c_str()); + } + + void CUIMainWindow::SetTextToStatusBar(const uint32_t index, const std::wstring text) + { + Statusbar.Perform(SB_SETTEXTW, index, (LPARAM)text.c_str()); + } + } + } +} + + diff --git a/fallout4_test/src/patches/CKF4/UIBaseWindow.h b/fallout4_test/src/patches/CKF4/UIBaseWindow.h new file mode 100644 index 00000000..0987f19a --- /dev/null +++ b/fallout4_test/src/patches/CKF4/UIBaseWindow.h @@ -0,0 +1,309 @@ +#pragma once + +#define VC_EXTRALEAN +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include "UIMenus.h" + +#define PROPERTY(read_func, write_func) __declspec(property(get = read_func, put = write_func)) +#define READ_PROPERTY(read_func) __declspec(property(get = read_func)) + +namespace Core +{ + enum WindowState_t { + wsNormal, + wsMaximized, + wsMinimized, + wsHide + }; + + namespace Classes + { + namespace UI + { + class CUIBaseWindow; + class CUIBaseControl; + class CUICustomWindow; + class CUIMainWindow; + class CUIToolWindow; + + class CRECT + { + private: + LONG m_left; + LONG m_top; + LONG m_right; + LONG m_bottom; + public: + CRECT() : m_left(0), m_top(0), m_right(0), m_bottom(0) {} + CRECT(const LONG l, const LONG t, const LONG r, const LONG b) : m_left(l), m_top(t), m_right(r), m_bottom(b) {} + CRECT(const RECT &r) : m_left(r.left), m_top(r.top), m_right(r.right), m_bottom(r.bottom) {} + CRECT(const CRECT &r) : m_left(r.m_left), m_top(r.m_top), m_right(r.m_right), m_bottom(r.m_bottom) {} + public: + inline LONG GetWidth(void) const { return m_right - m_left; } + inline LONG GetHeight(void) const { return m_bottom - m_top; } + inline LONG GetLeft(void) const { return m_left; } + inline LONG GetTop(void) const { return m_top; } + inline LONG GetRight(void) const { return m_right; } + inline LONG GetBottom(void) const { return m_bottom; } + inline void SetWidth(const LONG value) { m_right = value + m_left; } + inline void SetHeight(const LONG value) { m_bottom = value + m_top; } + inline void SetLeft(const LONG value) { m_right = GetWidth() + value; m_left = value; } + inline void SetTop(const LONG value) { m_bottom = GetHeight() + value; m_top = value; } + inline void SetRight(const LONG value) { m_right = value; } + inline void SetBottom(const LONG value) { m_bottom = value; } + public: + inline CRECT& Inflate(const LONG x, const LONG y) { m_left -= x; m_top -= y; m_right += x; m_bottom += y; return *this; } + inline BOOL IsEmpty(void) const { return GetWidth() == 0 || GetHeight() == 0; } + inline CRECT Dublicate(void) const { return *this; } + public: + PROPERTY(GetWidth, SetWidth) LONG Width; + PROPERTY(GetHeight, SetHeight) LONG Height; + PROPERTY(GetLeft, SetLeft) LONG Left; + PROPERTY(GetTop, SetTop) LONG Top; + PROPERTY(GetRight, SetRight) LONG Right; + PROPERTY(GetBottom, SetBottom) LONG Bottom; + }; + + enum CFontStyle { + fsBold, + fsItalic, + fsUnderline, + fsStrikeOut + }; + typedef std::set CFontStyles; + enum CFontQuality { + fqDefault, + fqClearType, + fqClearTypeNatural, + fqProof, + fqAntialiased, + fqNoAntialiased, + }; + enum CFontPitch { + fpDefault, + fpFixed, + fpVariable, + fpMono + }; + + class CFont + { + private: + HFONT m_Handle; + std::string m_Name; + CUIBaseWindow* m_Cnt; + CFontStyles m_FontStyles; + CFontQuality m_Quality; + CFontPitch m_Pitch; + LONG m_Height; + private: + void Change(void); + public: + inline HFONT GetHandle(void) const { return m_Handle; } + std::string GetName(void) const { return m_Name; } + void SetName(const std::string &name); + LONG GetSize(void) const; + void SetSize(const LONG value); + inline LONG GetHeight(void) const { return m_Height; } + void SetHeight(const LONG value); + inline CFontStyles GetStyles(void) const { return m_FontStyles; } + void SetStyles(const CFontStyles &styles); + inline CFontQuality GetQuality(void) const { return m_Quality; } + void SetQuality(const CFontQuality quality); + inline CFontPitch GetPitch(void) const { return m_Pitch; } + void SetPitch(const CFontPitch pitch); + private: + void Recreate(void); + void Recreate(const HDC hDC); + public: + void Apply(HWND window); + void Apply(CUIBaseWindow* window); + inline void Release(void) { DeleteObject(m_Handle); } + public: + READ_PROPERTY(GetHandle) HFONT Handle; + PROPERTY(GetName, SetName) const std::string Name; + PROPERTY(GetSize, SetSize) const LONG Size; + PROPERTY(GetHeight, SetHeight) const LONG Height; + PROPERTY(GetStyles, SetStyles) const CFontStyles Styles; + PROPERTY(GetQuality, SetQuality) const CFontQuality Quality; + PROPERTY(GetPitch, SetPitch) const CFontPitch Pitch; + public: + CFont(const std::string &name, const LONG size, const CFontStyles &styles = {}, const CFontQuality quality = fqClearTypeNatural, + const CFontPitch pitch = fpVariable); + CFont(const HDC hDC); + CFont(const CFont &parent) : m_Cnt(NULL), m_FontStyles(parent.m_FontStyles), m_Quality(parent.m_Quality), m_Pitch(parent.m_Pitch), + m_Name(parent.m_Name), m_Height(parent.m_Height) { Recreate(); } + CFont(CUIBaseWindow* Cnt); + }; + + class CUIBaseWindow + { + private: + BOOL m_LockUpdate; + HDC m_hDC; + WindowState_t m_WindowState; + public: + CFont Font; + protected: + HWND m_hWnd; + public: + virtual void __ChangeFont(const CFont* font); + public: + inline HWND GetHandle(void) const { return m_hWnd; } + inline WindowState_t GetWindowState(void) const { return m_WindowState; } + void SetWindowState(const WindowState_t state); + BOOL GetVisible(void); + BOOL GetConstVisible(void) const; + void SetVisible(const BOOL value); + std::string GetCaption(void) const; + void SetCaption(const std::string &value); + std::wstring GetWideCaption(void) const; + void SetWideCaption(const std::wstring &value); + ULONG_PTR GetStyle(void) const; + void SetStyle(const ULONG_PTR value); + ULONG_PTR GetStyleEx(void) const; + void SetStyleEx(const ULONG_PTR value); + BOOL GetEnabled(void) const; + void SetEnabled(const BOOL value); + std::string GetName(void) const; + std::wstring GetWideName(void) const; + CRECT GetBoundsRect(void) const; + virtual void SetBoundsRect(const CRECT &bounds); + LONG GetLeft(void) const; + virtual void SetLeft(const LONG value); + LONG GetTop(void) const; + virtual void SetTop(const LONG value); + LONG GetWidth(void) const; + virtual void SetWidth(const LONG value); + LONG GetHeight(void) const; + virtual void SetHeight(const LONG value); + public: + BOOL Is(void) const; + CRECT WindowRect(void) const; + CRECT ClientRect(void) const; + LONG ClientWidth(void) const; + LONG ClientHeight(void) const; + void Move(const LONG x, const LONG y); + void SetSize(const LONG cx, const LONG cy); + HWND Parent(void) const; + CUIBaseWindow ParentWindow(void) const; + POINT ScreenToControl(const POINT &p) const; + POINT ControlToScreen(const POINT &p) const; + inline BOOL IsLockUpdate(void) const { return m_LockUpdate; } + void LockUpdate(void); + void UnlockUpdate(void); + void Invalidate(void); + void Repaint(void); + void SetFocus(void); + inline HDC DeviceContext(void) const { return m_hDC; } + public: + virtual LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + public: + LRESULT Perform(UINT uMsg, WPARAM wParam, LPARAM lParam); + LRESULT Perform(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + public: + CUIBaseWindow(void) : m_LockUpdate(FALSE), m_hWnd(NULL), m_WindowState(wsNormal), m_hDC(GetDC(0)), Font(this) + { } + CUIBaseWindow(const HWND hWnd) : m_LockUpdate(FALSE), m_hWnd(hWnd), m_WindowState(wsNormal), m_hDC(GetDC(hWnd)), Font(m_hDC) + { } + CUIBaseWindow(const CUIBaseWindow &base) : m_LockUpdate(base.m_LockUpdate), m_hWnd(base.m_hWnd), m_WindowState(base.m_WindowState), m_hDC(GetDC(m_hWnd)), Font(this) + { } + virtual ~CUIBaseWindow(void) { ReleaseDC(m_hWnd, m_hDC); } + public: + READ_PROPERTY(GetHandle) HWND Handle; + PROPERTY(GetWindowState, SetWindowState) WindowState_t WindowState; + PROPERTY(GetConstWindowState, SetWindowState) const WindowState_t ConstWindowState; + PROPERTY(GetVisible, SetVisible) BOOL Visible; + PROPERTY(GetConstVisible, SetVisible) const BOOL ConstVisible; + PROPERTY(GetCaption, SetCaption) std::string Caption; + PROPERTY(GetWideCaption, SetWideCaption) const std::wstring WideCaption; + PROPERTY(GetStyle, SetStyle) ULONG_PTR Style; + PROPERTY(GetStyleEx, SetStyleEx) ULONG_PTR StyleEx; + PROPERTY(GetEnabled, SetEnabled) BOOL Enabled; + READ_PROPERTY(GetName) const std::string Name; + READ_PROPERTY(GetWideName) const std::wstring WideName; + PROPERTY(GetBoundsRect, SetBoundsRect) CRECT BoundsRect; + PROPERTY(GetLeft, SetLeft) LONG Left; + PROPERTY(GetTop, SetTop) LONG Top; + PROPERTY(GetWidth, SetWidth) LONG Width; + PROPERTY(GetHeight, SetHeight) LONG Height; + }; + + class CUICustomWindow : public CUIBaseWindow + { + public: + CUIBaseControl GetControl(const uint32_t index); + void Foreground(void); + public: + CUICustomWindow(void) : CUIBaseWindow() {} + CUICustomWindow(const HWND hWnd) : CUIBaseWindow(hWnd) {} + CUICustomWindow(const CUICustomWindow &base) : CUIBaseWindow(base) {} + }; + + class CUIToolWindow : public CUIBaseWindow + { + public: + inline void SetBoundsRect(const CRECT &) {} + inline void SetLeft(const LONG) {} + inline void SetTop(const LONG) {} + inline void SetWidth(const LONG) {} + inline void SetHeight(const LONG) {} + public: + CUIToolWindow(void) : CUIBaseWindow() {} + CUIToolWindow(const HWND hWnd) : CUIBaseWindow(hWnd) {} + CUIToolWindow(const CUIToolWindow &base) : CUIBaseWindow(base) {} + }; + + class CUIMainWindow : public CUICustomWindow + { + public: + CUIToolWindow Toolbar; + CUIToolWindow Statusbar; + CUIMenu m_MainMenu; + protected: + void FindToolWindow(void); + public: + inline CUIMenu GetMainMenu(void) const { return m_MainMenu; } + void SetTextToStatusBar(const uint32_t index, const std::string text); + void SetTextToStatusBar(const uint32_t index, const std::wstring text); + static void ProcessMessages(void); + public: + CUIMainWindow(void) : CUICustomWindow() {} + CUIMainWindow(const HWND hWnd) : CUICustomWindow(hWnd) {} + CUIMainWindow(const CUIMainWindow &base) : CUICustomWindow(base) {} + public: + READ_PROPERTY(GetMainMenu) CUIMenu MainMenu; + }; + + class CUICustomDialog : public CUICustomWindow + { + private: + UINT m_ResId; + DLGPROC m_DlgProc; + CUICustomWindow* m_Parent; + public: + void Create(void); + void FreeRelease(void); + inline UINT GetResourceID() const { return m_ResId; } + inline DLGPROC GetDialogProc() const { return m_DlgProc; } + public: + CUICustomDialog(CUICustomWindow* parent, const UINT res_id, DLGPROC dlg_proc); + virtual ~CUICustomDialog(void); + public: + READ_PROPERTY(GetResourceID) UINT ResourceID; + READ_PROPERTY(GetDialogProc) DLGPROC DialogProc; + }; + + class CUIBaseControl : public CUIBaseWindow + { + public: + CUIBaseControl(void) : CUIBaseWindow() {} + CUIBaseControl(const HWND hWnd) : CUIBaseWindow(hWnd) {} + CUIBaseControl(const CUIBaseControl &base) : CUIBaseWindow(base) {} + }; + } + } +} diff --git a/fallout4_test/src/patches/CKF4/UICheckboxControl.cpp b/fallout4_test/src/patches/CKF4/UICheckboxControl.cpp new file mode 100644 index 00000000..f1b856b6 --- /dev/null +++ b/fallout4_test/src/patches/CKF4/UICheckboxControl.cpp @@ -0,0 +1,54 @@ +#include "..\..\Common.h" +#include "UICheckboxControl.h" + +#pragma warning(disable: 4312) + +namespace Core +{ + namespace Classes + { + namespace UI + { + void CUICheckbox::CreateWnd(const CUIBaseWindow &parent, const std::string &caption, const LONG l, const LONG t, const LONG w, const LONG h, const UINT menu_id) + { + Assert(!m_hWnd); + Assert(menu_id); + Assert(parent.Is()); + + m_hWnd = CreateWindowExA(NULL, "button", caption.c_str(), + WS_VISIBLE | WS_CHILD | BS_CHECKBOX, + l, t, w, h, parent.Handle, + (HMENU)menu_id, + GetModuleHandle(NULL), NULL); + + Font = parent.Font; + Font.Apply(m_hWnd); + + Assert(m_hWnd); + + m_Checked = FALSE; + CheckDlgButton(m_hWnd, menu_id, BST_UNCHECKED); + m_MenuId = menu_id; + } + + void CUICheckbox::SetChecked(const BOOL value) + { + if (m_Checked == value) + return; + + m_Checked = value; + + if (m_Checked) + CheckDlgButton(Parent(), m_MenuId, BST_CHECKED); + else + CheckDlgButton(Parent(), m_MenuId, BST_UNCHECKED); + } + + BOOL CUICheckbox::GetChecked(void) const + { + return m_Checked; + } + } + } +} + diff --git a/fallout4_test/src/patches/CKF4/UICheckboxControl.h b/fallout4_test/src/patches/CKF4/UICheckboxControl.h new file mode 100644 index 00000000..9e9cf039 --- /dev/null +++ b/fallout4_test/src/patches/CKF4/UICheckboxControl.h @@ -0,0 +1,25 @@ +#pragma once + +#include "UIBaseWindow.h" + +namespace Core +{ + namespace Classes + { + namespace UI + { + class CUICheckbox : public CUIBaseControl + { + private: + BOOL m_Checked; + UINT m_MenuId; + public: + void CreateWnd(const CUIBaseWindow &parent, const std::string &caption, const LONG l, const LONG t, const LONG w, const LONG h, const UINT menu_id); + void SetChecked(const BOOL value); + BOOL GetChecked(void) const; + public: + PROPERTY(GetChecked, SetChecked) BOOL Checked; + }; + } + } +} \ No newline at end of file diff --git a/fallout4_test/src/patches/CKF4/UIMenus.cpp b/fallout4_test/src/patches/CKF4/UIMenus.cpp new file mode 100644 index 00000000..e1a08bf3 --- /dev/null +++ b/fallout4_test/src/patches/CKF4/UIMenus.cpp @@ -0,0 +1,249 @@ +#include "../../common.h" +#include "UIMenus.h" +#include "LogWindow.h" + +namespace Core +{ + namespace Classes + { + namespace UI + { + void CUIMenuItem::SetText(const std::string& text) + { + MENUITEMINFOA m_mif = {0}; + m_mif.cbSize = sizeof(MENUITEMINFOA); + m_mif.fMask = MIIM_STRING; + m_mif.fType = MFT_STRING; + m_mif.cch = text.length(); + m_mif.dwTypeData = const_cast(&text[0]); + + SetMenuItemInfoA(m_Menu->Handle, m_Pos, m_ByPos, &m_mif); + } + + std::string CUIMenuItem::GetText(void) const + { + std::string str; + + MENUITEMINFOA m_mif = { 0 }; + m_mif.cbSize = sizeof(MENUITEMINFOA); + m_mif.fMask = MIIM_STRING; + m_mif.fType = MFT_STRING; + m_mif.dwTypeData = NULL; + + if (GetMenuItemInfoA(m_Menu->Handle, m_Pos, m_ByPos, &m_mif)) + { + m_mif.cch++; + str.resize(m_mif.cch); + m_mif.dwTypeData = &str[0]; + GetMenuItemInfoA(m_Menu->Handle, m_Pos, m_ByPos, &m_mif); + } + + return str; + } + + void CUIMenuItem::SetID(const UINT menu_id) + { + MENUITEMINFOA m_mif = { 0 }; + m_mif.cbSize = sizeof(MENUITEMINFOA); + m_mif.fMask = MIIM_ID; + m_mif.wID = menu_id; + + SetMenuItemInfoA(m_Menu->Handle, m_Pos, m_ByPos, &m_mif); + } + + UINT CUIMenuItem::GetID(void) const + { + MENUITEMINFOA m_mif = { 0 }; + m_mif.cbSize = sizeof(MENUITEMINFOA); + m_mif.fMask = MIIM_ID; + + GetMenuItemInfoA(m_Menu->Handle, m_Pos, m_ByPos, &m_mif); + + return m_mif.wID; + } + + void CUIMenuItem::SetChecked(const BOOL value) + { + if (GetChecked() == value) + return; + + MENUITEMINFOA m_mif = { 0 }; + m_mif.cbSize = sizeof(MENUITEMINFOA); + m_mif.fMask = MIIM_STATE; + + if (value) + m_mif.fState = (GetEnabled() ? MFS_ENABLED : MFS_DISABLED) | MFS_CHECKED; + else + m_mif.fState = (GetEnabled() ? MFS_ENABLED : MFS_DISABLED) | MFS_UNCHECKED; + + SetMenuItemInfoA(m_Menu->Handle, m_Pos, m_ByPos, &m_mif); + } + + BOOL CUIMenuItem::GetChecked(void) const + { + MENUITEMINFOA m_mif = { 0 }; + m_mif.cbSize = sizeof(MENUITEMINFOA); + m_mif.fMask = MIIM_STATE; + GetMenuItemInfoA(m_Menu->Handle, m_Pos, m_ByPos, &m_mif); + + return (m_mif.fState & MFS_CHECKED) == MFS_CHECKED; + } + + void CUIMenuItem::SetEnabled(const BOOL value) + { + if (GetEnabled() == value) + return; + + MENUITEMINFOA m_mif = { 0 }; + m_mif.cbSize = sizeof(MENUITEMINFOA); + m_mif.fMask = MIIM_STATE; + + if (value) + m_mif.fState = (GetChecked() ? MFS_CHECKED : MFS_UNCHECKED) | MFS_ENABLED; + else + m_mif.fState = (GetChecked() ? MFS_CHECKED : MFS_UNCHECKED) | MFS_DISABLED; + + SetMenuItemInfoA(m_Menu->Handle, m_Pos, m_ByPos, &m_mif); + } + + BOOL CUIMenuItem::GetEnabled(void) const + { + MENUITEMINFOA m_mif = { 0 }; + m_mif.cbSize = sizeof(MENUITEMINFOA); + m_mif.fMask = MIIM_STATE; + GetMenuItemInfoA(m_Menu->Handle, m_Pos, m_ByPos, &m_mif); + + return (m_mif.fState & MFS_ENABLED) == MFS_ENABLED; + } + + BOOL CUIMenuItem::IsSeparate(void) const + { + MENUITEMINFOA m_mif = { 0 }; + m_mif.cbSize = sizeof(MENUITEMINFOA); + m_mif.fMask = MIIM_FTYPE; + GetMenuItemInfoA(m_Menu->Handle, m_Pos, m_ByPos, &m_mif); + + return (m_mif.fType & MFT_SEPARATOR) == MFT_SEPARATOR; + } + + void CUIMenuItem::Remove(CUIMenuItem* MenuItem) + { + Assert(MenuItem && IsMenu(MenuItem->Menu()->Handle)); + DeleteMenu(MenuItem->Menu()->Handle, (UINT)MenuItem->ID, MenuItem->ByPosition()); + } + + void CUIMenuItem::Remove(CUIMenuItem& MenuItem) + { + Remove(&MenuItem); + } + + // CUIMenu + + void CUIMenu::SetHandle(const HMENU value) + { + Assert(IsMenu(value)); + m_Handle = value; + } + + HMENU CUIMenu::GetHandle(void) const + { + return m_Handle; + } + + CUIMenu CUIMenu::CreateSubMenu(void) + { + return CreateMenu(); + } + + UINT CUIMenu::Count(void) const + { + return GetMenuItemCount(m_Handle); + } + + void CUIMenu::Remove(const UINT MenuID) + { + Assert(IsMenu(m_Handle)); + DeleteMenu(m_Handle, MenuID, FALSE); + } + + void CUIMenu::RemoveByPos(const UINT Position) + { + Assert(IsMenu(m_Handle)); + DeleteMenu(m_Handle, Position, TRUE); + } + + CUIMenuItem CUIMenu::GetItem(const UINT MenuID) + { + return CUIMenuItem(*this, MenuID, FALSE); + } + + CUIMenuItem CUIMenu::GetItemByPos(const UINT Position) + { + return CUIMenuItem(*this, Position, TRUE); + } + + BOOL CUIMenu::Insert(const std::string& Text, const UINT Position, const UINT MenuID, const BOOL Enabled, const BOOL Checked) + { + MENUITEMINFOA minfo = { 0 }; + minfo.cbSize = sizeof(MENUITEMINFOA); + minfo.fMask = MIIM_STRING | MIIM_ID | MIIM_STATE; + minfo.wID = (UINT)MenuID; + minfo.cch = Text.length(); + minfo.dwTypeData = const_cast(&Text[0]); + minfo.fState = (Enabled ? MFS_ENABLED : MFS_DISABLED) | (Checked ? MFS_CHECKED : MFS_UNCHECKED); + return InsertMenuItemA(m_Handle, Position, TRUE, &minfo); + } + + BOOL CUIMenu::Append(const std::string& Text, const UINT MenuID, const BOOL Enabled, const BOOL Checked) + { + return Insert(Text, -1, MenuID, Enabled, Checked); + } + + BOOL CUIMenu::Insert(const std::string& Text, const UINT Position, const CUIMenu& Menu, const BOOL Enabled) + { + MENUITEMINFOA minfo = { 0 }; + minfo.cbSize = sizeof(MENUITEMINFOA); + minfo.fMask = MIIM_STRING | MIIM_SUBMENU | MIIM_STATE; + minfo.hSubMenu = Menu.Handle; + minfo.cch = Text.length(); + minfo.dwTypeData = const_cast(&Text[0]); + minfo.fState = Enabled ? MFS_ENABLED : MFS_DISABLED; + return InsertMenuItemA(m_Handle, Position, TRUE, &minfo); + } + + BOOL CUIMenu::Append(const std::string& Text, const CUIMenu& Menu, const BOOL Enabled) + { + return Insert(Text, -1, Menu, Enabled); + } + + BOOL CUIMenu::InsertSeparator(const UINT Position) + { + MENUITEMINFOA minfo = { 0 }; + minfo.cbSize = sizeof(MENUITEMINFOA); + minfo.fMask = MIIM_FTYPE | MIIM_STATE; + minfo.fType = MFT_SEPARATOR; + return InsertMenuItemA(m_Handle, Position, TRUE, &minfo); + } + + BOOL CUIMenu::AppendSeparator(void) + { + return InsertSeparator(-1); + } + + CUIMenu CUIMenu::GetSubMenuItem(const UINT Position) + { + return GetSubMenu(m_Handle, Position); + } + + CUIMenuItem CUIMenu::First(void) + { + return CUIMenuItem(*this, 0, TRUE); + } + + CUIMenuItem CUIMenu::Last(void) + { + return CUIMenuItem(*this, Count() - 1, TRUE); + } + } + } +} \ No newline at end of file diff --git a/fallout4_test/src/patches/CKF4/UIMenus.h b/fallout4_test/src/patches/CKF4/UIMenus.h new file mode 100644 index 00000000..bc8fab7e --- /dev/null +++ b/fallout4_test/src/patches/CKF4/UIMenus.h @@ -0,0 +1,88 @@ +#pragma once + +#define VC_EXTRALEAN +#define WIN32_LEAN_AND_MEAN + +#include +#include + +namespace Core +{ + namespace Classes + { + namespace UI + { + class CUIMenu; + class CUIMenuItem + { + private: + CUIMenu* m_Menu; + BOOL m_ByPos; + UINT m_Pos; + public: + void SetText(const std::string &text); + std::string GetText(void) const; + void SetID(const UINT menu_id); + UINT GetID(void) const; + void SetChecked(const BOOL value); + BOOL GetChecked(void) const; + void SetEnabled(const BOOL value); + BOOL GetEnabled(void) const; + BOOL IsSeparate(void) const; + inline CUIMenu* Menu(void) const { return m_Menu; }; + inline BOOL ByPosition(void) const { return m_ByPos; }; + public: + static void Remove(CUIMenuItem* MenuItem); + static void Remove(CUIMenuItem& MenuItem); + public: + CUIMenuItem(void) : + m_Menu(NULL), m_Pos(0), m_ByPos(TRUE) + {} + CUIMenuItem(CUIMenu& Menu, const UINT Position, const BOOL ByPosition = TRUE) : + m_Menu(&Menu), m_Pos(Position), m_ByPos(ByPosition) + {} + CUIMenuItem(const CUIMenuItem &base) : + m_Menu(base.m_Menu), m_Pos(base.m_Pos), m_ByPos(base.m_ByPos) + {} + public: + __declspec(property(get = GetChecked, put = SetChecked)) const BOOL Checked; + __declspec(property(get = GetEnabled, put = SetEnabled)) const BOOL Enabled; + __declspec(property(get = GetID, put = SetID)) const UINT ID; + __declspec(property(get = GetText, put = SetText)) std::string Text; + }; + + class CUIMenu + { + private: + HMENU m_Handle; + public: + void SetHandle(const HMENU value); + HMENU GetHandle(void) const; + public: + UINT Count(void) const; + BOOL Insert(const std::string &Text, const UINT Position, const UINT MenuID, const BOOL Enabled = TRUE, const BOOL Checked = FALSE); + BOOL Append(const std::string &Text, const UINT MenuID, const BOOL Enabled = TRUE, const BOOL Checked = FALSE); + BOOL Insert(const std::string& Text, const UINT Position, const CUIMenu &Menu, const BOOL Enabled = TRUE); + BOOL Append(const std::string& Text, const CUIMenu &Menu, const BOOL Enabled = TRUE); + BOOL InsertSeparator(const UINT Position); + BOOL AppendSeparator(void); + void Remove(const UINT MenuID); + void RemoveByPos(const UINT Position); + inline BOOL IsEmpty(void) const { return (BOOL)Count(); } + CUIMenuItem GetItem(const UINT MenuID); + CUIMenuItem GetItemByPos(const UINT Position); + CUIMenuItem First(void); + CUIMenuItem Last(void); + CUIMenu GetSubMenuItem(const UINT Position); + public: + static CUIMenu CreateSubMenu(void); + public: + CUIMenu(void) : m_Handle(NULL) {} + CUIMenu(const HMENU menu) : m_Handle(menu) {} + CUIMenu(const CUIMenu& base) : m_Handle(base.m_Handle) {} + public: + __declspec(property(get = GetHandle, put = SetHandle)) const HMENU Handle; + }; + } + } +} \ No newline at end of file From 9917236c1fdf468386e5e7d8372e470e9c704c71 Mon Sep 17 00:00:00 2001 From: Perchik71 Date: Wed, 21 Oct 2020 12:12:04 +0300 Subject: [PATCH 002/257] additionally Lower and Upper --- fallout4_test/src/xutil.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/fallout4_test/src/xutil.h b/fallout4_test/src/xutil.h index 52f7806c..294c5242 100644 --- a/fallout4_test/src/xutil.h +++ b/fallout4_test/src/xutil.h @@ -84,6 +84,25 @@ struct __declspec(empty_bases)CheckOffset namespace XUtil { + namespace Str + { + // convert string to upper case + static inline std::string& UpperCase(std::string& s) { + std::for_each(s.begin(), s.end(), [](char& c) { + c = ::toupper(c); + }); + return s; + } + + // convert string to upper case + static inline std::string& LowerCase(std::string& s) { + std::for_each(s.begin(), s.end(), [](char& c) { + c = ::tolower(c); + }); + return s; + } + } + void SetThreadName(uint32_t ThreadID, const char *ThreadName); void Trim(char *Buffer, char C); void XAssert(const char *File, int Line, const char *Format, ...); @@ -113,4 +132,16 @@ namespace XUtil DetourCall(Target, *(uintptr_t *)&Destination); } + + template + void __stdcall DetourClassCall(uintptr_t target, T destination) + { + Detours::X64::DetourFunctionClass(target, destination, Detours::X64Option::USE_REL32_CALL); + } + + template + void __stdcall DetourClassJump(uintptr_t target, T destination) + { + Detours::X64::DetourFunctionClass(target, destination, Detours::X64Option::USE_REL32_JUMP); + } } \ No newline at end of file From a0fea845b1a32248b6acf70f8186dc256dba4aa3 Mon Sep 17 00:00:00 2001 From: Perchik71 Date: Wed, 21 Oct 2020 12:13:00 +0300 Subject: [PATCH 003/257] Active and FormID property Active and FormID property --- fallout4_test/src/patches/CKF4/TESForm_CK.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fallout4_test/src/patches/CKF4/TESForm_CK.h b/fallout4_test/src/patches/CKF4/TESForm_CK.h index 275656ac..dffc9058 100644 --- a/fallout4_test/src/patches/CKF4/TESForm_CK.h +++ b/fallout4_test/src/patches/CKF4/TESForm_CK.h @@ -4,6 +4,16 @@ class TESForm_CK { +private: + char _pad[0x8]; + uint32_t FormFlags; + uint32_t FormID; +public: + inline bool GetActive(void) const { return (FormFlags & 2) != 0; } + inline uint32_t GetFormID() const { return FormID; } +public: + __declspec(property(get = GetActive)) bool Active; + __declspec(property(get = GetFormID)) uint32_t FormID; public: using Array = BSTArray; From 78cac2f7dbb713749ced44a412ea35d758f99cc6 Mon Sep 17 00:00:00 2001 From: Perchik71 Date: Wed, 21 Oct 2020 12:14:41 +0300 Subject: [PATCH 004/257] pathes add Apply filtered Active only, fixed resize etc --- fallout4_test/src/patches/patches_f4ck.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fallout4_test/src/patches/patches_f4ck.cpp b/fallout4_test/src/patches/patches_f4ck.cpp index eabdd7ac..bf362066 100644 --- a/fallout4_test/src/patches/patches_f4ck.cpp +++ b/fallout4_test/src/patches/patches_f4ck.cpp @@ -112,6 +112,7 @@ void Patch_Fallout4CreationKit() if (g_INI.GetBoolean("CreationKit", "UI", false)) { EditorUI::Initialize(); + *(uintptr_t *)&EditorUI::OldWndProc = Detours::X64::DetourFunctionClass(OFFSET(0x05B74D0, 0), &EditorUI::WndProc); *(uintptr_t *)&EditorUI::OldObjectWindowProc = Detours::X64::DetourFunctionClass(OFFSET(0x03F9020, 0), &EditorUI::ObjectWindowProc); *(uintptr_t *)&EditorUI::OldCellViewProc = Detours::X64::DetourFunctionClass(OFFSET(0x059D820, 0), &EditorUI::CellViewProc); @@ -137,6 +138,15 @@ void Patch_Fallout4CreationKit() XUtil::DetourCall(OFFSET(0x03FE701, 0), &UpdateObjectWindowTreeView); XUtil::DetourCall(OFFSET(0x05A05FB, 0), &UpdateCellViewCellList); XUtil::DetourCall(OFFSET(0x05A1B0A, 0), &UpdateCellViewObjectList); + + // Fix resize ObjectWindowProc + XUtil::DetourCall(OFFSET(0x5669D8, 0), &EditorUI::hk_0x5669D8); + XUtil::PatchMemoryNop(OFFSET(0x5669DD, 0), 0x40); + + // Allow forms to be filtered in ObjectWindowProc + XUtil::DetourCall(OFFSET(0x3FE4CA, 0), &EditorUI::hk_7FF72F57F8F0); + // Allow forms to be filtered in CellViewProc + XUtil::DetourCall(OFFSET(0x6435BF, 0), &EditorUI::hk_7FF70C322BC0); } if (g_INI.GetBoolean("CreationKit", "DisableWindowGhosting", false)) From d4e5a7dbe2d1afe0275eac31df9444f7db443e15 Mon Sep 17 00:00:00 2001 From: Perchik71 Date: Wed, 21 Oct 2020 12:15:15 +0300 Subject: [PATCH 005/257] adaptation VC2019 adaptation VC2019 --- fallout4_test/src/patches/CKF4/LogWindow.cpp | 38 ++++++++------------ 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/fallout4_test/src/patches/CKF4/LogWindow.cpp b/fallout4_test/src/patches/CKF4/LogWindow.cpp index c723d421..9bd48051 100644 --- a/fallout4_test/src/patches/CKF4/LogWindow.cpp +++ b/fallout4_test/src/patches/CKF4/LogWindow.cpp @@ -58,18 +58,16 @@ namespace LogWindow // Output window auto instance = (HINSTANCE)GetModuleHandle(nullptr); - WNDCLASSEX wc - { - .cbSize = sizeof(WNDCLASSEX), - .style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, - .lpfnWndProc = WndProc, - .hInstance = instance, - .hIcon = LoadIcon(instance, MAKEINTRESOURCE(0x13E)), - .hCursor = LoadCursor(nullptr, IDC_ARROW), - .hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH), - .lpszClassName = TEXT("RTEDITLOG"), - .hIconSm = wc.hIcon, - }; + WNDCLASSEX wc = { 0 }; + wc.cbSize = sizeof(WNDCLASSEX); + wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS; + wc.lpfnWndProc = WndProc; + wc.hInstance = instance; + wc.hIcon = LoadIcon(instance, MAKEINTRESOURCE(0x13E)); + wc.hCursor = LoadCursor(nullptr, IDC_ARROW); + wc.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH); + wc.lpszClassName = TEXT("RTEDITLOG"); + wc.hIconSm = wc.hIcon; if (!RegisterClassEx(&wc)) return false; @@ -100,12 +98,10 @@ namespace LogWindow bool CreateStdoutListener() { - SECURITY_ATTRIBUTES saAttr - { - .nLength = sizeof(SECURITY_ATTRIBUTES), - .lpSecurityDescriptor = nullptr, - .bInheritHandle = TRUE, - }; + SECURITY_ATTRIBUTES saAttr = { 0 }; + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.lpSecurityDescriptor = nullptr; + saAttr.bInheritHandle = TRUE; if (!CreatePipe(&ExternalPipeReaderHandle, &ExternalPipeWriterHandle, &saAttr, 0)) return false; @@ -321,11 +317,7 @@ namespace LogWindow for (const char *message : messages) { // Move caret to the end, then write - CHARRANGE range - { - .cpMin = LONG_MAX, - .cpMax = LONG_MAX, - }; + CHARRANGE range = { LONG_MAX, LONG_MAX }; SendMessageA(richEditHwnd, EM_EXSETSEL, 0, (LPARAM)&range); SendMessageA(richEditHwnd, EM_REPLACESEL, FALSE, (LPARAM)message); From 36533c877579ee4b525314a8a693c5ea4226c3de Mon Sep 17 00:00:00 2001 From: Perchik71 Date: Wed, 21 Oct 2020 12:15:50 +0300 Subject: [PATCH 006/257] Update EditorUI.h Apply filtered etc --- fallout4_test/src/patches/CKF4/EditorUI.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fallout4_test/src/patches/CKF4/EditorUI.h b/fallout4_test/src/patches/CKF4/EditorUI.h index 5ca9d8c1..05d8c8d7 100644 --- a/fallout4_test/src/patches/CKF4/EditorUI.h +++ b/fallout4_test/src/patches/CKF4/EditorUI.h @@ -1,6 +1,7 @@ #pragma once #include "../../common.h" +#include "TESForm_CK.h" #define UI_EDITOR_TOOLBAR 1 #define UI_EDITOR_OPENFORMBYID 52001 // Sent from the LogWindow on double click @@ -34,9 +35,11 @@ namespace EditorUI extern DLGPROC OldResponseWindowProc; HWND GetWindow(); + HWND GetObjectWindow(); + HWND GetCellViewWindow(); void Initialize(); - bool CreateExtensionMenu(HWND MainWindow, HMENU MainMenu); + //bool CreateExtensionMenu(HWND MainWindow, HMENU MainMenu); LRESULT CALLBACK WndProc(HWND Hwnd, UINT Message, WPARAM wParam, LPARAM lParam); INT_PTR CALLBACK ObjectWindowProc(HWND DialogHwnd, UINT Message, WPARAM wParam, LPARAM lParam); @@ -49,4 +52,8 @@ namespace EditorUI void *ListViewGetSelectedItem(HWND ListViewHandle); void ListViewDeselectItem(HWND ListViewHandle, void *Parameter); void TabControlDeleteItem(HWND TabControlHandle, uint32_t TabIndex); + + LRESULT WINAPI hk_0x5669D8(void); + int32_t WINAPI hk_7FF72F57F8F0(const int64_t ObjectListInsertData, TESForm_CK* Form); + void WINAPI hk_7FF70C322BC0(HWND ListViewHandle, TESForm_CK* Form, bool UseImage, int32_t ItemIndex); } \ No newline at end of file From 9d72765cb2c06f7998c09e64e2207285e8668b2d Mon Sep 17 00:00:00 2001 From: Perchik71 Date: Wed, 21 Oct 2020 12:17:38 +0300 Subject: [PATCH 007/257] Implementing the UI Implementing the UI, erase Warning from menu, UpperCase MainMenu, filtered, fixed resize, fixed show/hide --- fallout4_test/src/patches/CKF4/EditorUI.cpp | 570 ++++++++++++++++---- 1 file changed, 451 insertions(+), 119 deletions(-) diff --git a/fallout4_test/src/patches/CKF4/EditorUI.cpp b/fallout4_test/src/patches/CKF4/EditorUI.cpp index c3117a4e..0e405671 100644 --- a/fallout4_test/src/patches/CKF4/EditorUI.cpp +++ b/fallout4_test/src/patches/CKF4/EditorUI.cpp @@ -7,12 +7,64 @@ #include "LogWindow.h" #include "TESForm_CK.h" +#include "UIMenus.h" +#include "UIBaseWindow.h" +#include "UICheckboxControl.h" + #pragma comment(lib, "comctl32.lib") +#define UI_CUSTOM_MESSAGE 52000 +#define UI_CMD_SHOWHIDE_OBJECTWINDOW (UI_CUSTOM_MESSAGE + 2) +#define UI_CMD_SHOWHIDE_CELLVIEWWINDOW (UI_CUSTOM_MESSAGE + 3) +#define UI_CMD_CHANGE_SPLITTER_OBJECTWINDOW (UI_CUSTOM_MESSAGE + 4) +#define UI_CELL_WINDOW_ADD_ITEM UI_OBJECT_WINDOW_ADD_ITEM + namespace EditorUI { - HWND MainWindowHandle; - HMENU ExtensionMenuHandle; + // Since very little is described, it is easier for me to implement some of the developments + + Core::Classes::UI::CUICustomWindow MainWindow; + Core::Classes::UI::CUICustomWindow ObjectWindow; + Core::Classes::UI::CUICustomWindow CellViewWindow; + + struct ObjectWindowControls_t + { + BOOL StartResize = FALSE; + + Core::Classes::UI::CUIBaseControl TreeList; + Core::Classes::UI::CUIBaseControl ItemList; + Core::Classes::UI::CUIBaseControl ToggleDecompose; + Core::Classes::UI::CUIBaseControl BtnObjLayout; + Core::Classes::UI::CUIBaseControl ComboLayout; + Core::Classes::UI::CUIBaseControl EditFilter; + Core::Classes::UI::CUIBaseControl Spliter; + Core::Classes::UI::CUICheckbox ActiveOnly; + } ObjectWindowControls; + + struct CellViewWindowControls_t + { + BOOL Initialize = FALSE; + + Core::Classes::UI::CUIBaseControl LabelWorldSpace; + Core::Classes::UI::CUIBaseControl NoCellSellected; + Core::Classes::UI::CUIBaseControl Interiors; + Core::Classes::UI::CUIBaseControl LoadedAtTop; + Core::Classes::UI::CUIBaseControl FilteredOnly; + Core::Classes::UI::CUIBaseControl VisibleOnly; + Core::Classes::UI::CUIBaseControl SelectedOnly; + Core::Classes::UI::CUIBaseControl LabelX; + Core::Classes::UI::CUIBaseControl LabelY; + Core::Classes::UI::CUIBaseControl EditX; + Core::Classes::UI::CUIBaseControl EditY; + Core::Classes::UI::CUIBaseControl EditCellFiltered; + Core::Classes::UI::CUIBaseControl BtnGo; + Core::Classes::UI::CUIBaseControl Lst1; + Core::Classes::UI::CUIBaseControl Lst2; + Core::Classes::UI::CUICheckbox ActiveOnly; + } CellViewWindowControls; + + // A pointer to the main menu, I will create in WM_CREATE and delete it WM_DESTROY. I will use it everywhere. + Core::Classes::UI::CUIMenu* MainMenu; WNDPROC OldWndProc; DLGPROC OldObjectWindowProc; @@ -21,7 +73,24 @@ namespace EditorUI HWND GetWindow() { - return MainWindowHandle; + return MainWindow.Handle; + } + + HWND GetObjectWindow() + { + return ObjectWindow.Handle; + } + + HWND GetCellViewWindow() + { + return CellViewWindow.Handle; + } + + LRESULT WINAPI hk_0x5669D8(void) + { + ObjectWindow.Perform(WM_COMMAND, UI_CMD_CHANGE_SPLITTER_OBJECTWINDOW, 0); + + return 0; } void Initialize() @@ -33,12 +102,17 @@ namespace EditorUI MessageBoxA(nullptr, "Failed to create console log window", "Error", MB_ICONERROR); } - bool CreateExtensionMenu(HWND MainWindow, HMENU MainMenu) + bool CreateExtensionMenu(/*HWND MainWindow, */Core::Classes::UI::CUIMenu* MainMenu) { - // Create extended menu options - ExtensionMenuHandle = CreateMenu(); - BOOL result = TRUE; + + // Microsoft does not recommend using InsertMenu + // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-insertmenua + // "Note The InsertMenu function has been superseded by the InsertMenuItem function. + // You can still use InsertMenu, however, if you do not need any of the extended features of InsertMenuItem." + + /* // Create extended menu options + ExtensionMenuHandle = CreateMenu(); result = result && InsertMenu(ExtensionMenuHandle, -1, MF_BYPOSITION | MF_STRING, UI_EXTMENU_SHOWLOG, "Show Log"); result = result && InsertMenu(ExtensionMenuHandle, -1, MF_BYPOSITION | MF_STRING, UI_EXTMENU_CLEARLOG, "Clear Log"); result = result && InsertMenu(ExtensionMenuHandle, -1, MF_BYPOSITION | MF_STRING | MF_CHECKED, UI_EXTMENU_AUTOSCROLL, "Autoscroll Log"); @@ -47,15 +121,14 @@ namespace EditorUI result = result && InsertMenu(ExtensionMenuHandle, -1, MF_BYPOSITION | MF_SEPARATOR, UI_EXTMENU_SPACER, ""); result = result && InsertMenu(ExtensionMenuHandle, -1, MF_BYPOSITION | MF_STRING, UI_EXTMENU_HARDCODEDFORMS, "Save Hardcoded Forms"); - MENUITEMINFO menuInfo - { - .cbSize = sizeof(MENUITEMINFO), - .fMask = MIIM_SUBMENU | MIIM_ID | MIIM_STRING, - .wID = UI_EXTMENU_ID, - .hSubMenu = ExtensionMenuHandle, - .dwTypeData = "Extensions", - .cch = (uint32_t)strlen(menuInfo.dwTypeData) - }; + MENUITEMINFO menuInfo = { 0 }; + menuInfo.cbSize = sizeof(MENUITEMINFO); + menuInfo.fMask = MIIM_SUBMENU | MIIM_ID | MIIM_STRING; + menuInfo.wID = UI_EXTMENU_ID; + menuInfo.hSubMenu = ExtensionMenuHandle; + menuInfo.dwTypeData = "Extensions"; + menuInfo.cch = (uint32_t)strlen(menuInfo.dwTypeData); + result = result && InsertMenuItem(MainMenu, -1, TRUE, &menuInfo); // Links @@ -70,14 +143,43 @@ namespace EditorUI menuInfo.wID = UI_EXTMENU_LINKS_ID; menuInfo.dwTypeData = "Links"; menuInfo.cch = (uint32_t)strlen(menuInfo.dwTypeData); - result = result && InsertMenuItem(MainMenu, -1, TRUE, &menuInfo); + result = result && InsertMenuItem(MainMenu, -1, TRUE, &menuInfo);*/ + + // I tend to write object-oriented + + Core::Classes::UI::CUIMenu* ExtensionSubMenu = new Core::Classes::UI::CUIMenu(Core::Classes::UI::CUIMenu::CreateSubMenu()); + Core::Classes::UI::CUIMenu* LinksSubMenu = new Core::Classes::UI::CUIMenu(Core::Classes::UI::CUIMenu::CreateSubMenu()); + + Assert(ExtensionSubMenu); + Assert(LinksSubMenu); + + // I'll add hiding and showing the log window + + result = result && ExtensionSubMenu->Append("Show/Hide Log", UI_EXTMENU_SHOWLOG, TRUE, IsWindowVisible(LogWindow::GetWindow())); + result = result && ExtensionSubMenu->Append("Clear Log", UI_EXTMENU_CLEARLOG); + result = result && ExtensionSubMenu->Append("Autoscroll Log", UI_EXTMENU_AUTOSCROLL, TRUE, TRUE); + result = result && ExtensionSubMenu->AppendSeparator(); + result = result && ExtensionSubMenu->Append("Dump Active Forms", UI_EXTMENU_LOADEDESPINFO); + result = result && ExtensionSubMenu->AppendSeparator(); + result = result && ExtensionSubMenu->Append("Save Hardcoded Forms", UI_EXTMENU_HARDCODEDFORMS); + result = result && MainMenu->Append("Extensions", *ExtensionSubMenu); + + result = result && LinksSubMenu->Append("Cascadia Wiki", UI_EXTMENU_LINKS_WIKI); + result = result && MainMenu->Append("Links", *LinksSubMenu); + + // I don't use DeleteMenu when destroying, I don't need to store a pointer and all that. + + delete LinksSubMenu; + delete ExtensionSubMenu; AssertMsg(result, "Failed to create extension submenus"); - return result != FALSE; + return result; } LRESULT CALLBACK WndProc(HWND Hwnd, UINT Message, WPARAM wParam, LPARAM lParam) { + Core::Classes::UI::CUIMenuItem MenuItem; + if (Message == WM_CREATE) { auto createInfo = (const CREATESTRUCT *)lParam; @@ -85,14 +187,46 @@ namespace EditorUI if (!_stricmp(createInfo->lpszName, "Creation Kit") && !_stricmp(createInfo->lpszClass, "Creation Kit")) { // Initialize the original window before adding anything - LRESULT status = CallWindowProc(OldWndProc, Hwnd, Message, wParam, lParam); - MainWindowHandle = Hwnd; + + // The entire API is in the Creation Kit ANSI, so it's better to use A + LRESULT status = CallWindowProcA(OldWndProc, Hwnd, Message, wParam, lParam); + MainWindow = Hwnd; + + // Set font default + // This is the default value, but I need an object record to create the missing controls + MainWindow.Font = Core::Classes::UI::CFont("MS Sans Serif", 8, {}, Core::Classes::UI::fqClearTypeNatural, Core::Classes::UI::fpVariable); // Create custom menu controls - CreateExtensionMenu(Hwnd, createInfo->hMenu); + MainMenu = new Core::Classes::UI::CUIMenu(createInfo->hMenu); + CreateExtensionMenu(MainMenu); + + // All main menus change to uppercase letters + for (UINT i = 0; i < MainMenu->Count(); i++) + { + MenuItem = MainMenu->GetItemByPos(i); + MenuItem.Text = XUtil::Str::UpperCase(MenuItem.Text); + } + + Core::Classes::UI::CUIMenu ViewMenu = MainMenu->GetSubMenuItem(2); + + // How annoying is this window Warnings, delete from the menu. + ViewMenu.RemoveByPos(34); + + // Fix show/hide object & cell view windows + MenuItem = ViewMenu.GetItemByPos(2); + MenuItem.ID = UI_CMD_SHOWHIDE_OBJECTWINDOW; + MenuItem.Checked = TRUE; + MenuItem = ViewMenu.GetItemByPos(3); + MenuItem.ID = UI_CMD_SHOWHIDE_CELLVIEWWINDOW; + MenuItem.Checked = TRUE; + return status; } } + else if (Message == WM_DESTROY) + { + delete MainMenu; + } else if (Message == WM_COMMAND) { const uint32_t param = LOWORD(wParam); @@ -110,8 +244,20 @@ namespace EditorUI case UI_EXTMENU_SHOWLOG: { - ShowWindow(LogWindow::GetWindow(), SW_SHOW); - SetForegroundWindow(LogWindow::GetWindow()); + // I already have a class that describes the basic functions of Windows, + // but there is no point in rewriting it too much, so I'll add hiding and showing the log window. + + if (IsWindowVisible(LogWindow::GetWindow())) + ShowWindow(LogWindow::GetWindow(), SW_HIDE); + else + { + ShowWindow(LogWindow::GetWindow(), SW_SHOW); + SetForegroundWindow(LogWindow::GetWindow()); + } + + // Change the checkbox + MenuItem = MainMenu->GetItem(UI_EXTMENU_SHOWLOG); + MenuItem.Checked = !MenuItem.Checked; } return 0; @@ -123,11 +269,10 @@ namespace EditorUI case UI_EXTMENU_AUTOSCROLL: { - MENUITEMINFO info - { - .cbSize = sizeof(MENUITEMINFO), - .fMask = MIIM_STATE - }; + /* MENUITEMINFO info = { 0 }; + info.cbSize = sizeof(MENUITEMINFO); + info.fMask = MIIM_STATE; + GetMenuItemInfo(ExtensionMenuHandle, param, FALSE, &info); bool check = !((info.fState & MFS_CHECKED) == MFS_CHECKED); @@ -138,22 +283,26 @@ namespace EditorUI info.fState |= MFS_CHECKED; PostMessageA(LogWindow::GetWindow(), UI_LOG_CMD_AUTOSCROLL, (WPARAM)check, 0); - SetMenuItemInfo(ExtensionMenuHandle, param, FALSE, &info); + SetMenuItemInfo(ExtensionMenuHandle, param, FALSE, &info);*/ + + // Change the checkbox + MenuItem = MainMenu->GetItem(UI_EXTMENU_AUTOSCROLL); + MenuItem.Checked = !MenuItem.Checked; + + PostMessageA(LogWindow::GetWindow(), UI_LOG_CMD_AUTOSCROLL, (WPARAM)MenuItem.Checked, 0); } return 0; case UI_EXTMENU_LOADEDESPINFO: { char filePath[MAX_PATH] = {}; - OPENFILENAME ofnData - { - .lStructSize = sizeof(OPENFILENAME), - .lpstrFilter = "Text Files (*.txt)\0*.txt\0\0", - .lpstrFile = filePath, - .nMaxFile = ARRAYSIZE(filePath), - .Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR, - .lpstrDefExt = "txt" - }; + OPENFILENAME ofnData = { 0 }; + ofnData.lStructSize = sizeof(OPENFILENAME); + ofnData.lpstrFilter = "Text Files (*.txt)\0*.txt\0\0"; + ofnData.lpstrFile = filePath; + ofnData.nMaxFile = ARRAYSIZE(filePath); + ofnData.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR; + ofnData.lpstrDefExt = "txt"; if (FILE *f; GetSaveFileName(&ofnData) && fopen_s(&f, filePath, "w") == 0) { @@ -254,7 +403,39 @@ namespace EditorUI case UI_EXTMENU_LINKS_WIKI: { - ShellExecute(nullptr, "open", "https://wiki.falloutcascadia.com/index.php?title=Main_Page", "", "", SW_SHOW); + ShellExecuteA(nullptr, "open", "https://wiki.falloutcascadia.com/index.php?title=Main_Page", "", "", SW_SHOW); + } + return 0; + + case UI_CMD_SHOWHIDE_OBJECTWINDOW: + { + if (IsWindowVisible(GetObjectWindow())) + ShowWindow(GetObjectWindow(), SW_HIDE); + else + { + ShowWindow(GetObjectWindow(), SW_SHOW); + SetForegroundWindow(GetObjectWindow()); + } + + // Change the checkbox + MenuItem = MainMenu->GetItem(UI_CMD_SHOWHIDE_OBJECTWINDOW); + MenuItem.Checked = !MenuItem.Checked; + } + return 0; + + case UI_CMD_SHOWHIDE_CELLVIEWWINDOW: + { + if (IsWindowVisible(GetCellViewWindow())) + ShowWindow(GetCellViewWindow(), SW_HIDE); + else + { + ShowWindow(GetCellViewWindow(), SW_SHOW); + SetForegroundWindow(GetCellViewWindow()); + } + + // Change the checkbox + MenuItem = MainMenu->GetItem(UI_CMD_SHOWHIDE_CELLVIEWWINDOW); + MenuItem.Checked = !MenuItem.Checked; } return 0; } @@ -265,115 +446,274 @@ namespace EditorUI char customTitle[1024]; sprintf_s(customTitle, "%s [CK64Fixes Rev. F4-%s]", (const char *)lParam, g_GitVersion); - return CallWindowProc(OldWndProc, Hwnd, Message, wParam, (LPARAM)customTitle); + return CallWindowProcA(OldWndProc, Hwnd, Message, wParam, (LPARAM)customTitle); } - return CallWindowProc(OldWndProc, Hwnd, Message, wParam, lParam); + return CallWindowProcA(OldWndProc, Hwnd, Message, wParam, lParam); + } + + void ResizeObjectWndChildControls(void) + { + // The perfectionist in me is dying.... + + ObjectWindowControls.TreeList.LockUpdate(); + ObjectWindowControls.ItemList.LockUpdate(); + ObjectWindowControls.EditFilter.LockUpdate(); + ObjectWindowControls.ToggleDecompose.LockUpdate(); + ObjectWindowControls.BtnObjLayout.LockUpdate(); + + LONG w_tree = ObjectWindowControls.TreeList.Width; + LONG w_left = w_tree - ObjectWindowControls.BtnObjLayout.Width + 1; + ObjectWindowControls.BtnObjLayout.Left = w_left; + ObjectWindowControls.ToggleDecompose.Left = w_left; + + w_left = w_left - ObjectWindowControls.EditFilter.Left - 3; + ObjectWindowControls.EditFilter.Width = w_left; + ObjectWindowControls.ComboLayout.Width = w_left; + + ObjectWindowControls.ItemList.Left = w_tree + 5; + ObjectWindowControls.ItemList.Width = ObjectWindow.ClientWidth() - (w_tree + 5); + + RedrawWindow(ObjectWindow.Handle, NULL, NULL, RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW | RDW_NOCHILDREN); + ObjectWindowControls.BtnObjLayout.UnlockUpdate(); + ObjectWindowControls.ToggleDecompose.UnlockUpdate(); + ObjectWindowControls.EditFilter.UnlockUpdate(); + ObjectWindowControls.ItemList.UnlockUpdate(); + ObjectWindowControls.TreeList.UnlockUpdate(); + ObjectWindowControls.BtnObjLayout.Repaint(); + ObjectWindowControls.ToggleDecompose.Repaint(); + ObjectWindowControls.EditFilter.Repaint(); + ObjectWindowControls.ItemList.Repaint(); + ObjectWindowControls.TreeList.Repaint(); + } + + void SetObjectWindowFilter(const std::string& name, const BOOL SkipText, const BOOL actived) + { + if (!SkipText) + ObjectWindowControls.EditFilter.Caption = name; + + ObjectWindowControls.ActiveOnly.Checked = actived; + // Force the list items to update as if it was by timer + ObjectWindow.Perform(WM_TIMER, 0x1B58, 0); + } + + int32_t WINAPI hk_7FF72F57F8F0(const int64_t ObjectListInsertData, TESForm_CK* Form) + { + bool allowInsert = true; + ObjectWindow.Perform(UI_OBJECT_WINDOW_ADD_ITEM, (WPARAM)Form, (LPARAM)& allowInsert); + + if (!allowInsert) + return 1; + + return ((int32_t(__fastcall*)(int64_t, TESForm_CK*))OFFSET(0x40F8F0, 0))(ObjectListInsertData, Form); } INT_PTR CALLBACK ObjectWindowProc(HWND DialogHwnd, UINT Message, WPARAM wParam, LPARAM lParam) { if (Message == WM_INITDIALOG) { + ObjectWindow = DialogHwnd; + + // Set font default + // This is the default value, but I need an object record to create the missing controls + ObjectWindow.Font = Core::Classes::UI::CFont("MS Sans Serif", 8, {}, Core::Classes::UI::fqClearTypeNatural, Core::Classes::UI::fpVariable); + + ObjectWindowControls.TreeList = ObjectWindow.GetControl(2093); + ObjectWindowControls.ItemList = ObjectWindow.GetControl(1041); + ObjectWindowControls.ToggleDecompose = ObjectWindow.GetControl(6027); + ObjectWindowControls.BtnObjLayout = ObjectWindow.GetControl(6025); + ObjectWindowControls.ComboLayout = ObjectWindow.GetControl(6024); + ObjectWindowControls.EditFilter = ObjectWindow.GetControl(2581); + ObjectWindowControls.Spliter = ObjectWindow.GetControl(2157); + + // Eliminate the flicker when resizing + ObjectWindowControls.TreeList.Perform(TVM_SETEXTENDEDSTYLE, TVS_EX_DOUBLEBUFFER, TVS_EX_DOUBLEBUFFER); + // Eliminate the flicker when changing categories - ListView_SetExtendedListViewStyleEx(GetDlgItem(DialogHwnd, 1041), LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER); + ListView_SetExtendedListViewStyleEx(ObjectWindowControls.ItemList.Handle, LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER); + + // Erase Icon and SysMenu + ObjectWindow.Style = WS_OVERLAPPED | WS_CAPTION | WS_THICKFRAME; + + // Include filter "Active Only" + Core::Classes::UI::CRECT bounds = ObjectWindowControls.EditFilter.BoundsRect; + ObjectWindowControls.ActiveOnly.CreateWnd(ObjectWindow, "Active Only *", bounds.Left, ObjectWindowControls.TreeList.Top, bounds.Width, 14, UI_OBJECT_WINDOW_ADD_ITEM); + ObjectWindowControls.ActiveOnly.Visible = TRUE; + ObjectWindowControls.TreeList.Top += 17; + + ObjectWindowControls.BtnObjLayout.Top = ObjectWindowControls.ComboLayout.Top - 1; + ObjectWindowControls.BtnObjLayout.Height = ObjectWindowControls.ComboLayout.Height + 2; + ObjectWindowControls.ToggleDecompose.Top = ObjectWindowControls.EditFilter.Top - 1; + ObjectWindowControls.ToggleDecompose.Height = ObjectWindowControls.EditFilter.Height + 2; } - /* - else if (Message == WM_COMMAND) + // Don't let us reduce the window too much + else if (Message == WM_GETMINMAXINFO) { - const uint32_t param = LOWORD(wParam); + LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam; + lpMMI->ptMinTrackSize.x = 350; + lpMMI->ptMinTrackSize.y = 500; - if (param == UI_OBJECT_WINDOW_CHECKBOX) + return 0; + } + else if (Message == WM_SIZE) + { + if (!ObjectWindowControls.StartResize) { - bool enableFilter = SendMessage((HWND)lParam, BM_GETCHECK, 0, 0) == BST_CHECKED; - SetPropA(DialogHwnd, "ActiveOnly", (HANDLE)enableFilter); - - // Force the list items to update as if it was by timer - SendMessageA(DialogHwnd, WM_TIMER, 0x4D, 0); - return 1; + ObjectWindowControls.StartResize = TRUE; + ResizeObjectWndChildControls(); } } else if (Message == UI_OBJECT_WINDOW_ADD_ITEM) { - const bool onlyActiveForms = (bool)GetPropA(DialogHwnd, "ActiveOnly"); - const auto form = (TESForm_CK *)wParam; - bool *allowInsert = (bool *)lParam; - + auto form = reinterpret_cast(wParam); + auto allowInsert = reinterpret_cast(lParam); *allowInsert = true; - if (onlyActiveForms) + if (ObjectWindowControls.ActiveOnly.Checked) { - if (form && !form->GetActive()) - *allowInsert = false; + if (form && !form->Active) + * allowInsert = false; } - return 1; + return 0; } - */ + else if (Message == WM_COMMAND) + { + switch (wParam) + { + case UI_OBJECT_WINDOW_ADD_ITEM: + SetObjectWindowFilter("", TRUE, !ObjectWindowControls.ActiveOnly.Checked); + return 0; + case UI_CMD_CHANGE_SPLITTER_OBJECTWINDOW: + ResizeObjectWndChildControls(); + return 0; + } + } + return OldObjectWindowProc(DialogHwnd, Message, wParam, lParam); } + + void SetCellWindowFilter(const BOOL actived) + { + CellViewWindowControls.ActiveOnly.Checked = actived; + // Fake the dropdown list being activated + CellViewWindow.Perform(WM_COMMAND, MAKEWPARAM(2083, 1), 0); + } + + void WINAPI hk_7FF70C322BC0(HWND ListViewHandle, TESForm_CK* Form, bool UseImage, int32_t ItemIndex) + { + bool allowInsert = true; + CellViewWindow.Perform(UI_CELL_WINDOW_ADD_ITEM, (WPARAM)Form, (LPARAM)& allowInsert); + + if (!allowInsert) + return; + + return ((void(__fastcall*)(HWND, TESForm_CK*, bool, int32_t))OFFSET(0x562BC0, 0))(ListViewHandle, Form, UseImage, ItemIndex); + } INT_PTR CALLBACK CellViewProc(HWND DialogHwnd, UINT Message, WPARAM wParam, LPARAM lParam) { if (Message == WM_INITDIALOG) { - // Eliminate the flicker when changing cells - ListView_SetExtendedListViewStyleEx(GetDlgItem(DialogHwnd, 1155), LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER); - ListView_SetExtendedListViewStyleEx(GetDlgItem(DialogHwnd, 1156), LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER); + // This message is called a great many times, especially when updating World Space - ShowWindow(GetDlgItem(DialogHwnd, 1007), SW_HIDE); - } - /* - else if (Message == WM_SIZE) - { - auto *labelRect = (RECT *)OFFSET(0x3AFB570, 1530); + if (!CellViewWindowControls.Initialize) + { + CellViewWindow = DialogHwnd; + + // Set font default + // This is the default value, but I need an object record to create the missing controls + CellViewWindow.Font = Core::Classes::UI::CFont("MS Sans Serif", 8, {}, Core::Classes::UI::fqClearTypeNatural, Core::Classes::UI::fpVariable); + + CellViewWindowControls.LabelWorldSpace = CellViewWindow.GetControl(1164); + CellViewWindowControls.NoCellSellected = CellViewWindow.GetControl(1163); + CellViewWindowControls.Interiors = CellViewWindow.GetControl(2083); + CellViewWindowControls.LoadedAtTop = CellViewWindow.GetControl(5662); + CellViewWindowControls.FilteredOnly = CellViewWindow.GetControl(5664); + CellViewWindowControls.VisibleOnly = CellViewWindow.GetControl(5666); + CellViewWindowControls.SelectedOnly = CellViewWindow.GetControl(5665); + CellViewWindowControls.LabelX = CellViewWindow.GetControl(5281); + CellViewWindowControls.LabelY = CellViewWindow.GetControl(5282); + CellViewWindowControls.EditX = CellViewWindow.GetControl(5283); + CellViewWindowControls.EditY = CellViewWindow.GetControl(5099); + CellViewWindowControls.EditCellFiltered = CellViewWindow.GetControl(2581); + CellViewWindowControls.BtnGo = CellViewWindow.GetControl(3681); + CellViewWindowControls.Lst1 = CellViewWindow.GetControl(1155); + CellViewWindowControls.Lst2 = CellViewWindow.GetControl(1156); + + CellViewWindowControls.LabelWorldSpace.Style |= SS_CENTER; + + // Eliminate the flicker when changing cells + ListView_SetExtendedListViewStyleEx(CellViewWindowControls.Lst1.Handle, LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER); + ListView_SetExtendedListViewStyleEx(CellViewWindowControls.Lst2.Handle, LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER); + + ShowWindow(GetDlgItem(DialogHwnd, 1007), SW_HIDE); + } - // Fix the "World Space" label positioning on window resize - RECT label; - GetClientRect(GetDlgItem(DialogHwnd, 1164), &label); + Core::Classes::UI::CRECT rBounds_1 = CellViewWindowControls.Lst1.BoundsRect; + Core::Classes::UI::CRECT rBounds_2 = CellViewWindowControls.EditCellFiltered.BoundsRect; - RECT rect; - GetClientRect(GetDlgItem(DialogHwnd, 2083), &rect); + CellViewWindowControls.Interiors.BoundsRect = { rBounds_1.Left, rBounds_2.Top, rBounds_1.Right, rBounds_2.Bottom }; + rBounds_2 = CellViewWindowControls.NoCellSellected.BoundsRect; + CellViewWindowControls.LabelWorldSpace.BoundsRect = { rBounds_1.Left, rBounds_2.Top, rBounds_1.Right, rBounds_2.Bottom }; - int ddMid = rect.left + ((rect.right - rect.left) / 2); - int labelMid = (label.right - label.left) / 2; + CellViewWindowControls.LoadedAtTop.Move(161, 44); + CellViewWindowControls.LabelX.Move(14, 56); + CellViewWindowControls.EditX.Move(28, 50); + CellViewWindowControls.LabelY.Move(68, 56); + CellViewWindowControls.EditY.Move(80, 50); + CellViewWindowControls.BtnGo.Move(124, 51); - SetWindowPos(GetDlgItem(DialogHwnd, 1164), nullptr, ddMid - (labelMid / 2), labelRect->top, 0, 0, SWP_NOSIZE); + rBounds_1 = CellViewWindowControls.Lst2.BoundsRect; + rBounds_2 = CellViewWindowControls.Interiors.BoundsRect; + CellViewWindowControls.EditCellFiltered.BoundsRect = { rBounds_1.Left, rBounds_2.Top, rBounds_1.Right, rBounds_2.Bottom }; - // Force the dropdown to extend the full length of the column - labelRect->right = 0; - } - else if (Message == WM_COMMAND) - { - const uint32_t param = LOWORD(wParam); + CellViewWindowControls.SelectedOnly.Move(rBounds_1.Left + 5, 61); + CellViewWindowControls.VisibleOnly.Move(rBounds_1.Left + 115, 44); - if (param == UI_CELL_VIEW_CHECKBOX) + CellViewWindowControls.Lst1.Top += 1; + CellViewWindowControls.Lst2.Top += 1; + + if (!CellViewWindowControls.Initialize) { - bool enableFilter = SendMessage((HWND)lParam, BM_GETCHECK, 0, 0) == BST_CHECKED; - SetPropA(DialogHwnd, "ActiveOnly", (HANDLE)enableFilter); + // Include filter "Active Only" + rBounds_1 = CellViewWindowControls.LoadedAtTop.BoundsRect; + CellViewWindowControls.ActiveOnly.CreateWnd(CellViewWindow, "Active Only *", rBounds_1.Left, rBounds_1.Bottom, rBounds_1.Width, 14, UI_CELL_WINDOW_ADD_ITEM); - // Fake the dropdown list being activated - SendMessageA(DialogHwnd, WM_COMMAND, MAKEWPARAM(2083, 1), 0); - return 1; + CellViewWindowControls.Initialize = TRUE; } } - else if (Message == UI_CELL_VIEW_ADD_CELL_ITEM) + else if (Message == UI_CELL_WINDOW_ADD_ITEM) { - const bool onlyActiveForms = (bool)GetPropA(DialogHwnd, "ActiveOnly"); - const auto form = (TESForm_CK *)wParam; - bool *allowInsert = (bool *)lParam; + auto form = reinterpret_cast(wParam); + auto allowInsert = reinterpret_cast(lParam); *allowInsert = true; - if (onlyActiveForms) + // Skip the entry if "Show only active forms" is checked + if (CellViewWindowControls.ActiveOnly.Checked) { - if (form && !form->GetActive()) + if (form && !form->Active) *allowInsert = false; } - return 1; + return 0; + } + else if (Message == WM_COMMAND) + { + switch (wParam) + { + case UI_CELL_WINDOW_ADD_ITEM: + SetCellWindowFilter(!CellViewWindowControls.ActiveOnly.Checked); + return 0; + } } - */ + else if (Message == WM_SIZE) + { + // Fix the "World Space" label positioning on window resize + CellViewWindowControls.LabelWorldSpace.Width = CellViewWindowControls.Lst1.Width; + } + return OldCellViewProc(DialogHwnd, Message, wParam, lParam); } @@ -472,12 +812,10 @@ namespace EditorUI BOOL ListViewCustomSetItemState(HWND ListViewHandle, WPARAM Index, UINT Data, UINT Mask) { // Microsoft's implementation of this define is broken (ListView_SetItemState) - LVITEMA item - { - .mask = LVIF_STATE, - .state = Data, - .stateMask = Mask - }; + LVITEMA item = { 0 }; + item.mask = LVIF_STATE; + item.state = Data; + item.stateMask = Mask; return (BOOL)SendMessageA(ListViewHandle, LVM_SETITEMSTATE, Index, (LPARAM)&item); } @@ -499,11 +837,9 @@ namespace EditorUI if (!KeepOtherSelections) ListViewCustomSetItemState(ListViewHandle, -1, 0, LVIS_SELECTED); - LVFINDINFOA findInfo - { - .flags = LVFI_PARAM, - .lParam = (LPARAM)Parameter - }; + LVFINDINFOA findInfo = { 0 }; + findInfo.flags = LVFI_PARAM; + findInfo.lParam = (LPARAM)Parameter; int index = ListView_FindItem(ListViewHandle, -1, &findInfo); @@ -521,11 +857,9 @@ namespace EditorUI if (index == -1) return nullptr; - LVITEMA item - { - .mask = LVIF_PARAM, - .iItem = index - }; + LVITEMA item = { 0 }; + item.mask = LVIF_PARAM; + item.iItem = index; ListView_GetItem(ListViewHandle, &item); return (void *)item.lParam; @@ -533,11 +867,9 @@ namespace EditorUI void ListViewDeselectItem(HWND ListViewHandle, void *Parameter) { - LVFINDINFOA findInfo - { - .flags = LVFI_PARAM, - .lParam = (LPARAM)Parameter - }; + LVFINDINFOA findInfo = { 0 }; + findInfo.flags = LVFI_PARAM; + findInfo.lParam = (LPARAM)Parameter; int index = ListView_FindItem(ListViewHandle, -1, &findInfo); From 152ec2eb5969ae1a83d0497216c92b5b0782b46b Mon Sep 17 00:00:00 2001 From: Perchik71 Date: Wed, 21 Oct 2020 12:17:58 +0300 Subject: [PATCH 008/257] Update fallout4_test.vcxproj --- fallout4_test/fallout4_test.vcxproj | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fallout4_test/fallout4_test.vcxproj b/fallout4_test/fallout4_test.vcxproj index f3564c2a..22703021 100644 --- a/fallout4_test/fallout4_test.vcxproj +++ b/fallout4_test/fallout4_test.vcxproj @@ -115,6 +115,7 @@ stdcpplatest /D "NOMINMAX" /D "ZYDIS_STATIC_DEFINE" /D "TRACY_ON_DEMAND" /D "_REENTRANT" /D "JEMALLOC_EXPORT=" %(AdditionalOptions) true + 4430;4267;4302;4311;4312;4996;4244;4477 Windows @@ -122,6 +123,7 @@ true true /ignore:4104 %(AdditionalOptions) + ..\x64\Release-MT;%(AdditionalLibraryDirectories) powershell -ExecutionPolicy Bypass -File "$(SolutionDir)release.ps1" 4 @@ -167,6 +169,9 @@ + + + @@ -204,7 +209,6 @@ - @@ -220,6 +224,9 @@ + + + From c01758d4c8082c0154e7bff41ab34a401a99e664 Mon Sep 17 00:00:00 2001 From: Perchik71 Date: Wed, 21 Oct 2020 12:18:19 +0300 Subject: [PATCH 009/257] Update fallout4_test.vcxproj.filters --- fallout4_test/fallout4_test.vcxproj.filters | 243 +++++++++++--------- 1 file changed, 138 insertions(+), 105 deletions(-) diff --git a/fallout4_test/fallout4_test.vcxproj.filters b/fallout4_test/fallout4_test.vcxproj.filters index 0d9d497c..200da2a2 100644 --- a/fallout4_test/fallout4_test.vcxproj.filters +++ b/fallout4_test/fallout4_test.vcxproj.filters @@ -12,6 +12,24 @@ {6ecca651-f6ac-4430-b4b5-a9d5fb31e2fc} + + {35ec8d65-0314-4ba5-b3b8-c4b0d305fd93} + + + {50666074-f04e-49df-b1ef-ebec84513633} + + + {744f2e83-7807-45a2-846e-e098caf43cc5} + + + {11571499-241a-44d6-ae8f-ecf1517440b0} + + + {e015c614-1eea-42df-b254-b63b981c8468} + + + {8ac21372-4a9a-4e6b-ac6d-0248039f9a3c} + @@ -35,146 +53,152 @@ Header Files - - Header Files - - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - Header Files - - - Header Files + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI\NiRTTI - - Header Files + + Header Files\RTTI - - Header Files + + Header Files\RTTI - Header Files + Header Files\UI + + + Header Files\UI - Header Files + Header Files\UI - - Header Files + + Header Files\UI + + + Header Files\UI + + + Header Files\UI + + + Header Files\UI @@ -193,27 +217,12 @@ Source Files\patches - - Source Files - Source Files Source Files - - Source Files - - - Source Files - - - Source Files - - - Source Files - Source Files @@ -226,37 +235,61 @@ Source Files - - Source Files - Source Files Source Files - + Source Files - - Source Files + + Source Files\RTTI\NiRTTI + + + Source Files\RTTI\NiRTTI + + + Source Files\RTTI\NiRTTI + + + Source Files\RTTI\NiRTTI + + + Source Files\RTTI + + + Source Files\RTTI - Source Files + Source Files\UI + + + Source Files\UI + + + Source Files\UI - Source Files + Source Files\UI - - Source Files + + Source Files\UI + + + Source Files\UI + + + Source Files\UI - - Header Files - - Header Files + Header Files\RTTI\NiRTTI + + + Header Files\RTTI\NiRTTI From c7464390383d6618b15308d286a665747cd0e58b Mon Sep 17 00:00:00 2001 From: Perchik71 Date: Wed, 21 Oct 2020 12:43:31 +0300 Subject: [PATCH 010/257] fixed RemoveMenu fixed RemoveMenu --- fallout4_test/src/patches/CKF4/UIMenus.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fallout4_test/src/patches/CKF4/UIMenus.cpp b/fallout4_test/src/patches/CKF4/UIMenus.cpp index e1a08bf3..ff32f850 100644 --- a/fallout4_test/src/patches/CKF4/UIMenus.cpp +++ b/fallout4_test/src/patches/CKF4/UIMenus.cpp @@ -129,7 +129,7 @@ namespace Core void CUIMenuItem::Remove(CUIMenuItem* MenuItem) { Assert(MenuItem && IsMenu(MenuItem->Menu()->Handle)); - DeleteMenu(MenuItem->Menu()->Handle, (UINT)MenuItem->ID, MenuItem->ByPosition()); + DeleteMenu(MenuItem->Menu()->Handle, MenuItem->ID, MenuItem->ByPosition() ? MF_BYPOSITION : MF_BYCOMMAND); } void CUIMenuItem::Remove(CUIMenuItem& MenuItem) @@ -163,13 +163,13 @@ namespace Core void CUIMenu::Remove(const UINT MenuID) { Assert(IsMenu(m_Handle)); - DeleteMenu(m_Handle, MenuID, FALSE); + DeleteMenu(m_Handle, MenuID, MF_BYCOMMAND); } void CUIMenu::RemoveByPos(const UINT Position) { Assert(IsMenu(m_Handle)); - DeleteMenu(m_Handle, Position, TRUE); + DeleteMenu(m_Handle, Position, MF_BYPOSITION); } CUIMenuItem CUIMenu::GetItem(const UINT MenuID) From 7bcbe821e3565d00db588725acec260ddadc4a2a Mon Sep 17 00:00:00 2001 From: Perchik71 Date: Wed, 21 Oct 2020 12:52:51 +0300 Subject: [PATCH 011/257] fixed Close Lg Window didn't uncheck the box to Main Menu fixed Close Lg Window didn't uncheck the box to Main Menu --- fallout4_test/src/patches/CKF4/EditorUI.cpp | 24 ++++++++++++++++---- fallout4_test/src/patches/CKF4/EditorUI.h | 8 +++++++ fallout4_test/src/patches/CKF4/LogWindow.cpp | 1 + 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/fallout4_test/src/patches/CKF4/EditorUI.cpp b/fallout4_test/src/patches/CKF4/EditorUI.cpp index 0e405671..18583b72 100644 --- a/fallout4_test/src/patches/CKF4/EditorUI.cpp +++ b/fallout4_test/src/patches/CKF4/EditorUI.cpp @@ -7,10 +7,6 @@ #include "LogWindow.h" #include "TESForm_CK.h" -#include "UIMenus.h" -#include "UIBaseWindow.h" -#include "UICheckboxControl.h" - #pragma comment(lib, "comctl32.lib") #define UI_CUSTOM_MESSAGE 52000 @@ -86,6 +82,26 @@ namespace EditorUI return CellViewWindow.Handle; } + Core::Classes::UI::CUICustomWindow& GetWindowObj() + { + return MainWindow; + } + + Core::Classes::UI::CUICustomWindow& GetObjectWindowObj() + { + return ObjectWindow; + } + + Core::Classes::UI::CUICustomWindow& GetCellViewWindowObj() + { + return CellViewWindow; + } + + Core::Classes::UI::CUIMenu* GetMainMenuObj() + { + return MainMenu; + } + LRESULT WINAPI hk_0x5669D8(void) { ObjectWindow.Perform(WM_COMMAND, UI_CMD_CHANGE_SPLITTER_OBJECTWINDOW, 0); diff --git a/fallout4_test/src/patches/CKF4/EditorUI.h b/fallout4_test/src/patches/CKF4/EditorUI.h index 05d8c8d7..7c5bd9db 100644 --- a/fallout4_test/src/patches/CKF4/EditorUI.h +++ b/fallout4_test/src/patches/CKF4/EditorUI.h @@ -3,6 +3,10 @@ #include "../../common.h" #include "TESForm_CK.h" +#include "UIMenus.h" +#include "UIBaseWindow.h" +#include "UICheckboxControl.h" + #define UI_EDITOR_TOOLBAR 1 #define UI_EDITOR_OPENFORMBYID 52001 // Sent from the LogWindow on double click @@ -37,6 +41,10 @@ namespace EditorUI HWND GetWindow(); HWND GetObjectWindow(); HWND GetCellViewWindow(); + Core::Classes::UI::CUICustomWindow& GetWindowObj(); + Core::Classes::UI::CUICustomWindow& GetObjectWindowObj(); + Core::Classes::UI::CUICustomWindow& GetCellViewWindowObj(); + Core::Classes::UI::CUIMenu* GetMainMenuObj(); void Initialize(); //bool CreateExtensionMenu(HWND MainWindow, HMENU MainMenu); diff --git a/fallout4_test/src/patches/CKF4/LogWindow.cpp b/fallout4_test/src/patches/CKF4/LogWindow.cpp index 9bd48051..db2ff40f 100644 --- a/fallout4_test/src/patches/CKF4/LogWindow.cpp +++ b/fallout4_test/src/patches/CKF4/LogWindow.cpp @@ -240,6 +240,7 @@ namespace LogWindow case WM_CLOSE: ShowWindow(Hwnd, SW_HIDE); + EditorUI::GetMainMenuObj()->GetItem(UI_EXTMENU_SHOWLOG).Checked = FALSE; return 0; case WM_NOTIFY: From 1f221b7d8e6750aa6e55513a2419f41e784234a3 Mon Sep 17 00:00:00 2001 From: Perchik71 Date: Wed, 21 Oct 2020 14:08:41 +0300 Subject: [PATCH 012/257] for_each threads for_each threads --- .gitignore | 4 + Dependencies/libdeflate.vcxproj | 1 + fallout4_test/fallout4_test.vcxproj | 4 +- fallout4_test/fallout4_test.vcxproj.filters | 31 +- fallout4_test/src/common.h | 2 + fallout4_test/src/patches/CKF4/EditorUI.cpp | 20 +- .../src/patches/CKF4/ExperimentalNuukem.cpp | 282 ++++++++++++++++++ .../src/patches/CKF4/ExperimentalNuukem.h | 6 + fallout4_test/src/patches/patches_f4ck.cpp | 6 + fallout4_test/src/xutil.h | 65 ++++ 10 files changed, 395 insertions(+), 26 deletions(-) create mode 100644 fallout4_test/src/patches/CKF4/ExperimentalNuukem.cpp create mode 100644 fallout4_test/src/patches/CKF4/ExperimentalNuukem.h diff --git a/.gitignore b/.gitignore index 79658557..2f0a5439 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,7 @@ x86/ fallout4_test/src/version_info.h fallout4_test/VTune Amplifier Results/ +fallout4_test/fallout4_test.vcxproj.user +fallout4_test/resource.aps +Dependencies/libdeflate.vcxproj.user +Dependencies/libdeflate.vcxproj diff --git a/Dependencies/libdeflate.vcxproj b/Dependencies/libdeflate.vcxproj index 60e8fbb7..aa4b3d88 100644 --- a/Dependencies/libdeflate.vcxproj +++ b/Dependencies/libdeflate.vcxproj @@ -117,6 +117,7 @@ NDEBUG;_LIB;%(PreprocessorDefinitions) true /D "NOMINMAX" %(AdditionalOptions) + 4244;4267;4146;4018;%(DisableSpecificWarnings) Windows diff --git a/fallout4_test/fallout4_test.vcxproj b/fallout4_test/fallout4_test.vcxproj index 22703021..39ffb83f 100644 --- a/fallout4_test/fallout4_test.vcxproj +++ b/fallout4_test/fallout4_test.vcxproj @@ -65,7 +65,7 @@ false - $(SolutionDir)Dependencies\detours;$(SolutionDir)Dependencies\zydis\msvc\zydis;$(SolutionDir)Dependencies\zydis\include;$(SolutionDir)Dependencies\tbb\include;$(SolutionDir)Dependencies;E:\Program Files %28x86%29\IntelSWTools\VTune Amplifier 2019\include;$(IncludePath) + $(WindowsSDK_IncludePath);$(VC_IncludePath);$(SolutionDir)Dependencies\detours;$(SolutionDir)Dependencies\zydis\msvc\zydis;$(SolutionDir)Dependencies\zydis\include;$(SolutionDir)Dependencies\tbb\include;$(SolutionDir)Dependencies;E:\Program Files %28x86%29\IntelSWTools\VTune Amplifier 2019\include;$(IncludePath) $(OutDir);E:\Program Files %28x86%29\IntelSWTools\VTune Amplifier 2019\lib64;$(LibraryPath) winhttp $(SolutionDir)x64\$(ProjectName)\ @@ -167,6 +167,7 @@ + @@ -222,6 +223,7 @@ + diff --git a/fallout4_test/fallout4_test.vcxproj.filters b/fallout4_test/fallout4_test.vcxproj.filters index 200da2a2..24929918 100644 --- a/fallout4_test/fallout4_test.vcxproj.filters +++ b/fallout4_test/fallout4_test.vcxproj.filters @@ -9,9 +9,6 @@ {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;xsd - - {6ecca651-f6ac-4430-b4b5-a9d5fb31e2fc} - {35ec8d65-0314-4ba5-b3b8-c4b0d305fd93} @@ -30,11 +27,14 @@ {8ac21372-4a9a-4e6b-ac6d-0248039f9a3c} + + {ae770148-f22f-4c77-8ee3-fb61014bba30} + + + {6ecca651-f6ac-4430-b4b5-a9d5fb31e2fc} + - - Header Files - Header Files @@ -44,9 +44,6 @@ Header Files - - Header Files - Header Files @@ -200,13 +197,22 @@ Header Files\UI + + Header Files\Patches + + + Header Files\Patches + + + Header Files\Patches + Source Files - Source Files\patches + Source Files\Patches Source Files @@ -215,7 +221,7 @@ Source Files - Source Files\patches + Source Files\Patches Source Files @@ -283,6 +289,9 @@ Source Files\UI + + Source Files\Patches + diff --git a/fallout4_test/src/common.h b/fallout4_test/src/common.h index f01b42df..cdc533a0 100644 --- a/fallout4_test/src/common.h +++ b/fallout4_test/src/common.h @@ -4,6 +4,8 @@ #include #include +#include +#include #include #include #include diff --git a/fallout4_test/src/patches/CKF4/EditorUI.cpp b/fallout4_test/src/patches/CKF4/EditorUI.cpp index 18583b72..30d5dd93 100644 --- a/fallout4_test/src/patches/CKF4/EditorUI.cpp +++ b/fallout4_test/src/patches/CKF4/EditorUI.cpp @@ -425,13 +425,9 @@ namespace EditorUI case UI_CMD_SHOWHIDE_OBJECTWINDOW: { - if (IsWindowVisible(GetObjectWindow())) - ShowWindow(GetObjectWindow(), SW_HIDE); - else - { - ShowWindow(GetObjectWindow(), SW_SHOW); - SetForegroundWindow(GetObjectWindow()); - } + ObjectWindow.Visible = !ObjectWindow.Visible; + if (ObjectWindow.Visible) + ObjectWindow.Foreground(); // Change the checkbox MenuItem = MainMenu->GetItem(UI_CMD_SHOWHIDE_OBJECTWINDOW); @@ -441,13 +437,9 @@ namespace EditorUI case UI_CMD_SHOWHIDE_CELLVIEWWINDOW: { - if (IsWindowVisible(GetCellViewWindow())) - ShowWindow(GetCellViewWindow(), SW_HIDE); - else - { - ShowWindow(GetCellViewWindow(), SW_SHOW); - SetForegroundWindow(GetCellViewWindow()); - } + CellViewWindow.Visible = !CellViewWindow.Visible; + if (CellViewWindow.Visible) + CellViewWindow.Foreground(); // Change the checkbox MenuItem = MainMenu->GetItem(UI_CMD_SHOWHIDE_CELLVIEWWINDOW); diff --git a/fallout4_test/src/patches/CKF4/ExperimentalNuukem.cpp b/fallout4_test/src/patches/CKF4/ExperimentalNuukem.cpp new file mode 100644 index 00000000..88feb13f --- /dev/null +++ b/fallout4_test/src/patches/CKF4/ExperimentalNuukem.cpp @@ -0,0 +1,282 @@ +#include "..\..\Common.h" +#include "ExperimentalNuukem.h" +#include "LogWindow.h" + +#include + +#include +#include +#include + +namespace Experimental +{ + struct __addr_t + { + uintptr_t Based; + uintptr_t End; + }; + + struct range_t + { + __addr_t addr; + DWORD protection; + }; + + namespace Nuukem + { + struct NullsubPatch + { + std::initializer_list Signature; + uint8_t JumpPatch[5]; + uint8_t CallPatch[5]; + }; + + const NullsubPatch Patches[] = + { + // Nullsub || retn; int3; int3; int3; int3; || nop; + { { 0xC2, 0x00, 0x00 },{ 0xC3, 0xCC, 0xCC, 0xCC, 0xCC },{ 0x0F, 0x1F, 0x44, 0x00, 0x00 } }, + { { 0xC3 },{ 0xC3, 0xCC, 0xCC, 0xCC, 0xCC },{ 0x0F, 0x1F, 0x44, 0x00, 0x00 } }, + { { 0x48, 0x89, 0x4C, 0x24, 0x08, 0xC3 },{ 0xC3, 0xCC, 0xCC, 0xCC, 0xCC },{ 0x0F, 0x1F, 0x44, 0x00, 0x00 } }, + { { 0x48, 0x89, 0x54, 0x24, 0x10, 0x48, 0x89, 0x4C, 0x24, 0x08, 0xC3 },{ 0xC3, 0xCC, 0xCC, 0xCC, 0xCC },{ 0x0F, 0x1F, 0x44, 0x00, 0x00 } }, + { { 0x48, 0x89, 0x4C, 0x24, 0x08, 0x48, 0x83, 0xEC, 0x28, 0x48, 0x8B, 0x4C, 0x24, 0x30, 0x0F, 0x1F, 0x44, 0x00, 0x00, 0x48, 0x83, 0xC4, 0x28, 0xC3 },{ 0xC3, 0xCC, 0xCC, 0xCC, 0xCC },{ 0x0F, 0x1F, 0x44, 0x00, 0x00 } }, + + { + { 0x48, 0x89, 0x4C, 0x24, 0x08, 0x48, 0x8B, 0x44, 0x24, 0x08, 0xC3 }, // return this; + { 0x48, 0x89, 0xC8, 0xC3, 0xCC }, // mov rax, rcx; retn; int3; + { 0x48, 0x89, 0xC8, 0x66, 0x90 } // mov rax, rcx; nop; + }, + + { + { 0x48, 0x89, 0x4C, 0x24, 0x08, 0x48, 0x8B, 0x44, 0x24, 0x08, 0x48, 0x8B, 0x00, 0xC3 }, // return *(__int64 *)this; + { 0x48, 0x8B, 0x01, 0xC3, 0xCC }, // mov rax, [rcx]; retn; int3; + { 0x48, 0x8B, 0x01, 0x66, 0x90 } // mov rax, [rcx]; nop; + }, + + { + { 0x48, 0x89, 0x4C, 0x24, 0x08, 0x48, 0x8B, 0x44, 0x24, 0x08, 0x48, 0x8B, 0x40, 0x08, 0xC3 }, // return *(__int64 *)(this + 0x8); + { 0x48, 0x8B, 0x41, 0x08, 0xC3 }, // mov rax, [rcx + 0x8]; retn; + { 0x48, 0x8B, 0x41, 0x08, 0x90 } // mov rax, [rcx + 0x8]; nop; + }, + + { + { 0x48, 0x89, 0x4C, 0x24, 0x08, 0x48, 0x8B, 0x44, 0x24, 0x08, 0x48, 0x8B, 0x40, 0x50, 0xC3 }, // return *(__int64 *)(this + 0x50); + { 0x48, 0x8B, 0x41, 0x50, 0xC3 }, // mov rax, [rcx + 0x50]; retn; + { 0x48, 0x8B, 0x41, 0x50, 0x90 } // mov rax, [rcx + 0x50]; nop; + }, + + { + { 0x48, 0x89, 0x4C, 0x24, 0x08, 0x48, 0x8B, 0x44, 0x24, 0x08, 0x8B, 0x00, 0xC3 }, // return *(__int32 *)this; + { 0x8B, 0x01, 0xC3, 0xCC, 0xCC }, // mov eax, [rcx]; retn; int3; int3; + { 0x8B, 0x01, 0x0F, 0x1F, 0x00 } // mov eax, [rcx]; nop; + }, + + { + { 0x48, 0x89, 0x4C, 0x24, 0x08, 0x48, 0x8B, 0x44, 0x24, 0x08, 0x8B, 0x40, 0x08, 0xC3 }, // return *(__int32 *)(this + 0x8); + { 0x8B, 0x41, 0x08, 0xC3, 0xCC }, // mov eax, [rcx + 0x8]; retn; int3; + { 0x8B, 0x41, 0x08, 0x66, 0x90 } // mov eax, [rcx + 0x8]; nop; + }, + + { + { 0x48, 0x89, 0x4C, 0x24, 0x08, 0x48, 0x8B, 0x44, 0x24, 0x08, 0x8B, 0x40, 0x14, 0xC3 }, // return *(__int32 *)(this + 0x14); + { 0x8B, 0x41, 0x14, 0xC3, 0xCC }, // mov eax, [rcx + 0x14]; retn; int3; + { 0x8B, 0x41, 0x14, 0x66, 0x90 } // mov eax, [rcx + 0x14]; nop; + }, + + { + { 0x48, 0x89, 0x4C, 0x24, 0x08, 0x48, 0x8B, 0x44, 0x24, 0x08, 0x0F, 0xB6, 0x40, 0x08, 0xC3 }, // return ZERO_EXTEND(*(__int8 *)(this + 0x8)); + { 0x0F, 0xB6, 0x41, 0x08, 0xC3 }, // movzx eax, [rcx + 0x8]; retn; + { 0x0F, 0xB6, 0x41, 0x08, 0x90 } // movzx eax, [rcx + 0x8]; nop; + }, + + { + { 0x48, 0x89, 0x4C, 0x24, 0x08, 0x48, 0x8B, 0x44, 0x24, 0x08, 0x0F, 0xB6, 0x40, 0x26, 0xC3 }, // return ZERO_EXTEND(*(__int8 *)(this + 0x26)); + { 0x0F, 0xB6, 0x41, 0x26, 0xC3 }, // movzx eax, [rcx + 0x26]; retn; + { 0x0F, 0xB6, 0x41, 0x26, 0x90 } // movzx eax, [rcx + 0x26]; nop; + }, + + { + { 0x89, 0x54, 0x24, 0x10, 0x48, 0x89, 0x4C, 0x24, 0x08, 0x8B, 0x44, 0x24, 0x10, 0x48, 0x8B, 0x4C, 0x24, 0x08, 0x0F, 0xB7, 0x04, 0x41, 0xC3 }, // return ZERO_EXTEND(*(unsigned __int16 *)(a1 + 2i64 * a2)); + { 0x0F, 0xB7, 0x04, 0x51, 0xC3 }, // movzx eax, word ptr ds:[rcx+rdx*2]; retn; + { 0x0F, 0xB7, 0x04, 0x51, 0x90 } // movzx eax, word ptr ds:[rcx+rdx*2]; nop; + }, + }; + + const NullsubPatch *FindNullsubPatch(uintptr_t SourceAddress, uintptr_t TargetFunction) + { + for (auto& patch : Patches) + { + if (!memcmp((void *)TargetFunction, patch.Signature.begin(), patch.Signature.size())) + return &patch; + } + + return nullptr; + } + + bool PatchNullsub(uintptr_t SourceAddress, uintptr_t TargetFunction, const NullsubPatch *Patch) + { + const bool isJump = *(uint8_t *)SourceAddress == 0xE9; + + // Check if the given function is "unoptimized" and remove the branch completely + if (!Patch) + Patch = FindNullsubPatch(SourceAddress, TargetFunction); + + if (Patch) + { + if (isJump) + memcpy((void *)SourceAddress, Patch->JumpPatch, 5); + else + memcpy((void *)SourceAddress, Patch->CallPatch, 5); + + return true; + } + + return false; + } + + uint64_t patch_edit_and_continue(const range_t *ranges) + { + // + // Remove any references to the giant trampoline table generated for edit & continue + // + // Before: [Function call] -> [E&C trampoline] -> [Function] + // After: [Function call] -> [Function] + // + tbb::concurrent_vector> nullsubTargets; + tbb::concurrent_vector branchTargets; + + // Enumerate all functions present in the x64 exception directory section + auto ntHeaders = (PIMAGE_NT_HEADERS64)(g_ModuleBase + ((PIMAGE_DOS_HEADER)g_ModuleBase)->e_lfanew); + const auto sectionRVA = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress; + const auto sectionSize = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size; + + Assert(sectionRVA > 0 && sectionSize > 0); + + auto functionEntries = (PRUNTIME_FUNCTION)(g_ModuleBase + sectionRVA); + auto functionEntryCount = sectionSize / sizeof(RUNTIME_FUNCTION); + + // Init threadsafe instruction decoder + ZydisDecoder decoder; + Assert(ZYDIS_SUCCESS(ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64))); + + XUtil::Parallel::for_each(&functionEntries[0], &functionEntries[functionEntryCount], + [&branchTargets, &nullsubTargets, &decoder](const RUNTIME_FUNCTION& Function) + { + const uintptr_t ecTableStart = OFFSET(0x1005, 0); + const uintptr_t ecTableEnd = OFFSET(0x86994, 0); + + for (uint32_t offset = Function.BeginAddress; offset < Function.EndAddress;) + { + const uintptr_t ip = g_ModuleBase + offset; + const uint8_t opcode = *(uint8_t *)ip; + ZydisDecodedInstruction instruction; + + if (!ZYDIS_SUCCESS(ZydisDecoderDecodeBuffer(&decoder, (void *)ip, ZYDIS_MAX_INSTRUCTION_LENGTH, ip, &instruction))) + { + // Decode failed. Always increase byte offset by 1. + offset += 1; + continue; + } + + offset += instruction.length; + + // Must be a call or a jump + if (opcode != 0xE9 && opcode != 0xE8) + continue; + + uintptr_t destination = ip + *(int32_t *)(ip + 1) + 5; + + // if (destination is within E&C table) + if (destination >= ecTableStart && destination < ecTableEnd && *(uint8_t *)destination == 0xE9) + { + // Determine where the E&C trampoline jumps to, then remove it. Each function is processed separately so thread + // safety is not an issue when patching. The 0xE9 opcode never changes. + uintptr_t real = destination + *(int32_t *)(destination + 1) + 5; + + int32_t disp = (int32_t)(real - ip) - 5; + memcpy((void *)(ip + 1), &disp, sizeof(disp)); + + if (auto patch = FindNullsubPatch(ip, real)) + nullsubTargets.emplace_back(ip, patch); + else + branchTargets.emplace_back(ip); + } + } + }); + + uint64_t patchCount = nullsubTargets.size() + branchTargets.size(); + + for (auto patch : nullsubTargets) + { + + uintptr_t destination = patch.first + *(int32_t *)(patch.first + 1) + 5; + + if (PatchNullsub(patch.first, destination, patch.second)) + patchCount++; + } + + // Secondary pass to remove nullsubs missed or created above + for (uintptr_t ip : branchTargets) + { + uintptr_t destination = ip + *(int32_t *)(ip + 1) + 5; + + if (PatchNullsub(ip, destination, nullptr)) + patchCount++; + } + + return patchCount; + } + + uint64_t patch_mem_init(const range_t *ranges) + { + // + // Remove the thousands of [code below] since they're useless checks: + // + // if ( dword_141ED6C88 != 2 ) // MemoryManager initialized flag + // sub_140C00D30((__int64)&unk_141ED6800, &dword_141ED6C88); + // + std::vector matches = XUtil::FindPatterns(ranges[0].addr.Based, ranges[0].addr.End - ranges[0].addr.Based, + "83 3D ? ? ? ? 02 74 13 48 8D 15 ? ? ? ? 48 8D 0D ? ? ? ? E8"); + + auto func = [](auto it) { memcpy((void *)it, "\xEB\x1A", 2); }; + + std::vector::iterator match; + XUtil::Parallel::for_each(match = matches.begin(), matches.end(), func); + + return matches.size(); + } + } + + void __stdcall RunOptimizations(void) + { + using namespace std::chrono; + auto timerStart = high_resolution_clock::now(); + + range_t ranges[] = { + { { g_CodeBase, g_CodeEnd }, 0 }, // .text and .bsstext + { { g_RdataBase, g_RdataEnd }, 0 }, // .rdata + { { g_DataBase, g_DataEnd }, 0 } // .data + }; + + // Mark every page as writable + for (auto& range : ranges) + Assert(VirtualProtect((void *)range.addr.Based, range.addr.End - range.addr.Based, PAGE_READWRITE, &range.protection)); + + std::array counts + { + Nuukem::patch_mem_init(ranges), + Nuukem::patch_edit_and_continue(ranges), + }; + + // Then restore the old permissions + for (auto& range : ranges) + { + Assert(VirtualProtect((void *)range.addr.Based, range.addr.End - range.addr.Based, range.protection, &range.protection)); + Assert(FlushInstructionCache(GetCurrentProcess(), (void *)range.addr.Based, range.addr.End - range.addr.Based)); + } + + auto duration = duration_cast(high_resolution_clock::now() - timerStart).count(); + + LogWindow::Log("%s: (%llu + %llu) = %llu patches applied in %llums.\n", __FUNCTION__, + counts[0], counts[1], counts[0] + counts[1], duration); + } +} \ No newline at end of file diff --git a/fallout4_test/src/patches/CKF4/ExperimentalNuukem.h b/fallout4_test/src/patches/CKF4/ExperimentalNuukem.h new file mode 100644 index 00000000..5d842204 --- /dev/null +++ b/fallout4_test/src/patches/CKF4/ExperimentalNuukem.h @@ -0,0 +1,6 @@ +#pragma once + +namespace Experimental +{ + void __stdcall RunOptimizations(void); +} \ No newline at end of file diff --git a/fallout4_test/src/patches/patches_f4ck.cpp b/fallout4_test/src/patches/patches_f4ck.cpp index bf362066..c35a7a5f 100644 --- a/fallout4_test/src/patches/patches_f4ck.cpp +++ b/fallout4_test/src/patches/patches_f4ck.cpp @@ -7,6 +7,7 @@ #include "CKF4/EditorUI.h" #include "CKF4/EditorUIDarkMode.h" #include "CKF4/LogWindow.h" +#include "CKF4/ExperimentalNuukem.h" void PatchMemory(); void PatchFileIO(); @@ -215,4 +216,9 @@ void Patch_Fallout4CreationKit() XUtil::DetourCall(OFFSET(0x08056B7, 0), &hk_inflateInit); XUtil::DetourCall(OFFSET(0x08056F7, 0), &hk_inflate); + + // Experimental. Must be run last to avoid interfering with other hooks and patches. + // Optimization that nuukem did in CK SSE + // https://github.com/Nukem9/SkyrimSETest/blob/master/skyrim64_test/src/patches/CKSSE/Experimental.cpp + Experimental::RunOptimizations(); } \ No newline at end of file diff --git a/fallout4_test/src/xutil.h b/fallout4_test/src/xutil.h index 294c5242..97851826 100644 --- a/fallout4_test/src/xutil.h +++ b/fallout4_test/src/xutil.h @@ -2,6 +2,8 @@ #pragma warning(disable:4094) // untagged 'struct' declared no symbols +#include + #define Assert(Cond) if(!(Cond)) XUtil::XAssert(__FILE__, __LINE__, #Cond); #define AssertDebug(Cond) if(!(Cond)) XUtil::XAssert(__FILE__, __LINE__, #Cond); #define AssertMsg(Cond, Msg) AssertMsgVa(Cond, Msg); @@ -84,6 +86,69 @@ struct __declspec(empty_bases)CheckOffset namespace XUtil { + namespace Parallel + { + // https://stackoverflow.com/questions/40805197/parallel-for-each-more-than-two-times-slower-than-stdfor-each + // fast than tbb::parallel_for_each + + class join_threads + { + public: + explicit join_threads(std::vector& threads) + : threads_(threads) {} + + ~join_threads() + { + for (size_t i = 0; i < threads_.size(); ++i) + { + if (threads_[i].joinable()) + { + threads_[i].join(); + } + } + } + + private: + std::vector& threads_; + }; + + template + void for_each(Iterator first, Iterator last, Func func) + { + const auto length = std::distance(first, last); + if (0 == length) return; + + const auto min_per_thread = 25u; + const unsigned max_threads = (length + min_per_thread - 1) / min_per_thread; + const auto hardware_threads = std::thread::hardware_concurrency(); + const auto num_threads = std::min(hardware_threads != 0 ? hardware_threads : 2u, max_threads); + const auto block_size = length / num_threads; + + std::vector> futures(num_threads - 1); + std::vector threads(num_threads - 1); + join_threads joiner(threads); + + auto block_start = first; + for (unsigned i = 0; i < num_threads - 1; ++i) + { + auto block_end = block_start; + std::advance(block_end, block_size); + std::packaged_task task([block_start, block_end, func]() + { + std::for_each(block_start, block_end, func); + }); + futures[i] = task.get_future(); + threads[i] = std::thread(std::move(task)); + block_start = block_end; + } + + std::for_each(block_start, last, func); + + for (size_t i = 0; i < num_threads - 1; ++i) + futures[i].get(); + } + } + namespace Str { // convert string to upper case From 90cec839997c8d37665d1c4569899b6fec98c17d Mon Sep 17 00:00:00 2001 From: Perchik71 Date: Thu, 22 Oct 2020 06:08:12 +0300 Subject: [PATCH 013/257] trim --- fallout4_test/src/xutil.cpp | 41 +++++++++++++++++++++++++++++++++++++ fallout4_test/src/xutil.h | 39 +++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/fallout4_test/src/xutil.cpp b/fallout4_test/src/xutil.cpp index b4133248..e8afee53 100644 --- a/fallout4_test/src/xutil.cpp +++ b/fallout4_test/src/xutil.cpp @@ -1,5 +1,46 @@ #include "common.h" +#include + +std::string __stdcall XUtil::Str::GetLastErrorToStr(DWORD err, const std::string& namefunc) +{ + // Retrieve the system error message for the last-error code + + std::string str; + LPVOID lpMsgBuf; + LPVOID lpDisplayBuf; + + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR)&lpMsgBuf, + 0, NULL); + + // Display the error message and exit the process + + lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, + (lstrlenA((LPCSTR)lpMsgBuf) + lstrlenA(namefunc.c_str()) + 40) * sizeof(CHAR)); + StringCchPrintfA((LPSTR)lpDisplayBuf, + LocalSize(lpDisplayBuf) / sizeof(CHAR), + "%s failed with error %d: %s", + namefunc.c_str(), err, lpMsgBuf); + str = (LPSTR)lpDisplayBuf; + + LocalFree(lpMsgBuf); + LocalFree(lpDisplayBuf); + + return str; +} + +std::string __stdcall XUtil::Str::GetLastErrorToStr(const std::string& namefunc) +{ + return GetLastErrorToStr(GetLastError(), namefunc); +} + VtableIndexUtil *VtableIndexUtil::GlobalInstance; VtableIndexUtil *VtableIndexUtil::Instance() diff --git a/fallout4_test/src/xutil.h b/fallout4_test/src/xutil.h index 97851826..4c341849 100644 --- a/fallout4_test/src/xutil.h +++ b/fallout4_test/src/xutil.h @@ -2,6 +2,10 @@ #pragma warning(disable:4094) // untagged 'struct' declared no symbols +#include +#include +#include +#include #include #define Assert(Cond) if(!(Cond)) XUtil::XAssert(__FILE__, __LINE__, #Cond); @@ -151,6 +155,37 @@ namespace XUtil namespace Str { + // trim https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring + + // trim from start (in place) + static inline std::string ltrim(std::string& s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); })); + return s; + } + + static inline std::string ltrim(const std::string& s) { + return ltrim(const_cast(s)); + } + + // trim from end (in place) + static inline std::string rtrim(std::string& s) { + s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end()); + return s; + } + + static inline std::string rtrim(const std::string& s) { + return rtrim(const_cast(s)); + } + + // trim from both ends + static inline std::string& trim(std::string& s) { + return ltrim(rtrim(s)); + } + + static inline std::string trim(const std::string& s) { + return trim(const_cast(s)); + } + // convert string to upper case static inline std::string& UpperCase(std::string& s) { std::for_each(s.begin(), s.end(), [](char& c) { @@ -166,6 +201,10 @@ namespace XUtil }); return s; } + + // https://docs.microsoft.com/en-us/windows/win32/debug/retrieving-the-last-error-code + std::string __stdcall GetLastErrorToStr(DWORD err, const std::string& namefunc); + std::string __stdcall GetLastErrorToStr(const std::string& namefunc); } void SetThreadName(uint32_t ThreadID, const char *ThreadName); From cb9afa4a9622da8bdea87df290064129834304fd Mon Sep 17 00:00:00 2001 From: Perchik71 Date: Thu, 22 Oct 2020 06:08:56 +0300 Subject: [PATCH 014/257] Unnecessary code --- fallout4_test/src/patches/CKF4/EditorUI.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/fallout4_test/src/patches/CKF4/EditorUI.cpp b/fallout4_test/src/patches/CKF4/EditorUI.cpp index 30d5dd93..5ff7bd84 100644 --- a/fallout4_test/src/patches/CKF4/EditorUI.cpp +++ b/fallout4_test/src/patches/CKF4/EditorUI.cpp @@ -546,7 +546,6 @@ namespace EditorUI // Include filter "Active Only" Core::Classes::UI::CRECT bounds = ObjectWindowControls.EditFilter.BoundsRect; ObjectWindowControls.ActiveOnly.CreateWnd(ObjectWindow, "Active Only *", bounds.Left, ObjectWindowControls.TreeList.Top, bounds.Width, 14, UI_OBJECT_WINDOW_ADD_ITEM); - ObjectWindowControls.ActiveOnly.Visible = TRUE; ObjectWindowControls.TreeList.Top += 17; ObjectWindowControls.BtnObjLayout.Top = ObjectWindowControls.ComboLayout.Top - 1; From 5e9f02c849acf76c1ee3c32b59af8a1b1eb633a6 Mon Sep 17 00:00:00 2001 From: Perchik71 Date: Thu, 22 Oct 2020 06:09:12 +0300 Subject: [PATCH 015/257] Update fallout4_test.vcxproj --- fallout4_test/fallout4_test.vcxproj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fallout4_test/fallout4_test.vcxproj b/fallout4_test/fallout4_test.vcxproj index 39ffb83f..9d4f71ed 100644 --- a/fallout4_test/fallout4_test.vcxproj +++ b/fallout4_test/fallout4_test.vcxproj @@ -168,16 +168,15 @@ + - - @@ -217,6 +216,7 @@ + @@ -224,6 +224,7 @@ + @@ -232,6 +233,7 @@ + From c61bf05bb5aaa9de191b6c0cddefee8eab4f1644 Mon Sep 17 00:00:00 2001 From: Perchik71 Date: Thu, 22 Oct 2020 06:09:15 +0300 Subject: [PATCH 016/257] Update fallout4_test.vcxproj.filters --- fallout4_test/fallout4_test.vcxproj.filters | 150 ++++++++++++-------- 1 file changed, 90 insertions(+), 60 deletions(-) diff --git a/fallout4_test/fallout4_test.vcxproj.filters b/fallout4_test/fallout4_test.vcxproj.filters index 24929918..2c6cabe2 100644 --- a/fallout4_test/fallout4_test.vcxproj.filters +++ b/fallout4_test/fallout4_test.vcxproj.filters @@ -33,6 +33,30 @@ {6ecca651-f6ac-4430-b4b5-a9d5fb31e2fc} + + {a7766be7-54b7-4d4d-8e6b-5e4819048994} + + + {7a39de88-cf1e-4c4d-9288-3967c45c9d73} + + + {b7db8a77-7b3c-45db-b7ed-adbc6fbea900} + + + {895435af-7cd1-4201-a693-09007da7f43b} + + + {4f2e0375-dc16-4a4b-943c-bb12aafa811a} + + + {bc1302bb-1063-4a3a-89a4-53364001bd4f} + + + {9a61be2f-a22e-47e3-a8dc-97bb27863811} + + + {cd26e7a2-a163-4aa0-923d-f4759c8c3eac} + @@ -41,66 +65,27 @@ Header Files - - Header Files - Header Files - - Header Files - - - Header Files - Header Files - - Header Files - Header Files - - Header Files - - - Header Files - - - Header Files - - - Header Files - Header Files - - Header Files - - - Header Files - Header Files - - Header Files - Header Files Header Files - - Header Files - - - Header Files - Header Files\RTTI\NiRTTI @@ -188,23 +173,62 @@ Header Files\UI + + Header Files\Patches + + + Header Files\Patches + + + Header Files\Classes + + + Header Files\Classes + + + Header Files\Classes + + + Header Files\Classes + + + Header Files\Classes + + + Header Files\Classes + + + Header Files\Classes + + + Header Files\Classes + + + Header Files\Classes + + + Header Files\UI\Classes + - Header Files\UI + Header Files\UI\Classes - Header Files\UI + Header Files\UI\Classes - - Header Files\UI + + Header Files\Utils - - Header Files\Patches + + Header Files\Utils - - Header Files\Patches + + Header Files\Utils - - Header Files\Patches + + Header Files\Patches\CK + + + Header Files\Patches\CK @@ -217,9 +241,6 @@ Source Files - - Source Files - Source Files\Patches @@ -247,9 +268,6 @@ Source Files - - Source Files - Source Files\RTTI\NiRTTI @@ -280,17 +298,29 @@ Source Files\UI + + Source Files\Classes + + + Source Files\UI\Classes + - Source Files\UI + Source Files\UI\Classes - Source Files\UI + Source Files\UI\Classes - - Source Files\UI + + Source Files\Utils + + + Source Files\Utils - Source Files\Patches + Source Files\Patches\CK + + + Source Files\Patches\CK From 2214bab47ff21847ea02e0d1f4c2d12190100ac8 Mon Sep 17 00:00:00 2001 From: Perchik71 Date: Thu, 22 Oct 2020 06:09:44 +0300 Subject: [PATCH 017/257] Add option Experimental::Unicode --- fallout4_test.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fallout4_test.ini b/fallout4_test.ini index c53b5021..fd2c906a 100644 --- a/fallout4_test.ini +++ b/fallout4_test.ini @@ -20,6 +20,9 @@ RenderWindowUnlockedFPS=false ; Unlock the framerate in the Render Window. DisableWindowGhosting=false ; Disable "Not Responding" overlay while performing certain tasks WarningBlacklist=false ; In the log, hide specific warnings or errors produced by the vanilla game/DLC ESM files. See [CreationKit_Warnings] section. +[Experimental] +Unicode=true ; Convert Utf-8 to WinCP when loading and back when saving + [CreationKit_Log] Width=1024 ; Initial log window width Height=480 ; Initial log window height From 2d7436148cbba67c6441f58821f5c76458425edd Mon Sep 17 00:00:00 2001 From: Perchik71 Date: Thu, 22 Oct 2020 06:13:45 +0300 Subject: [PATCH 018/257] Unicode lib on FreePascal Since the game contains partial localization, then Ansi, then Utf-8. Standard tools can't cope with the definition of all available Ansi Code page. The guys from Lazarus solved this problem, and I decided to implement their solution in the project. --- .../x86_64/UnicodePlugin.compiled | 5 + .../x86_64/UnicodePlugin.res | Bin 0 -> 1032 bytes Dependencies/UnicodePlugin/UnicodePlugin.lpi | 89 +++++++++++++++++ Dependencies/UnicodePlugin/UnicodePlugin.lpr | 92 ++++++++++++++++++ Dependencies/UnicodePlugin/UnicodePlugin.lps | 89 +++++++++++++++++ Dependencies/UnicodePlugin/UnicodePlugin.res | Bin 0 -> 1032 bytes 6 files changed, 275 insertions(+) create mode 100644 Dependencies/UnicodePlugin/CreationKitUnicodePlugin/x86_64/UnicodePlugin.compiled create mode 100644 Dependencies/UnicodePlugin/CreationKitUnicodePlugin/x86_64/UnicodePlugin.res create mode 100644 Dependencies/UnicodePlugin/UnicodePlugin.lpi create mode 100644 Dependencies/UnicodePlugin/UnicodePlugin.lpr create mode 100644 Dependencies/UnicodePlugin/UnicodePlugin.lps create mode 100644 Dependencies/UnicodePlugin/UnicodePlugin.res diff --git a/Dependencies/UnicodePlugin/CreationKitUnicodePlugin/x86_64/UnicodePlugin.compiled b/Dependencies/UnicodePlugin/CreationKitUnicodePlugin/x86_64/UnicodePlugin.compiled new file mode 100644 index 00000000..8b475cf2 --- /dev/null +++ b/Dependencies/UnicodePlugin/CreationKitUnicodePlugin/x86_64/UnicodePlugin.compiled @@ -0,0 +1,5 @@ + + + + + diff --git a/Dependencies/UnicodePlugin/CreationKitUnicodePlugin/x86_64/UnicodePlugin.res b/Dependencies/UnicodePlugin/CreationKitUnicodePlugin/x86_64/UnicodePlugin.res new file mode 100644 index 0000000000000000000000000000000000000000..16e04f16a8c044361a8d577103a573311c6ae6ea GIT binary patch literal 1032 zcmb7@%}&BV6orqm3w6cTM3XLDxKIM7i9Z_`AdzTUXD1L`XEYnNFwo+;i?dGnJ%}@%fnzUVmJ#%RZh1v*cpA+&AQPIbzY&m2P>S=#sap zYPw)ut0Tx;j$k637?@j|_DNVoapM*BF~-nT_+iqj~jA`x;yqogsX&X6v>_%F4lcPNd`5%&i2Utm6*OjKM7W zv_oZkIBi2Zo#SYb-EwMu<`i#7CiN#zTbtBUn`)ao)}P-CTV*@<-#+uyV&2PL;s9$#Qr;p*BlaW>yc$;pQMmU+BnxgXf<%e7){(M?zwy&8}0%v@__jx99 pRy~i+wvYCoG@ai*%T7x6he%ueTXuqMX9lSBnEfdwpw-|l_yUMQn_mC` literal 0 HcmV?d00001 diff --git a/Dependencies/UnicodePlugin/UnicodePlugin.lpi b/Dependencies/UnicodePlugin/UnicodePlugin.lpi new file mode 100644 index 00000000..181ab8b8 --- /dev/null +++ b/Dependencies/UnicodePlugin/UnicodePlugin.lpi @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +