Conversation
dbc6c35 to
96be230
Compare
0b76486 to
e6f6ab7
Compare
4de8840 to
4dbcfb4
Compare
797be2d to
3c5ea81
Compare
f1f1f12 to
d33fac6
Compare
|
The cpp linter report is mistaken, there is nothing wrong with the function's formatting. |
99eca9a to
bf10e68
Compare
- move UI functions from the removed OpenSHC/UI.func.hpp into their new namespaces/folders (MenuModals, DisplayElements, Credits, Helpers, ...) - remap *_Func references to the resolvers' current namespaces - fix return types/fields that no longer matched the generated headers - drop duplicate TribesState/LandscapeState constructor sources Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Re-measured all 45 functions this branch added that were recorded below 100%, with the Globals struct resolvers active. 31 are at normalized 100% -- only call and tail-jump targets differ -- which the project records as 100% Reimplemented. Two of those were recorded as 0.0%; both are single-instruction tail-call thunks whose one instruction is the jump. The remaining 14 carry the reason the diff actually shows instead of the generic "jump target differs" / "instruction selection differs". Most are register allocation or instruction scheduling, which is not reachable from the source. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2a496be to
f462cf7
Compare
| @@ -68321,7 +68321,7 @@ SHC_3BB0A8C1_0x00584001 | 0.0% | Pending | |||
|
|
|||
| SHC_3BB0A8C1_0x0058401B | 0.0% | Pending | |||
|
|
|||
| SHC_3BB0A8C1_0x00584026 | 0.0% | Pending | |||
| SHC_3BB0A8C1_0x00584026 | 0.0% | instruction selection differs | |||
There was a problem hiding this comment.
If this is the entry function, we would kinda need another marker: These thing would only be relevant if we are going for the full/full match. I would consider this infrastructure.
There was a problem hiding this comment.
yes I think this is related in: #231
I think I will even just move entry out of the OpenSHC scope in Ghidra.
| SHC_3BB0A8C1_0x0053B530 | 0.0% | Pending | ||
| SHC_3BB0A8C1_0x0053B530 | 92.3% | global store scheduling | ||
|
|
||
| SHC_3BB0A8C1_0x0053B570 | 0.0% | Pending | ||
| SHC_3BB0A8C1_0x0053B570 | 90.0% | global store scheduling | ||
|
|
||
| SHC_3BB0A8C1_0x0053B5A0 | 0.0% | Pending | ||
|
|
||
| SHC_3BB0A8C1_0x0053B5E0 | 0.0% | Pending |
There was a problem hiding this comment.
Actually tested these out of curiosity: Just to be sure, these where not tested together with the parent function?
That would be the only idea I had left: A shared file an them being static helpers. Might have caused pressure issues, assuming it does not inline.
There was a problem hiding this comment.
I just tried by placing the calling function's body into the same cpp file ( and activating the reimplementation) but it didn't help.
There was a problem hiding this comment.
Had an epiphany on my way to work today: This works (test since it is easier for me to change the type this way):
// GLOBAL: STRONGHOLDCRUSADER 0x00EE0FC8
int test;
// FUNCTION: STRONGHOLDCRUSADER 0x0053B5E0
void Version::UpgradeMapUnitsTo_155()
{
// DAT_CurrentUnitSlotID::instance = 1;
for (test = 1; test < 2500; ++test) {
if (DAT_UnitsState::instance.units[test].logicalState != Units::ULS_INVISIBLE) {
DAT_UnitsState::instance.units[test].killedFlagUnk = 0;
}
}
}
This also works:
// GLOBAL: STRONGHOLDCRUSADER 0x00EE0FC8
int test;
// FUNCTION: STRONGHOLDCRUSADER 0x0053B5E0
void Version::UpgradeMapUnitsTo_155()
{
test = 1;
for (int i = 1; i < 2500; ++i) {
test += 1;
if (DAT_UnitsState::instance.units[i].logicalState != Units::ULS_INVISIBLE) {
DAT_UnitsState::instance.units[i].killedFlagUnk = 0;
}
}
}
The compiler knows it adds up to the number and makes it a simple set, since not volatile and no function call is inbetween.
There was a problem hiding this comment.
Ah clever! Great so the compiler makes the devs silly code smarter... (or less dumb). I was breaking my brain on why the 2500 assignment was somehow inside the for loop, but now it makes sense...
|
|
||
| SHC_3BB0A8C1_0x0053B340 | 0.0% | Pending | ||
| SHC_3BB0A8C1_0x0053B340 | 81.8% | store sched + jne vs jl |
There was a problem hiding this comment.
Might also be parent file pressure that could provoke be the change? Also, you do you average on 81, it gets with the unroll a good bit less?
There was a problem hiding this comment.
Yes 53% with the unroll. The != 2500 instead of the < 2500 was the good trick to prevent the unroll from happening.
There was a problem hiding this comment.
See my other comment, might be a similar reason.
There was a problem hiding this comment.
Yes! Just the jne vs jl left because of the unroll prevention trick...
| int GameStateStructures::getSalesPrice(int playerID, int resourceType) | ||
| { | ||
| return (this->mapAndTime.buyAndSalesPriceArray[resourceType].salesPrice / 5) | ||
| * MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::getSellResourceAmount, this)( |
There was a problem hiding this comment.
So this signature (the carry over EDX beyond a call, could also happen to ECX) means /GL.
0x45b7fb : -mov edx, ecx
0x45b7fd : -call <OFFSET1>
0x45b802 : -mov edx, dword ptr [edx + esi*8 + 0x51f20]
yes /GL leads to 100%
There was a problem hiding this comment.
Yes, I always need to look them up again, but EAX, EDX and ECX are scratch registers. If they are used after a function call in msvc-x86 without being reassigned first, it means:
- For EDX and ECX that the compiler knew the function body.
- For EAX that the function may have returned something... or maybe it knew the function body? Have no example for this.
Of course the compiler may also do other stuff when the body is known, but these are very clear signs.
| // FUNCTION: STRONGHOLDCRUSADER 0x004549C0 | ||
| BOOLEnum TextureRenderCore::checkIfGfxTgxStartsWithTransparentPixels(int gfxIndex) | ||
| { | ||
| return (((byte*)this->gmAndGfxImageDataBuffer)[this->loadedGfxArray[gfxIndex].offsetInBuffer + 8] |
There was a problem hiding this comment.
I assume there is some split magic going on if this does not match. Or types?
There was a problem hiding this comment.
No dice, I tried char * and:
BOOLEnum TextureRenderCore::checkIfGfxTgxStartsWithTransparentPixels(int gfxIndex)
{
GMFileHeaderColorpalette* gm = (GMFileHeaderColorpalette*)(&(
((char*)this->gmAndGfxImageDataBuffer)[this->loadedGfxArray[gfxIndex].offsetInBuffer]));
return (gm->unknown2 & OpenSHC::IO::Graphics::TT_TGX_PIXEL_HEADER)
== OpenSHC::IO::Graphics::TT_TRANSPARENT_PIXELS;
}
But stuck at 40%
There was a problem hiding this comment.
The magic is actually the return:
BOOLEnum TextureRenderCore::checkIfGfxTgxStartsWithTransparentPixels(int gfxIndex)
{
int byteIndex = this->loadedGfxArray[gfxIndex].offsetInBuffer + 8;
byte checkByte = ((byte*)this->gmAndGfxImageDataBuffer)[byteIndex];
if ((checkByte & OpenSHC::IO::Graphics::TT_TGX_PIXEL_HEADER)
== OpenSHC::IO::Graphics::TT_TRANSPARENT_PIXELS) {
return TRUE;
} else {
return FALSE;
}
}
They rarely use a direct condition result as return. So both variants are always something one could try.
There was a problem hiding this comment.
Awesome. Didn't expect this form though based on the assembly.
| this->entityArray[entityID].logicalState = 2; | ||
| this->entityArray[entityID].field72_0xa8 = 1; |
There was a problem hiding this comment.
Please check again, I think these are absolute accesses to this object.
They sometimes do this and it gets hidden if ECX is set in Ghidra.
There was a problem hiding this comment.
No, then fit sinks to 15%
There was a problem hiding this comment.
Yeah, I read Ghidra wrong, sorry.
As an apology, I found the mismatch reason: The signature is wrong. The function returns the entityID it receives again. 🙃
Notice that EAX is assigned after the call again before the return.
There was a problem hiding this comment.
hahahaha love it thanks
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
||
| SHC_3BB0A8C1_0x0053B340 | 0.0% | Pending | ||
| SHC_3BB0A8C1_0x0053B340 | 81.8% | store sched + jne vs jl |
There was a problem hiding this comment.
See my other comment, might be a similar reason.
| this->entityArray[entityID].logicalState = 2; | ||
| this->entityArray[entityID].field72_0xa8 = 1; |
There was a problem hiding this comment.
Yeah, I read Ghidra wrong, sorry.
As an apology, I found the mismatch reason: The signature is wrong. The function returns the entityID it receives again. 🙃
Notice that EAX is assigned after the call again before the return.
| // FUNCTION: STRONGHOLDCRUSADER 0x004549C0 | ||
| BOOLEnum TextureRenderCore::checkIfGfxTgxStartsWithTransparentPixels(int gfxIndex) | ||
| { | ||
| return (((byte*)this->gmAndGfxImageDataBuffer)[this->loadedGfxArray[gfxIndex].offsetInBuffer + 8] |
There was a problem hiding this comment.
The magic is actually the return:
BOOLEnum TextureRenderCore::checkIfGfxTgxStartsWithTransparentPixels(int gfxIndex)
{
int byteIndex = this->loadedGfxArray[gfxIndex].offsetInBuffer + 8;
byte checkByte = ((byte*)this->gmAndGfxImageDataBuffer)[byteIndex];
if ((checkByte & OpenSHC::IO::Graphics::TT_TGX_PIXEL_HEADER)
== OpenSHC::IO::Graphics::TT_TRANSPARENT_PIXELS) {
return TRUE;
} else {
return FALSE;
}
}
They rarely use a direct condition result as return. So both variants are always something one could try.
| int GameStateStructures::getSalesPrice(int playerID, int resourceType) | ||
| { | ||
| return (this->mapAndTime.buyAndSalesPriceArray[resourceType].salesPrice / 5) | ||
| * MACRO_CALL_MEMBER(OpenSHC::Game::GameStateStructures_Func::getSellResourceAmount, this)( |
There was a problem hiding this comment.
Yes, I always need to look them up again, but EAX, EDX and ECX are scratch registers. If they are used after a function call in msvc-x86 without being reassigned first, it means:
- For EDX and ECX that the compiler knew the function body.
- For EAX that the function may have returned something... or maybe it knew the function body? Have no example for this.
Of course the compiler may also do other stuff when the body is known, but these are very clear signs.
No description provided.