Absturz mit Stacktrace ASMINT_CallmyExternal | Quickbar
Hallo zusammen,
in meiner Mod ist ein Fehler aufgeschlagen, den ich jetzt reproduzieren konnte und welcher sehr merkwürdig ist, weswegen ich um Rat frage. So ganz, weiß ich auch nicht wohin damit. Deswegen ein neues Thema.
Jetzt zum Fehler: In der Atariar Edition nutze ich die Quickbar-Scripte von Siemekk, welche ich auch überarbeitet habe. Wenn der Spieler zwei gleiche Waffen im Inventar hat. Eine davon angelegt am Gürtel und er jetzt diese eine Waffe aus dem Inventar werfen möchte, dann stürzt das Spiel mit der nachfolgenden Stacktrace ab. Der selbe Fehler tritt auf, wenn der Spieler eine Waffe ausgerüstet hat und die selbe zusätzlich im Inventar ist. Jetzt wird er von einem NSC niedergeschlagen. Sobald dieser NSC seine eigene Waffe wegsteckt, stürzt das Spiel mit nachfolgender Stacktrace ab.
Siehe hier folgende zwei Videos: Diese zwei Handlungen führen zum Absturz (=VIDEO-Ende).
func void QS_WriteNOP(var int address, var int numBytes){ if(!IsHooked(address)) {
return;
};
MemoryProtectionOverride(address, numBytes);
repeat(i, numBytes); var int i;
MEM_WriteByte(address+i, ASMINT_OP_nop);
end;
};
QS_WriteNOP bei der QuickslotBar genutzt und einmal per Session aufgerufen / initialisiert
Code:
func void QS_InitHooks(){
// Fix hide weapon on using last spell
QS_WriteNOP(oCMag_Book_SpellCast_Check_SpellBook,
oCMag_Book_SpellCast_Check_SpellBook_NumBytes);
// Disable mag_book
MemoryProtectionOverride(7577148, 5);
MEM_WriteByte(7577148 + 0, 233);
MEM_WriteByte(7577148 + 1, 229);
MEM_WriteByte(7577148 + 2, 001);
MEM_WriteByte(7577148 + 3, 000);
MEM_WriteByte(7577148 + 4, 000);
HookEngineF(oCGame__RenderX,
oCGame__RenderX_Len,
QS_RenderHook);
HookEngineF(oCGame__UpdateResolution,
oCGame__UpdateResolution_Len,
QS_UpdateResolution);
HookEngineF(oCNpcInventory__HandleEvent,
oCNpcInventory__HandleEvent_Len,
QS_HandleEvent_Inventory);
HookEngineF(oCGame__HandleEvent,
oCGame__HandleEvent_Len,
QS_Game_HandleEvent);
HookEngineF(oCNpc__SetAsPlayer,
oCNpc__SetAsPlayer_Len,
QS_SwitchHeroFix);
HookEngineF(oCAIHuman__PC_WeaponMove,
oCAIHuman__PC_WeaponMove_Len,
QS_KeyWeapon_Hook);
HookEngineF(oCNpc__OpenInventory,
oCNpc__OpenInventory_Len,
QS_OpenInventory);
HookEngineF(oCMag_Book_SpellCast_Check_SpellBook,
oCMag_Book_SpellCast_Check_SpellBook_NumBytes,
QS_FixUseLastSpell);
HookEngineF(oCMag_Book_SpellCast,
oCMag_Book_SpellCast_Len,
QS_SpellCast_GetSpell);
// Draw quickslot numbers
HookEngineF(oCItemContainer_Draw_PrintText,
oCItemContainer_Draw_PrintText_Len,
QS_DrawNumbersInInv);
HookEngineF(oCItemContainer_Draw_NF_posX,
oCItemContainer_Draw_NF_posX_Len,
QS_RemoveInvNumber_NF);
HookEngineF(oCItemContainer_Draw_FF_posX,
oCItemContainer_Draw_FF_posX_Len,
QS_RemoveInvNumber_FF);
// Fix keyWeapon in using oCItemContainer
HookEngineF(6914805 /*006982F5*/, 8, Hook_ReturnFalse);
HookEngineF(6935581 /*0069D41D*/, 8, Hook_ReturnFalse);
};
func void QS_InitBaseData()
{
// Initialize zCView background object
if(!Hlp_IsValidHandle(QS_BackgroundView))
{
QS_BackgroundView = View_CreateCenter(4096, 8000 - Print_ToVirtual(QS_SizeY, PS_Y),
Print_ToVirtual(QS_SizeX, PS_X),
Print_ToVirtual(QS_SizeY, PS_Y));
View_SetTexture(QS_BackgroundView, QS_BackgroundTexture);
};
// Init Render world
if(!QS_RenderWorld)
{
// Create world
CALL__thiscall(MEM_ReadInt(zfactory), zCObjectFactory__CreateWorld);
QS_RenderWorld = CALL_RetValAsPtr();
// Apply world only for inventory
MEM_WriteInt (QS_RenderWorld + zCWorld__bIsInventoryWorld_offset, true);
};
};
func int QuickSlot_InitOnce()
{
// Check LeGo flags
if(!(_LeGo_Flags & (QS_LeGo_Flags)))
{
MEM_Error("Insufficient LeGo flags for QuickSlot.");
return FALSE;
};
QS_InitHooks ();
return TRUE;
};
func void QuickSlot_InitAlways()
{
// Init base quickslot data
QS_InitBaseData();
// Restore items pointers
QS_RestoreData();
// Remove last spellItem
QS_LastSpell_Item = 0;
// Remove 'keyWeapon' fix bug
QS_KeyWeapon_Locked = 0;
// On using last item, remove instance saved in this variable
QS_UseLastItem_Fix = 0;
};
func void QuickSlot_Init()
{
if (!Hlp_IsValidNpc(hero)) {
// Try again next frame
FF_ApplyOnceExt(QuickSlot_Init, 1, 1);
return;
};
// Init one time per session
if(!QuickSlot_Inited)
{
if(!QuickSlot_InitOnce())
{
MEM_Warn("Failed to initialize Quicklot");
return;
};
QuickSlot_Inited = 1;
};
QuickSlot_InitAlways();
MEM_Info("QuickSlot successfull inited!");
};
func void QuickSlot_OnLoadFirstWorld()
{
// Initalize in "Startup_YouFirstWorldName"
QS_LastWeaponSlot = -1;
};
Falls ihr eine Idee habt, wie ich den Fehler näher untersuchen könnte - gerne her damit! Für Ansätze und Vorschläge bin ich dankbar.
asmint_callmyexternal() (ideal)
|
asm_runonce() (ideal)
|
callint_makecall(var int adr, var int cleanstack) (ideal)
|
call__stdcall(var int adr) (ideal)
|
call__thiscall(var int this, var int adr) (ideal)
|
ocitem_render(var int itm, var int wld, var int view, var int rot) (ideal)
|
qs_renderslot(var int idx) (ideal)
|
mem_callbyptr(var int ptr) (ideal)
|
mem_callbyid(var int symbid) (ideal)
|
_hook (48023)
Spoiler:(zum lesen bitte Text markieren)
00BB97 pushv _hook._eax
00BB9C is
00BB9D pushv _hook._ecx
00BBA2 is
00BBA3 pushv _hook._edx
00BBA8 is
00BBA9 pushv _hook._ebx
00BBAE is
00BBAF pushv _hook._esp
00BBB4 is
00BBB5 pushv _hook._ebp
00BBBA is
00BBBB pushv _hook._esi
00BBC0 is
00BBC1 pushv _hook._edi
00BBC6 is
00BBC7 pushv _hook.evthaddr
00BBCC is
00BBCD call mem_getuseinstance
00BBD2 pushv _hook._instbak_temp
00BBD7 is
00BBD8 call locals
00BBDD pushv eax
00BBE2 pushv _hook.eaxbak
00BBE7 is
00BBE8 pushv ecx
00BBED pushv _hook.ecxbak
00BBF2 is
00BBF3 pushv edx
00BBF8 pushv _hook.edxbak
00BBFD is
00BBFE pushv ebx
00BC03 pushv _hook.ebxbak
00BC08 is
00BC09 pushv esp
00BC0E pushv _hook.espbak
00BC13 is
00BC14 pushv ebp
00BC19 pushv _hook.ebpbak
00BC1E is
00BC1F pushv esi
00BC24 pushv _hook.esibak
00BC29 is
00BC2A pushv edi
00BC2F pushv _hook.edibak
00BC34 is
00BC35 pushv _hook.insthlpaddr, 0
00BC3A not
00BC3B jmpf BC56
00BC40 pushv zcparsymbol_offset_offset, 28
00BC45 pushi 0
00BC4A call mem_getsymbolbyindex
00BC4F add
00BC50 pushv _hook.insthlpaddr, 0
00BC55 is
00BC56 pushi 1666
00BC5B call _@
00BC60 pushv _hook.selfbak
00BC65 is
00BC66 pushi 1667
00BC6B call _@
00BC70 pushv _hook.otherbak
00BC75 is
00BC76 pushi 1669
00BC7B call _@
00BC80 pushv _hook.itembak
00BC85 is
00BC86 pushv _hook.insthlpaddr, 0
00BC8B call mem_readint
00BC90 pushv _hook.ihlpbak
00BC95 is
00BC96 pushv _hook._instbak_temp
00BC9B pushv _hook.instbak
00BCA0 is
00BCA1 pushv _hook._eax
00BCA6 pushv eax
00BCAB is
00BCAC pushv _hook._ecx
00BCB1 pushv ecx
00BCB6 is
00BCB7 pushv _hook._edx
00BCBC pushv edx
00BCC1 is
00BCC2 pushv _hook._ebx
00BCC7 pushv ebx
00BCCC is
00BCCD pushv _hook._esp
00BCD2 pushv esp
00BCD7 is
00BCD8 pushv _hook._ebp
00BCDD pushv ebp
00BCE2 is
00BCE3 pushv _hook._esi
00BCE8 pushv esi
00BCED is
00BCEE pushv _hook._edi
00BCF3 pushv edi
00BCF8 is
00BCF9 pushi 6226
00BCFE call _@
00BD03 not
00BD04 jmpf BD13
00BD09 call mem_initlabels
00BD0E call mem_initglobalinst
00BD13 pushv false, 0
00BD18 pushv hookoverwriteinstances
00BD1D is
00BD1E pushv _hook.evthaddr
00BD23 call _^
00BD28 pushin _hook.a
00BD2D ains
00BD2E pushv _hook.i
00BD33 sinst _hook.a
00BD38 pushv zcarray.numinarray
00BD3D call repeat
00BD42 pushi 0
00BD47 sinst mem_parser
00BD4C pushv zcparser.datastack_sptr
00BD51 is
00BD52 sinst _hook.a
00BD57 pushv zcarray.array
00BD5C pushv _hook.i
00BD61 call mem_readintarray
00BD66 call mem_callbyid
00BD6B pushv hookoverwriteinstances
00BD70 not
00BD71 jmpf BDBC
00BD76 pushv true, 1
00BD7B pushv mem_assigninstsuppressnullwarning
00BD80 is
00BD81 pushv _hook.selfbak
00BD86 call _^
00BD8B pushin self
00BD90 ains
00BD91 pushv _hook.otherbak
00BD96 call _^
00BD9B pushin other
00BDA0 ains
00BDA1 pushv _hook.itembak
00BDA6 call _^
00BDAB pushin item
00BDB0 ains
00BDB1 pushv false, 0
00BDB6 pushv mem_assigninstsuppressnullwarning
00BDBB is
00BDBC pushv _hook.insthlpaddr, 0
00BDC1 pushv _hook.ihlpbak
00BDC6 call mem_writeint
00BDCB pushv _hook.instbak
00BDD0 call mem_setuseinstance
00BDD5 pushv _hook._esp
00BDDA pushv esp
00BDDF is
00BDE0 pushv _hook._ebp
00BDE5 pushv ebp
00BDEA is
00BDEB pushv end, -72
00BDF0 pushi 32
00BDF5 pushv esp
00BDFA sub
00BDFB pushv edi
00BE00 call mem_writeint
00BE05 pushi 28
00BE0A pushv esp
00BE0F sub
00BE10 pushv esi
00BE15 call mem_writeint
00BE1A pushi 16
00BE1F pushv esp
00BE24 sub
00BE25 pushv ebx
00BE2A call mem_writeint
00BE2F pushi 12
00BE34 pushv esp
00BE39 sub
00BE3A pushv edx
00BE3F call mem_writeint
00BE44 pushi 8
00BE49 pushv esp
00BE4E sub
00BE4F pushv ecx
00BE54 call mem_writeint
00BE59 pushi 4
00BE5E pushv esp
00BE63 sub
00BE64 pushv eax
00BE69 call mem_writeint
00BE6E pushv _hook.edibak
00BE73 pushv edi
00BE78 is
00BE79 pushv _hook.esibak
00BE7E pushv esi
00BE83 is
00BE84 pushv _hook.ebpbak
00BE89 pushv ebp
00BE8E is
00BE8F pushv _hook.espbak
00BE94 pushv esp
00BE99 is
00BE9A pushv _hook.ebxbak
00BE9F pushv ebx
00BEA4 is
00BEA5 pushv _hook.edxbak
00BEAA pushv edx
00BEAF is
00BEB0 pushv _hook.ecxbak
00BEB5 pushv ecx
00BEBA is
00BEBB pushv _hook.eaxbak
00BEC0 pushv eax
00BEC5 is
00BEC6 pushv _hook.instbak
00BECB call mem_setuseinstance
00BED0 retn
function _Hook
Spoiler:(zum lesen bitte Text markieren)
Code:
func void _Hook(var int evtHAddr, // ESP-36 var int _edi, // ESP-32 // Function parameters in order of popad (reverse order of pushad)
var int _esi, // ESP-28
var int _ebp, // ESP-24
var int _esp, // ESP-20
var int _ebx, // ESP-16
var int _edx, // ESP-12
var int _ecx, // ESP-8
var int _eax) { // ESP-4
// Backup use-instance before anything else. Temporary variable for now, because it's done before locals()
var int _instBak_temp; _instBak_temp = MEM_GetUseInstance();
// Local backup for recursive hooks
locals();
// Secure register variables locally for recursive hooks
var int eaxBak; eaxBak = EAX;
var int ecxBak; ecxBak = ECX;
var int edxBak; edxBak = EDX;
var int ebxBak; ebxBak = EBX;
var int espBak; espBak = ESP;
var int ebpBak; ebpBak = EBP;
var int esiBak; esiBak = ESI;
var int ediBak; ediBak = EDI;
// Get address of yINSTANCE_HELP by symbol index 0
const int instHlpAddr = 0;
if (!instHlpAddr) {
instHlpAddr = MEM_GetSymbolByIndex(0)+zCParSymbol_offset_offset;
};
// Also secure global instances
var int selfBak; selfBak = _@(self);
var int otherBak; otherBak = _@(other);
var int itemBak; itemBak = _@(item);
var int iHlpBak; iHlpBak = MEM_ReadInt(instHlpAddr);
var int instBak; instBak = _instBak_temp;
// Update register variables
EAX = _eax;
ECX = _ecx;
EDX = _edx;
EBX = _ebx;
ESP = _esp;
EBP = _ebp;
ESI = _esi;
EDI = _edi;
// Check whether Ikarus is initialized for hooks that happen during level change
if (!_@(MEM_Parser)) {
MEM_InitLabels();
MEM_InitGlobalInst();
};
// Do not overwrite the global instances by default
HookOverwriteInstances = FALSE;
// Iterate over all registered event handler functions
var zCArray a; a = _^(evtHAddr);
repeat(i, a.numInArray); var int i;
// Clear data stack in-between function calls
MEM_Parser.datastack_sptr = 0;
// Call the function
MEM_CallByID(MEM_ReadIntArray(a.array, i));
// Restore global instances in between function calls
if (!HookOverwriteInstances) {
MEM_AssignInstSuppressNullWarning = TRUE;
self = _^(selfBak);
other = _^(otherBak);
item = _^(itemBak);
MEM_AssignInstSuppressNullWarning = FALSE;
};
MEM_WriteInt(instHlpAddr, iHlpBak);
MEM_SetUseInstance(instBak);
// Stack registers should be kept read-only in between function calls
ESP = _esp; // Stack pointer is read-only
EBP = _ebp; // Base pointer is read-only
end;
// Update modifiable registers on stack (ESP points to the position before pushad)
MEM_WriteInt(ESP-32, EDI);
MEM_WriteInt(ESP-28, ESI);
MEM_WriteInt(ESP-16, EBX);
MEM_WriteInt(ESP-12, EDX);
MEM_WriteInt(ESP-8, ECX);
MEM_WriteInt(ESP-4, EAX);
// Restore register variables for recursive hooks
EDI = ediBak;
ESI = esiBak;
EBP = ebpBak;
ESP = espBak;
EBX = ebxBak;
EDX = edxBak;
ECX = ecxBak;
EAX = eaxBak;
// Just to be safe: restore again at the very end of the function
MEM_SetUseInstance(instBak);
};
Function MEM_CallByID
Spoiler:(zum lesen bitte Text markieren)
Code:
func void MEM_CallByID (var int symbID) {
if (symbID < 0) {
MEM_Error(ConcatStrings("MEM_CallByID: symbID may not be negative but is ", IntToString(symbID)));
return;
};
var zCPar_Symbol sym;
sym = _^(MEM_ReadIntArray (contentSymbolTableAddress, symbID));
var int type;
type = (sym.bitfield & zCPar_Symbol_bitfield_type);
if (type != zPAR_TYPE_FUNC) && (type != zPAR_TYPE_PROTOTYPE) && (type != zPAR_TYPE_INSTANCE) {
MEM_Error (ConcatStrings ("MEM_CallByID: Provided symbol is not callable (not function, prototype or instance): ", sym.name));
return;
};
if (sym.bitfield & zPAR_FLAG_EXTERNAL) {
CALL__stdcall(sym.content);
} else {
MEM_CallByPtr(sym.content + currParserStackAddress);
};
};
in the changed functions (unlike the original does not participate)
function MEM_CallByPtr
Spoiler:(zum lesen bitte Text markieren)
Code:
func void MEM_CallByPtr(var int ptr) {
MEM_StackPos.position = ptr;
};
func void QS_RenderSlot(var int idx){
if(!QS_RenderWorld) {
MEM_Error("QS_RenderWorld is null!");
return;
};
var int slotHndl; slotHndl = MEM_ReadStatArr(QS_Data, idx);
if(!Hlp_IsValidHandle(slotHndl)){
return;
};
var CSlot slot; slot = get (slotHndl);
var int pItem; pItem = slot.itemPtr;
var int pView; pView = slot.pView;
var int pViewText; pViewText = slot.pViewText;
if(!pItem
|| !Hlp_IsValidHandle(pView)
|| !Hlp_IsValidHandle(pViewText))
{
MEM_Error(ConcatStrings("RenderSlot pItem or pView or pViewText is null! idx: ", IntToString(idx)));
MEM_Info(ConcatStrings("pItem: ", IntToString(pItem)));
MEM_Info(ConcatStrings("pView: ", IntToString(pView)));
MEM_Info(ConcatStrings("pViewText: ", IntToString(pViewText)));
return;
};
var oCItem it; it = _^(pItem);
// Remove item
if(!Npc_HasItems(hero, it.instanz)) {
MEM_Info("QS_CheckSlot_REMOVE");
QS_RemoveSlot(idx);
return;
};
pView = getPtr(pView);
pViewText = getPtr(pViewText);
// Render slot
ViewPtr_InsertItem (getPtr(QS_BackgroundView), pView);
oCItem_Render (pItem, QS_RenderWorld, pView, FLOATNULL);
ViewPtr_Render (pView);
// Draw numbers
QS_AddText(pViewText, idx, it.amount, it.instanz);
ViewPtr_RemoveItem (getPtr(QS_BackgroundView), pView);
};
func oCItem_Render (G2)
Spoiler:(zum lesen bitte Text markieren)
Code:
func void oCItem_Render(var int itm, var int wld, var int view, var int rot) {
var zCView v; v = _^(view);
if(v.vposy < 0||(v.vposy+v.vsizey) > 8192) { return; };
if(v.vposy < 0||(v.vposy+v.vsizey) > 8192) { return; };
CALL_FloatParam(rot);
CALL_PtrParam(view);
CALL_PtrParam(wld);
CALL__thiscall(itm, oCItem__Render);
};
func CALL__thiscall
Spoiler:(zum lesen bitte Text markieren)
Code:
func void CALL__thiscall (var int this, var int adr) {
/* this -> ecx */
if (CALLINT_CodeMode == CALLINT_CodeMode_Recyclable) {
ASM_2(ASMINT_OP_movMemToECX);
} else {
ASM_1(ASMINT_OP_movImToECX);
};
ASM_4 (this);
CALL__stdcall (adr);
};
func void CALLINT_makecall (var int adr, var int cleanStack) {
if (CALLINT_RetValStructSize) {
CALL_IntParam (MEM_Alloc (CALLINT_RetValStructSize));
CALLINT_RetValStructSize = 0;
};
/* make the call: */
ASM_1 (ASMINT_OP_call);
ASM_4 (adr - ASM_Here() - 4); /* -4, because the jump is relative to the _next_ instruction. */
/* copy the result into a daedalus variable */
if (CALLINT_PutRetValTo != -1) {
if (!CALLINT_RetValIsFloat) {
ASM_2 (ASMINT_OP_movEAXToMem); /* mov CALLINT_Result eax */
} else {
ASM_2 (ASMINT_OP_floatStoreToMem); /* fstp CALLINT_Result */
};
if (CALLINT_PutRetValTo) {
ASM_4 (CALLINT_PutRetValTo);
} else {
ASM_4 (MEM_GetIntAddress(CALLINT_Result));
};
};
/* default: return value is not a float
* and has default location */
CALLINT_RetValIsFloat = false; //fьrs nдchste mal muss neugeschaltet werden.
CALLINT_PutRetValTo = 0;
/* __cdecl has to clean the stack here: */
if (cleanStack) {
ASM_2 (ASMINT_OP_addImToESP);
ASM_1 (CALLINT_numParams * 4);
};
/* reset Param Counter */
CALLINT_numParams = 0;
/* run the code that was build and discard it afterwards */
if (CALLINT_CodeMode != CALLINT_CodeMode_Recyclable) {
ASM_RunOnce();
};
};
func ASM_RunOnce
Spoiler:(zum lesen bitte Text markieren)
Code:
func void ASM_RunOnce() {
if (!ASMINT_currRun) {
MEM_Error ("ASM: ASM_Open has to be called before calling ASM_RunOnce.");
};
ASM (ASMINT_OP_retn, 1);
/* Save this code in an array of codes.
* Reason: On calling it another instance of this function may be
* executing his own code */
ASMINT_Push(ASMINT_currRun);
MEM_WriteInt(ASMINT_CallTarget, ASMINT_currRun);
ASMINT_currRun = 0; //more Code can be build while this one is running.
ASMINT_CallMyExternal();
/* Discard the code again */
MEM_Free(ASMINT_Pop()); //free the run
};
func ASMINT_CallMyExternal
Spoiler:(zum lesen bitte Text markieren)
Code:
func void ASMINT_CallMyExternal() { /* calls some external */
ExitGame(); /* will be changed so that it calls MyExternal */
};
Der Absturz entsteht, weil das Item, das in QS_RenderSlot gerendert werden soll an der Stelle scheinbar ungültig ist. Du kannst mal ausprobieren, ob es hilft folgende Abfrage (grün) in QS_RenderSlot hinzuzufügen.
Code:
func void QS_RenderSlot(var int idx){
// ...
if (!Hlp_Is_oCItem(pItem)) {
return;
};
var oCItem it; it = _^(pItem);
// ...
};
Der Absturz entsteht, weil das Item, das in QS_RenderSlot gerendert werden soll an der Stelle scheinbar ungültig ist. Du kannst mal ausprobieren, ob es hilft folgende Abfrage (grün) in QS_RenderSlot hinzuzufügen.
Code:
func void QS_RenderSlot(var int idx){
// ...
if (!Hlp_Is_oCItem(pItem)) {
return;
};
var oCItem it; it = _^(pItem);
// ...
};
Hallo mud-freak,
danke, das war es! Jetzt kann ich es nachvollziehen.
Der Fehler ist damit behoben.