Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion IMPLEMENTATION_CHEAT_SHEET.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ do {

#### Known special cases

- `memset` so far was seen optimizing a "set all bytes to zero" case, if it was smaller then a unknown amount. In all known cases it used `EAX` for this. Would still recommend to try `memset` for cases with a register filling multiple memory locations.
- `memset` so far was seen optimizing a "set all bytes to zero" case, if it was smaller then a unknown amount. In all known cases it used `EAX` for this. Would still recommend to try `memset` for cases with a register filling multiple **byte-sized** memory locations.

### CRT Functions

Expand Down Expand Up @@ -322,3 +322,13 @@ If the value is assigned to another object, the compiler tries to avoid creating
**This only works if the function is called directly. Any form of indirection via pointer or resolver will not optimize. This is a fundamental limitation of the MSVC2005 compiler.**

As a result, such cases do not use the resolver. The limitation through this is accepted, although, it should be noted in the status entry for the caller.

### Stack

The stack of the function is an important orientation for decompilation.
The reference point is the `esp` register, which points to the top of the stack. The stack grows **downwards** from the top, so actions that grow the stack reduce the `esp` register, for example `push` reduces the `esp` register by 4 and grows the stack by 4.

Naturally, the stack reserved for the function indicated by `sub esp, <number>` at the start and `add esp, <number>` at the end of the function, need to match. This can be achieved by adding or removing locals or shrinking or growing arrays within the logical boundaries of the function. **It must never break the logic**.
At best, not only the stack fits, but variables are also at the correct positions and sizes within.

If the stack usage shows a proper order in the function (i.e. the initial array occupies the first part in the allocated function stack, which means it requires the highest `[esp + <number>]` to reach), it might be sometimes good to fit one "side" of the stack first and then experiment with the rest of the function that has a non-fitting stack usage.
12 changes: 8 additions & 4 deletions src/OpenSHC/Game/GameCore.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@
namespace OpenSHC {
namespace Game {

struct ScenarioProgress {
int progressCallToArms;
int progressSaladinsConquest;
int progressTheKingsCrusade;
int progressCrusaderStates;
};

using OpenSHC::Game::GameMode2Int;
using OpenSHC::Game::TrailTypeInt;
using OpenSHC::Map::MapLockStateInt;
Expand Down Expand Up @@ -59,10 +66,7 @@ namespace Game {
BuildMenuTabTypeShort tabTypeSiegeSubset; // 0x00000038 length: 4
dword historicCampaignNumber; // 0x0000003C length: 4
int missionNumber1to20; // 0x00000040 length: 4
int progressCallToArms; // 0x00000044 length: 4
int progressSaladinsConquest; // 0x00000048 length: 4
int progressTheKingsCrusade; // 0x0000004C length: 4
int progressCrusaderStates; // 0x00000050 length: 4
ScenarioProgress scenarioProgress; // 0x00000044 length: 16
dword furthestMission; // 0x00000054 length: 4
dword unlockAllHistoricalCampaigns; // 0x00000058 length: 4
dword unused1; // 0x0000005C length: 4
Expand Down
308 changes: 308 additions & 0 deletions src/OpenSHC/IO/SettingsFileState/readUserConfig.cpp

Large diffs are not rendered by default.

183 changes: 183 additions & 0 deletions src/OpenSHC/IO/SettingsFileState/writeUserConfig.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// disable deprecation warnings for strcpy and strcat
#pragma warning(disable : 4996)

#include "../SettingsFileState.func.hpp"

#include "OpenSHC/IO/ResourceManager.func.hpp"
#include "OpenSHC/OS.func.hpp"
#include "OpenSHC/Text/UserTextHandler.func.hpp"
#include "OpenSHC/Util/WideCharMultiByteState.func.hpp"
#include "OpenSHC/string-literals.hpp"

#include "OpenSHC/Globals/DAT_GameCore.hpp"
#include "OpenSHC/Globals/DAT_GameSynchronyState.hpp"
#include "OpenSHC/Globals/DAT_MouseState.hpp"
#include "OpenSHC/Globals/DAT_ResourceManager.hpp"
#include "OpenSHC/Globals/DAT_ScrollingHandler.hpp"
#include "OpenSHC/Globals/DAT_SoundSystemState.hpp"
#include "OpenSHC/Globals/DAT_UserTextHandlerState.hpp"
#include "OpenSHC/Globals/DAT_ViewportRenderState.hpp"
#include "OpenSHC/Globals/DAT_WideCharMultiByteState.hpp"
#include "OpenSHC/Globals/DAT_WindowAndDirectDraw.hpp"

namespace OpenSHC {
namespace IO {

// FUNCTION: STRONGHOLDCRUSADER 0x00496400
void SettingsFileState::writeUserConfig()
{
char configPath[1000];
// NOTE: Requires direct, non-resolved call due to copy elision.
strcpy(configPath, DAT_ResourceManager::ptr->paths_getDocumentsFolderString(true).c_str());
strcat(configPath, s_crusader_cfg_005a75bc);

FILE* _File = MACRO_CALL(OS_Func::_fopen)(configPath, s_wb_005a5510);
if (!_File) {
return;
}
WCHAR _wideCharBuffer[265];

MACRO_CALL_MEMBER(Util::WideCharMultiByteState_Func::multiByteToWideCharThunk, DAT_WideCharMultiByteState::ptr)(
_wideCharBuffer,
MACRO_CALL_MEMBER(Text::UserTextHandler_Func::getTextArrayPointer, DAT_UserTextHandlerState::ptr)(5), 15);
MACRO_CALL(OS_Func::_fwrite)(_wideCharBuffer, sizeof(WCHAR), 15, _File);

MACRO_CALL_MEMBER(Util::WideCharMultiByteState_Func::multiByteToWideCharThunk, DAT_WideCharMultiByteState::ptr)(
_wideCharBuffer,
MACRO_CALL_MEMBER(Text::UserTextHandler_Func::getTextArrayPointer, DAT_UserTextHandlerState::ptr)(6), 5);
MACRO_CALL(OS_Func::_fwrite)(_wideCharBuffer, sizeof(WCHAR), 5, _File);

MACRO_CALL_MEMBER(Util::WideCharMultiByteState_Func::multiByteToWideCharThunk, DAT_WideCharMultiByteState::ptr)(
_wideCharBuffer,
MACRO_CALL_MEMBER(Text::UserTextHandler_Func::getTextArrayPointer, DAT_UserTextHandlerState::ptr)(7), 15);
MACRO_CALL(OS_Func::_fwrite)(_wideCharBuffer, sizeof(WCHAR), 15, _File);

MACRO_CALL_MEMBER(Util::WideCharMultiByteState_Func::multiByteToWideCharThunk, DAT_WideCharMultiByteState::ptr)(
_wideCharBuffer,
MACRO_CALL_MEMBER(Text::UserTextHandler_Func::getTextArrayPointer, DAT_UserTextHandlerState::ptr)(0), 256);
MACRO_CALL(OS_Func::_fwrite)(_wideCharBuffer, sizeof(WCHAR), 256, _File);

MACRO_CALL(OS_Func::_fwrite)(
&DAT_GameCore::instance.gameSpeedLevel, sizeof(DAT_GameCore::instance.gameSpeedLevel), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(
&DAT_GameCore::instance.settingBubbleHelp, sizeof(DAT_GameCore::instance.settingBubbleHelp), 1, _File);

MACRO_CALL(OS_Func::_fwrite)(&DAT_WindowAndDirectDraw::instance.currentGameResolution,
sizeof(DAT_WindowAndDirectDraw::instance.currentGameResolution), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(&DAT_ViewportRenderState::instance.viewportState.isZoomedOutUnk,
sizeof(DAT_ViewportRenderState::instance.viewportState.isZoomedOutUnk), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(&DAT_ScrollingHandler::instance.scrollSpeedSetting_0x38,
sizeof(DAT_ScrollingHandler::instance.scrollSpeedSetting_0x38), 1, _File);

MACRO_CALL(OS_Func::_fwrite)(&DAT_SoundSystemState::instance.soundActiveUnk_0x0,
sizeof(DAT_SoundSystemState::instance.soundActiveUnk_0x0), 1, _File);

MACRO_CALL(OS_Func::_fwrite)(&DAT_SoundSystemState::instance.streamVolume[0],
sizeof(DAT_SoundSystemState::instance.streamVolume[0]), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(&DAT_SoundSystemState::instance.streamVolume[1],
sizeof(DAT_SoundSystemState::instance.streamVolume[1]), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(&DAT_SoundSystemState::instance.streamVolume[3],
sizeof(DAT_SoundSystemState::instance.streamVolume[3]), 1, _File);

MACRO_CALL(OS_Func::_fwrite)(
&DAT_GameCore::instance.scenarioProgress, sizeof(DAT_GameCore::instance.scenarioProgress), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(
&DAT_GameCore::instance.furthestMission, sizeof(DAT_GameCore::instance.furthestMission), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(&DAT_GameCore::instance.unused1, sizeof(DAT_GameCore::instance.unused1), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(&DAT_GameSynchronyState::instance.skirmishAutoSaveEveryMinutes,
sizeof(DAT_GameSynchronyState::instance.skirmishAutoSaveEveryMinutes), 1, _File);

MACRO_CALL(OS_Func::_fwrite)(
&DAT_GameCore::instance.missionDifficulty, sizeof(DAT_GameCore::instance.missionDifficulty), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(
&DAT_GameCore::instance.missionDifficulty2, sizeof(DAT_GameCore::instance.missionDifficulty2), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(
&DAT_GameCore::instance.missionDifficulty_2, sizeof(DAT_GameCore::instance.missionDifficulty_2), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(
&DAT_GameCore::instance.missionDifficulty_3, sizeof(DAT_GameCore::instance.missionDifficulty_3), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(
&DAT_GameCore::instance.missionDifficulty_1, sizeof(DAT_GameCore::instance.missionDifficulty_1), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(
&DAT_GameCore::instance.missionDifficulty_0, sizeof(DAT_GameCore::instance.missionDifficulty_0), 1, _File);

MACRO_CALL(OS_Func::_fwrite)(
&DAT_MouseState::instance.cursorType, sizeof(DAT_MouseState::instance.cursorType), 1, _File);

MACRO_CALL_MEMBER(Util::WideCharMultiByteState_Func::multiByteToWideCharThunk, DAT_WideCharMultiByteState::ptr)(
_wideCharBuffer, DAT_GameSynchronyState::instance.ipRelatedArray[0],
sizeof(DAT_GameSynchronyState::instance.ipRelatedArray[0]));
MACRO_CALL(OS_Func::_fwrite)(
_wideCharBuffer, sizeof(WCHAR), sizeof(DAT_GameSynchronyState::instance.ipRelatedArray[0]), _File);

MACRO_CALL_MEMBER(Util::WideCharMultiByteState_Func::multiByteToWideCharThunk, DAT_WideCharMultiByteState::ptr)(
_wideCharBuffer, DAT_GameSynchronyState::instance.ipRelatedArray[1],
sizeof(DAT_GameSynchronyState::instance.ipRelatedArray[1]));
MACRO_CALL(OS_Func::_fwrite)(
_wideCharBuffer, sizeof(WCHAR), sizeof(DAT_GameSynchronyState::instance.ipRelatedArray[1]), _File);

MACRO_CALL_MEMBER(Util::WideCharMultiByteState_Func::multiByteToWideCharThunk, DAT_WideCharMultiByteState::ptr)(
_wideCharBuffer, DAT_GameSynchronyState::instance.ipRelatedArray[2],
sizeof(DAT_GameSynchronyState::instance.ipRelatedArray[2]));
MACRO_CALL(OS_Func::_fwrite)(
_wideCharBuffer, sizeof(WCHAR), sizeof(DAT_GameSynchronyState::instance.ipRelatedArray[2]), _File);

MACRO_CALL_MEMBER(Util::WideCharMultiByteState_Func::multiByteToWideCharThunk, DAT_WideCharMultiByteState::ptr)(
_wideCharBuffer, DAT_GameSynchronyState::instance.ipRelatedArray[3],
sizeof(DAT_GameSynchronyState::instance.ipRelatedArray[3]));
MACRO_CALL(OS_Func::_fwrite)(
_wideCharBuffer, sizeof(WCHAR), sizeof(DAT_GameSynchronyState::instance.ipRelatedArray[3]), _File);

MACRO_CALL(OS_Func::_fwrite)(&DAT_GameCore::instance.furthestSkirmishTrailMission,
sizeof(DAT_GameCore::instance.furthestSkirmishTrailMission), 1, _File);

MACRO_CALL(OS_Func::_fwrite)(
&DAT_GameCore::instance.lordIconUnk, sizeof(DAT_GameCore::instance.lordIconUnk), 1, _File);

MACRO_CALL(OS_Func::_fwrite)(&DAT_GameCore::instance.skirmishTrailProgress,
sizeof(DAT_GameCore::instance.skirmishTrailProgress), 1, _File);

MACRO_CALL(OS_Func::_fwrite)(
&DAT_GameCore::instance.selectedLordTypeUnk, sizeof(DAT_GameCore::instance.selectedLordTypeUnk), 1, _File);

MACRO_CALL(OS_Func::_fwrite)(&DAT_GameCore::instance.skirmishTrailYearReached,
sizeof(DAT_GameCore::instance.skirmishTrailYearReached), 1, _File);

MACRO_CALL(OS_Func::_fwrite)(DAT_GameCore::instance.skirmishTrailMonthsTakenOrChicken,
sizeof(DAT_GameCore::instance.skirmishTrailMonthsTakenOrChicken[0]),
sizeof(DAT_GameCore::instance.skirmishTrailMonthsTakenOrChicken)
/ sizeof(DAT_GameCore::instance.skirmishTrailMonthsTakenOrChicken[0]),
_File);

MACRO_CALL(OS_Func::_fwrite)(
&DAT_GameCore::instance.genieVoiceActive, sizeof(DAT_GameCore::instance.genieVoiceActive), 1, _File);

MACRO_CALL(OS_Func::_fwrite)(&DAT_GameCore::instance.furthestWarchestTrailMission,
sizeof(DAT_GameCore::instance.furthestWarchestTrailMission), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(&DAT_GameCore::instance.warchestTrailProgress,
sizeof(DAT_GameCore::instance.warchestTrailProgress), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(&DAT_GameCore::instance.warchestTrailYearReached,
sizeof(DAT_GameCore::instance.warchestTrailYearReached), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(DAT_GameCore::instance.warchestTrailMonthsTakenOrChicken,
sizeof(DAT_GameCore::instance.warchestTrailMonthsTakenOrChicken[0]), 30, _File);

MACRO_CALL(OS_Func::_fwrite)(&DAT_GameCore::instance.furthestExtremeTrailMission,
sizeof(DAT_GameCore::instance.furthestExtremeTrailMission), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(&DAT_GameCore::instance.extremeTrailProgress,
sizeof(DAT_GameCore::instance.extremeTrailProgress), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(&DAT_GameCore::instance.extremeTrailYearReached,
sizeof(DAT_GameCore::instance.extremeTrailYearReached), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(DAT_GameCore::instance.extremeTrailMonthsTakenOrChicken,
sizeof(DAT_GameCore::instance.extremeTrailMonthsTakenOrChicken[0]), 30, _File);

MACRO_CALL(OS_Func::_fwrite)(&DAT_GameCore::instance.tacticalPowersDisplayFlag,
sizeof(DAT_GameCore::instance.tacticalPowersDisplayFlag), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(
&DAT_GameCore::instance.unusedOption1, sizeof(DAT_GameCore::instance.unusedOption1), 1, _File);
MACRO_CALL(OS_Func::_fwrite)(&DAT_WindowAndDirectDraw::instance.currentGameResolution,
sizeof(DAT_WindowAndDirectDraw::instance.currentGameResolution), 1, _File);

MACRO_CALL(OS_Func::_fclose)(_File);
}

}
}
5 changes: 1 addition & 4 deletions src/OpenSHC/Synchrony/GameSynchronyState.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,7 @@ namespace Synchrony {
int unknownPlayerInfoArray_01[9]; // 0x001092D0 length: 36
int DAT_ReceivedAIVFileAvailabilityPerAIArray[9][20]; // 0x001092F4 length: 720
SkirmishStatistics finalResults; // 0x001095C4 length: 1912
char ipRelatedArray[20]; // 0x00109D3C length: 20
char ipRelatedArray2[20]; // 0x00109D50 length: 20
char ipRelatedArray3[20]; // 0x00109D64 length: 20
char ipRelatedArray4[20]; // 0x00109D78 length: 20
char ipRelatedArray[4][20]; // 0x00109D3C length: 80
int ipArrayIndex; // 0x00109D8C length: 4
undefined4 DAT_TwoIfNotHost; // 0x00109D90 length: 4
undefined4 DAT_MapFileReceivingState; // 0x00109D94 length: 4
Expand Down
2 changes: 1 addition & 1 deletion src/OpenSHC/Text/UserTextHandler.func.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace Text {
char* (UserTextHandler::*)(), false, Address::SHC_3BB0A8C1_0x004697C0, &UserTextHandler::getCurrentText)
getCurrentText;

MACRO_FUNCTION_RESOLVER(int (UserTextHandler::*)(int), false, Address::SHC_3BB0A8C1_0x004697E0,
MACRO_FUNCTION_RESOLVER(char* (UserTextHandler::*)(int), false, Address::SHC_3BB0A8C1_0x004697E0,
&UserTextHandler::getTextArrayPointer)
getTextArrayPointer;

Expand Down
2 changes: 1 addition & 1 deletion src/OpenSHC/Text/UserTextHandler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ namespace Text {

char* getCurrentText();

int getTextArrayPointer(int param_1);
char* getTextArrayPointer(int param_1);

void setTextEntryAndUpdateCursor(undefined4 param_1, undefined4 param_2);

Expand Down
4 changes: 2 additions & 2 deletions status/addresses-SHC-3BB0A8C1.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24229,9 +24229,9 @@ SHC_3BB0A8C1_0x00495A20 | 0.0% | Pending

SHC_3BB0A8C1_0x00495A24 | 0.0% | Pending

SHC_3BB0A8C1_0x00495A30 | 0.0% | Pending
SHC_3BB0A8C1_0x00495A30 | 100.0% | Reimplemented

SHC_3BB0A8C1_0x00496400 | 0.0% | Pending
SHC_3BB0A8C1_0x00496400 | 100.0% | Reimplemented

SHC_3BB0A8C1_0x004968A0 | 0.0% | Pending

Expand Down