From a7cf92ce7e3a2c2435866dfb66e16c7b63e4aebc Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Fri, 3 Mar 2023 13:52:19 +0800 Subject: [PATCH 01/30] Reimplement MixFile Reading system --- FA2pp | 2 +- FA2sp.vcxproj | 3 + FA2sp.vcxproj.filters | 6 + FA2sp/Ext/CLoading/Body.cpp | 187 +++++++++++++++++++++++++++++--- FA2sp/Ext/CLoading/Body.h | 2 + FA2sp/Ext/CLoading/Hooks.cpp | 84 ++++++++++++++ FA2sp/Miscs/Hooks.FileNames.cpp | 10 -- FA2sp/Miscs/Hooks.Memory.cpp | 31 ++++-- FA2sp/Miscs/Hooks.Mix.cpp | 56 ---------- FA2sp/Miscs/PlaceHolder.cpp | 23 ++++ FA2sp/RunTime.cpp | 18 +++ FA2sp/RunTime.h | 2 + FA2sp/Source/CIsoView.cpp | 4 +- 13 files changed, 336 insertions(+), 92 deletions(-) create mode 100644 FA2sp/Ext/CLoading/Hooks.cpp create mode 100644 FA2sp/Miscs/PlaceHolder.cpp diff --git a/FA2pp b/FA2pp index 0a2506f..f17609f 160000 --- a/FA2pp +++ b/FA2pp @@ -1 +1 @@ -Subproject commit 0a2506fc9c56be1d87782306afc3d4d49083f4a9 +Subproject commit f17609f105cac5315ccfe3c3e7fbfb7b16d981e8 diff --git a/FA2sp.vcxproj b/FA2sp.vcxproj index ce0f898..a5db597 100644 --- a/FA2sp.vcxproj +++ b/FA2sp.vcxproj @@ -154,6 +154,7 @@ + @@ -192,6 +193,8 @@ + + diff --git a/FA2sp.vcxproj.filters b/FA2sp.vcxproj.filters index c05b0f8..b009630 100644 --- a/FA2sp.vcxproj.filters +++ b/FA2sp.vcxproj.filters @@ -473,6 +473,12 @@ 源文件 + + 源文件 + + + 源文件 + diff --git a/FA2sp/Ext/CLoading/Body.cpp b/FA2sp/Ext/CLoading/Body.cpp index 3a3aa6f..c709f6e 100644 --- a/FA2sp/Ext/CLoading/Body.cpp +++ b/FA2sp/Ext/CLoading/Body.cpp @@ -1,18 +1,173 @@ #include "Body.h" -//CLoading* CLoadingExt::Instance = nullptr; - -//void CLoadingExt::ProgramStartupInit() -//{ -// RunTime::ResetMemoryContentAt(0x5948A8, &CLoadingExt::PreTranslateMessageExt); -//} - -//BOOL CLoadingExt::PreTranslateMessageExt(MSG* pMsg) -//{ -// switch (pMsg->message) { -// -// default: -// break; -// } -// return this->ppmfc::CDialog::PreTranslateMessage(pMsg); -//} \ No newline at end of file +#include +#include +#include + +bool CLoadingExt::InitMixFilesFix() +{ + HasMdFile = true; + + // Load Extra Mixes + { + + if (auto pSection = CINI::FAData->GetSection("ExtraMixes")) + { + std::map collector; + + for (auto& pair : pSection->GetIndices()) + collector[pair.second] = pair.first; + + ppmfc::CString path; + + for (auto& pair : collector) + { + if (CINI::FAData->GetBool("ExtraMixes", pair.second)) + path = CFinalSunApp::Instance->ExePath; + else + path = CFinalSunApp::Instance->FilePath; + path += "\\" + pair.second; + if (auto id = CMixFile::Open(path, 0)) + { + Logger::Raw("[MixLoader][EXTRA] %04d - %s loaded.\n", id, path); + } + else + { + Logger::Raw("[MixLoader][EXTRA] %s failed!\n", id); + } + } + } + } + + ppmfc::CString Dir = CFinalSunApp::Instance->FilePath + "\\"; + auto LoadMixFile = [this, Dir](const char* Mix, int Parent = 0) + { + if (Parent) + { + int result = CMixFile::Open(Mix, Parent); + if (result) + Logger::Raw("[MixLoader] %04d - %s loaded.\n", result, Mix); + else + Logger::Raw("[MixLoader] %s failed!\n", Mix); + return result; + } + else + { + ppmfc::CString FullPath = Dir + Mix; + int result = CMixFile::Open(FullPath, 0); + if (result) + { + Logger::Raw("[MixLoader] %04d - %s loaded.\n", result, FullPath); + return result; + } + if (int nMix = SearchFile(Mix)) + { + result = CMixFile::Open(Mix, nMix); + if (result) + Logger::Raw("[MixLoader] %04d - %s loaded.\n", result, Mix); + else + Logger::Raw("[MixLoader] %s failed!\n", Mix); + return result; + } + Logger::Raw("[MixLoader] %s failed!\n", Mix); + return result; + } + }; + auto SetMixFile = [LoadMixFile](const char* Mix, int& value) + { + value = LoadMixFile(Mix); + return value; + }; + + // Init_Bootstrap_Mixfiles + ppmfc::CString format = "EXPAND" + CINI::FAData->GetString("Filenames", "MixExtension", "MD") + "%02d.MIX"; + for (int i = 99; i >= 0; --i) + { + ppmfc::CString filename; + filename.Format(format, i); + LoadMixFile(filename); + } + + if (!LoadMixFile("RA2MD.MIX")) return false; + if (!LoadMixFile("RA2.MIX")) return false; + if (!LoadMixFile("CACHEMD.MIX")) return false; + if (!LoadMixFile("CACHE.MIX")) return false; + if (!LoadMixFile("LOCALMD.MIX")) return false; + if (!LoadMixFile("LOCAL.MIX")) return false; + + // Init_Expansion_Mixfiles + // ECACHE and ELOCAL + WIN32_FIND_DATA fd; + HANDLE hf = FindFirstFile(Dir + "ECACHE*.MIX", &fd); + if (hf != INVALID_HANDLE_VALUE) + { + do + { + if (fd.dwFileAttributes & (FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)) + continue; + LoadMixFile(fd.cFileName); + } while (FindNextFile(hf, &fd)); + FindClose(hf); + } + hf = FindFirstFile(Dir + "ELOCAL*.MIX", &fd); + if (hf != INVALID_HANDLE_VALUE) + { + do + { + if (fd.dwFileAttributes & (FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)) + continue; + LoadMixFile(fd.cFileName); + } while (FindNextFile(hf, &fd)); + FindClose(hf); + } + + // Init_Secondary_Mixfiles + if (!LoadMixFile("CONQMD.MIX")) return false; + if (!LoadMixFile("GENERMD.MIX")) return false; + if (!LoadMixFile("GENERIC.MIX")) return false; + if (!LoadMixFile("ISOGENMD.MIX")) return false; + if (!LoadMixFile("ISOGEN.MIX")) return false; + if (!LoadMixFile("CONQUER.MIX")) return false; + + // Init_Theaters + LoadMixFile("TEMPERAT.MIX"); + LoadMixFile("ISOTEMP.MIX"); + LoadMixFile("ISOTEMMD.MIX"); + LoadMixFile("TEM.MIX"); + LoadMixFile("TEMPERATMD.MIX"); + + LoadMixFile("SNOW.MIX"); + LoadMixFile("ISOSNOW.MIX"); + LoadMixFile("ISOSNO.MIX"); + LoadMixFile("ISOSNOMD.MIX"); + LoadMixFile("SNO.MIX"); + LoadMixFile("SNOWMD.MIX"); + + LoadMixFile("URBAN.MIX"); + LoadMixFile("ISOURB.MIX"); + LoadMixFile("ISOURBMD.MIX"); + LoadMixFile("URB.MIX"); + LoadMixFile("URBANMD.MIX"); + + LoadMixFile("DESERT.MIX"); + LoadMixFile("ISODES.MIX"); + LoadMixFile("ISODESMD.MIX"); + LoadMixFile("DES.MIX"); + LoadMixFile("DESERTMD.MIX"); + + LoadMixFile("URBANN.MIX"); + LoadMixFile("ISOUBN.MIX"); + LoadMixFile("ISOUBNMD.MIX"); + LoadMixFile("UBN.MIX"); + LoadMixFile("URBANNMD.MIX"); + + LoadMixFile("LUNAR.MIX"); + LoadMixFile("ISOLUN.MIX"); + LoadMixFile("ISOLUNMD.MIX"); + LoadMixFile("LUN.MIX"); + LoadMixFile("LUNARMD.MIX"); + + LoadMixFile("MARBLE.MIX"); + + return true; +} \ No newline at end of file diff --git a/FA2sp/Ext/CLoading/Body.h b/FA2sp/Ext/CLoading/Body.h index 502b56d..da162d7 100644 --- a/FA2sp/Ext/CLoading/Body.h +++ b/FA2sp/Ext/CLoading/Body.h @@ -18,6 +18,8 @@ class NOVTABLE CLoadingExt : public CLoading //static void ProgramStartupInit(); + bool InitMixFilesFix(); + void LoadObjects(ppmfc::CString pRegName); static ppmfc::CString GetImageName(ppmfc::CString ID, int nFacing); static void ClearItemTypes(); diff --git a/FA2sp/Ext/CLoading/Hooks.cpp b/FA2sp/Ext/CLoading/Hooks.cpp new file mode 100644 index 0000000..54d092a --- /dev/null +++ b/FA2sp/Ext/CLoading/Hooks.cpp @@ -0,0 +1,84 @@ +#include "Body.h" + +#include +#include +#include +#include + +DEFINE_HOOK(486B00, CLoading_InitMixFiles, 7) +{ + GET(CLoadingExt*, pThis, ECX); + + bool result = pThis->InitMixFilesFix(); + R->EAX(result); + + if (!result) + { + MessageBox(NULL, "Failed to load necessary mix file!", "Fatal Error!", MB_OK); + ExitProcess(114514); + } + + return 0x48A26A; +} + +DEFINE_HOOK(48A650, CLoading_SearchFile, 6) +{ + GET(CLoadingExt*, pThis, ECX); + GET_STACK(const char*, Filename, 0x4); + GET_STACK(unsigned char*, pTheaterType, 0x8); + + for (int i = 0; i < CMixFile::ArraySize; ++i) + { + auto& mix = CMixFile::Array[i]; + if (!mix.is_open()) + break; + if (CMixFile::HasFile(Filename, i + 1)) + { +#ifndef NDEBUG + Logger::Debug("[SearchFile] %s - %d\n", Filename, i + 1); +#endif + R->EAX(i + 1); + return 0x48AA63; + } + } + +#ifndef NDEBUG + Logger::Debug("[SearchFile] %s - NOT FOUND\n", Filename); +#endif + R->EAX(0); + return 0x48AA63; +} + +DEFINE_HOOK(49DD64, CMapData_LoadMap_InitMixFiles_Removal, 5) +{ + CLoading::Instance->CSCBuiltby.~CStatic(); + CLoading::Instance->CSCLoading.~CStatic(); + CLoading::Instance->CSCVersion.~CStatic(); + CLoading::Instance->CPCProgress.~CProgressCtrl(); + struct CLoadingHelper + { + void DTOR() + { + JMP_STD(0x551A1D); + } + }; + reinterpret_cast(CLoading::Instance())->DTOR(); + return 0x49DD74; +} + +DEFINE_HOOK(4B8CFC, CMapData_CreateMap_InitMixFiles_Removal, 5) +{ + CLoading::Instance->CSCBuiltby.~CStatic(); + CLoading::Instance->CSCLoading.~CStatic(); + CLoading::Instance->CSCVersion.~CStatic(); + CLoading::Instance->CPCProgress.~CProgressCtrl(); + struct CLoadingHelper + { + void DTOR() + { + JMP_STD(0x551A1D); + } + }; + reinterpret_cast(CLoading::Instance())->DTOR(); + return 0x4B8D0C; +} diff --git a/FA2sp/Miscs/Hooks.FileNames.cpp b/FA2sp/Miscs/Hooks.FileNames.cpp index 6dcf39e..1d8685f 100644 --- a/FA2sp/Miscs/Hooks.FileNames.cpp +++ b/FA2sp/Miscs/Hooks.FileNames.cpp @@ -152,13 +152,3 @@ DEFINE_HOOK(47AA67, FileNames_DesertIni, 7) return 0x47AA7A; } - -DEFINE_HOOK(48768B, FileNames_MixExtension, 5) -{ - REF_STACK(ppmfc::CString, name, 40); - - name += CINI::FAData->GetString("Filenames", "MixExtension", "md"); - R->ECX(name); - - return 0x487699; -} \ No newline at end of file diff --git a/FA2sp/Miscs/Hooks.Memory.cpp b/FA2sp/Miscs/Hooks.Memory.cpp index a532040..82a42a1 100644 --- a/FA2sp/Miscs/Hooks.Memory.cpp +++ b/FA2sp/Miscs/Hooks.Memory.cpp @@ -2,14 +2,31 @@ #include +class MemoryWrapper +{ +public: + static void* __cdecl _malloc(size_t sz) + { + return ::malloc(sz); + } + + static void __cdecl _free(void* ptr) + { + ::free(ptr); + } +}; + DEFINE_HOOK(537128, ExeStart_MemoryHooks, 5) { - RunTime::ResetMemoryContentAt(0x591110, HeapFree); - RunTime::ResetMemoryContentAt(0x591128, HeapReAlloc); - RunTime::ResetMemoryContentAt(0x59112C, HeapAlloc); - RunTime::ResetMemoryContentAt(0x591138, HeapSize); - RunTime::ResetMemoryContentAt(0x591154, HeapDestroy); - RunTime::ResetMemoryContentAt(0x591158, HeapCreate); + RunTime::ResetMemoryContentAt(0x591110, ::HeapFree); + RunTime::ResetMemoryContentAt(0x591128, ::HeapReAlloc); + RunTime::ResetMemoryContentAt(0x59112C, ::HeapAlloc); + RunTime::ResetMemoryContentAt(0x591138, ::HeapSize); + RunTime::ResetMemoryContentAt(0x591154, ::HeapDestroy); + RunTime::ResetMemoryContentAt(0x591158, ::HeapCreate); + + RunTime::SetJump(0x536106, (DWORD)MemoryWrapper::_free); + RunTime::SetJump(0x537CA0, (DWORD)MemoryWrapper::_malloc); return 0; -} \ No newline at end of file +} diff --git a/FA2sp/Miscs/Hooks.Mix.cpp b/FA2sp/Miscs/Hooks.Mix.cpp index 078278a..2ffd522 100644 --- a/FA2sp/Miscs/Hooks.Mix.cpp +++ b/FA2sp/Miscs/Hooks.Mix.cpp @@ -15,59 +15,3 @@ DEFINE_HOOK(5280FC, MixFile_Open_CheckRAEncrypted, 7) return 0x528124; } -#include "../FA2sp.h" -#include -#include -#include -#include - -#include - -std::vector ExtraMixes; - -DEFINE_HOOK(48A1AD, CLoading_InitMixFiles_ExtraMix, 7) -{ - ExtraMixes.clear(); - - if (auto pSection = CINI::FAData->GetSection("ExtraMixes")) - { - std::map collector; - - for (auto& pair : pSection->GetIndices()) - collector[pair.second] = pair.first; - - ppmfc::CString path; - - for (auto& pair : collector) - { - if (CINI::FAData->GetBool("ExtraMixes", pair.second)) - path = CFinalSunApp::Instance->ExePath; - else - path = CFinalSunApp::Instance->FilePath; - path += "\\" + pair.second; - if (auto id = CMixFile::Open(path, 0)) - { - ExtraMixes.push_back(id); - CFA2Logger::WriteLine("Successfully loaded extra mix file from %s", path); - } - } - } - - return 0; -} - -DEFINE_HOOK(48A650, CLoading_SearchFile_ExtraMix, 6) -{ - GET_STACK(const char*, pName, 0x4); - - for (auto& id : ExtraMixes) - { - if (CMixFile::HasFile(pName, id)) - { - R->EAX(id); - return 0x48ABD8; - } - } - - return 0; -} \ No newline at end of file diff --git a/FA2sp/Miscs/PlaceHolder.cpp b/FA2sp/Miscs/PlaceHolder.cpp new file mode 100644 index 0000000..7db9d8f --- /dev/null +++ b/FA2sp/Miscs/PlaceHolder.cpp @@ -0,0 +1,23 @@ +#include +#include + +#include + +#include + +#ifndef NDEBUG +DEFINE_HOOK(438DB0, CFinalSunDlg_MapTools_SearchWaypoint_DEBUGDumpFile, 6) +{ + constexpr const char* Filename = "snowmo.ini"; + + DWORD size; + auto buffer = CLoading::Instance->ReadWholeFile(Filename, &size); + + std::ofstream fout; + fout.open(Filename, std::ios::out | std::ios::binary); + fout.write((const char*)buffer, size); + fout.close(); + GameDelete(buffer); + return 0x438E4E; +} +#endif \ No newline at end of file diff --git a/FA2sp/RunTime.cpp b/FA2sp/RunTime.cpp index b3bd4c2..c2d9458 100644 --- a/FA2sp/RunTime.cpp +++ b/FA2sp/RunTime.cpp @@ -1,5 +1,23 @@ #include "RunTime.h" +void RunTime::SetJump(DWORD from, DWORD to) +{ +#pragma pack(push, 1) + struct JumpStruct + { + JumpStruct(DWORD from, DWORD to) + : OpCode{ 0xE9 } + , Offset(to - from - 5) + {} + unsigned char OpCode; + DWORD Offset; + }; + static_assert(sizeof(JumpStruct) == 5); +#pragma pack(pop) + JumpStruct data{ from,to }; + ResetMemoryContentAt(from, data); +} + RunTime::RunTime() { } diff --git a/FA2sp/RunTime.h b/FA2sp/RunTime.h index 3739177..9fcc977 100644 --- a/FA2sp/RunTime.h +++ b/FA2sp/RunTime.h @@ -22,6 +22,8 @@ class RunTime RunTime::ResetMemoryContentAt(addr, &value, sizeof(T)); } + static void SetJump(DWORD from, DWORD to); + RunTime(); ~RunTime(); diff --git a/FA2sp/Source/CIsoView.cpp b/FA2sp/Source/CIsoView.cpp index a972c46..5bb56a1 100644 --- a/FA2sp/Source/CIsoView.cpp +++ b/FA2sp/Source/CIsoView.cpp @@ -4,8 +4,8 @@ void CIsoViewImpl::ScreenCoord2MapCoord_Flat(int& X, int& Y) { - double x = X * 0.016666668; - double y = Y * 0.033333335; + double x = X * 1.0 / 60; + double y = Y * 1.0 / 30; X = y - x + CMapData::Instance->MapWidthPlusHeight * 0.5 + 1.5; Y = y + x - CMapData::Instance->MapWidthPlusHeight * 0.5 + 0.5; } From 0329c92b6c448633bdbd027a26a6b008b3ec8a89 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Fri, 3 Mar 2023 17:57:57 +0800 Subject: [PATCH 02/30] Support Ares Custom Foundation --- CHANGELOG.md | 1 + FA2pp | 2 +- FA2sp.vcxproj | 1 + FA2sp/Ext/CIsoView/Body.cpp | 181 ++++++++++++++++ FA2sp/Ext/CIsoView/Body.h | 1 + FA2sp/Ext/CIsoView/Hooks.cpp | 32 ++- FA2sp/Ext/CMapData/Body.cpp | 1 + FA2sp/Ext/CMapData/Body.h | 24 +++ .../Ext/CMapData/Hooks.BuildingTypeDatas.cpp | 201 ++++++++++++++++++ 9 files changed, 440 insertions(+), 4 deletions(-) create mode 100644 FA2sp/Ext/CMapData/Hooks.BuildingTypeDatas.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fc4d5d..913cf00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## RELEASE 1.6.0 (2023-XX-XX) - Reimplemented file reading system +- Support Ares Custom Foundation ## RELEASE 1.5.2 (2023-03-03) - Fixed the bug that money calculation is incorrect diff --git a/FA2pp b/FA2pp index f17609f..00b2faf 160000 --- a/FA2pp +++ b/FA2pp @@ -1 +1 @@ -Subproject commit f17609f105cac5315ccfe3c3e7fbfb7b16d981e8 +Subproject commit 00b2faf21b574bfbf9b6cc541efef8fd14ba0dfd diff --git a/FA2sp.vcxproj b/FA2sp.vcxproj index a5db597..1348ae5 100644 --- a/FA2sp.vcxproj +++ b/FA2sp.vcxproj @@ -159,6 +159,7 @@ + diff --git a/FA2sp/Ext/CIsoView/Body.cpp b/FA2sp/Ext/CIsoView/Body.cpp index 415a0c5..eba93d6 100644 --- a/FA2sp/Ext/CIsoView/Body.cpp +++ b/FA2sp/Ext/CIsoView/Body.cpp @@ -368,6 +368,187 @@ void CIsoViewExt::DrawLockedCellOutline(int X, int Y, int W, int H, COLORREF col } +void CIsoViewExt::DrawLockedLines(const std::vector>& lines, int X, int Y, COLORREF color, bool bUseDot, bool bUsePrimary, LPDDSURFACEDESC2 lpDesc) +{ + if (lpDesc->lpSurface == nullptr) + return; + + RECT rect; + this->GetWindowRect(&rect); + + auto lPitch = lpDesc->lPitch; + auto nBytesPerPixel = *(int*)0x72A8C0; + + auto pRGB = (ColorStruct*)&color; + BGRStruct ddColor; + ddColor.R = pRGB->red; + ddColor.G = pRGB->green; + ddColor.B = pRGB->blue; + + auto DrawLine = [lPitch, nBytesPerPixel, ddColor, lpDesc, &rect](int X1, int Y1, int X2, int Y2) + { + int color = *(int*)&ddColor; + + if (X1 > X2) + { + std::swap(X1, X2); + std::swap(Y1, Y2); + } + + int dx = X2 - X1; + int dy = Y2 - Y1; + + auto ptr = (unsigned char*)lpDesc->lpSurface + lPitch * Y1 + X1 * nBytesPerPixel; + + if (dy == 0) + { + for (int i = 0; i <= dx; ++i) + { + memcpy(ptr, &ddColor, nBytesPerPixel); + ptr += nBytesPerPixel; + } + } + else if (dx == 0) + { + int pitch = lPitch; + if (dy < 0) + { + pitch = -pitch; + dy = -dy; + } + + for (int i = 0; i <= dy; ++i) + { + memcpy(ptr, &ddColor, nBytesPerPixel); + ptr += pitch; + } + } + else + { + int pitch = lPitch; + if (dy < 0) + { + pitch = -pitch; + dy = -dy; + } + + int dx2 = 2 * dx; + int dy2 = 2 * dy; + + if (dx > dy) + { + int delta = dy2 - dx; + for (int i = 0; i < dx; ++i) + { + memcpy(ptr + i * nBytesPerPixel, &ddColor, nBytesPerPixel); + if (delta > 0) + { + ptr += pitch; + delta -= dx2; + } + delta += dy2; + } + } + else + { + int delta = dx2 - dy; + int k = 0; + + for (int i = 0; i < dy; ++i) + { + memcpy(ptr + k * nBytesPerPixel, &ddColor, nBytesPerPixel); + if (delta > 0) + { + ++k; + delta -= dy2; + } + delta += dx2; + ptr += pitch; + } + } + } + }; + auto ClipAndDrawLine = [&rect, DrawLine](int X1, int Y1, int X2, int Y2) + { + auto encode = [&rect](int x, int y) + { + int c = 0; + if (x < rect.left) c = c | 0x1; + else if (x > rect.right) c = c | 0x2; + if (y > rect.bottom) c = c | 0x4; + else if (y < rect.top) c = c | 0x8; + return c; + }; + auto clip = [&rect, encode](int& X1, int& Y1, int& X2, int& Y2) -> bool + { + int code1, code2, code; + int x = 0, y = 0; + code1 = encode(X1, Y1); + code2 = encode(X2, Y2); + while (code1 != 0 || code2 != 0) + { + if ((code1 & code2) != 0) return false; + code = code1; + if (code == 0) code = code2; + if ((0b1 & code) != 0) + { + x = rect.left; + y = Y1 + (Y2 - Y1) * (rect.left - X1) / (X2 - X1); + } + else if ((0b10 & code) != 0) + { + x = rect.right; + y = Y1 + (Y2 - Y1) * (rect.right - X1) / (X2 - X1); + } + else if ((0b100 & code) != 0) + { + y = rect.bottom; + x = X1 + (X2 - X1) * (rect.bottom - Y1) / (Y2 - Y1); + } + else if ((0b1000 & code) != 0) + { + y = rect.top; + x = X1 + (X2 - X1) * (rect.top - Y1) / (Y2 - Y1); + } + if (code == code1) + { + X1 = x; + Y1 = y; + code1 = encode(x, y); + } + else + { + X2 = x; + Y2 = y; + code2 = encode(x, y); + } + } + return true; + }; + if (clip(X1, Y1, X2, Y2)) + DrawLine(X1, Y1, X2, Y2); + }; + + Y -= 30; + X += 30; + for (const auto& line : lines) + { + int x1 = X + line.first.X; + int y1 = Y + line.first.Y; + int x2 = X + line.second.X; + int y2 = Y + line.second.Y; + ClipAndDrawLine(x1, y1, x2, y2); + ClipAndDrawLine(x1, y1, x2, y2); + if (!bUseDot) + { + ClipAndDrawLine(x1 + 1, y1, x2 + 1, y2); + ClipAndDrawLine(x1 - 1, y1, x2 - 1, y2); + ClipAndDrawLine(x1 + 2, y1, x2 + 2, y2); + ClipAndDrawLine(x1 - 2, y1, x2 - 2, y2); + } + } +} + void CIsoViewExt::DrawCelltag(int X, int Y) { this->BltToBackBuffer(ImageDataMapHelper::GetImageDataFromMap("CELLTAG")->lpSurface, X, Y, -1, -1); diff --git a/FA2sp/Ext/CIsoView/Body.h b/FA2sp/Ext/CIsoView/Body.h index 492a67f..e7194de 100644 --- a/FA2sp/Ext/CIsoView/Body.h +++ b/FA2sp/Ext/CIsoView/Body.h @@ -17,6 +17,7 @@ class NOVTABLE CIsoViewExt : public CIsoView BOOL OnMouseWheelExt(UINT Flags, short zDelta, CPoint pt); void DrawLockedCellOutline(int X, int Y, int W, int H, COLORREF color, bool bUseDot, bool bUsePrimary, LPDDSURFACEDESC2 lpDesc); + void DrawLockedLines(const std::vector>& lines, int X, int Y, COLORREF color, bool bUseDot, bool bUsePrimary, LPDDSURFACEDESC2 lpDesc); void DrawCelltag(int X, int Y); void DrawWaypointFlag(int X, int Y); void DrawTube(CellData* pData, int X, int Y); diff --git a/FA2sp/Ext/CIsoView/Hooks.cpp b/FA2sp/Ext/CIsoView/Hooks.cpp index 5ab88da..4f623ca 100644 --- a/FA2sp/Ext/CIsoView/Hooks.cpp +++ b/FA2sp/Ext/CIsoView/Hooks.cpp @@ -216,6 +216,11 @@ DEFINE_HOOK(4720D3, CIsoView_Draw_PowerUp3Loc_PosFix, 5) return 0x47230B; } +namespace CIsoViewDrawTemp +{ + int BuildingIndex; +} + DEFINE_HOOK(470986, CIsoView_Draw_BuildingImageDataQuery_1, 8) { REF_STACK(ImageDataClass, image, STACK_OFFS(0xD18, 0xAFC)); @@ -226,6 +231,8 @@ DEFINE_HOOK(470986, CIsoView_Draw_BuildingImageDataQuery_1, 8) nFacing = 7 - (type.Facing / 32) % 8; image = *ImageDataMapHelper::GetImageDataFromMap(CLoadingExt::GetImageName(type.ID, nFacing)); + CIsoViewDrawTemp::BuildingIndex = R->ESI(); + return 0x4709E1; } @@ -252,11 +259,21 @@ DEFINE_HOOK(4709EE, CIsoView_Draw_ShowBuildingOutline, 6) GET_STACK(COLORREF, dwColor, STACK_OFFS(0xD18, 0xD04)); LEA_STACK(LPDDSURFACEDESC2, lpDesc, STACK_OFFS(0xD18, 0x92C)); - pThis->DrawLockedCellOutline(X, Y, W, H, dwColor, false, false, lpDesc); + const auto& DataExt = CMapDataExt::BuildingDataExts[CIsoViewDrawTemp::BuildingIndex]; + if (DataExt.IsCustomFoundation()) + pThis->DrawLockedLines(*DataExt.LinesToDraw, X, Y, dwColor, false, false, lpDesc); + else + pThis->DrawLockedCellOutline(X, Y, W, H, dwColor, false, false, lpDesc); return 0x470A38; } +DEFINE_HOOK(4727B2, CIsoView_Draw_BasenodeOutline_CustomFoundation, B) +{ + CIsoViewDrawTemp::BuildingIndex = R->ESI(); + return 0; +} + DEFINE_HOOK(47280B, CIsoView_Draw_BasenodeOutline, 6) { GET_STACK(CIsoViewExt*, pThis, STACK_OFFS(0xD18, 0xCD4)); @@ -267,8 +284,17 @@ DEFINE_HOOK(47280B, CIsoView_Draw_BasenodeOutline, 6) GET_STACK(COLORREF, dwColor, STACK_OFFS(0xD18, 0xB94)); LEA_STACK(LPDDSURFACEDESC2, lpDesc, STACK_OFFS(0xD18, 0x92C)); - pThis->DrawLockedCellOutline(X, Y, W, H, dwColor, true, false, lpDesc); - pThis->DrawLockedCellOutline(X + 1, Y, W, H, dwColor, true, false, lpDesc); + const auto& DataExt = CMapDataExt::BuildingDataExts[CIsoViewDrawTemp::BuildingIndex]; + if (DataExt.IsCustomFoundation()) + { + pThis->DrawLockedLines(*DataExt.LinesToDraw, X, Y, dwColor, true, false, lpDesc); + pThis->DrawLockedLines(*DataExt.LinesToDraw, X + 1, Y, dwColor, true, false, lpDesc); + } + else + { + pThis->DrawLockedCellOutline(X, Y, W, H, dwColor, true, false, lpDesc); + pThis->DrawLockedCellOutline(X + 1, Y, W, H, dwColor, true, false, lpDesc); + } return 0x472884; } diff --git a/FA2sp/Ext/CMapData/Body.cpp b/FA2sp/Ext/CMapData/Body.cpp index 45b97db..e2a3beb 100644 --- a/FA2sp/Ext/CMapData/Body.cpp +++ b/FA2sp/Ext/CMapData/Body.cpp @@ -11,6 +11,7 @@ int CMapDataExt::OreValue[4] { -1,-1,-1,-1 }; std::vector CMapDataExt::OverlayTypeDatas; +std::vector CMapDataExt::BuildingDataExts; int CMapDataExt::GetOreValue(unsigned char nOverlay, unsigned char nOverlayData) { diff --git a/FA2sp/Ext/CMapData/Body.h b/FA2sp/Ext/CMapData/Body.h index c024371..386621a 100644 --- a/FA2sp/Ext/CMapData/Body.h +++ b/FA2sp/Ext/CMapData/Body.h @@ -9,6 +9,27 @@ struct OverlayTypeData bool Rock; }; +struct BuildingDataExt +{ + ~BuildingDataExt() + { + if (Foundations) + delete Foundations; + if (LinesToDraw) + delete LinesToDraw; + } + + bool IsCustomFoundation() const + { + return Foundations != nullptr; + } + + int Width{ 0 }; + int Height{ 0 }; + std::vector* Foundations{ nullptr }; + std::vector>* LinesToDraw{ nullptr }; +}; + class CMapDataExt : public CMapData { public: @@ -25,6 +46,9 @@ class CMapDataExt : public CMapData int GetOreValueAt(CellData& cell); void InitOreValue(); + void InitializeBuildingTypesExt(const char* ID); + + static std::vector BuildingDataExts; static std::vector OverlayTypeDatas; static int OreValue[4]; }; \ No newline at end of file diff --git a/FA2sp/Ext/CMapData/Hooks.BuildingTypeDatas.cpp b/FA2sp/Ext/CMapData/Hooks.BuildingTypeDatas.cpp new file mode 100644 index 0000000..e3ef5ca --- /dev/null +++ b/FA2sp/Ext/CMapData/Hooks.BuildingTypeDatas.cpp @@ -0,0 +1,201 @@ +#include "Body.h" + +#include + +#include "../../FA2sp.h" + +#include + +DEFINE_HOOK(4B5460, CMapData_InitializeBuildingTypes, 7) +{ + GET(CMapDataExt*, pThis, ECX); + GET_STACK(const char*, ID, 0x4); + + auto ProcessType = [pThis](const char* ID) + { + int idx = pThis->GetBuildingTypeID(ID); + auto& DataExt = pThis->BuildingDataExts[idx]; + ppmfc::CString ImageID = ID; + if (ppmfc::CString* pArtID = Variables::Rules.TryGetString(ID, "Image")) + ImageID = *pArtID; + + auto foundation = CINI::Art->GetString(ImageID, "Foundation"); + if (_strcmpi(foundation, "Custom")) + { + DataExt.Width = atoi(foundation); + DataExt.Height = atoi(&foundation[2]); + if (DataExt.Width == 0) + DataExt.Width = 1; + if (DataExt.Height == 0) + DataExt.Height = 1; + } + else + { + // Custom, code reference Ares + DataExt.Width = CINI::Art->GetInteger(ImageID, "Foundation.X", 0); + DataExt.Height = CINI::Art->GetInteger(ImageID, "Foundation.Y", 0); + DataExt.Foundations = new std::vector; + auto ParsePoint = [](const char* str) + { + int x = 0, y = 0; + switch (sscanf_s(str, "%d,%d", &x, &y)) + { + case 0: + x = 0; + y = 0; + break; + case 1: + y = 0; + break; + case 2: + break; + default: + __assume(0); + } + return MapCoord{ x,y }; + }; + for (int i = 0; i < DataExt.Width * DataExt.Height; ++i) + { + ppmfc::CString key; + key.Format("Foundation.%d", i); + if (auto ppPoint = CINI::Art->TryGetString(ImageID, key)) + DataExt.Foundations->push_back(ParsePoint(*ppPoint)); + else + break; + } + + // Build outline draw data + DataExt.LinesToDraw = new std::vector>; + std::vector> LinesX, LinesY; + + LinesX.resize(DataExt.Width); + for (auto& l : LinesX) + l.resize(DataExt.Height + 1); + LinesY.resize(DataExt.Width + 1); + for (auto& l : LinesY) + l.resize(DataExt.Height); + + for (const auto& block : *DataExt.Foundations) + { + LinesX[block.X][block.Y] = !LinesX[block.X][block.Y]; + LinesX[block.X][block.Y + 1] = !LinesX[block.X][block.Y + 1]; + LinesY[block.X][block.Y] = !LinesY[block.X][block.Y]; + LinesY[block.X + 1][block.Y] = !LinesY[block.X + 1][block.Y]; + } + + for (size_t y = 0; y < DataExt.Height + 1; ++y) + { + size_t length = 0; + for (size_t x = 0; x < DataExt.Width; ++x) + { + if (LinesX[x][y]) + ++length; + else + { + if (!length) + continue; + MapCoord start, end; + start.X = ((x - length) - y) * 30; + start.Y = ((x - length) + y) * 15; + end.X = (x - y) * 30; + end.Y = (x + y) * 15; + DataExt.LinesToDraw->push_back(std::make_pair(start, end)); + length = 0; + } + } + if (length) + { + MapCoord start, end; + start.X = ((DataExt.Width - length) - y) * 30; + start.Y = ((DataExt.Width - length) + y) * 15; + end.X = (DataExt.Width - y) * 30; + end.Y = (DataExt.Width + y) * 15; + DataExt.LinesToDraw->push_back(std::make_pair(start, end)); + } + } + + for (size_t x = 0; x < DataExt.Width + 1; ++x) + { + size_t length = 0; + for (size_t y = 0; y < DataExt.Height; ++y) + { + if (LinesY[x][y]) + ++length; + else + { + if (!length) + continue; + MapCoord start, end; + start.X = (x - (y - length)) * 30; + start.Y = (x + (y - length)) * 15; + end.X = (x - y) * 30; + end.Y = (x + y) * 15; + DataExt.LinesToDraw->push_back(std::make_pair(start, end)); + length = 0; + } + } + if (length) + { + MapCoord start, end; + start.X = (x - (DataExt.Height - length)) * 30; + start.Y = (x + (DataExt.Height - length)) * 15; + end.X = (x - DataExt.Height) * 30; + end.Y = (x + DataExt.Height) * 15; + DataExt.LinesToDraw->push_back(std::make_pair(start, end)); + } + } + } + }; + + pThis->UpdateTypeDatas(); + if (ID) + ProcessType(ID); + else + { + pThis->BuildingDataExts.clear(); + const auto Types = Variables::Rules.GetSection("BuildingTypes"); + pThis->BuildingDataExts.resize(Types.size()); + for (auto& Type : Types) + ProcessType(Type.second); + } + + return 0; +} + +DEFINE_HOOK(4A5089, CMapData_UpdateMapFieldData_Structures_CustomFoundation, 6) +{ + GET(int, BuildingIndex, ESI); + GET_STACK(const int, X, STACK_OFFS(0x16C, 0x104)); + GET_STACK(const int, Y, STACK_OFFS(0x16C, 0x94)); + + const auto& DataExt = CMapDataExt::BuildingDataExts[BuildingIndex]; + if (!DataExt.IsCustomFoundation()) + { + for (int dy = 0; dy < DataExt.Width; ++dy) + { + for (int dx = 0; dx < DataExt.Height; ++dx) + { + const int x = X + dx; + const int y = Y + dy; + auto pCell = CMapData::Instance->GetCellAt(x, y); + pCell->Structure = R->BX(); + pCell->TypeListIndex = BuildingIndex; + CMapData::Instance->UpdateMapPreviewAt(x, y); + } + } + } + else + { + for (const auto& block : *DataExt.Foundations) + { + const int x = X + block.Y; + const int y = Y + block.X; + auto pCell = CMapData::Instance->GetCellAt(x, y); + pCell->Structure = R->BX(); + pCell->TypeListIndex = BuildingIndex; + CMapData::Instance->UpdateMapPreviewAt(x, y); + } + } + + return 0x4A57CD; +} \ No newline at end of file From 76534b920bb0c836ff333bf1de3de5af3bce908a Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Fri, 3 Mar 2023 19:15:20 +0800 Subject: [PATCH 03/30] MapValidator now validates the overlapping structures --- CHANGELOG.md | 2 + DOCUMENT.md | 1 + FA2pp | 2 +- FA2sp.vcxproj | 3 ++ FA2sp.vcxproj.filters | 15 +++++++ FA2sp/Ext/CAITriggerTypes/Body.cpp | 3 +- FA2sp/Ext/CMapValidator/Body.cpp | 20 +++++++++ FA2sp/Ext/CMapValidator/Body.h | 10 +++++ FA2sp/Ext/CMapValidator/Hooks.cpp | 65 ++++++++++++++++++++++++++++++ FA2sp/FA2sp.Constants.h | 4 +- 10 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 FA2sp/Ext/CMapValidator/Body.cpp create mode 100644 FA2sp/Ext/CMapValidator/Body.h create mode 100644 FA2sp/Ext/CMapValidator/Hooks.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 913cf00..9ff518f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,10 @@ # FINALALERT2 - SP CHANGELOG ## RELEASE 1.6.0 (2023-XX-XX) +- **ONLY YURI's REVENGE WILL BE SUPPORTED SINCE FA2SP 1.6.0** - Reimplemented file reading system - Support Ares Custom Foundation +- Map validator now checks overlapping structures, raise an error instead of warning for now. String can be modified by `MV_OverlapStructures` ## RELEASE 1.5.2 (2023-03-03) - Fixed the bug that money calculation is incorrect diff --git a/DOCUMENT.md b/DOCUMENT.md index b2d04be..e32c3da 100644 --- a/DOCUMENT.md +++ b/DOCUMENT.md @@ -367,6 +367,7 @@ NOTICE THAT UNDOREDO AND COPYPASTE HASN'T BEEN SUPPORTED YET! [CURRENTLANGUAGE-TranslationsRA2] [CURRENTLANGUAGE-Translations] ; Those four are all acceptable, just write under one of them is okey + MV_OverlapStructures = TEXT(%1 for count, %2 for X, %3 for Y) TabPages.TilePlacement = TEXT TabPages.TriggerSort = TEXT Menu.File = TEXT diff --git a/FA2pp b/FA2pp index 00b2faf..de7a476 160000 --- a/FA2pp +++ b/FA2pp @@ -1 +1 @@ -Subproject commit 00b2faf21b574bfbf9b6cc541efef8fd14ba0dfd +Subproject commit de7a4760782533670bd66eff61a2ff6d5f92cd27 diff --git a/FA2sp.vcxproj b/FA2sp.vcxproj index 1348ae5..18607f4 100644 --- a/FA2sp.vcxproj +++ b/FA2sp.vcxproj @@ -163,6 +163,8 @@ + + @@ -244,6 +246,7 @@ + diff --git a/FA2sp.vcxproj.filters b/FA2sp.vcxproj.filters index b009630..c14078b 100644 --- a/FA2sp.vcxproj.filters +++ b/FA2sp.vcxproj.filters @@ -183,6 +183,9 @@ 头文件 + + 头文件 + @@ -479,6 +482,18 @@ 源文件 + + 源文件 + + + 源文件 + + + 源文件 + + + 源文件 + diff --git a/FA2sp/Ext/CAITriggerTypes/Body.cpp b/FA2sp/Ext/CAITriggerTypes/Body.cpp index efcd58d..a79536c 100644 --- a/FA2sp/Ext/CAITriggerTypes/Body.cpp +++ b/FA2sp/Ext/CAITriggerTypes/Body.cpp @@ -6,6 +6,7 @@ #include "../../Helpers/Translations.h" #include "..\CMapData\Body.h" #include "..\CFinalSunApp\Body.h" +#include "Body.h" void CAITriggerTypesExt::ProgramStartupInit() { @@ -118,4 +119,4 @@ void CAITriggerTypesExt::OnBNCloneAITriggerClicked() this->SetDlgItemText(1010, name); // update the name huh } -} \ No newline at end of file +} diff --git a/FA2sp/Ext/CMapValidator/Body.cpp b/FA2sp/Ext/CMapValidator/Body.cpp new file mode 100644 index 0000000..c9557a5 --- /dev/null +++ b/FA2sp/Ext/CMapValidator/Body.cpp @@ -0,0 +1,20 @@ +#include "Body.h" + +#include "../../Helpers/Translations.h" + +#include + +ppmfc::CString CMapValidatorExt::FetchLanguageString(const char* Key, const char* def) +{ + ppmfc::CString buffer; + + if (!Translations::GetTranslationItem(Key, buffer)) + buffer = def; + + return buffer; +} + +void CMapValidatorExt::InsertString(const char* String, bool IsWarning) +{ + CLCResults.InsertItem(LVIF_TEXT | LVIF_IMAGE, CLCResults.GetItemCount(), String, NULL, NULL, IsWarning, NULL); +} \ No newline at end of file diff --git a/FA2sp/Ext/CMapValidator/Body.h b/FA2sp/Ext/CMapValidator/Body.h new file mode 100644 index 0000000..c401e34 --- /dev/null +++ b/FA2sp/Ext/CMapValidator/Body.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class CMapValidatorExt : public CMapValidator +{ +public: + ppmfc::CString FetchLanguageString(const char* Key, const char* def); + void InsertString(const char* String, bool IsWarning); +}; \ No newline at end of file diff --git a/FA2sp/Ext/CMapValidator/Hooks.cpp b/FA2sp/Ext/CMapValidator/Hooks.cpp new file mode 100644 index 0000000..96df39a --- /dev/null +++ b/FA2sp/Ext/CMapValidator/Hooks.cpp @@ -0,0 +1,65 @@ +#include "Body.h" + +#include + +#include + +#include "../../Helpers/STDHelpers.h" +#include "../../Ext/CMapData/Body.h" + +DEFINE_HOOK(4D19A0, CMapValidator_DoValidator_Extra, 5) +{ + GET(CMapValidatorExt*, pThis, EDI); + REF_STACK(BOOL, result, STACK_OFFS(0x200, 0x168)); + + // Check for overlapped buildings + { + std::vector Occupied; + int length = CMapData::Instance->MapWidthPlusHeight; + length *= length; + Occupied.resize(length); + + if (auto pSection = CMapData::Instance->INI.GetSection("Structures")) + { + for (const auto& [_, Data] : pSection->GetEntities()) + { + const auto splits = STDHelpers::SplitString(Data, 4); + const int Index = CMapData::Instance->GetBuildingTypeID(splits[1]); + const int Y = atoi(splits[3]); + const int X = atoi(splits[4]); + const auto& DataExt = CMapDataExt::BuildingDataExts[Index]; + + if (!DataExt.IsCustomFoundation()) + { + for (int dx = 0; dx < DataExt.Height; ++dx) + { + for (int dy = 0; dy < DataExt.Width; ++dy) + ++Occupied[CMapData::Instance->GetCoordIndex(X + dx, Y + dy)]; + } + } + else + { + for (const auto& block : *DataExt.Foundations) + ++Occupied[CMapData::Instance->GetCoordIndex(X + block.Y, Y + block.X)]; + } + } + } + + ppmfc::CString Format = pThis->FetchLanguageString( + "MV_OverlapStructures", "%1 structures overlap at (%2, %3)"); + for (int i = 0; i < length; ++i) + { + if (Occupied[i] > 1) + { + result = FALSE; + auto buffer = Format; + buffer.ReplaceNumString(1, Occupied[i]); + buffer.ReplaceNumString(2, CMapData::Instance->GetYFromCoordIndex(i)); + buffer.ReplaceNumString(3, CMapData::Instance->GetXFromCoordIndex(i)); + pThis->InsertString(buffer, false); + } + } + } + + return 0; +} \ No newline at end of file diff --git a/FA2sp/FA2sp.Constants.h b/FA2sp/FA2sp.Constants.h index 79f15b8..6257c41 100644 --- a/FA2sp/FA2sp.Constants.h +++ b/FA2sp/FA2sp.Constants.h @@ -4,8 +4,8 @@ #define __str_(x) #x #define PRODUCT_MAJOR 1 -#define PRODUCT_MINOR 5 -#define PRODUCT_REVISION 2 +#define PRODUCT_MINOR 6 +#define PRODUCT_REVISION 0 #ifdef NDEBUG #define PRODUCT_STR __str(PRODUCT_MAJOR) "." __str(PRODUCT_MINOR) "." __str(PRODUCT_REVISION) From e6288e731fdafbe08e1aa296e06e3fa409a23a60 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Fri, 3 Mar 2023 19:26:41 +0800 Subject: [PATCH 04/30] Update CMapValidator UI --- FA2sp.vcxproj | 1 + FA2sp.vcxproj.filters | 3 +++ FA2sp/UI/CMapValidator.rc | 13 +++++++++++++ 3 files changed, 17 insertions(+) create mode 100644 FA2sp/UI/CMapValidator.rc diff --git a/FA2sp.vcxproj b/FA2sp.vcxproj index 18607f4..f859aaa 100644 --- a/FA2sp.vcxproj +++ b/FA2sp.vcxproj @@ -296,6 +296,7 @@ + diff --git a/FA2sp.vcxproj.filters b/FA2sp.vcxproj.filters index c14078b..bcd72ef 100644 --- a/FA2sp.vcxproj.filters +++ b/FA2sp.vcxproj.filters @@ -562,6 +562,9 @@ 资源文件 + + 资源文件 + diff --git a/FA2sp/UI/CMapValidator.rc b/FA2sp/UI/CMapValidator.rc new file mode 100644 index 0000000..b135fbe --- /dev/null +++ b/FA2sp/UI/CMapValidator.rc @@ -0,0 +1,13 @@ +#include + +219 DIALOG 0, 0, 245, 337 +STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION +CAPTION "Map-Validator" +LANGUAGE LANG_GERMAN, SUBLANG_GERMAN +FONT 8, "MS Sans Serif" +{ + CONTROL "OK", 1, BUTTON, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 185, 316, 50, 14 + CONTROL "Cancel", 2, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 128, 316, 50, 14 + CONTROL "These possible problems were found:", 1358, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 7, 7, 225, 14 + CONTROL "List1", 1357, "SysListView32", LVS_SMALLICON | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 7, 21, 225, 284 +} From 0344613b024662e4760302bb5e411648dddbe6d8 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Tue, 7 Mar 2023 16:32:30 +0800 Subject: [PATCH 05/30] No longer sort houses by the string order --- FA2sp/UI/CHouses.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FA2sp/UI/CHouses.rc b/FA2sp/UI/CHouses.rc index 73dc520..0099cdd 100644 --- a/FA2sp/UI/CHouses.rc +++ b/FA2sp/UI/CHouses.rc @@ -8,7 +8,7 @@ FONT 8, "TAHOMA" { CONTROL "", 1091, COMBOBOX, CBS_DROPDOWNLIST | WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP, 77, 63, 263, 121 CONTROL "", -1, BUTTON, BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 1, 95, 343, 145 - CONTROL "", 1256, COMBOBOX, CBS_DROPDOWNLIST | CBS_SORT | WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP, 77, 47, 263, 68 + CONTROL "", 1256, COMBOBOX, CBS_DROPDOWNLIST | WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP, 77, 47, 263, 68 CONTROL "DESC [quite long]", 1285, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 7, 5, 333, 39 CONTROL "House:", 1258, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP, 7, 63, 64, 22 CONTROL "Add house", 1092, BUTTON, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 260, 79, 80, 15 From 06a56f94edff4451bc199bc18c9f6599c6798f6c Mon Sep 17 00:00:00 2001 From: MapleEve Date: Sat, 8 Apr 2023 13:36:25 +0800 Subject: [PATCH 06/30] Add Missing TriggerRepeatType in FA2Data (#32) --- DOCUMENT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DOCUMENT.md b/DOCUMENT.md index e32c3da..1fa5aed 100644 --- a/DOCUMENT.md +++ b/DOCUMENT.md @@ -659,4 +659,4 @@ NOTICE THAT UNDOREDO AND COPYPASTE HASN'T BEEN SUPPORTED YET! - thomassneddon provided me with vxl drawing lib and drawing stuff assistence, without I cannot draw the VXL stuffs. -- btw the code of FinalAlert 2 is really in a MESSY! Full of unnecessary constructors. I HATE IT! \ No newline at end of file +- btw the code of FinalAlert 2 is really in a MESSY! Full of unnecessary constructors. I HATE IT! From da4798d8ab9d1481519bbf3b464e23e27b12b8c9 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Mon, 20 Mar 2023 19:04:34 +0800 Subject: [PATCH 07/30] Minor adjustment --- FA2sp/Ext/CFinalSunDlg/Body.ObjectBrowserControl.cpp | 4 ++++ FA2sp/Ext/CLoading/Body.LoadObjects.cpp | 7 ++----- FA2sp/Ext/CTaskforce/Body.cpp | 3 +++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/FA2sp/Ext/CFinalSunDlg/Body.ObjectBrowserControl.cpp b/FA2sp/Ext/CFinalSunDlg/Body.ObjectBrowserControl.cpp index 5a901b5..a6ee03d 100644 --- a/FA2sp/Ext/CFinalSunDlg/Body.ObjectBrowserControl.cpp +++ b/FA2sp/Ext/CFinalSunDlg/Body.ObjectBrowserControl.cpp @@ -1001,10 +1001,14 @@ int CViewObjectsExt::GuessGenericSide(const char* pRegName, int nType) int guess = -1; for (auto& subprep : STDHelpers::SplitString(Variables::Rules.GetString("GenericPrerequisites", prep))) { + if (subprep == pRegName) // Avoid build myself crash + return -1; guess = GuessSide(subprep, GuessType(subprep)); if (guess != -1) return guess; } + if (prep == pRegName) // Avoid build myself crash + return -1; guess = GuessSide(prep, GuessType(prep)); if (guess != -1) return guess; diff --git a/FA2sp/Ext/CLoading/Body.LoadObjects.cpp b/FA2sp/Ext/CLoading/Body.LoadObjects.cpp index 474c153..c50832c 100644 --- a/FA2sp/Ext/CLoading/Body.LoadObjects.cpp +++ b/FA2sp/Ext/CLoading/Body.LoadObjects.cpp @@ -601,11 +601,8 @@ void CLoadingExt::LoadVehicleOrAircraft(ppmfc::CString ID) framesToRead[i] = nStartWalkFrame + (i * facingCount / 8) * nWalkFrames; } } - // fix from cmcc - int temp = framesToRead[0]; - for (int i = 0; i < 7; i++) - framesToRead[i] = framesToRead[i + 1]; - framesToRead[7] = temp; + + std::rotate(framesToRead, framesToRead + 1, framesToRead + 8); ppmfc::CString FileName = ImageID + ".shp"; int nMix = this->SearchFile(FileName); diff --git a/FA2sp/Ext/CTaskforce/Body.cpp b/FA2sp/Ext/CTaskforce/Body.cpp index b765efb..205c53f 100644 --- a/FA2sp/Ext/CTaskforce/Body.cpp +++ b/FA2sp/Ext/CTaskforce/Body.cpp @@ -71,7 +71,10 @@ BOOL CTaskForceExt::PreTranslateMessageExt(MSG* pMsg) case WM_LBUTTONUP: { if (pMsg->hwnd == this->GetDlgItem(50807)->GetSafeHwnd()) + { this->OnBNCloneTaskforceClicked(); + return TRUE; + } } } From c7897fa2523d16ddb8207a56445bb789cef6cc18 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Sat, 8 Apr 2023 13:37:05 +0800 Subject: [PATCH 08/30] Implement LogicMissingParam map validator check --- CHANGELOG.md | 1 + DOCUMENT.md | 1 + FA2sp/Ext/CMapValidator/Body.cpp | 80 +++++++++++++++++++++++++++++++ FA2sp/Ext/CMapValidator/Body.h | 3 ++ FA2sp/Ext/CMapValidator/Hooks.cpp | 52 +------------------- 5 files changed, 87 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ff518f..1451d69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Reimplemented file reading system - Support Ares Custom Foundation - Map validator now checks overlapping structures, raise an error instead of warning for now. String can be modified by `MV_OverlapStructures` +- Map validator now checks missing logic params, raise an error instead of warning for now. String can be modified by `MV_LogicMissingParams` ## RELEASE 1.5.2 (2023-03-03) - Fixed the bug that money calculation is incorrect diff --git a/DOCUMENT.md b/DOCUMENT.md index 1fa5aed..9788b5e 100644 --- a/DOCUMENT.md +++ b/DOCUMENT.md @@ -368,6 +368,7 @@ NOTICE THAT UNDOREDO AND COPYPASTE HASN'T BEEN SUPPORTED YET! [CURRENTLANGUAGE-Translations] ; Those four are all acceptable, just write under one of them is okey MV_OverlapStructures = TEXT(%1 for count, %2 for X, %3 for Y) + MV_LogicMissingParams = TEXT(%1 for section, %2 for key) TabPages.TilePlacement = TEXT TabPages.TriggerSort = TEXT Menu.File = TEXT diff --git a/FA2sp/Ext/CMapValidator/Body.cpp b/FA2sp/Ext/CMapValidator/Body.cpp index c9557a5..5bb1d64 100644 --- a/FA2sp/Ext/CMapValidator/Body.cpp +++ b/FA2sp/Ext/CMapValidator/Body.cpp @@ -1,9 +1,89 @@ #include "Body.h" #include "../../Helpers/Translations.h" +#include "../../Helpers/STDHelpers.h" +#include "../../Ext/CMapData/Body.h" #include +void CMapValidatorExt::ValidateStructureOverlapping(BOOL& result) +{ + std::vector Occupied; + int length = CMapData::Instance->MapWidthPlusHeight; + length *= length; + Occupied.resize(length); + + if (auto pSection = CMapData::Instance->INI.GetSection("Structures")) + { + for (const auto& [_, Data] : pSection->GetEntities()) + { + const auto splits = STDHelpers::SplitString(Data, 4); + const int Index = CMapData::Instance->GetBuildingTypeID(splits[1]); + const int Y = atoi(splits[3]); + const int X = atoi(splits[4]); + const auto& DataExt = CMapDataExt::BuildingDataExts[Index]; + + if (!DataExt.IsCustomFoundation()) + { + for (int dx = 0; dx < DataExt.Height; ++dx) + { + for (int dy = 0; dy < DataExt.Width; ++dy) + ++Occupied[CMapData::Instance->GetCoordIndex(X + dx, Y + dy)]; + } + } + else + { + for (const auto& block : *DataExt.Foundations) + ++Occupied[CMapData::Instance->GetCoordIndex(X + block.Y, Y + block.X)]; + } + } + } + + ppmfc::CString Format = this->FetchLanguageString( + "MV_OverlapStructures", "%1 structures overlap at (%2, %3)"); + for (int i = 0; i < length; ++i) + { + if (Occupied[i] > 1) + { + result = FALSE; + auto buffer = Format; + buffer.ReplaceNumString(1, Occupied[i]); + buffer.ReplaceNumString(2, CMapData::Instance->GetYFromCoordIndex(i)); + buffer.ReplaceNumString(3, CMapData::Instance->GetXFromCoordIndex(i)); + this->InsertString(buffer, false); + } + } +} + +void CMapValidatorExt::ValidateMissingParams(BOOL& result) +{ + auto ValidateSection = [&](const char* section) + { + if (auto pSection = CMapData::Instance->INI.GetSection(section)) + { + ppmfc::CString Format = this->FetchLanguageString( + "MV_LogicMissingParams", "%1 - %2 may has a missing param! Please have a check on it."); + Format.ReplaceNumString(1, section); + + for (const auto& [key, value] : pSection->GetEntities()) + { + if (value.Find(",,") != -1) // has missing param! + { + result = FALSE; + auto tmp = Format; + tmp.ReplaceNumString(2, key); + InsertString(tmp, false); + } + } + } + }; + + ValidateSection("Triggers"); + ValidateSection("Actions"); + ValidateSection("Events"); + ValidateSection("Tags"); +} + ppmfc::CString CMapValidatorExt::FetchLanguageString(const char* Key, const char* def) { ppmfc::CString buffer; diff --git a/FA2sp/Ext/CMapValidator/Body.h b/FA2sp/Ext/CMapValidator/Body.h index c401e34..590662a 100644 --- a/FA2sp/Ext/CMapValidator/Body.h +++ b/FA2sp/Ext/CMapValidator/Body.h @@ -5,6 +5,9 @@ class CMapValidatorExt : public CMapValidator { public: + void ValidateStructureOverlapping(BOOL& result); + void ValidateMissingParams(BOOL& result); + ppmfc::CString FetchLanguageString(const char* Key, const char* def); void InsertString(const char* String, bool IsWarning); }; \ No newline at end of file diff --git a/FA2sp/Ext/CMapValidator/Hooks.cpp b/FA2sp/Ext/CMapValidator/Hooks.cpp index 96df39a..e0e7c00 100644 --- a/FA2sp/Ext/CMapValidator/Hooks.cpp +++ b/FA2sp/Ext/CMapValidator/Hooks.cpp @@ -4,62 +4,14 @@ #include -#include "../../Helpers/STDHelpers.h" -#include "../../Ext/CMapData/Body.h" - DEFINE_HOOK(4D19A0, CMapValidator_DoValidator_Extra, 5) { GET(CMapValidatorExt*, pThis, EDI); REF_STACK(BOOL, result, STACK_OFFS(0x200, 0x168)); - // Check for overlapped buildings - { - std::vector Occupied; - int length = CMapData::Instance->MapWidthPlusHeight; - length *= length; - Occupied.resize(length); - - if (auto pSection = CMapData::Instance->INI.GetSection("Structures")) - { - for (const auto& [_, Data] : pSection->GetEntities()) - { - const auto splits = STDHelpers::SplitString(Data, 4); - const int Index = CMapData::Instance->GetBuildingTypeID(splits[1]); - const int Y = atoi(splits[3]); - const int X = atoi(splits[4]); - const auto& DataExt = CMapDataExt::BuildingDataExts[Index]; - - if (!DataExt.IsCustomFoundation()) - { - for (int dx = 0; dx < DataExt.Height; ++dx) - { - for (int dy = 0; dy < DataExt.Width; ++dy) - ++Occupied[CMapData::Instance->GetCoordIndex(X + dx, Y + dy)]; - } - } - else - { - for (const auto& block : *DataExt.Foundations) - ++Occupied[CMapData::Instance->GetCoordIndex(X + block.Y, Y + block.X)]; - } - } - } + pThis->ValidateStructureOverlapping(result); + pThis->ValidateMissingParams(result); - ppmfc::CString Format = pThis->FetchLanguageString( - "MV_OverlapStructures", "%1 structures overlap at (%2, %3)"); - for (int i = 0; i < length; ++i) - { - if (Occupied[i] > 1) - { - result = FALSE; - auto buffer = Format; - buffer.ReplaceNumString(1, Occupied[i]); - buffer.ReplaceNumString(2, CMapData::Instance->GetYFromCoordIndex(i)); - buffer.ReplaceNumString(3, CMapData::Instance->GetXFromCoordIndex(i)); - pThis->InsertString(buffer, false); - } - } - } return 0; } \ No newline at end of file From 619b52a6cd9efa7e132b9a00335cc76c29839679 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Sat, 8 Apr 2023 14:02:53 +0800 Subject: [PATCH 09/30] Fix a potential crash by replacing the ext container ...though we dunno why the fucking index work lead to this --- FA2sp/Ext/CMapData/Body.cpp | 2 +- FA2sp/Ext/CMapData/Body.h | 3 ++- FA2sp/Ext/CMapData/Hooks.BuildingTypeDatas.cpp | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/FA2sp/Ext/CMapData/Body.cpp b/FA2sp/Ext/CMapData/Body.cpp index e2a3beb..0f6aad5 100644 --- a/FA2sp/Ext/CMapData/Body.cpp +++ b/FA2sp/Ext/CMapData/Body.cpp @@ -11,7 +11,7 @@ int CMapDataExt::OreValue[4] { -1,-1,-1,-1 }; std::vector CMapDataExt::OverlayTypeDatas; -std::vector CMapDataExt::BuildingDataExts; +std::unordered_map CMapDataExt::BuildingDataExts; int CMapDataExt::GetOreValue(unsigned char nOverlay, unsigned char nOverlayData) { diff --git a/FA2sp/Ext/CMapData/Body.h b/FA2sp/Ext/CMapData/Body.h index 386621a..b49f621 100644 --- a/FA2sp/Ext/CMapData/Body.h +++ b/FA2sp/Ext/CMapData/Body.h @@ -2,6 +2,7 @@ #include +#include #include struct OverlayTypeData @@ -48,7 +49,7 @@ class CMapDataExt : public CMapData void InitializeBuildingTypesExt(const char* ID); - static std::vector BuildingDataExts; + static std::unordered_map BuildingDataExts; static std::vector OverlayTypeDatas; static int OreValue[4]; }; \ No newline at end of file diff --git a/FA2sp/Ext/CMapData/Hooks.BuildingTypeDatas.cpp b/FA2sp/Ext/CMapData/Hooks.BuildingTypeDatas.cpp index e3ef5ca..728492e 100644 --- a/FA2sp/Ext/CMapData/Hooks.BuildingTypeDatas.cpp +++ b/FA2sp/Ext/CMapData/Hooks.BuildingTypeDatas.cpp @@ -154,7 +154,6 @@ DEFINE_HOOK(4B5460, CMapData_InitializeBuildingTypes, 7) { pThis->BuildingDataExts.clear(); const auto Types = Variables::Rules.GetSection("BuildingTypes"); - pThis->BuildingDataExts.resize(Types.size()); for (auto& Type : Types) ProcessType(Type.second); } From 2baf35f3864ff0038ed62704acea60e7e3600261 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Sun, 9 Apr 2023 23:28:37 +0800 Subject: [PATCH 10/30] Fix stupid marble mix reading --- FA2sp/Ext/CLoading/Body.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/FA2sp/Ext/CLoading/Body.cpp b/FA2sp/Ext/CLoading/Body.cpp index c709f6e..0b7925f 100644 --- a/FA2sp/Ext/CLoading/Body.cpp +++ b/FA2sp/Ext/CLoading/Body.cpp @@ -167,7 +167,23 @@ bool CLoadingExt::InitMixFilesFix() LoadMixFile("LUN.MIX"); LoadMixFile("LUNARMD.MIX"); - LoadMixFile("MARBLE.MIX"); + if (LoadMixFile("MARBLE.MIX")) + CFinalSunApp::Instance->MarbleLoaded = TRUE; + else + { + ppmfc::CString FullPath = CFinalSunApp::ExePath + "MARBLE.MIX"; + int result = CMixFile::Open(FullPath, 0); + if (result) + { + Logger::Raw("[MixLoader] %04d - %s loaded.\n", result, FullPath); + CFinalSunApp::Instance->MarbleLoaded = TRUE; + } + else + { + CFinalSunApp::Instance->MarbleLoaded = FALSE; + ::MessageBox(NULL, "Failed to load marble.mix! Framework mode won't be able to use!", "FA2sp", MB_OK | MB_ICONEXCLAMATION); + } + } return true; } \ No newline at end of file From 314ab149902f987814015f687e2f6d084ef90bca Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Sun, 9 Apr 2023 23:31:05 +0800 Subject: [PATCH 11/30] Update README --- CHANGELOG.md | 2 +- README.md | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1451d69..c5e5a4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # FINALALERT2 - SP CHANGELOG -## RELEASE 1.6.0 (2023-XX-XX) +## RELEASE 1.6.0 (2023-04-XX) - **ONLY YURI's REVENGE WILL BE SUPPORTED SINCE FA2SP 1.6.0** - Reimplemented file reading system - Support Ares Custom Foundation diff --git a/README.md b/README.md index b1beb8a..2ab5bf5 100644 --- a/README.md +++ b/README.md @@ -21,12 +21,12 @@ You can choose one of the following: Changelog --------- -You can check the full changelog [here](https://github.com/secsome/FA2sp/blob/master/CHANGELOG.md). +You can check the full changelog [here](./CHANGELOG.md). Document --------- -You can check the document [here](https://github.com/secsome/FA2sp/blob/master/DOCUMENT.md). +You can check the document [here](./DOCUMENT.md). -[Unexplored](https://github.com/secsome/FA2sp/blob/master/UNEXPLORED.md) +[Unexplored](./UNEXPLORED.md) --------- \ No newline at end of file From c6928b21c621899634d7dfbf26bc8980dec27eb2 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Sun, 9 Apr 2023 23:38:59 +0800 Subject: [PATCH 12/30] Update Horizonal DialogBar Height --- FA2sp/UI/CTileBrowserFrame.DialogBar.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FA2sp/UI/CTileBrowserFrame.DialogBar.rc b/FA2sp/UI/CTileBrowserFrame.DialogBar.rc index 0b1437c..09c2a0c 100644 --- a/FA2sp/UI/CTileBrowserFrame.DialogBar.rc +++ b/FA2sp/UI/CTileBrowserFrame.DialogBar.rc @@ -1,7 +1,7 @@ #include "Windows.h" // Horizonal -227 DIALOG 0, 0, 480, 29 +227 DIALOG 0, 0, 587, 56 STYLE DS_SETFONT | WS_CHILD LANGUAGE LANG_GERMAN, SUBLANG_GERMAN FONT 8, "MS Sans Serif" From 95b034f069ab86b66c2908b300454f7e5f500fd1 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Mon, 10 Apr 2023 13:44:41 +0800 Subject: [PATCH 13/30] Reset lighting to None when loading/creating a new map --- FA2sp/Miscs/Hooks.Palette.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/FA2sp/Miscs/Hooks.Palette.cpp b/FA2sp/Miscs/Hooks.Palette.cpp index c7fef0d..90df518 100644 --- a/FA2sp/Miscs/Hooks.Palette.cpp +++ b/FA2sp/Miscs/Hooks.Palette.cpp @@ -5,15 +5,21 @@ #include "Palettes.h" +#include "../Ext/CFinalSunDlg/Body.h" + DEFINE_HOOK(4B9F7A, CreateMap_ClearUp_Pals, 5) { + CFinalSunDlgExt::CurrentLighting = 31000; + CheckMenuRadioItem(*CFinalSunDlg::Instance->GetMenu(), 31000, 31003, 31000, MF_UNCHECKED); PalettesManager::Release(); - + return 0; } DEFINE_HOOK(49D2C0, LoadMap_ClearUp_Pals, 5) { + CFinalSunDlgExt::CurrentLighting = 31000; + CheckMenuRadioItem(*CFinalSunDlg::Instance->GetMenu(), 31000, 31003, 31000, MF_UNCHECKED); PalettesManager::Release(); return 0; From 9209ba2247baa3dcaedb5ab3416db510c6daddbf Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Sun, 23 Apr 2023 19:17:52 +0800 Subject: [PATCH 14/30] Implement StructureOverlappingCheckIgnorance for stupid lightposts --- CHANGELOG.md | 3 +++ DOCUMENT.md | 7 +++++++ FA2sp.vcxproj | 2 ++ FA2sp/Ext/CMapValidator/Body.cpp | 7 +++++++ FA2sp/Ext/CMapValidator/Body.h | 4 ++++ FA2sp/Ext/CMapValidator/Hooks.cpp | 18 ++++++++++++++++++ 6 files changed, 41 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5e5a4b..e0058a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,10 @@ - Reimplemented file reading system - Support Ares Custom Foundation - Map validator now checks overlapping structures, raise an error instead of warning for now. String can be modified by `MV_OverlapStructures` + - **You should manually add buildings that need to be ignored in `FADATA.INI` section `[StructureOverlappingCheckIgnorance]`, as it is raising an error, and you probably want some lightposts to be ignored.** + - The Ignorance list might be removed in the future, when I can finally figure out what is that stupid problem - Map validator now checks missing logic params, raise an error instead of warning for now. String can be modified by `MV_LogicMissingParams` +- Fixed the bug that lighting is not reset correctly when loading/creating a map ## RELEASE 1.5.2 (2023-03-03) - Fixed the bug that money calculation is incorrect diff --git a/DOCUMENT.md b/DOCUMENT.md index 9788b5e..340f672 100644 --- a/DOCUMENT.md +++ b/DOCUMENT.md @@ -360,6 +360,13 @@ NOTICE THAT UNDOREDO AND COPYPASTE HASN'T BEEN SUPPORTED YET! 70=70 - Disable Weapons (Ares 3.0 Only),20,0,1,Disables the ability of all team members to fire for a number of FRAMES. ``` - `[VehicleVoxelBarrelsRA2]` + - `[StructureOverlappingCheckIgnorance]` + - id = BuildingRegName + ```ini + [StructureOverlappingCheckIgnorance] + Index = RegName + ; Like 0=INORANLAMP, value must be a valid building regname + ``` - `FALanguage.ini` ```ini [CURRENTLANGUAGE-StringsRA2] diff --git a/FA2sp.vcxproj b/FA2sp.vcxproj index f859aaa..6d3b679 100644 --- a/FA2sp.vcxproj +++ b/FA2sp.vcxproj @@ -77,6 +77,7 @@ stdcpplatest false MaxSpeed + false Console @@ -112,6 +113,7 @@ stdcpplatest false Disabled + false Console diff --git a/FA2sp/Ext/CMapValidator/Body.cpp b/FA2sp/Ext/CMapValidator/Body.cpp index 5bb1d64..5c2091d 100644 --- a/FA2sp/Ext/CMapValidator/Body.cpp +++ b/FA2sp/Ext/CMapValidator/Body.cpp @@ -6,6 +6,8 @@ #include +std::unordered_set CMapValidatorExt::StructureOverlappingIgnorance; + void CMapValidatorExt::ValidateStructureOverlapping(BOOL& result) { std::vector Occupied; @@ -18,6 +20,11 @@ void CMapValidatorExt::ValidateStructureOverlapping(BOOL& result) for (const auto& [_, Data] : pSection->GetEntities()) { const auto splits = STDHelpers::SplitString(Data, 4); + + // In the list, ignore it. + if (StructureOverlappingIgnorance.count(std::string{splits[1].m_pchData})) + continue; + const int Index = CMapData::Instance->GetBuildingTypeID(splits[1]); const int Y = atoi(splits[3]); const int X = atoi(splits[4]); diff --git a/FA2sp/Ext/CMapValidator/Body.h b/FA2sp/Ext/CMapValidator/Body.h index 590662a..cce57ba 100644 --- a/FA2sp/Ext/CMapValidator/Body.h +++ b/FA2sp/Ext/CMapValidator/Body.h @@ -2,6 +2,8 @@ #include +#include + class CMapValidatorExt : public CMapValidator { public: @@ -10,4 +12,6 @@ class CMapValidatorExt : public CMapValidator ppmfc::CString FetchLanguageString(const char* Key, const char* def); void InsertString(const char* String, bool IsWarning); + + static std::unordered_set StructureOverlappingIgnorance; }; \ No newline at end of file diff --git a/FA2sp/Ext/CMapValidator/Hooks.cpp b/FA2sp/Ext/CMapValidator/Hooks.cpp index e0e7c00..05750f9 100644 --- a/FA2sp/Ext/CMapValidator/Hooks.cpp +++ b/FA2sp/Ext/CMapValidator/Hooks.cpp @@ -13,5 +13,23 @@ DEFINE_HOOK(4D19A0, CMapValidator_DoValidator_Extra, 5) pThis->ValidateMissingParams(result); + return 0; +} + +DEFINE_HOOK(4CEA50, CMapValidator_CTOR, 7) +{ + if (auto pSection = CINI::FAData->GetSection("StructureOverlappingCheckIgnorance")) + { + for (const auto& [key, value] : pSection->GetEntities()) + CMapValidatorExt::StructureOverlappingIgnorance.emplace(std::string{value.m_pchData}); + } + + return 0; +} + +DEFINE_HOOK(426A60, CMapValidator_DTOR, 7) +{ + CMapValidatorExt::StructureOverlappingIgnorance.clear(); + return 0; } \ No newline at end of file From be97bba2dc3d464cfdf4bf2695e10a5c9316d3d0 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Sun, 23 Apr 2023 19:27:29 +0800 Subject: [PATCH 15/30] Display overlapped building detail --- FA2sp/Ext/CMapValidator/Body.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/FA2sp/Ext/CMapValidator/Body.cpp b/FA2sp/Ext/CMapValidator/Body.cpp index 5c2091d..746501e 100644 --- a/FA2sp/Ext/CMapValidator/Body.cpp +++ b/FA2sp/Ext/CMapValidator/Body.cpp @@ -10,7 +10,7 @@ std::unordered_set CMapValidatorExt::StructureOverlappingIgnorance; void CMapValidatorExt::ValidateStructureOverlapping(BOOL& result) { - std::vector Occupied; + std::vector> Occupied; int length = CMapData::Instance->MapWidthPlusHeight; length *= length; Occupied.resize(length); @@ -22,7 +22,7 @@ void CMapValidatorExt::ValidateStructureOverlapping(BOOL& result) const auto splits = STDHelpers::SplitString(Data, 4); // In the list, ignore it. - if (StructureOverlappingIgnorance.count(std::string{splits[1].m_pchData})) + if (StructureOverlappingIgnorance.count(splits[1].m_pchData)) continue; const int Index = CMapData::Instance->GetBuildingTypeID(splits[1]); @@ -35,28 +35,34 @@ void CMapValidatorExt::ValidateStructureOverlapping(BOOL& result) for (int dx = 0; dx < DataExt.Height; ++dx) { for (int dy = 0; dy < DataExt.Width; ++dy) - ++Occupied[CMapData::Instance->GetCoordIndex(X + dx, Y + dy)]; + Occupied[CMapData::Instance->GetCoordIndex(X + dx, Y + dy)].emplace_back(splits[1].m_pchData); } } else { for (const auto& block : *DataExt.Foundations) - ++Occupied[CMapData::Instance->GetCoordIndex(X + block.Y, Y + block.X)]; + Occupied[CMapData::Instance->GetCoordIndex(X + block.Y, Y + block.X)].emplace_back(splits[1].m_pchData); } } } ppmfc::CString Format = this->FetchLanguageString( - "MV_OverlapStructures", "%1 structures overlap at (%2, %3)"); + "MV_OverlapStructures", "%1 structures overlap at (%2, %3): "); for (int i = 0; i < length; ++i) { - if (Occupied[i] > 1) + if (Occupied[i].size() > 1) { result = FALSE; auto buffer = Format; - buffer.ReplaceNumString(1, Occupied[i]); + buffer.ReplaceNumString(1, Occupied[i].size()); buffer.ReplaceNumString(2, CMapData::Instance->GetYFromCoordIndex(i)); buffer.ReplaceNumString(3, CMapData::Instance->GetXFromCoordIndex(i)); + for (size_t k = 0; k < Occupied[i].size() - 1; ++k) + { + buffer += Occupied[i][k].c_str(); + buffer += ", "; + } + buffer += Occupied[i].back().c_str(); this->InsertString(buffer, false); } } From 799a78e9eee340fee9c6a707d794fe7bfba1f364 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Mon, 24 Apr 2023 10:46:14 +0800 Subject: [PATCH 16/30] Fixed the problem that Basenode & Smudge not been placed correctly... when resizing the map --- FA2pp | 2 +- FA2sp/Ext/CMapValidator/Hooks.cpp | 1 - FA2sp/Miscs/Hooks.BugFixes.cpp | 66 +++++++++++++++++++++++++++---- 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/FA2pp b/FA2pp index de7a476..6cb80ff 160000 --- a/FA2pp +++ b/FA2pp @@ -1 +1 @@ -Subproject commit de7a4760782533670bd66eff61a2ff6d5f92cd27 +Subproject commit 6cb80ff52a27cd23a1f8a2e4ea1b0fa6d46a6e71 diff --git a/FA2sp/Ext/CMapValidator/Hooks.cpp b/FA2sp/Ext/CMapValidator/Hooks.cpp index 05750f9..0215b47 100644 --- a/FA2sp/Ext/CMapValidator/Hooks.cpp +++ b/FA2sp/Ext/CMapValidator/Hooks.cpp @@ -12,7 +12,6 @@ DEFINE_HOOK(4D19A0, CMapValidator_DoValidator_Extra, 5) pThis->ValidateStructureOverlapping(result); pThis->ValidateMissingParams(result); - return 0; } diff --git a/FA2sp/Miscs/Hooks.BugFixes.cpp b/FA2sp/Miscs/Hooks.BugFixes.cpp index 8b4ff1a..e73a77c 100644 --- a/FA2sp/Miscs/Hooks.BugFixes.cpp +++ b/FA2sp/Miscs/Hooks.BugFixes.cpp @@ -14,6 +14,8 @@ #include "../FA2sp.h" +#include "../Helpers/STDHelpers.h" + // FA2 will no longer automatically change the extension of map DEFINE_HOOK(42700A, CFinalSunDlg_SaveMap_Extension, 9) { @@ -151,11 +153,59 @@ DEFINE_HOOK(4564F0, CInputMessageBox_OnOK, 7) // // return 0; //} -// -//DEFINE_HOOK(4C76C6, CMapData_ResizeMap_PositionFix_SyncToINI, 5) -//{ -// CMapData::Instance->UpdateMapFieldData_Smudge(true); -// CMapData::Instance->UpdateMapFieldData_House(true); -// -// return 0; -//} \ No newline at end of file + +DEFINE_HOOK(4C76C6, CMapData_ResizeMap_PositionFix_SmudgeAndBasenode, 5) +{ + GET_STACK(int, XOFF, STACK_OFFS(0x1C4, 0x194)); + GET_STACK(int, YOFF, STACK_OFFS(0x1C4, 0x19C)); + + ppmfc::CString buffer; + + { + std::vector> smudges; + for (size_t i = 0; i < CMapData::Instance->SmudgeDatas.size();++i) + { + const auto& data = CMapData::Instance->SmudgeDatas[i]; + buffer.Format("%d", i); + smudges.emplace_back(buffer, data.TypeID, data.X + XOFF, data.Y + YOFF); + } + + CMapData::Instance->INI.DeleteSection("Smudge"); + if (auto pSection = CMapData::Instance->INI.AddSection("Smudge")) + { + for (const auto& [key, id, x, y] : smudges) + { + buffer.Format("%s,%d,%d,0", id, x, y); + CMapData::Instance->INI.WriteString(pSection, key, buffer); + } + } + } + CMapData::Instance->UpdateFieldSmudgeData(false); + + for (const auto& [_, house] : Variables::Rules.GetSection("Houses")) + { + if (auto pSection = CMapData::Instance->INI.GetSection(house)) + { + const int nodeCount = CMapData::Instance->INI.GetInteger(pSection, "NodeCount"); + + std::vector> nodes; + for (int i = 0; i < nodeCount; ++i) + { + buffer.Format("%03d", i); + const auto value = CMapData::Instance->INI.GetString(pSection, buffer); + const auto splits = STDHelpers::SplitString(value); + nodes.emplace_back(buffer, splits[0], atoi(splits[1]) + XOFF, atoi(splits[2]) + YOFF); + } + + for (const auto& [key, id, x, y] : nodes) + { + buffer.Format("%s,%d,%d", id, x, y); + // CMapData::Instance->INI.DeleteKey(pSection, key); // useless + CMapData::Instance->INI.WriteString(pSection, key, buffer); + } + } + } + CMapData::Instance->UpdateFieldBasenodeData(false); + + return 0; +} \ No newline at end of file From 7d2a00252ea584d361fd162082308ce00a0e80b7 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Mon, 24 Apr 2023 22:04:18 +0800 Subject: [PATCH 17/30] Update CHANGELOG --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0058a3..061fb01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # FINALALERT2 - SP CHANGELOG -## RELEASE 1.6.0 (2023-04-XX) +## RELEASE 1.6.0 (2023-04-30) - **ONLY YURI's REVENGE WILL BE SUPPORTED SINCE FA2SP 1.6.0** - Reimplemented file reading system - Support Ares Custom Foundation @@ -9,6 +9,7 @@ - The Ignorance list might be removed in the future, when I can finally figure out what is that stupid problem - Map validator now checks missing logic params, raise an error instead of warning for now. String can be modified by `MV_LogicMissingParams` - Fixed the bug that lighting is not reset correctly when loading/creating a map +- Fixed the bug that smudges and basenodes drifting away when resizing the map ## RELEASE 1.5.2 (2023-03-03) - Fixed the bug that money calculation is incorrect From 8a4058dcc2dc00310150eb17b790dbb7d1c89bd7 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Mon, 24 Apr 2023 22:16:20 +0800 Subject: [PATCH 18/30] Remove unused codes --- FA2pp | 2 +- FA2sp/Ext/CTriggerOption/Body.cpp | 17 ++++++++++++++--- FA2sp/Ext/CTriggerOption/Hooks.cpp | 2 +- FA2sp/Miscs/Hooks.BugFixes.cpp | 14 +------------- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/FA2pp b/FA2pp index 6cb80ff..375a8d2 160000 --- a/FA2pp +++ b/FA2pp @@ -1 +1 @@ -Subproject commit 6cb80ff52a27cd23a1f8a2e4ea1b0fa6d46a6e71 +Subproject commit 375a8d2349fc22b5b63e42ef4791cd8f71576573 diff --git a/FA2sp/Ext/CTriggerOption/Body.cpp b/FA2sp/Ext/CTriggerOption/Body.cpp index be23aec..c52dac3 100644 --- a/FA2sp/Ext/CTriggerOption/Body.cpp +++ b/FA2sp/Ext/CTriggerOption/Body.cpp @@ -5,7 +5,11 @@ void CTriggerOptionExt::ProgramStartupInit() { + // Name Update, EN_CHANGE -> EN_KILLFOCUS, reduce lag + // RunTime::ResetMemoryContentAt(0x597D60 + 0x4, &RunTime::Messages::EDIT_KILLFOCUS, 4); + RunTime::ResetMemoryContentAt(0x597F24, &CTriggerOptionExt::OnInitDialogExt); + // RunTime::ResetMemoryContentAt(0x597EF8, &CTriggerOptionExt::PreTranslateMessageExt); } BOOL CTriggerOptionExt::OnInitDialogExt() @@ -30,10 +34,17 @@ BOOL CTriggerOptionExt::OnInitDialogExt() BOOL CTriggerOptionExt::PreTranslateMessageExt(MSG* pMsg) { - switch (pMsg->message) { + // Original tooltip + this->CTTCHouse.RelayEvent(pMsg); - default: - break; + if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN) + { + if (::GetDlgCtrlID(pMsg->hwnd) == 1010) + { + this->OnETNameEdited(); + return TRUE; + } } + return this->ppmfc::CDialog::PreTranslateMessage(pMsg); } \ No newline at end of file diff --git a/FA2sp/Ext/CTriggerOption/Hooks.cpp b/FA2sp/Ext/CTriggerOption/Hooks.cpp index 911d938..e914f9c 100644 --- a/FA2sp/Ext/CTriggerOption/Hooks.cpp +++ b/FA2sp/Ext/CTriggerOption/Hooks.cpp @@ -16,4 +16,4 @@ DEFINE_HOOK(502E66, CTriggerOption_OnInitDialog_RepeatTypeFix, 7) pThis->CCBRepeatType.AddString(Translations::TranslateOrDefault("TriggerRepeatType.RepeatingOr", "2 - Repeating OR")); return 0; -} \ No newline at end of file +} diff --git a/FA2sp/Miscs/Hooks.BugFixes.cpp b/FA2sp/Miscs/Hooks.BugFixes.cpp index e73a77c..af73a09 100644 --- a/FA2sp/Miscs/Hooks.BugFixes.cpp +++ b/FA2sp/Miscs/Hooks.BugFixes.cpp @@ -142,18 +142,6 @@ DEFINE_HOOK(4564F0, CInputMessageBox_OnOK, 7) return 0x4565A5; } -//DEFINE_HOOK(4C61C5, CMapData_ResizeMap_PositionFix_MoveOnField, 5) -//{ -// GET(CellData*, pCell, EAX); -// auto const pSrc = CONTAINING_RECORD(R->ECX(), CellData, Flag); -// -// pCell->Smudge = pSrc->Smudge; -// pCell->SmudgeType = pSrc->SmudgeType; -// pCell->BaseNode = pSrc->BaseNode; -// -// return 0; -//} - DEFINE_HOOK(4C76C6, CMapData_ResizeMap_PositionFix_SmudgeAndBasenode, 5) { GET_STACK(int, XOFF, STACK_OFFS(0x1C4, 0x194)); @@ -169,7 +157,7 @@ DEFINE_HOOK(4C76C6, CMapData_ResizeMap_PositionFix_SmudgeAndBasenode, 5) buffer.Format("%d", i); smudges.emplace_back(buffer, data.TypeID, data.X + XOFF, data.Y + YOFF); } - + CMapData::Instance->INI.DeleteSection("Smudge"); if (auto pSection = CMapData::Instance->INI.AddSection("Smudge")) { From 54974e4ce4ed035ec674b78a2a02edd56811a211 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Wed, 26 Apr 2023 11:55:40 +0800 Subject: [PATCH 19/30] Fix typo --- CHANGELOG.md | 2 +- DOCUMENT.md | 2 +- FA2sp/Ext/CMapValidator/Body.cpp | 4 ++-- FA2sp/Ext/CMapValidator/Body.h | 2 +- FA2sp/Ext/CMapValidator/Hooks.cpp | 6 +++--- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 061fb01..9a708eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ - Reimplemented file reading system - Support Ares Custom Foundation - Map validator now checks overlapping structures, raise an error instead of warning for now. String can be modified by `MV_OverlapStructures` - - **You should manually add buildings that need to be ignored in `FADATA.INI` section `[StructureOverlappingCheckIgnorance]`, as it is raising an error, and you probably want some lightposts to be ignored.** + - **You should manually add buildings that need to be ignored in `FADATA.INI` section `[StructureOverlappingCheckIgnores]`, as it is raising an error, and you probably want some lightposts to be ignored.** - The Ignorance list might be removed in the future, when I can finally figure out what is that stupid problem - Map validator now checks missing logic params, raise an error instead of warning for now. String can be modified by `MV_LogicMissingParams` - Fixed the bug that lighting is not reset correctly when loading/creating a map diff --git a/DOCUMENT.md b/DOCUMENT.md index 340f672..85e5127 100644 --- a/DOCUMENT.md +++ b/DOCUMENT.md @@ -360,7 +360,7 @@ NOTICE THAT UNDOREDO AND COPYPASTE HASN'T BEEN SUPPORTED YET! 70=70 - Disable Weapons (Ares 3.0 Only),20,0,1,Disables the ability of all team members to fire for a number of FRAMES. ``` - `[VehicleVoxelBarrelsRA2]` - - `[StructureOverlappingCheckIgnorance]` + - `[StructureOverlappingCheckIgnores]` - id = BuildingRegName ```ini [StructureOverlappingCheckIgnorance] diff --git a/FA2sp/Ext/CMapValidator/Body.cpp b/FA2sp/Ext/CMapValidator/Body.cpp index 746501e..73bce42 100644 --- a/FA2sp/Ext/CMapValidator/Body.cpp +++ b/FA2sp/Ext/CMapValidator/Body.cpp @@ -6,7 +6,7 @@ #include -std::unordered_set CMapValidatorExt::StructureOverlappingIgnorance; +std::unordered_set CMapValidatorExt::StructureOverlappingIgnores; void CMapValidatorExt::ValidateStructureOverlapping(BOOL& result) { @@ -22,7 +22,7 @@ void CMapValidatorExt::ValidateStructureOverlapping(BOOL& result) const auto splits = STDHelpers::SplitString(Data, 4); // In the list, ignore it. - if (StructureOverlappingIgnorance.count(splits[1].m_pchData)) + if (StructureOverlappingIgnores.count(splits[1].m_pchData)) continue; const int Index = CMapData::Instance->GetBuildingTypeID(splits[1]); diff --git a/FA2sp/Ext/CMapValidator/Body.h b/FA2sp/Ext/CMapValidator/Body.h index cce57ba..3951a63 100644 --- a/FA2sp/Ext/CMapValidator/Body.h +++ b/FA2sp/Ext/CMapValidator/Body.h @@ -13,5 +13,5 @@ class CMapValidatorExt : public CMapValidator ppmfc::CString FetchLanguageString(const char* Key, const char* def); void InsertString(const char* String, bool IsWarning); - static std::unordered_set StructureOverlappingIgnorance; + static std::unordered_set StructureOverlappingIgnores; }; \ No newline at end of file diff --git a/FA2sp/Ext/CMapValidator/Hooks.cpp b/FA2sp/Ext/CMapValidator/Hooks.cpp index 0215b47..2026ad5 100644 --- a/FA2sp/Ext/CMapValidator/Hooks.cpp +++ b/FA2sp/Ext/CMapValidator/Hooks.cpp @@ -17,10 +17,10 @@ DEFINE_HOOK(4D19A0, CMapValidator_DoValidator_Extra, 5) DEFINE_HOOK(4CEA50, CMapValidator_CTOR, 7) { - if (auto pSection = CINI::FAData->GetSection("StructureOverlappingCheckIgnorance")) + if (auto pSection = CINI::FAData->GetSection("StructureOverlappingCheckIgnores")) { for (const auto& [key, value] : pSection->GetEntities()) - CMapValidatorExt::StructureOverlappingIgnorance.emplace(std::string{value.m_pchData}); + CMapValidatorExt::StructureOverlappingIgnores.emplace(std::string{value.m_pchData}); } return 0; @@ -28,7 +28,7 @@ DEFINE_HOOK(4CEA50, CMapValidator_CTOR, 7) DEFINE_HOOK(426A60, CMapValidator_DTOR, 7) { - CMapValidatorExt::StructureOverlappingIgnorance.clear(); + CMapValidatorExt::StructureOverlappingIgnores.clear(); return 0; } \ No newline at end of file From b47ccc1fbf102c65c721a6af3e1153a473c23a43 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Wed, 26 Apr 2023 14:49:32 +0800 Subject: [PATCH 20/30] Implement Copy&Paste for MultiSelection --- CHANGELOG.md | 1 + FA2pp | 2 +- FA2sp/Algorithms/lcw.cpp | 4 +- FA2sp/Miscs/MultiSelection.cpp | 194 ++++++++++++++++++++++++++++----- FA2sp/Miscs/MultiSelection.h | 16 +++ 5 files changed, 188 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a708eb..2c46a15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - Map validator now checks missing logic params, raise an error instead of warning for now. String can be modified by `MV_LogicMissingParams` - Fixed the bug that lighting is not reset correctly when loading/creating a map - Fixed the bug that smudges and basenodes drifting away when resizing the map +- Multiselection now supports copy & paste ## RELEASE 1.5.2 (2023-03-03) - Fixed the bug that money calculation is incorrect diff --git a/FA2pp b/FA2pp index 375a8d2..44bb329 160000 --- a/FA2pp +++ b/FA2pp @@ -1 +1 @@ -Subproject commit 375a8d2349fc22b5b63e42ef4791cd8f71576573 +Subproject commit 44bb329af2de50b90760286fa45c4cb66beeaaa9 diff --git a/FA2sp/Algorithms/lcw.cpp b/FA2sp/Algorithms/lcw.cpp index 3b97591..3488133 100644 --- a/FA2sp/Algorithms/lcw.cpp +++ b/FA2sp/Algorithms/lcw.cpp @@ -33,8 +33,8 @@ std::string lcw::decompress(const void* src, int slen) while (slen > 0) { auto blocksize = reinterpret_cast(ptr)[0]; - LCW_Uncompress(ptr + 4, buffer); - ret.append(buffer, buffer); + auto count = LCW_Uncompress(ptr + 4, buffer); + ret.append(buffer, count); ptr += blocksize; slen -= blocksize; } diff --git a/FA2sp/Miscs/MultiSelection.cpp b/FA2sp/Miscs/MultiSelection.cpp index d6acd25..69435da 100644 --- a/FA2sp/Miscs/MultiSelection.cpp +++ b/FA2sp/Miscs/MultiSelection.cpp @@ -4,10 +4,14 @@ #include #include #include +#include #include "../Ext/CIsoView/Body.h" #include "../Ext/CMapData/Body.h" +#include +#include + // #include "../Source/CIsoView.h" #include "../FA2sp.h" @@ -72,6 +76,118 @@ inline bool MultiSelection::IsSelected(int X, int Y) return SelectedCoords.find(MapCoord{ X,Y }) != SelectedCoords.end(); } +void MultiSelection::Copy() +{ + std::vector datas; + for (const auto& coord : SelectedCoords) + { + auto pCell = CMapData::Instance->GetCellAt(coord.X, coord.Y); + MyClipboardData data; + data.X = coord.X; + data.Y = coord.Y; + data.Overlay = pCell->Overlay; + data.OverlayData = pCell->OverlayData; + data.TileIndex = pCell->TileIndex; + data.TileIndexHiPart = pCell->TileIndexHiPart; + data.TileSubIndex = pCell->TileSubIndex; + data.Height = pCell->Height; + data.IceGrowth = pCell->IceGrowth; + data.Flag = pCell->Flag; + datas.push_back(data); + } + + auto hGlobal = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, 8 + sizeof(MyClipboardData) * datas.size()); + if (hGlobal == NULL) + { + MessageBox(NULL, "Error", "Failed to allocate global memory!", MB_OK); + return; + } + + auto pBuffer = GlobalLock(hGlobal); + if (pBuffer == nullptr) + { + MessageBox(NULL, "Error", "Failed to lock hGlobal handle!", MB_OK); + return; + } + while (GlobalUnlock(hGlobal)) + ; + + reinterpret_cast(pBuffer)[0] = 0; // Flag indicate this is multiselection + reinterpret_cast(pBuffer)[1] = datas.size(); + memcpy(reinterpret_cast(pBuffer) + 8, datas.data(), sizeof(MyClipboardData) * datas.size()); + + OpenClipboard(CFinalSunApp::Instance->m_pMainWnd->m_hWnd); + EmptyClipboard(); + if (FALSE == SetClipboardData(CFinalSunApp::Instance->ClipboardFormat, hGlobal)) + MessageBox(NULL, "Failed to set clipboard data", "Error", 0); + CloseClipboard(); +} + +void MultiSelection::Paste(int X, int Y, int nBaseHeight, MyClipboardData* data, size_t length) +{ + std::span cells {data, data + length}; + + RECT rect + { + std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::min(), + std::numeric_limits::min() + }; + for (const auto& cell : cells) + { + if (cell.X < rect.left) + rect.left = cell.X; + if (cell.X > rect.right) + rect.right = cell.X; + if (cell.Y < rect.top) + rect.top = cell.Y; + if (cell.Y > rect.bottom) + rect.bottom = cell.Y; + } + + const MapCoord center = { (rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2 }; + + auto lowest_height = std::numeric_limits::min(); + for (const auto& cell : cells) + { + int offx = cell.X - center.X; + int offy = cell.Y - center.Y; + + const auto pCell = CMapData::Instance->TryGetCellAt(X + offx, Y + offy); + if (pCell->Height < lowest_height) + lowest_height = pCell->Height; + } + + nBaseHeight += lowest_height; + for (const auto& cell : cells) + { + int offx = cell.X - center.X; + int offy = cell.Y - center.Y; + + auto nCellIndex = CMapData::Instance->GetCoordIndex(X + offx, Y + offy); + if (nCellIndex < 0 || nCellIndex >= CMapData::Instance->CellDataCount) + continue; + + auto pCell = CMapData::Instance->GetCellAt(nCellIndex); + + CMapData::Instance->DeleteTiberium(pCell->Overlay, pCell->OverlayData); + pCell->Overlay = cell.Overlay; + pCell->OverlayData = cell.OverlayData; + CMapData::Instance->AddTiberium(pCell->Overlay, pCell->OverlayData); + + pCell->TileIndex = cell.TileIndex; + pCell->TileIndexHiPart = cell.TileIndexHiPart; + pCell->TileSubIndex = cell.TileSubIndex; + + pCell->Height = std::clamp(cell.Height + nBaseHeight, 0, 14); + + pCell->IceGrowth = cell.IceGrowth; + pCell->Flag = cell.Flag; + + CMapData::Instance->UpdateMapPreviewAt(X + offx, Y + offy); + } +} DEFINE_HOOK(456EFC, CIsoView_OnMouseMove_MultiSelect_ReverseStatus, 6) { @@ -306,32 +422,58 @@ DEFINE_HOOK(433F70, CFinalSunDlg_Tools_HideSingleField, 5) return 0x433F83; } -//DEFINE_HOOK(435F10, CFinalSunDlg_Tools_Copy, 7) -//{ -// GET(CFinalSunDlg*, pThis, ECX); -// -// pThis->PlaySound(CFinalSunDlg::FASoundType::Normal); -// -// if (MultiSelection::GetCount()) -// { -// -// } -// else -// CIsoView::CurrentCommand->Command = FACurrentCommand::TileCopy; -// -// return 0x435F24; -//} -// -//DEFINE_HOOK(4C3850, CMapData_PasteAt, 8) -//{ -// GET_STACK(const int, X, 0x4); -// GET_STACK(const int, Y, 0x8); -// GET_STACK(const char, nBaseHeight, 0xC); -// -// -// -// return 0x4C388B; -//} +DEFINE_HOOK(435F10, CFinalSunDlg_Tools_Copy, 7) +{ + if (!ExtConfigs::EnableMultiSelection) + return 0; + + GET(CFinalSunDlg*, pThis, ECX); + + pThis->PlaySound(CFinalSunDlg::FASoundType::Normal); + + if (MultiSelection::GetCount()) + MultiSelection::Copy(); + else + CIsoView::CurrentCommand->Command = FACurrentCommand::TileCopy; + + return 0x435F24; +} + +DEFINE_HOOK(4C3850, CMapData_PasteAt, 8) +{ + if (!ExtConfigs::EnableMultiSelection) + return 0; + + GET_STACK(const int, X, 0x4); + GET_STACK(const int, Y, 0x8); + GET_STACK(const char, nBaseHeight, 0xC); + + OpenClipboard(CFinalSunApp::Instance->m_pMainWnd->m_hWnd); + HANDLE hData = GetClipboardData(CFinalSunApp::Instance->ClipboardFormat); + auto ptr = GlobalLock(hData); + + if (ptr) + { + if (reinterpret_cast(ptr)[0] == 0) // Multiselection + { + const auto length = reinterpret_cast(ptr)[1]; + const auto p = reinterpret_cast(reinterpret_cast(ptr) + 8); + MultiSelection::Paste(X, Y, nBaseHeight, p, length); + GlobalUnlock(hData); + CloseClipboard(); + return 0x4C388B; + } + else // Default selection + { + GlobalUnlock(hData); + CloseClipboard(); + return 0; + } + } + + CloseClipboard(); + return 0x4C388B; +} DEFINE_HOOK(474FE0, CIsoView_Draw_MultiSelectionMoney, 5) { diff --git a/FA2sp/Miscs/MultiSelection.h b/FA2sp/Miscs/MultiSelection.h index 0e2edc3..3f72361 100644 --- a/FA2sp/Miscs/MultiSelection.h +++ b/FA2sp/Miscs/MultiSelection.h @@ -15,6 +15,22 @@ class MultiSelection inline static void ReverseStatus(int X, int Y); inline static bool IsSelected(int X, int Y); + struct MyClipboardData + { + int X; + int Y; + unsigned char Overlay; + unsigned char OverlayData; + unsigned short TileIndex; + unsigned short TileIndexHiPart; + unsigned char TileSubIndex; + unsigned char Height; + unsigned char IceGrowth; + CellData::CellDataFlag Flag; + }; + static void Copy(); + static void Paste(int X, int Y, int nBaseHeight, MyClipboardData* data, size_t length); + template requires std::invocable<_Fn, CellData&> static void ApplyForEach(_Fn _Func) { From 62992e5b86f20c63943de9506dbec1458e35319c Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Wed, 26 Apr 2023 15:33:53 +0800 Subject: [PATCH 21/30] Restrict copy paste in same theater --- FA2sp/Miscs/MultiSelection.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/FA2sp/Miscs/MultiSelection.cpp b/FA2sp/Miscs/MultiSelection.cpp index 69435da..d511e51 100644 --- a/FA2sp/Miscs/MultiSelection.cpp +++ b/FA2sp/Miscs/MultiSelection.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "../Ext/CIsoView/Body.h" #include "../Ext/CMapData/Body.h" @@ -96,7 +97,7 @@ void MultiSelection::Copy() datas.push_back(data); } - auto hGlobal = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, 8 + sizeof(MyClipboardData) * datas.size()); + auto hGlobal = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, 12 + sizeof(MyClipboardData) * datas.size()); if (hGlobal == NULL) { MessageBox(NULL, "Error", "Failed to allocate global memory!", MB_OK); @@ -114,8 +115,9 @@ void MultiSelection::Copy() reinterpret_cast(pBuffer)[0] = 0; // Flag indicate this is multiselection reinterpret_cast(pBuffer)[1] = datas.size(); - memcpy(reinterpret_cast(pBuffer) + 8, datas.data(), sizeof(MyClipboardData) * datas.size()); - + reinterpret_cast(pBuffer)[2] = CLoading::Instance->TheaterIdentifier; + memcpy(reinterpret_cast(pBuffer) + 12, datas.data(), sizeof(MyClipboardData) * datas.size()); + OpenClipboard(CFinalSunApp::Instance->m_pMainWnd->m_hWnd); EmptyClipboard(); if (FALSE == SetClipboardData(CFinalSunApp::Instance->ClipboardFormat, hGlobal)) @@ -457,8 +459,12 @@ DEFINE_HOOK(4C3850, CMapData_PasteAt, 8) if (reinterpret_cast(ptr)[0] == 0) // Multiselection { const auto length = reinterpret_cast(ptr)[1]; - const auto p = reinterpret_cast(reinterpret_cast(ptr) + 8); - MultiSelection::Paste(X, Y, nBaseHeight, p, length); + const int identifier = reinterpret_cast(ptr)[2]; + if (identifier == CLoading::Instance->TheaterIdentifier) + { + const auto p = reinterpret_cast(reinterpret_cast(ptr) + 12); + MultiSelection::Paste(X, Y, nBaseHeight, p, length); + } GlobalUnlock(hData); CloseClipboard(); return 0x4C388B; From f0821cfcd569047ed0130a6ba9a5d510173f0314 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Thu, 27 Apr 2023 13:31:34 +0800 Subject: [PATCH 22/30] Replace CurrentSHP buffer, now read from dir first --- FA2sp.vcxproj | 1 + FA2sp.vcxproj.filters | 3 + FA2sp/Miscs/Hooks.Memory.cpp | 14 ++++ FA2sp/Miscs/Hooks.Mix.cpp | 6 +- FA2sp/Miscs/Hooks.ShpLoad.cpp | 137 ++++++++++++++++++++++++++++++++++ 5 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 FA2sp/Miscs/Hooks.ShpLoad.cpp diff --git a/FA2sp.vcxproj b/FA2sp.vcxproj index 6d3b679..d4df572 100644 --- a/FA2sp.vcxproj +++ b/FA2sp.vcxproj @@ -198,6 +198,7 @@ + diff --git a/FA2sp.vcxproj.filters b/FA2sp.vcxproj.filters index bcd72ef..1daaf7a 100644 --- a/FA2sp.vcxproj.filters +++ b/FA2sp.vcxproj.filters @@ -494,6 +494,9 @@ 源文件 + + 源文件 + diff --git a/FA2sp/Miscs/Hooks.Memory.cpp b/FA2sp/Miscs/Hooks.Memory.cpp index 82a42a1..eeb15f9 100644 --- a/FA2sp/Miscs/Hooks.Memory.cpp +++ b/FA2sp/Miscs/Hooks.Memory.cpp @@ -25,6 +25,20 @@ DEFINE_HOOK(537128, ExeStart_MemoryHooks, 5) RunTime::ResetMemoryContentAt(0x591154, ::HeapDestroy); RunTime::ResetMemoryContentAt(0x591158, ::HeapCreate); + // RunTime::ResetMemoryContentAt(0x59128C, ::GlobalAlloc); + // RunTime::ResetMemoryContentAt(0x5912FC, ::GlobalFree); + // RunTime::ResetMemoryContentAt(0x591254, ::GlobalReAlloc); + // + // RunTime::ResetMemoryContentAt(0x591270, ::LocalAlloc); + // RunTime::ResetMemoryContentAt(0x5912B8, ::LocalFree); + // RunTime::ResetMemoryContentAt(0x591248, ::LocalReAlloc); + // + // RunTime::ResetMemoryContentAt(0x591268, ::TlsAlloc); + // RunTime::ResetMemoryContentAt(0x59125C, ::TlsFree); + // + // RunTime::ResetMemoryContentAt(0x591160, ::VirtualAlloc); + // RunTime::ResetMemoryContentAt(0x59115C, ::VirtualFree); + RunTime::SetJump(0x536106, (DWORD)MemoryWrapper::_free); RunTime::SetJump(0x537CA0, (DWORD)MemoryWrapper::_malloc); diff --git a/FA2sp/Miscs/Hooks.Mix.cpp b/FA2sp/Miscs/Hooks.Mix.cpp index 2ffd522..72774d9 100644 --- a/FA2sp/Miscs/Hooks.Mix.cpp +++ b/FA2sp/Miscs/Hooks.Mix.cpp @@ -1,16 +1,16 @@ #include -DEFINE_HOOK(527F95, MixFile_Open_CheckTD, 7) +DEFINE_HOOK(527F95, MixFile_PostOpen_CheckTD, 7) { return 0x527FBB; } -DEFINE_HOOK(5281EE, MixFile_Open_CheckRAUnencrypted, 5) +DEFINE_HOOK(5281EE, MixFile_PostOpen_CheckRAUnencrypted, 5) { return 0x52823E; } -DEFINE_HOOK(5280FC, MixFile_Open_CheckRAEncrypted, 7) +DEFINE_HOOK(5280FC, MixFile_PostOpen_CheckRAEncrypted, 7) { return 0x528124; } diff --git a/FA2sp/Miscs/Hooks.ShpLoad.cpp b/FA2sp/Miscs/Hooks.ShpLoad.cpp new file mode 100644 index 0000000..fa8c758 --- /dev/null +++ b/FA2sp/Miscs/Hooks.ShpLoad.cpp @@ -0,0 +1,137 @@ +#include +#include +#include +#include +#include + +#include + +#include +#include "../RunTime.h" + +namespace hooks_files_detail +{ + struct shape_file + { + bool is_open() const + { + return m_data != nullptr; + } + + void close() + { + if (m_data) + { + GameDelete(m_data); + m_data = nullptr; + } + } + + template + T& at(size_t offset) const + { + return *const_cast(reinterpret_cast(m_data + offset)); + } + + unsigned char* m_data; + }; + static shape_file current_shape_file; +} +using namespace hooks_files_detail; + +DEFINE_HOOK(525C50, CMixFile_LoadSHP, 5) +{ + GET_STACK(const char*, filename, 0x4); + GET_STACK(int, nMix, 0x8); + + if (current_shape_file.is_open()) + current_shape_file.close(); + + ppmfc::CString filepath = CFinalSunApp::FilePath(); + filepath += filename; + std::ifstream fin; + fin.open(filepath, std::ios::in | std::ios::binary); + if (fin.is_open()) + { + fin.seekg(0, std::ios::end); + const int size = static_cast(fin.tellg()); + if (size == 0) + return false; + + fin.seekg(0, std::ios::beg); + current_shape_file.m_data = GameCreateArray(size); + fin.read((char*)current_shape_file.m_data, size); + fin.close(); + R->EAX(true); + } + else if (CMixFile::HasFile(filename, nMix)) + { + Ccc_file file(true); + file.open(filename, CMixFile::Array[nMix - 1]); + current_shape_file.m_data = GameCreateArray(file.get_size()); + memcpy(current_shape_file.m_data, file.get_data(), file.get_size()); + R->EAX(true); + } + else + R->EAX(false); + + return 0x525CFC; +} + +DEFINE_HOOK(525A30, CSHPFile_GetHeader, 6) +{ + GET_STACK(ShapeHeader*, pHeader, 0x4); + + if (pHeader && current_shape_file.is_open()) + { + *pHeader = current_shape_file.at(0); + R->EAX(true); + } + else + { + R->EAX(false); + } + + return 0x525A5B; +} + +DEFINE_HOOK(525A60, CSHPFile_GetImageHeader, 7) +{ + GET_STACK(int, nFrame, 0x4); + GET_STACK(ShapeImageHeader*, pImageHeader, 0x8); + + if (pImageHeader && nFrame >= 0 && current_shape_file.is_open() && nFrame < current_shape_file.at(0x6)) + { + *pImageHeader = current_shape_file.at(nFrame * sizeof(ShapeImageHeader) + sizeof(ShapeHeader)); + R->EAX(true); + } + else + { + R->EAX(false); + } + + return 0x525ACB; +} + +DEFINE_HOOK(537129, ExeRun_CSHPFileBufferReplace, 9) +{ +#pragma pack(push, 1) + struct SHPHelperStruct + { + unsigned char Op; + unsigned char** Address; + }; +#pragma pack(pop) + static_assert(sizeof(SHPHelperStruct) == 5); + + SHPHelperStruct data; + data.Op = 0xA1; + data.Address = ¤t_shape_file.m_data; + + RunTime::ResetMemoryContentAt(0x526023, data); + RunTime::ResetMemoryContentAt(0x5260B9, data); + RunTime::ResetMemoryContentAt(0x52617F, data); + RunTime::ResetMemoryContentAt(0x52619D, data); + + return 0; +} \ No newline at end of file From 62aa09a41cbdbb3e0daf707c5064ae8a0fd13824 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Thu, 27 Apr 2023 18:33:17 +0800 Subject: [PATCH 23/30] Implement ExtConfig::ExtendedValidationNoError --- CHANGELOG.md | 1 + DOCUMENT.md | 9 +++++---- FA2sp/Ext/CMapValidator/Body.cpp | 16 ++++++++++++---- FA2sp/Ext/CMapValidator/Body.h | 1 + FA2sp/FA2sp.cpp | 3 +++ FA2sp/FA2sp.h | 1 + 6 files changed, 23 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c46a15..718e429 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Fixed the bug that lighting is not reset correctly when loading/creating a map - Fixed the bug that smudges and basenodes drifting away when resizing the map - Multiselection now supports copy & paste +- New ***ExtConfig*** : `ExtendedValidationNoError` = **BOOLEAN**, defaults to false ## RELEASE 1.5.2 (2023-03-03) - Fixed the bug that money calculation is incorrect diff --git a/DOCUMENT.md b/DOCUMENT.md index 85e5127..89056d7 100644 --- a/DOCUMENT.md +++ b/DOCUMENT.md @@ -65,10 +65,11 @@ NOTICE THAT UNDOREDO AND COPYPASTE HASN'T BEEN SUPPORTED YET! - `RecentFileLimit` = **INTEGER** ; How many recent files should I keep? ranges from $4$ to $9$ - `MultiSelectionColor` = **COLORREF** ; Determines the back color of selected tiles - `RandomTerrainObjects` = **BOOLEAN** ; Determines if FA2 will display all terrain objects in random tree dialog, defaults to **false** - - `DDrawInVideoMem` = **BOOLEAN** ; Determines if FA2 will allocate DirectDraw surface in the video memory, defaults to true - - `DDrawEmulation` = **BOOLEAN** ; Determines if FA2 will use emulation mode for DirectDrawCreate, defaults to false - - `NoHouseNameTranslation` = **BOOLEAN** ; Determines if FA2 will translate house to their UIName, defaults to false - - `EnableMultiSelection` = **BOOLEAN** ; Determines if FA2sp will enable expermental multi-selection features, defaults to false + - `DDrawInVideoMem` = **BOOLEAN** ; Determines if FA2 will allocate DirectDraw surface in the video memory, defaults to **true** + - `DDrawEmulation` = **BOOLEAN** ; Determines if FA2 will use emulation mode for DirectDrawCreate, defaults to **false** + - `NoHouseNameTranslation` = **BOOLEAN** ; Determines if FA2 will translate house to their UIName, defaults to **false** + - `EnableMultiSelection` = **BOOLEAN** ; Determines if FA2sp will enable expermental multi-selection features, defaults to **false** + - `ExtendedValidationNoError` = **BOOLEAN** ; If this value is true, then extended map validation won't be shown as error but warning, defaults to **false** - **`[Sides]`** (**x** means this item is **essensial**, fa2sp need this section to work properly) - Contains a list of sides registered in rules ```ini diff --git a/FA2sp/Ext/CMapValidator/Body.cpp b/FA2sp/Ext/CMapValidator/Body.cpp index 73bce42..c4cb20b 100644 --- a/FA2sp/Ext/CMapValidator/Body.cpp +++ b/FA2sp/Ext/CMapValidator/Body.cpp @@ -3,6 +3,7 @@ #include "../../Helpers/Translations.h" #include "../../Helpers/STDHelpers.h" #include "../../Ext/CMapData/Body.h" +#include "../../FA2sp.h" #include @@ -52,7 +53,8 @@ void CMapValidatorExt::ValidateStructureOverlapping(BOOL& result) { if (Occupied[i].size() > 1) { - result = FALSE; + if (!ExtConfigs::ExtendedValidationNoError) + result = FALSE; auto buffer = Format; buffer.ReplaceNumString(1, Occupied[i].size()); buffer.ReplaceNumString(2, CMapData::Instance->GetYFromCoordIndex(i)); @@ -63,7 +65,7 @@ void CMapValidatorExt::ValidateStructureOverlapping(BOOL& result) buffer += ", "; } buffer += Occupied[i].back().c_str(); - this->InsertString(buffer, false); + this->InsertStringAsError(buffer); } } } @@ -82,10 +84,11 @@ void CMapValidatorExt::ValidateMissingParams(BOOL& result) { if (value.Find(",,") != -1) // has missing param! { - result = FALSE; + if (!ExtConfigs::ExtendedValidationNoError) + result = FALSE; auto tmp = Format; tmp.ReplaceNumString(2, key); - InsertString(tmp, false); + InsertStringAsError(tmp); } } } @@ -107,6 +110,11 @@ ppmfc::CString CMapValidatorExt::FetchLanguageString(const char* Key, const char return buffer; } +void CMapValidatorExt::InsertStringAsError(const char* String) +{ + CLCResults.InsertItem(LVIF_TEXT | LVIF_IMAGE, CLCResults.GetItemCount(), String, NULL, NULL, ExtConfigs::ExtendedValidationNoError, NULL); +} + void CMapValidatorExt::InsertString(const char* String, bool IsWarning) { CLCResults.InsertItem(LVIF_TEXT | LVIF_IMAGE, CLCResults.GetItemCount(), String, NULL, NULL, IsWarning, NULL); diff --git a/FA2sp/Ext/CMapValidator/Body.h b/FA2sp/Ext/CMapValidator/Body.h index 3951a63..eb57126 100644 --- a/FA2sp/Ext/CMapValidator/Body.h +++ b/FA2sp/Ext/CMapValidator/Body.h @@ -11,6 +11,7 @@ class CMapValidatorExt : public CMapValidator void ValidateMissingParams(BOOL& result); ppmfc::CString FetchLanguageString(const char* Key, const char* def); + void InsertStringAsError(const char* String); void InsertString(const char* String, bool IsWarning); static std::unordered_set StructureOverlappingIgnores; diff --git a/FA2sp/FA2sp.cpp b/FA2sp/FA2sp.cpp index 534a22c..d3b6f5f 100644 --- a/FA2sp/FA2sp.cpp +++ b/FA2sp/FA2sp.cpp @@ -55,6 +55,7 @@ bool ExtConfigs::DDrawInVideoMem; bool ExtConfigs::DDrawEmulation; bool ExtConfigs::NoHouseNameTranslation; bool ExtConfigs::EnableMultiSelection; +bool ExtConfigs::ExtendedValidationNoError; MultimapHelper Variables::Rules = { &CINI::Rules(), &CINI::CurrentDocument() }; MultimapHelper Variables::FAData = { &CINI::FAData() }; @@ -148,6 +149,8 @@ void FA2sp::ExtConfigsInitialize() ExtConfigs::NoHouseNameTranslation = CINI::FAData->GetBool("ExtConfigs", "NoHouseNameTranslation"); ExtConfigs::EnableMultiSelection = CINI::FAData->GetBool("ExtConfigs", "EnableMultiSelection"); + + ExtConfigs::ExtendedValidationNoError = CINI::FAData->GetBool("ExtConfigs", "ExtendedValidationNoError"); } // DllMain diff --git a/FA2sp/FA2sp.h b/FA2sp/FA2sp.h index 21141ae..407e07d 100644 --- a/FA2sp/FA2sp.h +++ b/FA2sp/FA2sp.h @@ -64,6 +64,7 @@ class ExtConfigs static bool DDrawEmulation; static bool NoHouseNameTranslation; static bool EnableMultiSelection; + static bool ExtendedValidationNoError; }; class Variables From f56d47ba17d03491d3752c9a0bb3b75f13f4495e Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Fri, 28 Apr 2023 13:24:59 +0800 Subject: [PATCH 24/30] Add right slash before file load --- FA2sp/Ext/CLoading/Body.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FA2sp/Ext/CLoading/Body.cpp b/FA2sp/Ext/CLoading/Body.cpp index 0b7925f..5673834 100644 --- a/FA2sp/Ext/CLoading/Body.cpp +++ b/FA2sp/Ext/CLoading/Body.cpp @@ -171,7 +171,7 @@ bool CLoadingExt::InitMixFilesFix() CFinalSunApp::Instance->MarbleLoaded = TRUE; else { - ppmfc::CString FullPath = CFinalSunApp::ExePath + "MARBLE.MIX"; + ppmfc::CString FullPath = CFinalSunApp::ExePath + "\\MARBLE.MIX"; int result = CMixFile::Open(FullPath, 0); if (result) { From 1577884cbe4331fd3c96bfa7de882a1828d73d47 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Fri, 28 Apr 2023 13:35:38 +0800 Subject: [PATCH 25/30] Clean-up --- FA2pp | 2 +- FA2sp/Ext/CFinalSunApp/Body.cpp | 5 ++++- FA2sp/Ext/CLoading/Hooks.InitPalettes.cpp | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/FA2pp b/FA2pp index 44bb329..eeec0e6 160000 --- a/FA2pp +++ b/FA2pp @@ -1 +1 @@ -Subproject commit 44bb329af2de50b90760286fa45c4cb66beeaaa9 +Subproject commit eeec0e6167dc1c6597c2e38f257285f019e9d6b5 diff --git a/FA2sp/Ext/CFinalSunApp/Body.cpp b/FA2sp/Ext/CFinalSunApp/Body.cpp index 7121c17..afc7bc5 100644 --- a/FA2sp/Ext/CFinalSunApp/Body.cpp +++ b/FA2sp/Ext/CFinalSunApp/Body.cpp @@ -69,7 +69,10 @@ BOOL CFinalSunAppExt::InitInstanceExt() this->FileSearchLikeTS = ini.GetBool("FinalSun", "FileSearchLikeTS"); this->Language = ini.GetString("FinalSun", "Language"); } - + + // HACK, Game like pls + this->FileSearchLikeTS = TRUE; + // No graphics, no need for them in fact this->NoAircraftGraphics = ini.GetBool("Graphics", "NoAircraftGraphics"); this->NoVehicleGraphics = ini.GetBool("Graphics", "NoVehicleGraphics"); diff --git a/FA2sp/Ext/CLoading/Hooks.InitPalettes.cpp b/FA2sp/Ext/CLoading/Hooks.InitPalettes.cpp index 94aa506..55b827e 100644 --- a/FA2sp/Ext/CLoading/Hooks.InitPalettes.cpp +++ b/FA2sp/Ext/CLoading/Hooks.InitPalettes.cpp @@ -53,8 +53,8 @@ DEFINE_HOOK(48B020, CLoading_InitPalettes, 7) loadPalette("lunar.pal", pThis->PAL_LUNAR); loadPalette("desert.pal", pThis->PAL_DESERT); - if (!loadPalette("libtem.pal", pThis->PAL_LIB_ID2124019542)) - loadPalette("_ID2124019542", pThis->PAL_LIB_ID2124019542); + loadPalette("libtem.pal", pThis->PAL_LIBTEM); + return 0x48C3CD; } \ No newline at end of file From 8b34fc99ca1e1c891cffcdd0e07fe74a19157472 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Sun, 30 Apr 2023 13:21:28 +0800 Subject: [PATCH 26/30] Remove useless menu items --- FA2sp/UI/CMenu.rc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/FA2sp/UI/CMenu.rc b/FA2sp/UI/CMenu.rc index 2a2bc66..df01ac8 100644 --- a/FA2sp/UI/CMenu.rc +++ b/FA2sp/UI/CMenu.rc @@ -99,11 +99,11 @@ POPUP "Options" } POPUP "Help" { - MENUITEM "Manual\tF1", 57670 - MENUITEM SEPARATOR + // MENUITEM "Manual\tF1", 57670 + // MENUITEM SEPARATOR MENUITEM "Info", 40006 MENUITEM "Credits", 40075 - MENUITEM "Tip of the day", 40022 + // MENUITEM "Tip of the day", 40022 } POPUP "Layers" { From 9e3eb2b1a4c1194f5f123a2bac06317458f46471 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Sun, 30 Apr 2023 13:21:42 +0800 Subject: [PATCH 27/30] Reduce warnings, correct spell --- FA2pp | 2 +- FA2sp/Ext/CHouses/Body.cpp | 15 ++---- FA2sp/Ext/CHouses/Body.h | 2 - FA2sp/Ext/CScriptTypes/Body.cpp | 17 +++++-- FA2sp/Helpers/Helper.h | 2 +- FA2sp/Miscs/MultiSelection.cpp | 88 +++++++++++++++++---------------- MFC42 | 2 +- 7 files changed, 65 insertions(+), 63 deletions(-) diff --git a/FA2pp b/FA2pp index eeec0e6..289f5d2 160000 --- a/FA2pp +++ b/FA2pp @@ -1 +1 @@ -Subproject commit eeec0e6167dc1c6597c2e38f257285f019e9d6b5 +Subproject commit 289f5d25092c8a126a4896d5533ce1221fbfab60 diff --git a/FA2sp/Ext/CHouses/Body.cpp b/FA2sp/Ext/CHouses/Body.cpp index 13d7373..68d4923 100644 --- a/FA2sp/Ext/CHouses/Body.cpp +++ b/FA2sp/Ext/CHouses/Body.cpp @@ -11,24 +11,17 @@ void CHousesExt::ProgramStartupInit() //RunTime::ResetMemoryContentAt(0x593C80 + 0x4, &RunTime::Messages::COMBOBOX_KILLFOCUS, 4); } -void CHousesExt::UpdateComboboxContents() -{ - this->CCBColor.DeleteAllStrings(); - this->CCBCountry.DeleteAllStrings(); - this->CCBHouses.DeleteAllStrings(); - this->CCBHumanHouse.DeleteAllStrings(); - - auto& doc = CINI::CurrentDocument(); - -} - BOOL CHousesExt::PreTranslateMessageExt(MSG* pMsg) { switch (pMsg->message) { case WM_LBUTTONUP: { if (pMsg->hwnd == this->GetDlgItem(1145)->m_hWnd) + { CAllieEditor::Create(); + return TRUE; + } + break; } default: break; diff --git a/FA2sp/Ext/CHouses/Body.h b/FA2sp/Ext/CHouses/Body.h index 98e3c79..ac1b7d1 100644 --- a/FA2sp/Ext/CHouses/Body.h +++ b/FA2sp/Ext/CHouses/Body.h @@ -13,8 +13,6 @@ class NOVTABLE CHousesExt : public CHouses // Ext Functions // - void UpdateComboboxContents(); - static void ProgramStartupInit(); CHousesExt() {}; diff --git a/FA2sp/Ext/CScriptTypes/Body.cpp b/FA2sp/Ext/CScriptTypes/Body.cpp index 5d2b43d..36a930c 100644 --- a/FA2sp/Ext/CScriptTypes/Body.cpp +++ b/FA2sp/Ext/CScriptTypes/Body.cpp @@ -199,11 +199,11 @@ BOOL CScriptTypesExt::OnInitDialogExt() curAction.ParamCode_ = 0; }*/ - auto& fadata = CINI::FAData(); + auto& ini = CINI::FAData(); - if (auto entities = fadata.GetSection("ScriptParams")) + if (auto entities = ini.GetSection("ScriptParams")) { - char* pParseBuffer[2]; + char* pParseBuffer[2] = { nullptr }; for (auto& pair : entities->GetEntities()) { int id = atoi(pair.first); @@ -216,18 +216,20 @@ BOOL CScriptTypesExt::OnInitDialogExt() case 2: CScriptTypeParam::ExtParams[id].Param_ = atoi((const char*)pParseBuffer[1]); SAFE_RELEASE(pParseBuffer[1]); + [[fallthrough]]; case 1: CScriptTypeParam::ExtParams[id].Label_ = pParseBuffer[0]; SAFE_RELEASE(pParseBuffer[0]); + [[fallthrough]]; case 0: continue; } } } - if (auto entities = fadata.GetSection("ScriptsRA2")) + if (auto entities = ini.GetSection("ScriptsRA2")) { - char* pParseBuffer[5]; + char* pParseBuffer[5] = { nullptr }; for (auto& pair : entities->GetEntities()) { int id = atoi(pair.first); @@ -240,18 +242,23 @@ BOOL CScriptTypesExt::OnInitDialogExt() default: CScriptTypeAction::ExtActions[id].Description_ = pParseBuffer[4]; SAFE_RELEASE(pParseBuffer[4]); + [[fallthrough]]; case 4: CScriptTypeAction::ExtActions[id].Editable_ = ParseBool((const char*)pParseBuffer[3]); SAFE_RELEASE(pParseBuffer[3]); + [[fallthrough]]; case 3: CScriptTypeAction::ExtActions[id].Hide_ = ParseBool((const char*)pParseBuffer[2]); SAFE_RELEASE(pParseBuffer[2]); + [[fallthrough]]; case 2: CScriptTypeAction::ExtActions[id].ParamCode_ = atoi((const char*)pParseBuffer[1]); SAFE_RELEASE(pParseBuffer[1]); + [[fallthrough]]; case 1: CScriptTypeAction::ExtActions[id].Name_ = pParseBuffer[0]; SAFE_RELEASE(pParseBuffer[0]); + [[fallthrough]]; case 0: continue; } diff --git a/FA2sp/Helpers/Helper.h b/FA2sp/Helpers/Helper.h index 8e05109..e873f70 100644 --- a/FA2sp/Helpers/Helper.h +++ b/FA2sp/Helpers/Helper.h @@ -3,7 +3,7 @@ #include static size_t ParseList(const char* pValue, const char** outValue, size_t Count = 1) { - char buffer[0x400]; + char buffer[0x400] = { 0 }; for (size_t i = 0; i < Count; ++i) { // skip the leading spaces while (isspace(static_cast(*pValue))) { diff --git a/FA2sp/Miscs/MultiSelection.cpp b/FA2sp/Miscs/MultiSelection.cpp index d511e51..a369f85 100644 --- a/FA2sp/Miscs/MultiSelection.cpp +++ b/FA2sp/Miscs/MultiSelection.cpp @@ -27,11 +27,11 @@ bool MultiSelection::AddCoord(int X, int Y) if (X == -1 || Y == -1) return false; - MapCoord coord{ X,Y }; - auto itr = SelectedCoords.find(coord); + MapCoord coords{ X,Y }; + auto itr = SelectedCoords.find(coords); if (itr == SelectedCoords.end()) { - SelectedCoords.insert(itr, coord); + SelectedCoords.insert(itr, coords); return true; } return false; @@ -42,8 +42,8 @@ bool MultiSelection::RemoveCoord(int X, int Y) if (X == -1 || Y == -1) return false; - MapCoord coord{ X,Y }; - auto itr = SelectedCoords.find(coord); + MapCoord coords{ X,Y }; + auto itr = SelectedCoords.find(coords); if (itr != SelectedCoords.end()) { SelectedCoords.erase(itr); @@ -79,25 +79,25 @@ inline bool MultiSelection::IsSelected(int X, int Y) void MultiSelection::Copy() { - std::vector datas; - for (const auto& coord : SelectedCoords) + std::vector data; + for (const auto& coords : SelectedCoords) { - auto pCell = CMapData::Instance->GetCellAt(coord.X, coord.Y); - MyClipboardData data; - data.X = coord.X; - data.Y = coord.Y; - data.Overlay = pCell->Overlay; - data.OverlayData = pCell->OverlayData; - data.TileIndex = pCell->TileIndex; - data.TileIndexHiPart = pCell->TileIndexHiPart; - data.TileSubIndex = pCell->TileSubIndex; - data.Height = pCell->Height; - data.IceGrowth = pCell->IceGrowth; - data.Flag = pCell->Flag; - datas.push_back(data); + auto pCell = CMapData::Instance->GetCellAt(coords.X, coords.Y); + MyClipboardData item = {}; + item.X = coords.X; + item.Y = coords.Y; + item.Overlay = pCell->Overlay; + item.OverlayData = pCell->OverlayData; + item.TileIndex = pCell->TileIndex; + item.TileIndexHiPart = pCell->TileIndexHiPart; + item.TileSubIndex = pCell->TileSubIndex; + item.Height = pCell->Height; + item.IceGrowth = pCell->IceGrowth; + item.Flag = pCell->Flag; + data.push_back(item); } - auto hGlobal = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, 12 + sizeof(MyClipboardData) * datas.size()); + auto hGlobal = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, 12 + sizeof(MyClipboardData) * data.size()); if (hGlobal == NULL) { MessageBox(NULL, "Error", "Failed to allocate global memory!", MB_OK); @@ -113,10 +113,10 @@ void MultiSelection::Copy() while (GlobalUnlock(hGlobal)) ; - reinterpret_cast(pBuffer)[0] = 0; // Flag indicate this is multiselection - reinterpret_cast(pBuffer)[1] = datas.size(); + reinterpret_cast(pBuffer)[0] = 0; // Flag indicate this is multi-selection + reinterpret_cast(pBuffer)[1] = data.size(); reinterpret_cast(pBuffer)[2] = CLoading::Instance->TheaterIdentifier; - memcpy(reinterpret_cast(pBuffer) + 12, datas.data(), sizeof(MyClipboardData) * datas.size()); + memcpy(reinterpret_cast(pBuffer) + 12, data.data(), sizeof(MyClipboardData) * data.size()); OpenClipboard(CFinalSunApp::Instance->m_pMainWnd->m_hWnd); EmptyClipboard(); @@ -129,7 +129,7 @@ void MultiSelection::Paste(int X, int Y, int nBaseHeight, MyClipboardData* data, { std::span cells {data, data + length}; - RECT rect + RECT bounds { std::numeric_limits::max(), std::numeric_limits::max(), @@ -138,25 +138,25 @@ void MultiSelection::Paste(int X, int Y, int nBaseHeight, MyClipboardData* data, }; for (const auto& cell : cells) { - if (cell.X < rect.left) - rect.left = cell.X; - if (cell.X > rect.right) - rect.right = cell.X; - if (cell.Y < rect.top) - rect.top = cell.Y; - if (cell.Y > rect.bottom) - rect.bottom = cell.Y; + if (cell.X < bounds.left) + bounds.left = cell.X; + if (cell.X > bounds.right) + bounds.right = cell.X; + if (cell.Y < bounds.top) + bounds.top = cell.Y; + if (cell.Y > bounds.bottom) + bounds.bottom = cell.Y; } - const MapCoord center = { (rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2 }; + const MapCoord center = { (bounds.left + bounds.right) / 2, (bounds.top + bounds.bottom) / 2 }; auto lowest_height = std::numeric_limits::min(); for (const auto& cell : cells) { - int offx = cell.X - center.X; - int offy = cell.Y - center.Y; + int offset_x = cell.X - center.X; + int offset_y = cell.Y - center.Y; - const auto pCell = CMapData::Instance->TryGetCellAt(X + offx, Y + offy); + const auto pCell = CMapData::Instance->TryGetCellAt(X + offset_x, Y + offset_y); if (pCell->Height < lowest_height) lowest_height = pCell->Height; } @@ -164,10 +164,10 @@ void MultiSelection::Paste(int X, int Y, int nBaseHeight, MyClipboardData* data, nBaseHeight += lowest_height; for (const auto& cell : cells) { - int offx = cell.X - center.X; - int offy = cell.Y - center.Y; + int offset_x = cell.X - center.X; + int offset_y = cell.Y - center.Y; - auto nCellIndex = CMapData::Instance->GetCoordIndex(X + offx, Y + offy); + auto nCellIndex = CMapData::Instance->GetCoordIndex(X + offset_x, Y + offset_y); if (nCellIndex < 0 || nCellIndex >= CMapData::Instance->CellDataCount) continue; @@ -187,7 +187,7 @@ void MultiSelection::Paste(int X, int Y, int nBaseHeight, MyClipboardData* data, pCell->IceGrowth = cell.IceGrowth; pCell->Flag = cell.Flag; - CMapData::Instance->UpdateMapPreviewAt(X + offx, Y + offy); + CMapData::Instance->UpdateMapPreviewAt(X + offset_x, Y + offset_y); } } @@ -433,11 +433,15 @@ DEFINE_HOOK(435F10, CFinalSunDlg_Tools_Copy, 7) pThis->PlaySound(CFinalSunDlg::FASoundType::Normal); + Logger::Raw("Before Call MultiSelection::GetCount!\n"); + if (MultiSelection::GetCount()) MultiSelection::Copy(); else CIsoView::CurrentCommand->Command = FACurrentCommand::TileCopy; + Logger::Raw("After Call MultiSelection::GetCount!\n"); + return 0x435F24; } @@ -456,7 +460,7 @@ DEFINE_HOOK(4C3850, CMapData_PasteAt, 8) if (ptr) { - if (reinterpret_cast(ptr)[0] == 0) // Multiselection + if (reinterpret_cast(ptr)[0] == 0) // Multi-selection { const auto length = reinterpret_cast(ptr)[1]; const int identifier = reinterpret_cast(ptr)[2]; diff --git a/MFC42 b/MFC42 index 8c1e6b2..d9c904c 160000 --- a/MFC42 +++ b/MFC42 @@ -1 +1 @@ -Subproject commit 8c1e6b20b916a6521b35b97c4ccd817ff87b1685 +Subproject commit d9c904c3405bd8735dbba258f06bd1aadcc41cc8 From 442ec188aebd35effe547a85c8921ca1ecdcd761 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Sun, 30 Apr 2023 14:36:28 +0800 Subject: [PATCH 28/30] Add CPUReport --- FA2sp.vcxproj | 6 + FA2sp.vcxproj.filters | 22 +- FA2sp/Algorithms/sha1.cpp | 336 ++----------------------------- FA2sp/Algorithms/sha1.h | 73 +------ FA2sp/Algorithms/sha1impl.cpp | 326 ++++++++++++++++++++++++++++++ FA2sp/Algorithms/sha1impl.h | 76 +++++++ FA2sp/Algorithms/sha1x86.cpp | 192 ++++++++++++++++++ FA2sp/Algorithms/sha1x86.h | 11 + FA2sp/Ext/CLoading/Body.cpp | 12 +- FA2sp/FA2sp.cpp | 4 + FA2sp/Helpers/InstructionSet.cpp | 3 + FA2sp/Helpers/InstructionSet.h | 265 ++++++++++++++++++++++++ FA2sp/Miscs/SaveMap.cpp | 17 +- 13 files changed, 943 insertions(+), 400 deletions(-) create mode 100644 FA2sp/Algorithms/sha1impl.cpp create mode 100644 FA2sp/Algorithms/sha1impl.h create mode 100644 FA2sp/Algorithms/sha1x86.cpp create mode 100644 FA2sp/Algorithms/sha1x86.h create mode 100644 FA2sp/Helpers/InstructionSet.cpp create mode 100644 FA2sp/Helpers/InstructionSet.h diff --git a/FA2sp.vcxproj b/FA2sp.vcxproj index d4df572..3dba661 100644 --- a/FA2sp.vcxproj +++ b/FA2sp.vcxproj @@ -137,6 +137,7 @@ + @@ -194,6 +195,7 @@ + @@ -216,6 +218,7 @@ + @@ -240,6 +243,7 @@ + @@ -271,6 +275,7 @@ + @@ -288,6 +293,7 @@ + diff --git a/FA2sp.vcxproj.filters b/FA2sp.vcxproj.filters index 1daaf7a..9f8635d 100644 --- a/FA2sp.vcxproj.filters +++ b/FA2sp.vcxproj.filters @@ -165,7 +165,7 @@ 头文件 - + 头文件 @@ -186,6 +186,15 @@ 头文件 + + 头文件 + + + 头文件 + + + 头文件 + @@ -452,7 +461,7 @@ 源文件 - + 源文件 @@ -497,6 +506,15 @@ 源文件 + + 源文件 + + + 源文件 + + + 源文件 + diff --git a/FA2sp/Algorithms/sha1.cpp b/FA2sp/Algorithms/sha1.cpp index 5293379..4d14754 100644 --- a/FA2sp/Algorithms/sha1.cpp +++ b/FA2sp/Algorithms/sha1.cpp @@ -1,326 +1,26 @@ -// ////////////////////////////////////////////////////////// -// sha1.cpp -// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved. -// see http://create.stephan-brumme.com/disclaimer.html -// - #include "sha1.h" -// big endian architectures need #define __BYTE_ORDER __BIG_ENDIAN -#ifndef _MSC_VER -#include -#endif +#include "sha1impl.h" +#include "sha1x86.h" +#include "../Helpers/InstructionSet.h" -/// same as reset() -SHA1::SHA1() +void SHA1::hash(unsigned char buffer[20], const void* source, size_t length) { - reset(); -} - - -/// restart -void SHA1::reset() -{ - m_numBytes = 0; - m_bufferSize = 0; - - // according to RFC 1321 - m_hash[0] = 0x67452301; - m_hash[1] = 0xefcdab89; - m_hash[2] = 0x98badcfe; - m_hash[3] = 0x10325476; - m_hash[4] = 0xc3d2e1f0; -} - - -namespace -{ - // mix functions for processBlock() - inline uint32_t f1(uint32_t b, uint32_t c, uint32_t d) - { - return d ^ (b & (c ^ d)); // original: f = (b & c) | ((~b) & d); - } - - inline uint32_t f2(uint32_t b, uint32_t c, uint32_t d) + // if (InstructionSet::SHA()) + // { + // auto state = reinterpret_cast(buffer); + // state[0] = 0x67452301; + // state[1] = 0xefcdab89; + // state[2] = 0x98badcfe; + // state[3] = 0x10325476; + // state[4] = 0xc3d2e1f0; + // sha1x86::sha1_compress(state, reinterpret_cast(source), length); + // } + // else { - return b ^ c ^ d; - } - - inline uint32_t f3(uint32_t b, uint32_t c, uint32_t d) - { - return (b & c) | (b & d) | (c & d); - } - - inline uint32_t rotate(uint32_t a, uint32_t c) - { - return (a << c) | (a >> (32 - c)); - } - - inline uint32_t swap(uint32_t x) - { -#if defined(__GNUC__) || defined(__clang__) - return __builtin_bswap32(x); -#endif -#ifdef MSC_VER - return _byteswap_ulong(x); -#endif - - return (x >> 24) | - ((x >> 8) & 0x0000FF00) | - ((x << 8) & 0x00FF0000) | - (x << 24); + SHA1Impl engine; + engine.add(source, length); + engine.getHash(buffer); } } - - -/// process 64 bytes -void SHA1::processBlock(const void* data) -{ - // get last hash - uint32_t a = m_hash[0]; - uint32_t b = m_hash[1]; - uint32_t c = m_hash[2]; - uint32_t d = m_hash[3]; - uint32_t e = m_hash[4]; - - // data represented as 16x 32-bit words - const uint32_t* input = (uint32_t*)data; - // convert to big endian - uint32_t words[80]; - for (int i = 0; i < 16; i++) -#if defined(__BYTE_ORDER) && (__BYTE_ORDER != 0) && (__BYTE_ORDER == __BIG_ENDIAN) - words[i] = input[i]; -#else - words[i] = swap(input[i]); -#endif - - // extend to 80 words - for (int i = 16; i < 80; i++) - words[i] = rotate(words[i - 3] ^ words[i - 8] ^ words[i - 14] ^ words[i - 16], 1); - - // first round - for (int i = 0; i < 4; i++) - { - int offset = 5 * i; - e += rotate(a, 5) + f1(b, c, d) + words[offset] + 0x5a827999; b = rotate(b, 30); - d += rotate(e, 5) + f1(a, b, c) + words[offset + 1] + 0x5a827999; a = rotate(a, 30); - c += rotate(d, 5) + f1(e, a, b) + words[offset + 2] + 0x5a827999; e = rotate(e, 30); - b += rotate(c, 5) + f1(d, e, a) + words[offset + 3] + 0x5a827999; d = rotate(d, 30); - a += rotate(b, 5) + f1(c, d, e) + words[offset + 4] + 0x5a827999; c = rotate(c, 30); - } - - // second round - for (int i = 4; i < 8; i++) - { - int offset = 5 * i; - e += rotate(a, 5) + f2(b, c, d) + words[offset] + 0x6ed9eba1; b = rotate(b, 30); - d += rotate(e, 5) + f2(a, b, c) + words[offset + 1] + 0x6ed9eba1; a = rotate(a, 30); - c += rotate(d, 5) + f2(e, a, b) + words[offset + 2] + 0x6ed9eba1; e = rotate(e, 30); - b += rotate(c, 5) + f2(d, e, a) + words[offset + 3] + 0x6ed9eba1; d = rotate(d, 30); - a += rotate(b, 5) + f2(c, d, e) + words[offset + 4] + 0x6ed9eba1; c = rotate(c, 30); - } - - // third round - for (int i = 8; i < 12; i++) - { - int offset = 5 * i; - e += rotate(a, 5) + f3(b, c, d) + words[offset] + 0x8f1bbcdc; b = rotate(b, 30); - d += rotate(e, 5) + f3(a, b, c) + words[offset + 1] + 0x8f1bbcdc; a = rotate(a, 30); - c += rotate(d, 5) + f3(e, a, b) + words[offset + 2] + 0x8f1bbcdc; e = rotate(e, 30); - b += rotate(c, 5) + f3(d, e, a) + words[offset + 3] + 0x8f1bbcdc; d = rotate(d, 30); - a += rotate(b, 5) + f3(c, d, e) + words[offset + 4] + 0x8f1bbcdc; c = rotate(c, 30); - } - - // fourth round - for (int i = 12; i < 16; i++) - { - int offset = 5 * i; - e += rotate(a, 5) + f2(b, c, d) + words[offset] + 0xca62c1d6; b = rotate(b, 30); - d += rotate(e, 5) + f2(a, b, c) + words[offset + 1] + 0xca62c1d6; a = rotate(a, 30); - c += rotate(d, 5) + f2(e, a, b) + words[offset + 2] + 0xca62c1d6; e = rotate(e, 30); - b += rotate(c, 5) + f2(d, e, a) + words[offset + 3] + 0xca62c1d6; d = rotate(d, 30); - a += rotate(b, 5) + f2(c, d, e) + words[offset + 4] + 0xca62c1d6; c = rotate(c, 30); - } - - // update hash - m_hash[0] += a; - m_hash[1] += b; - m_hash[2] += c; - m_hash[3] += d; - m_hash[4] += e; -} - - -/// add arbitrary number of bytes -void SHA1::add(const void* data, size_t numBytes) -{ - const uint8_t* current = (const uint8_t*)data; - - if (m_bufferSize > 0) - { - while (numBytes > 0 && m_bufferSize < BlockSize) - { - m_buffer[m_bufferSize++] = *current++; - numBytes--; - } - } - - // full buffer - if (m_bufferSize == BlockSize) - { - processBlock((void*)m_buffer); - m_numBytes += BlockSize; - m_bufferSize = 0; - } - - // no more data ? - if (numBytes == 0) - return; - - // process full blocks - while (numBytes >= BlockSize) - { - processBlock(current); - current += BlockSize; - m_numBytes += BlockSize; - numBytes -= BlockSize; - } - - // keep remaining bytes in buffer - while (numBytes > 0) - { - m_buffer[m_bufferSize++] = *current++; - numBytes--; - } -} - - -/// process final block, less than 64 bytes -void SHA1::processBuffer() -{ - // the input bytes are considered as bits strings, where the first bit is the most significant bit of the byte - - // - append "1" bit to message - // - append "0" bits until message length in bit mod 512 is 448 - // - append length as 64 bit integer - - // number of bits - size_t paddedLength = m_bufferSize * 8; - - // plus one bit set to 1 (always appended) - paddedLength++; - - // number of bits must be (numBits % 512) = 448 - size_t lower11Bits = paddedLength & 511; - if (lower11Bits <= 448) - paddedLength += 448 - lower11Bits; - else - paddedLength += 512 + 448 - lower11Bits; - // convert from bits to bytes - paddedLength /= 8; - - // only needed if additional data flows over into a second block - unsigned char extra[BlockSize]; - - // append a "1" bit, 128 => binary 10000000 - if (m_bufferSize < BlockSize) - m_buffer[m_bufferSize] = 128; - else - extra[0] = 128; - - size_t i; - for (i = m_bufferSize + 1; i < BlockSize; i++) - m_buffer[i] = 0; - for (; i < paddedLength; i++) - extra[i - BlockSize] = 0; - - // add message length in bits as 64 bit number - uint64_t msgBits = 8 * (m_numBytes + m_bufferSize); - // find right position - unsigned char* addLength; - if (paddedLength < BlockSize) - addLength = m_buffer + paddedLength; - else - addLength = extra + paddedLength - BlockSize; - - // must be big endian - *addLength++ = (unsigned char)((msgBits >> 56) & 0xFF); - *addLength++ = (unsigned char)((msgBits >> 48) & 0xFF); - *addLength++ = (unsigned char)((msgBits >> 40) & 0xFF); - *addLength++ = (unsigned char)((msgBits >> 32) & 0xFF); - *addLength++ = (unsigned char)((msgBits >> 24) & 0xFF); - *addLength++ = (unsigned char)((msgBits >> 16) & 0xFF); - *addLength++ = (unsigned char)((msgBits >> 8) & 0xFF); - *addLength = (unsigned char)(msgBits & 0xFF); - - // process blocks - processBlock(m_buffer); - // flowed over into a second block ? - if (paddedLength > BlockSize) - processBlock(extra); -} - - -/// return latest hash as 40 hex characters -std::string SHA1::getHash() -{ - // compute hash (as raw bytes) - unsigned char rawHash[HashBytes]; - getHash(rawHash); - - // convert to hex string - std::string result; - result.reserve(2 * HashBytes); - for (int i = 0; i < HashBytes; i++) - { - static const char dec2hex[16 + 1] = "0123456789abcdef"; - result += dec2hex[(rawHash[i] >> 4) & 15]; - result += dec2hex[rawHash[i] & 15]; - } - - return result; -} - - -/// return latest hash as bytes -void SHA1::getHash(unsigned char buffer[SHA1::HashBytes]) -{ - // save old hash if buffer is partially filled - uint32_t oldHash[HashValues]; - for (int i = 0; i < HashValues; i++) - oldHash[i] = m_hash[i]; - - // process remaining bytes - processBuffer(); - - unsigned char* current = buffer; - for (int i = 0; i < HashValues; i++) - { - *current++ = (m_hash[i] >> 24) & 0xFF; - *current++ = (m_hash[i] >> 16) & 0xFF; - *current++ = (m_hash[i] >> 8) & 0xFF; - *current++ = m_hash[i] & 0xFF; - - // restore old hash - m_hash[i] = oldHash[i]; - } -} - - -/// compute SHA1 of a memory block -std::string SHA1::operator()(const void* data, size_t numBytes) -{ - reset(); - add(data, numBytes); - return getHash(); -} - - -/// compute SHA1 of a string, excluding final zero -std::string SHA1::operator()(const std::string& text) -{ - reset(); - add(text.c_str(), text.size()); - return getHash(); -} \ No newline at end of file diff --git a/FA2sp/Algorithms/sha1.h b/FA2sp/Algorithms/sha1.h index 7625c8c..f03b0c5 100644 --- a/FA2sp/Algorithms/sha1.h +++ b/FA2sp/Algorithms/sha1.h @@ -1,76 +1,9 @@ -// ////////////////////////////////////////////////////////// -// sha1.h -// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved. -// see http://create.stephan-brumme.com/disclaimer.html -// - #pragma once -//#include "hash.h" -#include - -// define fixed size integer types -#ifdef _MSC_VER -// Windows -typedef unsigned __int8 uint8_t; -typedef unsigned __int32 uint32_t; -typedef unsigned __int64 uint64_t; -#else -// GCC -#include -#endif - +#include -/// compute SHA1 hash -/** Usage: - SHA1 sha1; - std::string myHash = sha1("Hello World"); // std::string - std::string myHash2 = sha1("How are you", 11); // arbitrary data, 11 bytes - // or in a streaming fashion: - SHA1 sha1; - while (more data available) - sha1.add(pointer to fresh data, number of new bytes); - std::string myHash3 = sha1.getHash(); - */ -class SHA1 //: public Hash +class SHA1 { public: - /// split into 64 byte blocks (=> 512 bits), hash is 20 bytes long - enum { BlockSize = 512 / 8, HashBytes = 20 }; - - /// same as reset() - SHA1(); - - /// compute SHA1 of a memory block - std::string operator()(const void* data, size_t numBytes); - /// compute SHA1 of a string, excluding final zero - std::string operator()(const std::string& text); - - /// add arbitrary number of bytes - void add(const void* data, size_t numBytes); - - /// return latest hash as 40 hex characters - std::string getHash(); - /// return latest hash as bytes - void getHash(unsigned char buffer[HashBytes]); - - /// restart - void reset(); - -private: - /// process 64 bytes - void processBlock(const void* data); - /// process everything left in the internal buffer - void processBuffer(); - - /// size of processed data in bytes - uint64_t m_numBytes; - /// valid bytes in m_buffer - size_t m_bufferSize; - /// bytes not processed yet - uint8_t m_buffer[BlockSize]; - - enum { HashValues = HashBytes / 4 }; - /// hash, stored as integers - uint32_t m_hash[HashValues]; + static void hash(unsigned char buffer[20], const void* source, size_t length); }; \ No newline at end of file diff --git a/FA2sp/Algorithms/sha1impl.cpp b/FA2sp/Algorithms/sha1impl.cpp new file mode 100644 index 0000000..db79f07 --- /dev/null +++ b/FA2sp/Algorithms/sha1impl.cpp @@ -0,0 +1,326 @@ +// ////////////////////////////////////////////////////////// +// sha1.cpp +// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved. +// see http://create.stephan-brumme.com/disclaimer.html +// + +#include "sha1impl.h" + +// big endian architectures need #define __BYTE_ORDER __BIG_ENDIAN +#ifndef _MSC_VER +#include +#endif + + +/// same as reset() +SHA1Impl::SHA1Impl() +{ + reset(); +} + + +/// restart +void SHA1Impl::reset() +{ + m_numBytes = 0; + m_bufferSize = 0; + + // according to RFC 1321 + m_hash[0] = 0x67452301; + m_hash[1] = 0xefcdab89; + m_hash[2] = 0x98badcfe; + m_hash[3] = 0x10325476; + m_hash[4] = 0xc3d2e1f0; +} + + +namespace +{ + // mix functions for processBlock() + inline uint32_t f1(uint32_t b, uint32_t c, uint32_t d) + { + return d ^ (b & (c ^ d)); // original: f = (b & c) | ((~b) & d); + } + + inline uint32_t f2(uint32_t b, uint32_t c, uint32_t d) + { + return b ^ c ^ d; + } + + inline uint32_t f3(uint32_t b, uint32_t c, uint32_t d) + { + return (b & c) | (b & d) | (c & d); + } + + inline uint32_t rotate(uint32_t a, uint32_t c) + { + return (a << c) | (a >> (32 - c)); + } + + inline uint32_t swap(uint32_t x) + { +#if defined(__GNUC__) || defined(__clang__) + return __builtin_bswap32(x); +#endif +#ifdef MSC_VER + return _byteswap_ulong(x); +#endif + + return (x >> 24) | + ((x >> 8) & 0x0000FF00) | + ((x << 8) & 0x00FF0000) | + (x << 24); + } +} + + +/// process 64 bytes +void SHA1Impl::processBlock(const void* data) +{ + // get last hash + uint32_t a = m_hash[0]; + uint32_t b = m_hash[1]; + uint32_t c = m_hash[2]; + uint32_t d = m_hash[3]; + uint32_t e = m_hash[4]; + + // data represented as 16x 32-bit words + const uint32_t* input = (uint32_t*)data; + // convert to big endian + uint32_t words[80]; + for (int i = 0; i < 16; i++) +#if defined(__BYTE_ORDER) && (__BYTE_ORDER != 0) && (__BYTE_ORDER == __BIG_ENDIAN) + words[i] = input[i]; +#else + words[i] = swap(input[i]); +#endif + + // extend to 80 words + for (int i = 16; i < 80; i++) + words[i] = rotate(words[i - 3] ^ words[i - 8] ^ words[i - 14] ^ words[i - 16], 1); + + // first round + for (int i = 0; i < 4; i++) + { + int offset = 5 * i; + e += rotate(a, 5) + f1(b, c, d) + words[offset] + 0x5a827999; b = rotate(b, 30); + d += rotate(e, 5) + f1(a, b, c) + words[offset + 1] + 0x5a827999; a = rotate(a, 30); + c += rotate(d, 5) + f1(e, a, b) + words[offset + 2] + 0x5a827999; e = rotate(e, 30); + b += rotate(c, 5) + f1(d, e, a) + words[offset + 3] + 0x5a827999; d = rotate(d, 30); + a += rotate(b, 5) + f1(c, d, e) + words[offset + 4] + 0x5a827999; c = rotate(c, 30); + } + + // second round + for (int i = 4; i < 8; i++) + { + int offset = 5 * i; + e += rotate(a, 5) + f2(b, c, d) + words[offset] + 0x6ed9eba1; b = rotate(b, 30); + d += rotate(e, 5) + f2(a, b, c) + words[offset + 1] + 0x6ed9eba1; a = rotate(a, 30); + c += rotate(d, 5) + f2(e, a, b) + words[offset + 2] + 0x6ed9eba1; e = rotate(e, 30); + b += rotate(c, 5) + f2(d, e, a) + words[offset + 3] + 0x6ed9eba1; d = rotate(d, 30); + a += rotate(b, 5) + f2(c, d, e) + words[offset + 4] + 0x6ed9eba1; c = rotate(c, 30); + } + + // third round + for (int i = 8; i < 12; i++) + { + int offset = 5 * i; + e += rotate(a, 5) + f3(b, c, d) + words[offset] + 0x8f1bbcdc; b = rotate(b, 30); + d += rotate(e, 5) + f3(a, b, c) + words[offset + 1] + 0x8f1bbcdc; a = rotate(a, 30); + c += rotate(d, 5) + f3(e, a, b) + words[offset + 2] + 0x8f1bbcdc; e = rotate(e, 30); + b += rotate(c, 5) + f3(d, e, a) + words[offset + 3] + 0x8f1bbcdc; d = rotate(d, 30); + a += rotate(b, 5) + f3(c, d, e) + words[offset + 4] + 0x8f1bbcdc; c = rotate(c, 30); + } + + // fourth round + for (int i = 12; i < 16; i++) + { + int offset = 5 * i; + e += rotate(a, 5) + f2(b, c, d) + words[offset] + 0xca62c1d6; b = rotate(b, 30); + d += rotate(e, 5) + f2(a, b, c) + words[offset + 1] + 0xca62c1d6; a = rotate(a, 30); + c += rotate(d, 5) + f2(e, a, b) + words[offset + 2] + 0xca62c1d6; e = rotate(e, 30); + b += rotate(c, 5) + f2(d, e, a) + words[offset + 3] + 0xca62c1d6; d = rotate(d, 30); + a += rotate(b, 5) + f2(c, d, e) + words[offset + 4] + 0xca62c1d6; c = rotate(c, 30); + } + + // update hash + m_hash[0] += a; + m_hash[1] += b; + m_hash[2] += c; + m_hash[3] += d; + m_hash[4] += e; +} + + +/// add arbitrary number of bytes +void SHA1Impl::add(const void* data, size_t numBytes) +{ + const uint8_t* current = (const uint8_t*)data; + + if (m_bufferSize > 0) + { + while (numBytes > 0 && m_bufferSize < BlockSize) + { + m_buffer[m_bufferSize++] = *current++; + numBytes--; + } + } + + // full buffer + if (m_bufferSize == BlockSize) + { + processBlock((void*)m_buffer); + m_numBytes += BlockSize; + m_bufferSize = 0; + } + + // no more data ? + if (numBytes == 0) + return; + + // process full blocks + while (numBytes >= BlockSize) + { + processBlock(current); + current += BlockSize; + m_numBytes += BlockSize; + numBytes -= BlockSize; + } + + // keep remaining bytes in buffer + while (numBytes > 0) + { + m_buffer[m_bufferSize++] = *current++; + numBytes--; + } +} + + +/// process final block, less than 64 bytes +void SHA1Impl::processBuffer() +{ + // the input bytes are considered as bits strings, where the first bit is the most significant bit of the byte + + // - append "1" bit to message + // - append "0" bits until message length in bit mod 512 is 448 + // - append length as 64 bit integer + + // number of bits + size_t paddedLength = m_bufferSize * 8; + + // plus one bit set to 1 (always appended) + paddedLength++; + + // number of bits must be (numBits % 512) = 448 + size_t lower11Bits = paddedLength & 511; + if (lower11Bits <= 448) + paddedLength += 448 - lower11Bits; + else + paddedLength += 512 + 448 - lower11Bits; + // convert from bits to bytes + paddedLength /= 8; + + // only needed if additional data flows over into a second block + unsigned char extra[BlockSize]; + + // append a "1" bit, 128 => binary 10000000 + if (m_bufferSize < BlockSize) + m_buffer[m_bufferSize] = 128; + else + extra[0] = 128; + + size_t i; + for (i = m_bufferSize + 1; i < BlockSize; i++) + m_buffer[i] = 0; + for (; i < paddedLength; i++) + extra[i - BlockSize] = 0; + + // add message length in bits as 64 bit number + uint64_t msgBits = 8 * (m_numBytes + m_bufferSize); + // find right position + unsigned char* addLength; + if (paddedLength < BlockSize) + addLength = m_buffer + paddedLength; + else + addLength = extra + paddedLength - BlockSize; + + // must be big endian + *addLength++ = (unsigned char)((msgBits >> 56) & 0xFF); + *addLength++ = (unsigned char)((msgBits >> 48) & 0xFF); + *addLength++ = (unsigned char)((msgBits >> 40) & 0xFF); + *addLength++ = (unsigned char)((msgBits >> 32) & 0xFF); + *addLength++ = (unsigned char)((msgBits >> 24) & 0xFF); + *addLength++ = (unsigned char)((msgBits >> 16) & 0xFF); + *addLength++ = (unsigned char)((msgBits >> 8) & 0xFF); + *addLength = (unsigned char)(msgBits & 0xFF); + + // process blocks + processBlock(m_buffer); + // flowed over into a second block ? + if (paddedLength > BlockSize) + processBlock(extra); +} + + +/// return latest hash as 40 hex characters +std::string SHA1Impl::getHash() +{ + // compute hash (as raw bytes) + unsigned char rawHash[HashBytes]; + getHash(rawHash); + + // convert to hex string + std::string result; + result.reserve(2 * HashBytes); + for (int i = 0; i < HashBytes; i++) + { + static const char dec2hex[16 + 1] = "0123456789abcdef"; + result += dec2hex[(rawHash[i] >> 4) & 15]; + result += dec2hex[rawHash[i] & 15]; + } + + return result; +} + + +/// return latest hash as bytes +void SHA1Impl::getHash(unsigned char buffer[SHA1Impl::HashBytes]) +{ + // save old hash if buffer is partially filled + uint32_t oldHash[HashValues]; + for (int i = 0; i < HashValues; i++) + oldHash[i] = m_hash[i]; + + // process remaining bytes + processBuffer(); + + unsigned char* current = buffer; + for (int i = 0; i < HashValues; i++) + { + *current++ = (m_hash[i] >> 24) & 0xFF; + *current++ = (m_hash[i] >> 16) & 0xFF; + *current++ = (m_hash[i] >> 8) & 0xFF; + *current++ = m_hash[i] & 0xFF; + + // restore old hash + m_hash[i] = oldHash[i]; + } +} + + +/// compute SHA1 of a memory block +std::string SHA1Impl::operator()(const void* data, size_t numBytes) +{ + reset(); + add(data, numBytes); + return getHash(); +} + + +/// compute SHA1 of a string, excluding final zero +std::string SHA1Impl::operator()(const std::string& text) +{ + reset(); + add(text.c_str(), text.size()); + return getHash(); +} \ No newline at end of file diff --git a/FA2sp/Algorithms/sha1impl.h b/FA2sp/Algorithms/sha1impl.h new file mode 100644 index 0000000..5210050 --- /dev/null +++ b/FA2sp/Algorithms/sha1impl.h @@ -0,0 +1,76 @@ +// ////////////////////////////////////////////////////////// +// sha1.h +// Copyright (c) 2014,2015 Stephan Brumme. All rights reserved. +// see http://create.stephan-brumme.com/disclaimer.html +// + +#pragma once + +//#include "hash.h" +#include + +// define fixed size integer types +#ifdef _MSC_VER +// Windows +typedef unsigned __int8 uint8_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +#else +// GCC +#include +#endif + + +/// compute SHA1 hash +/** Usage: + SHA1 sha1; + std::string myHash = sha1("Hello World"); // std::string + std::string myHash2 = sha1("How are you", 11); // arbitrary data, 11 bytes + // or in a streaming fashion: + SHA1 sha1; + while (more data available) + sha1.add(pointer to fresh data, number of new bytes); + std::string myHash3 = sha1.getHash(); + */ +class SHA1Impl //: public Hash +{ +public: + /// split into 64 byte blocks (=> 512 bits), hash is 20 bytes long + enum { BlockSize = 512 / 8, HashBytes = 20 }; + + /// same as reset() + SHA1Impl(); + + /// compute SHA1 of a memory block + std::string operator()(const void* data, size_t numBytes); + /// compute SHA1 of a string, excluding final zero + std::string operator()(const std::string& text); + + /// add arbitrary number of bytes + void add(const void* data, size_t numBytes); + + /// return latest hash as 40 hex characters + std::string getHash(); + /// return latest hash as bytes + void getHash(unsigned char buffer[HashBytes]); + + /// restart + void reset(); + +private: + /// process 64 bytes + void processBlock(const void* data); + /// process everything left in the internal buffer + void processBuffer(); + + /// size of processed data in bytes + uint64_t m_numBytes; + /// valid bytes in m_buffer + size_t m_bufferSize; + /// bytes not processed yet + uint8_t m_buffer[BlockSize]; + + enum { HashValues = HashBytes / 4 }; + /// hash, stored as integers + uint32_t m_hash[HashValues]; +}; \ No newline at end of file diff --git a/FA2sp/Algorithms/sha1x86.cpp b/FA2sp/Algorithms/sha1x86.cpp new file mode 100644 index 0000000..433bf2b --- /dev/null +++ b/FA2sp/Algorithms/sha1x86.cpp @@ -0,0 +1,192 @@ +#include "sha1x86.h" + +#include + +void sha1x86::sha1_compress_x86(uint32_t state[5], const uint8_t input[], size_t blocks) +{ + const __m128i MASK = _mm_set_epi64x(0x0001020304050607ULL, 0x08090a0b0c0d0e0fULL); + const __m128i* input_mm = reinterpret_cast(input); + + // Load initial values + __m128i ABCD = _mm_loadu_si128((__m128i*) state); + __m128i E0 = _mm_set_epi32(state[4], 0, 0, 0); + ABCD = _mm_shuffle_epi32(ABCD, 0x1B); + + while (blocks) + { + // Save current hash + const __m128i ABCD_SAVE = ABCD; + const __m128i E0_SAVE = E0; + + __m128i MSG0, MSG1, MSG2, MSG3; + __m128i E1; + + // Rounds 0-3 + MSG0 = _mm_loadu_si128(input_mm + 0); + MSG0 = _mm_shuffle_epi8(MSG0, MASK); + E0 = _mm_add_epi32(E0, MSG0); + E1 = ABCD; + ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0); + + // Rounds 4-7 + MSG1 = _mm_loadu_si128(input_mm + 1); + MSG1 = _mm_shuffle_epi8(MSG1, MASK); + E1 = _mm_sha1nexte_epu32(E1, MSG1); + E0 = ABCD; + ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 0); + MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1); + + // Rounds 8-11 + MSG2 = _mm_loadu_si128(input_mm + 2); + MSG2 = _mm_shuffle_epi8(MSG2, MASK); + E0 = _mm_sha1nexte_epu32(E0, MSG2); + E1 = ABCD; + ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0); + MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2); + MSG0 = _mm_xor_si128(MSG0, MSG2); + + // Rounds 12-15 + MSG3 = _mm_loadu_si128(input_mm + 3); + MSG3 = _mm_shuffle_epi8(MSG3, MASK); + E1 = _mm_sha1nexte_epu32(E1, MSG3); + E0 = ABCD; + MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3); + ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 0); + MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3); + MSG1 = _mm_xor_si128(MSG1, MSG3); + + // Rounds 16-19 + E0 = _mm_sha1nexte_epu32(E0, MSG0); + E1 = ABCD; + MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0); + ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0); + MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0); + MSG2 = _mm_xor_si128(MSG2, MSG0); + + // Rounds 20-23 + E1 = _mm_sha1nexte_epu32(E1, MSG1); + E0 = ABCD; + MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1); + ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1); + MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1); + MSG3 = _mm_xor_si128(MSG3, MSG1); + + // Rounds 24-27 + E0 = _mm_sha1nexte_epu32(E0, MSG2); + E1 = ABCD; + MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2); + ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 1); + MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2); + MSG0 = _mm_xor_si128(MSG0, MSG2); + + // Rounds 28-31 + E1 = _mm_sha1nexte_epu32(E1, MSG3); + E0 = ABCD; + MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3); + ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1); + MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3); + MSG1 = _mm_xor_si128(MSG1, MSG3); + + // Rounds 32-35 + E0 = _mm_sha1nexte_epu32(E0, MSG0); + E1 = ABCD; + MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0); + ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 1); + MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0); + MSG2 = _mm_xor_si128(MSG2, MSG0); + + // Rounds 36-39 + E1 = _mm_sha1nexte_epu32(E1, MSG1); + E0 = ABCD; + MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1); + ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1); + MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1); + MSG3 = _mm_xor_si128(MSG3, MSG1); + + // Rounds 40-43 + E0 = _mm_sha1nexte_epu32(E0, MSG2); + E1 = ABCD; + MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2); + ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2); + MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2); + MSG0 = _mm_xor_si128(MSG0, MSG2); + + // Rounds 44-47 + E1 = _mm_sha1nexte_epu32(E1, MSG3); + E0 = ABCD; + MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3); + ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 2); + MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3); + MSG1 = _mm_xor_si128(MSG1, MSG3); + + // Rounds 48-51 + E0 = _mm_sha1nexte_epu32(E0, MSG0); + E1 = ABCD; + MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0); + ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2); + MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0); + MSG2 = _mm_xor_si128(MSG2, MSG0); + + // Rounds 52-55 + E1 = _mm_sha1nexte_epu32(E1, MSG1); + E0 = ABCD; + MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1); + ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 2); + MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1); + MSG3 = _mm_xor_si128(MSG3, MSG1); + + // Rounds 56-59 + E0 = _mm_sha1nexte_epu32(E0, MSG2); + E1 = ABCD; + MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2); + ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2); + MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2); + MSG0 = _mm_xor_si128(MSG0, MSG2); + + // Rounds 60-63 + E1 = _mm_sha1nexte_epu32(E1, MSG3); + E0 = ABCD; + MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3); + ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3); + MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3); + MSG1 = _mm_xor_si128(MSG1, MSG3); + + // Rounds 64-67 + E0 = _mm_sha1nexte_epu32(E0, MSG0); + E1 = ABCD; + MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0); + ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 3); + MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0); + MSG2 = _mm_xor_si128(MSG2, MSG0); + + // Rounds 68-71 + E1 = _mm_sha1nexte_epu32(E1, MSG1); + E0 = ABCD; + MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1); + ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3); + MSG3 = _mm_xor_si128(MSG3, MSG1); + + // Rounds 72-75 + E0 = _mm_sha1nexte_epu32(E0, MSG2); + E1 = ABCD; + MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2); + ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 3); + + // Rounds 76-79 + E1 = _mm_sha1nexte_epu32(E1, MSG3); + E0 = ABCD; + ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3); + + // Add values back to state + E0 = _mm_sha1nexte_epu32(E0, E0_SAVE); + ABCD = _mm_add_epi32(ABCD, ABCD_SAVE); + + input_mm += 4; + blocks--; + } + + // Save state + ABCD = _mm_shuffle_epi32(ABCD, 0x1B); + _mm_storeu_si128((__m128i*) state, ABCD); + state[4] = _mm_extract_epi32(E0, 3); +} \ No newline at end of file diff --git a/FA2sp/Algorithms/sha1x86.h b/FA2sp/Algorithms/sha1x86.h new file mode 100644 index 0000000..8a1880c --- /dev/null +++ b/FA2sp/Algorithms/sha1x86.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +class sha1x86 +{ +public: + // static void sha1_compress(uint32_t state[5], const uint8_t input[], size_t length); + static void sha1_compress_x86(uint32_t state[5], const uint8_t input[], size_t blocks); +}; \ No newline at end of file diff --git a/FA2sp/Ext/CLoading/Body.cpp b/FA2sp/Ext/CLoading/Body.cpp index 5673834..91417dd 100644 --- a/FA2sp/Ext/CLoading/Body.cpp +++ b/FA2sp/Ext/CLoading/Body.cpp @@ -15,25 +15,25 @@ bool CLoadingExt::InitMixFilesFix() { std::map collector; - for (auto& pair : pSection->GetIndices()) - collector[pair.second] = pair.first; + for (const auto& [key, index] : pSection->GetIndices()) + collector[index] = key; ppmfc::CString path; - for (auto& pair : collector) + for (const auto& [_, key] : collector) { - if (CINI::FAData->GetBool("ExtraMixes", pair.second)) + if (CINI::FAData->GetBool("ExtraMixes", key)) path = CFinalSunApp::Instance->ExePath; else path = CFinalSunApp::Instance->FilePath; - path += "\\" + pair.second; + path += "\\" + key; if (auto id = CMixFile::Open(path, 0)) { Logger::Raw("[MixLoader][EXTRA] %04d - %s loaded.\n", id, path); } else { - Logger::Raw("[MixLoader][EXTRA] %s failed!\n", id); + Logger::Raw("[MixLoader][EXTRA] %s failed!\n", path); } } } diff --git a/FA2sp/FA2sp.cpp b/FA2sp/FA2sp.cpp index d3b6f5f..c52e111 100644 --- a/FA2sp/FA2sp.cpp +++ b/FA2sp/FA2sp.cpp @@ -2,6 +2,7 @@ #include "FA2sp.Constants.h" #include "Helpers/MutexHelper.h" +#include "Helpers/InstructionSet.h" #include "Miscs/Palettes.h" #include "Miscs/VoxelDrawer.h" #include "Miscs/Exception.h" @@ -194,6 +195,9 @@ DEFINE_HOOK(537129, ExeRun, 9) Logger::Info(APPLY_INFO); Logger::Wrap(1); + Logger::Raw("==============================\nCPU Report:\n%s==============================\n", + InstructionSet::Report().c_str()); + if (DetachFromDebugger()) Logger::Info("Syringe detached!\n"); else diff --git a/FA2sp/Helpers/InstructionSet.cpp b/FA2sp/Helpers/InstructionSet.cpp new file mode 100644 index 0000000..46bd4b2 --- /dev/null +++ b/FA2sp/Helpers/InstructionSet.cpp @@ -0,0 +1,3 @@ +#include "InstructionSet.h" + +const InstructionSet::InstructionSet_Internal InstructionSet::CPU_Rep; \ No newline at end of file diff --git a/FA2sp/Helpers/InstructionSet.h b/FA2sp/Helpers/InstructionSet.h new file mode 100644 index 0000000..212e0a0 --- /dev/null +++ b/FA2sp/Helpers/InstructionSet.h @@ -0,0 +1,265 @@ +// https://learn.microsoft.com/zh-cn/cpp/intrinsics/cpuid-cpuidex?view=msvc-170 + +// InstructionSet.cpp +// Compile by using: cl /EHsc /W4 InstructionSet.cpp +// processor: x86, x64 +// Uses the __cpuid intrinsic to get information about +// CPU extended instruction set support. + +#include +#include +#include +#include +#include +#include +#include + +class InstructionSet +{ + // forward declarations + class InstructionSet_Internal; + +public: + // getters + static std::string Vendor(void) { return CPU_Rep.vendor_; } + static std::string Brand(void) { return CPU_Rep.brand_; } + + static bool SSE3(void) { return CPU_Rep.f_1_ECX_[0]; } + static bool PCLMULQDQ(void) { return CPU_Rep.f_1_ECX_[1]; } + static bool MONITOR(void) { return CPU_Rep.f_1_ECX_[3]; } + static bool SSSE3(void) { return CPU_Rep.f_1_ECX_[9]; } + static bool FMA(void) { return CPU_Rep.f_1_ECX_[12]; } + static bool CMPXCHG16B(void) { return CPU_Rep.f_1_ECX_[13]; } + static bool SSE41(void) { return CPU_Rep.f_1_ECX_[19]; } + static bool SSE42(void) { return CPU_Rep.f_1_ECX_[20]; } + static bool MOVBE(void) { return CPU_Rep.f_1_ECX_[22]; } + static bool POPCNT(void) { return CPU_Rep.f_1_ECX_[23]; } + static bool AES(void) { return CPU_Rep.f_1_ECX_[25]; } + static bool XSAVE(void) { return CPU_Rep.f_1_ECX_[26]; } + static bool OSXSAVE(void) { return CPU_Rep.f_1_ECX_[27]; } + static bool AVX(void) { return CPU_Rep.f_1_ECX_[28]; } + static bool F16C(void) { return CPU_Rep.f_1_ECX_[29]; } + static bool RDRAND(void) { return CPU_Rep.f_1_ECX_[30]; } + + static bool MSR(void) { return CPU_Rep.f_1_EDX_[5]; } + static bool CX8(void) { return CPU_Rep.f_1_EDX_[8]; } + static bool SEP(void) { return CPU_Rep.f_1_EDX_[11]; } + static bool CMOV(void) { return CPU_Rep.f_1_EDX_[15]; } + static bool CLFSH(void) { return CPU_Rep.f_1_EDX_[19]; } + static bool MMX(void) { return CPU_Rep.f_1_EDX_[23]; } + static bool FXSR(void) { return CPU_Rep.f_1_EDX_[24]; } + static bool SSE(void) { return CPU_Rep.f_1_EDX_[25]; } + static bool SSE2(void) { return CPU_Rep.f_1_EDX_[26]; } + + static bool FSGSBASE(void) { return CPU_Rep.f_7_EBX_[0]; } + static bool BMI1(void) { return CPU_Rep.f_7_EBX_[3]; } + static bool HLE(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_7_EBX_[4]; } + static bool AVX2(void) { return CPU_Rep.f_7_EBX_[5]; } + static bool BMI2(void) { return CPU_Rep.f_7_EBX_[8]; } + static bool ERMS(void) { return CPU_Rep.f_7_EBX_[9]; } + static bool INVPCID(void) { return CPU_Rep.f_7_EBX_[10]; } + static bool RTM(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_7_EBX_[11]; } + static bool AVX512F(void) { return CPU_Rep.f_7_EBX_[16]; } + static bool RDSEED(void) { return CPU_Rep.f_7_EBX_[18]; } + static bool ADX(void) { return CPU_Rep.f_7_EBX_[19]; } + static bool AVX512PF(void) { return CPU_Rep.f_7_EBX_[26]; } + static bool AVX512ER(void) { return CPU_Rep.f_7_EBX_[27]; } + static bool AVX512CD(void) { return CPU_Rep.f_7_EBX_[28]; } + static bool SHA(void) { return CPU_Rep.f_7_EBX_[29]; } + + static bool PREFETCHWT1(void) { return CPU_Rep.f_7_ECX_[0]; } + + static bool LAHF(void) { return CPU_Rep.f_81_ECX_[0]; } + static bool LZCNT(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_ECX_[5]; } + static bool ABM(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[5]; } + static bool SSE4a(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[6]; } + static bool XOP(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[11]; } + static bool TBM(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_ECX_[21]; } + + static bool SYSCALL(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_EDX_[11]; } + static bool MMXEXT(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[22]; } + static bool RDTSCP(void) { return CPU_Rep.isIntel_ && CPU_Rep.f_81_EDX_[27]; } + static bool _3DNOWEXT(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[30]; } + static bool _3DNOW(void) { return CPU_Rep.isAMD_ && CPU_Rep.f_81_EDX_[31]; } + + static std::string Report() + { + std::ostringstream oss; + + auto& outstream = oss; + + auto support_message = [&outstream](std::string isa_feature, bool is_supported) { + outstream << isa_feature << (is_supported ? " supported" : " not supported") << "\n"; + }; + + outstream << InstructionSet::Vendor() << "\n"; + outstream << InstructionSet::Brand() << "\n"; + + support_message("3DNOW", InstructionSet::_3DNOW()); + support_message("3DNOWEXT", InstructionSet::_3DNOWEXT()); + support_message("ABM", InstructionSet::ABM()); + support_message("ADX", InstructionSet::ADX()); + support_message("AES", InstructionSet::AES()); + support_message("AVX", InstructionSet::AVX()); + support_message("AVX2", InstructionSet::AVX2()); + support_message("AVX512CD", InstructionSet::AVX512CD()); + support_message("AVX512ER", InstructionSet::AVX512ER()); + support_message("AVX512F", InstructionSet::AVX512F()); + support_message("AVX512PF", InstructionSet::AVX512PF()); + support_message("BMI1", InstructionSet::BMI1()); + support_message("BMI2", InstructionSet::BMI2()); + support_message("CLFSH", InstructionSet::CLFSH()); + support_message("CMPXCHG16B", InstructionSet::CMPXCHG16B()); + support_message("CX8", InstructionSet::CX8()); + support_message("ERMS", InstructionSet::ERMS()); + support_message("F16C", InstructionSet::F16C()); + support_message("FMA", InstructionSet::FMA()); + support_message("FSGSBASE", InstructionSet::FSGSBASE()); + support_message("FXSR", InstructionSet::FXSR()); + support_message("HLE", InstructionSet::HLE()); + support_message("INVPCID", InstructionSet::INVPCID()); + support_message("LAHF", InstructionSet::LAHF()); + support_message("LZCNT", InstructionSet::LZCNT()); + support_message("MMX", InstructionSet::MMX()); + support_message("MMXEXT", InstructionSet::MMXEXT()); + support_message("MONITOR", InstructionSet::MONITOR()); + support_message("MOVBE", InstructionSet::MOVBE()); + support_message("MSR", InstructionSet::MSR()); + support_message("OSXSAVE", InstructionSet::OSXSAVE()); + support_message("PCLMULQDQ", InstructionSet::PCLMULQDQ()); + support_message("POPCNT", InstructionSet::POPCNT()); + support_message("PREFETCHWT1", InstructionSet::PREFETCHWT1()); + support_message("RDRAND", InstructionSet::RDRAND()); + support_message("RDSEED", InstructionSet::RDSEED()); + support_message("RDTSCP", InstructionSet::RDTSCP()); + support_message("RTM", InstructionSet::RTM()); + support_message("SEP", InstructionSet::SEP()); + support_message("SHA", InstructionSet::SHA()); + support_message("SSE", InstructionSet::SSE()); + support_message("SSE2", InstructionSet::SSE2()); + support_message("SSE3", InstructionSet::SSE3()); + support_message("SSE4.1", InstructionSet::SSE41()); + support_message("SSE4.2", InstructionSet::SSE42()); + support_message("SSE4a", InstructionSet::SSE4a()); + support_message("SSSE3", InstructionSet::SSSE3()); + support_message("SYSCALL", InstructionSet::SYSCALL()); + support_message("TBM", InstructionSet::TBM()); + support_message("XOP", InstructionSet::XOP()); + support_message("XSAVE", InstructionSet::XSAVE()); + + oss.flush(); + + return oss.str(); + } + +private: + static const InstructionSet_Internal CPU_Rep; + + class InstructionSet_Internal + { + public: + InstructionSet_Internal() + : nIds_{ 0 }, + nExIds_{ 0 }, + isIntel_{ false }, + isAMD_{ false }, + f_1_ECX_{ 0 }, + f_1_EDX_{ 0 }, + f_7_EBX_{ 0 }, + f_7_ECX_{ 0 }, + f_81_ECX_{ 0 }, + f_81_EDX_{ 0 }, + data_{}, + extdata_{} + { + //int cpuInfo[4] = {-1}; + std::array cpui; + + // Calling __cpuid with 0x0 as the function_id argument + // gets the number of the highest valid function ID. + __cpuid(cpui.data(), 0); + nIds_ = cpui[0]; + + for (int i = 0; i <= nIds_; ++i) + { + __cpuidex(cpui.data(), i, 0); + data_.push_back(cpui); + } + + // Capture vendor string + char vendor[0x20]; + memset(vendor, 0, sizeof(vendor)); + *reinterpret_cast(vendor) = data_[0][1]; + *reinterpret_cast(vendor + 4) = data_[0][3]; + *reinterpret_cast(vendor + 8) = data_[0][2]; + vendor_ = vendor; + if (vendor_ == "GenuineIntel") + { + isIntel_ = true; + } + else if (vendor_ == "AuthenticAMD") + { + isAMD_ = true; + } + + // load bitset with flags for function 0x00000001 + if (nIds_ >= 1) + { + f_1_ECX_ = data_[1][2]; + f_1_EDX_ = data_[1][3]; + } + + // load bitset with flags for function 0x00000007 + if (nIds_ >= 7) + { + f_7_EBX_ = data_[7][1]; + f_7_ECX_ = data_[7][2]; + } + + // Calling __cpuid with 0x80000000 as the function_id argument + // gets the number of the highest valid extended ID. + __cpuid(cpui.data(), 0x80000000); + nExIds_ = cpui[0]; + + char brand[0x40]; + memset(brand, 0, sizeof(brand)); + + for (int i = 0x80000000; i <= nExIds_; ++i) + { + __cpuidex(cpui.data(), i, 0); + extdata_.push_back(cpui); + } + + // load bitset with flags for function 0x80000001 + if (nExIds_ >= 0x80000001) + { + f_81_ECX_ = extdata_[1][2]; + f_81_EDX_ = extdata_[1][3]; + } + + // Interpret CPU brand string if reported + if (nExIds_ >= 0x80000004) + { + memcpy(brand, extdata_[2].data(), sizeof(cpui)); + memcpy(brand + 16, extdata_[3].data(), sizeof(cpui)); + memcpy(brand + 32, extdata_[4].data(), sizeof(cpui)); + brand_ = brand; + } + }; + + int nIds_; + int nExIds_; + std::string vendor_; + std::string brand_; + bool isIntel_; + bool isAMD_; + std::bitset<32> f_1_ECX_; + std::bitset<32> f_1_EDX_; + std::bitset<32> f_7_EBX_; + std::bitset<32> f_7_ECX_; + std::bitset<32> f_81_ECX_; + std::bitset<32> f_81_EDX_; + std::vector> data_; + std::vector> extdata_; + }; +}; diff --git a/FA2sp/Miscs/SaveMap.cpp b/FA2sp/Miscs/SaveMap.cpp index 7e6ffe4..c3f4ad8 100644 --- a/FA2sp/Miscs/SaveMap.cpp +++ b/FA2sp/Miscs/SaveMap.cpp @@ -155,11 +155,20 @@ DEFINE_HOOK(428D97, CFinalSunDlg_SaveMap, 7) } // Generate the Digest - SHA1 sha1; - sha1.add(oss.str().data(), oss.str().length()); unsigned char hash[20]; - sha1.getHash(hash); - Logger::Raw("SaveMap : Map SHA1 hash: %s\n", sha1.getHash().c_str()); + const auto& hash_source = oss.str(); + SHA1::hash(hash, hash_source.data(), hash_source.length()); + + char hash_value[64] = { 0 }; + sprintf_s( + hash_value, + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + hash[0], hash[1], hash[2], hash[3], hash[4], + hash[5], hash[6], hash[7], hash[8], hash[9], + hash[10], hash[11], hash[12], hash[13], hash[14], + hash[15], hash[16], hash[17], hash[18], hash[19] + ); + Logger::Raw("SaveMap : Map SHA1 hash: %s\n", hash_value); // As sha1 hash length is only 20, the length of base64 result won't // go over the limitation of uublock's 70 per line. So only one row! From 91781197dbeb60b0dadf0997aa2d2394ec89d36a Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Sun, 30 Apr 2023 15:19:47 +0800 Subject: [PATCH 29/30] Fix the problem that crash the copy function when not enable MultiSelection --- FA2sp/Helpers/InstructionSet.h | 2 +- FA2sp/Miscs/MultiSelection.cpp | 35 ++++++++++++++++------------------ 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/FA2sp/Helpers/InstructionSet.h b/FA2sp/Helpers/InstructionSet.h index 212e0a0..6c6c4a9 100644 --- a/FA2sp/Helpers/InstructionSet.h +++ b/FA2sp/Helpers/InstructionSet.h @@ -88,7 +88,7 @@ class InstructionSet auto& outstream = oss; - auto support_message = [&outstream](std::string isa_feature, bool is_supported) { + auto support_message = [&outstream](const std::string_view&& isa_feature, bool is_supported) { outstream << isa_feature << (is_supported ? " supported" : " not supported") << "\n"; }; diff --git a/FA2sp/Miscs/MultiSelection.cpp b/FA2sp/Miscs/MultiSelection.cpp index a369f85..d32f8dc 100644 --- a/FA2sp/Miscs/MultiSelection.cpp +++ b/FA2sp/Miscs/MultiSelection.cpp @@ -425,25 +425,22 @@ DEFINE_HOOK(433F70, CFinalSunDlg_Tools_HideSingleField, 5) } DEFINE_HOOK(435F10, CFinalSunDlg_Tools_Copy, 7) -{ - if (!ExtConfigs::EnableMultiSelection) - return 0; - - GET(CFinalSunDlg*, pThis, ECX); - - pThis->PlaySound(CFinalSunDlg::FASoundType::Normal); - - Logger::Raw("Before Call MultiSelection::GetCount!\n"); - - if (MultiSelection::GetCount()) - MultiSelection::Copy(); - else - CIsoView::CurrentCommand->Command = FACurrentCommand::TileCopy; - - Logger::Raw("After Call MultiSelection::GetCount!\n"); - - return 0x435F24; -} + { + GET(CFinalSunDlg*, pThis, ECX); + + pThis->PlaySound(CFinalSunDlg::FASoundType::Normal); + + Logger::Raw("Before Call MultiSelection::GetCount!\n"); + + if (ExtConfigs::EnableMultiSelection && MultiSelection::GetCount()) + MultiSelection::Copy(); + else + CIsoView::CurrentCommand->Command = FACurrentCommand::TileCopy; + + Logger::Raw("After Call MultiSelection::GetCount!\n"); + + return 0x435F24; + } DEFINE_HOOK(4C3850, CMapData_PasteAt, 8) { From 9f595d5ee3550bd1f32054a007926ea0c3e52770 Mon Sep 17 00:00:00 2001 From: secsome <302702960@qq.com> Date: Sun, 30 Apr 2023 15:29:11 +0800 Subject: [PATCH 30/30] Support Undo&Redo for Multiselection operations --- CHANGELOG.md | 1 + DOCUMENT.md | 3 +-- FA2pp | 2 +- FA2sp/Miscs/MultiSelection.cpp | 6 ++++++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 718e429..9d19060 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Fixed the bug that lighting is not reset correctly when loading/creating a map - Fixed the bug that smudges and basenodes drifting away when resizing the map - Multiselection now supports copy & paste +- Multiselection operations(raise or lower cells) now supports undo & redo - New ***ExtConfig*** : `ExtendedValidationNoError` = **BOOLEAN**, defaults to false ## RELEASE 1.5.2 (2023-03-03) diff --git a/DOCUMENT.md b/DOCUMENT.md index 89056d7..01492b5 100644 --- a/DOCUMENT.md +++ b/DOCUMENT.md @@ -9,8 +9,7 @@ - For now, I cannot ensure the stability of it, so save your maps frequently before heavy loss! XD The multiple selection function is now available. Press Ctrl key to select tiles and press Ctrl+Shift key to deselect them. Ctrl+D can clear all selected tiles. -Now this feature supports RaiseSingleTile/LowerSingleTile (though they are not "Single" anymore) and calucate selected area ore value. -NOTICE THAT UNDOREDO AND COPYPASTE HASN'T BEEN SUPPORTED YET! +Now this feature supports RaiseSingleTile/LowerSingleTile (though they are not "Single" anymore), copy paste, and calucating selected area ore value. ## BASIC TYPES - **INTEGER** diff --git a/FA2pp b/FA2pp index 289f5d2..dda2da4 160000 --- a/FA2pp +++ b/FA2pp @@ -1 +1 @@ -Subproject commit 289f5d25092c8a126a4896d5533ce1221fbfab60 +Subproject commit dda2da4da0ddd9400d3a741e44c88d2ac7b3b70d diff --git a/FA2sp/Miscs/MultiSelection.cpp b/FA2sp/Miscs/MultiSelection.cpp index d32f8dc..80f8a58 100644 --- a/FA2sp/Miscs/MultiSelection.cpp +++ b/FA2sp/Miscs/MultiSelection.cpp @@ -330,6 +330,7 @@ DEFINE_HOOK(433DA0, CFinalSunDlg_Tools_RaiseSingleTile, 5) { if (MultiSelection::GetCount()) { + CMapData::Instance->SaveUndoRedoData(true, 0, 0, 0, 0); MultiSelection::ApplyForEach( [](CellData& cell) { if (cell.Height < 14) @@ -337,6 +338,8 @@ DEFINE_HOOK(433DA0, CFinalSunDlg_Tools_RaiseSingleTile, 5) } ); pThis->MyViewFrame.pIsoView->RedrawWindow(nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW); + CMapData::Instance->SaveUndoRedoData(true, 0, 0, 0, 0); + CMapData::Instance->DoUndo(); } else { @@ -368,6 +371,7 @@ DEFINE_HOOK(433D30, CFinalSunDlg_Tools_LowerSingleTile, 5) { if (MultiSelection::GetCount()) { + CMapData::Instance->SaveUndoRedoData(true, 0, 0, 0, 0); MultiSelection::ApplyForEach( [](CellData& cell) { if (cell.Height > 0) @@ -375,6 +379,8 @@ DEFINE_HOOK(433D30, CFinalSunDlg_Tools_LowerSingleTile, 5) } ); pThis->MyViewFrame.pIsoView->RedrawWindow(nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW); + CMapData::Instance->SaveUndoRedoData(true, 0, 0, 0, 0); + CMapData::Instance->DoUndo(); } else {