diff --git a/IMPLEMENTATION_CHEAT_SHEET.md b/IMPLEMENTATION_CHEAT_SHEET.md index 2a9d75471..3aba857da 100644 --- a/IMPLEMENTATION_CHEAT_SHEET.md +++ b/IMPLEMENTATION_CHEAT_SHEET.md @@ -305,6 +305,7 @@ SEC_RNG::ptr->currentNumber1 % 4 } ``` Consider this, should a switch structure arise with strange fallthrough and loops, like SHC_3BB0A8C1_0x004870B0. +- Non-consecutive case labels will likely result in a mixture of if-else and switch cases, sometimes even only if-elses. These are hard to spot. One sign, outside of weird decompiler artifacts, is that logic might be put inside a lot of conditions that feature only a single variable. Another can be a lot of GOTOs. ### Loops @@ -357,6 +358,8 @@ you might have found an unrolled loop. Therefore, try to reproduce the logic in If GOTOs are present that clearly jump to the start of a loop, but the logic does not allow to do this without a GOTO, for example from a loop inside a loop, you might be able to move the continue or break condition to the outside. Methods could be placing a fitting condition related to the contained loop conditions after the loop or using a boolean flag that then functions as conditional. Both can sometimes be optimized away. +If an explicit loop condition is removed from the compiler, it is able to prove all paths through the loop. If such a case happens where the condition is actually eliminated from the re-implementation, the re-implementation might be missing a condition that is present in the logic somewhere, but removed from the loop condition itself. The optimization pass failed to detect the resulting predictable loop in this latter case and the condition remains. + A global used as a running counter alongside the loop index is usually incremented inside the loop, even when its final value is a constant. Ghidra shows the folded result: `DAT_CurrentUnitSlotID = 2500;` placed around the loop is really `DAT_CurrentUnitSlotID = 1;` before it and `DAT_CurrentUnitSlotID += 1;` as the first statement of the body. This was @@ -399,6 +402,7 @@ unrolled body, write `for (int i = 1; i != 2500; ++i)` instead of `i < 2500` (SH declared where the original's `xor esi, esi` sits (here before an earlier early-return test, which then compares against that zero register) this took `generateSiegeCreationInformation` from 39.4% to 100%. + ### GOTO A function may contain multiple GOTOs. @@ -449,7 +453,8 @@ so it is built with `/GL`. SHC_3BB0A8C1_0x00504EE0 went from 38.5% to 100% and S ### Parameters Parameters might not be pushed like normal in certain cases. -Usually, if two functions are followed by each other, the parameters are pushed for the first function, then the call is executed and then parameters for the second function are pushed. If parameters for the second function are pushed before the first call, it might indicate that the first call was executed in place of a variable, to directly feed the return into the second function. +Usually, if two functions are followed by each other, the parameters are pushed for the first function, then the call is executed and then parameters for the second function are pushed. If parameters for the second function are pushed before the first call, it might indicate that the first call was executed in place of a variable, to directly feed the return into the second function. +This possibility is actually universal. If a computation of a value happens between pushes of parameters, it makes it likely the value was computed in-place. The other way around is also possible. If a computed value is created completely or partially before any push, it might have been a temporary. ### Implicit functions @@ -478,25 +483,36 @@ They are replaced by specific assembly instructions or otherwise inlined. A list of all intrinsics can be found in `intrin.h` in the std library, but these are mainly very low level instructions. However, there are also C functions that can be replaced by intrinsics. The following attempts to extract these from the header: -| Intrinsic | Does | -| --------- | ---------------------------------------------------------------------------------------------- | -| `memcpy` | Copies a memory block. **Does not support overlapping regions** (use `memmove` for overlap). | -| `memset` | Fills a memory block with a byte value. | -| `memcmp` | Compares two memory blocks byte-by-byte. | -| `memchr` | Searches memory for the first occurrence of a byte. | -| `strcpy` | Copies a null-terminated C string. | -| `strlen` | Returns the length of a null-terminated string (excluding `\0`). | -| `strcmp` | Compares two null-terminated C strings. | -| `strcat` | Appends one null-terminated C string to another. | -| `strncpy` | Copies up to N characters from a string; **may not null-terminate** if the source is too long. | -| `strncmp` | Compares up to N characters of two strings. | -| `wcscpy` | Copies a null-terminated wide-character string. | -| `wcslen` | Returns the length of a null-terminated wide string. | -| `ceil` | Rounds a floating-point value upward to the nearest integer value. | -| `abs` | Returns the absolute value of an `int`. | -| `labs` | Returns the absolute value of a `long`. | -| `longjmp` | Restores a saved execution context created by `setjmp`, continuing execution from that point. | -| `_setjmp` | Saves the current execution context for later restoration with `longjmp`. | +| Intrinsic | Does | +|---------------------------|----------------------------------------------------------------------------------------------------------------------| +| `memcpy` | Copies a memory block. **Does not support overlapping regions** (use `memmove` for overlap). | +| `memset` | Fills a memory block with a byte value. | +| `memcmp` | Compares two memory blocks byte-by-byte. | +| `memchr` | Searches memory for the first occurrence of a byte. | +| `strcpy` | Copies a null-terminated C string. | +| `strlen` | Returns the length of a null-terminated string (excluding `\0`). | +| `strcmp` | Compares two null-terminated C strings. | +| `strcat` | Appends one null-terminated C string to another. | +| `strncpy` | Copies up to N characters from a string; **may not null-terminate** if the source is too long. | +| `strncmp` | Compares up to N characters of two strings. | +| `strset` | Sets every character in a null-terminated string to the specified character. | +| `_strset` | Microsoft-specific version of `strset`; sets every character in a null-terminated string to the specified character. | +| `wcscpy` | Copies a null-terminated wide-character string. | +| `wcslen` | Returns the length of a null-terminated wide string. | +| `wcscmp` | Compares two null-terminated wide-character strings. | +| `wcscat` | Appends one null-terminated wide-character string to another. | +| `wcsncpy` | Copies up to N wide characters; **may not null-terminate** if the source is too long. | +| `wcsncmp` | Compares up to N wide characters of two strings. | +| `_wcsset` | Sets every character in a null-terminated wide string to the specified wide character. | +| `ceil` | Returns the smallest integral floating-point value greater than or equal to the argument. | +| `abs` | Returns the absolute value of an `int`. | +| `labs` | Returns the absolute value of a `long`. | +| `longjmp` | Restores a saved execution context created by `setjmp`, continuing execution from that point. | +| `_setjmp` | Saves the current execution context for later restoration with `longjmp`. | +| `_AddressOfReturnAddress` | Returns the address of the current function's return-address storage. | +| `_WriteBarrier` | Prevents certain compiler memory-write reorderings across the barrier. | +| `__trap` | Generates a processor/compiler trap, optionally with arguments. | + In cases where the decompiler seems to perform one of these actions via simple instructions, one can also try one of these functions. diff --git a/src/OpenSHC/OS/OS.cpp b/src/OpenSHC/OS/OS.cpp index 057972635..686831bf5 100644 --- a/src/OpenSHC/OS/OS.cpp +++ b/src/OpenSHC/OS/OS.cpp @@ -85,7 +85,7 @@ namespace OS { wchar_t* _wcsncpy(wchar_t* _Dest, wchar_t* _Source, size_t _Count) { return wcsncpy(_Dest, _Source, _Count); } // STUB: STRONGHOLDCRUSADER 0x00580B69 - int __wcsicmp(wchar_t* _Str1, wchar_t* _Str2) { return _wcsicmp(_Str1, _Str2); } + int __wcsicmp(wchar_t const* _Str1, wchar_t const* _Str2) { return _wcsicmp(_Str1, _Str2); } // Only jumps to _free in game version // STUB: STRONGHOLDCRUSADER 0x00580DC1 @@ -146,5 +146,19 @@ namespace OS { // STUB: STRONGHOLDCRUSADER 0x00583D55 void _exit(int _Code) { exit(_Code); } + // STUB: STRONGHOLDCRUSADER 0x005807A8 + int _fwprintf(FILE* stream, wchar_t const* format, ...) + { + // needed for proxy + va_list args; + va_start(args, format); + int result = vfwprintf(stream, format, args); + va_end(args); + return result; + } + + // STUB: STRONGHOLDCRUSADER 0x00580C0B + long __wtol(wchar_t const* str) { return _wtol(str); } + } } diff --git a/src/OpenSHC/Text/TextEditorState.func.hpp b/src/OpenSHC/Text/TextEditorState.func.hpp index 9dcbb2aeb..970da238c 100644 --- a/src/OpenSHC/Text/TextEditorState.func.hpp +++ b/src/OpenSHC/Text/TextEditorState.func.hpp @@ -169,7 +169,7 @@ namespace Text { &TextEditorState::openMapDescriptionEditorDialog) openMapDescriptionEditorDialog; - MACRO_FUNCTION_RESOLVER(void (TextEditorState::*)(char*, undefined4), false, Address::SHC_3BB0A8C1_0x004620F0, + MACRO_FUNCTION_RESOLVER(void (TextEditorState::*)(char*, int), false, Address::SHC_3BB0A8C1_0x004620F0, &TextEditorState::setCustomHelpText) setCustomHelpText; diff --git a/src/OpenSHC/Text/TextEditorState.hpp b/src/OpenSHC/Text/TextEditorState.hpp index 884a3fbd1..5c547d91c 100644 --- a/src/OpenSHC/Text/TextEditorState.hpp +++ b/src/OpenSHC/Text/TextEditorState.hpp @@ -94,7 +94,7 @@ namespace Text { // Constructor TextEditorState* Constructor_TextEditorState(); - void setHelpWindowBounds(int x, int y, int height, int width); + void setHelpWindowBounds(int x, int y, int width, int height); void resetHelpStateFields(); @@ -168,7 +168,7 @@ namespace Text { void openMapDescriptionEditorDialog(int sectionId); - void setCustomHelpText(char* helpText, undefined4 bufferSize); + void setCustomHelpText(char* helpText, int bufferSize); void popHelpDialogStack(); diff --git a/src/OpenSHC/Text/TextEditorState/Constructor_TextEditorState.cpp b/src/OpenSHC/Text/TextEditorState/Constructor_TextEditorState.cpp new file mode 100644 index 000000000..e98ffe2c0 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/Constructor_TextEditorState.cpp @@ -0,0 +1,43 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/OS.func.hpp" + +#include "OpenSHC/Globals/DAT_UserHelpDefinedData.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045F130 + TextEditorState* TextEditorState::Constructor_TextEditorState() + { + this->isDialogStateInitialized = 0; + this->helpDialogVariant = 0; + this->field48_0x23960 = 0; + this->helpDialogSubMode = 0; + this->useAlternateHelpTab = 0; + this->customHelpTextLength = 0; + this->pendingTokenTypeToSkip = 0; + this->useWideHelpLayout = 0; + + MACRO_CALL_MEMBER(TextEditorState_Func::resetHelpStateFields, this)(); + + this->DAT_PointerToTemporaryTextMemory = (LPWSTR)MACRO_CALL(OS_Func::_malloc)(40000); + this->customTextMaxLength = -1; + + // FIXME:: Requires a finishing zero case, or it will not set the counter + for (int counter = 0; counter < 500; ++counter) { + if (strlen(DAT_UserHelpDefinedData::instance.HelpSections[counter])) { + continue; + } + this->counter = counter; + break; + } + + for (int i = 0; i < 20; ++i) { + this->intArray1[i] = 0; + } + return this; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/bltTextToScreenIfNeedBe.cpp b/src/OpenSHC/Text/TextEditorState/bltTextToScreenIfNeedBe.cpp new file mode 100644 index 000000000..8e81abad4 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/bltTextToScreenIfNeedBe.cpp @@ -0,0 +1,27 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/UI/Rendering/WindowAndDirectDraw.func.hpp" + +#include "OpenSHC/Globals/DAT_TextEditorState.hpp" +#include "OpenSHC/Globals/DAT_WindowAndDirectDraw.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045D690 + void TextEditorState::bltTextToScreenIfNeedBe() + { + if (!DAT_TextEditorState::instance.field48_0x23960) { + return; + } + if (this->savedMenuViewType == UI::Enums::MVT_MAP_EDITOR_LANDSCAPING + || this->savedMenuViewType == UI::Enums::MVT_BUILD_MENU + || this->savedMenuViewType == UI::Enums::MVT_BUILDING_AND_STATUS_MENU) { + MACRO_CALL_MEMBER(UI::Rendering::WindowAndDirectDraw_Func::bltMapGameSurfaceToScreenMenuSurfaceComplete, + DAT_WindowAndDirectDraw::ptr)(); + } + DAT_TextEditorState::instance.field48_0x23960 = FALSE; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/closeHelpDialogAndReturnToMenu.cpp b/src/OpenSHC/Text/TextEditorState/closeHelpDialogAndReturnToMenu.cpp new file mode 100644 index 000000000..04a89a421 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/closeHelpDialogAndReturnToMenu.cpp @@ -0,0 +1,78 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/Game/GameCore.func.hpp" +#include "OpenSHC/OS.func.hpp" +#include "OpenSHC/UI/MenuModalComposition.func.hpp" +#include "OpenSHC/Util/WideCharMultiByteState.func.hpp" + +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_MenuModalComposition1.hpp" +#include "OpenSHC/Globals/DAT_MenuView_TriggerPrepare.hpp" +#include "OpenSHC/Globals/DAT_WideCharMultiByteState.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045F240 + void TextEditorState::closeHelpDialogAndReturnToMenu() + { + if (!this->isDialogStateInitialized) { + return; + } + MACRO_CALL_MEMBER(TextEditorState_Func::resetHelpStateFields, this)(); + this->isDialogStateInitialized = 0; + + if (this->isCustomTextMode) { + MACRO_CALL_MEMBER(UI::MenuModalComposition_Func::activateModalDialog, DAT_MenuModalComposition1::ptr)( + UI::Enums::MMT_NONE, FALSE); + for (int i = 0; i < 20; ++i) { + this->intArray1[i] = 0; + } + if (this->customHelpTextPointer) { + if (!this->isCustomHelpTextWide) { + MACRO_CALL_MEMBER(Util::WideCharMultiByteState_Func::wideCharToMultiByteWithSize, + DAT_WideCharMultiByteState::ptr)(this->customHelpTextPointer, + this->DAT_PointerToTemporaryTextMemory, this->customHelpTextBufferSize); + } else { + MACRO_CALL(OS_Func::_wcsncpy)((wchar_t*)this->customHelpTextPointer, + this->DAT_PointerToTemporaryTextMemory, this->customHelpTextBufferSize); + } + } + DAT_MenuView_TriggerPrepare::instance = TRUE; + + } else if (this->helpDialogVariant == 0) { + + if (this->savedMenuViewType == UI::Enums::MVT_BUILD_MENU) { + DAT_GameCore::instance.buildmenuMenuTabToSwitchTo.tabType = this->savedActiveMenuTab; + MACRO_CALL_MEMBER(Game::GameCore_Func::switchToMenuView, DAT_GameCore::ptr)( + (UI::Enums::MenuViewType)this->savedMenuViewType, 0); + + } else if (this->savedMenuViewType == UI::Enums::MVT_MAP_EDITOR_LANDSCAPING) { + DAT_GameCore::instance.landscapingmenuMenuTabToSwitchTo = this->savedActiveMenuTab; + MACRO_CALL_MEMBER(Game::GameCore_Func::switchToMenuView, DAT_GameCore::ptr)( + (UI::Enums::MenuViewType)this->savedMenuViewType, 0); + + } else if (this->savedMenuViewType == UI::Enums::MVT_BUILDING_AND_STATUS_MENU) { + DAT_GameCore::instance.buildingandstatusmenuMenuTabToSwitchTo = this->savedActiveMenuTab; + MACRO_CALL_MEMBER(Game::GameCore_Func::switchToMenuView, DAT_GameCore::ptr)( + (UI::Enums::MenuViewType)this->savedMenuViewType, 0); + + } else { + DAT_GameCore::instance.menuTabToSwitchTo.tabType = this->savedActiveMenuTab; + MACRO_CALL_MEMBER(Game::GameCore_Func::switchToMenuView, DAT_GameCore::ptr)( + (UI::Enums::MenuViewType)this->savedMenuViewType, 0); + } + + } else { + MACRO_CALL_MEMBER(UI::MenuModalComposition_Func::activateModalDialog, DAT_MenuModalComposition1::ptr)( + UI::Enums::MMT_NONE, FALSE); + for (int i = 0; i < 20; ++i) { + this->intArray1[i] = 0; + } + } + + this->helpDialogVariant = 0; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/drawBorderStyle0x20.cpp b/src/OpenSHC/Text/TextEditorState/drawBorderStyle0x20.cpp new file mode 100644 index 000000000..81cf0d158 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/drawBorderStyle0x20.cpp @@ -0,0 +1,34 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/Globals/DAT_00df2964.hpp" +#include "OpenSHC/Globals/DAT_00df296c.hpp" +#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp" +#include "OpenSHC/Globals/INT_00df2968.hpp" +#include "OpenSHC/Globals/INT_00df2970.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045D6C0 + void TextEditorState::drawBorderStyle0x20(int dialogX, int dialogY, int dialogWidth, int dialogHeight) + { + this->dialogX = dialogX; + this->dialogY = dialogY; + this->dialogWidth = dialogWidth; + this->dialogHeight = dialogHeight; + + int const contentX = this->dialogX + DAT_00df2964::instance; + int const contentY = this->dialogY + DAT_00df296c::instance; + int const contentHeight = this->dialogHeight - INT_00df2970::instance; + int const contentWidth = this->dialogWidth - INT_00df2968::instance; + this->dialogContentX = contentX; + this->dialogContentY = contentY; + this->dialogContentWidth = contentWidth; + this->dialogContentHeight = contentHeight; + + this->helpContentScrollX = DAT_ViewportRenderState::instance.viewportState.currentCameraOffsetX; + this->helpContentScrollY = DAT_ViewportRenderState::instance.viewportState.currentCameraOffsetY; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/drawHelpWindowBackground.cpp b/src/OpenSHC/Text/TextEditorState/drawHelpWindowBackground.cpp new file mode 100644 index 000000000..82378f4ff --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/drawHelpWindowBackground.cpp @@ -0,0 +1,52 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/UI/Rendering/PencilRenderCore.func.hpp" + +#include "OpenSHC/Globals/COL_BLACK.hpp" +#include "OpenSHC/Globals/COL_DARK_CYAN_GREY.hpp" +#include "OpenSHC/Globals/COL_VERY_DARK_GREY.hpp" +#include "OpenSHC/Globals/DAT_PencilRenderCore.hpp" +#include "OpenSHC/Globals/DAT_TextManagerObject.hpp" +#include "OpenSHC/Globals/DAT_TextureRenderCoreObject.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045D740 + void TextEditorState::drawHelpWindowBackground() + { + if (this->helpDialogVariant != 1) { + DAT_TextureRenderCoreObject::instance.drawBufferChoiceValue = Rendering::Enums::RT_SCREEN_MENU; + } else { + DAT_TextManagerObject::instance.textSurfaceTarget = Rendering::Enums::RT_MAP_GAME; + DAT_PencilRenderCore::instance.surfaceTarget = Rendering::Enums::RT_MAP_GAME; + } + if (this->isCustomTextMode) { + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawBoxWithRoundedEdges, DAT_PencilRenderCore::ptr)( + this->dialogX, this->dialogY, this->dialogWidth + this->dialogX, this->dialogHeight + this->dialogY, + UI::Enums::RBERL_SLIGHT); + } else if (!this->useAlternateHelpTab) { + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawColorBox, DAT_PencilRenderCore::ptr)( + this->dialogX, this->dialogY, this->dialogWidth + this->dialogX, this->dialogHeight + this->dialogY, + COL_BLACK::instance.shortValue); + } else if (this->helpDialogVariant != 2) { + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawColorBox, DAT_PencilRenderCore::ptr)( + this->dialogX, this->dialogY, this->dialogWidth + this->dialogX, this->dialogHeight + this->dialogY, + COL_VERY_DARK_GREY::instance.shortValue); + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawColorBox, DAT_PencilRenderCore::ptr)( + this->dialogContentX, this->dialogContentY, this->dialogContentWidth + this->dialogContentX, + this->dialogContentHeight + this->dialogContentY, COL_BLACK::instance.shortValue); + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawColorBox, DAT_PencilRenderCore::ptr)( + this->dialogX, this->dialogY + this->dialogHeight, this->dialogWidth + this->dialogX, + this->dialogY + this->dialogHeight + 60, COL_DARK_CYAN_GREY::instance.shortValue); + } + if (this->helpDialogVariant != 1) { + DAT_TextureRenderCoreObject::instance.drawBufferChoiceValue = Rendering::Enums::RT_MAP_GAME; + } else { + DAT_TextManagerObject::instance.textSurfaceTarget = Rendering::Enums::RT_SCREEN_MENU; + DAT_PencilRenderCore::instance.surfaceTarget = Rendering::Enums::RT_SCREEN_MENU; + } + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/findHelpGraphicIndexByName.cpp b/src/OpenSHC/Text/TextEditorState/findHelpGraphicIndexByName.cpp new file mode 100644 index 000000000..76afd0b00 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/findHelpGraphicIndexByName.cpp @@ -0,0 +1,20 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/OS.func.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045D890 + int TextEditorState::findHelpGraphicIndexByName(char* param_1) + { + for (int i = 0; i < this->graphicFileCount; ++i) { + if (!MACRO_CALL(OS_Func::__stricmp)(this->graphicFileNames[i], param_1)) { + return i; + } + } + return -1; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/findHelpSectionIndexByName.cpp b/src/OpenSHC/Text/TextEditorState/findHelpSectionIndexByName.cpp new file mode 100644 index 000000000..018ef45a5 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/findHelpSectionIndexByName.cpp @@ -0,0 +1,22 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/OS.func.hpp" + +#include "OpenSHC/Globals/DAT_UserHelpDefinedData.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045D140 + int TextEditorState::findHelpSectionIndexByName(char* param_1) + { + for (int counter = 0; counter < this->counter; ++counter) { + if (!MACRO_CALL(OS_Func::__stricmp)(DAT_UserHelpDefinedData::instance.HelpSections[counter], param_1)) { + return counter; + } + } + return -1; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/findOrAddHelpGraphicName.cpp b/src/OpenSHC/Text/TextEditorState/findOrAddHelpGraphicName.cpp new file mode 100644 index 000000000..4e668a43b --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/findOrAddHelpGraphicName.cpp @@ -0,0 +1,25 @@ +// disable deprecation warnings for strcpy +#pragma warning(disable : 4996) + +#include "../TextEditorState.func.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045D8F0 + int TextEditorState::findOrAddHelpGraphicName(char* param_1) + { + int graphicIndex = MACRO_CALL_MEMBER(TextEditorState_Func::findHelpGraphicIndexByName, this)(param_1); + if (graphicIndex != -1) { + return graphicIndex; + } + graphicIndex = this->graphicFileCount; + strcpy(this->graphicFileNames[graphicIndex], param_1); + if (this->graphicFileCount < 20) { + ++this->graphicFileCount; + } + return graphicIndex; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/findOrAddHelpSectionName.cpp b/src/OpenSHC/Text/TextEditorState/findOrAddHelpSectionName.cpp new file mode 100644 index 000000000..d1be3a1e5 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/findOrAddHelpSectionName.cpp @@ -0,0 +1,32 @@ +// disable deprecation warnings for strcpy +#pragma warning(disable : 4996) + +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/Text/TextEditorState.func.hpp" + +#include "OpenSHC/Globals/DAT_UserHelpDefinedData.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045D1A0 + int TextEditorState::findOrAddHelpSectionName(char* param_1) + { + int helpSectionId + = MACRO_CALL_MEMBER(OpenSHC::Text::TextEditorState_Func::findHelpSectionIndexByName, this)(param_1); + if (helpSectionId != -1) { + return helpSectionId; + } + // FIXME:: This should overflow the buffer if filled to much. The counter can reach 500, which is one over the + // buffer, ignoring that it will just overwrite this again and again. + helpSectionId = this->counter; + strcpy(DAT_UserHelpDefinedData::instance.HelpSections[helpSectionId], param_1); + if (this->counter < 500) { + ++this->counter; + } + return helpSectionId; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/findOrAddSoundName.cpp b/src/OpenSHC/Text/TextEditorState/findOrAddSoundName.cpp new file mode 100644 index 000000000..154d8464b --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/findOrAddSoundName.cpp @@ -0,0 +1,29 @@ +// disable deprecation warnings for strcpy +#pragma warning(disable : 4996) + +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/Text/TextEditorState.func.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045DAE0 + int TextEditorState::findOrAddSoundName(char* param_1) + { + int index = MACRO_CALL_MEMBER(OpenSHC::Text::TextEditorState_Func::findSoundIndexByName, this)(param_1); + if (index != -1) { + return index; + } + + index = this->soundFileCount; + strcpy(this->soundFileNames[index], param_1); + this->soundFilePlayedFlags[index] = 0; + if (this->soundFileCount < 5) { + ++this->soundFileCount; + } + return index; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/findSoundIndexByName.cpp b/src/OpenSHC/Text/TextEditorState/findSoundIndexByName.cpp new file mode 100644 index 000000000..f105e1de4 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/findSoundIndexByName.cpp @@ -0,0 +1,20 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/OS.func.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045DA80 + int TextEditorState::findSoundIndexByName(char* param_1) + { + for (int i = 0; i < this->soundFileCount; i = i + 1) { + if (!MACRO_CALL(OpenSHC::OS_Func::__stricmp)(this->soundFileNames[i], param_1)) { + return i; + } + } + return -1; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/getHelpSectionText.cpp b/src/OpenSHC/Text/TextEditorState/getHelpSectionText.cpp new file mode 100644 index 000000000..a0c88560b --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/getHelpSectionText.cpp @@ -0,0 +1,20 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/Globals/DAT_UserHelpDefinedData.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045D9E0 + char* TextEditorState::getHelpSectionText(int param_1) + { + for (int index = 0; DAT_UserHelpDefinedData::instance.field5_0x7a124[index].unknown_0x4 != -1; ++index) { + if (DAT_UserHelpDefinedData::instance.field5_0x7a124[index].unknown_0x4 == param_1) { + return DAT_UserHelpDefinedData::instance.field5_0x7a124[index].text_0x0; + } + } + return NULL; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/getNextHelpColorEntryIndex.cpp b/src/OpenSHC/Text/TextEditorState/getNextHelpColorEntryIndex.cpp new file mode 100644 index 000000000..98ad9021a --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/getNextHelpColorEntryIndex.cpp @@ -0,0 +1,19 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/Globals/DAT_UserHelpDefinedData.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045DA20 + int TextEditorState::getNextHelpColorEntryIndex(int param_1) + { + int newIndex = param_1 + 1; + if (DAT_UserHelpDefinedData::instance.field6_0x7a16c[newIndex].name_0x0 == NULL) { + newIndex = 0; + } + return newIndex; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/getNextHelpSectionID.cpp b/src/OpenSHC/Text/TextEditorState/getNextHelpSectionID.cpp new file mode 100644 index 000000000..dd1e12b33 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/getNextHelpSectionID.cpp @@ -0,0 +1,26 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/Globals/DAT_UserHelpDefinedData.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045D950 + int TextEditorState::getNextHelpSectionID(int param_1) + { + for (int index = 0; DAT_UserHelpDefinedData::instance.field5_0x7a124[index].unknown_0x4 != -1; ++index) { + if (DAT_UserHelpDefinedData::instance.field5_0x7a124[index].unknown_0x4 != param_1) { + continue; + } + + ++index; + if (DAT_UserHelpDefinedData::instance.field5_0x7a124[index].unknown_0x4 == -1) { + index = 0; + } + return DAT_UserHelpDefinedData::instance.field5_0x7a124[index].unknown_0x4; + } + return -1; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/getPrevHelpColorEntryIndex.cpp b/src/OpenSHC/Text/TextEditorState/getPrevHelpColorEntryIndex.cpp new file mode 100644 index 000000000..d3f15557e --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/getPrevHelpColorEntryIndex.cpp @@ -0,0 +1,23 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/Globals/DAT_UserHelpDefinedData.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045DA40 + int TextEditorState::getPrevHelpColorEntryIndex(int param_1) + { + --param_1; + if (param_1 < 0) { + param_1 = 0; + while (DAT_UserHelpDefinedData::instance.field6_0x7a16c[param_1].name_0x0 != NULL) { + ++param_1; + } + --param_1; + } + return param_1; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/getPrevHelpSectionID.cpp b/src/OpenSHC/Text/TextEditorState/getPrevHelpSectionID.cpp new file mode 100644 index 000000000..4e5d3ba84 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/getPrevHelpSectionID.cpp @@ -0,0 +1,28 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/Globals/DAT_UserHelpDefinedData.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045D990 + int TextEditorState::getPrevHelpSectionID(int param_1) + { + for (int index = 0; DAT_UserHelpDefinedData::instance.field5_0x7a124[index].unknown_0x4 != -1; ++index) { + if (DAT_UserHelpDefinedData::instance.field5_0x7a124[index].unknown_0x4 == param_1) { + --index; + if (index < 0) { + index = 0; + while (DAT_UserHelpDefinedData::instance.field5_0x7a124[index].unknown_0x4 != -1) { + ++index; + } + --index; + } + return DAT_UserHelpDefinedData::instance.field5_0x7a124[index].unknown_0x4; + } + } + return -1; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/getWideCharOrWideCharPointer.cpp b/src/OpenSHC/Text/TextEditorState/getWideCharOrWideCharPointer.cpp new file mode 100644 index 000000000..c6a74bae4 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/getWideCharOrWideCharPointer.cpp @@ -0,0 +1,35 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/Globals/DAT_ARRAY_00df2b78.hpp" + +namespace OpenSHC { +namespace Text { + + // NOTE: Return is essentially a "union", however, it needs to be a value for the proper structure + + // FUNCTION: STRONGHOLDCRUSADER 0x0045D3C0 + uint TextEditorState::getWideCharOrWideCharPointer(int* param_1) + { + int result = this->DAT_PointerToTemporaryTextMemory[*param_1]; + *param_1 += 1; + + if (result >= L' ') { + DAT_ARRAY_00df2b78::instance[0] = result; + + int length = 1; + while (this->DAT_PointerToTemporaryTextMemory[*param_1] >= L' ') { + DAT_ARRAY_00df2b78::instance[length] = this->DAT_PointerToTemporaryTextMemory[*param_1]; + *param_1 += 1; + ++length; + if (this->DAT_PointerToTemporaryTextMemory[*param_1 - 1] == L' ') { + break; + } + } + DAT_ARRAY_00df2b78::instance[length] = L'\0'; + result = (int)DAT_ARRAY_00df2b78::instance; + } + return (uint)result; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/helpToken.cpp b/src/OpenSHC/Text/TextEditorState/helpToken.cpp new file mode 100644 index 000000000..8cfb867ff --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/helpToken.cpp @@ -0,0 +1,39 @@ +#include "../TextEditorState.func.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045F080 + int TextEditorState::helpToken_getHelpTokenAdvanceLength(HelpTextToken token) + { + switch (token) { + case Enums::HTT_PIC: + return 4; + + case Enums::HTT_FONT: + case Enums::HTT_COLOUR: + case Enums::HTT_LINK: + case Enums::HTT_LINKCOLOUR: + case Enums::HTT_SOUND: + case Enums::HTT_STRING: + case Enums::HTT_INCLUDE: + return 3; + + default: + return 1; + } + } + + // FUNCTION: STRONGHOLDCRUSADER 0x0045F0D0 + void TextEditorState::helpToken_insertSpaceForHelpTextToken(HelpTextToken token) + { + int tokenLength = MACRO_CALL_MEMBER(TextEditorState_Func::helpToken_getHelpTokenAdvanceLength, this)(token); + for (int textIndex = this->customHelpTextLength; textIndex >= this->activeHelpHotspotIndex; --textIndex) { + this->DAT_PointerToTemporaryTextMemory[textIndex + tokenLength] + = this->DAT_PointerToTemporaryTextMemory[textIndex]; + } + this->customHelpTextLength += tokenLength; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/initializeAndLayoutHelpText.cpp b/src/OpenSHC/Text/TextEditorState/initializeAndLayoutHelpText.cpp new file mode 100644 index 000000000..01f33a511 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/initializeAndLayoutHelpText.cpp @@ -0,0 +1,23 @@ +#include "../TextEditorState.func.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x004614D0 + void TextEditorState::initializeAndLayoutHelpText() + { + for (int i = 0; i < 20000; ++i) { + this->lineLayoutTable[i].leftBorder = 25; + this->lineLayoutTable[i].rightBorder = this->dialogContentWidth - 25; + this->lineLayoutTable[i].widthForCentering = 0; + } + // Adds vertical space at start + for (int i = 0; i < 4; ++i) { + this->lineLayoutTable[i].leftBorder = -1; + } + this->imageHotspotCount = 0; + while (!MACRO_CALL_MEMBER(TextEditorState_Func::processHelpRichTextTokens, this)(0)) {}; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/loadAndLayoutHelpContent.cpp b/src/OpenSHC/Text/TextEditorState/loadAndLayoutHelpContent.cpp new file mode 100644 index 000000000..62a5eddb7 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/loadAndLayoutHelpContent.cpp @@ -0,0 +1,22 @@ +#include "../TextEditorState.func.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x004619D0 + void TextEditorState::loadAndLayoutHelpContent() + { + this->graphicFileCount = 0; + this->soundFileCount = 0; + this->topVisibleLineIndex = -1; + this->helpContentScrollOffsetY = 0; + if (!this->isCustomTextMode) { + MACRO_CALL_MEMBER(TextEditorState_Func::parseHlp, this)(); + MACRO_CALL_MEMBER(TextEditorState_Func::loadHelpSectionGraphics, this)(); + } + this->activeHelpHotspotIndex = 0; + MACRO_CALL_MEMBER(TextEditorState_Func::initializeAndLayoutHelpText, this)(); + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/loadAndParseHelpFile.cpp b/src/OpenSHC/Text/TextEditorState/loadAndParseHelpFile.cpp new file mode 100644 index 000000000..7a0e6bf84 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/loadAndParseHelpFile.cpp @@ -0,0 +1,22 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/OS.func.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045F580 + BOOLEnum TextEditorState::loadAndParseHelpFile(char const* searchedPart) + { + FILE* _File = MACRO_CALL_MEMBER(TextEditorState_Func::readCrusaderHelpHlp, this)(searchedPart); + if (_File) { + MACRO_CALL(OS_Func::_fclose)(_File); + if (this->helpSectionParseSucceeded) { + return TRUE; + } + } + return FALSE; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/loadHelpSectionGraphics.cpp b/src/OpenSHC/Text/TextEditorState/loadHelpSectionGraphics.cpp new file mode 100644 index 000000000..ea5ba6b53 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/loadHelpSectionGraphics.cpp @@ -0,0 +1,22 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/UI/Rendering/TextureRenderCore.func.hpp" + +#include "OpenSHC/Globals/DAT_TextureRenderCoreObject.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045D370 + void TextEditorState::loadHelpSectionGraphics() + { + DAT_TextureRenderCoreObject::instance.backwardsLoadedGfxIndex_0x16C850 = 99; + DAT_TextureRenderCoreObject::instance.loadedGfxArray[99].backwardsOffsetInBuffer = 0; + for (int i = 0; i < this->graphicFileCount; ++i) { + MACRO_CALL_MEMBER(UI::Rendering::TextureRenderCore_Func::loadGfxAtBufferEnd, + DAT_TextureRenderCoreObject::ptr)(this->graphicFileNames[i]); + } + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/openBuildingHelpDialog.cpp b/src/OpenSHC/Text/TextEditorState/openBuildingHelpDialog.cpp new file mode 100644 index 000000000..5c40b0e56 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/openBuildingHelpDialog.cpp @@ -0,0 +1,79 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/Text/TextEditorState.func.hpp" +#include "OpenSHC/UI/MenuModalComposition.func.hpp" + +#include "OpenSHC/Globals/DAT_00df2964.hpp" +#include "OpenSHC/Globals/DAT_00df296c.hpp" +#include "OpenSHC/Globals/DAT_MenuModalComposition1.hpp" +#include "OpenSHC/Globals/DAT_UserTextHandlerState.hpp" +#include "OpenSHC/Globals/INT_00df2968.hpp" +#include "OpenSHC/Globals/INT_00df2970.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x00461A20 + void TextEditorState::openBuildingHelpDialog(int sectionId) + { + if (!this->isDialogStateInitialized) { + this->isDialogStateInitialized = TRUE; + this->helpDialogVariant = 1; + this->useInGameHelpHandler = 0; + this->helpDialogSubMode = 0; + this->useAlternateHelpTab = 0; + this->isCustomTextMode = 0; + DAT_UserTextHandlerState::instance.inputBufferIndex = 0; + this->helpContentScrollX = 0; + this->helpContentScrollY = 0; + } else if (sectionId == this->currentHelpSectionID) { + return; + } + + this->activeHelpHotspotIndex = 0; + if (sectionId == -1) { + MACRO_CALL_MEMBER(TextEditorState_Func::closeHelpDialogAndReturnToMenu, this)(); + return; + } + + this->isTextHelpDialogMode = 1; + MACRO_CALL_MEMBER(UI::MenuModalComposition_Func::activateModalDialog, DAT_MenuModalComposition1::ptr)( + UI::Enums::MMT_BUILDING_HELP_TEXT, FALSE); + + int modalX; + int modalY; + int modalWidth; + int modalHeight; + MACRO_CALL_MEMBER(UI::MenuModalComposition_Func::fillWithMenuModalDimensions, DAT_MenuModalComposition1::ptr)( + &modalX, &modalY, &modalWidth, &modalHeight); + // Likely also some rect structure + DAT_00df2964::instance = 15; + DAT_00df296c::instance = 13; + INT_00df2968::instance = 60; + INT_00df2970::instance = 65; + this->dialogX = modalX; + this->dialogY = modalY; + this->dialogWidth = modalWidth; + this->dialogHeight = modalHeight; + + int const dialogContentX = this->dialogX + DAT_00df2964::instance; + int const dialogContentY = this->dialogY + DAT_00df296c::instance; + int const dialogContentHeight = this->dialogHeight - INT_00df2970::instance; + int const dialogContentWidth = this->dialogWidth - INT_00df2968::instance; + this->dialogContentX = dialogContentX; + this->dialogContentY = dialogContentY; + this->dialogContentWidth = dialogContentWidth; + this->dialogContentHeight = dialogContentHeight; + + for (int i = 28; i >= 0; --i) { + this->helpSectionHistoryStack[i + 1] = this->helpSectionHistoryStack[i]; + } + this->helpSectionHistoryStack[0] = this->currentHelpSectionID; + + this->currentHelpSectionID = sectionId; + this->helpContentScrollOffsetY = 0; + MACRO_CALL_MEMBER(TextEditorState_Func::loadAndLayoutHelpContent, this)(); + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/openCreditsScrollDialog.cpp b/src/OpenSHC/Text/TextEditorState/openCreditsScrollDialog.cpp new file mode 100644 index 000000000..f7b398bae --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/openCreditsScrollDialog.cpp @@ -0,0 +1,73 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/UI/MenuModalComposition.func.hpp" + +#include "OpenSHC/Globals/DAT_00df2964.hpp" +#include "OpenSHC/Globals/DAT_00df296c.hpp" +#include "OpenSHC/Globals/DAT_MenuModalComposition1.hpp" +#include "OpenSHC/Globals/DAT_UserTextHandlerState.hpp" +#include "OpenSHC/Globals/INT_00df2968.hpp" +#include "OpenSHC/Globals/INT_00df2970.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x00461E50 + void TextEditorState::openCreditsScrollDialog(int sectionId) + { + if (!this->isDialogStateInitialized) { + this->isDialogStateInitialized = TRUE; + this->helpDialogVariant = 2; + this->useInGameHelpHandler = FALSE; + this->helpDialogSubMode = 0; + this->useAlternateHelpTab = FALSE; + this->isCustomTextMode = FALSE; + DAT_UserTextHandlerState::instance.inputBufferIndex = 0; + } + this->activeHelpHotspotIndex = 0; + if (sectionId == -1) { + MACRO_CALL_MEMBER(TextEditorState_Func::closeHelpDialogAndReturnToMenu, this)(); + return; + } + + this->isTextHelpDialogMode = TRUE; + MACRO_CALL_MEMBER(UI::MenuModalComposition_Func::activateModalDialog, DAT_MenuModalComposition1::ptr)( + UI::Enums::MMT_CREDITS_SCROLL, FALSE); + + int modalX; + int modalY; + int modalWidth; + int modalHeight; + MACRO_CALL_MEMBER(UI::MenuModalComposition_Func::fillWithMenuModalDimensions, DAT_MenuModalComposition1::ptr)( + &modalX, &modalY, &modalWidth, &modalHeight); + + DAT_00df2964::instance = 0; + DAT_00df296c::instance = 0; + INT_00df2968::instance = 0; + INT_00df2970::instance = 0; + this->dialogX = modalX; + this->dialogY = modalY; + this->dialogWidth = modalWidth; + this->dialogHeight = modalHeight; + + int const dialogContentX = this->dialogX + DAT_00df2964::instance; + int const dialogContentY = this->dialogY + DAT_00df296c::instance; + int const dialogContentHeight = this->dialogHeight - INT_00df2970::instance; + int const dialogContentWidth = this->dialogWidth - INT_00df2968::instance; + this->dialogContentX = dialogContentX; + this->dialogContentY = dialogContentY; + this->dialogContentWidth = dialogContentWidth; + this->dialogContentHeight = dialogContentHeight; + + for (int i = 28; i >= 0; --i) { + this->helpSectionHistoryStack[i + 1] = this->helpSectionHistoryStack[i]; + } + this->helpSectionHistoryStack[0] = this->currentHelpSectionID; + + this->currentHelpSectionID = sectionId; + this->helpContentScrollOffsetY = 0; + MACRO_CALL_MEMBER(TextEditorState_Func::loadAndLayoutHelpContent, this)(); + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/openInGameHelpDialog.cpp b/src/OpenSHC/Text/TextEditorState/openInGameHelpDialog.cpp new file mode 100644 index 000000000..f8c1c455b --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/openInGameHelpDialog.cpp @@ -0,0 +1,74 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/UI/MenuModalComposition.func.hpp" + +#include "OpenSHC/Globals/DAT_00df2964.hpp" +#include "OpenSHC/Globals/DAT_00df296c.hpp" +#include "OpenSHC/Globals/DAT_MenuModalComposition1.hpp" +#include "OpenSHC/Globals/DAT_UserTextHandlerState.hpp" +#include "OpenSHC/Globals/INT_00df2968.hpp" +#include "OpenSHC/Globals/INT_00df2970.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x00461B90 + void TextEditorState::openInGameHelpDialog(int sectionId) + { + if (!this->isDialogStateInitialized) { + this->isDialogStateInitialized = TRUE; + this->helpDialogVariant = 1; + this->useInGameHelpHandler = TRUE; + this->helpDialogSubMode = 0; + this->useAlternateHelpTab = 0; + this->isCustomTextMode = 0; + DAT_UserTextHandlerState::instance.inputBufferIndex = 0; + this->helpContentScrollX = 0; + this->helpContentScrollY = 0; + } + this->activeHelpHotspotIndex = 0; + if (sectionId == -1) { + MACRO_CALL_MEMBER(TextEditorState_Func::closeHelpDialogAndReturnToMenu, this)(); + return; + } + this->isTextHelpDialogMode = TRUE; + MACRO_CALL_MEMBER(UI::MenuModalComposition_Func::activateModalDialog, DAT_MenuModalComposition1::ptr)( + UI::Enums::MMT_IN_GAME_HELP_TEXT, FALSE); + + int modalX; + int modalY; + int modalWidth; + int modalHeight; + MACRO_CALL_MEMBER(UI::MenuModalComposition_Func::fillWithMenuModalDimensions, DAT_MenuModalComposition1::ptr)( + &modalX, &modalY, &modalWidth, &modalHeight); + + DAT_00df2964::instance = 15; + DAT_00df296c::instance = 13; + INT_00df2968::instance = 60; + INT_00df2970::instance = 65; + this->dialogX = modalX; + this->dialogY = modalY; + this->dialogWidth = modalWidth; + this->dialogHeight = modalHeight; + + int const dialogContentX = this->dialogX + DAT_00df2964::instance; + int const dialogContentY = this->dialogY + DAT_00df296c::instance; + int const dialogContentHeight = this->dialogHeight - INT_00df2970::instance; + int const dialogContentWidth = this->dialogWidth - INT_00df2968::instance; + this->dialogContentX = dialogContentX; + this->dialogContentY = dialogContentY; + this->dialogContentWidth = dialogContentWidth; + this->dialogContentHeight = dialogContentHeight; + + for (int i = 28; i >= 0; --i) { + this->helpSectionHistoryStack[i + 1] = this->helpSectionHistoryStack[i]; + } + this->helpSectionHistoryStack[0] = this->currentHelpSectionID; + + this->currentHelpSectionID = sectionId; + this->helpContentScrollOffsetY = 0; + MACRO_CALL_MEMBER(TextEditorState_Func::loadAndLayoutHelpContent, this)(); + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/openMapDescriptionEditorDialog.cpp b/src/OpenSHC/Text/TextEditorState/openMapDescriptionEditorDialog.cpp new file mode 100644 index 000000000..d024a872a --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/openMapDescriptionEditorDialog.cpp @@ -0,0 +1,75 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/UI/MenuModalComposition.func.hpp" + +#include "OpenSHC/Globals/DAT_00df2964.hpp" +#include "OpenSHC/Globals/DAT_00df296c.hpp" +#include "OpenSHC/Globals/DAT_MenuModalComposition1.hpp" +#include "OpenSHC/Globals/DAT_UserTextHandlerState.hpp" +#include "OpenSHC/Globals/INT_00df2968.hpp" +#include "OpenSHC/Globals/INT_00df2970.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x00461F90 + void TextEditorState::openMapDescriptionEditorDialog(int sectionId) + { + if (!this->isDialogStateInitialized) { + this->isDialogStateInitialized = TRUE; + this->helpDialogVariant = 0; + this->useInGameHelpHandler = FALSE; + this->helpDialogSubMode = 0; + this->useAlternateHelpTab = TRUE; + this->isCustomTextMode = TRUE; + DAT_UserTextHandlerState::instance.inputBufferIndex = 0; + this->helpContentScrollX = 0; + this->helpContentScrollY = 0; + } + this->activeHelpHotspotIndex = 0; + this->customHelpTextPointer = NULL; + if (sectionId == -1) { + MACRO_CALL_MEMBER(TextEditorState_Func::closeHelpDialogAndReturnToMenu, this)(); + return; + } + this->isTextHelpDialogMode = 0; + MACRO_CALL_MEMBER(UI::MenuModalComposition_Func::activateModalDialog, DAT_MenuModalComposition1::ptr)( + UI::Enums::MMT_MAP_DESCRIPTION_EDITOR, FALSE); + + int modalX; + int modalY; + int modalWidth; + int modalHeight; + MACRO_CALL_MEMBER(UI::MenuModalComposition_Func::fillWithMenuModalDimensions, DAT_MenuModalComposition1::ptr)( + &modalX, &modalY, &modalWidth, &modalHeight); + + DAT_00df2964::instance = 10; + DAT_00df296c::instance = 10; + INT_00df2968::instance = 45; + INT_00df2970::instance = 53; + this->dialogX = modalX; + this->dialogY = modalY; + this->dialogWidth = modalWidth; + this->dialogHeight = modalHeight; + + int const dialogContentX = this->dialogX + DAT_00df2964::instance; + int const dialogContentY = this->dialogY + DAT_00df296c::instance; + int const dialogContentHeight = this->dialogHeight - INT_00df2970::instance; + int const dialogContentWidth = this->dialogWidth - INT_00df2968::instance; + this->dialogContentX = dialogContentX; + this->dialogContentY = dialogContentY; + this->dialogContentWidth = dialogContentWidth; + this->dialogContentHeight = dialogContentHeight; + + for (int i = 28; i >= 0; --i) { + this->helpSectionHistoryStack[i + 1] = this->helpSectionHistoryStack[i]; + } + this->helpSectionHistoryStack[0] = this->currentHelpSectionID; + + this->currentHelpSectionID = -1; + this->helpContentScrollOffsetY = 0; + MACRO_CALL_MEMBER(TextEditorState_Func::loadAndLayoutHelpContent, this)(); + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/openScenarioHelpDialog.cpp b/src/OpenSHC/Text/TextEditorState/openScenarioHelpDialog.cpp new file mode 100644 index 000000000..8ce0c7f67 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/openScenarioHelpDialog.cpp @@ -0,0 +1,74 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/UI/MenuModalComposition.func.hpp" + +#include "OpenSHC/Globals/DAT_00df2964.hpp" +#include "OpenSHC/Globals/DAT_00df296c.hpp" +#include "OpenSHC/Globals/DAT_MenuModalComposition1.hpp" +#include "OpenSHC/Globals/DAT_UserTextHandlerState.hpp" +#include "OpenSHC/Globals/INT_00df2968.hpp" +#include "OpenSHC/Globals/INT_00df2970.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x00461CF0 + void TextEditorState::openScenarioHelpDialog(int sectionId) + { + if (!this->isDialogStateInitialized) { + this->isDialogStateInitialized = TRUE; + this->helpDialogVariant = 2; + this->useInGameHelpHandler = FALSE; + this->helpDialogSubMode = 0; + this->useAlternateHelpTab = FALSE; + this->isCustomTextMode = FALSE; + DAT_UserTextHandlerState::instance.inputBufferIndex = 0; + this->helpContentScrollX = 0; + this->helpContentScrollY = 0; + } + this->activeHelpHotspotIndex = 0; + if (sectionId == -1) { + MACRO_CALL_MEMBER(OpenSHC::Text::TextEditorState_Func::closeHelpDialogAndReturnToMenu, this)(); + return; + } + this->isTextHelpDialogMode = TRUE; + MACRO_CALL_MEMBER(OpenSHC::UI::MenuModalComposition_Func::activateModalDialog, DAT_MenuModalComposition1::ptr)( + OpenSHC::UI::Enums::MMT_DISPLAY_SCENARIO_HELP_TEXT, FALSE); + + int modalX; + int modalY; + int modalWidth; + int modalHeight; + MACRO_CALL_MEMBER(UI::MenuModalComposition_Func::fillWithMenuModalDimensions, DAT_MenuModalComposition1::ptr)( + &modalX, &modalY, &modalWidth, &modalHeight); + + DAT_00df2964::instance = 10; + DAT_00df296c::instance = 10; + INT_00df2968::instance = 45; + INT_00df2970::instance = 20; + this->dialogX = modalX; + this->dialogY = modalY; + this->dialogWidth = modalWidth; + this->dialogHeight = modalHeight; + + int const dialogContentX = this->dialogX + DAT_00df2964::instance; + int const dialogContentY = this->dialogY + DAT_00df296c::instance; + int const dialogContentHeight = this->dialogHeight - INT_00df2970::instance; + int const dialogContentWidth = this->dialogWidth - INT_00df2968::instance; + this->dialogContentX = dialogContentX; + this->dialogContentY = dialogContentY; + this->dialogContentWidth = dialogContentWidth; + this->dialogContentHeight = dialogContentHeight; + + for (int i = 28; i >= 0; --i) { + this->helpSectionHistoryStack[i + 1] = this->helpSectionHistoryStack[i]; + } + this->helpSectionHistoryStack[0] = this->currentHelpSectionID; + + this->currentHelpSectionID = sectionId; + this->helpContentScrollOffsetY = 0; + MACRO_CALL_MEMBER(OpenSHC::Text::TextEditorState_Func::loadAndLayoutHelpContent, this)(); + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/openUnusedHelpTextEditorDialog.cpp b/src/OpenSHC/Text/TextEditorState/openUnusedHelpTextEditorDialog.cpp new file mode 100644 index 000000000..9ffdd0038 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/openUnusedHelpTextEditorDialog.cpp @@ -0,0 +1,95 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/Game/GameCore.func.hpp" + +#include "OpenSHC/Globals/DAT_GameCore.hpp" +#include "OpenSHC/Globals/DAT_UserTextHandlerState.hpp" +#include "OpenSHC/Globals/DAT_WindowAndDirectDraw.hpp" +#include "OpenSHC/Globals/Menu_UnusedHelpTextEditor.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x00462190 + void TextEditorState::openUnusedHelpTextEditorDialog(int sectionId) + { + if (this->helpDialogVariant) { + if (this->helpDialogVariant == 2) { + MACRO_CALL_MEMBER(TextEditorState_Func::openScenarioHelpDialog, this)(sectionId); + return; + } + if (this->useInGameHelpHandler) { + MACRO_CALL_MEMBER(TextEditorState_Func::openInGameHelpDialog, this)(sectionId); + return; + } + MACRO_CALL_MEMBER(TextEditorState_Func::openBuildingHelpDialog, this)(sectionId); + return; + } + + if (!this->isDialogStateInitialized) { + this->savedMenuViewType = DAT_GameCore::instance.currentMenuViewType; + this->savedActiveMenuTab = DAT_GameCore::instance.activeMenuTab.tabType; + this->savedMenuFlag = DAT_GameCore::instance.unknownAlwaysZero01; + this->field48_0x23960 = 1; + this->isDialogStateInitialized = TRUE; + this->useInGameHelpHandler = FALSE; + this->helpDialogVariant = 0; + this->helpDialogSubMode = 0; + this->useAlternateHelpTab = FALSE; + this->isCustomTextMode = FALSE; + this->helpContentScrollX = 0; + this->helpContentScrollY = 0; + this->useWideHelpLayout = FALSE; + DAT_UserTextHandlerState::instance.inputBufferIndex = 0; + } + this->activeHelpHotspotIndex = 0; + if (sectionId == -1) { + MACRO_CALL_MEMBER(TextEditorState_Func::closeHelpDialogAndReturnToMenu, this)(); + return; + } + if (!this->useAlternateHelpTab) { + this->isTextHelpDialogMode = TRUE; + DAT_GameCore::instance.menuTabToSwitchTo.tabType = 0; + MACRO_CALL_MEMBER(Game::GameCore_Func::switchToMenuView, DAT_GameCore::ptr)( + UI::Enums::MVT_UNUSED_HELP_TEXT_EDITOR, 0); + } else { + this->isTextHelpDialogMode = TRUE; + DAT_GameCore::instance.menuTabToSwitchTo.tabType = 1; + MACRO_CALL_MEMBER(Game::GameCore_Func::switchToMenuView, DAT_GameCore::ptr)( + UI::Enums::MVT_UNUSED_HELP_TEXT_EDITOR, 0); + } + Menu_UnusedHelpTextEditor::instance.thousand = 0; + + int const dialogX = DAT_WindowAndDirectDraw::instance.mainMenuBorderWidth + 80; + int const dialogY = DAT_WindowAndDirectDraw::instance.mainMenuBorderHeight + 60; + int const dialogWidth = 640; + int const dialogHeight = 480; + this->dialogX = dialogX; + this->dialogY = dialogY; + this->dialogWidth = dialogWidth; + this->dialogHeight = dialogHeight; + + if (!this->useWideHelpLayout) { + this->dialogContentX = this->dialogX + 10; + this->dialogContentY = this->dialogY + 10; + this->dialogContentWidth = 600; + this->dialogContentHeight = 435; + } else { + this->dialogContentX = this->dialogX + 10; + this->dialogContentY = this->dialogY + 10; + this->dialogContentWidth = 664; + this->dialogContentHeight = 291; + } + + for (int i = 28; i >= 0; --i) { + this->helpSectionHistoryStack[i + 1] = this->helpSectionHistoryStack[i]; + } + this->helpSectionHistoryStack[0] = this->currentHelpSectionID; + + this->currentHelpSectionID = sectionId; + this->helpContentScrollOffsetY = 0; + MACRO_CALL_MEMBER(TextEditorState_Func::loadAndLayoutHelpContent, this)(); + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/parseHLPPart.cpp b/src/OpenSHC/Text/TextEditorState/parseHLPPart.cpp new file mode 100644 index 000000000..cc97503d3 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/parseHLPPart.cpp @@ -0,0 +1,78 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/OS.func.hpp" + +#include "OpenSHC/Globals/HLP_WCHAR_Buffer.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045D200 + LPWSTR TextEditorState::parseHLPPart(FILE* filePointer) + { + + int finishedParsing = FALSE; + int quoteActive = FALSE; + int reachedWEOF = FALSE; + wchar_t* bufferPtr = HLP_WCHAR_Buffer::instance; + while (!reachedWEOF) { + if (finishedParsing) { + return HLP_WCHAR_Buffer::instance; + } + + int readWChar = MACRO_CALL(OS_Func::_fgetwc)(filePointer); + switch (readWChar) { + case L'"': + quoteActive ^= TRUE; + continue; + + case L'\t': + case L' ': + case L',': + case L'=': + if (!quoteActive) { + int consumed = FALSE; + while (!reachedWEOF && !consumed) { + readWChar = MACRO_CALL(OS_Func::_fgetwc)(filePointer); + + if (readWChar == WEOF) { + reachedWEOF = TRUE; + continue; + } + + switch (readWChar) { + default: + MACRO_CALL(OS_Func::_fseek)(filePointer, -2, FILE_CURRENT); + case L'>': + consumed = true; + case L'\t': + case L' ': + case L',': + case L'=': + break; + } + } + } + case L'>': + if (!quoteActive) { + finishedParsing = true; + *bufferPtr = L'\0'; + continue; + } + break; + + case L'\n': + case L'\r': + continue; + + case WEOF: + reachedWEOF = true; + continue; + } + *bufferPtr++ = readWChar; + } + return NULL; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/parseHlp.cpp b/src/OpenSHC/Text/TextEditorState/parseHlp.cpp new file mode 100644 index 000000000..cfc8eb8fb --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/parseHlp.cpp @@ -0,0 +1,353 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/IO/LowLevelMemory.func.hpp" +#include "OpenSHC/OS.func.hpp" +#include "OpenSHC/UI/Rendering/TextureRenderCore.func.hpp" +#include "OpenSHC/Util/WideCharMultiByteState.func.hpp" +#include "OpenSHC/Text/Enums/HelpTextPicturePositionToken.hpp" +#include "OpenSHC/wstring-literals.hpp" +#include "OpenSHC/string-literals.hpp" + +#include "OpenSHC/Globals/DAT_LowLevelMemory.hpp" +#include "OpenSHC/Globals/DAT_TextEditorState.hpp" +#include "OpenSHC/Globals/DAT_TextureRenderCoreObject.hpp" +#include "OpenSHC/Globals/DAT_UserHelpDefinedData.hpp" +#include "OpenSHC/Globals/DAT_WideCharMultiByteState.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045F5B0 + void TextEditorState::parseHlp() + { + FILE* filePointer[4]; + int graphicIndexes[4][100]; + + int const currentHelpSectionID = this->currentHelpSectionID; + int textMemIndex = 0; + + BOOL headerActive = FALSE; + BOOL bodyActive = FALSE; + int _filePointerIndex = 0; + + int loadedGraphics[4] = { 0, 0, 0, 0 }; + this->customHelpTextLength = 0; + MACRO_CALL_MEMBER(IO::LowLevelMemory_Func::fillMemory_ByteValue, DAT_LowLevelMemory::ptr)( + 40000, NULL, this->DAT_PointerToTemporaryTextMemory); + filePointer[0] = MACRO_CALL_MEMBER(TextEditorState_Func::readCrusaderHelpHlp, this)( + DAT_UserHelpDefinedData::instance.HelpSections[currentHelpSectionID]); + if (!filePointer[0]) { + return; + } + + while (_filePointerIndex >= 0) { + BOOL closeFile = FALSE; + while (!closeFile) { + int _wchar = MACRO_CALL(OS_Func::_fgetwc)(filePointer[_filePointerIndex]); + switch (_wchar) { + case '\n': + case '\r': + continue; + + case '<': { + wchar_t* hlpTag + = MACRO_CALL_MEMBER(TextEditorState_Func::parseHLPPart, this)(filePointer[_filePointerIndex]); + if (hlpTag) { + if (!MACRO_CALL(OS_Func::__wcsicmp)(u_header_005a5774, hlpTag)) { + headerActive = TRUE; + continue; + + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u__header_005a5764, hlpTag)) { + headerActive = FALSE; + continue; + + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u_body_005a5758, hlpTag)) { + bodyActive = TRUE; + continue; + + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u__body_005a574c, hlpTag)) { + bodyActive = FALSE; + continue; + + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u_loadpic_005a573c, hlpTag)) { + if (headerActive) { + wchar_t* resourceName = MACRO_CALL_MEMBER(TextEditorState_Func::parseHLPPart, this)( + filePointer[_filePointerIndex]); + if (resourceName) { + char resourceNameChars[1000]; + MACRO_CALL_MEMBER(Util::WideCharMultiByteState_Func::wideCharToMultiByteComplete, + DAT_WideCharMultiByteState::ptr)(resourceNameChars, resourceName); + + bool validPic = true; + if (!MACRO_CALL_MEMBER( + UI::Rendering::TextureRenderCore_Func::checkGfxResourceExists, + DAT_TextureRenderCoreObject::ptr)(resourceNameChars)) { + validPic = false; + } else if (!MACRO_CALL(OS_Func::__stricmp)( + resourceNameChars, s_st99_dog_cage_tgx_005a5728)) { + validPic = false; + } + if (!validPic) { + graphicIndexes[_filePointerIndex][loadedGraphics[_filePointerIndex]++] = -1; + } else { + graphicIndexes[_filePointerIndex][loadedGraphics[_filePointerIndex]++] + = MACRO_CALL_MEMBER(TextEditorState_Func::findOrAddHelpGraphicName, this)( + resourceNameChars); + } + continue; + } + } + + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u_pic_005a5720, hlpTag)) { + if (bodyActive) { + wchar_t* picId = MACRO_CALL_MEMBER(TextEditorState_Func::parseHLPPart, this)( + filePointer[_filePointerIndex]); + if (picId) { + if (MACRO_CALL(OS_Func::__wtol)(picId) < loadedGraphics[_filePointerIndex]) { + if (graphicIndexes[_filePointerIndex][MACRO_CALL(OS_Func::__wtol)(picId)] + != -1) { + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_PIC; + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] + = graphicIndexes[_filePointerIndex][MACRO_CALL(OS_Func::__wtol)(picId)]; + wchar_t* picPosition = MACRO_CALL_MEMBER(TextEditorState_Func::parseHLPPart, + this)(filePointer[_filePointerIndex]); + if (!picPosition) { + closeFile = TRUE; + } else { + if (!MACRO_CALL(OS_Func::__wcsicmp)(u_left_005a5714, picPosition)) { + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] + = Enums::HTPPT_LEFT; + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u_right_005a5708, picPosition)) { + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] + = Enums::HTPPT_RIGHT; + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u_centre_005a56f8, picPosition)) { + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] + = Enums::HTPPT_CENTRE; + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u_here_005a56ec, picPosition)) { + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] + = Enums::HTPPT_HERE; + } + } + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_PIC; + continue; + } + } + if (!MACRO_CALL_MEMBER(TextEditorState_Func::parseHLPPart, this)( + filePointer[_filePointerIndex])) { + closeFile = TRUE; + } + continue; + } + } + + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u_font_005a56e0, hlpTag)) { + if (bodyActive) { + wchar_t* fontId = MACRO_CALL_MEMBER(TextEditorState_Func::parseHLPPart, this)( + filePointer[_filePointerIndex]); + if (fontId) { + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_FONT; + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] + = (short)MACRO_CALL(OS_Func::__wtol)(fontId); + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_FONT; + continue; + } + } + + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u_colour_005a56d0, hlpTag)) { + if (bodyActive) { + wchar_t* colorId = MACRO_CALL_MEMBER(TextEditorState_Func::parseHLPPart, this)( + filePointer[_filePointerIndex]); + if (colorId) { + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_COLOUR; + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] + = (short)MACRO_CALL(OS_Func::__wtol)(colorId); + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_COLOUR; + continue; + } + } + + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u_linkcolour_005a56b8, hlpTag)) { + if (bodyActive) { + wchar_t* linkColorId = MACRO_CALL_MEMBER(TextEditorState_Func::parseHLPPart, this)( + filePointer[_filePointerIndex]); + if (linkColorId) { + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_LINKCOLOUR; + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] + = (short)MACRO_CALL(OS_Func::__wtol)(linkColorId); + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_LINKCOLOUR; + continue; + } + } + + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u_link_005a56ac, hlpTag)) { + if (bodyActive) { + wchar_t* linkName = MACRO_CALL_MEMBER(TextEditorState_Func::parseHLPPart, this)( + filePointer[_filePointerIndex]); + if (linkName) { + char linkNameChars[1000]; + MACRO_CALL_MEMBER(Util::WideCharMultiByteState_Func::wideCharToMultiByteComplete, + DAT_WideCharMultiByteState::ptr)(linkNameChars, linkName); + int const helpSectionIndex = MACRO_CALL_MEMBER( + TextEditorState_Func::findOrAddHelpSectionName, this)(linkNameChars); + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_LINK; + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = helpSectionIndex; + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_LINK; + continue; + } + } + + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u__link_005a56a0, hlpTag)) { + if (bodyActive) { + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_ENDLINK; + continue; + } + + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u_newparagraph_005a5684, hlpTag)) { + if (bodyActive) { + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_NEWPARAGRAPH; + continue; + } + + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u_centre_005a56f8, hlpTag)) { + if (bodyActive) { + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_CENTRE; + continue; + } + + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u__centre_005a5674, hlpTag)) { + if (bodyActive) { + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_ENDCENTRE; + continue; + } + + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u_sound_005a5668, hlpTag)) { + if (bodyActive) { + wchar_t* soundName = MACRO_CALL_MEMBER(TextEditorState_Func::parseHLPPart, this)( + filePointer[_filePointerIndex]); + if (soundName) { + char soundNameChars[1000]; + MACRO_CALL_MEMBER(Util::WideCharMultiByteState_Func::wideCharToMultiByteComplete, + DAT_WideCharMultiByteState::ptr)(soundNameChars, soundName); + int const soundIndex = MACRO_CALL_MEMBER( + TextEditorState_Func::findOrAddSoundName, this)(soundNameChars); + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_SOUND; + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = soundIndex; + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_SOUND; + continue; + } + } + + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u_include_005a5658, hlpTag)) { + if (bodyActive) { + if (DAT_TextEditorState::instance.useAlternateHelpTab) { + wchar_t* includeName = MACRO_CALL_MEMBER(TextEditorState_Func::parseHLPPart, this)( + filePointer[_filePointerIndex]); + if (includeName) { + char includeNameChars[1000]; + MACRO_CALL_MEMBER( + Util::WideCharMultiByteState_Func::wideCharToMultiByteComplete, + DAT_WideCharMultiByteState::ptr)(includeNameChars, includeName); + int const helpSectionIndex = MACRO_CALL_MEMBER( + TextEditorState_Func::findOrAddHelpSectionName, this)(includeNameChars); + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_INCLUDE; + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = helpSectionIndex; + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_INCLUDE; + continue; + } + } else { + wchar_t* includeName = MACRO_CALL_MEMBER(TextEditorState_Func::parseHLPPart, this)( + filePointer[_filePointerIndex]); + if (includeName) { + char includeNameChars[1000]; + MACRO_CALL_MEMBER( + Util::WideCharMultiByteState_Func::wideCharToMultiByteComplete, + DAT_WideCharMultiByteState::ptr)(includeNameChars, includeName); + int sectionId = MACRO_CALL_MEMBER( + TextEditorState_Func::findOrAddHelpSectionName, this)(includeNameChars); + ++_filePointerIndex; + bodyActive = FALSE; + headerActive = FALSE; + loadedGraphics[_filePointerIndex] = 0; + filePointer[_filePointerIndex] + = MACRO_CALL_MEMBER(TextEditorState_Func::readCrusaderHelpHlp, this)( + DAT_UserHelpDefinedData::instance.HelpSections[sectionId]); + continue; + } + } + } + + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u_string_005a5648, hlpTag)) { + if (bodyActive) { + wchar_t* stringId = MACRO_CALL_MEMBER(TextEditorState_Func::parseHLPPart, this)( + filePointer[_filePointerIndex]); + if (stringId) { + if (this->helpDialogVariant) { + long const textIndex = MACRO_CALL(OS_Func::__wtol)(stringId); + + if (this->intArray1[textIndex]) { + int i = 0; + // Mismatch: wrong order of ptr access addition + while (this->intArray1[textIndex][i]) { + wchar_t const ch = this->intArray1[textIndex][i++]; + if (ch == L'\n') { + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] + = Enums::HTT_NEWPARAGRAPH; + } else if (ch >= L' ') { + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = ch; + } + } + } + + } else { + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_STRING; + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] + = (short)MACRO_CALL(OS_Func::__wtol)(stringId); + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = Enums::HTT_STRING; + } + continue; + } + } + } else if (!MACRO_CALL(OS_Func::__wcsicmp)(u__section_005a5634, hlpTag)) { + closeFile = TRUE; + continue; + } else { + continue; + } + } + + closeFile = TRUE; + continue; + } + + case L'\u2019': // ’ + _wchar = L'`'; + break; + + case L'\u201C': // “ + case L'\u201D': // ” + _wchar = L'"'; + break; + + case WEOF: + closeFile = TRUE; + continue; + + default: + break; + } + + if (bodyActive) { + this->DAT_PointerToTemporaryTextMemory[textMemIndex++] = _wchar; + } + }; + + MACRO_CALL(OS_Func::_fclose)(filePointer[_filePointerIndex]); + bodyActive = TRUE; + headerActive = FALSE; + --_filePointerIndex; + } + this->customHelpTextLength = textMemIndex; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/popHelpDialogStack.cpp b/src/OpenSHC/Text/TextEditorState/popHelpDialogStack.cpp new file mode 100644 index 000000000..5fd6604c0 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/popHelpDialogStack.cpp @@ -0,0 +1,25 @@ +#include "../TextEditorState.func.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x00462150 + void TextEditorState::popHelpDialogStack() + { + int sectionId = this->helpSectionHistoryStack[0]; + + for (int i = 0; i < 29; ++i) { + this->helpSectionHistoryStack[i] = this->helpSectionHistoryStack[i + 1]; + } + this->helpSectionHistoryStack[29] = -1; + + this->currentHelpSectionID = sectionId; + if (sectionId == -1) { + MACRO_CALL_MEMBER(TextEditorState_Func::closeHelpDialogAndReturnToMenu, this)(); + } else { + MACRO_CALL_MEMBER(TextEditorState_Func::loadAndLayoutHelpContent, this)(); + } + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/processHelpRichTextTokens.cpp b/src/OpenSHC/Text/TextEditorState/processHelpRichTextTokens.cpp new file mode 100644 index 000000000..ca3e75a9c --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/processHelpRichTextTokens.cpp @@ -0,0 +1,793 @@ +// disable deprecation warnings for wcscpy and wcscat +#pragma warning(disable : 4996) + +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/Global.func.hpp" +#include "OpenSHC/Input/MouseState.func.hpp" +#include "OpenSHC/Text/FontSizeClass.func.hpp" +#include "OpenSHC/UI/Rendering/PencilRenderCore.func.hpp" +#include "OpenSHC/Text/Enums/HelpTextPicturePositionToken.hpp" +#include "OpenSHC/wstring-literals.hpp" + +#include "OpenSHC/Globals/COL_DARK_LIME.hpp" +#include "OpenSHC/Globals/DAT_00df3348.hpp" +#include "OpenSHC/Globals/DAT_00df334c.hpp" +#include "OpenSHC/Globals/DAT_MouseState.hpp" +#include "OpenSHC/Globals/DAT_PencilRenderCore.hpp" +#include "OpenSHC/Globals/DAT_TextManagerObject.hpp" +#include "OpenSHC/Globals/DAT_TextureRenderCoreObject.hpp" +#include "OpenSHC/Globals/DAT_UserHelpDefinedData.hpp" + +// Even now, part of the logic after the L' ' is pulled into the switch, but not the right way +// the position after endlink might be related to being the first position that has no +2 on the the memindex, but still +// sets the wideBuffer; no idea how to trigger it, though + +// It is possible to move the pulled section by messing with certain branches, however, this likely really requires the +// right structure over all cases, it might even involve mixed structures, since a human wrote it + +// most unmarked things are related to the alternate help text, which only seems to be active for the map editor text, +// which is strange. So either this is a left over, or maybe the help text editor hat a switch for this in some way + +namespace OpenSHC { +namespace Text { + + union TextMemUnion { + int value; + wchar_t* text; + HelpTextToken helpToken; + Enums::HelpTextPicturePositionToken picturePositionToken; + }; + + // FUNCTION: STRONGHOLDCRUSADER 0x0045FDC0 + int TextEditorState::processHelpRichTextTokens(int param_1) + { + wchar_t wideTextBuffer[100]; + + int local_f8; + + int currentLinkId = -1; + int textMemIndex = 0; + FontSizeClass* currentFontClass = DAT_TextManagerObject::instance.fontSizeClassArray + 17; + int currentFontLineHeight = currentFontClass->lineHeight_0x14; + BOOL local_f0 = FALSE; + BGR24 color = 0xccfaff; + BGR24 linkColor = 0x8bcf84; + int imageCount = -1; + BOOL centreActive = FALSE; + BOOL centerCurrentLine = FALSE; + int lineStartY = 0; + int lineEndY = 0; + int local_11c = 0; + int local_100 = 0; + int local_10c = 0; + BOOL local_e4 = FALSE; + int local_134 = -10000; + if (this->helpDialogVariant == 2 || this->isCustomTextMode) { + color = 0xc2f0eb; + } + if (!this->isTextHelpDialogMode) { + return 1; + } + this->field49_0x23964 = -1; + this->currentLinkId = -1; + if (this->field51_0x2396c) { + DAT_00df334c::instance = 1; + } + if (this->activeHelpHotspotIndex < 0) { + this->activeHelpHotspotIndex = 0; + } + if (this->activeHelpHotspotIndex > this->customHelpTextLength) { + this->activeHelpHotspotIndex = this->customHelpTextLength; + } + int lineY = -1; + int leftPosition; + do { + leftPosition = this->lineLayoutTable[++lineY].leftBorder; + } while (leftPosition == -1); + + bool abortProcessing; + do { + abortProcessing = false; + + TextMemUnion textToRender; + int local_114; + BGR24 charColor; + // false if text debug view token handled (this->helpDialogSubMode == 1), maybe related to input? + BOOL local_12c; + + charColor = color; + local_12c = TRUE; + if (0 <= currentLinkId) { + charColor = linkColor; + } + local_114 = textMemIndex; + textToRender.value + = MACRO_CALL_MEMBER(TextEditorState_Func::getWideCharOrWideCharPointer, this)(&textMemIndex); + if (this->useAlternateHelpTab && textToRender.value < L' ' + && textToRender.helpToken != Enums::HTT_NEWPARAGRAPH && local_114 == this->activeHelpHotspotIndex) { + lineStartY = lineY; + lineEndY = lineY + currentFontLineHeight; + if (param_1 == 1) { + int xModify = 0; + if (centerCurrentLine) { + xModify = this->lineLayoutTable[lineY].widthForCentering / 2; + } + local_10c = local_114 - local_11c; + if (this->field51_0x2396c < 0) { + this->activeHelpHotspotIndex = local_100 + DAT_00df3348::instance; + if (this->activeHelpHotspotIndex >= local_11c) { + this->activeHelpHotspotIndex = local_11c - 1; + } + this->field51_0x2396c = 0; + } + local_134 = 0; + int xPos = this->dialogContentX + xModify; + int yPos = this->dialogContentY - this->helpContentScrollOffsetY; + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawLine, DAT_PencilRenderCore::ptr)( + xPos + leftPosition - 1, yPos + lineY - 1, xPos + leftPosition - 1, yPos + lineEndY + 1, + COL_DARK_LIME::instance.shortValue); + xPos = this->dialogContentX + xModify; + yPos = this->dialogContentY - this->helpContentScrollOffsetY; + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawLine, DAT_PencilRenderCore::ptr)( + xPos + leftPosition, yPos + lineY - 1, xPos + leftPosition, yPos + lineEndY + 1, + COL_DARK_LIME::instance.shortValue); + local_e4 = TRUE; + } + } + + switch (textToRender.helpToken) { + case Enums::HTT_PIC: { + if (this->helpDialogSubMode == 1) { + charColor = 0xff; + local_12c = FALSE; + MACRO_CALL(Global_Func::PrintToDestination)(wideTextBuffer, u__PIC__d__S__005a583c, + this->DAT_PointerToTemporaryTextMemory[textMemIndex], + this->graphicFileNames[this->DAT_PointerToTemporaryTextMemory[textMemIndex]]); + Enums::HelpTextPicturePositionToken positionToken + = (Enums::HelpTextPicturePositionToken)this->DAT_PointerToTemporaryTextMemory[textMemIndex + 1]; + ++textMemIndex; + switch (positionToken) { + case Enums::HTPPT_LEFT: + wcscat(wideTextBuffer, u_LEFT__005a5494); + textMemIndex += 2; + textToRender.text = wideTextBuffer; + break; + case Enums::HTPPT_RIGHT: + wcscat(wideTextBuffer, u_RIGHT__005a5484); + textMemIndex += 2; + textToRender.text = wideTextBuffer; + break; + case Enums::HTPPT_CENTRE: + wcscat(wideTextBuffer, u_CENTRE__005a5474); + textMemIndex += 2; + textToRender.text = wideTextBuffer; + break; + case Enums::HTPPT_HERE: + wcscat(wideTextBuffer, u_HERE__005a5468); + textMemIndex += 2; + textToRender.text = wideTextBuffer; + break; + default: + textMemIndex += 2; + textToRender.text = wideTextBuffer; + break; + } + break; + } + ++imageCount; + + bool validImageIndex = false; + int imageIndex; + if (param_1 == 1 && this->DAT_PointerToTemporaryTextMemory[textMemIndex + 1] == Enums::HTPPT_HERE) { + imageIndex = this->DAT_PointerToTemporaryTextMemory[textMemIndex]; + validImageIndex = true; + } else if (!param_1 + && (imageCount == this->imageHotspotCount + || this->DAT_PointerToTemporaryTextMemory[textMemIndex + 1] == Enums::HTPPT_HERE)) { + imageIndex = this->DAT_PointerToTemporaryTextMemory[textMemIndex]; + if (imageCount == this->imageHotspotCount) { + this->imageHotspotTable[this->imageHotspotCount].yPos = lineY + 25; + this->imageHotspotTable[this->imageHotspotCount].imageIndex = imageIndex; + this->imageHotspotTable[this->imageHotspotCount].linkId = currentLinkId; + } + validImageIndex = true; + } + if (validImageIndex) { + Enums::HelpTextPicturePositionToken positionToken + = (Enums::HelpTextPicturePositionToken)this->DAT_PointerToTemporaryTextMemory[textMemIndex + 1]; + ++textMemIndex; + int imageWidth = DAT_TextureRenderCoreObject::instance.loadedGfxArray[99 - imageIndex].width; + int imageHeight = DAT_TextureRenderCoreObject::instance.loadedGfxArray[99 - imageIndex].height; + switch (positionToken) { + case Enums::HTPPT_LEFT: { + this->imageHotspotTable[this->imageHotspotCount].xPos = this->lineLayoutTable[lineY].leftBorder; + int newLeftBorder = this->lineLayoutTable[lineY].leftBorder + 25 + imageWidth; + for (int i = 0; i < imageHeight + 50; ++i) { + this->lineLayoutTable[lineY + i].leftBorder = newLeftBorder; + } + break; + } + case Enums::HTPPT_RIGHT: { + this->imageHotspotTable[this->imageHotspotCount].xPos + = this->lineLayoutTable[lineY].rightBorder - imageWidth; + int newRightBorder = this->lineLayoutTable[lineY].rightBorder - imageWidth - 25; + for (int i = 0; i < imageHeight + 50; ++i) { + this->lineLayoutTable[lineY + i].rightBorder = newRightBorder; + } + break; + } + case Enums::HTPPT_CENTRE: + this->imageHotspotTable[this->imageHotspotCount].xPos + = (this->dialogContentWidth - imageWidth) / 2; + for (int i = 0; i < imageHeight + 50; ++i) { + this->lineLayoutTable[lineY + i].leftBorder = -1; + } + break; + case Enums::HTPPT_HERE: { + int const hereWidth = imageWidth + 3; + if (hereWidth + leftPosition > this->lineLayoutTable[lineY].rightBorder) { + local_f0 = FALSE; + if (centerCurrentLine) { + this->lineLayoutTable[lineY].widthForCentering + = this->lineLayoutTable[lineY].rightBorder - leftPosition; + centerCurrentLine = centreActive; + } + do { + lineY += currentFontLineHeight + 1; + do { + leftPosition = this->lineLayoutTable[++lineY].leftBorder; + } while (leftPosition == -1); + } while (leftPosition + hereWidth > this->lineLayoutTable[lineY].rightBorder); + if (this->useAlternateHelpTab && param_1 == 1) { + local_100 = local_11c; + local_11c = local_114; + ++local_134; + if (0 < this->field51_0x2396c && 2 <= local_134) { + local_134 = -10000; + this->activeHelpHotspotIndex = local_100 + local_10c; + if (this->activeHelpHotspotIndex >= local_11c) { + this->activeHelpHotspotIndex = local_11c - 1; + } + this->field51_0x2396c = 0; + } + } + currentFontLineHeight = currentFontClass->lineHeight_0x14; + } + if (imageHeight > currentFontLineHeight) { + currentFontLineHeight = imageHeight; + } + leftPosition += hereWidth; + if (param_1 != 1 && imageCount == this->imageHotspotCount) { + this->imageHotspotTable[this->imageHotspotCount].xPos = leftPosition - hereWidth; + this->imageHotspotTable[this->imageHotspotCount].yPos = lineY; + ++this->imageHotspotCount; + } + break; + } + } + textMemIndex += 2; + if (positionToken != Enums::HTPPT_HERE && !param_1) { + ++this->imageHotspotCount; + DAT_TextManagerObject::instance.helpTextLinkActive = FALSE; + abortProcessing = true; + // continue; + } + } else { + textMemIndex += 3; + } + break; + } + case Enums::HTT_FONT: { + if (this->helpDialogSubMode == 1) { + charColor = 0xff; + local_12c = FALSE; + MACRO_CALL(Global_Func::PrintToDestination)( + wideTextBuffer, u__FONT__d__005a5454, this->DAT_PointerToTemporaryTextMemory[textMemIndex]); + textMemIndex += 2; + textToRender.text = wideTextBuffer; + break; + } + int fontSize = this->DAT_PointerToTemporaryTextMemory[textMemIndex]; + if (fontSize < 15) { + fontSize += 5; + } + textMemIndex += 2; + currentFontClass = &DAT_TextManagerObject::instance.fontSizeClassArray[fontSize]; + if (currentFontClass->lineHeight_0x14 > currentFontLineHeight || !local_f0) { + currentFontLineHeight = currentFontClass->lineHeight_0x14; + } + break; + } + case Enums::HTT_COLOUR: { + if (this->helpDialogSubMode == 1) { + charColor = 0xff; + local_12c = FALSE; + MACRO_CALL(Global_Func::PrintToDestination)(wideTextBuffer, u__COLOUR__S__005a5824, + DAT_UserHelpDefinedData::instance + .field6_0x7a16c[this->DAT_PointerToTemporaryTextMemory[textMemIndex]] + .name_0x0); + textMemIndex += 2; + textToRender.text = wideTextBuffer; + break; + } + if (param_1 == 1) { + int const colorIndex = this->DAT_PointerToTemporaryTextMemory[textMemIndex]; + + uint colorRed; + uint colorGreen; + uint colorBlue; + if (this->helpDialogVariant == 2 && colorIndex == 1) { + colorRed = 0xd7; + colorGreen = 0xd2; + colorBlue = 0xa4; + } else { + colorRed = DAT_UserHelpDefinedData::instance.field6_0x7a16c[colorIndex].r_0x4; + colorGreen = DAT_UserHelpDefinedData::instance.field6_0x7a16c[colorIndex].g_0x8; + colorBlue = DAT_UserHelpDefinedData::instance.field6_0x7a16c[colorIndex].b_0xc; + } + textMemIndex += 2; + color = RGB(colorRed, colorGreen, colorBlue); + } else { + textMemIndex += 2; + } + break; + } + case Enums::HTT_LINKCOLOUR: { + if (this->helpDialogSubMode == 1) { + charColor = 0xff; + local_12c = FALSE; + MACRO_CALL(Global_Func::PrintToDestination)(wideTextBuffer, u__LINKCOLOUR__S__005a5804, + DAT_UserHelpDefinedData::instance + .field6_0x7a16c[this->DAT_PointerToTemporaryTextMemory[textMemIndex]] + .name_0x0); + textMemIndex += 2; + textToRender.text = wideTextBuffer; + break; + } + if (param_1 == 1) { + int const colorIndex = this->DAT_PointerToTemporaryTextMemory[textMemIndex]; + textMemIndex += 2; + linkColor = RGB(DAT_UserHelpDefinedData::instance.field6_0x7a16c[colorIndex].r_0x4, + DAT_UserHelpDefinedData::instance.field6_0x7a16c[colorIndex].g_0x8, + DAT_UserHelpDefinedData::instance.field6_0x7a16c[colorIndex].b_0xc); + } else { + textMemIndex += 2; + } + break; + } + case Enums::HTT_LINK: { + if (this->helpDialogSubMode == 1) { + charColor = 0xff; + local_12c = FALSE; + MACRO_CALL(Global_Func::PrintToDestination)(wideTextBuffer, u__LINK_S__005a57ec, + DAT_UserHelpDefinedData::instance + .HelpSections[this->DAT_PointerToTemporaryTextMemory[textMemIndex]]); + textMemIndex += 2; + textToRender.text = wideTextBuffer; + break; + } + currentLinkId = this->DAT_PointerToTemporaryTextMemory[textMemIndex]; + if (param_1 == 1) { + DAT_TextManagerObject::instance.helpTextLinkActive = TRUE; + } + textMemIndex += 2; + break; + } + case Enums::HTT_INCLUDE: { + if (this->helpDialogSubMode == 1) { + charColor = 0xff; + local_12c = FALSE; + MACRO_CALL(Global_Func::PrintToDestination)(wideTextBuffer, u__INCLUDE_S__005a57cc, + DAT_UserHelpDefinedData::instance + .HelpSections[this->DAT_PointerToTemporaryTextMemory[textMemIndex]]); + textMemIndex += 2; + textToRender.text = wideTextBuffer; + break; + } + textMemIndex += 2; + break; + } + case Enums::HTT_SOUND: { + if (this->helpDialogSubMode == 1) { + charColor = 0xff; + local_12c = FALSE; + MACRO_CALL(Global_Func::PrintToDestination)(wideTextBuffer, u__SOUND_S__005a57b0, + this->soundFileNames[this->DAT_PointerToTemporaryTextMemory[textMemIndex]]); + textMemIndex += 2; + textToRender.text = wideTextBuffer; + break; + } + if (param_1 == 1) { + int const visibleY = lineY - this->helpContentScrollOffsetY; + if (0 <= visibleY && visibleY < this->dialogContentHeight + && !this->soundFilePlayedFlags[this->DAT_PointerToTemporaryTextMemory[textMemIndex]]) { + this->soundFilePlayedFlags[this->DAT_PointerToTemporaryTextMemory[textMemIndex]] = 1; + } + } + textMemIndex += 2; + break; + } + case Enums::HTT_ENDLINK: { + if (this->helpDialogSubMode == 1) { + charColor = 0xff; + local_12c = FALSE; + wcscpy(wideTextBuffer, u__LINK__005a53d4); + textToRender.text = wideTextBuffer; + break; + } + currentLinkId = -1; + if (param_1 == 1) { + DAT_TextManagerObject::instance.helpTextLinkActive = FALSE; + } + break; + } + case Enums::HTT_STRING: { + if (this->helpDialogSubMode == 1) { + charColor = 0xff; + local_12c = FALSE; + MACRO_CALL(Global_Func::PrintToDestination)( + wideTextBuffer, u__STRING__d__005a5358, this->DAT_PointerToTemporaryTextMemory[textMemIndex]); + textMemIndex += 2; + textToRender.text = wideTextBuffer; + break; + } + textMemIndex += 2; + break; + } + case Enums::HTT_CENTRE: { + if (this->helpDialogSubMode == 1) { + charColor = 0xff; + local_12c = FALSE; + wcscpy(wideTextBuffer, u__CENTRE__005a539c); + textToRender.text = wideTextBuffer; + break; + } + centreActive = TRUE; + centerCurrentLine = TRUE; + break; + } + case Enums::HTT_ENDCENTRE: { + if (this->helpDialogSubMode == 1) { + charColor = 0xff; + local_12c = FALSE; + wcscpy(wideTextBuffer, u__CENTRE__005a5388); + textToRender.text = wideTextBuffer; + break; + } + centreActive = FALSE; + break; + } + case Enums::HTT_NEWPARAGRAPH: { + if (centerCurrentLine) { + this->lineLayoutTable[lineY].widthForCentering + = this->lineLayoutTable[lineY].rightBorder - leftPosition; + centerCurrentLine = centreActive; + } + if (this->useAlternateHelpTab && local_114 == this->activeHelpHotspotIndex) { + lineStartY = lineY; + lineEndY = currentFontLineHeight + lineY; + if (param_1 == 1) { + int xModify = 0; + if (centerCurrentLine) { + xModify = this->lineLayoutTable[lineY].widthForCentering / 2; + } + int xPos = this->dialogContentX + xModify; + int yPos = this->dialogContentY - this->helpContentScrollOffsetY; + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawLine, DAT_PencilRenderCore::ptr)( + xPos - 1 + leftPosition, yPos - 1 + lineStartY, xPos - 1 + leftPosition, + yPos + 1 + lineEndY, COL_DARK_LIME::instance.shortValue); + xPos = this->dialogContentX + xModify; + yPos = this->dialogContentY - this->helpContentScrollOffsetY; + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawLine, DAT_PencilRenderCore::ptr)( + xPos + leftPosition, yPos - 1 + lineStartY, xPos + leftPosition, yPos + 1 + lineEndY, + COL_DARK_LIME::instance.shortValue); + local_e4 = TRUE; + } + } + local_f0 = FALSE; + lineY += 3 + currentFontLineHeight; + do { + leftPosition = this->lineLayoutTable[++lineY].leftBorder; + } while (leftPosition == -1); + currentFontLineHeight = currentFontClass->lineHeight_0x14; + + if (this->useAlternateHelpTab) { + local_100 = local_11c; + local_11c = local_114; + if (param_1 == 1) { + ++local_134; + if (0 < this->field51_0x2396c && 2 <= local_134) { + local_134 = -10000; + this->activeHelpHotspotIndex = local_10c + local_100; + if (this->activeHelpHotspotIndex >= local_11c) { + this->activeHelpHotspotIndex = local_11c - 1; + } + this->field51_0x2396c = 0; + } + } + if (local_11c == this->activeHelpHotspotIndex && param_1 == 1) { + local_10c = 0; + if (this->field51_0x2396c < 0) { + this->activeHelpHotspotIndex = DAT_00df3348::instance + local_100; + if (this->activeHelpHotspotIndex >= local_11c) { + this->activeHelpHotspotIndex = local_11c - 1; + } + this->field51_0x2396c = 0; + } + local_134 = 0; + } + } + if (this->helpDialogSubMode == 1) { + charColor = 0xff; + local_12c = FALSE; + wcscpy(wideTextBuffer, u__NEWPARAGRAPH__005a5790); + textToRender.text = wideTextBuffer; + break; + } + break; + } + case Enums::HTT_TAB: { // TODO?: unknown token + if (this->helpDialogSubMode == 1) { + charColor = 0xff; + local_12c = FALSE; + wcscpy(wideTextBuffer, u__tab__005a5784); + textToRender.text = wideTextBuffer; + break; + } + if (leftPosition + 30 > this->lineLayoutTable[lineY].rightBorder) { + local_f0 = FALSE; + if (centerCurrentLine) { + this->lineLayoutTable[lineY].widthForCentering + = this->lineLayoutTable[lineY].rightBorder - leftPosition; + centerCurrentLine = centreActive; + } + do { + lineY += currentFontLineHeight + 1; + do { + leftPosition = this->lineLayoutTable[++lineY].leftBorder; + } while (leftPosition == -1); + } while (leftPosition + 30 > this->lineLayoutTable[lineY].rightBorder); + + if (this->useAlternateHelpTab && param_1 == 1) { + local_100 = local_11c; + local_11c = local_114; + ++local_134; + if (0 < this->field51_0x2396c && 2 <= local_134) { + local_134 = -10000; + this->activeHelpHotspotIndex = local_100 + local_10c; + if (this->activeHelpHotspotIndex >= local_11c) { + this->activeHelpHotspotIndex = local_11c - 1; + } + this->field51_0x2396c = 0; + } + } + currentFontLineHeight = currentFontClass->lineHeight_0x14; + } + leftPosition += 30; + break; + } + case (wchar_t*)0: { // TODO?: unknown token, or just NULL, meaning end? + if (param_1 == 0) { + this->topVisibleLineIndex = lineY + currentFontLineHeight - 1; + do { + ++this->topVisibleLineIndex; + if (this->lineLayoutTable[this->topVisibleLineIndex].leftBorder == 25 + && this->lineLayoutTable[this->topVisibleLineIndex].rightBorder + == this->dialogContentWidth - 25) { + break; + } + } while (this->topVisibleLineIndex < 20000); + this->topVisibleLineIndex += 5; + if (this->useAlternateHelpTab) { + if (lineStartY < this->helpContentScrollOffsetY) { + this->helpContentScrollOffsetY = lineStartY; + } + if (lineEndY > this->helpContentScrollOffsetY + this->dialogContentHeight) { + this->helpContentScrollOffsetY = (lineEndY - this->dialogContentHeight) + 5; + DAT_TextManagerObject::instance.helpTextLinkActive = FALSE; + return TRUE; + } + } + } else if (this->useAlternateHelpTab) { + DAT_00df3348::instance = local_10c; + if (0 < this->field51_0x2396c && local_134 == 1) { + this->activeHelpHotspotIndex = local_10c + local_11c; + if (this->activeHelpHotspotIndex > this->customHelpTextLength) { + this->activeHelpHotspotIndex = this->customHelpTextLength; + } + this->field51_0x2396c = 0; + } + if (!local_e4 && this->activeHelpHotspotIndex && DAT_00df334c::instance) { + --this->activeHelpHotspotIndex; + this->field52_0x23970 = 0; + DAT_TextManagerObject::instance.helpTextLinkActive = FALSE; + return TRUE; + } + DAT_00df334c::instance = 0; + } + DAT_TextManagerObject::instance.helpTextLinkActive = FALSE; + return TRUE; + } + } + if (textToRender.value < L' ') { + continue; + } + + int textWidth = MACRO_CALL_MEMBER(FontSizeClass_Func::getWidthOfWideText, currentFontClass)( + textToRender.text, wcslen(textToRender.text)); + if (leftPosition + textWidth > this->lineLayoutTable[lineY].rightBorder) { + local_f0 = FALSE; + if (centerCurrentLine) { + this->lineLayoutTable[lineY].widthForCentering + = this->lineLayoutTable[lineY].rightBorder - leftPosition; + centerCurrentLine = centreActive; + } + lineY += 1 + currentFontLineHeight; + do { + leftPosition = this->lineLayoutTable[++lineY].leftBorder; + } while (leftPosition == -1); + currentFontLineHeight = currentFontClass->lineHeight_0x14; + if (this->useAlternateHelpTab) { + ++local_134; + local_100 = local_11c; + local_11c = local_114; + if (0 < this->field51_0x2396c && 2 <= local_134) { + local_134 = -10000; + this->activeHelpHotspotIndex = local_100 + local_10c; + if (this->activeHelpHotspotIndex >= local_11c) { + this->activeHelpHotspotIndex = local_11c - 1; + } + this->field51_0x2396c = 0; + } + } + if (leftPosition + textWidth > this->lineLayoutTable[lineY].rightBorder) { + local_f8 = 0; + if (centerCurrentLine) { + local_f8 = this->lineLayoutTable[lineY].widthForCentering / 2; + } + int wordLength = wcslen(textToRender.text); + for (int i = 0; i < wordLength; ++i) { + int wideCharWidth = MACRO_CALL_MEMBER(FontSizeClass_Func::getWideCharWidth, currentFontClass)( + textToRender.text[i]); + while (leftPosition + wideCharWidth > this->lineLayoutTable[lineY].rightBorder) { + + if (centerCurrentLine) { + this->lineLayoutTable[lineY].widthForCentering + = this->lineLayoutTable[lineY].rightBorder - leftPosition; + centerCurrentLine = centreActive; + } + lineY += currentFontLineHeight + 1; + if (this->useAlternateHelpTab && param_1 == 1) { + local_100 = local_11c; + local_11c = local_114 + i; + ++local_134; + if (0 < this->field51_0x2396c && 2 <= local_134) { + local_134 = -10000; + this->activeHelpHotspotIndex = local_100 + local_10c; + if (this->activeHelpHotspotIndex >= local_11c) { + this->activeHelpHotspotIndex = local_11c - 1; + } + this->field51_0x2396c = 0; + } + } + do { + leftPosition = this->lineLayoutTable[++lineY].leftBorder; + } while (leftPosition == -1); + if (centerCurrentLine) { + local_f8 = this->lineLayoutTable[lineY].widthForCentering / 2; + } + } + if (this->useAlternateHelpTab && local_114 + i == this->activeHelpHotspotIndex) { + lineStartY = lineY; + lineEndY = lineY + currentFontLineHeight; + if (param_1 == 1) { + if (local_12c) { + local_10c = (i - local_11c) + local_114; + if (this->field51_0x2396c < 0) { + this->activeHelpHotspotIndex = DAT_00df3348::instance + local_100; + if (this->activeHelpHotspotIndex >= local_11c) { + this->activeHelpHotspotIndex = local_11c - 1; + } + this->field51_0x2396c = 0; + } + local_134 = 0; + int xPos = this->dialogContentX + local_f8; + int yPos = this->dialogContentY - this->helpContentScrollOffsetY; + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawLine, + DAT_PencilRenderCore::ptr)(xPos - 1 + leftPosition, yPos - 1 + lineY, + xPos - 1 + leftPosition, yPos + 1 + lineEndY, + COL_DARK_LIME::instance.shortValue); + xPos = this->dialogContentX + local_f8; + yPos = this->dialogContentY - this->helpContentScrollOffsetY; + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawLine, + DAT_PencilRenderCore::ptr)(xPos + leftPosition, yPos - 1 + lineY, + xPos + leftPosition, yPos + 1 + lineEndY, COL_DARK_LIME::instance.shortValue); + local_e4 = TRUE; + } + } + } + if (param_1 == 1) { + int charXPos = this->dialogContentX + local_f8 + leftPosition; + int charYPos = this->dialogContentY - this->helpContentScrollOffsetY + lineY; + + wchar_t currentChar = textToRender.text[i]; + int const newCharXPos = MACRO_CALL_MEMBER(FontSizeClass_Func::renderWideChar, + currentFontClass)(currentChar, charXPos, charYPos, charColor, 0); + local_f0 = TRUE; + if (MACRO_CALL_MEMBER(Input::MouseState_Func::isMouseInsideBox, DAT_MouseState::ptr)( + charXPos - this->helpContentScrollX, charYPos - this->helpContentScrollY, + newCharXPos - charXPos, currentFontLineHeight + 2)) { + if (local_12c) { + this->field49_0x23964 = local_114 + i; + this->currentLinkId = currentLinkId; + } else { + this->field49_0x23964 = local_114; + } + } + } + + leftPosition += wideCharWidth; + } + continue; + } + } + int charXPos = this->dialogContentX + leftPosition; + int charYPos = (this->dialogContentY - this->helpContentScrollOffsetY) + lineY; + if (centerCurrentLine) { + charXPos += this->lineLayoutTable[lineY].widthForCentering / 2; + } + int wordLength = wcslen(textToRender.text); + for (int i = 0; i < wordLength; ++i) { + if (this->useAlternateHelpTab && i + local_114 == this->activeHelpHotspotIndex) { + if (param_1 == 1 && local_12c) { + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawLine, DAT_PencilRenderCore::ptr)( + charXPos - 1, charYPos - 1, charXPos - 1, charYPos + 1 + currentFontLineHeight, + COL_DARK_LIME::instance.shortValue); + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawLine, DAT_PencilRenderCore::ptr)( + charXPos, charYPos - 1, charXPos, charYPos + 1 + currentFontLineHeight, + COL_DARK_LIME::instance.shortValue); + local_e4 = TRUE; + } + lineStartY = lineY; + lineEndY = lineY + currentFontLineHeight; + if (param_1 == 1) { + local_134 = 0; + local_10c = local_114 - local_11c + i; + if (this->field51_0x2396c < 0 && local_12c) { + this->activeHelpHotspotIndex = local_100 + DAT_00df3348::instance; + if (this->activeHelpHotspotIndex >= local_11c) { + this->activeHelpHotspotIndex = local_11c - 1; + } + this->field51_0x2396c = 0; + } + } + } + + if (param_1 == 1) { + local_f0 = TRUE; + int oldCharPos = charXPos; + wchar_t const currentChar = textToRender.text[i]; + charXPos = MACRO_CALL_MEMBER(FontSizeClass_Func::renderWideChar, currentFontClass)( + currentChar, charXPos, charYPos, charColor, 0); + if (MACRO_CALL_MEMBER(Input::MouseState_Func::isMouseInsideBox, DAT_MouseState::ptr)( + oldCharPos - this->helpContentScrollX, charYPos - this->helpContentScrollY, + charXPos - oldCharPos, currentFontLineHeight + 2)) { + if (local_12c) { + this->field49_0x23964 = i + local_114; + this->currentLinkId = currentLinkId; + } else { + this->field49_0x23964 = local_114; + } + } + } else { + charXPos += MACRO_CALL_MEMBER(FontSizeClass_Func::getWideCharWidth, currentFontClass)( + textToRender.text[i]); + } + } + leftPosition += textWidth; + } while (!abortProcessing); + return FALSE; + } +} +} diff --git a/src/OpenSHC/Text/TextEditorState/readCrusaderHelpHlp.cpp b/src/OpenSHC/Text/TextEditorState/readCrusaderHelpHlp.cpp new file mode 100644 index 000000000..523598141 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/readCrusaderHelpHlp.cpp @@ -0,0 +1,65 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/IO/ResourceManager.func.hpp" +#include "OpenSHC/OS.func.hpp" +#include "OpenSHC/Util/WideCharMultiByteState.func.hpp" +#include "OpenSHC/string-literals.hpp" +#include "OpenSHC/wstring-literals.hpp" + +#include "OpenSHC/Globals/DAT_ResourceManager.hpp" +#include "OpenSHC/Globals/DAT_WideCharMultiByteState.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045F470 + FILE* TextEditorState::readCrusaderHelpHlp(LPCSTR searchedPart) + { + this->helpSectionParseSucceeded = FALSE; + + WCHAR searchedPartWide[200]; + MACRO_CALL_MEMBER(Util::WideCharMultiByteState_Func::multiByteToWideCharacter, DAT_WideCharMultiByteState::ptr)( + searchedPartWide, searchedPart); + MACRO_CALL_MEMBER(IO::ResourceManager_Func::resolveResourceFileName, DAT_ResourceManager::ptr)( + IO::FRT_HELP, s_crusader_help_hlp_005a5620); + + char const* const _Filename = MACRO_CALL_MEMBER( + IO::ResourceManager_Func::getFileNameOfCurrentActiveResource, DAT_ResourceManager::ptr)(); + FILE* _File = MACRO_CALL(OS_Func::_fopen)(_Filename, s_rb_005a4e18); + if (_File) { + MACRO_CALL(OS_Func::_fseek)(_File, 2, FILE_BEGIN); // Skip BOM? + + int wideChar; + while (true) { + wideChar = MACRO_CALL(OS_Func::_fgetwc)(_File); + if (wideChar != L'<') { + if (wideChar == WEOF) { + return _File; + } + continue; + } + wchar_t* hlpPart = MACRO_CALL_MEMBER(TextEditorState_Func::parseHLPPart, this)(_File); + if (!hlpPart || MACRO_CALL(OS_Func::__wcsicmp)(u_section_005a5610, hlpPart)) { + continue; + } + wchar_t* sectionHlpPart = MACRO_CALL_MEMBER(TextEditorState_Func::parseHLPPart, this)(_File); + if (!sectionHlpPart || MACRO_CALL(OS_Func::__wcsicmp)(searchedPartWide, sectionHlpPart)) { + continue; + } + break; + }; + + do { + wideChar = MACRO_CALL(OS_Func::_fgetwc)(_File); + if (wideChar == L'>') { + this->helpSectionParseSucceeded = TRUE; + break; + } + } while (wideChar != WEOF); + } + + return _File; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/renderHelpDialogIfOpen.cpp b/src/OpenSHC/Text/TextEditorState/renderHelpDialogIfOpen.cpp new file mode 100644 index 000000000..13ff6f4a4 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/renderHelpDialogIfOpen.cpp @@ -0,0 +1,16 @@ +#include "../TextEditorState.func.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x00461550 + void TextEditorState::renderHelpDialogIfOpen() + { + if (this->isDialogStateInitialized) { + MACRO_CALL_MEMBER(TextEditorState_Func::drawHelpWindowBackground, this)(); + MACRO_CALL_MEMBER(TextEditorState_Func::setTextRenderingLogic, this)(); + } + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/renderHelpImageHotspots.cpp b/src/OpenSHC/Text/TextEditorState/renderHelpImageHotspots.cpp new file mode 100644 index 000000000..08998118c --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/renderHelpImageHotspots.cpp @@ -0,0 +1,73 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/Input/MouseState.func.hpp" +#include "OpenSHC/UI/Rendering/PencilRenderCore.func.hpp" +#include "OpenSHC/UI/Rendering/TextureRenderCore.func.hpp" + +#include "OpenSHC/Globals/COL_GREYISH_YELLOW.hpp" +#include "OpenSHC/Globals/DAT_MouseState.hpp" +#include "OpenSHC/Globals/DAT_PencilRenderCore.hpp" +#include "OpenSHC/Globals/DAT_TextureRenderCoreObject.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045D430 + void TextEditorState::renderHelpImageHotspots() + { + for (int i = 0; i < this->imageHotspotCount; ++i) { + int _gfxIndex = 99 - this->imageHotspotTable[i].imageIndex; + if ((this->useInGameHelpHandler || this->helpDialogVariant == 1) + && !MACRO_CALL_MEMBER(UI::Rendering::TextureRenderCore_Func::checkIfGfxTgxStartsWithTransparentPixels, + DAT_TextureRenderCoreObject::ptr)(_gfxIndex)) { + int x1 = this->imageHotspotTable[i].xPos + this->dialogContentX - 2; + int y1 = this->imageHotspotTable[i].yPos - this->helpContentScrollOffsetY + this->dialogContentY - 2; + int width = DAT_TextureRenderCoreObject::instance.loadedGfxArray[_gfxIndex].width + 3; + int height = DAT_TextureRenderCoreObject::instance.loadedGfxArray[_gfxIndex].height + 3; + if (y1 < this->dialogContentY) { + height += y1 - this->dialogContentY; + y1 = this->dialogContentY; + } + + if (height + y1 > this->dialogContentY + this->dialogContentHeight) { + height = this->dialogContentHeight * 2 - y1; + } + int const y2 = y1 + height; + if (y2 >= this->dialogContentY && y1 < this->dialogContentY + this->dialogContentHeight) { + int const x2 = x1 + width; + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawLine, DAT_PencilRenderCore::ptr)( + x1, y1, x2, y1, COL_GREYISH_YELLOW::instance.shortValue); + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawLine, DAT_PencilRenderCore::ptr)( + x1, y1 + 1, x2, y1 + 1, COL_GREYISH_YELLOW::instance.shortValue); + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawLine, DAT_PencilRenderCore::ptr)( + x1, y2, x2, y2, COL_GREYISH_YELLOW::instance.shortValue); + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawLine, DAT_PencilRenderCore::ptr)( + x1, y2 + -1, x2, y2 + -1, COL_GREYISH_YELLOW::instance.shortValue); + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawLine, DAT_PencilRenderCore::ptr)( + x1, y1, x1, y2, COL_GREYISH_YELLOW::instance.shortValue); + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawLine, DAT_PencilRenderCore::ptr)( + x1 + 1, y1, x1 + 1, y2, COL_GREYISH_YELLOW::instance.shortValue); + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawLine, DAT_PencilRenderCore::ptr)( + x2, y1, x2, y2, COL_GREYISH_YELLOW::instance.shortValue); + MACRO_CALL_MEMBER(UI::Rendering::PencilRenderCore_Func::drawLine, DAT_PencilRenderCore::ptr)( + x2 + -1, y1, x2 + -1, y2, COL_GREYISH_YELLOW::instance.shortValue); + } + } + MACRO_CALL_MEMBER(UI::Rendering::TextureRenderCore_Func::drawGfxOnFlaggedSurface, + DAT_TextureRenderCoreObject::ptr)(_gfxIndex, this->imageHotspotTable[i].xPos + this->dialogContentX, + this->imageHotspotTable[i].yPos - this->helpContentScrollOffsetY + this->dialogContentY); + + int const boxHeight = DAT_TextureRenderCoreObject::instance.loadedGfxArray[_gfxIndex].height; + int const boxWidth = DAT_TextureRenderCoreObject::instance.loadedGfxArray[_gfxIndex].width; + if (MACRO_CALL_MEMBER(Input::MouseState_Func::isMouseInsideBox, DAT_MouseState::ptr)( + this->imageHotspotTable[i].xPos - this->helpContentScrollX + this->dialogContentX, + this->imageHotspotTable[i].yPos - this->helpContentScrollY + this->dialogContentY + - this->helpContentScrollOffsetY, + boxWidth, boxHeight)) { + this->currentLinkId = this->imageHotspotTable[i].linkId; + } + } + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/resetHelpStateFields.cpp b/src/OpenSHC/Text/TextEditorState/resetHelpStateFields.cpp new file mode 100644 index 000000000..de0e1f34a --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/resetHelpStateFields.cpp @@ -0,0 +1,16 @@ +#include "../TextEditorState.func.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045D0C0 + void TextEditorState::resetHelpStateFields() + { + for (int i = 0; i < 30; ++i) { + this->helpSectionHistoryStack[i] = -1; + } + this->currentHelpSectionID = -1; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/saveHelpFileToResource.cpp b/src/OpenSHC/Text/TextEditorState/saveHelpFileToResource.cpp new file mode 100644 index 000000000..009aa59dc --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/saveHelpFileToResource.cpp @@ -0,0 +1,120 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/IO/ResourceManager.func.hpp" +#include "OpenSHC/OS.func.hpp" +#include "OpenSHC/Text/Enums/HelpTextPicturePositionToken.hpp" +#include "OpenSHC/string-literals.hpp" +#include "OpenSHC/wstring-literals.hpp" + +#include "OpenSHC/Globals/DAT_ResourceManager.hpp" +#include "OpenSHC/Globals/DAT_UserHelpDefinedData.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045DB40 + void TextEditorState::saveHelpFileToResource() + { + MACRO_CALL_MEMBER(IO::ResourceManager_Func::resolveResourceFileName, DAT_ResourceManager::ptr)( + IO::FRT_HELP, DAT_UserHelpDefinedData::instance.HelpSections[this->currentHelpSectionID]); + char const* const _Filename = MACRO_CALL_MEMBER( + IO::ResourceManager_Func::getFileNameOfCurrentActiveResource, DAT_ResourceManager::ptr)(); + FILE* _File = MACRO_CALL(OS_Func::_fopen)(_Filename, s_wb_005a5510); + if (!_File) { + return; + } + wchar_t const utf16bom = L'\uFEFF'; + MACRO_CALL(OS_Func::_fwrite)((void*)&utf16bom, sizeof(wchar_t), 1, _File); + + MACRO_CALL(OS_Func::_fwprintf)(_File, u__HEADER__005a54fc); + for (int i = 0; i < this->graphicFileCount; ++i) { + MACRO_CALL(OS_Func::_fwprintf)(_File, u__LOADPIC_S__005a54dc, this->graphicFileNames[i]); + } + MACRO_CALL(OS_Func::_fwprintf)(_File, u__HEADER__005a54c4); + MACRO_CALL(OS_Func::_fwprintf)(_File, u__BODY__005a54b4); + + for (int i = 0; this->DAT_PointerToTemporaryTextMemory[i];) { + HelpTextToken const switchValue = (HelpTextToken)this->DAT_PointerToTemporaryTextMemory[i]; + i += 1; + switch (switchValue) { + case Enums::HTT_PIC: + MACRO_CALL(OS_Func::_fwprintf)(_File, u__PIC__d__005a54a0, this->DAT_PointerToTemporaryTextMemory[i]); + i += 1; + switch ((Enums::HelpTextPicturePositionToken)this->DAT_PointerToTemporaryTextMemory[i]) { + case Enums::HTPPT_LEFT: + MACRO_CALL(OS_Func::_fwprintf)(_File, u_LEFT__005a5494); + i += 2; + break; + case Enums::HTPPT_RIGHT: + MACRO_CALL(OS_Func::_fwprintf)(_File, u_RIGHT__005a5484); + i += 2; + break; + case Enums::HTPPT_CENTRE: + MACRO_CALL(OS_Func::_fwprintf)(_File, u_CENTRE__005a5474); + i += 2; + break; + case Enums::HTPPT_HERE: + MACRO_CALL(OS_Func::_fwprintf)(_File, u_HERE__005a5468); + default: + i += 2; + break; + } + break; + case Enums::HTT_FONT: + MACRO_CALL(OS_Func::_fwprintf)(_File, u__FONT__d__005a5454, this->DAT_PointerToTemporaryTextMemory[i]); + i += 2; + break; + case Enums::HTT_COLOUR: + MACRO_CALL(OS_Func::_fwprintf)(_File, u__COLOUR_d__005a543c, this->DAT_PointerToTemporaryTextMemory[i]); + i += 2; + break; + case Enums::HTT_LINKCOLOUR: + MACRO_CALL(OS_Func::_fwprintf)( + _File, u__LINKCOLOUR_d__005a541c, this->DAT_PointerToTemporaryTextMemory[i]); + i += 2; + break; + case Enums::HTT_LINK: + MACRO_CALL(OS_Func::_fwprintf)(_File, u__LINK__S__005a5404, + DAT_UserHelpDefinedData::instance.HelpSections[this->DAT_PointerToTemporaryTextMemory[i]]); + i += 2; + break; + case Enums::HTT_INCLUDE: + MACRO_CALL(OS_Func::_fwprintf)(_File, u__INCLUDE__S__005a53e4, + DAT_UserHelpDefinedData::instance.HelpSections[this->DAT_PointerToTemporaryTextMemory[i]]); + i += 2; + break; + case Enums::HTT_ENDLINK: + MACRO_CALL(OS_Func::_fwprintf)(_File, u__LINK__005a53d4); + break; + case Enums::HTT_NEWPARAGRAPH: + MACRO_CALL(OS_Func::_fwprintf)(_File, u__NEWPARAGRAPH__005a53b0); + break; + case Enums::HTT_CENTRE: + MACRO_CALL(OS_Func::_fwprintf)(_File, u__CENTRE__005a539c); + break; + case Enums::HTT_ENDCENTRE: + MACRO_CALL(OS_Func::_fwprintf)(_File, u__CENTRE__005a5388); + break; + case Enums::HTT_SOUND: + MACRO_CALL(OS_Func::_fwprintf)(_File, u__SOUND__d__005a5370, this->DAT_PointerToTemporaryTextMemory[i]); + i += 2; + break; + case Enums::HTT_STRING: + MACRO_CALL(OS_Func::_fwprintf)( + _File, u__STRING__d__005a5358, this->DAT_PointerToTemporaryTextMemory[i]); + i += 2; + break; + default: + MACRO_CALL(OS_Func::_fwprintf)(_File, u__c_005a5350, (wchar_t)switchValue); + break; + } + } + + MACRO_CALL(OS_Func::_fwprintf)(_File, u__BODY__005a533c); + MACRO_CALL(OS_Func::_fclose)(_File); + } + +#undef MACRO_TEXT_ACCESS + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/setCustomHelpText.cpp b/src/OpenSHC/Text/TextEditorState/setCustomHelpText.cpp new file mode 100644 index 000000000..83c6495a6 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/setCustomHelpText.cpp @@ -0,0 +1,24 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/Util/WideCharMultiByteState.func.hpp" + +#include "OpenSHC/Globals/DAT_WideCharMultiByteState.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x004620F0 + void TextEditorState::setCustomHelpText(char* helpText, int bufferSize) + { + this->customHelpTextPointer = helpText; + this->isCustomHelpTextWide = 0; + this->customHelpTextBufferSize = bufferSize; + MACRO_CALL_MEMBER(Util::WideCharMultiByteState_Func::multiByteToWideCharacter, DAT_WideCharMultiByteState::ptr)( + this->DAT_PointerToTemporaryTextMemory, helpText); + this->customHelpTextLength = strlen(helpText); + this->isTextHelpDialogMode = TRUE; + MACRO_CALL_MEMBER(TextEditorState_Func::loadAndLayoutHelpContent, this)(); + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/setHelpWindowBounds.cpp b/src/OpenSHC/Text/TextEditorState/setHelpWindowBounds.cpp new file mode 100644 index 000000000..eab80e92c --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/setHelpWindowBounds.cpp @@ -0,0 +1,16 @@ +#include "../TextEditorState.func.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x0045D080 + void TextEditorState::setHelpWindowBounds(int x, int y, int width, int height) + { + this->dialogContentX = x; + this->dialogContentY = y; + this->dialogContentWidth = width; + this->dialogContentHeight = height; + } + +} +} diff --git a/src/OpenSHC/Text/TextEditorState/setTextRenderingLogic.cpp b/src/OpenSHC/Text/TextEditorState/setTextRenderingLogic.cpp new file mode 100644 index 000000000..c553407e2 --- /dev/null +++ b/src/OpenSHC/Text/TextEditorState/setTextRenderingLogic.cpp @@ -0,0 +1,55 @@ +#include "../TextEditorState.func.hpp" + +#include "OpenSHC/UI/Rendering/TextureRenderCore.func.hpp" + +#include "OpenSHC/Globals/DAT_PencilRenderCore.hpp" +#include "OpenSHC/Globals/DAT_TextManagerObject.hpp" +#include "OpenSHC/Globals/DAT_TextureRenderCoreObject.hpp" + +namespace OpenSHC { +namespace Text { + + // FUNCTION: STRONGHOLDCRUSADER 0x004613E0 + void TextEditorState::setTextRenderingLogic() + { + if (this->helpDialogVariant != 1) { + MACRO_CALL_MEMBER(UI::Rendering::TextureRenderCore_Func::setRenderingRect, + DAT_TextureRenderCoreObject::ptr)(this->dialogContentX, this->dialogContentY, + this->dialogContentWidth + this->dialogContentX, this->dialogContentHeight + this->dialogContentY); + MACRO_CALL_MEMBER(UI::Rendering::TextureRenderCore_Func::setScreenMenuSurfaceHeightRange, + DAT_TextureRenderCoreObject::ptr)( + this->dialogContentY, this->dialogContentHeight + this->dialogContentY); + DAT_TextureRenderCoreObject::instance.drawBufferChoiceValue = Rendering::Enums::RT_SCREEN_MENU; + } else { + MACRO_CALL_MEMBER(UI::Rendering::TextureRenderCore_Func::setRenderingRect, + DAT_TextureRenderCoreObject::ptr)(this->dialogContentX, this->dialogContentY, + this->dialogContentWidth + this->dialogContentX, this->dialogContentHeight + this->dialogContentY); + MACRO_CALL_MEMBER( + UI::Rendering::TextureRenderCore_Func::setMapSurfaceHeightRange, DAT_TextureRenderCoreObject::ptr)( + this->dialogContentY, this->dialogContentHeight + this->dialogContentY); + DAT_TextManagerObject::instance.textSurfaceTarget = Rendering::Enums::RT_MAP_GAME; + DAT_PencilRenderCore::instance.surfaceTarget = Rendering::Enums::RT_MAP_GAME; + DAT_TextureRenderCoreObject::instance.currentRenderSurfaceIdentifierUnk_0x8 = Rendering::Enums::RT_MAP_GAME; + } + MACRO_CALL_MEMBER(TextEditorState_Func::processHelpRichTextTokens, this)(TRUE); + MACRO_CALL_MEMBER(TextEditorState_Func::renderHelpImageHotspots, this)(); + if (this->helpDialogVariant != 1) { + DAT_TextureRenderCoreObject::instance.drawBufferChoiceValue = Rendering::Enums::RT_MAP_GAME; + MACRO_CALL_MEMBER(UI::Rendering::TextureRenderCore_Func::setScreenMenuSurfaceHeightRangeToResolution, + DAT_TextureRenderCoreObject::ptr)(); + MACRO_CALL_MEMBER(UI::Rendering::TextureRenderCore_Func::setRenderingRectToGameResolution, + DAT_TextureRenderCoreObject::ptr)(); + } else { + MACRO_CALL_MEMBER( + UI::Rendering::TextureRenderCore_Func::setMapSurfaceHeightRangeUnk, DAT_TextureRenderCoreObject::ptr)(); + MACRO_CALL_MEMBER(UI::Rendering::TextureRenderCore_Func::setRenderingRectToGameResolution, + DAT_TextureRenderCoreObject::ptr)(); + DAT_TextManagerObject::instance.textSurfaceTarget = Rendering::Enums::RT_SCREEN_MENU; + DAT_PencilRenderCore::instance.surfaceTarget = Rendering::Enums::RT_SCREEN_MENU; + DAT_TextureRenderCoreObject::instance.currentRenderSurfaceIdentifierUnk_0x8 + = Rendering::Enums::RT_SCREEN_MENU; + } + } + +} +} diff --git a/status/addresses-SHC-3BB0A8C1.txt b/status/addresses-SHC-3BB0A8C1.txt index f6e47278b..e9977c224 100644 --- a/status/addresses-SHC-3BB0A8C1.txt +++ b/status/addresses-SHC-3BB0A8C1.txt @@ -18147,17 +18147,17 @@ SHC_3BB0A8C1_0x0045CD10 | 0.0% | Pending SHC_3BB0A8C1_0x0045D060 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045D080 | 0.0% | Pending +SHC_3BB0A8C1_0x0045D080 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0045D0B0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045D0C0 | 0.0% | Pending +SHC_3BB0A8C1_0x0045D0C0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045D140 | 0.0% | Pending +SHC_3BB0A8C1_0x0045D140 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045D1A0 | 0.0% | Pending +SHC_3BB0A8C1_0x0045D1A0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045D200 | 0.0% | Pending +SHC_3BB0A8C1_0x0045D200 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0045D300 | 0.0% | Pending @@ -18327,41 +18327,41 @@ SHC_3BB0A8C1_0x0045D364 | 0.0% | Pending SHC_3BB0A8C1_0x0045D365 | 0.0% | Pending -SHC_3BB0A8C1_0x0045D370 | 0.0% | Pending +SHC_3BB0A8C1_0x0045D370 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045D3C0 | 0.0% | Pending +SHC_3BB0A8C1_0x0045D3C0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045D430 | 0.0% | Pending +SHC_3BB0A8C1_0x0045D430 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0045D460 | 0.0% | Pending -SHC_3BB0A8C1_0x0045D690 | 0.0% | Pending +SHC_3BB0A8C1_0x0045D690 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045D6C0 | 0.0% | Pending +SHC_3BB0A8C1_0x0045D6C0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045D740 | 0.0% | Pending +SHC_3BB0A8C1_0x0045D740 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045D890 | 0.0% | Pending +SHC_3BB0A8C1_0x0045D890 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045D8F0 | 0.0% | Pending +SHC_3BB0A8C1_0x0045D8F0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045D950 | 0.0% | Pending +SHC_3BB0A8C1_0x0045D950 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045D990 | 0.0% | Pending +SHC_3BB0A8C1_0x0045D990 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045D9E0 | 0.0% | Pending +SHC_3BB0A8C1_0x0045D9E0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045DA20 | 0.0% | Pending +SHC_3BB0A8C1_0x0045DA20 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045DA40 | 0.0% | Pending +SHC_3BB0A8C1_0x0045DA40 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0045DA60 | 0.0% | Pending -SHC_3BB0A8C1_0x0045DA80 | 0.0% | Pending +SHC_3BB0A8C1_0x0045DA80 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045DAE0 | 0.0% | Pending +SHC_3BB0A8C1_0x0045DAE0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045DB40 | 0.0% | Pending +SHC_3BB0A8C1_0x0045DB40 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0045DDF4 | 0.0% | Pending @@ -18709,7 +18709,7 @@ SHC_3BB0A8C1_0x0045EF84 | 0.0% | Pending SHC_3BB0A8C1_0x0045EF90 | 0.0% | Pending -SHC_3BB0A8C1_0x0045F080 | 0.0% | Pending +SHC_3BB0A8C1_0x0045F080 | 100.0% | Reimplemented SHC_3BB0A8C1_0x0045F0B4 | 0.0% | Pending @@ -18745,21 +18745,21 @@ SHC_3BB0A8C1_0x0045F0CC | 0.0% | Pending SHC_3BB0A8C1_0x0045F0CD | 0.0% | Pending -SHC_3BB0A8C1_0x0045F0D0 | 0.0% | Pending +SHC_3BB0A8C1_0x0045F0D0 | 100.0% | Reimplemented, requires SHC_3BB0A8C1_0x0045F130 active SHC_3BB0A8C1_0x0045F120 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045F130 | 0.0% | Pending +SHC_3BB0A8C1_0x0045F130 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045F240 | 0.0% | Pending +SHC_3BB0A8C1_0x0045F240 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045F470 | 0.0% | Pending +SHC_3BB0A8C1_0x0045F470 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045F580 | 0.0% | Pending +SHC_3BB0A8C1_0x0045F580 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x0045F5B0 | 0.0% | Pending +SHC_3BB0A8C1_0x0045F5B0 | 99.0% | Reimplemented, two cases where an "edx + ecx" is flipped to "ecx + edx" -SHC_3BB0A8C1_0x0045FDC0 | 0.0% | Pending +SHC_3BB0A8C1_0x0045FDC0 | 70.1% | Reimplemented, but heavy structural mismatch, seems functional SHC_3BB0A8C1_0x0045FEC0 | 0.0% | Pending @@ -18825,15 +18825,15 @@ SHC_3BB0A8C1_0x004613D0 | 0.0% | Pending SHC_3BB0A8C1_0x004613D4 | 0.0% | Pending -SHC_3BB0A8C1_0x004613E0 | 0.0% | Pending +SHC_3BB0A8C1_0x004613E0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004614D0 | 0.0% | Pending +SHC_3BB0A8C1_0x004614D0 | 100.0% | Reimplemented SHC_3BB0A8C1_0x004614F0 | 0.0% | Pending SHC_3BB0A8C1_0x00461520 | 0.0% | Pending -SHC_3BB0A8C1_0x00461550 | 0.0% | Pending +SHC_3BB0A8C1_0x00461550 | 100.0% | Reimplemented SHC_3BB0A8C1_0x00461570 | 0.0% | Pending @@ -18879,23 +18879,23 @@ SHC_3BB0A8C1_0x004619C4 | 0.0% | Pending SHC_3BB0A8C1_0x004619C8 | 0.0% | Pending -SHC_3BB0A8C1_0x004619D0 | 0.0% | Pending +SHC_3BB0A8C1_0x004619D0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00461A20 | 0.0% | Pending +SHC_3BB0A8C1_0x00461A20 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00461B90 | 0.0% | Pending +SHC_3BB0A8C1_0x00461B90 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00461CF0 | 0.0% | Pending +SHC_3BB0A8C1_0x00461CF0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00461E50 | 0.0% | Pending +SHC_3BB0A8C1_0x00461E50 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00461F90 | 0.0% | Pending +SHC_3BB0A8C1_0x00461F90 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x004620F0 | 0.0% | Pending +SHC_3BB0A8C1_0x004620F0 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00462150 | 0.0% | Pending +SHC_3BB0A8C1_0x00462150 | 100.0% | Reimplemented -SHC_3BB0A8C1_0x00462190 | 0.0% | Pending +SHC_3BB0A8C1_0x00462190 | 100.0% | Reimplemented SHC_3BB0A8C1_0x00462340 | 0.0% | Pending