/*================================================================= File created by Yohann NICOLAS. Add support 1.13d by L'Autour. Add support 1.14d by haxifix. Gestion of the infinity and shared Stash. =================================================================*/ #include "infinityStash.h" #include "updateClient.h" #include "interface_Stash.h" #include "common.h" #define STASH_TAG 0x5453 //"ST" #define JM_TAG 0x4D4A //"JM" DWORD maxSelfPages = -1; DWORD maxSharedPages = -1; DWORD nbPagesPerIndex = 10; DWORD nbPagesPerIndex2 = 100; bool active_multiPageStash = false; bool active_sharedStash = false; bool separateHardSoftStash = false; bool active_sharedGold=false; char* sharedStashFilename = NULL; typedef int (*TchangeToSelectedStash)(Unit* ptChar, Stash* newStash, DWORD bOnlyItems, DWORD bIsClient); Unit* firstClassicStashItem(Unit* ptChar) { Unit* ptItem = D2InventoryGetFirstItem(PCInventory); while (ptItem) { if (D2ItemGetPage(D2GetRealItem(ptItem)) == 4) return ptItem; ptItem = D2UnitGetNextItem(ptItem); } return NULL; } DWORD endStashList(Unit* ptChar, Stash* ptStash)//WORKS { Stash* stash = ptStash; while (stash) { if (stash->ptListItem || ((stash == PCPY->currentStash) && firstClassicStashItem(ptChar))) return 0; if (stash->isIndex || (stash->name != NULL && stash->name[0])) return 0; stash = stash->nextStash; } return 1; } //bool isInList(Stash* list, Stash* pStash) //{ // Stash* ptStash = list; // while(ptStash) // { // if (ptStash->id == pStash->id) return true; // ptStash = ptStash->nextStash; // } // return false; //} Stash* getLastStash(Stash* ptStash)//WORKS { Stash* stash = ptStash; if (!stash) return NULL; while (stash->nextStash) stash = stash->nextStash; return stash; } Stash* newStash(DWORD id) { d2_assert( id == 0xFFFFFFFF , "Too much stash", __FILE__, __LINE__); Stash* stash = (Stash*)malloc(sizeof(Stash));//D2AllocMem(memPool, sizeof(Stash),__FILE__,__LINE__,0); d2_assert(!stash , "Error on stash allocation.", __FILE__, __LINE__); ZeroMemory(stash, sizeof(Stash)); stash->id = id; return stash; } Stash* addStash(Unit* ptChar, bool isShared) { Stash* previous; Stash* stash; // DWORD memPool = PCGame ? PCGame->memoryPool :0; if (isShared) { previous = getLastStash(PCPY->sharedStash); stash = newStash(PCPY->nbSharedPages++); } else { previous = getLastStash(PCPY->selfStash); stash = newStash(PCPY->nbSelfPages++); } stash->isShared = isShared; stash->previousStash = previous; if (previous) previous->nextStash = stash; else if (isShared) PCPY->sharedStash = stash; else PCPY->selfStash = stash; log_msg("AddStash: stash->id=%d\tstash->isShared=%d\tstash->previous=%08X\tnbSelf=%d\tnbShared=%d\n", stash->id,stash->isShared,stash->previousStash,PCPY->nbSelfPages,PCPY->nbSharedPages); return stash; } Stash* getStash(Unit* ptChar, DWORD isShared, DWORD id)//WORKS { Stash* ptStash = isShared ? PCPY->sharedStash : PCPY->selfStash; while(ptStash) { if (ptStash->id == id) return ptStash; ptStash = ptStash->nextStash; } return NULL; } int changeToSelectedStash_9(Unit* ptChar, Stash* newStash, DWORD bOnlyItems, DWORD bIsClient) { if (!newStash) return 0; log_msg("changeToSelectedStash ID:%d\tshared:%d\tonlyItems:%d\tclient:%d\n", newStash->id, newStash->isShared, bOnlyItems, bIsClient); Stash* currentStash = PCPY->currentStash; if (currentStash == newStash) return 0; d2_assert( currentStash && currentStash->ptListItem, "ERROR : currentStash isn't empty (ptListItem != NULL)",__FILE__,__LINE__); // collect items to remove Inventory* ptInventory = PCInventory; Unit* ptNextItem; Unit* ptPrevItem = NULL; Unit* ptItem = D2InventoryGetFirstItem(ptInventory); while (ptItem) { ptNextItem = D2UnitGetNextItem(ptItem); if (D2ItemGetPage(D2GetRealItem(ptItem)) == 4) { D2SetAnim(D2GetRealItem(ptItem),-1); if (ptPrevItem) { ptPrevItem->CurrentAnim = (DWORD)ptNextItem;//is ptPrevItem->nextNode = ptNextItem; } else { ptInventory->currentUsedSocket = (DWORD)ptNextItem;//is ptInventory->ptListItem = ptNextItem; } if (!ptNextItem) ptInventory->Inventory2C = (DWORD)ptPrevItem; ptInventory->Inventory30 = ptInventory->Inventory30 - 1; D2Common10250(__FILE__,__LINE__,ptInventory, D2GetPosX(D2GetRealItem(ptItem)), D2GetPosY(D2GetRealItem(ptItem)), 0xC, bIsClient, 4); // ptItem = D2InvRemoveItem(PCInventory, D2GetRealItem(ptItem)); // D2Common10250(__FILE__,__LINE__,PCInventory, D2GetPosX(ptItem), D2GetPosY(ptItem), 0xC, bIsClient, 4); if (currentStash) { // ptItem = setNextItem(ptItem, PCPY->currentStash->ptListItem); ptItem->CurrentAnim = (DWORD)currentStash->ptListItem;//is ptItem->nextNode = ptListItem currentStash->ptListItem = ptItem; }; } else ptPrevItem = ptItem; ptItem = ptNextItem; } // add items of new stash ptItem = newStash->ptListItem; while (ptItem) { D2InvAddItem(PCInventory, D2GetRealItem(ptItem), D2GetPosX(D2GetRealItem(ptItem)), D2GetPosY(D2GetRealItem(ptItem)), 0xC, bIsClient, 4); D2Common10242(PCInventory, D2GetRealItem(ptItem), 1); ptItem = D2UnitGetNextItem(ptItem); } if (bOnlyItems) newStash->ptListItem = PCPY->currentStash->ptListItem; else PCPY->currentStash = newStash; PCPY->currentStash->ptListItem = NULL; return 1; } int changeToSelectedStash_10(Unit* ptChar, Stash* newStash, DWORD bOnlyItems, DWORD bIsClient) { if (!newStash) return 0; log_msg("changeToSelectedStash ID:%d\tshared:%d\tonlyItems:%d\tclient:%d\n",newStash->id,newStash->isShared, bOnlyItems,bIsClient); Stash* currentStash = PCPY->currentStash; if (currentStash == newStash) return 0; d2_assert( currentStash && currentStash->ptListItem, "ERROR : currentStash isn't empty (ptListItem != NULL)",__FILE__,__LINE__); // Remove items from current page Unit* ptNextItem; Unit* ptItem = D2InventoryGetFirstItem(PCInventory); while (ptItem) { ptNextItem = D2UnitGetNextItem(ptItem); if (D2ItemGetPage(ptItem) == 4) { BYTE tmp = ptItem->ptItemData->ItemData2; ptItem = D2InvRemoveItem(PCInventory, ptItem); ptItem->ptItemData->ItemData2 = tmp; if (currentStash) { ptItem->ptItemData->ptNextItem = currentStash->ptListItem; currentStash->ptListItem = ptItem; } } ptItem = ptNextItem; } // add items of new stash ptItem = newStash->ptListItem; while (ptItem) { D2InvAddItem(PCInventory, ptItem, ptItem->path->x, ptItem->path->y, 0xC, bIsClient, 4); ptItem = D2UnitGetNextItem(ptItem); } if (bOnlyItems) newStash->ptListItem = PCPY->currentStash->ptListItem; else PCPY->currentStash = newStash; PCPY->currentStash->ptListItem = NULL; return 1; } TchangeToSelectedStash changeToSelectedStash; DWORD loadStash(Unit* ptChar, Stash* ptStash, BYTE data[], DWORD startSize, DWORD maxSize, DWORD* retSize) { DWORD curSize = startSize; DWORD nbBytesRead; log_msg("loadStash\n"); *retSize = curSize; if( *(WORD *)&data[curSize] != STASH_TAG )//"ST" { log_msg("loadStash -> Bad tag of stash of character %s : %04X\n",PCPlayerData->name,*(WORD *)&data[curSize]); return 0x7;//Unable to enter game. Bad inventory data } curSize += 2; // Read flags. int len = strlen((char*)&data[curSize]); if (*(WORD*)&data[curSize + len + 1] != JM_TAG) { ptStash->flags = *(DWORD *)&data[curSize]; curSize += 4; } // Read Name // if (strlen((char *)&data[curSize]) > 0xF) // *(char *)&data[curSize+0xF] = NULL; if (strlen((char *)&data[curSize])) ptStash->name = (char*)malloc(strlen((char *)&data[curSize]) + 1);//D2AllocMem(PCGame->memoryPool, strlen((char *)&data[curSize]),__FILE__,__LINE__,0); if (ptStash->name) strcpy(ptStash->name, (char *)&data[curSize]); curSize += strlen((char *)&data[curSize]) + 1; // Read inventory. DWORD ret = D2LoadInventory(PCGame, ptChar, (saveBitField*)&data[curSize], 0x60, maxSize-curSize, 0, &nbBytesRead); if (ret) log_msg("loadStash -> D2LoadInventory failed\n"); log_msg("Stash loaded (%d : %s)\n", ptStash->id ,ptStash->name); *retSize=curSize + nbBytesRead; return ret; } DWORD loadStashList(Unit* ptChar, BYTE* data, DWORD maxSize, DWORD* curSize, bool isShared)//WORKS { DWORD curStash = 0; Stash* newStash; DWORD nbStash = *(DWORD*)&data[*curSize]; *curSize += 4; log_msg("loadStashList\n\nnbStash = %d\n\n", nbStash); while (curStash < nbStash) { newStash = addStash(ptChar, isShared); changeToSelectedStash(ptChar, newStash, 0, false); DWORD ret = loadStash(ptChar, newStash, data, *curSize, 10000000, curSize); if (ret) return ret; curStash++; } if (!isShared && !PCPY->selfStash) { newStash = addStash(ptChar, isShared); PCPY->currentStash = newStash; } if (isShared && !PCPY->sharedStash) { newStash = addStash(ptChar, isShared); if (!PCPY->currentStash) PCPY->currentStash = newStash; } return 0; } //ADDDATA(DWORD, curSize, 0); #define DATA (*data + *curSize) #define ADDDATA(T) (T*)DATA; *curSize += sizeof(T) #define SETDATA(T,V) *(T*)DATA = V; *curSize += sizeof(T) void saveStash(Unit* ptChar, Stash* ptStash, BYTE** data, DWORD* maxSize, DWORD* curSize) { //write "ST" SETDATA(WORD, STASH_TAG); //Write flags. SETDATA(DWORD, ptStash->flags); //save name if (ptStash->name) { int size = strlen(ptStash->name); if (size > 15) size = 15; strncpy((char*)DATA, ptStash->name, size); *curSize += size; } SETDATA(char, NULL); //Write "JM" of inventory SETDATA(WORD, JM_TAG); //Get position of counter of items in inventory WORD* ptNBItem = ADDDATA(WORD); *ptNBItem = 0; //Get first item Unit* ptItem; if ((ptStash->id == PCPY->currentStash->id) && (ptStash->isShared == PCPY->currentStash->isShared)) ptItem = D2InventoryGetFirstItem(PCInventory); else ptItem = ptStash->ptListItem; //Write all items while (ptItem) { if (D2ItemGetPage(D2GetRealItem(ptItem)) == 4) { int nbBytes = D2SaveItem(D2GetRealItem(ptItem), (saveBitField*)DATA, *maxSize - *curSize, 1, 1, 0); d2_assert(!nbBytes, "!\"Character has too many items\"", __FILE__, __LINE__ ); *curSize += nbBytes; (*ptNBItem)++; } ptItem = D2UnitGetNextItem(ptItem); } } void saveStashList(Unit* ptChar, Stash* ptStash, BYTE** data, DWORD* maxSize, DWORD* curSize) { DWORD curSizeNbStash = *curSize; *curSize += sizeof(DWORD); DWORD nbStash=0; while(ptStash) { if (*curSize + 0x2000 > *maxSize) { BYTE* oldData = *data; *maxSize *= 2; *data = (BYTE *)D2AllocMem(PCGame->memoryPool, *maxSize,__FILE__,__LINE__,0); d2_assert(!*data, "Error : Memory allocation", __FILE__, __LINE__); CopyMemory(*data, oldData, *curSize); D2FreeMem(PCGame->memoryPool, oldData,__FILE__,__LINE__,0); } saveStash(ptChar, ptStash, data, maxSize, curSize); nbStash++; ptStash = endStashList(ptChar, ptStash->nextStash) ? NULL : ptStash->nextStash; } *(DWORD*)(*data + curSizeNbStash) = nbStash; } /////// client void updateSelectedStashClient(Unit* ptChar)//WORKS { Stash* newStash = PCPY->currentStash; if (!newStash) return; updateClient(ptChar, UC_SELECT_STASH, newStash->id, newStash->flags, PCPY->flags); updateClient(ptChar, UC_PAGE_NAME, newStash->name); } void setSelectedStashClient(DWORD stashId, DWORD stashFlags, DWORD flags, bool bOnlyItems)//WORKS { log_msg("setSelectedStashClient ID:%d, stashFlags:%d, flags:%08X\n", stashId, stashFlags, flags); Unit* ptChar = D2GetClientPlayer(); Stash* newStash = getStash(ptChar, (stashFlags & 1) == 1, stashId); if (!newStash) do newStash = addStash(ptChar, (stashFlags & 1) == 1); while (newStash->id < stashId); newStash->flags = stashFlags; changeToSelectedStash(ptChar, newStash, bOnlyItems, 1); PCPY->flags = flags; } void selectStash(Unit* ptChar, Stash* newStash) { if (!newStash) return; changeToSelectedStash(ptChar, newStash, 0, 0); updateSelectedStashClient(ptChar); } ///// public functions void toggleToSelfStash(Unit* ptChar) { Stash* selStash = PCPY->selfStash; if (selStash && (selStash != PCPY->currentStash)) { PCPY->showSharedStash = false; selectStash(ptChar, selStash); } } void toggleToSharedStash(Unit* ptChar) { Stash* selStash = PCPY->sharedStash; if (selStash && (selStash != PCPY->currentStash)) { PCPY->showSharedStash = true; selectStash(ptChar, selStash); } } void swapStash(Unit* ptChar, Stash* curStash, Stash* swpStash) { if (!ptChar || !curStash || !swpStash || curStash == swpStash) return; changeToSelectedStash(ptChar, swpStash, 1, 0); updateClient(ptChar, UC_SELECT_STASH, swpStash->id, swpStash->flags | 8, PCPY->flags); } void toggleStash(Unit* ptChar, DWORD page) { log_msg("toggle stash page = %u\n", page); Stash* curStash = PCPY->currentStash; Stash* swpStash = curStash->isShared ? PCPY->selfStash : PCPY->sharedStash; swapStash(ptChar, curStash, swpStash); } void swapStash(Unit* ptChar, DWORD page, bool toggle) { log_msg("swap stash page = %u\n", page); Stash* curStash = PCPY->currentStash; Stash* swpStash = curStash->isShared == toggle ? PCPY->selfStash : PCPY->sharedStash; for (DWORD i = 0; i < page; i++) { if (curStash->nextStash == NULL) addStash(ptChar, swpStash->isShared); swpStash = swpStash->nextStash; } swapStash(ptChar, curStash, swpStash); } void insertStash(Unit* ptChar) { Stash* curStash = PCPY->currentStash; Stash* stash = addStash(ptChar, curStash->isShared); while (stash->previousStash != curStash) { stash->flags = stash->previousStash->flags; stash->name = stash->previousStash->name; stash->ptListItem = stash->previousStash->ptListItem; stash = stash->previousStash; } stash->isIndex = 0; stash->isMainIndex = 0; stash->name = NULL; stash->ptListItem = NULL; } bool deleteStash(Unit* ptChar, bool isClient) { if (firstClassicStashItem(ptChar) != NULL) return false; //if (D2InventoryGetFirstItem()) Stash* stash = PCPY->currentStash; if (stash->nextStash == NULL) { stash->isIndex = 0; stash->isMainIndex = 0; stash->name = NULL; return true; } stash->flags = stash->nextStash->flags; stash->name = stash->nextStash->name; if (stash->nextStash->ptListItem != NULL) changeToSelectedStash(ptChar, stash->nextStash, 1, isClient); stash = stash->nextStash; while (stash->nextStash) { stash->flags = stash->nextStash->flags; stash->name = stash->nextStash->name; stash->ptListItem = stash->nextStash->ptListItem; stash = stash->nextStash; } stash->isIndex = 0; stash->isMainIndex = 0; stash->name = NULL; stash->ptListItem = NULL; return true; } void renameCurrentStash(Unit* ptChar, char* name) { log_msg("renameCurrentStash : %08X, %s\n", ptChar, name); Stash* stash = PCPY->currentStash; int len = 0; if (name != NULL) len = strlen(name); log_msg("renameCurrentStash : %d\n", len); if (stash->name) D2FogMemDeAlloc(stash->name, __FILE__, __LINE__, 0); log_msg("renameCurrentStash 3\n"); if (len > 0) { stash->name = (char *)malloc(len + 1);//D2FogMemAlloc(len,__FILE__,__LINE__,0); strcpy(stash->name, name); } else stash->name = NULL; log_msg("renameCurrentStash 4\n"); } void setCurrentStashIndex(Unit* ptChar, int index) { if (!PCPY->currentStash) return; PCPY->currentStash->isIndex = index >= 1; PCPY->currentStash->isMainIndex = index == 2; updateSelectedStashClient(ptChar); } void selectPreviousStash(Unit* ptChar) { Stash* selStash = PCPY->currentStash->previousStash; if (selStash && (selStash != PCPY->currentStash)) selectStash(ptChar, selStash); } void selectPrevious2Stash(Unit* ptChar)// Select first stash { Stash* selStash = PCPY->showSharedStash ? PCPY->sharedStash : PCPY->selfStash; if (selStash && (selStash != PCPY->currentStash)) selectStash(ptChar, selStash); } void selectNextStash(Unit* ptChar) { Stash* selStash = PCPY->currentStash; if (!selStash->isShared && (selStash->id >= maxSelfPages)) return; if (selStash->isShared && (selStash->id >= maxSharedPages)) return; selStash = selStash->nextStash ? selStash->nextStash : addStash(ptChar, PCPY->showSharedStash); if (selStash && (selStash != PCPY->currentStash)) selectStash(ptChar, selStash); } void selectNext2Stash(Unit* ptChar)//select last stash { Stash* selStash = PCPY->showSharedStash ? PCPY->sharedStash : PCPY->selfStash;//PCPY->currentStash; Stash* lastStash = NULL; Unit* currentStashItem = firstClassicStashItem(ptChar); while (selStash) { if (selStash->ptListItem || (selStash == PCPY->currentStash) && currentStashItem) lastStash=selStash; selStash = selStash->nextStash; } if (!lastStash) lastStash = PCPY->showSharedStash ? PCPY->sharedStash : PCPY->selfStash; if (lastStash != PCPY->currentStash) selectStash(ptChar, lastStash); } void selectPreviousIndexStash(Unit* ptChar) { selectPreviousStash(ptChar); Stash* selStash = PCPY->currentStash; while (selStash && !selStash->isIndex) selStash = selStash->previousStash; if (selStash == NULL) { selStash = PCPY->currentStash; while (selStash->previousStash && ((selStash->id + 1) % nbPagesPerIndex != 0)) selStash = selStash->previousStash; } if (selStash && (selStash != PCPY->currentStash)) selectStash(ptChar, selStash); } void selectPreviousIndex2Stash(Unit* ptChar) { selectPreviousStash(ptChar); Stash* selStash = PCPY->currentStash; while (selStash && !selStash->isMainIndex) selStash = selStash->previousStash; if (selStash == NULL) { selStash = PCPY->currentStash; while (selStash->previousStash && ((selStash->id+1) % nbPagesPerIndex2 != 0)) selStash = selStash->previousStash; } if (selStash && (selStash != PCPY->currentStash)) selectStash(ptChar, selStash); } void selectNextIndexStash(Unit* ptChar) { selectNextStash(ptChar); Stash* selStash = PCPY->currentStash; while (selStash && !selStash->isIndex) selStash = selStash->nextStash; if (selStash == NULL) { selStash = PCPY->currentStash; while ((selStash->id + 1) % nbPagesPerIndex != 0) { if (!selStash->isShared && (selStash->id >= maxSelfPages)) break; if (selStash->isShared && (selStash->id >= maxSharedPages)) break; selStash = selStash->nextStash ? selStash->nextStash : addStash(ptChar, PCPY->showSharedStash);; } } if (selStash && (selStash != PCPY->currentStash)) selectStash(ptChar, selStash); } void selectNextIndex2Stash(Unit* ptChar) { selectNextStash(ptChar); Stash* selStash = PCPY->currentStash; while (selStash && !selStash->isMainIndex) selStash = selStash->nextStash; if (selStash == NULL) { selStash = PCPY->currentStash; while ((selStash->id+1) % nbPagesPerIndex2 != 0) { if (!selStash->isShared && (selStash->id >= maxSelfPages)) break; if (selStash->isShared && (selStash->id >= maxSharedPages)) break; selStash = selStash->nextStash ? selStash->nextStash : addStash(ptChar, PCPY->showSharedStash);; } } if (selStash && (selStash != PCPY->currentStash)) selectStash(ptChar, selStash); } ////////////////////////////////////////////////////////////////////// Stash* curStash2; DWORD currentSawStash2; Unit* STDCALL getNextItem(Unit* ptChar, Unit* ptItem) { Unit* item = D2UnitGetNextItem(ptItem); if (item) return item; if (!curStash2) { switch (currentSawStash2) { case 0: curStash2 = PCPY->selfStash; currentSawStash2 = 1; break; case 1: curStash2 = PCPY->sharedStash; currentSawStash2 = 2; break; default: return NULL;//case 2: } } else { curStash2 = curStash2->nextStash; } if (curStash2) { item = curStash2->ptListItem; if (item) return item; } return getNextItem(ptChar,item); } Unit* STDCALL initGetNextItem(Unit* ptChar, Unit* ptItem) { if (ptChar->nUnitType != UNIT_PLAYER) return NULL; if (!PCPY) return NULL; curStash2 = NULL; currentSawStash2 = 0; if (ptItem) return ptItem; return getNextItem(ptChar,ptItem); } FCT_ASM ( caller_initGetNextItem ) PUSH DWORD PTR SS:[ESP+0x20] PUSH DWORD PTR SS:[ESP+0xC] CALL initGetNextItem MOV EDI,EAX TEST EDI,EDI RETN }} FCT_ASM ( caller_getNextItem ) PUSH DWORD PTR SS:[ESP+4] PUSH DWORD PTR SS:[ESP+0x10] CALL getNextItem RETN 4 }} DWORD STDCALL carry1Limit(Unit* ptChar, Unit* ptItemParam, DWORD itemNum, BYTE page) { if (!ptChar) return 0; Unit* ptItem = ptItemParam ? ptItemParam : D2GameGetObject(PCGame, UNIT_ITEM, itemNum); if ((page != 4) && (D2GetItemQuality(ptItem) == 7) && ptChar) { int uniqueID = D2GetUniqueID(ptItem); if ((uniqueID>=0) && (uniqueID < (int)SgptDataTables->nbUniqueItems)) { UniqueItemsBIN* uniqueItems = SgptDataTables->uniqueItems + uniqueID; if (uniqueItems && (uniqueItems->carry1==1)) { ItemsBIN* ptItemsBin = D2GetItemsBIN(ptItem->nTxtFileNo); Unit* ptFirstItem = D2InventoryGetFirstItem(PCInventory); if (ptItemsBin && ptFirstItem) return D2VerifIfNotCarry1(ptItem, ptItemsBin, ptFirstItem); } } } return 0; } FCT_ASM( caller_carry1Limit_114 ) PUSH DWORD PTR SS : [ESP + 0x08]//page PUSH 0//EDI PUSH DWORD PTR SS : [ESP + 0x0C] PUSH ESI//ptChar CALL carry1Limit TEST EAX, EAX JNZ end_carry1Limit JMP D2ItemSetPage end_carry1Limit : ADD ESP, 0x10 XOR EAX, EAX POP EDI POP EBX POP ESI MOV ESP, EBP POP EBP RETN 8 }} FCT_ASM ( caller_carry1Limit_111 ) PUSH DWORD PTR SS:[ESP+0x08]//page PUSH 0//EDI PUSH DWORD PTR SS:[ESP+0x0C] PUSH ESI//ptChar CALL carry1Limit TEST EAX,EAX JNZ end_carry1Limit JMP D2ItemSetPage end_carry1Limit: ADD ESP,0xC XOR EAX,EAX POP EDI POP EBX POP ESI POP EBP RETN 8 }} FCT_ASM ( caller_carry1Limit ) PUSH DWORD PTR SS:[ESP+0x08]//page PUSH 0//EBP PUSH DWORD PTR SS:[ESP+0x0C] PUSH DWORD PTR SS:[ESP+0x28]//ptChar CALL carry1Limit TEST EAX,EAX JNZ end_carry1Limit JMP D2ItemSetPage end_carry1Limit: ADD ESP,0xC POP EDI POP ESI POP EBP XOR EAX,EAX POP EBX ADD ESP,0x24 RETN 8 }} FCT_ASM( caller_carry1LimitSwap_114 ) PUSH EAX PUSH DWORD PTR SS : [ESP + 0x20] PUSH 0 PUSH EDI//ptChar CALL carry1Limit TEST EAX, EAX JNZ end_carry1Limit JMP D2ItemGetPage end_carry1Limit : ADD ESP, 8 XOR EAX, EAX POP EBX POP EDI POP ESI MOV ESP, EBP POP EBP RETN 8 }} FCT_ASM ( caller_carry1LimitSwap_112 ) PUSH EAX PUSH DWORD PTR SS:[ESP+0x1C] PUSH 0 PUSH ESI//ptChar CALL carry1Limit TEST EAX,EAX JNZ end_carry1Limit JMP D2ItemGetPage end_carry1Limit: ADD ESP,8 XOR EAX,EAX POP EDI POP EBP POP ESI POP EBX POP ECX RETN 8 }} FCT_ASM ( caller_carry1LimitSwap_111 ) PUSH EAX PUSH EBP PUSH 0 PUSH DWORD PTR SS:[ESP+0x24]//ptChar CALL carry1Limit TEST EAX,EAX JNZ end_carry1Limit JMP D2ItemGetPage end_carry1Limit: ADD ESP,8 XOR EAX,EAX POP EDI POP EBP POP ESI POP EBX POP ECX RETN 8 }} FCT_ASM ( caller_carry1LimitSwap ) PUSH EAX PUSH DWORD PTR SS:[ESP+0x20] PUSH 0 PUSH EBP//ptChar CALL carry1Limit TEST EAX,EAX JNZ end_carry1Limit JMP D2ItemGetPage end_carry1Limit: ADD ESP,8 POP EDI POP ESI POP EBP XOR EAX,EAX POP EBX ADD ESP,0x4C RETN 8 }} /* FCT_ASM ( caller_carry1LimitWhenDrop ) PUSH EAX PUSH 0 PUSH ESI//ptItem PUSH EDI//ptChar CALL carry1Limit TEST EAX,EAX POP EAX JE END_carry1LimitWhenDrop MOV EDX,0x806 RETN END_carry1LimitWhenDrop: ADD DWORD PTR SS:[ESP],0x1F RETN }}*/ FCT_ASM( caller_carry1LimitWhenDrop_114 ) PUSH 0 PUSH 0 PUSH DWORD PTR SS : [ESP + 0x10] //ptItem PUSH EBX //ptChar CALL carry1Limit TEST EAX, EAX JNZ end_carry1Limit JMP D2CanPutItemInInv end_carry1Limit : XOR EAX, EAX RETN 0x1C }} FCT_ASM ( caller_carry1LimitWhenDrop_111 ) PUSH 0 PUSH 0 PUSH DWORD PTR SS:[ESP+0x10] //ptItem PUSH ESI //ptChar CALL carry1Limit TEST EAX,EAX JNZ end_carry1Limit JMP D2CanPutItemInInv end_carry1Limit: XOR EAX,EAX RETN 0x1C }} /* FCT_ASM ( caller_carry1LimitWhenDrop ) PUSH 0 PUSH 0 PUSH DWORD PTR SS:[ESP+0x10] //ptItem PUSH EDI //ptChar CALL carry1Limit JNZ end_carry1Limit JMP D2CanPutItemInInv end_carry1Limit: XOR EAX,EAX RETN 0x1C }}*/ FCT_ASM ( caller_carry1LimitWhenDrop ) PUSH EAX PUSH 0 PUSH 0 PUSH ESI//ptItem PUSH EDI//ptChar CALL carry1Limit TEST EAX,EAX POP EAX JNZ END_carry1LimitWhenDrop MOV EDX,0x806 RETN END_carry1LimitWhenDrop: ADD DWORD PTR SS:[ESP],0x1F RETN }} FCT_ASM( caller_carry1OutOfStash_114 ) PUSH ESI CALL D2ItemGetPage CMP AL, 4 JNZ continue_carry1OutOfStash SUB DWORD PTR SS : [ESP], 0xC RETN continue_carry1OutOfStash : MOV EDI, DWORD PTR SS : [EBP - 0x4] TEST EDI, EDI RETN }} FCT_ASM ( caller_carry1OutOfStash_111 ) PUSH EDI CALL D2ItemGetPage CMP AL,4 JNZ continue_carry1OutOfStash ADD DWORD PTR SS:[ESP],0x17C RETN continue_carry1OutOfStash: MOV ESI,DWORD PTR SS:[ESP+0x10] TEST ESI,ESI RETN }} FCT_ASM ( caller_carry1OutOfStash ) PUSH ESI CALL D2ItemGetPage CMP AL,4 JNZ continue_carry1OutOfStash ADD DWORD PTR SS:[ESP],0x1AF RETN continue_carry1OutOfStash: MOV EAX,DWORD PTR SS:[ESP+0x14] TEST EAX,EAX RETN }} void Install_MultiPageStash() { static int isInstalled = false; if (isInstalled) return; Install_PlayerCustomData(); Install_InterfaceStash(); changeToSelectedStash = version_D2Common >= V110 ? changeToSelectedStash_10 : changeToSelectedStash_9; if ( version_D2Game >= V110 ) { log_msg("Patch D2Game for carry1 unique item. (MultiPageStash)\n"); // Cannot put 2 items carry1 in inventory mem_seek R8(D2Game, 0000, 0000, 55050, 57CA3, 2FE63, 99B03, CF1E3, 6B013, 14AC7F); if (version_D2Game == V114d) { MEMT_REF4(0x000DD5FD, caller_carry1Limit_114); } else { MEMJ_REF4(D2ItemSetPage, version_D2Game >= V111 ? caller_carry1Limit_111 : caller_carry1Limit); } //6FC8504F . E8 94670900 CALL //01FD7CA2 . E8 6329FBFF CALL //01F9FE62 . E8 47A8FDFF CALL //6FCB9B02 . E8 9709F7FF CALL //6FCEF1E2 . E8 47B7F3FF CALL //6FC8B012 . E8 13F7F9FF CALL // Cannot put 2 items carry1 in inventory by swapping mem_seek R8(D2Game, 0000, 0000, 558D9, 58968, 310E8, 9B6E8, D10C8, 6BC78, 14B179); if (version_D2Game == V114d) { MEMT_REF4(0x000DD0D3, caller_carry1LimitSwap_114); } else { MEMJ_REF4(D2ItemGetPage, version_D2Game >= V112 ? caller_carry1LimitSwap_112 : version_D2Game >= V111 ? caller_carry1LimitSwap_111 : caller_carry1LimitSwap); } //6FC858D8 . E8 175F0900 CALL //01FD8967 . E8 8E1DFBFF CALL //01FA10E7 . E8 9A96FDFF CALL //6FCBB6E7 . E8 CAEDF6FF CALL //6FCF10C7 . E8 F895F3FF CALL //6FC8BC77 . E8 22E9F9FF CALL if ( version_D2Game >= V111 ) { // Cannot put 2 items carry1 in inventory when drop cube mem_seek R8(D2Game, 0000, 0000, 0000, 3D935, 49FD5, 17AD5, D7B75, B7B15, 163A89); if (version_D2Game == V114d) { MEMT_REF4(0x000D7EC3, caller_carry1LimitWhenDrop_114); } else { MEMJ_REF4(D2CanPutItemInInv, caller_carry1LimitWhenDrop_111); } //01FBD934 |. E8 5BD3FCFF |CALL //01FB9FD4 |. E8 3912FCFF |CALL //6FC37AD4 |. E8 0535FFFF |CALL //6FCF7B74 |. E8 232FF3FF |CALL //6FCD7B14 |. E8 7D32F5FF |CALL } else { // Cannot put 2 items carry1 in inventory when drop cube mem_seek R8(D2Game, 0000, 0000, 14341, 0000, 0000, 0000, 0000, 0000, 0000); memt_byte( 0xBA ,0xE8); MEMT_REF4( 0x00000806 , caller_carry1LimitWhenDrop); //6FC44341 |. BA 06080000 |MOV EDX,806 } // Verif only carry1 out of stash page when pick up an item mem_seek R8(D2Game, 0000, 0000, 1299E, 38E3B, 43F0B, 1209B, D211B, B301B, 15CADD); if (version_D2Game == V114d) { memt_byte(0x8B, 0xE8); MEMT_REF4(0xFF85FC7D, caller_carry1OutOfStash_114); } else { memt_byte(0x8B, 0xE8); MEMT_REF4(version_D2Game >= V111 ? 0x850C2474 : 0x85102444, version_D2Game >= V111 ? caller_carry1OutOfStash_111 : caller_carry1OutOfStash); memt_byte(version_D2Game >= V111 ? 0xF6 : 0xC0, 0x90); } //6FC4299E |. 8B4424 10 |MOV EAX,DWORD PTR SS:[ESP+10] //6FC429A2 |. 85C0 |TEST EAX,EAX //01FB8E3B |. 8B7424 0C |MOV ESI,DWORD PTR SS:[ESP+C] //01FB8E3F |. 85F6 |TEST ESI,ESI //01FB3F0B |. 8B7424 0C |MOV ESI,DWORD PTR SS:[ESP+C] //01FB3F0F |. 85F6 |TEST ESI,ESI //6FC3209B |. 8B7424 0C |MOV ESI,DWORD PTR SS:[ESP+C] //6FC3209F |. 85F6 |TEST ESI,ESI //6FCF211B |. 8B7424 0C |MOV ESI,DWORD PTR SS:[ESP+C] //6FCF211F |. 85F6 |TEST ESI,ESI //6FCD301B |. 8B7424 0C |MOV ESI,DWORD PTR SS:[ESP+C] //6FCD301F |. 85F6 |TEST ESI,ESI log_msg("\n"); } isInstalled = true; } /*================================= END OF FILE =================================*/