diff --git a/.gitignore b/.gitignore index afd4bf54..0ab04f5b 100644 --- a/.gitignore +++ b/.gitignore @@ -56,4 +56,9 @@ fallout4_test/resource.aps Dependencies/libdeflate.vcxproj.user Dependencies/libdeflate.vcxproj Dependencies/tbb +Dependencies/json *.lnk + +# other +*.cer +*.bat diff --git a/.gitmodules b/.gitmodules index cc756f39..4b519f66 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,10 +2,6 @@ path = Dependencies/meshoptimizer url = https://github.com/zeux/meshoptimizer.git branch = master -[submodule "Dependencies/tbb"] - path = Dependencies/tbb - url = https://github.com/01org/tbb.git - branch = tbb_2019 [submodule "Dependencies/libdeflate"] path = Dependencies/libdeflate url = https://github.com/ebiggers/libdeflate.git @@ -15,3 +11,13 @@ [submodule "Perchik71"] path = Dependencies/mINI url = https://github.com/Perchik71/mINI +[submodule "Dependencies/json"] + path = Dependencies/json + url = https://github.com/nlohmann/json.git +[submodule "Dependencies/tbb"] + path = Dependencies/tbb + url = https://github.com/oneapi-src/oneTBB.git + branch = tbb_2020 +[submodule "Dependencies/zipper"] + path = Dependencies/zipper + url = https://github.com/kuba--/zip.git diff --git a/Dependencies/json b/Dependencies/json new file mode 160000 index 00000000..e10a3fac --- /dev/null +++ b/Dependencies/json @@ -0,0 +1 @@ +Subproject commit e10a3fac8a255433146e3f06a703dc110fc3c3da diff --git a/Dependencies/tbb b/Dependencies/tbb index a803f276..eca91f16 160000 --- a/Dependencies/tbb +++ b/Dependencies/tbb @@ -1 +1 @@ -Subproject commit a803f276186fa2c286a357207832112265b448e4 +Subproject commit eca91f16d7490a8abfdee652dadf457ec820cc37 diff --git a/Dependencies/zipper b/Dependencies/zipper new file mode 160000 index 00000000..f02a47de --- /dev/null +++ b/Dependencies/zipper @@ -0,0 +1 @@ +Subproject commit f02a47dee0858a7f34651d29616d462406f53a70 diff --git a/Resources/dialogs/1221-8.json b/Resources/dialogs/1221-8.json new file mode 100644 index 00000000..10e5073d --- /dev/null +++ b/Resources/dialogs/1221-8.json @@ -0,0 +1,12 @@ +{ + "name": "Habrahabr", + "nothing": null, + "answer": { + "everything": 42 + }, + "companies": ["Infopulse", "TM"], + "user": { + "name": "tangro", + "active": true + } +} \ No newline at end of file diff --git a/fallout4_test/fallout4_test.vcxproj b/fallout4_test/fallout4_test.vcxproj index e37bd814..4aa8811b 100644 --- a/fallout4_test/fallout4_test.vcxproj +++ b/fallout4_test/fallout4_test.vcxproj @@ -164,6 +164,8 @@ + + @@ -173,6 +175,7 @@ + @@ -258,12 +261,14 @@ + + diff --git a/fallout4_test/fallout4_test.vcxproj.filters b/fallout4_test/fallout4_test.vcxproj.filters index 43eb8f6e..7fe52cd6 100644 --- a/fallout4_test/fallout4_test.vcxproj.filters +++ b/fallout4_test/fallout4_test.vcxproj.filters @@ -81,6 +81,12 @@ {15465807-fb72-43fe-a815-6fe39cdff7e4} + + {81995caa-f5d0-49b7-965d-68c1333396e6} + + + {966ff70e-c235-430f-9b45-98e040be03f6} + @@ -359,6 +365,15 @@ Header Files\UI\Windows + + Header Files\UI\Classes + + + __dependencies\zipper + + + __dependencies\zipper + @@ -589,6 +604,12 @@ Source Files\UI\Windows + + Source Files\UI\Classes + + + __dependencies\zipper + diff --git a/fallout4_test/resource.rc b/fallout4_test/resource.rc index 0406946a..3e7dcd91 100644 Binary files a/fallout4_test/resource.rc and b/fallout4_test/resource.rc differ diff --git a/fallout4_test/src/patches/CKF4/Editor.cpp b/fallout4_test/src/patches/CKF4/Editor.cpp index 0760ab8f..819104c3 100644 --- a/fallout4_test/src/patches/CKF4/Editor.cpp +++ b/fallout4_test/src/patches/CKF4/Editor.cpp @@ -258,8 +258,8 @@ uint32_t FIXAPI sub_1405B31C0_SSE41(BSTArray& Array, LPCVOID &Target) for (uint32_t iter = 0; iter < vectorizedIterations; iter++) { - __m128i test1 = _mm_cmpeq_epi64(targets, _mm_loadu_si128((__m128i *)&data[index + 0])); - __m128i test2 = _mm_cmpeq_epi64(targets, _mm_loadu_si128((__m128i *)&data[index + 2])); + __m128i test1 = _mm_cmpeq_epi64(targets, _mm_loadu_si128((__m128i*)&data[index + 0])); + __m128i test2 = _mm_cmpeq_epi64(targets, _mm_loadu_si128((__m128i*)&data[index + 2])); INT32 mask = _mm_movemask_pd(_mm_castsi128_pd(_mm_or_si128(test1, test2))); diff --git a/fallout4_test/src/patches/CKF4/JsonDialogGenerator.cpp b/fallout4_test/src/patches/CKF4/JsonDialogGenerator.cpp new file mode 100644 index 00000000..682f9348 --- /dev/null +++ b/fallout4_test/src/patches/CKF4/JsonDialogGenerator.cpp @@ -0,0 +1,270 @@ +#include "JsonDialogGenerator.h" +#include "LogWindow.h" +#include "UIGraphics.h" + +#include "..\..\profiler_internal.h" + +#include "..\..\..\..\Dependencies\json\single_include\nlohmann\json.hpp" +#include "..\..\..\..\Dependencies\zipper\src\zip.h" + +#include + +#define ARCHIVE_DIALOGS_FILENAME "data/f4ckfixes/dialogs.zip" +#define FONTNAME "Microsoft Sans Serif" + +namespace Core +{ + namespace Classes + { + namespace UI + { + using json = nlohmann::json; + namespace fs = std::filesystem; + + LPJsonDialogManager g_JsonDialogManager = NULL; + + LPWORD lpwAlign(LPWORD lpIn) + { + ULONG ul; + ul = (ULONG)lpIn; + ul += 3; + ul >>= 2; + ul <<= 2; + return (LPWORD)ul; + } + + BOOL CJsonDialogManager::AddDialog(const char* json_code, const LONG uid, const CJsonDialogType type) + { + if (type == CJsonDialogType::jdtUnknown) + return FALSE; + + auto jDialog = json::parse(json_code); + if (jDialog.empty()) + return FALSE; + + WORD fsize = 8; + // Font point size. + switch (type) + { + case Core::Classes::UI::jdt10pt: + fsize = 10; + break; + case Core::Classes::UI::jdt12pt: + fsize = 12; + break; + } + + CUICanvas* Canvas = new CUICanvas(GetDC(GetDesktopWindow())); + + Canvas->Font.Name = FONTNAME; + Canvas->Font.Styles = {}; + Canvas->Font.Size = fsize; + + INT cxDlgUnit; + INT cyDlgUnit; + Canvas->TextSize("X", cxDlgUnit, cyDlgUnit); + cxDlgUnit = cxDlgUnit >> 1; + cyDlgUnit = cyDlgUnit >> 1; + + ReleaseDC(GetDesktopWindow(), Canvas->Handle); + delete Canvas; + + std::string sTitle = jDialog["title"].get(); + std::string sFace = FONTNAME; + + const size_t titleSize = sTitle.length() + 1; + const size_t fontSize = sFace.length() + 1; + const size_t dataSize = sizeof(DLGTEMPLATE) + + sizeof(CHAR) + // Menu array + sizeof(CHAR) + // Class array + titleSize + // Title array + sizeof(WORD) + // Font point size + fontSize; // Font array + + LPDLGITEMTEMPLATE lpdit; + LPDLGTEMPLATE hgbl = (LPDLGTEMPLATE)GlobalAlloc(GMEM_ZEROINIT, dataSize + (512 * jDialog["items"].size())); + if (!hgbl) + return FALSE; + + LPDLGTEMPLATE dt = (LPDLGTEMPLATE)GlobalLock(hgbl); + dt->style = jDialog["style"].get() | DS_SETFONT | WS_VISIBLE; + dt->dwExtendedStyle = jDialog["styleex"].get(); + dt->cdit = jDialog["items"].size(); + dt->x = (jDialog["x"].get() * 4) / cxDlgUnit; + dt->y = (jDialog["y"].get() * 8) / cyDlgUnit; + dt->cx = (jDialog["width"].get() * 4) / cxDlgUnit; + dt->cy = (jDialog["height"].get() * 8) / cyDlgUnit; + + BYTE* dtExtra = (BYTE*)dt + sizeof(DLGTEMPLATE); + + // Menu array. + *(CHAR*)dtExtra = '\0'; + dtExtra += sizeof(CHAR); + // Class array. + *(CHAR*)dtExtra = '\0'; + dtExtra += sizeof(CHAR); + // Title array. + memcpy(dtExtra, sTitle.data(), titleSize); + dtExtra += titleSize; + // Font point size. + *(WORD*)dtExtra = fsize; + dtExtra += sizeof(WORD); + // Font array. + memcpy(dtExtra, sFace.data(), fontSize); + + LPWORD lpw = (LPWORD)dtExtra; + auto items = jDialog["items"]; + for (json::iterator it = items.begin(); it != items.end(); ++it) + { + lpdit = (LPDLGITEMTEMPLATE)lpwAlign(lpw); + lpdit->x = (it.value()["x"].get() * 4) / cxDlgUnit; + lpdit->y = (it.value()["y"].get() * 8) / cyDlgUnit; + lpdit->cx = (it.value()["width"].get() * 4) / cxDlgUnit; + lpdit->cy = (it.value()["height"].get() * 8) / cyDlgUnit; + lpdit->id = it.value()["id"].get(); + lpdit->style = WS_CHILD | it.value()["style"].get(); + + lpw = (LPWORD)(lpdit + 1); + *lpw++ = 0xFFFF; + + auto item_type = it.value()["type"].get(); + if (!stricmp(item_type.data(), "PUSHBUTTON")) + *lpw++ = 0x0080; + else if (!stricmp(item_type.data(), "EDITTEXT")) + *lpw++ = 0x0081; + else if (!stricmp(item_type.data(), "LTEXT")) + *lpw++ = 0x0082; + else if (!stricmp(item_type.data(), "PUSHBUTTON")) + *lpw++ = 0x0083; + else if (!stricmp(item_type.data(), "PUSHBUTTON")) + *lpw++ = 0x0084; + else if (!stricmp(item_type.data(), "COMBOBOX")) + *lpw++ = 0x0085; + else + *lpw++ = 0x0086; + + + /* static class */ + + auto caption = it.value()["id"].get(); + memcpy((LPWSTR)lpw, caption.data(), caption.length()); + lpw += 1 + caption.length(); + } + + /* + 0x0080 Button + Edit + Static + List box + Scroll bar +0x0085 + + 2581,29,16,61,12,ES_AUTOHSCROLL + CONTROL "",2093,"SysTreeView32",TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_DISABLEDRAGDROP | TVS_SHOWSELALWAYS | WS_BORDER | WS_HSCROLL | WS_TABSTOP,0,42,104,191,WS_EX_CLIENTEDGE + CONTROL "List2",1041,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_EDITLABELS | WS_TABSTOP,119,0,245,233,WS_EX_CLIENTEDGE + "",2157,105,0,10,233,NOT WS_GROUP + LTEXT "Filter",-1,3,18,19,10 + 6024,29,2,61,98,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Layout",-1,3,4,23,11 + PUSHBUTTON "...",6025,93,1,11,14 + PUSHBUTTON "-",6027,93,15,11,14 + CONTROL "Show only active (*) forms",2579,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,3,30,101,9 + */ + + + GlobalUnlock(hgbl); + + return TRUE; + } + + VOID CJsonDialogManager::Init(VOID) + { + // If there is no archive, this will cause an error and can't run the CK. + AssertMsgVa(fs::exists(fs::absolute(ARCHIVE_DIALOGS_FILENAME)), "File \"%s\" no found", ARCHIVE_DIALOGS_FILENAME); + + LogWindow::Log("Initialize JSON dialog manager"); + // open archive + struct zip_t* zip = zip_open(ARCHIVE_DIALOGS_FILENAME, 0, 'r'); + AssertMsgVa(zip, "File \"%s\" corrupted or not an archive", ARCHIVE_DIALOGS_FILENAME); + LogWindow::Log("Initialize JSON dialog manager"); + // reading the archive and retrieving data + CJsonDialogType jdtType = CJsonDialogType::jdtUnknown; + std::string sJson, sName; + INT nCount = zip_entries_total(zip); + for (INT i = 0; i < nCount; ++i) + { + zip_entry_openbyindex(zip, i); + // skip dirs + if (zip_entry_isdir(zip) != 0) + continue; + + sName = zip_entry_name(zip); + if (sName.find_first_of('-') == std::string::npos) + LogWindow::Log("JSON: Incorrect filename \"%s\"", sName.c_str()); + else + { + size_t nSize = zip_entry_size(zip); + if (!nSize) + continue; + // read + char* buf = NULL; + size_t bufsize = 0; + if (zip_entry_read(zip, (void**)&buf, &bufsize) != nSize) + LogWindow::Log("JSON: Error read file \"%s\"", sName.c_str()); + //else if (zip_entry_crc32(zip) != CRC32(buf)) + // LogWindow::Log("JSON: Incorrect CRC32 \"%s\"", sName.c_str()); + else + { + auto AtuID = sName.find_first_of('-'); + std::string sID = sName.substr(0, AtuID); + auto AtExt = sName.find_first_of('.'); + std::string sType = sName.substr(AtuID + 1, AtExt); + std::string sExt = sName.substr(AtExt + 1); + + switch (strtol(sType.data(), NULL, 10)) + { + case 8: + jdtType = CJsonDialogType::jdt8pt; + break; + case 10: + jdtType = CJsonDialogType::jdt10pt; + break; + case 12: + jdtType = CJsonDialogType::jdt12pt; + break; + default: + jdtType = CJsonDialogType::jdtUnknown; + break; + } + + if (jdtType == CJsonDialogType::jdtUnknown) + LogWindow::Log("JSON: Unknown dialog type %s \"%s\"", sType.c_str(), sName.c_str()); + else if (strnicmp(sExt.c_str(), "json", 8)) + LogWindow::Log("JSON: file \"%s\" not JSON", sName.c_str()); + else if (!AddDialog(buf, strtol(sID.data(), NULL, 10), jdtType)) + LogWindow::Log("JSON: Incorrect JSON data in file \"%s\"", sName.c_str()); + } + + if (buf) + free(buf); + } + } + } + + VOID CJsonDialogManager::Release(VOID) + { + LogWindow::Log("Shutdown JSON dialog manager"); + } + + CJsonDialogManager::CJsonDialogManager(VOID) + { + Init(); + } + + CJsonDialogManager::~CJsonDialogManager(VOID) + { + Release(); + } + } + } +} \ No newline at end of file diff --git a/fallout4_test/src/patches/CKF4/JsonDialogGenerator.h b/fallout4_test/src/patches/CKF4/JsonDialogGenerator.h new file mode 100644 index 00000000..b96a3a6b --- /dev/null +++ b/fallout4_test/src/patches/CKF4/JsonDialogGenerator.h @@ -0,0 +1,52 @@ +#pragma once + +/* +An apple fell on my head :D +I came up with the idea of using simple languages to generate dialogs. +This should solve the problem of bad dialogs in the future and support 10pt and 12pt text in dialogs. +*/ + +#include "..\..\common.h" + +#include + +namespace Core +{ + namespace Classes + { + namespace UI + { + enum CJsonDialogType + { + jdtUnknown, + jdt8pt, + jdt10pt, + jdt12pt + }; + + // + typedef class CJsonDialog + { + + } *PJsonDialog, *LPJsonDialog; + + + // + typedef class CJsonDialogManager + { + private: + tbb::concurrent_vector m_items; + public: + BOOL AddDialog(const char* json_code, const LONG uid, const CJsonDialogType type); + protected: + virtual VOID Init(VOID); + VOID Release(VOID); + public: + CJsonDialogManager(VOID); + virtual ~CJsonDialogManager(VOID); + } *PJsonDialogManager, *LPJsonDialogManager; + + extern LPJsonDialogManager g_JsonDialogManager; + } + } +} \ No newline at end of file diff --git a/fallout4_test/src/patches/CKF4/UIThemeMode.cpp b/fallout4_test/src/patches/CKF4/UIThemeMode.cpp index 1aab0710..0b226898 100644 --- a/fallout4_test/src/patches/CKF4/UIThemeMode.cpp +++ b/fallout4_test/src/patches/CKF4/UIThemeMode.cpp @@ -252,7 +252,7 @@ namespace UITheme { if (nCode == HC_ACTION) { - if (auto messageData = reinterpret_cast(lParam); (messageData->message == WM_CREATE)/* || messageData->message == WM_INITDIALOG*/) + if (auto messageData = reinterpret_cast(lParam); messageData->message == WM_CREATE) { LPCREATESTRUCTA lpCreateStruct = (LPCREATESTRUCTA)messageData->lParam; if (lpCreateStruct) @@ -557,8 +557,8 @@ namespace UITheme if ((Theme::GetTheme() == Theme::Theme_Dark) || (Theme::GetTheme() == Theme::Theme_DarkGray)) { // detected standart OS theme (comdlg32) - COLORREF clTest = GetPixel(hdc, pRect->left + 2, pRect->top + 2); - if (((GetRValue(clTest) + GetGValue(clTest) + GetBValue(clTest)) / 3) > 128) + COLORREF clTest = GetPixel(hdc, pRect->left + ((pRect->right - pRect->left) >> 1), pRect->top + 2); + if (CLR_INVALID != clTest && ((GetRValue(clTest) + GetGValue(clTest) + GetBValue(clTest)) / 3) > 128) return DrawThemeText(hTheme, hdc, iPartId, iStateId, pszText, cchText, dwTextFlags, dwTextFlags2, pRect); } diff --git a/fallout4_test/src/patches/patches_f4ck.cpp b/fallout4_test/src/patches/patches_f4ck.cpp index 5d3559ec..bc6bdb28 100644 --- a/fallout4_test/src/patches/patches_f4ck.cpp +++ b/fallout4_test/src/patches/patches_f4ck.cpp @@ -24,7 +24,10 @@ #include "CKF4/ResponseWindow.h" #include "CKF4/DataWindow.h" #include "CKF4/PreferencesWindow.h" -//#include "CKF4/ActorWindow.h" + +// include json generator dialogs +#include "CKF4/JsonDialogGenerator.h" + #include @@ -383,6 +386,10 @@ VOID FIXAPI F_UIPatches(VOID) XUtil::DetourClassJump(OFFSET(0xE1D2C7, 0), &Classes::CUIProgressDialog::ProcessMessagesOnlyLoadCellWorld); } } + + // Initializing the dialog manager. + // Loading all supported dialogs. + //Classes::g_JsonDialogManager = new Classes::CJsonDialogManager(); } diff --git a/fallout4_test/src/profiler_internal.h b/fallout4_test/src/profiler_internal.h index c938bec6..f50f858a 100644 --- a/fallout4_test/src/profiler_internal.h +++ b/fallout4_test/src/profiler_internal.h @@ -1,3 +1,5 @@ +#include "common.h" + static constexpr uint32_t crc_table[256] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, diff --git a/fallout4_test/src/xutil.cpp b/fallout4_test/src/xutil.cpp index 51dc3b51..40a8bd74 100644 --- a/fallout4_test/src/xutil.cpp +++ b/fallout4_test/src/xutil.cpp @@ -159,6 +159,18 @@ size_t XUtil::Str::findCaseInsensitive(std::string data, std::string toSearch, s return data.find(toSearch, pos); } +std::string XUtil::Str::format(const char* fmt, ...) +{ + va_list va; + char message[2048]; + + va_start(va, fmt); + _vsnprintf(&message[0], _TRUNCATE, fmt, va); + va_end(va); + + return message; +} + void XUtil::Trim(char *Buffer, char C) { size_t len = strlen(Buffer); diff --git a/fallout4_test/src/xutil.h b/fallout4_test/src/xutil.h index 0472c2dc..9221e57f 100644 --- a/fallout4_test/src/xutil.h +++ b/fallout4_test/src/xutil.h @@ -215,6 +215,9 @@ namespace XUtil // 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); + + // formating string + std::string format(const char* fmt, ...); } void SetThreadName(uint32_t ThreadID, const char *ThreadName);