├── .github └── workflows │ └── build-branch.yaml ├── .gitignore ├── Action.cpp ├── Action.h ├── Condition.cpp ├── Condition.h ├── Configuration.cpp ├── Configuration.h ├── D2Constants.h ├── D2Ptrs.h ├── D2Structs.h ├── D2Tables.h ├── Expression.cpp ├── Expression.h ├── Globals.cpp ├── Globals.h ├── Hooking.cpp ├── Hooking.h ├── ItemFilter.cpp ├── ItemFilter.h ├── LICENSE ├── README.md ├── Rule.cpp ├── Rule.h ├── Utils.cpp ├── Utils.h ├── d2lootfilter.sln ├── d2lootfilter.vcxproj ├── d2lootfilter.vcxproj.filters ├── d2lootfilter.vcxproj.user ├── dllmain.cpp ├── doc └── ENG │ ├── Class.md │ ├── Runes.md │ ├── Skills.md │ ├── Stats.md │ ├── Types.md │ └── item.filter └── mINI.h /.github/workflows/build-branch.yaml: -------------------------------------------------------------------------------- 1 | name: Build Branch 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | build: 11 | runs-on: windows-latest 12 | steps: 13 | - name: Setup MSBuild.exe 14 | uses: microsoft/setup-msbuild@v1.0.2 15 | - uses: actions/checkout@master 16 | - name: MSBuild 17 | run: msbuild /t:d2lootfilter:Rebuild /p:Configuration=Release /property:Platform=x86 d2lootfilter.sln 18 | - name: Release 19 | uses: marvinpinto/action-automatic-releases@v1.2.1 20 | with: 21 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 22 | automatic_release_tag: "latest" 23 | prerelease: true 24 | title: "Latest Development Build" 25 | files: | 26 | Release/d2lootfilter.dll 27 | doc/ENG/item.filter -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vcpkg_installed 2 | Release 3 | Debug 4 | .vs -------------------------------------------------------------------------------- /Action.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Action.h" 4 | #include "D2Structs.h" 5 | #include "D2Ptrs.h" 6 | #include "ItemFilter.h" 7 | 8 | typedef std::wstring(*TokenReplaceFunction)(ActionResult& action, Unit* pItem); 9 | 10 | #define COLOR(STR, IDX) { L"{"#STR"}", ##IDX## } 11 | static const utility::string_umap COLOR_TO_STRING = { 12 | COLOR(White, TEXT_WHITE), 13 | COLOR(Red, TEXT_RED), 14 | COLOR(Green, TEXT_GREEN), 15 | COLOR(Blue, TEXT_BLUE), 16 | COLOR(Gold, TEXT_GOLD), 17 | COLOR(Gray, TEXT_GRAY), 18 | COLOR(Grey, TEXT_GRAY), 19 | COLOR(Black, TEXT_BLACK), 20 | COLOR(Tan, TEXT_TAN), 21 | COLOR(Orange, TEXT_ORANGE), 22 | COLOR(Yellow, TEXT_YELLOW), 23 | COLOR(Purple, TEXT_PURPLE), 24 | COLOR(Dark Green, TEXT_DARK_GREEN), 25 | //Glide Only 26 | COLOR(Coral, TEXT_CORAL), 27 | COLOR(Sage, TEXT_SAGE), 28 | COLOR(Teal, TEXT_TEAL), 29 | COLOR(Light Gray, TEXT_LIGHT_GRAY), 30 | COLOR(Light Grey, TEXT_LIGHT_GRAY) 31 | }; 32 | #undef COLOR 33 | 34 | #define COLOR(STR, IDX) { L#STR, ##IDX## }, { L"\""#STR"\"", ##IDX## } 35 | static const utility::string_umap COLOR_TO_PALETTE_IDX = { 36 | COLOR(White, 0x20), 37 | COLOR(White, 0x20), 38 | COLOR(Red, 0x0A), 39 | COLOR(Green, 0x84), 40 | COLOR(Blue, 0x97), 41 | COLOR(Gold, 0x0D), 42 | COLOR(Gray, 0xD0), 43 | COLOR(Grey, 0xD0), 44 | COLOR(Black, 0x00), 45 | COLOR(Tan, 0x5A), 46 | COLOR(Orange, 0x60), 47 | COLOR(Yellow, 0x0C), 48 | COLOR(Purple, 0x9B), 49 | COLOR(Dark Green, 0x76), 50 | COLOR(Coral, 0x66), 51 | COLOR(Sage, 0x82), 52 | COLOR(Teal, 0xCB), 53 | COLOR(Light Gray, 0xD6), 54 | COLOR(Light Grey, 0xD6) 55 | }; 56 | #undef COLOR 57 | 58 | static std::wstring GetItemName(ActionResult& action, Unit* pItem) { 59 | if (action.bItemNameSet) { 60 | return action.wsItemName; 61 | } else { 62 | return L"{Name}"; 63 | } 64 | } 65 | 66 | static std::wstring GetItemDesc(ActionResult& action, Unit* pItem) { 67 | if (action.bItemDescSet) { 68 | return action.wsItemDesc; 69 | } else { 70 | return L"{Description}"; 71 | } 72 | } 73 | 74 | static std::wstring GetItemLevel(ActionResult& action, Unit* pItem) { 75 | return std::to_wstring(pItem->pItemData->dwItemLevel); 76 | } 77 | 78 | static std::wstring GetPotionNumber(ActionResult& action, Unit* pItem) { 79 | if(D2COMMON_ITEMS_CheckItemTypeId(pItem, ItemType::HEALING_POTION) 80 | || D2COMMON_ITEMS_CheckItemTypeId(pItem, ItemType::MANA_POTION)) { 81 | return GetItemCode(pItem).substr(2); 82 | } 83 | if (D2COMMON_ITEMS_CheckItemTypeId(pItem, ItemType::REJUV_POTION)) { 84 | std::wstring code = GetItemCode(pItem); 85 | return code == L"rvl" ? L"2" : L"1"; 86 | } 87 | if (D2COMMON_ITEMS_CheckItemTypeId(pItem, ItemType::POTION)) { 88 | return L"1"; 89 | } 90 | return L""; 91 | } 92 | 93 | static std::wstring GetRuneNumber(ActionResult& action, Unit* pItem) { 94 | if (D2COMMON_ITEMS_CheckItemTypeId(pItem, ItemType::RUNE)) { 95 | return std::to_wstring(std::stoi(GetItemCode(pItem).substr(1))); 96 | } 97 | return L""; 98 | } 99 | 100 | static std::wstring Newline(ActionResult& action, Unit* pItem) { 101 | return L"\n"; 102 | } 103 | 104 | static std::wstring GetItemPrice(ActionResult& action, Unit* pItem) { 105 | int nPrice = 0; 106 | Unit* pPlayer = D2CLIENT_GetPlayerUnit(); 107 | if (pItem != NULL && pPlayer != NULL) { 108 | nPrice = D2COMMON_ITEMS_GetTransactionCost(pPlayer, pItem, D2CLIENT_GetDifficulty(), D2CLIENT_GetQuestFlags(), 0x201, 1); 109 | } 110 | return std::to_wstring(nPrice); 111 | } 112 | 113 | static std::wstring GetItemSockets(ActionResult& action, Unit* pItem) { 114 | return std::to_wstring(D2COMMON_STATLIST_GetUnitStatUnsigned(pItem, Stat::ITEM_NUMSOCKETS, 0)); 115 | } 116 | 117 | 118 | ColorTextAction::ColorTextAction(std::wstring_view value, ActionType type) : Action(value, type) { 119 | for (auto const& color : COLOR_TO_STRING) { 120 | replace(m_Value, color.first, color.second); 121 | } 122 | } 123 | 124 | PaletteIndexAction::PaletteIndexAction(std::wstring_view value, ActionType type) : Action(value, type) { 125 | if (auto search = COLOR_TO_PALETTE_IDX.find(m_Value); search != COLOR_TO_PALETTE_IDX.end()) { 126 | m_PaletteIndex = search->second; 127 | } 128 | else { 129 | m_PaletteIndex = static_cast(std::stoi(m_Value, nullptr, 16)); 130 | } 131 | } 132 | 133 | 134 | void HideAction::SetResult(ActionResult& action, Unit* pItem) const { 135 | action.bHide = true; 136 | } 137 | 138 | void ShowAction::SetResult(ActionResult& action, Unit* pItem) const { 139 | action.bHide = false; 140 | } 141 | 142 | void ContinueAction::SetResult(ActionResult& action, Unit* pItem) const { 143 | action.bContinue = true; 144 | } 145 | 146 | void SetStyleAction::SetResult(ActionResult& action, Unit* pItem) const { 147 | if (auto search = GlobalStyles.find(m_Value); search != GlobalStyles.end()) { 148 | for (const auto& act : search->second) { 149 | act->SetResult(action, pItem); 150 | } 151 | } 152 | } 153 | 154 | void SetNameAction::SetResult(ActionResult& action, Unit* pItem) const { 155 | static const utility::string_umap TOKEN_REPLACEMENT_FUNCTIONS = { 156 | { L"{Name}", &GetItemName }, 157 | { L"{Sockets}", &GetItemSockets }, 158 | { L"{Price}", &GetItemPrice }, 159 | { L"{Item Level}", &GetItemLevel }, 160 | { L"{Rune Number}", &GetRuneNumber }, 161 | { L"{Potion Number}", &GetPotionNumber }, 162 | { L"{Newline}", &Newline } 163 | }; 164 | //we got here from a CONTINUE 165 | std::wstring result = m_Value; 166 | for (const auto& token : TOKEN_REPLACEMENT_FUNCTIONS) { 167 | replace(result, token.first, token.second(action, pItem)); 168 | } 169 | action.wsItemName = result; 170 | action.bItemNameSet = true; 171 | } 172 | 173 | void SetDescriptionAction::SetResult(ActionResult& action, Unit* pItem) const { 174 | static const utility::string_umap TOKEN_REPLACEMENT_FUNCTIONS = { 175 | { L"{Description}", &GetItemDesc }, 176 | { L"{Sockets}", &GetItemSockets }, 177 | { L"{Price}", &GetItemPrice }, 178 | { L"{Item Level}", &GetItemLevel }, 179 | { L"{Rune Number}", &GetRuneNumber }, 180 | { L"{Newline}", &Newline } 181 | }; 182 | //we got here from a CONTINUE 183 | std::wstring result = m_Value; 184 | for (const auto& token : TOKEN_REPLACEMENT_FUNCTIONS) { 185 | replace(result, token.first, token.second(action, pItem)); 186 | } 187 | action.wsItemDesc = result; 188 | action.bItemDescSet = true; 189 | } 190 | 191 | void SetBackgroundColorAction::SetResult(ActionResult& action, Unit* pItem) const { 192 | action.bBackgroundPaletteIndexSet = true; 193 | action.nBackgroundPaletteIndex = m_PaletteIndex; 194 | } 195 | 196 | void SetInventoryColorAction::SetResult(ActionResult& action, Unit* pItem) const { 197 | action.bInvBackgroundPaletteIndexSet = true; 198 | action.nInvBackgroundPaletteIndex = m_PaletteIndex; 199 | } 200 | 201 | void SetBorderColorAction::SetResult(ActionResult& action, Unit* pItem) const { 202 | action.bBorderPaletteIndexSet = true; 203 | action.nBorderPaletteIndex = m_PaletteIndex; 204 | } 205 | 206 | ChatNotifyAction::ChatNotifyAction(std::wstring_view value) : Action(value, ActionType::CHAT_NOTIFY) { 207 | m_Expression = Parser::Parse(m_Value.c_str()); 208 | } 209 | 210 | void ChatNotifyAction::SetResult(ActionResult& action, Unit* pItem) const { 211 | action.bChatAlert = m_Expression->Evaluate(pItem) != 0; 212 | } 213 | 214 | void PlayAlertAction::SetResult(ActionResult& action, Unit* pItem) const { 215 | } 216 | 217 | void MinimapIconAction::SetResult(ActionResult& action, Unit* pItem) const { 218 | action.bMinimapIcon = true; 219 | action.nMinimapIconPaletteIndex = m_PaletteIndex; 220 | } 221 | 222 | void WeightAction::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 223 | for (auto stat : CustomStats) { 224 | replace(m_Value, stat.first, stat.second); 225 | } 226 | m_Expression = Parser::Parse(m_Value.c_str()); 227 | m_Expression->SetVariables(nLineNumber, variables); 228 | } 229 | 230 | void WeightAction::SetResult(ActionResult& action, Unit* pItem) const { 231 | action.nWeight += m_Expression->Evaluate(pItem); 232 | } -------------------------------------------------------------------------------- /Action.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "Expression.h" 6 | #include "Utils.h" 7 | #include "D2Structs.h" 8 | 9 | 10 | struct ActionResult { 11 | std::vector vMatchedRules; 12 | 13 | bool bHide = false; 14 | bool bContinue = false; 15 | 16 | bool bBackgroundPaletteIndexSet = false; 17 | uint8_t nBackgroundPaletteIndex = 0; 18 | DrawMode eDrawModeAlt = DrawMode::TRANS_25; 19 | DrawMode eDrawModeHover = DrawMode::TRANS_50; 20 | 21 | bool bBorderPaletteIndexSet = false; 22 | uint8_t nBorderPaletteIndex = 0; 23 | 24 | bool bInvBackgroundPaletteIndexSet = false; 25 | uint8_t nInvBackgroundPaletteIndex = 0; 26 | 27 | bool bChatAlert = false; 28 | 29 | bool bItemNameSet = false; 30 | std::wstring wsItemName = L""; 31 | 32 | bool bItemDescSet = false; 33 | std::wstring wsItemDesc = L""; 34 | 35 | bool bMinimapIcon = false; 36 | uint8_t nMinimapIconPaletteIndex = 0; 37 | 38 | int32_t nWeight = 0; 39 | }; 40 | 41 | 42 | enum class ActionType : uint8_t { 43 | NONE, SHOW, HIDE, CONTINUE, SET_STYLE, SET_NAME, 44 | SET_DESCRIPTION, SET_BG_COLOR, SET_INVENTORY_COLOR, 45 | SET_BORDER_COLOR, CHAT_NOTIFY, PLAY_ALERT, MINIMAP_ICON, 46 | WEIGHT 47 | }; 48 | 49 | class Action { 50 | protected: 51 | ActionType m_Type; 52 | std::wstring m_Value; 53 | public: 54 | Action(std::wstring_view value = {}, ActionType type = ActionType::NONE) : m_Value(value), m_Type(type) {}; 55 | virtual ~Action() = default; 56 | ActionType GetType() const { return m_Type; } 57 | virtual void Initialize(uint32_t nLineNumber, const utility::string_umap& variables) {}; 58 | virtual void SetResult(ActionResult& action, Unit* pItem) const = 0; 59 | }; 60 | 61 | class ShowAction : public Action { 62 | public: 63 | ShowAction(std::wstring_view value = {}) : Action(value, ActionType::SHOW) {}; 64 | void SetResult(ActionResult& action, Unit* pItem) const override; 65 | 66 | static std::unique_ptr MakeInstance(std::wstring_view value = {}) { return std::make_unique(value); } 67 | }; 68 | 69 | class HideAction : public Action { 70 | public: 71 | HideAction(std::wstring_view value = {}) : Action(value, ActionType::HIDE) {}; 72 | void SetResult(ActionResult& action, Unit* pItem) const override; 73 | 74 | static std::unique_ptr MakeInstance(std::wstring_view value = {}) { return std::make_unique(value); } 75 | }; 76 | 77 | class ContinueAction : public Action { 78 | public: 79 | ContinueAction(std::wstring_view value = {}) : Action(value, ActionType::CONTINUE) { }; 80 | void SetResult(ActionResult& action, Unit* pItem) const override; 81 | 82 | static std::unique_ptr MakeInstance(std::wstring_view value = {}) { return std::make_unique(value); } 83 | }; 84 | 85 | class ColorTextAction : public Action { 86 | public: 87 | ColorTextAction(std::wstring_view value = {}, ActionType type = ActionType::NONE); 88 | virtual void SetResult(ActionResult& action, Unit* pItem) const = 0; 89 | }; 90 | 91 | class PaletteIndexAction : public Action { 92 | protected: 93 | uint8_t m_PaletteIndex = 0; 94 | public: 95 | PaletteIndexAction(std::wstring_view value = {}, ActionType type = ActionType::NONE); 96 | virtual void SetResult(ActionResult& action, Unit* pItem) const = 0; 97 | }; 98 | 99 | class SetStyleAction : public Action { 100 | public: 101 | SetStyleAction(std::wstring_view value = {}) : Action(value, ActionType::SET_STYLE) {}; 102 | void SetResult(ActionResult& action, Unit* pItem) const override; 103 | 104 | static std::unique_ptr MakeInstance(std::wstring_view value = {}) { return std::make_unique(value); } 105 | }; 106 | 107 | class SetNameAction : public ColorTextAction { 108 | public: 109 | SetNameAction(std::wstring_view value = {}) : ColorTextAction(value, ActionType::SET_NAME) {}; 110 | void SetResult(ActionResult& action, Unit* pItem) const override; 111 | 112 | static std::unique_ptr MakeInstance(std::wstring_view value = {}) { return std::make_unique(value); } 113 | }; 114 | 115 | class SetDescriptionAction : public ColorTextAction { 116 | public: 117 | SetDescriptionAction(std::wstring_view value = {}) : ColorTextAction(value, ActionType::SET_DESCRIPTION) {}; 118 | void SetResult(ActionResult& action, Unit* pItem) const override; 119 | 120 | static std::unique_ptr MakeInstance(std::wstring_view value = {}) { return std::make_unique(value); } 121 | }; 122 | 123 | class SetBackgroundColorAction : public PaletteIndexAction { 124 | public: 125 | SetBackgroundColorAction(std::wstring_view value = {}) : PaletteIndexAction(value, ActionType::SET_BORDER_COLOR) {}; 126 | void SetResult(ActionResult& action, Unit* pItem) const override; 127 | 128 | static std::unique_ptr MakeInstance(std::wstring_view value = {}) { return std::make_unique(value); } 129 | }; 130 | 131 | class SetInventoryColorAction : public PaletteIndexAction { 132 | public: 133 | SetInventoryColorAction(std::wstring_view value = {}) : PaletteIndexAction(value, ActionType::SET_INVENTORY_COLOR) {}; 134 | void SetResult(ActionResult& action, Unit* pItem) const override; 135 | 136 | static std::unique_ptr MakeInstance(std::wstring_view value = {}) { return std::make_unique(value); } 137 | }; 138 | 139 | class SetBorderColorAction : public PaletteIndexAction { 140 | public: 141 | SetBorderColorAction(std::wstring_view value = {}) : PaletteIndexAction(value, ActionType::SET_BORDER_COLOR) {}; 142 | void SetResult(ActionResult& action, Unit* pItem) const override; 143 | 144 | static std::unique_ptr MakeInstance(std::wstring_view value = {}) { return std::make_unique(value); } 145 | }; 146 | 147 | class ChatNotifyAction : public Action { 148 | protected: 149 | std::unique_ptr m_Expression; 150 | public: 151 | ChatNotifyAction(std::wstring_view value = {}); 152 | void SetResult(ActionResult& action, Unit* pItem) const override; 153 | 154 | static std::unique_ptr MakeInstance(std::wstring_view value = {}) { return std::make_unique(value); } 155 | }; 156 | 157 | class PlayAlertAction : public Action { 158 | public: 159 | PlayAlertAction(std::wstring_view value = {}) : Action(value, ActionType::PLAY_ALERT) {}; 160 | void SetResult(ActionResult& action, Unit* pItem) const override; 161 | 162 | static std::unique_ptr MakeInstance(std::wstring_view value = {}) { return std::make_unique(value); } 163 | }; 164 | 165 | class MinimapIconAction : public PaletteIndexAction { 166 | public: 167 | MinimapIconAction(std::wstring_view value = {}) : PaletteIndexAction(value, ActionType::MINIMAP_ICON) {}; 168 | void SetResult(ActionResult& action, Unit* pItem) const override; 169 | 170 | static std::unique_ptr MakeInstance(std::wstring_view value = {}) { return std::make_unique(value); } 171 | }; 172 | 173 | class WeightAction : public Action { 174 | protected: 175 | std::unique_ptr m_Expression; 176 | public: 177 | WeightAction(std::wstring_view value = L"") : Action(value, ActionType::WEIGHT) {}; 178 | void Initialize(uint32_t nLineNumber, const utility::string_umap& variables) override; 179 | void SetResult(ActionResult& action, Unit* pItem) const override; 180 | 181 | static std::unique_ptr MakeInstance(std::wstring_view value = {}) { return std::make_unique(value); } 182 | }; 183 | 184 | class ActionFactory { 185 | public: 186 | static std::unique_ptr MakeInstance(std::wstring_view action, std::wstring_view value = {}) { 187 | static const utility::string_umap_icase(&)(std::wstring_view)> lookup = { 188 | { L"Continue", ContinueAction::MakeInstance }, 189 | { L"SetStyle", SetStyleAction::MakeInstance }, 190 | { L"SetName", SetNameAction::MakeInstance }, 191 | { L"SetDescription", SetDescriptionAction::MakeInstance }, 192 | { L"SetBackgroundColor", SetBackgroundColorAction::MakeInstance }, 193 | { L"SetInventoryColor", SetInventoryColorAction::MakeInstance }, 194 | { L"SetBorderColor", SetBorderColorAction::MakeInstance }, 195 | { L"ChatNotify", ChatNotifyAction::MakeInstance }, 196 | { L"PlayAlert", PlayAlertAction::MakeInstance }, 197 | { L"MinimapIcon", MinimapIconAction::MakeInstance }, 198 | { L"Weight", WeightAction::MakeInstance }, 199 | }; 200 | 201 | if (auto search = lookup.find(action); search != lookup.end()) { 202 | return search->second(value); 203 | } 204 | return nullptr; 205 | } 206 | }; -------------------------------------------------------------------------------- /Condition.cpp: -------------------------------------------------------------------------------- 1 | #include "Condition.h" 2 | #include "Utils.h" 3 | #include "Globals.h" 4 | #include 5 | 6 | bool CodeCondition::Evaluate(Unit* pItem) { 7 | m_Left.SetValue(GetItemsTxt(pItem)->dwCode); 8 | return m_Expression->Evaluate(pItem); 9 | } 10 | 11 | void CodeCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 12 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 13 | m_Expression->SetVariables(nLineNumber, variables); 14 | }; 15 | 16 | bool TypeCondition::Evaluate(Unit* pItem) { 17 | m_Left.SetValue(GetItemsTxt(pItem)->dwCode); 18 | return m_Expression->Evaluate(pItem); 19 | } 20 | 21 | void TypeCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 22 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 23 | m_Expression->SetVariables(nLineNumber, variables); 24 | }; 25 | 26 | bool PlayerClassCondition::Evaluate(Unit* pItem) { 27 | Unit* pUnit = D2CLIENT_GetPlayerUnit(); 28 | m_Left.SetValue(static_cast(pUnit->dwClassId)); 29 | return m_Expression->Evaluate(pItem); 30 | } 31 | 32 | void PlayerClassCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 33 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 34 | m_Expression->SetVariables(nLineNumber, variables); 35 | }; 36 | 37 | bool ClassCondition::Evaluate(Unit* pItem) { 38 | return m_Expression->Evaluate(pItem); 39 | } 40 | 41 | void ClassCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 42 | m_Expression = Parser::ParseCall(m_Value.c_str(), Token::CLASS); 43 | m_Expression->SetVariables(nLineNumber, variables); 44 | }; 45 | 46 | bool RarityCondition::Evaluate(Unit* pItem) { 47 | m_Left.SetValue(static_cast(pItem->pItemData->dwRarity)); 48 | return m_Expression->Evaluate(pItem); 49 | } 50 | 51 | void RarityCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 52 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 53 | m_Expression->SetVariables(nLineNumber, variables); 54 | }; 55 | 56 | bool EtherealCondition::Evaluate(Unit* pItem) { 57 | m_Left.SetValue((pItem->pItemData->dwItemFlags & ItemFlags::ETHEREAL) == ItemFlags::ETHEREAL); 58 | return m_Expression->Evaluate(pItem); 59 | } 60 | 61 | void EtherealCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 62 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 63 | m_Expression->SetVariables(nLineNumber, variables); 64 | }; 65 | 66 | bool RunewordCondition::Evaluate(Unit* pItem) { 67 | m_Left.SetValue((pItem->pItemData->dwItemFlags & ItemFlags::RUNEWORD) == ItemFlags::RUNEWORD); 68 | return m_Expression->Evaluate(pItem); 69 | } 70 | 71 | void RunewordCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 72 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 73 | m_Expression->SetVariables(nLineNumber, variables); 74 | }; 75 | 76 | bool PrefixCondition::Evaluate(Unit* pItem) { 77 | uint8_t isIdentified = (pItem->pItemData->dwItemFlags & ItemFlags::IDENTIFIED) == ItemFlags::IDENTIFIED; 78 | if (!isIdentified) { 79 | return false; 80 | } 81 | for (auto& prefix : pItem->pItemData->wMagicPrefix) { 82 | m_Left.SetValue(prefix); 83 | if (m_Expression->Evaluate(pItem)) { 84 | return true; 85 | } 86 | } 87 | return false; 88 | } 89 | 90 | void PrefixCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 91 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 92 | m_Expression->SetVariables(nLineNumber, variables); 93 | }; 94 | 95 | bool SuffixCondition::Evaluate(Unit* pItem) { 96 | uint8_t isIdentified = (pItem->pItemData->dwItemFlags & ItemFlags::IDENTIFIED) == ItemFlags::IDENTIFIED; 97 | if (!isIdentified) { 98 | return false; 99 | } 100 | for (auto& prefix : pItem->pItemData->wMagicSuffix) { 101 | m_Left.SetValue(prefix); 102 | if (m_Expression->Evaluate(pItem)) { 103 | return true; 104 | } 105 | } 106 | return false; 107 | } 108 | 109 | void SuffixCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 110 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 111 | m_Expression->SetVariables(nLineNumber, variables); 112 | }; 113 | 114 | bool ItemLevelCondition::Evaluate(Unit* pItem) { 115 | m_Left.SetValue(pItem->pItemData->dwItemLevel); 116 | return m_Expression->Evaluate(pItem); 117 | } 118 | 119 | void ItemLevelCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 120 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 121 | m_Expression->SetVariables(nLineNumber, variables); 122 | }; 123 | 124 | bool QualityCondition::Evaluate(Unit* pItem) { 125 | m_Left.SetValue(GetQualityLevel(pItem)); 126 | return m_Expression->Evaluate(pItem); 127 | } 128 | 129 | void QualityCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 130 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 131 | m_Expression->SetVariables(nLineNumber, variables); 132 | }; 133 | 134 | bool AreaLevelCondition::Evaluate(Unit* pItem) { 135 | return false; 136 | } 137 | 138 | void AreaLevelCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 139 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 140 | m_Expression->SetVariables(nLineNumber, variables); 141 | }; 142 | 143 | bool CharacterLevelCondition::Evaluate(Unit* pItem) { 144 | m_Left.SetValue(D2COMMON_STATLIST_GetUnitStatUnsigned(D2CLIENT_GetPlayerUnit(), Stat::LEVEL, 0)); 145 | return m_Expression->Evaluate(pItem); 146 | } 147 | 148 | void CharacterLevelCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 149 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 150 | m_Expression->SetVariables(nLineNumber, variables); 151 | }; 152 | 153 | bool DifficultyCondition::Evaluate(Unit* pItem) { 154 | m_Left.SetValue(D2CLIENT_GetDifficulty()); 155 | return m_Expression->Evaluate(pItem); 156 | } 157 | 158 | void DifficultyCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 159 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 160 | m_Expression->SetVariables(nLineNumber, variables); 161 | }; 162 | 163 | bool RuneCondition::Evaluate(Unit* pItem) { 164 | if (!D2COMMON_ITEMS_CheckItemTypeId(pItem, ItemType::RUNE)) { 165 | return false; 166 | } 167 | int nRuneNumber = std::stoi(std::string(&GetItemsTxt(pItem)->szCode[1], 3)); 168 | m_Left.SetValue(nRuneNumber); 169 | return m_Expression->Evaluate(pItem); 170 | } 171 | 172 | void RuneCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 173 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 174 | m_Expression->SetVariables(nLineNumber, variables); 175 | }; 176 | 177 | bool IdCondition::Evaluate(Unit* pItem) { 178 | m_Left.SetValue(pItem->dwLineId); 179 | return m_Expression->Evaluate(pItem); 180 | } 181 | 182 | void IdCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 183 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 184 | m_Expression->SetVariables(nLineNumber, variables); 185 | }; 186 | 187 | bool GoldCondition::Evaluate(Unit* pItem) { 188 | if (!D2COMMON_ITEMS_CheckItemTypeId(pItem, ItemType::GOLD)) { 189 | return false; 190 | } 191 | m_Left.SetValue(D2COMMON_STATLIST_GetUnitStatUnsigned(pItem, Stat::GOLD, 0)); 192 | return m_Expression->Evaluate(pItem); 193 | } 194 | 195 | void GoldCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 196 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 197 | m_Expression->SetVariables(nLineNumber, variables); 198 | }; 199 | 200 | bool StatsCondition::Evaluate(Unit* pItem) { 201 | return m_Expression->Evaluate(pItem); 202 | } 203 | 204 | void StatsCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 205 | for (const auto& stat : CustomStats) { 206 | replace(m_Value, stat.first, stat.second); 207 | } 208 | m_Expression = Parser::Parse(m_Value.c_str()); 209 | m_Expression->SetVariables(nLineNumber, variables); 210 | }; 211 | 212 | bool DefenseCondition::Evaluate(Unit* pItem) { 213 | m_Left.SetValue(D2COMMON_STATLIST_GetUnitStatUnsigned(pItem, Stat::ARMORCLASS, 0)); 214 | return m_Expression->Evaluate(pItem); 215 | } 216 | 217 | void DefenseCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 218 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 219 | m_Expression->SetVariables(nLineNumber, variables); 220 | }; 221 | 222 | bool ArmorCondition::Evaluate(Unit* pItem) { 223 | m_Left.SetValue(D2COMMON_ITEMS_CheckItemTypeId(pItem, ItemType::ANY_ARMOR)); 224 | return m_Expression->Evaluate(pItem); 225 | } 226 | 227 | void ArmorCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 228 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 229 | m_Expression->SetVariables(nLineNumber, variables); 230 | }; 231 | 232 | bool WeaponCondition::Evaluate(Unit* pItem) { 233 | m_Left.SetValue(D2COMMON_ITEMS_CheckItemTypeId(pItem, ItemType::WEAPON)); 234 | return m_Expression->Evaluate(pItem); 235 | } 236 | 237 | void WeaponCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 238 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 239 | m_Expression->SetVariables(nLineNumber, variables); 240 | }; 241 | 242 | bool PriceCondition::Evaluate(Unit* pItem) { 243 | Unit* pPlayer = D2CLIENT_GetPlayerUnit(); 244 | if (pItem != NULL && pPlayer != NULL) { 245 | int nPrice = D2COMMON_ITEMS_GetTransactionCost(pPlayer, pItem, D2CLIENT_GetDifficulty(), D2CLIENT_GetQuestFlags(), 0x201, 1); 246 | m_Left.SetValue(nPrice); 247 | return m_Expression->Evaluate(pItem); 248 | } 249 | return false; 250 | } 251 | 252 | void PriceCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 253 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 254 | m_Expression->SetVariables(nLineNumber, variables); 255 | }; 256 | 257 | bool ModeCondition::Evaluate(Unit* pItem) { 258 | return false; 259 | } 260 | 261 | void ModeCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 262 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 263 | m_Expression->SetVariables(nLineNumber, variables); 264 | }; 265 | 266 | bool IdentifiedCondition::Evaluate(Unit* pItem) { 267 | m_Left.SetValue((pItem->pItemData->dwItemFlags & ItemFlags::IDENTIFIED) == ItemFlags::IDENTIFIED); 268 | return m_Expression->Evaluate(pItem); 269 | } 270 | 271 | void IdentifiedCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 272 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 273 | m_Expression->SetVariables(nLineNumber, variables); 274 | }; 275 | 276 | bool SocketsCondition::Evaluate(Unit* pItem) { 277 | m_Left.SetValue(D2COMMON_STATLIST_GetUnitStatUnsigned(pItem, Stat::ITEM_NUMSOCKETS, 0)); 278 | return m_Expression->Evaluate(pItem); 279 | } 280 | 281 | void SocketsCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 282 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 283 | m_Expression->SetVariables(nLineNumber, variables); 284 | }; 285 | 286 | bool WidthCondition::Evaluate(Unit* pItem) { 287 | m_Left.SetValue(GetItemsTxt(pItem)->nInvWidth); 288 | return m_Expression->Evaluate(pItem); 289 | } 290 | 291 | void WidthCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 292 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 293 | m_Expression->SetVariables(nLineNumber, variables); 294 | }; 295 | 296 | bool HeightCondition::Evaluate(Unit* pItem) { 297 | m_Left.SetValue(GetItemsTxt(pItem)->nInvHeight); 298 | return m_Expression->Evaluate(pItem); 299 | } 300 | 301 | void HeightCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 302 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 303 | m_Expression->SetVariables(nLineNumber, variables); 304 | }; 305 | 306 | bool RandomCondition::Evaluate(Unit* pItem) { 307 | m_Left.SetValue(rand() % 100); //random between 0-99 308 | return m_Expression->Evaluate(pItem); 309 | } 310 | 311 | void RandomCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 312 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 313 | m_Expression->SetVariables(nLineNumber, variables); 314 | }; 315 | 316 | bool OwnedCondition::Evaluate(Unit* pItem) { 317 | //only check set/unique items for duplicates 318 | if (pItem->pItemData->dwRarity != ItemRarity::SET 319 | && pItem->pItemData->dwRarity != ItemRarity::UNIQUE) { 320 | return false; 321 | } 322 | 323 | Unit* pPlayer = D2CLIENT_GetPlayerUnit(); 324 | if (!pPlayer || !pPlayer->pInventory) { 325 | return false; 326 | } 327 | 328 | int32_t unitId = pItem->dwUnitId; 329 | int32_t fileIndex = pItem->pItemData->dwFileIndex; 330 | ItemRarity rarity = pItem->pItemData->dwRarity; 331 | int value = 0; 332 | 333 | for (int i = 0; i < 128; i++) { 334 | Unit* pOtherItem = FindUnitFromTable(i, UnitType::ITEM); 335 | while (pOtherItem) { 336 | if (pOtherItem->pItemData->dwRarity != ItemRarity::SET 337 | && pOtherItem->pItemData->dwRarity != ItemRarity::UNIQUE) { 338 | pOtherItem = pOtherItem->pRoomNext; 339 | continue; 340 | } 341 | if (fileIndex == pOtherItem->pItemData->dwFileIndex 342 | && pItem->pItemData->dwRarity == pOtherItem->pItemData->dwRarity 343 | && unitId != pOtherItem->dwUnitId) { 344 | value = 1; 345 | break; 346 | } 347 | pOtherItem = pOtherItem->pRoomNext; 348 | } 349 | if (value == 1) { 350 | break; 351 | } 352 | } 353 | 354 | m_Left.SetValue(value); 355 | return m_Expression->Evaluate(pItem); 356 | } 357 | 358 | void OwnedCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 359 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 360 | m_Expression->SetVariables(nLineNumber, variables); 361 | }; 362 | 363 | bool HasWeightCondition::Evaluate(Unit* pItem) { 364 | m_Left.SetValue(ITEM_ACTIONS[pItem->dwUnitId].nWeight); 365 | return m_Expression->Evaluate(pItem); 366 | } 367 | 368 | void HasWeightCondition::Initialize(uint32_t nLineNumber, const utility::string_umap& variables) { 369 | m_Expression = Parser::Parse(m_Value.c_str(), &m_Left); 370 | m_Expression->SetVariables(nLineNumber, variables); 371 | } 372 | -------------------------------------------------------------------------------- /Configuration.cpp: -------------------------------------------------------------------------------- 1 | #include "Configuration.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "Utils.h" 10 | #include "mINI.h" 11 | #include "ItemFilter.h" 12 | 13 | namespace fs = std::filesystem; 14 | 15 | utility::string_umap>> GlobalStyles; 16 | std::map GlobalRules; 17 | 18 | #define COMMENT_STR L"#" 19 | #define STYLE_STR L"Style" 20 | #define SHOW_STR L"Show" 21 | #define HIDE_STR L"Hide" 22 | 23 | #define SETTINGS_FILE "d2lootfilter.ini" 24 | 25 | //todo wstring 26 | void Configuration::ReadSettings() { 27 | mINI::INIFile file(SETTINGS_FILE); 28 | mINI::INIStructure data; 29 | 30 | file.read(data); 31 | 32 | if (std::string& value = data["Setting"]["Path"]; !value.empty()) 33 | m_Settings.wPath = value; 34 | if (std::string& value = data["Setting"]["FilterLevel"]; !value.empty()) 35 | m_Settings.nFilterLevel = std::stoi(value); 36 | if (std::string& value = data["Setting"]["PingLevel"]; !value.empty()) 37 | m_Settings.nPingLevel = std::stoi(value); 38 | } 39 | 40 | void Configuration::SaveSettings() { 41 | mINI::INIFile file(SETTINGS_FILE); 42 | mINI::INIStructure data; 43 | 44 | data["Setting"]["Path"] = m_Settings.wPath; 45 | data["Setting"]["FilterLevel"] = std::to_string(m_Settings.nFilterLevel); 46 | data["Setting"]["PingLevel"] = std::to_string(m_Settings.nPingLevel); 47 | 48 | file.write(data); 49 | } 50 | 51 | void Configuration::LoadSettings() { 52 | if (!ItemFilter::IsTxtDataLoaded()) { 53 | return; 54 | } 55 | 56 | ReadSettings(); 57 | if (!fs::exists(SETTINGS_FILE)) { 58 | SaveSettings(); 59 | } 60 | 61 | auto t1 = std::chrono::high_resolution_clock::now(); 62 | 63 | GlobalRules.clear(); 64 | GlobalStyles.clear(); 65 | 66 | uint32_t tokenLineNumber = 0; 67 | uint32_t currentLineNumber = 0; 68 | 69 | // Create an empty item filter file if it doesn't exist 70 | std::wfstream in(m_Settings.wPath, std::ios::in | std::ios::app); 71 | std::wstring line; 72 | std::vector lines; 73 | 74 | while (std::getline(in, line)) { 75 | currentLineNumber++; 76 | if (line.find(COMMENT_STR) != std::wstring::npos) { 77 | line = line.erase(line.find(COMMENT_STR)); 78 | } 79 | line = trim(line); 80 | if (line.length() == 0) { 81 | continue; 82 | } 83 | if (line.compare(0, 5, STYLE_STR) == 0 84 | || line.compare(0, 4, SHOW_STR) == 0 85 | || line.compare(0, 4, HIDE_STR) == 0) { 86 | HandleToken(tokenLineNumber, lines); 87 | tokenLineNumber = currentLineNumber; 88 | } 89 | lines.push_back(line); 90 | } 91 | HandleToken(tokenLineNumber, lines); 92 | 93 | InitalizeConditionVariables(); 94 | m_Loaded = true; 95 | 96 | auto t2 = std::chrono::high_resolution_clock::now(); 97 | auto ms_int = std::chrono::duration_cast(t2 - t1); 98 | DEBUG_LOG(std::format(L"Rules parsed in {}ms\n", ms_int.count())); 99 | } 100 | 101 | void Configuration::HandleToken(uint32_t lineNumber, std::vector& lines) { 102 | if (lines.empty()) 103 | return; 104 | std::wstring line = lines[0]; lines.erase(lines.begin()); 105 | if (line.compare(0, 5, STYLE_STR) == 0) { 106 | std::wstring style = line.substr(5); 107 | style = trim(style); 108 | GlobalStyles[style] = ParseActions(lines); 109 | } 110 | else { 111 | if (auto [rule, result] = GlobalRules.try_emplace(lineNumber, ParseRule(lineNumber, lines)); result) { 112 | rule->second.AddAction(line.compare(0, 4, SHOW_STR) == 0 ? ShowAction::MakeInstance() : HideAction::MakeInstance()); 113 | } 114 | } 115 | for (const auto& line : lines) { 116 | ERROR_LOG(std::format(L"Rule {}: Error parsing {}.", lineNumber, line)); 117 | } 118 | lines.clear(); 119 | } 120 | 121 | std::vector> Configuration::ParseStyle(std::vector& lines) { 122 | return ParseActions(lines); 123 | } 124 | 125 | Rule Configuration::ParseRule(uint32_t lineNumber, std::vector& lines) { 126 | Rule rule{ lineNumber }; 127 | rule.AddConditions(ParseConditions(lines)); 128 | rule.AddActions(ParseActions(lines)); 129 | return rule; 130 | } 131 | 132 | std::vector> Configuration::ParseConditions(std::vector& lines) { 133 | std::vector> items; 134 | lines.erase(std::remove_if(lines.begin(), lines.end(), [&items](std::wstring_view line) { 135 | size_t pos = (std::min)(line.find_first_of(L' '), line.length()); 136 | if (auto condition = ConditionFactory::MakeInstance(line.substr(0, pos), trim(line.substr(pos)))) { 137 | items.push_back(std::move(condition)); 138 | return true; 139 | } 140 | return false; 141 | }), lines.end()); 142 | return items; 143 | } 144 | 145 | std::vector> Configuration::ParseActions(std::vector& lines) { 146 | std::vector> items; 147 | lines.erase(std::remove_if(lines.begin(), lines.end(), [&items](std::wstring_view line) { 148 | size_t pos = (std::min)(line.find_first_of(L' '), line.length()); 149 | if (auto action = ActionFactory::MakeInstance(line.substr(0, pos), trim(line.substr(pos)))) { 150 | items.push_back(std::move(action)); 151 | return true; 152 | } 153 | return false; 154 | }), lines.end()); 155 | return items; 156 | } 157 | 158 | //init any constants used in rules i.e. item names/skill names 159 | void Configuration::InitalizeConditionVariables() { 160 | //replace any variables used in the filter. i.e. item names/codes/rune numbers/etc... 161 | InitializeTypesCodesAndRunes(); 162 | InitializeClass(); 163 | InitializeRaritiesAndQualities(); 164 | InitializeOther(); 165 | InitalizeActionVariables(); 166 | 167 | /* 168 | for (auto &rule : GlobalRules) { 169 | for (auto& condition : rule->GetConditions()) { 170 | Logger::Info("%s\n", condition->ToString().c_str()); 171 | } 172 | } 173 | */ 174 | } 175 | 176 | void Configuration::InitalizeActionVariables() { 177 | utility::string_umap variables; 178 | for (auto& rule : GlobalRules) { 179 | for (auto& action : rule.second.GetActions()) { 180 | switch (action->GetType()) { 181 | default: 182 | action->Initialize(rule.first, variables); 183 | break; 184 | } 185 | } 186 | } 187 | for (auto& style : GlobalStyles) { 188 | for (auto& action : style.second) { 189 | switch (action->GetType()) { 190 | default: 191 | action->Initialize(0, variables); 192 | break; 193 | } 194 | } 195 | } 196 | } 197 | 198 | void Configuration::InitializeTypesCodesAndRunes() { 199 | static utility::string_umap names; 200 | static utility::string_umap codes; 201 | static utility::string_umap runes; 202 | 203 | DataTables* sgptDataTables = *D2COMMON_gpDataTables; 204 | 205 | if (names.empty()) { 206 | for (int i = 0; i < D2COMMON_ItemDataTbl->nItemsTxtRecordCount; i++) { 207 | ItemsTxt* pItemTxt = &D2COMMON_ItemDataTbl->pItemsTxt[i]; 208 | std::wstring wNameStr = D2LANG_GetStringFromTblIndex(pItemTxt->wNameStr); 209 | auto [it, result] = names.try_emplace(wNameStr, pItemTxt->dwCode); 210 | // Mapping duplicated names to an empty code 211 | if (!result && it->second != pItemTxt->dwCode) { 212 | it->second = 0x20202020; 213 | } 214 | std::wstring wCode = std::wstring(4, L' '); 215 | mbstowcs(&wCode[0], pItemTxt->szCode, 4); 216 | wCode = trim(wCode); 217 | codes[wCode] = pItemTxt->dwCode; 218 | if (pItemTxt->wType[0] == ItemType::RUNE) { 219 | int nRuneNumber = std::stoi(std::string(&pItemTxt->szCode[1], 3)); 220 | runes[wNameStr] = nRuneNumber; 221 | size_t nFound = wNameStr.find(L" "); 222 | if (nFound != std::wstring::npos) { 223 | runes[wNameStr.substr(0, nFound)] = nRuneNumber; 224 | } 225 | } 226 | } 227 | } 228 | 229 | static utility::string_umap classNames; 230 | if (classNames.empty()) { 231 | for (int i = 0; i < sgptDataTables->nCharStatsTxtRecordCount; i++) { 232 | CharStatsTxt* pCharStats = &sgptDataTables->pCharStatsTxt[i]; 233 | std::wstring className = std::wstring(pCharStats->wszClassName); 234 | className = trim(className); 235 | classNames[className] = i; 236 | } 237 | } 238 | 239 | for (auto& rule : GlobalRules) { 240 | for (auto& condition : rule.second.GetConditions()) { 241 | switch (condition->GetType()) { 242 | case ConditionType::TYPE: 243 | condition->Initialize(rule.first, names); 244 | break; 245 | case ConditionType::CODE: 246 | condition->Initialize(rule.first, codes); 247 | break; 248 | case ConditionType::RUNE: 249 | condition->Initialize(rule.first, runes); 250 | break; 251 | case ConditionType::PLAYERCLASS: 252 | condition->Initialize(rule.first, classNames); 253 | break; 254 | default: 255 | break; 256 | } 257 | } 258 | } 259 | } 260 | 261 | 262 | void Configuration::InitializeClass() { 263 | for (auto& rule : GlobalRules) { 264 | for (auto& condition : rule.second.GetConditions()) { 265 | switch (condition->GetType()) { 266 | case ConditionType::CLASS: 267 | condition->Initialize(rule.first, ItemTypes); 268 | break; 269 | default: 270 | break; 271 | } 272 | } 273 | } 274 | } 275 | 276 | void Configuration::InitializeRaritiesAndQualities() { 277 | for (auto& rule : GlobalRules) { 278 | for (auto& condition : rule.second.GetConditions()) { 279 | switch (condition->GetType()) { 280 | case ConditionType::RARITY: 281 | condition->Initialize(rule.first, Rarities); 282 | break; 283 | case ConditionType::QUALITY: 284 | condition->Initialize(rule.first, Qualities); 285 | break; 286 | default: 287 | break; 288 | } 289 | } 290 | } 291 | } 292 | 293 | 294 | void Configuration::InitializeOther() { 295 | const utility::string_umap variables; 296 | 297 | for (auto& rule : GlobalRules) { 298 | for (auto& condition : rule.second.GetConditions()) { 299 | switch (condition->GetType()) { 300 | case ConditionType::STATS: 301 | case ConditionType::ETHEREAL: 302 | case ConditionType::RUNEWORD: 303 | case ConditionType::PREFIX: 304 | case ConditionType::SUFFIX: 305 | case ConditionType::ITEM_LEVEL: 306 | case ConditionType::AREA_LEVEL: 307 | case ConditionType::CHARACTER_LEVEL: 308 | case ConditionType::DIFFICULTY: 309 | case ConditionType::ID: 310 | case ConditionType::GOLD: 311 | case ConditionType::DEFENSE: 312 | case ConditionType::ARMOR: 313 | case ConditionType::WEAPON: 314 | case ConditionType::PRICE: 315 | case ConditionType::MODE: 316 | case ConditionType::IDENTIFIED: 317 | case ConditionType::SOCKETS: 318 | case ConditionType::WIDTH: 319 | case ConditionType::HEIGHT: 320 | case ConditionType::RANDOM: 321 | case ConditionType::OWNED: 322 | case ConditionType::HASWEIGHT: 323 | condition->Initialize(rule.first, variables); 324 | break; 325 | default: 326 | break; 327 | } 328 | } 329 | } 330 | } 331 | 332 | #undef COMMENT_STR 333 | #undef STYLE_STR 334 | #undef SHOW_STR 335 | #undef HIDE_STR -------------------------------------------------------------------------------- /Configuration.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Rule.h" 7 | 8 | extern utility::string_umap>> GlobalStyles; 9 | extern std::map GlobalRules; 10 | 11 | struct Settings { 12 | std::string wPath; 13 | int32_t nFilterLevel; 14 | int32_t nPingLevel; 15 | }; 16 | 17 | class Configuration { 18 | private: 19 | Settings m_Settings; 20 | bool m_Loaded = false; 21 | 22 | void HandleToken(uint32_t lineNumber, std::vector& lines); 23 | std::vector> ParseStyle(std::vector& lines); 24 | Rule ParseRule(uint32_t lineNumber, std::vector& lines); 25 | std::vector> ParseConditions(std::vector& lines); 26 | std::vector> ParseActions(std::vector& lines); 27 | void ReadSettings(); 28 | void InitalizeConditionVariables(); 29 | void InitializeTypesCodesAndRunes(); 30 | void InitializeClass(); 31 | void InitializeRaritiesAndQualities(); 32 | void InitializeOther(); 33 | void InitalizeActionVariables(); 34 | public: 35 | friend class ItemFilter; 36 | 37 | Configuration() : m_Settings{ "./item.filter", 6, 6 } {} 38 | ~Configuration() { SaveSettings(); } 39 | 40 | void SaveSettings(); 41 | void LoadSettings(); 42 | bool IsLoaded() { return m_Loaded; } 43 | }; 44 | 45 | -------------------------------------------------------------------------------- /D2Constants.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define TEXT_WHITE L"\xff" "c0" 9 | #define TEXT_RED L"\xff" "c1" 10 | #define TEXT_GREEN L"\xff" "c2" 11 | #define TEXT_BLUE L"\xff" "c3" 12 | #define TEXT_GOLD L"\xff" "c4" 13 | #define TEXT_GRAY L"\xff" "c5" 14 | #define TEXT_BLACK L"\xff" "c6" 15 | #define TEXT_TAN L"\xff" "c7" 16 | #define TEXT_ORANGE L"\xff" "c8" 17 | #define TEXT_YELLOW L"\xff" "c9" 18 | #define TEXT_PURPLE L"\xff" "c;" 19 | #define TEXT_DARK_GREEN L"\xff" "c:" 20 | #define TEXT_CORAL L"\xff" "0" "\x06" 21 | #define TEXT_SAGE L"\xff" "0" "\x07" 22 | #define TEXT_TEAL L"\xff" "0" "\x09" 23 | #define TEXT_LIGHT_GRAY L"\xff" "0" "\x0c" 24 | 25 | template>> 27 | std::ostream& operator<< (std::ostream& out, Enum e) { 28 | return out << static_cast(e); 29 | } 30 | 31 | template>> 33 | std::wostream& operator<< (std::wostream& out, Enum e) { 34 | return out << static_cast(e); 35 | } 36 | 37 | //user defined 38 | enum class D2Version : uint8_t { 39 | V110f, 40 | V113c, 41 | V114d, 42 | NONE, 43 | ERR 44 | }; 45 | 46 | //user defined 47 | enum class ItemQualityLevel : uint8_t { 48 | UNKNOWN = 0xff, 49 | NORMAL = 0, 50 | EXCEPTIONAL = 1, 51 | ELITE = 2 52 | }; 53 | 54 | enum class ItemType : uint16_t { 55 | NONE_1 = 0, 56 | NONE_2, 57 | SHIELD, 58 | ARMOR, 59 | GOLD, 60 | BOW_QUIVER, 61 | CROSSBOW_QUIVER, 62 | PLAYER_BODY_PART, 63 | HERB, 64 | POTION, 65 | RING, 66 | ELIXIR, 67 | AMULET, 68 | CHARM, 69 | NONE_3, 70 | BOOTS, 71 | GLOVES, 72 | NONE_4, 73 | BOOK, 74 | BELT, 75 | GEM, 76 | TORCH, 77 | SCROLL, 78 | NONE_5, 79 | SCEPTER, 80 | WAND, 81 | STAFF, 82 | BOW, 83 | AXE, 84 | CLUB, 85 | SWORD, 86 | HAMMER, 87 | KNIFE, 88 | SPEAR, 89 | POLEARM, 90 | CROSSBOW, 91 | MACE, 92 | HELM, 93 | MISSILE_POTION, 94 | QUEST, 95 | BODY_PART, 96 | KEY, 97 | THROWING_KNIFE, 98 | THROWING_AXE, 99 | JAVELIN, 100 | WEAPON, 101 | MELEE_WEAPON, 102 | MISSILE_WEAPON, 103 | THROWN_WEAPON, 104 | COMBO_WEAPON, 105 | ANY_ARMOR, 106 | ANY_SHIELD, 107 | MISCELLANEOUS, 108 | SOCKET_FILLER, 109 | SECOND_HAND, 110 | STAVES_AND_RODS, 111 | MISSILE, 112 | BLUNT, 113 | JEWEL, 114 | CLASS_SPECIFIC, 115 | AMAZON_ITEM, 116 | BARBARIAN_ITEM, 117 | NECROMANCER_ITEM, 118 | PALADIN_ITEM, 119 | SORCERESS_ITEM, 120 | ASSASSIN_ITEM, 121 | DRUID_ITEM, 122 | HAND_TO_HAND, 123 | ORB, 124 | VOODOO_HEADS, 125 | AURIC_SHIELDS, 126 | PRIMAL_HELM, 127 | PELT, 128 | CLOAK, 129 | RUNE, 130 | CIRCLET, 131 | HEALING_POTION, 132 | MANA_POTION, 133 | REJUV_POTION, 134 | STAMINA_POTION, 135 | ANTIDOTE_POTION, 136 | THAWING_POTION, 137 | SMALL_CHARM, 138 | MEDIUM_CHARM, 139 | LARGE_CHARM, 140 | AMAZON_BOW, 141 | AMAZON_SPEAR, 142 | AMAZON_JAVELIN, 143 | HAND_TO_HAND_2, 144 | MAGIC_BOW_QUIV, 145 | UNK, 146 | CHIPPED_GEM, 147 | FLAWED_GEM, 148 | STANDARD_GEM, 149 | FLAWLESS_GEM, 150 | PERFECT_GEM, 151 | AMETHYST, 152 | DIAMOND, 153 | EMERALD, 154 | RUBY, 155 | SAPPHIRE, 156 | TOPAZ, 157 | SKULL, 158 | // Own ItemTypes 159 | TOME, 160 | CLUESCROLL 161 | }; 162 | 163 | //0x9c and 0x9d actions 164 | enum class ItemAction : uint8_t { 165 | NEW_GROUND = 0x00, 166 | PICK_UP = 0x01, 167 | DROP = 0x02, 168 | OLD_GROUND = 0x03, 169 | TO_STORAGE = 0x04, 170 | FROM_STORAGE = 0x05, 171 | EQUIP = 0x06, 172 | INDIRECT_SWAP_BODY = 0x07, 173 | UNEQUIP = 0x08, 174 | SWAP_BODY = 0x09, 175 | ADD_QUANTITY = 0x0A, 176 | TO_STORE = 0x0B, 177 | FROM_STORE = 0x0C, 178 | SWAP_IN_CONTAINER = 0x0D, 179 | PLACE_BELT = 0x0E, 180 | REMOVE_BELT = 0x0F, 181 | SWAP_BELT = 0x10, 182 | AUTO_UNEQUIP = 0x11, 183 | TO_CURSOR = 0x12, 184 | ITEM_IN_SOCKET = 0x13, 185 | UPDATE_STATS = 0x15, 186 | WEAPON_SWITCH = 0x17, 187 | TOTAL_ITEM_ACTION 188 | }; 189 | 190 | enum class UnitType : uint32_t { 191 | PLAYER = 0, 192 | MONSTER, 193 | OBJECT, 194 | MISSILE, 195 | ITEM, 196 | TILE, 197 | TOTAL_UNIT_TYPES 198 | }; 199 | 200 | enum class PlayerClass : uint32_t { 201 | AMAZON = 0, 202 | SORCERESS, 203 | NECROMANCER, 204 | PALADIN, 205 | BARBARIAN, 206 | DRUID, 207 | ASSASSIN, 208 | TOTAL_PLAYER_CLASSES 209 | }; 210 | 211 | enum class ItemAnimationMode : uint32_t 212 | { 213 | INVENTORY, //Item is in Storage (inventory, cube, Stash?) 214 | BODY, //Item is Equippped 215 | BELT, //Item is in Belt Rows 216 | GROUND, //Item is on Ground 217 | CURSOR, //Item is on Cursor 218 | DROPPING, //Item is Being Dropped 219 | SOCKET, //Item is Socketed in another Item 220 | TOTAL_ITEM_MODES 221 | }; 222 | 223 | //https://d2mods.info/forum/viewtopic.php?p=487011#p487011 224 | //UnitAny 0xC4 225 | enum class UnitFlag : uint32_t 226 | { 227 | DOUPDATE = 0x00000001, //tells to update the unit 228 | TARGETABLE = 0x00000002, //whenever the unit can be selected or not 229 | CANBEATTACKED = 0x00000004, //whenever the unit can be attacked 230 | ISVALIDTARGET = 0x00000008, //used to check if unit is a valid target 231 | INITSEEDSET = 0x00000010, //tells whenever the unit seed has been initialized 232 | DRAWSHADOW = 0x00000020, //tells whenver to draw a shadow or not (client only) 233 | SKSRVDOFUNC = 0x00000040, //set when skill srvdofunc is executed 234 | OBJPREOPERATE = 0x00000080, //unknown, used by objects with pre-operate disabled 235 | HASTXTMSG = 0x00000100, //whenever this unit has a text message attached to it 236 | ISMERC = 0x00000200, //is mercenary unit 237 | HASEVENTSOUND = 0x00000400, //does this unit have an event-sound attached to it (server) 238 | SUMMONER = 0x00000800, //set for the summoner only 239 | SENDREFRESHMSG = 0x00001000, //used by items to send a refresh message when it drops on ground 240 | ISLINKREFRESHMSG = 0x00002000, //tells whenever this unit is linked to an update message chain 241 | SQGFXCHANGE = 0x00004000, //tells whenever to load new anim for skill SQ 242 | UPGRLIFENHITCLASS = 0x00008000, //updates life% and hitclass on client 243 | ISDEAD = 0x00010000, //unit is dead 244 | NOTC = 0x00020000, //disables treasureclass drops 245 | MONMODEISCHANGING = 0x00080000, //set when monmode changes 246 | PREDRAW = 0x00100000, //pre-draw this unit (like floor tiles, client only) 247 | ISASYNC = 0x00200000, //is async unit (critters) 248 | ISCLIENTUNIT = 0x00400000, //is client unit 249 | ISINIT = 0x01000000, //set when unit has been initialized 250 | ISRESURRECT = 0x02000000, //set for resurrected units and items on floor 251 | NOXP = 0x04000000, //no xp gain from killing this unit 252 | AUTOMAP = 0x10000000, //automap stuff 253 | AUTOMAP2 = 0x20000000, //automap stuff 254 | PETIGNORE = 0x40000000, //ignored by pets 255 | ISREVIVE = 0x80000000 //is revived monster 256 | }; 257 | 258 | //UnitAny 0xC8 259 | enum class UnitFlagEx : uint32_t 260 | { 261 | HASINV = 1 << 0, //unit has inventory attached to it 262 | UPDATEINV = 1 << 1, //tells to update inventory content 263 | ISVENDORITEM = 1 << 2, //set for vendor shop items 264 | ISSHAPESHIFTED = 1 << 3, //unit is shapeshifted 265 | ITEMINIT = 1 << 4, //set for items, related to init 266 | ISINLOS = 1 << 7, //unit is in client's line of sight 267 | HASBEENDELETED = 1 << 8, //unit has been deleted but not free'd yet 268 | STOREOWNERINFO = 1 << 10, //unit stores info about owner 269 | ISCORPSE = 1 << 12, //unit is a corpse (use ISDEAD instead) 270 | TELEPORTED = 1 << 16, //unit has been teleported, needs resync 271 | STORELASTATTACKER = 1 << 17, //unit stores info about last attacker 272 | NODRAW = 1 << 18, //don't draw this unit 273 | ISEXPANSION = 1 << 25, //is expansion unit 274 | SERVERUNIT = 1 << 26 //is server-side unit 275 | }; 276 | 277 | enum StatlistFlags : uint32_t 278 | { 279 | BASE = 0x0, 280 | BASIC = 0x1, 281 | NEWLENGTH = 0x2, 282 | TEMPONLY = 0x4, 283 | BUFF = 0x8, 284 | CURSE = 0x20, 285 | MAGIC = 0x40, 286 | OVERLAY = 0x80, 287 | UNK_0x100 = 0x100, 288 | TOGGLE = 0x200, 289 | CONVERT = 0x800, 290 | SET = 0x2000, 291 | ITEM_EX = 0x200000, 292 | PERMANENT = 0x20000000, 293 | DYNAMIC = 0x40000000, 294 | EXTENDED = 0x80000000, 295 | }; 296 | 297 | enum ItemFlags : uint32_t 298 | { 299 | NEWITEM = 0x00000001, 300 | TARGET = 0x00000002, 301 | TARGETING = 0x00000004, 302 | DELETED = 0x00000008, 303 | IDENTIFIED = 0x00000010, 304 | QUANTITY = 0x00000020, 305 | SWITCHIN = 0x00000040, 306 | SWITCHOUT = 0x00000080, 307 | BROKEN = 0x00000100, 308 | REPAIRED = 0x00000200, 309 | UNK1 = 0x00000400, 310 | SOCKETED = 0x00000800, 311 | NOSELL = 0x00001000, 312 | INSTORE = 0x00002000, 313 | NOEQUIP = 0x00004000, 314 | NAMED = 0x00008000, 315 | ISEAR = 0x00010000, 316 | STARTITEM = 0x00020000, 317 | UNK2 = 0x00040000, 318 | INIT = 0x00080000, 319 | UNK3 = 0x00100000, 320 | COMPACTSAVE = 0x00200000, 321 | ETHEREAL = 0x00400000, 322 | JUSTSAVED = 0x00800000, 323 | PERSONALIZED = 0x01000000, 324 | LOWQUALITY = 0x02000000, 325 | RUNEWORD = 0x04000000, 326 | ITEM = 0x08000000 327 | }; 328 | 329 | static int FONT_HEIGHT[] = { 10, 15, 18, 24, 10, 13, 7, 13, 10, 12, 8, 8, 7, 12 }; 330 | 331 | //https://user-images.githubusercontent.com/26683324/61428304-67d52380-a8d6-11e9-92ac-4be93dfe67d2.PNG 332 | /* 333 | //Mir Drualga 334 | 0 = Formal_8 335 | 1 = Exocet_16 336 | 2 = DiabloMenu_30 337 | 3 = DiabloMenu_42 338 | 4 = Formal_10 339 | 5 = Formal_12 340 | 6 = Formal_6 341 | 7 = DiabloMenu_24 342 | 8 = FormalWide_11 343 | 9 = Exocet_10 344 | 10 = Exocet_9 345 | 11 = Exocet_8 346 | 12 = (same as 6) 347 | 13 = Formal_11 348 | */ 349 | enum class Font : int32_t { 350 | Font8 = 0, 351 | Font16, //height = 11, /n spacing + 4px 352 | Font30, 353 | Font42, 354 | FontFormal10, 355 | FontFormal12, 356 | Font6, 357 | Font24, 358 | FontFormal11, 359 | FontExocet10, 360 | FontRidiculuos, 361 | FontExocet8, 362 | ReallyTheLastSucker, 363 | FontInGameChat 364 | }; 365 | 366 | enum class DrawMode : int32_t { 367 | TRANS_25 = 0, 368 | TRANS_50 = 1, 369 | TRANS_75 = 2, 370 | MODULATE = 3, 371 | BURN = 4, 372 | NORMAL = 5, 373 | MOD2X_TRANS = 6, 374 | MOD2X = 7 375 | }; 376 | 377 | enum class TextColor : int32_t { 378 | DISABLED = -1, 379 | WHITE = 0, 380 | RED, 381 | GREEN, 382 | BLUE, 383 | GOLD, 384 | GREY, 385 | BLACK, 386 | TAN, 387 | ORANGE, 388 | YELLOW, 389 | DARKGREEN, 390 | PURPLE, 391 | SILVER = 15 392 | }; 393 | 394 | enum class ItemRarity : uint32_t { 395 | NONE = 0x0, 396 | INFERIOR = 0x1, 397 | NORMAL = 0x2, 398 | SUPERIOR = 0x3, 399 | MAGIC = 0x4, 400 | SET = 0x5, 401 | RARE = 0x6, 402 | UNIQUE = 0x7, 403 | CRAFT = 0x8 404 | }; 405 | 406 | enum class Stat : int { 407 | INVALID = -1, 408 | STRENGTH = 0, // 000 409 | ENERGY, // 001 410 | DEXTERITY, // 002 411 | VITALITY, // 003 412 | STATPTS, // 004 413 | SKILLPTS, // 005 414 | HITPOINTS, // 006 415 | MAXHP, // 007 416 | MANA, // 008 417 | MAXMANA, // 009 418 | STAMINA, // 00A 419 | MAXSTAMINA, // 00B 420 | LEVEL, // 00C 421 | EXPERIENCE, // 00D 422 | GOLD, // 00E 423 | GOLDBANK, // 00F 424 | ITEM_ARMOR_PERCENT, // 010 425 | ITEM_MAXDAMAGE_PERCENT, // 011 426 | ITEM_MINDAMAGE_PERCENT, // 012 427 | TOHIT, 428 | TOBLOCK, 429 | MINDAMAGE, 430 | MAXDAMAGE, 431 | SECONDARY_MINDAMAGE, 432 | SECONDARY_MAXDAMAGE, 433 | DAMAGEPERCENT, 434 | MANARECOVERY, 435 | MANARECOVERYBONUS, 436 | STAMINARECOVERYBONUS, 437 | LASTEXP, 438 | NEXTEXP, 439 | ARMORCLASS, 440 | ARMORCLASS_VS_MISSILE, 441 | ARMORCLASS_VS_HTH, 442 | NORMAL_DAMAGE_REDUCTION, 443 | MAGIC_DAMAGE_REDUCTION, 444 | DAMAGERESIST, 445 | MAGICRESIST, 446 | MAXMAGICRESIST, 447 | FIRERESIST, 448 | MAXFIRERESIST, 449 | LIGHTRESIST, 450 | MAXLIGHTRESIST, 451 | COLDRESIST, 452 | MAXCOLDRESIST, 453 | POISONRESIST, 454 | MAXPOISONRESIST, 455 | DAMAGEAURA, 456 | FIREMINDAM, 457 | FIREMAXDAM, 458 | LIGHTMINDAM, 459 | LIGHTMAXDAM, 460 | MAGICMINDAM, 461 | MAGICMAXDAM, 462 | COLDMINDAM, 463 | COLDMAXDAM, 464 | COLDLENGTH, 465 | POISONMINDAM, 466 | POISONMAXDAM, 467 | POISONLENGTH, 468 | LIFEDRAINMINDAM, 469 | LIFEDRAINMAXDAM, 470 | MANADRAINMINDAM, 471 | MANADRAINMAXDAM, 472 | STAMDRAINMINDAM, 473 | STAMDRAINMAXDAM, 474 | STUNLENGTH, 475 | VELOCITYPERCENT, 476 | ATTACKRATE, 477 | OTHER_ANIMRATE, 478 | QUANTITY, 479 | VALUE, 480 | DURABILITY, 481 | MAXDURABILITY, 482 | HPREGEN, 483 | ITEM_MAXDURABILITY_PERCENT, 484 | ITEM_MAXHP_PERCENT, 485 | ITEM_MAXMANA_PERCENT, 486 | ITEM_ATTACKERTAKESDAMAGE, 487 | ITEM_GOLDBONUS, 488 | ITEM_MAGICBONUS, 489 | ITEM_KNOCKBACK, 490 | ITEM_TIMEDURATION, 491 | ITEM_ADDCLASSSKILLS, 492 | UNSENTPARAM1, 493 | ITEM_ADDEXPERIENCE, 494 | ITEM_HEALAFTERKILL, 495 | ITEM_REDUCEDPRICES, 496 | ITEM_DOUBLEHERBDURATION, 497 | ITEM_LIGHTRADIUS, 498 | ITEM_LIGHTCOLOR, 499 | ITEM_REQ_PERCENT, 500 | ITEM_LEVELREQ, 501 | ITEM_FASTERATTACKRATE, 502 | ITEM_LEVELREQPCT, 503 | LASTBLOCKFRAME, 504 | ITEM_FASTERMOVEVELOCITY, 505 | ITEM_NONCLASSSKILL, 506 | STATE, 507 | ITEM_FASTERGETHITRATE, 508 | MONSTER_PLAYERCOUNT, 509 | SKILL_POISON_OVERRIDE_LENGTH, 510 | ITEM_FASTERBLOCKRATE, 511 | SKILL_BYPASS_UNDEAD, 512 | SKILL_BYPASS_DEMONS, 513 | ITEM_FASTERCASTRATE, 514 | SKILL_BYPASS_BEASTS, 515 | ITEM_SINGLESKILL, 516 | ITEM_RESTINPEACE, 517 | CURSE_RESISTANCE, 518 | ITEM_POISONLENGTHRESIST, 519 | ITEM_NORMALDAMAGE, 520 | ITEM_HOWL, 521 | ITEM_STUPIDITY, 522 | ITEM_DAMAGETOMANA, 523 | ITEM_IGNORETARGETAC, 524 | ITEM_FRACTIONALTARGETAC, 525 | ITEM_PREVENTHEAL, 526 | ITEM_HALFFREEZEDURATION, 527 | ITEM_TOHIT_PERCENT, 528 | ITEM_DAMAGETARGETAC, 529 | ITEM_DEMONDAMAGE_PERCENT, 530 | ITEM_UNDEADDAMAGE_PERCENT, 531 | ITEM_DEMON_TOHIT, 532 | ITEM_UNDEAD_TOHIT, 533 | ITEM_THROWABLE, 534 | ITEM_ELEMSKILL, 535 | ITEM_ALLSKILLS, 536 | ITEM_ATTACKERTAKESLIGHTDAMAGE, 537 | IRONMAIDEN_LEVEL, 538 | LIFETAP_LEVEL, 539 | THORNS_PERCENT, 540 | BONEARMOR, 541 | BONEARMORMAX, 542 | ITEM_FREEZE, 543 | ITEM_OPENWOUNDS, 544 | ITEM_CRUSHINGBLOW, 545 | ITEM_KICKDAMAGE, 546 | ITEM_MANAAFTERKILL, 547 | ITEM_HEALAFTERDEMONKILL, 548 | ITEM_EXTRABLOOD, 549 | ITEM_DEADLYSTRIKE, 550 | ITEM_ABSORBFIRE_PERCENT, 551 | ITEM_ABSORBFIRE, 552 | ITEM_ABSORBLIGHT_PERCENT, 553 | ITEM_ABSORBLIGHT, 554 | ITEM_ABSORBMAGIC_PERCENT, 555 | ITEM_ABSORBMAGIC, 556 | ITEM_ABSORBCOLD_PERCENT, 557 | ITEM_ABSORBCOLD, 558 | ITEM_SLOW, 559 | ITEM_AURA, 560 | ITEM_INDESCTRUCTIBLE, 561 | ITEM_CANNOTBEFROZEN, 562 | ITEM_STAMINADRAINPCT, 563 | ITEM_REANIMATE, 564 | ITEM_PIERCE, 565 | ITEM_MAGICARROW, 566 | ITEM_EXPLOSIVEARROW, 567 | ITEM_THROW_MINDAMAGE, 568 | ITEM_THROW_MAXDAMAGE, 569 | SKILL_HANDOFATHENA, 570 | SKILL_STAMINAPERCENT, 571 | SKILL_PASSIVE_STAMINAPERCENT, 572 | SKILL_CONCENTRATION, 573 | SKILL_ENCHANT, 574 | SKILL_PIERCE, 575 | SKILL_CONVICTION, 576 | SKILL_CHILLINGARMOR, 577 | SKILL_FRENZY, 578 | SKILL_DECREPIFY, 579 | SKILL_ARMOR_PERCENT, 580 | ALIGNMENT, 581 | TARGET0, 582 | TARGET1, 583 | GOLDLOST, 584 | CONVERSION_LEVEL, 585 | CONVERSION_MAXHP, 586 | UNIT_DOOVERLAY, 587 | ATTACK_VS_MONTYPE, 588 | DAMAGE_VS_MONTYPE, 589 | FADE, 590 | ARMOR_OVERRIDE_PERCENT, 591 | UNUSED183, 592 | UNUSED184, 593 | UNUSED185, 594 | UNUSED186, 595 | UNUSED187, 596 | ITEM_ADDSKILL_TAB, 597 | UNUSED189, 598 | UNUSED190, 599 | UNUSED191, 600 | UNUSED192, 601 | UNUSED193, 602 | ITEM_NUMSOCKETS, 603 | ITEM_SKILLONATTACK, 604 | ITEM_SKILLONKILL, 605 | ITEM_SKILLONDEATH, 606 | ITEM_SKILLONHIT, 607 | ITEM_SKILLONLEVELUP, 608 | UNUSED200, 609 | ITEM_SKILLONGETHIT, 610 | UNUSED202, 611 | UNUSED203, 612 | ITEM_CHARGED_SKILL, 613 | UNUSED204, 614 | UNUSED205, 615 | UNUSED206, 616 | UNUSED207, 617 | UNUSED208, 618 | UNUSED209, 619 | UNUSED210, 620 | UNUSED211, 621 | UNUSED212, 622 | ITEM_ARMOR_PERLEVEL, 623 | ITEM_ARMORPERCENT_PERLEVEL, 624 | ITEM_HP_PERLEVEL, 625 | ITEM_MANA_PERLEVEL, 626 | ITEM_MAXDAMAGE_PERLEVEL, 627 | ITEM_MAXDAMAGE_PERCENT_PERLEVEL, 628 | ITEM_STRENGTH_PERLEVEL, 629 | ITEM_DEXTERITY_PERLEVEL, 630 | ITEM_ENERGY_PERLEVEL, 631 | ITEM_VITALITY_PERLEVEL, 632 | ITEM_TOHIT_PERLEVEL, 633 | ITEM_TOHITPERCENT_PERLEVEL, 634 | ITEM_COLD_DAMAGEMAX_PERLEVEL, 635 | ITEM_FIRE_DAMAGEMAX_PERLEVEL, 636 | ITEM_LTNG_DAMAGEMAX_PERLEVEL, 637 | ITEM_POIS_DAMAGEMAX_PERLEVEL, 638 | ITEM_RESIST_COLD_PERLEVEL, 639 | ITEM_RESIST_FIRE_PERLEVEL, 640 | ITEM_RESIST_LTNG_PERLEVEL, 641 | ITEM_RESIST_POIS_PERLEVEL, 642 | ITEM_ABSORB_COLD_PERLEVEL, 643 | ITEM_ABSORB_FIRE_PERLEVEL, 644 | ITEM_ABSORB_LTNG_PERLEVEL, 645 | ITEM_ABSORB_POIS_PERLEVEL, 646 | ITEM_THORNS_PERLEVEL, 647 | ITEM_FIND_GOLD_PERLEVEL, 648 | ITEM_FIND_MAGIC_PERLEVEL, 649 | ITEM_REGENSTAMINA_PERLEVEL, 650 | ITEM_STAMINA_PERLEVEL, 651 | ITEM_DAMAGE_DEMON_PERLEVEL, 652 | ITEM_DAMAGE_UNDEAD_PERLEVEL, 653 | ITEM_TOHIT_DEMON_PERLEVEL, 654 | ITEM_TOHIT_UNDEAD_PERLEVEL, 655 | ITEM_CRUSHINGBLOW_PERLEVEL, 656 | ITEM_OPENWOUNDS_PERLEVEL, 657 | ITEM_KICK_DAMAGE_PERLEVEL, 658 | ITEM_DEADLYSTRIKE_PERLEVEL, 659 | ITEM_FIND_GEMS_PERLEVEL, 660 | ITEM_REPLENISH_DURABILITY, 661 | ITEM_REPLENISH_QUANTITY, 662 | ITEM_EXTRA_STACK, 663 | ITEM_FIND_ITEM, 664 | ITEM_SLASH_DAMAGE, 665 | ITEM_SLASH_DAMAGE_PERCENT, 666 | ITEM_CRUSH_DAMAGE, 667 | ITEM_CRUSH_DAMAGE_PERCENT, 668 | ITEM_THRUST_DAMAGE, 669 | ITEM_THRUST_DAMAGE_PERCENT, 670 | ITEM_ABSORB_SLASH, 671 | ITEM_ABSORB_CRUSH, 672 | ITEM_ABSORB_THRUST, 673 | ITEM_ABSORB_SLASH_PERCENT, 674 | ITEM_ABSORB_CRUSH_PERCENT, 675 | ITEM_ABSORB_THRUST_PERCENT, 676 | ITEM_ARMOR_BYTIME, 677 | ITEM_ARMORPERCENT_BYTIME, 678 | ITEM_HP_BYTIME, 679 | ITEM_MANA_BYTIME, 680 | ITEM_MAXDAMAGE_BYTIME, 681 | ITEM_MAXDAMAGE_PERCENT_BYTIME, 682 | ITEM_STRENGTH_BYTIME, 683 | ITEM_DEXTERITY_BYTIME, 684 | ITEM_ENERGY_BYTIME, 685 | ITEM_VITALITY_BYTIME, 686 | ITEM_TOHIT_BYTIME, 687 | ITEM_TOHITPERCENT_BYTIME, 688 | ITEM_COLD_DAMAGEMAX_BYTIME, 689 | ITEM_FIRE_DAMAGEMAX_BYTIME, 690 | ITEM_LTNG_DAMAGEMAX_BYTIME, 691 | ITEM_POIS_DAMAGEMAX_BYTIME, 692 | ITEM_RESIST_COLD_BYTIME, 693 | ITEM_RESIST_FIRE_BYTIME, 694 | ITEM_RESIST_LTNG_BYTIME, 695 | ITEM_RESIST_POIS_BYTIME, 696 | ITEM_ABSORB_COLD_BYTIME, 697 | ITEM_ABSORB_FIRE_BYTIME, 698 | ITEM_ABSORB_LTNG_BYTIME, 699 | ITEM_ABSORB_POIS_BYTIME, 700 | ITEM_FIND_GOLD_BYTIME, 701 | ITEM_FIND_MAGIC_BYTIME, 702 | ITEM_REGENSTAMINA_BYTIME, 703 | ITEM_STAMINA_BYTIME, 704 | ITEM_DAMAGE_DEMON_BYTIME, 705 | ITEM_DAMAGE_UNDEAD_BYTIME, 706 | ITEM_TOHIT_DEMON_BYTIME, 707 | ITEM_TOHIT_UNDEAD_BYTIME, 708 | ITEM_CRUSHINGBLOW_BYTIME, 709 | ITEM_OPENWOUNDS_BYTIME, 710 | ITEM_KICK_DAMAGE_BYTIME, 711 | ITEM_DEADLYSTRIKE_BYTIME, 712 | ITEM_FIND_GEMS_BYTIME, 713 | ITEM_PIERCE_COLD, 714 | ITEM_PIERCE_FIRE, 715 | ITEM_PIERCE_LTNG, 716 | ITEM_PIERCE_POIS, 717 | ITEM_DAMAGE_VS_MONSTER, 718 | ITEM_DAMAGE_PERCENT_VS_MONSTER, 719 | ITEM_TOHIT_VS_MONSTER, 720 | ITEM_TOHIT_PERCENT_VS_MONSTER, 721 | ITEM_AC_VS_MONSTER, 722 | ITEM_AC_PERCENT_VS_MONSTER, 723 | FIRELENGTH, 724 | BURNINGMIN, 725 | BURNINGMAX, 726 | PROGRESSIVE_DAMAGE, 727 | PROGRESSIVE_STEAL, 728 | PROGRESSIVE_OTHER, 729 | PROGRESSIVE_FIRE, 730 | PROGRESSIVE_COLD, 731 | PROGRESSIVE_LIGHTNING, 732 | ITEM_EXTRA_CHARGES, 733 | PROGRESSIVE_TOHIT, 734 | POISON_COUNT, 735 | DAMAGE_FRAMERATE, 736 | PIERCE_IDX, 737 | PASSIVE_FIRE_MASTERY, 738 | PASSIVE_LTNG_MASTERY, 739 | PASSIVE_COLD_MASTERY, 740 | PASSIVE_POIS_MASTERY, 741 | PASSIVE_FIRE_PIERCE, 742 | PASSIVE_LTNG_PIERCE, 743 | PASSIVE_COLD_PIERCE, 744 | PASSIVE_POIS_PIERCE, 745 | PASSIVE_CRITICAL_STRIKE, 746 | PASSIVE_DODGE, 747 | PASSIVE_AVOID, 748 | PASSIVE_EVADE, 749 | PASSIVE_WARMTH, 750 | PASSIVE_MASTERY_MELEE_TH, 751 | PASSIVE_MASTERY_MELEE_DMG, 752 | PASSIVE_MASTERY_MELEE_CRIT, 753 | PASSIVE_MASTERY_THROW_TH, 754 | PASSIVE_MASTERY_THROW_DMG, 755 | PASSIVE_MASTERY_THROW_CRIT, 756 | PASSIVE_WEAPONBLOCK, 757 | PASSIVE_SUMMON_RESIST, 758 | MODIFIERLIST_SKILL, 759 | MODIFIERLIST_LEVEL, 760 | LAST_SENT_HP_PCT, 761 | SOURCE_UNIT_TYPE, 762 | SOURCE_UNIT_ID, 763 | SHORTPARAM1, 764 | QUESTITEMDIFFICULTY, 765 | PASSIVE_MAG_MASTERY, 766 | PASSIVE_MAG_PIERCE, 767 | }; -------------------------------------------------------------------------------- /D2Ptrs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "Utils.h" 6 | #include "D2Tables.h" 7 | #include "D2Structs.h" 8 | #include "Globals.h" 9 | 10 | static const uint32_t GAME_EXE = (uint32_t)GetModuleHandle(NULL); 11 | static const uint32_t DLLBASE_D2CLIENT = (uint32_t)LoadLibraryA("D2Client.dll"); 12 | static const uint32_t DLLBASE_D2CMP = (uint32_t)LoadLibraryA("D2CMP.dll"); 13 | static const uint32_t DLLBASE_D2COMMON = (uint32_t)LoadLibraryA("D2Common.dll"); 14 | static const uint32_t DLLBASE_D2GAME = (uint32_t)LoadLibraryA("D2Game.dll"); 15 | static const uint32_t DLLBASE_D2GFX = (uint32_t)LoadLibraryA("D2Gfx.dll"); 16 | static const uint32_t DLLBASE_D2LANG = (uint32_t)LoadLibraryA("D2Lang.dll"); 17 | static const uint32_t DLLBASE_D2LAUNCH = (uint32_t)LoadLibraryA("D2Launch.dll"); 18 | static const uint32_t DLLBASE_D2MCPCLIENT = (uint32_t)LoadLibraryA("D2MCPClient.dll"); 19 | static const uint32_t DLLBASE_D2MULTI = (uint32_t)LoadLibraryA("D2Multi.dll"); 20 | static const uint32_t DLLBASE_D2NET = (uint32_t)LoadLibraryA("D2Net.dll"); 21 | static const uint32_t DLLBASE_D2SOUND = (uint32_t)LoadLibraryA("D2Sound.dll"); 22 | static const uint32_t DLLBASE_D2WIN = (uint32_t)LoadLibraryA("D2Win.dll"); 23 | static const uint32_t DLLBASE_FOG = (uint32_t)LoadLibraryA("Fog.dll"); 24 | static const uint32_t DLLBASE_STORM = (uint32_t)LoadLibraryA("Storm.dll"); 25 | 26 | #define D2FUNC(DLL, N, R, C, A, O) typedef R (C* DLL##_##N##_t) A; __declspec(selectany) extern DLL##_##N##_t DLL##_##N = (DLL##_##N##_t)GetDllOffset((GetGameVersion() > D2Version::V113c ? GAME_EXE : DLLBASE_##DLL), O); /// 27 | #define D2PTR(DLL, N, T, O) __declspec(selectany) extern T* DLL##_##N = (T*)GetDllOffset((GetGameVersion() > D2Version::V113c ? GAME_EXE : DLLBASE_##DLL), O); 28 | 29 | #define F2(DLL, N, R, C, A, O110f, O113c, O114d) D2FUNC(DLL, N, R, C, A, (GetGameVersion() == D2Version::V114d ? O114d : (GetGameVersion() == D2Version::V113c ? O113c : O110f))) 30 | #define P2(DLL, N, T, O110f, O113c, O114d) D2PTR(DLL, N, T, (GetGameVersion() == D2Version::V114d ? O114d : (GetGameVersion() == D2Version::V113c ? O113c : O110f))) 31 | 32 | //Hooks for item filter 33 | P2(D2CLIENT, fpGetItemDescPatch, void, 0x531C9, 0x5612C, 0xE64CE); 34 | P2(D2WIN, callDrawAltDownItemRectPatch, void, 0xB160, 0x12F12, 0x102436); //Call to D2GFX_DrawSolidRectEx(uint32_t nXStart, uint32_t nYStart, uint32_t nXEnd, uint32_t nYEnd, uint8_t nPaletteIndex, DrawMode eDrawMode) 35 | P2(D2CLIENT, callDrawAltDownItemRectRet, void, 0x6A3F0, 0x59473, 0xA72F4); 36 | //1.13c $+59473 |. A1 0078BC6F |MOV EAX,DWORD PTR DS:[6FBC7800] 37 | //1.14d $ + A72F4 | > \5F POP EDI 38 | 39 | P2(D2CLIENT, fpGroundHoverUnit, void, 0x15A20, 0x51A80, 0x67A10); //check 1.14d??? 40 | P2(D2WIN, callDrawHoverItemRectPatch, void, 0xA89E, 0x1357B, 0x1031C1); //Call to D2GFX_DrawSolidRectEx(uint32_t nXStart, uint32_t nYStart, uint32_t nXEnd, uint32_t nYEnd, uint8_t nPaletteIndex, DrawMode eDrawMode) 41 | 42 | //in 110f D2Client.0x6FAE1710_DrawItemBackground(D2InventoryGridInfoStrc* pInventoryGridInfo, int nX, int nY, BYTE nItemWidth, BYTE nItemHeight, DWORD nColorIndex) 43 | //uses strange wrapper insted of digerct D2GFX_DrawSolidRectEx call 44 | //.text:6FAE179C call sub_6FB5B0F0 45 | //int __fastcall sub_6FB5B0F0(int a1, int a2, int a3, int a4, int a5, int a6) 46 | //{ 47 | // return D2gfx_10055(a1, a2, a1 + a3, a2 + a4, a5, a6); 48 | //} 49 | P2(D2CLIENT, callDrawInventoryItemRectPatch, void, 0x4179C, 0x8C678, 0x83A3F); //Call to D2GFX_DrawSolidRectEx(uint32_t nXStart, uint32_t nYStart, uint32_t nXEnd, uint32_t nYEnd, uint8_t nPaletteIndex, DrawMode eDrawMode) 50 | 51 | //from plugy 52 | P2(D2CLIENT, callCommand, void, 0x32BAB, 0x70AB5, 0x7C519); 53 | 54 | //113c 55 | //6FB166E1 | . 8B86 C8000000 MOV EAX, DWORD PTR DS : [ESI + 0C8] 56 | //6FB166E7 | .C1E8 12 SHR EAX, 12 57 | //6FB166EA | . 83E0 01 AND EAX, 00000001 58 | 59 | //6FB1B363 | . 8B97 C8000000 MOV EDX, DWORD PTR DS : [EDI + 0C8] 60 | //6FB1B369 | .C1EA 12 SHR EDX, 12 61 | //6FB1B36C | . 83E2 01 AND EDX, 00000001 62 | 63 | //110f 64 | // .text:6FB4DB5B mov eax, [esi+0C8h] 65 | // .text:6FB4DB61 shr eax, 12h 66 | // .text:6FB4DB64 and eax, 1 67 | // 68 | // .text:6FB5BADD mov eax, [edi+0C8h] 69 | // .text:6FB5BAE3 shr eax, 12h 70 | // .text:6FB5BAE6 and eax, 1 71 | // 72 | //Check NODRAW bit 73 | 74 | P2(D2CLIENT, checkUnitNoDrawPatch_1, void, 0xADB5B, 0x666E1, 0xDC7C8); 75 | P2(D2CLIENT, checkUnitNoDrawPatch_2, void, 0xBBADD, 0x6B363, 0x703ED); 76 | 77 | 78 | //Pointers 79 | P2(D2COMMON, gpDataTables, DataTables*, 0x96A20, -11170, 0x344304); //need to check for 110f 80 | P2(D2COMMON, ItemDataTbl, ItemDataTbl, 0xAA2E0, 0x9FB94, 0x4426EC); 81 | P2(D2CLIENT, GetHoverItem, Unit*, 0x1158F0, 0x11BC38, 0x3BCBF4); // 1.13c: 6FB44DBD 1.14d: 004876BE 82 | P2(D2CLIENT, AutomapOn, BOOL, 0x11A6D0, 0xFADA8, 0x3A27E8); 83 | //Can't say I understand these 84 | P2(D2CLIENT, Divisor, int, 0xD7BC0, 0xF16B0, 0x311254); 85 | P2(D2CLIENT, Offset, POINT, 0x1119C8, 0x11C1F8, 0x3A5198); //unshure for 110f 86 | P2(D2CLIENT, ResolutionX, uint32_t, 0xD40EC, 0xDBC48, 0x31146C); 87 | 88 | P2(D2CLIENT, UIVar_Game, BOOL, 0x11A6A8, 0xFAD80, 0x3A27C0); 89 | 90 | P2(D2CLIENT, ServerSideUnitHashTables, UnitHashTable, 0x11B600, 0x10A608, 0x3A5E70); // Updated 1.14d //007A5E70-BASE 91 | P2(D2CLIENT, ClientSideUnitHashTables, UnitHashTable, 0x11AA00, 0x109A08, 0x3A5270); // Updated 1.14d //007A5270-BASE 92 | 93 | /* 94 | FUNCPTR(D2COMMON, MapToAbsScreen, void __stdcall, (long* pX, long* pY), 0x243260) // Updated 1.14d //00643260-BASE 95 | FUNCPTR(D2COMMON, AbsScreenToMap, void __stdcall, (long* ptMouseX, long* ptMouseY), 0x243510) // Updated 1.14d //00643510-BASE 96 | */ 97 | 98 | //Functions 99 | //1.14d 004B3870 near here. 100 | F2(D2COMMON, ITEMS_GetTransactionCost, int, __stdcall, (Unit* pPlayer, Unit* pItem, int nDifficulty, BitBuffer* pQuestFlags, int nVendorId, int nTransactionType), -10775 , -10107, 0x22FDC0); 101 | F2(D2COMMON, ITEMS_CheckItemTypeId, BOOL, __stdcall, (Unit* pItem, ItemType nItemType), -10731, -10744, 0x229BB0); 102 | 103 | //TODO FIND IN 1.14 104 | F2(D2COMMON, STATLIST_GetStatListFromUnitStateOrFlag, StatListEx*, __stdcall, (const Unit* pUnit, int32_t nState, int32_t nFlag), -10483, -10930, 0); 105 | F2(D2COMMON, STATLIST_GetStatValue, int32_t, __stdcall, (const StatListEx* pStatList, Stat nStatId, uint16_t nLayer), -10466, -10680, 0); 106 | F2(D2COMMON, STATLIST_GetUnitStatUnsigned, uint32_t, __stdcall, (const Unit* pUnit, Stat nStatId, uint16_t nLayer), -10520, -10973, 0x225480); 107 | //todo check 1.14d 108 | F2(D2COMMON, DATATBLS_LoadAllTxts, void, __stdcall, (void* pMemPool, int a2, int a3), -10576, -10943, 0x219300); 109 | F2(D2COMMON, UNITS_FreeUnit, void, __stdcall, (Unit* pUnit), -11260, -10124, 0x220300); 110 | F2(D2CLIENT, DrawGameUI, void, __stdcall, (), 0x5E650, 0x285C0, 0x96B10); 111 | 112 | //110f 0xBA720 looks way more complex that 0x6CC00 113c, need to be checked 113 | F2(D2CLIENT, UNITDRAW_DrawUnit, BOOL, __fastcall, (Unit* pUnit, uint32_t dwColorTint, int nXpos, int nYpos, BOOL bFade, BOOL bDrawOverlays), 0xBA720, 0x6CC00, 0x71EC0); 114 | 115 | F2(D2CLIENT, ItemActionWorld, void, __fastcall, (uint8_t* pBitstream), 0x14420, 0xAE080, 0x5EB10); 116 | F2(D2CLIENT, ItemActionOwned, void, __fastcall, (uint8_t* pBitstream), 0x14590, 0xAE870, 0x5EC70); 117 | 118 | F2(D2CLIENT, AUTOMAP_Draw, void, __stdcall, (void), 0x2EF10, 0x5FFE0, 0x59E90); 119 | F2(D2CLIENT, GetQuestFlags, BitBuffer*, __stdcall, (void), 0x5BD20, 0x45A00, 0xB32D0); 120 | 121 | //for 110f propably __fastcall 122 | F2(D2CLIENT, GetDifficulty, uint8_t, __stdcall, (void), 0xC090, 0x41930, 0x4DCD0); 123 | 124 | F2(D2CLIENT, GetPlayerUnit, Unit*, __stdcall, (void), 0x883D0, 0xA4D60, 0x63DD0); 125 | 126 | //seems like GetItemName in 110f is __fastcall as well as in 114d 127 | F2(D2CLIENT, GetItemName, BOOL, __stdcall, (Unit* pItem, wchar_t* wBuffer, uint32_t dwSize), 0x0, 0x914F0, 0x0); 128 | F2(D2CLIENT, GetItemName_114d, BOOL, __fastcall, (Unit* pItem, wchar_t* wBuffer, uint32_t dwSize), 0x3D360, 0x0, 0x8C060); 129 | 130 | 131 | F2(D2CLIENT, GetAutomapSize, uint32_t, __stdcall, (void), 0xB4C50, 0x5F080, 0x5A710); //for 110f - 6FB54C50(B4C50) or 6FB54CB0(B4CBO) - need to check in debuger 132 | 133 | F2(D2CLIENT, FindClientSideUnit, Unit*, __fastcall, (uint32_t dwUnitId, UnitType dwType), 0x869F0, 0xA5B20, 0x63990); 134 | F2(D2CLIENT, FindServerSideUnit, Unit*, __fastcall, (uint32_t dwUnitId, UnitType dwType), 0x86A60, 0xA5B40, 0x639B0); 135 | 136 | /* 137 | atleast in 110f we have two simmilar functions 138 | D2ClientMsgTop 07C600 6FB1C600 139 | D2ClientMsgBottom 07C950 6FB1C950 140 | both are __fastcall 141 | assuming we use D2ClientMsgTop 142 | */ 143 | 144 | F2(D2CLIENT, PrintGameString, void, __stdcall, (const wchar_t* wMessage, TextColor eColor), 0x0, 0x7D850, 0x0); 145 | F2(D2CLIENT, PrintGameStringe_114d, void, __fastcall, (const wchar_t* wMessage, TextColor eColor), 0x7C600, 0x0, 0x9E3A0); 146 | 147 | F2(D2WIN, WndProc, LRESULT, __stdcall, (HWND hWnd, int msg, WPARAM wParam, LPARAM lParam), 0xD9B0, 0x17740, 0xF9A80); 148 | F2(D2WIN, SetTextFont, Font, __fastcall, (Font dwSize), -10127, -10184, 0x102EF0); 149 | 150 | /* 151 | There are two functions to draw text in 110F 152 | D2WIN_10117_DrawText (wchar_t* pString, int nXpos, int nYpos, int nColour, int nTransTbl) 153 | D2WIN_10129_DrawHoverPopUp (wchar_t* pString, int nXpos, int nYpos, int nColour, int dwTextAlign) 154 | they matches to 110C to 155 | D2WIN_10150_DrawText (wchar_t* pString, int nXpos, int nYpos, int nColour, int nTransTbl) 156 | D2WIN_10185_DrawHoverPopUp (wchar_t* pString, int nXpos, int nYpos, int nColour, int dwTextAlign) 157 | Need to cross-check 158 | */ 159 | 160 | F2(D2WIN, D2DrawText, void, __fastcall, (const wchar_t* wStr, uint32_t nXpos, uint32_t nYpos, TextColor eColor, uint32_t dwCentered), -10117 , -10150, 0x102320); 161 | 162 | //fix 1.14d 163 | F2(D2WIN, GetTextPixelWidthFileNo, uint32_t, __fastcall, (const wchar_t* wStr, uint32_t* dwWidth, uint32_t* dwFileNo), 0x0, -10177, 0x0); //Not sure about this two but looks like 110f have same f as 114d 164 | F2(D2WIN, GetTextPixelWidth, uint32_t, __fastcall, (const wchar_t* wStr), -10121, 0x0, 0x1017D0); 165 | 166 | //006C9030 / . 55 PUSH EBP 167 | //$ + 2C9030 / . 55 PUSH EBP 168 | F2(D2GFX, DrawRect, void, __stdcall, (RECT* pRect, uint8_t nPaletteIndex), -10052, -10035, 0xF62A0); 169 | //__fastcall for 114d: 0x6EFD0 170 | F2(D2GFX, DrawSolidRectEx, void, __stdcall, (uint32_t nXStart, uint32_t nYStart, uint32_t nXEnd, uint32_t nYEnd, uint8_t nPaletteIndex, DrawMode eDrawMode), -10055, -10014, 0xF6300); 171 | 172 | F2(D2LANG, GetStringFromTblIndex, wchar_t*, __fastcall, (uint16_t nTblIndex), -10004, -10003, 0x124A30); -------------------------------------------------------------------------------- /D2Structs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "D2Constants.h" 6 | 7 | struct PlayerData; 8 | struct MonsterData; 9 | struct ObjectData; 10 | struct MissleData; 11 | struct DrlgAct; 12 | struct ItemPath; 13 | struct DynamicPath; 14 | struct StaticPath; 15 | struct AnimSeq; 16 | struct AnimData; 17 | struct GfxData; 18 | struct StatListEx; 19 | struct Corpse; 20 | struct InventoryGrid; 21 | struct InventoryNode; 22 | struct Inventory; 23 | struct Light; 24 | struct QuestRecord; 25 | struct TimerArg; 26 | struct Game; 27 | struct EventList; 28 | struct HoverText; 29 | struct SkillList; 30 | struct Combat; 31 | struct QuestSrv; 32 | struct QuestClt; 33 | struct ParticleStream; 34 | struct Timer; 35 | struct Unit; 36 | struct DrlgEnvironment; 37 | struct Room; 38 | struct RoomEx; 39 | struct RoomCollisionGrid; 40 | struct Client; 41 | struct TimerQueue; 42 | struct InactiveUnitList; 43 | struct MonsterRegion; 44 | struct ObjectControl; 45 | struct QuestInfo; 46 | struct TargetNode; 47 | struct NpcControl; 48 | struct Arena; 49 | struct QuestChain; 50 | struct GfxLight; 51 | 52 | enum class InventoryPage : uint8_t { 53 | INVENTORY = 0x00, 54 | EQUIP = 0x01, 55 | BELT = 0x02, 56 | CUBE = 0x03, 57 | STASH = 0x04, 58 | UNK = 0xFF 59 | }; 60 | 61 | struct BitBuffer 62 | { 63 | uint8_t* pBuffer; //0x00 64 | int32_t nBits; //0x04 65 | int32_t nPos; //0x08 66 | int32_t nPosBits; //0x0C 67 | BOOL bFull; //0x10 68 | }; 69 | 70 | struct Inventory 71 | { 72 | uint32_t dwSignature; //0x00 73 | void* pMemPool; //0x04 74 | Unit* pOwner; //0x08 75 | Unit* pFirstItem; //0x0C 76 | Unit* pLastItem; //0x10 77 | InventoryGrid* pGrids; //0x14 78 | int32_t nGridCount; //0x18 79 | uint32_t dwLeftItemGUID; //0x1C 80 | Unit* pCursorItem; //0x20 81 | uint32_t dwOwnerId; //0x24 82 | uint32_t dwItemCount; //0x28 83 | InventoryNode* pFirstNode; //0x2C 84 | InventoryNode* pLastNode; //0x30 85 | Corpse* pFirstCorpse; //0x34 86 | Corpse* pLastCorpse; //0x38 87 | int32_t nCorpseCount; //0x3C 88 | }; 89 | 90 | 91 | struct ItemData 92 | { 93 | ItemRarity dwRarity; //0x00 94 | uint32_t dwLowSeed; //0x04 95 | uint32_t dwHighSeed; //0x08 96 | uint32_t dwOwnerGUID; //0x0C 97 | uint32_t dwInitSeed; //0x10 98 | uint32_t dwCommandFlags; //0x14 99 | uint32_t dwItemFlags; //0x18 100 | uint32_t unk0x1C[2]; //0x1C 101 | uint32_t dwActionStamp; //0x24 102 | uint32_t dwFileIndex; //0x28 103 | uint32_t dwItemLevel; //0x2C 104 | uint16_t wItemFormat; //0x30 105 | uint16_t wRarePrefix; //0x32 106 | uint16_t wRareSuffix; //0x34 107 | uint16_t wAutoPrefix; //0x36 108 | uint16_t wMagicPrefix[3]; //0x38 109 | uint16_t wMagicSuffix[3]; //0x3E 110 | uint8_t nBodyLoc; //0x44 111 | InventoryPage nInvPage; //0x45 112 | uint8_t unk0x46[2]; //0x46 113 | uint8_t nEarLvl; //0x48 114 | uint8_t nInvGfxIdx; //0x49 115 | char szPlayerName[16]; //0x4A 116 | uint8_t unk0x5A[2]; //0x5A 117 | Inventory* pNodeInv; //0x5C 118 | uint32_t unk0x60; //0x60 119 | Unit* pNextItem; //0x64 120 | uint8_t nNodePos; //0x68 121 | uint8_t nNodePosOther; //0x69 122 | }; 123 | 124 | #pragma pack(push, 1) 125 | 126 | struct PathPoint 127 | { 128 | uint16_t X; 129 | uint16_t Y; 130 | }; 131 | 132 | struct DynamicPath 133 | { 134 | union 135 | { 136 | struct 137 | { 138 | uint16_t wOffsetX; //0x00 139 | uint16_t wPosX; //0x02 140 | uint16_t wOffsetY; //0x04 141 | uint16_t wPosY; //0x06 142 | }; 143 | 144 | struct 145 | { 146 | uint32_t dwPrecisionX; //0x00 147 | uint32_t dwPrecisionY; //0x04 148 | }; 149 | }; 150 | uint32_t dwTargetX; //0x08 151 | uint32_t dwTargetY; //0x0C 152 | uint16_t xSP1; //0x10 153 | uint16_t ySP1; //0x12 154 | uint16_t xSP2; //0x14 155 | uint16_t ySP2; //0x16 156 | uint16_t xSP3; //0x18 157 | uint16_t ySP3; //0x1A 158 | Room* pRoom; //0x1C 159 | Room* pRoomNext; //0x20 160 | int32_t unk0x24; //0x24 161 | int32_t dwPathPoints; //0x28 162 | void* unk0x2C; //0x2C 163 | Unit* pUnit; //0x30 164 | uint32_t dwFlags; //0x34 165 | uint32_t unk0x38; //0x38 166 | uint32_t dwPathType; //0x3C 167 | uint32_t dwPrevPathType; //0x40 168 | uint32_t dwUnitSize; //0x44 169 | uint32_t dwCollisionPattern; //0x48 170 | uint32_t dwCollisionType; //0x4C 171 | uint32_t unk0x50; //0x50 172 | uint16_t unk0x54; //0x54 173 | uint16_t unk0x56; //0x56 174 | Unit* pTargetUnit; //0x58 175 | uint32_t dwTargetType; //0x5C 176 | uint32_t dwTargetId; //0x60 177 | uint8_t nDirection; //0x64 178 | uint8_t nNewDirection; //0x65 179 | uint8_t nDiffDirection; //0x66 180 | uint8_t unk0x67; //0x67 181 | uint8_t unk0x68[10]; //0x68 182 | int32_t unk0x72; //0x72 183 | int32_t unk0x76; //0x76 184 | char unk0x7A[2]; //0x7A 185 | uint32_t dwVelocity; //0x7C 186 | uint32_t unk0x80; //0x80 187 | uint32_t dwVelocity2; //0x84 188 | uint32_t unk0x88[2]; //0x88 189 | uint8_t nDist; //0x90 190 | uint8_t nDistMax; //0x91 191 | uint8_t unk0x92; //0x92 192 | uint8_t nStepNum; //0x93 193 | uint8_t nDistance; //0x94 194 | char unk0x95[3]; //0x95 195 | //uint32_t dwPathOffset; //0x98 196 | PathPoint PathPoints[7]; //0x98 197 | uint32_t unk0xB8[72]; //0xB8 198 | int32_t unk0x1D4; //0x1D4 199 | PathPoint unk0x1D8[7]; //0x1D8 200 | char unk0x1DC[12]; //0x1DC 201 | }; 202 | 203 | struct PathInfo 204 | { 205 | PathPoint pStartCoord; //0x00 206 | PathPoint field_4; //0x04 207 | Room* pRoom; //0x08 208 | Room* field_C; //0x0C 209 | int32_t field_10; //0x10 210 | uint8_t field_14; //0x14 211 | uint8_t field_15; //0x15 212 | uint16_t field_16; //0x16 213 | int32_t nDistMax; //0x18 214 | uint8_t field_1C; //0x1C 215 | uint8_t field_1D; //0x1D 216 | uint16_t field_1E; //0x1E 217 | int32_t nPathType; //0x20 218 | int32_t nUnitSize; //0x24 219 | int32_t nCollisionPattern; //0x28 220 | int32_t nCollisionType; //0x2C 221 | union 222 | { 223 | DynamicPath* pDynamicPath; //0x30 - not sure yet 224 | PathInfo* pNext; //0x30 225 | }; 226 | }; 227 | 228 | struct StaticPath 229 | { 230 | Room* pRoom; //0x00 231 | int32_t nTargetX; //0x04 232 | int32_t nTargetY; //0x08 233 | int32_t nXPos; //0x0C 234 | int32_t nYPos; //0x10 235 | uint32_t unk0x14[2]; //0x14 236 | uint8_t nDirection; //0x1C 237 | uint8_t unk0x1D[3]; //0x1D 238 | }; 239 | 240 | 241 | struct Seed { 242 | union 243 | { 244 | struct 245 | { 246 | int32_t nLowSeed; //0x00 247 | int32_t nHighSeed; //0x04 248 | }; 249 | uint64_t lSeed; //0x00 250 | }; 251 | }; 252 | 253 | struct DrlgCoords { 254 | int32_t nSubtileX; //0x00 255 | int32_t nSubtileY; //0x04 256 | int32_t nSubtileWidth; //0x08 257 | int32_t nSubtileHeight; //0x0C 258 | int32_t nTileXPos; //0x10 259 | int32_t nTileYPos; //0x14 260 | int32_t nTileWidth; //0x18 261 | int32_t nTileHeight; //0x1C 262 | }; 263 | 264 | struct Room 265 | { 266 | Room** pRoomsNear; //0x00 267 | uint32_t _1; //0x04 268 | void* _1s; //0x08 269 | uint32_t _1b; //0x0C 270 | RoomEx* pRoomEx; //0x10 271 | uint32_t _2[2]; //0x14 272 | Unit** pUnitChanged; //0x1C 273 | RoomCollisionGrid* Coll; //0x20 274 | uint32_t dwRoomsNear; //0x24 275 | uint32_t nPlayerUnits; //0x28 276 | DrlgAct* pAct; //0x2C 277 | uint32_t _4; //0x30 278 | uint32_t nUnknown; //0x34 279 | uint32_t _5[4]; //0x38 280 | Client** pClients; //0x48 281 | DrlgCoords pCoords; //0x4C 282 | Seed pSeed; //0x6C 283 | Unit* pUnitFirst; //0x74 284 | uint32_t nNumClients; //0x78 285 | Room* pRoomNext; //0x7C 286 | }; 287 | 288 | struct DrlgAct 289 | { 290 | uint32_t _1a; 291 | DrlgEnvironment* pEnviroment; //0x04 292 | uint32_t nTownLevel; //0x08 293 | uint32_t dwMapSeed; //0x0C 294 | Room* pRoom; //0x10 295 | /* 296 | uint32_t dwAct; //0x14 297 | TileData pTileData; //0x18 298 | ActMisc* pMisc; //0x48 299 | uint32_t _4; //0x4C 300 | uint32_t _5; //0x50 301 | uint32_t _6; //0x54 302 | uint32_t _7; //0x58 303 | void* pMemoryPool; //0x5C 304 | */ 305 | }; 306 | 307 | struct Game 308 | { 309 | uint32_t __0000[4]; //0x00 310 | Game* pNext; //0x10 311 | uint32_t __0014; //0x14 312 | LPCRITICAL_SECTION lpCriticalSection; //0x18 313 | void* pMemoryPool; //0x1C 314 | uint32_t __0020[2]; //0x20 315 | uint16_t nServerToken; //0x28 316 | char szGameName[16]; //0x2A 317 | char szGamePassword[16]; //0x3A 318 | char szGameDesc[32]; //0x4A 319 | uint8_t nGameType; //0x6A 320 | uint16_t __006B; //0x6B 321 | uint8_t nDifficulty; //0x6D 322 | uint16_t __006E; //0x6E 323 | BOOL bExpansion; //0x70 324 | uint32_t dwGameType; //0x74 325 | uint16_t wItemFormat; //0x78 326 | uint16_t unk0x7A; //0x7A 327 | uint32_t dwInitSeed; //0x7C 328 | uint32_t dwObjSeed; //0x80 329 | uint32_t InitSeed; //0x84 330 | Client* pClientList; //0x88 331 | uint32_t nClients; //0x8C 332 | uint32_t dwSpawnedPlayers; //0x90 333 | uint32_t dwSpawnedMonsters; //0x94 334 | uint32_t dwSpawnedObjects; //0x98 335 | uint32_t dwSpawnedMissiles; //0x9C 336 | uint32_t dwSpawnedItems; //0xA0 337 | uint32_t dwSpawnedTiles; //0xA4 338 | uint32_t dwGameFrame; //0xA8 339 | uint32_t unk0xAC[3]; //0xAC 340 | TimerQueue* pTimerQueue; //0xB8 341 | DrlgAct* pAct[5]; //0xBC 342 | Seed pGameSeed; //0xD0 343 | InactiveUnitList* pInactiveUnitList[5];//0xD8 344 | uint32_t dwMonSeed; //0xEC 345 | MonsterRegion* pMonReg[1024]; //0xF0 346 | ObjectControl* pObjectControl; //0x10F0 347 | QuestInfo* pQuestControl; //0x10F4 348 | TargetNode* pUnitNodes[10]; //0x10F8 349 | Unit* pUnitList[5][128]; //0x1120 350 | void* pTileList; //0x1B20 351 | uint32_t dwUniqueFlags[128]; //0x1B24 352 | NpcControl* pNpcControl; //0x1D24 353 | Arena* pArenaCtrl; //0x1D28 354 | void* pPartyControl; //0x1D2C 355 | uint8_t nBossFlagList[64]; //0x1D30 356 | uint32_t dwMonModeData[17]; //0x1D70 357 | uint32_t nMonModeData; //0x1DB4 358 | uint32_t unk0x1DB8[3]; //0x1DB8 359 | uint32_t nSyncTimer; //0x1DC4 360 | }; 361 | 362 | struct UnitHashTable { 363 | Unit* table[128]; 364 | }; 365 | 366 | struct Unit 367 | { 368 | UnitType dwUnitType; //0x00 369 | union //0x04 370 | { 371 | uint32_t dwLineId; // the id in the txt file 372 | PlayerClass dwClassId; 373 | }; 374 | void* pMemoryPool; //0x08 375 | uint32_t dwUnitId; //0x0C 376 | union { //0x10 377 | uint32_t dwAnimMode; 378 | ItemAnimationMode eItemAnimMode; 379 | }; 380 | union //0x14 381 | { 382 | PlayerData* pPlayerData; 383 | ItemData* pItemData; 384 | MonsterData* pMonsterData; 385 | ObjectData* pObjectData; 386 | MissleData* pMissileData; 387 | }; 388 | uint32_t dwAct; //0x18 389 | DrlgAct* pDrlgAct; //0x1C 390 | uint32_t dwLoSeed; //0x20 391 | uint32_t dwHiSeed; //0x24 392 | uint32_t dwInitSeed; //0x28 393 | union //0x2C 394 | { 395 | ItemPath* pItemPath; 396 | DynamicPath* pDynamicPath; 397 | StaticPath* pStaticPath; 398 | }; 399 | AnimSeq* pAnimSeq; //0x30 400 | uint32_t dwSeqFrameCount; //0x34 401 | uint32_t dwSeqFrame; //0x38 402 | uint32_t dwAnimSpeed; //0x3C 403 | uint32_t dwSeqMode; //0x40 404 | uint32_t dwGFXcurrentFrame; //0x44 405 | uint32_t dwFrameCount; //0x48 406 | uint16_t wAnimSpeed; //0x4C 407 | uint16_t wActionFrame; //0x4E 408 | AnimData* pAnimData; //0x50 409 | GfxData* pGfxData; //0x54 410 | GfxData* pGfxDataCopy; //0x58 411 | StatListEx* pStatListEx; //0x5C 412 | Inventory* pInventory; //0x60 413 | union 414 | { 415 | struct //Server Unit 416 | { 417 | uint32_t dwInteractGUID; //0x064 418 | uint32_t dwInteractType; //0x068 419 | uint16_t nInteract; //0x06C 420 | uint16_t nUpdateType; //0x06E 421 | Unit* pUpdateUnit; //0x070 422 | QuestChain* pQuestEventList; //0x074 423 | BOOL bSparkChest; //0x078 424 | void* pTimerParams; //0x07C 425 | Game* pGame; //0x080 426 | uint32_t __084[3]; //0x084 427 | Timer* pSrvTimerList; //0x090 428 | }; 429 | 430 | struct //Client Unit 431 | { 432 | GfxLight* pLight; //0x064 433 | uint32_t dwStartLight; //0x068 434 | int32_t nPaletteIndex; //0x06C 435 | BOOL bUnitSfx; //0x070 436 | uint32_t dwSfxMode; //0x074 437 | void* pUnitSfxData; //0x078 438 | uint32_t dwSfxTicks; //0x07C 439 | uint32_t dwSfxAsyncTicks; //0x080 440 | uint32_t dwSfxStepTicks; //0x084 441 | BOOL bHasActiveSound; //0x088 442 | uint16_t nLastClickX; //0x08C 443 | uint16_t nLastClickY; //0x08E 444 | EventList* pCltTimerList; //0x090 445 | }; 446 | }; 447 | uint32_t dwOwnerType; //0x94 448 | uint32_t dwOwnerGUID; //0x98 449 | uint32_t unk0x9C[2]; //0x9C 450 | HoverText* pHoverText; //0xA4 451 | SkillList* pSkills; //0xA8 452 | Combat* pCombat; //0xAC 453 | uint32_t dwLastHitClass; //0xB0 454 | uint32_t unk0xB4; //0xB4 455 | uint32_t dwDropItemCode; //0xB8 456 | uint32_t unk0xBC[2]; //0xBC 457 | uint32_t dwFlags; //0xC4 458 | uint32_t dwFlagEx; //0xC8 459 | union //0xCC 460 | { 461 | QuestSrv* pSrvQuestData; //Server pUnit 462 | QuestClt* pCltQuestData; //Client pUnit 463 | }; 464 | uint32_t dwNodeIndex; //0xD0 465 | uint32_t dwTickCount; //0xD4 466 | union //0xD8 467 | { 468 | uint32_t dwSrvTickCount; //Server pUnit 469 | ParticleStream* pParticleStream; //Client pUnit 470 | }; 471 | Timer* pTimer; //0xDC 472 | Unit* pChangeNextUnit; //0xE0 473 | Unit* pRoomNext; //0xE4 474 | Unit* pListNext; //0xE8 475 | void* pMsgFirst; //0xEC 476 | void* pMsgLast; //0xF0 477 | }; 478 | 479 | struct LayerUnitStatId 480 | { 481 | // We can not use a struct as function parameters here as it has a different effect when using the __fastcall calling convetion. 482 | // Instead we just use D2SLayerStatIdStrc::PackedType so that we may easily change it later 483 | using PackedType = int32_t; 484 | 485 | union 486 | { 487 | struct 488 | { 489 | uint16_t nLayer; //0x00 490 | uint16_t nStat; //0x02 491 | }; 492 | PackedType nPackedValue; //0x00 493 | }; 494 | 495 | static LayerUnitStatId Make(uint16_t wLayer, uint16_t wStatId) { return { wLayer, wStatId }; } 496 | static LayerUnitStatId MakeFromStatId(uint16_t wStatId) { return { 0, wStatId }; } 497 | static LayerUnitStatId FromPackedType(PackedType nPackedValue) { 498 | LayerUnitStatId ls; 499 | ls.nPackedValue = nPackedValue; 500 | return ls; 501 | } 502 | }; 503 | 504 | struct UnitStat : LayerUnitStatId 505 | { 506 | int32_t nValue; //0x04 507 | }; 508 | 509 | struct StatsArray 510 | { 511 | UnitStat* pStat; //0x00 An Array[wStatCount] 512 | uint16_t nStatCount; //0x04 513 | uint16_t nCapacity; //0x06 514 | static const int nGrowthAmount = 4; 515 | static const int nShrinkThreshold = 8; 516 | }; 517 | 518 | struct D2ModStatsArrayStrc 519 | { 520 | LayerUnitStatId* pStat; //0x00 An Array[wStatCount] 521 | uint16_t nStatCount; //0x04 522 | uint16_t nCapacity; //0x06 523 | static const int nGrowthAmount = 4; 524 | static const int nShrinkThreshold = 8; 525 | }; 526 | 527 | struct StatList 528 | { 529 | void* pMemPool; //0x00 530 | Unit* pUnit; //0x04 531 | uint32_t dwOwnerType; //0x08 532 | uint32_t dwOwnerId; //0x0C 533 | uint32_t dwFlags; //0x10 D2C_StatlistFlags 534 | uint32_t dwStateNo; //0x14 535 | int32_t dwExpireFrame; //0x18 536 | uint32_t dwSkillNo; //0x1C 537 | uint32_t dwSLvl; //0x20 538 | StatsArray Stats; //0x24 539 | StatList* pPrevLink; //0x2C 540 | StatList* pNextLink; //0x30 541 | StatList* pParent; //0x34 542 | void* fpStatRemove; //0x38 543 | }; 544 | 545 | struct StatListEx : public StatList 546 | { 547 | StatList* pMyLastList; //0x3C 548 | StatList* pMyStats; //0x40 549 | Unit* pOwner; //0x44 550 | StatsArray FullStats; //0x48 551 | D2ModStatsArrayStrc ModStats; //0x50 552 | uint32_t* StatFlags; //0x58 8bytes per states 553 | void* fpCallBack; //0x5C 554 | Game* pGame; //0x60 555 | }; 556 | 557 | #pragma pack(pop) -------------------------------------------------------------------------------- /D2Tables.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "D2Constants.h" 6 | 7 | struct ItemsTxt 8 | { 9 | char szFlippyFile[32]; //0x00 10 | char szInvFile[32]; //0x20 11 | char szUniqueInvFile[32]; //0x40 12 | char szSetInvFile[32]; //0x60 13 | union 14 | { 15 | uint32_t dwCode; //0x80 16 | char szCode[4]; //0x80 17 | }; 18 | uint32_t dwNormCode; //0x84 19 | uint32_t dwUberCode; //0x88 20 | uint32_t dwUltraCode; //0x8C 21 | uint32_t dwAlternateGfx; //0x90 22 | uint32_t dwPspell; //0x94 23 | uint16_t wState; //0x98 24 | uint16_t wCurseState[2]; //0x9A 25 | uint16_t wStat[3]; //0x9E 26 | uint32_t dwCalc[3]; //0xA4 27 | uint32_t dwLen; //0xB0 28 | uint8_t nSpellDesc; //0xB4 29 | uint8_t pad0xB5; //0xB5 30 | uint16_t wSpellDescStr; //0xB6 31 | uint32_t dwSpellDescCalc; //0xB8 32 | uint32_t dwBetterGem; //0xBC 33 | uint32_t dwWeapClass; //0xC0 34 | uint32_t dwWeapClass2Hand; //0xC4 35 | uint32_t dwTransmogrifyType; //0xC8 36 | int32_t dwMinAc; //0xCC 37 | int32_t dwMaxAc; //0xD0 38 | uint32_t dwGambleCost; //0xD4 39 | int32_t dwSpeed; //0xD8 40 | uint32_t dwBitField1; //0xDC 41 | uint32_t dwCost; //0xE0 42 | uint32_t dwMinStack; //0xE4 43 | uint32_t dwMaxStack; //0xE8 44 | uint32_t dwSpawnStack; //0xEC 45 | uint32_t dwGemOffset; //0xF0 46 | uint16_t wNameStr; //0xF4 47 | uint16_t wVersion; //0xF6 48 | uint16_t wAutoPrefix; //0xF8 49 | uint16_t wMissileType; //0xFA 50 | uint8_t nRarity; //0xFC 51 | uint8_t nLevel; //0xFD 52 | uint8_t nMinDam; //0xFE 53 | uint8_t nMaxDam; //0xFF 54 | uint8_t nMinMisDam; //0x100 55 | uint8_t nMaxMisDam; //0x101 56 | uint8_t n2HandMinDam; //0x102 57 | uint8_t n2HandMaxDam; //0x103 58 | uint8_t nRangeAdder; //0x104 59 | uint8_t unk0x105; //0x105 60 | int16_t nStrBonus; //0x106 61 | int16_t nDexBonus; //0x108 62 | uint16_t wReqStr; //0x10A 63 | uint16_t wReqDex; //0x10C 64 | uint8_t nAbsorb; //0x10E 65 | uint8_t nInvWidth; //0x10F 66 | uint8_t nInvHeight; //0x110 67 | uint8_t nBlock; //0x111 68 | uint8_t nDurability; //0x112 69 | uint8_t nNoDurability; //0x113 70 | uint8_t nMissile; //0x114 71 | uint8_t nComponent; //0x115 72 | uint8_t nArmorComp[6]; //0x116 73 | uint8_t n2Handed; //0x11C 74 | uint8_t nUseable; //0x11D 75 | ItemType wType[2]; //0x11E 76 | uint8_t nSubType; //0x122 77 | uint8_t unk0x123; //0x123 78 | uint16_t wDropSound; //0x124 79 | uint16_t wUseSound; //0x126 80 | uint8_t nDropSfxFrame; //0x128 81 | uint8_t nUnique; //0x129 82 | uint8_t nQuest; //0x12A 83 | uint8_t nQuestDiffCheck; //0x12B 84 | uint8_t nTransparent; //0x12C 85 | uint8_t nTransTbl; //0x12D 86 | uint8_t pad0x12E; //0x12E 87 | uint8_t nLightRadius; //0x12F 88 | uint8_t nBelt; //0x130 89 | uint8_t nAutoBelt; //0x131 90 | uint8_t nStackable; //0x132 91 | uint8_t nSpawnable; //0x133 92 | uint8_t nSpellIcon; //0x134 93 | uint8_t nDurWarning; //0x135 94 | uint8_t nQuantityWarning; //0x136 95 | uint8_t nHasInv; //0x137 96 | uint8_t nGemSockets; //0x138 97 | uint8_t nTransmogrify; //0x139 98 | uint8_t nTmogMin; //0x13A 99 | uint8_t nTmogMax; //0x13B 100 | uint8_t nHitClass; //0x13C 101 | uint8_t n1or2Handed; //0x13D 102 | uint8_t nGemApplyType; //0x13E 103 | uint8_t nLevelReq; //0x13F 104 | uint8_t nMagicLevel; //0x140 105 | int8_t nTransform; //0x141 106 | int8_t nInvTrans; //0x142 107 | uint8_t nCompactSave; //0x143 108 | uint8_t nSkipName; //0x144 109 | uint8_t nNameable; //0x145 110 | uint8_t nVendorMin[17]; //0x146 111 | uint8_t nVendorMax[17]; //0x157 112 | uint8_t nVendorMagicMin[17]; //0x168 113 | uint8_t nVendorMagicMax[17]; //0x179 114 | uint8_t nVendorMagicLvl[17]; //0x18A 115 | uint8_t pad0x19B; //0x19B 116 | uint32_t dwNightmareUpgrade; //0x19C 117 | uint32_t dwHellUpgrade; //0x1A0 118 | uint8_t nPermStoreItem; //0x1A4 119 | uint8_t nMultibuy; //0x1A5 120 | uint16_t pad0x1A6; //0x1A6 121 | }; 122 | 123 | struct SkillDescTxt // sgptDataTable + 0xB8C 124 | { 125 | uint16_t wSkillDesc; //0x00 126 | uint8_t nSkillPage; //0x02 127 | uint8_t nSkillRow; //0x03 128 | uint8_t nSkillColumn; //0x04 129 | uint8_t nListRow; //0x05 130 | uint8_t nListPool; //0x06 131 | uint8_t nIconCel; //0x07 132 | uint16_t wStrName; //0x08 133 | uint16_t wStrShort; //0x0A 134 | uint16_t wStrLong; //0x0C 135 | uint16_t wStrAlt; //0x0E 136 | uint16_t wStrMana; //0x10 137 | uint16_t wDescDam; //0x12 138 | uint16_t wDescAtt; //0x14 139 | uint16_t pad0x16; //0x16 140 | uint32_t dwDamCalc[2]; //0x18 141 | uint8_t nPrgDamElem[3]; //0x20 142 | uint8_t pad0x23; //0x23 143 | uint32_t dwProgDmgMin[3]; //0x24 144 | uint32_t dwProgDmgMax[3]; //0x30 145 | uint16_t wDescMissile[3]; //0x3C 146 | uint8_t nDescLine[17]; //0x42 147 | uint8_t pad0x53; //0x53 148 | uint16_t wDescTextA[17]; //0x54 149 | uint16_t wDescTextB[17]; //0x76 150 | uint32_t dwDescCalcA[17]; //0x98 151 | uint32_t dwDescCalcB[17]; //0xDC 152 | }; 153 | 154 | struct SkillsTxt //sgptDataTable + 0xB98 155 | { 156 | int16_t nSkillId; //0x00 157 | uint16_t unk0x02; //0x02 158 | uint32_t dwFlags[2]; //0x04 159 | int8_t nCharClass; //0x0C 160 | uint8_t unk0x0D[3]; //0x0D 161 | uint8_t nAnim; //0x10 162 | uint8_t nMonAnim; //0x11 163 | uint8_t nSeqTrans; //0x12 164 | uint8_t nSeqNum; //0x13 165 | uint8_t nRange; //0x14 166 | uint8_t nSelectProc; //0x15 167 | uint8_t nSeqInput; //0x16 168 | uint8_t pad0x17; //0x17 169 | int16_t nITypeA[3]; //0x18 170 | int16_t nITypeB[3]; //0x1E 171 | int16_t nETypeA[2]; //0x24 172 | int16_t nETypeB[2]; //0x28 173 | uint16_t wSrvStartFunc; //0x2C 174 | uint16_t wSrvDoFunc; //0x2E 175 | uint16_t wSrvPrgFunc[3]; //0x30 176 | uint16_t pad0x36; //0x36 177 | uint32_t dwPrgCalc[3]; //0x38 178 | uint8_t nPrgDamage; //0x44 179 | uint8_t pad0x45; //0x45 180 | uint16_t wSrvMissile; //0x46 181 | uint16_t wSrvMissileA; //0x48 182 | uint16_t wSrvMissileB; //0x4A 183 | uint16_t wSrvMissileC; //0x4C 184 | uint16_t wSrvOverlay; //0x4E 185 | uint32_t dwAuraFilter; //0x50 186 | uint16_t wAuraStat[6]; //0x54 187 | uint32_t dwAuraLenCalc; //0x60 188 | uint32_t dwAuraRangeCalc; //0x64 189 | int32_t dwAuraStatCalc[6]; //0x68 190 | int16_t nAuraState; //0x80 191 | int16_t wAuraTargetState; //0x82 192 | uint16_t wAuraEvent[3]; //0x84 193 | uint16_t wAuraEventFunc[3]; //0x8A 194 | uint16_t wAuraTgtEvent; //0x90 195 | uint16_t wAuraTgtEventFunc; //0x92 196 | int16_t nPassiveState; //0x94 197 | int16_t nPassiveIType; //0x96 198 | int16_t nPassiveStat[5]; //0x98 199 | uint16_t pad0xA2; //0xA2 200 | uint32_t dwPassiveCalc[5]; //0xA4 201 | uint16_t wPassiveEvent; //0xB8 202 | uint16_t wPassiveEventFunc; //0xBA 203 | uint16_t wSummon; //0xBC 204 | uint8_t nPetType; //0xBE 205 | uint8_t nSumMode; //0xBF 206 | uint32_t dwPetMax; //0xC0 207 | uint16_t wSumSkill[5]; //0xC4 208 | uint16_t pad0xCE; //0xCE 209 | uint32_t dwSumSkCalc[5]; //0xD0 210 | uint16_t wSumUMod; //0xE4 211 | uint16_t wSumOverlay; //0xE6 212 | uint16_t wCltMissile; //0xE8 213 | uint16_t wCltMissileA; //0xEA 214 | uint16_t wCltMissileB; //0xEC 215 | uint16_t wCltMissileC; //0xEE 216 | uint16_t wCltMissileD; //0xF0 217 | uint16_t wCltStFunc; //0xF2 218 | uint16_t wCltDoFunc; //0xF4 219 | uint16_t wCltPrgFunc[3]; //0xF6 220 | uint16_t wStSound; //0xFC 221 | uint16_t nStSoundClass; //0x0FE 222 | uint16_t wDoSound; //0x100 223 | uint16_t wDoSoundA; //0x102 224 | uint16_t wDoSoundB; //0x104 225 | uint16_t wCastOverlay; //0x106 226 | uint16_t wTgtOverlay; //0x108 227 | uint16_t wTgtSound; //0x10A 228 | uint16_t wPrgOverlay; //0x10C 229 | uint16_t wPrgSound; //0x10E 230 | uint16_t wCltOverlayA; //0x110 231 | uint16_t wCltOverlayB; //0x112 232 | int32_t dwCltCalc[3]; //0x114 233 | uint8_t nItemTarget; //0x120 234 | uint8_t pad0x121; //0x121 235 | uint16_t wItemCastSound; //0x122 236 | uint16_t wItemCastOverlay; //0x124 237 | uint16_t pad0x126; //0x126 238 | uint32_t dwPerDelay; //0x128 239 | uint16_t wMaxLvl; //0x12C 240 | uint16_t wResultFlags; //0x12E 241 | uint32_t dwHitFlags; //0x130 242 | uint32_t dwHitClass; //0x134 243 | uint32_t dwCalc[4]; //0x138 244 | int32_t dwParam[8]; //0x148 245 | uint8_t nWeapSel; //0x168 246 | uint8_t pad0x169; //0x169 247 | uint16_t wItemEffect; //0x16A 248 | uint16_t wItemCltEffect; //0x16C 249 | uint16_t pad0x16E; //0x16E 250 | uint32_t dwSkPoints; //0x170 251 | uint16_t wReqLevel; //0x174 252 | uint16_t wReqStr; //0x176 253 | uint16_t wReqDex; //0x178 254 | uint16_t wReqInt; //0x17A 255 | uint16_t wReqVit; //0x17C 256 | int16_t nReqSkill[3]; //0x17E 257 | uint16_t wStartMana; //0x184 258 | uint16_t wMinMana; //0x186 259 | uint8_t nManaShift; //0x188 260 | uint8_t pad0x189; //0x189 261 | uint16_t wMana; //0x18A 262 | int16_t wLevelMana; //0x18C 263 | uint8_t nAttackRank; //0x18E 264 | uint8_t nLineOfSight; //0x18F 265 | uint32_t dwDelay; //0x190 266 | uint16_t wSkillDesc; //0x194 267 | uint16_t pad0x196; //0x196 268 | uint32_t dwToHit; //0x198 269 | uint32_t dwLevToHit; //0x19C 270 | uint32_t dwToHitCalc; //0x1A0 271 | uint8_t nToHitShift; //0x1A4 272 | uint8_t nSrcDam; //0x1A5 273 | uint16_t pad0x1A6; //0x1A6 274 | uint32_t dwMinDam; //0x1A8 275 | uint32_t dwMaxDam; //0x1AC 276 | uint32_t dwMinLvlDam[5]; //0x1B0 277 | uint32_t dwMaxLvlDam[5]; //0x1C4 278 | uint32_t dwDmgSymPerCalc; //0x1D8 279 | uint8_t nEType; //0x1DC 280 | uint8_t pad0x1DD[3]; //0x1DD 281 | uint32_t dwEMin; //0x1E0 282 | uint32_t dwEMax; //0x1E4 283 | uint32_t dwEMinLev[5]; //0x1E8 284 | uint32_t dwEMaxLev[5]; //0x1FC 285 | uint32_t dwEDmgSymPerCalc; //0x210 286 | uint32_t dwELen; //0x214 287 | uint32_t dwELevLen[3]; //0x218 288 | uint32_t dwELenSymPerCalc; //0x224 289 | uint8_t nRestrict; //0x228 290 | uint8_t pad0x229; //0x229 291 | int16_t nState[3]; //0x22A 292 | uint8_t nAiType; //0x230 293 | uint8_t pad0x231; //0x231 294 | int16_t wAiBonus; //0x232 295 | int32_t nCostMult; //0x234 296 | int32_t nCostAdd; //0x238 297 | }; 298 | 299 | 300 | 301 | struct ItemTypesTxt { //sgptDataTable + 0xBF8 302 | char szCode[4]; //0x00 303 | ItemType nEquiv1; //0x04 304 | ItemType nEquiv2; //0x06 305 | uint8_t nRepair; //0x08 306 | uint8_t nBody; //0x09 307 | uint8_t nBodyLoc1; //0x0A 308 | uint8_t nBodyLoc2; //0x0B 309 | uint16_t wShoots; //0x0C 310 | uint16_t wQuiver; //0x0E 311 | uint8_t nThrowable; //0x10 312 | uint8_t nReload; //0x11 313 | uint8_t nReEquip; //0x12 314 | uint8_t nAutoStack; //0x13 315 | uint8_t nMagic; //0x14 316 | uint8_t nRare; //0x15 317 | uint8_t nNormal; //0x16 318 | uint8_t nCharm; //0x17 319 | uint8_t nGem; //0x18 320 | uint8_t nBeltable; //0x19 321 | uint8_t nMaxSock1; //0x1A 322 | uint8_t nMaxSock25; //0x1B 323 | uint8_t nMaxSock40; //0x1C 324 | uint8_t nTreasureClass; //0x1D 325 | uint8_t nRarity; //0x1E 326 | uint8_t nStaffMods; //0x1F 327 | uint8_t nCostFormula; //0x20 328 | uint8_t nClass; //0x21 329 | uint8_t nStorePage; //0x22 330 | uint8_t nVarInvGfx; //0x23 331 | char szInvGfx[6][32]; //0x24 332 | }; 333 | 334 | struct ItemDataTbl //sgptDataTable + 0xCD8 335 | { 336 | int nItemsTxtRecordCount; //0x00 337 | ItemsTxt* pItemsTxt; //0x04 338 | ItemsTxt* pWeapons; //0x08 339 | int nWeaponsTxtRecordCount; //0x0C 340 | ItemsTxt* pArmor; //0x10 341 | int nArmorTxtRecordCount; //0x14 342 | ItemsTxt* pMisc; //0x18 343 | int nMiscTxtRecordCount; //0x1C 344 | }; 345 | 346 | struct CharItem 347 | { 348 | uint32_t dwItemCode; //0x00 349 | uint8_t nBodyLoc; //0x04 350 | int8_t nItemCount; //0x05 351 | uint16_t pad0x06; //0x06 352 | }; 353 | 354 | struct CharStatsTxt //sgptDataTable + 0xBC4 355 | { 356 | wchar_t wszClassName[16]; //0x00 357 | char szClassName[16]; //0x20 358 | uint8_t nStr; //0x30 359 | uint8_t nDex; //0x31 360 | uint8_t nInt; //0x32 361 | uint8_t nVit; //0x33 362 | uint8_t nStamina; //0x34 363 | uint8_t nLifeAdd; //0x35 364 | uint8_t nPercentStr; //0x36 365 | uint8_t nPercentInt; //0x37 366 | uint8_t nPercentDex; //0x38 367 | uint8_t nPercentVit; //0x39 368 | uint8_t nManaRegen; //0x3A 369 | uint8_t pad0x3B; //0x3B 370 | uint32_t dwToHitFactor; //0x3C 371 | uint8_t nWalkSpeed; //0x40 372 | uint8_t nRunSpeed; //0x41 373 | uint8_t nRunDrain; //0x42 374 | uint8_t nLifePerLevel; //0x43 375 | uint8_t nStaminaPerLevel; //0x44 376 | uint8_t nManaPerLevel; //0x45 377 | uint8_t nLifePerVitality; //0x46 378 | uint8_t nStaminaPerVitality; //0x47 379 | uint8_t nManaPerMagic; //0x48 380 | uint8_t nBlockFactor; //0x49 381 | uint16_t pad0x4A; //0x4A 382 | uint32_t dwBaseWClass; //0x4C 383 | uint8_t nStatsPerLevel; //0x50 384 | uint8_t pad0x51; //0x51 385 | uint16_t wStrAllSkills; //0x52 386 | uint16_t wStrSkillTab[3]; //0x54 387 | uint16_t wStrClassOnly; //0x5A 388 | CharItem pItems[10]; //0x5C 389 | uint16_t wStartSkill; //0xAC 390 | int16_t nBaseSkill[10]; //0xAE 391 | uint16_t pad0xC2; //0xC2 392 | }; 393 | 394 | struct DataTables { 395 | uint8_t bSkip0x0[0xB8C]; //0x000 396 | SkillDescTxt* pSkillDescTxt; //0xB8C 397 | void* pSkillDescLinker; //0xB90 398 | int nSkillDescTxtRecordCount; //0xB94 399 | SkillsTxt* pSkillsTxt; //0xB98 400 | void* pSkillsLinker; //0xB9C 401 | int nSkillsTxtRecordCount; //0xBA0 402 | int* nClassSkillCount; //0xBA4 403 | int nHighestClassSkillCount; //0xBA8 404 | short* nClassSkillList; //0xBAC 405 | uint8_t bSkip0xBB0[0x14]; //0xBB0 406 | CharStatsTxt* pCharStatsTxt; //0xBC4 407 | int nCharStatsTxtRecordCount; //0xBC8 408 | uint8_t bSkip0xBCC[0x2C]; //0xBCC 409 | ItemTypesTxt* pItemTypesTxt; //0xBF8 410 | int nItemTypesTxtRecordCount; //0xBFC 411 | }; 412 | 413 | #define STATIC_ASSERT_EQUAL(x, y) static_assert(x == y); 414 | 415 | STATIC_ASSERT_EQUAL(offsetof(DataTables, pSkillDescTxt), 0xB8C); 416 | STATIC_ASSERT_EQUAL(offsetof(DataTables, pSkillsTxt), 0xB98); 417 | STATIC_ASSERT_EQUAL(offsetof(DataTables, pCharStatsTxt), 0xBC4); 418 | STATIC_ASSERT_EQUAL(offsetof(DataTables, pItemTypesTxt), 0xBF8); -------------------------------------------------------------------------------- /Expression.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include "D2Structs.h" 4 | #include "D2Constants.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "Utils.h" 10 | 11 | 12 | /* 13 | expression -> or ; 14 | or -> and ( ( "or" | "||" ) and )* ; 15 | and -> equality ( ( "and" | "&&" ) equality )* ; 16 | equality -> comparison ( ( "!=" | "==" ) comparison )* ; 17 | comparison -> term ( ( ">" | ">=" | "<" | "<=" ) term )* ; 18 | term -> factor ( ( "-" | "+" ) factor )* ; 19 | factor -> unary ( ( "/" | "*" ) unary )* ; 20 | unary -> ( "!" | "-" | call ) unary 21 | | primary ; 22 | primary -> NUMBER | VARIABLE | "true" | "false" | "nil" 23 | | "(" expression ")" ; 24 | call -> "(" expression ")" ; 25 | */ 26 | 27 | enum class Token : uint8_t { 28 | NONE, 29 | OR, AND, 30 | 31 | LEFT_PAREN, RIGHT_PAREN, 32 | BANG, BANG_EQUALS, 33 | 34 | EQUALS, 35 | GREATER_THAN, GREATER_THAN_EQUALS, 36 | LESS_THAN, LESS_THAN_EQUALS, 37 | _IN, 38 | 39 | MINUS, PLUS, 40 | MULTIPLY, DIVIDE, 41 | 42 | //funcs 43 | STAT, CLASS, 44 | TABSKILL, CLASSSKILL, CHARGEDSKILL, SKILL, 45 | MIN, MININ, MAX, 46 | COMMA, 47 | 48 | NUMBER, VARIABLE, VARREFERENCE, 49 | _TRUE, _FALSE, 50 | 51 | END 52 | }; 53 | 54 | static const wchar_t* OPS[] = { L"", L" or ", L" and ", L"(", L")", L"!", L"!=", L"=", L">", L">=", L"<", L"<=", L" in ", L"-", L"+", L"*", L"/", L"Stat", L"Class", L"TabSkill", L"ClassSkill", L"ChargedSkill", L"Skill", L"Min", L"MinIn", L"Max", L",", L"", L"", L"", L"True", L"False", L""}; 55 | 56 | class Expression { 57 | public: 58 | virtual ~Expression() = default; 59 | virtual int32_t Evaluate(Unit* pItem) const = 0; 60 | virtual std::wstring ToString(Unit* pItem) const = 0; 61 | virtual void SetVariables(uint32_t nLineNumber, const utility::string_umap& variables) = 0; 62 | }; 63 | 64 | class Logical : public Expression { 65 | protected: 66 | Token m_Operator; 67 | std::unique_ptr m_Left; 68 | std::unique_ptr m_Right; 69 | public: 70 | Logical(std::unique_ptr left = nullptr, std::unique_ptr right = nullptr, Token op = Token::NONE) : m_Left(std::move(left)), m_Right(std::move(right)), m_Operator(op) {}; 71 | int32_t Evaluate(Unit* pItem) const override; 72 | std::wstring ToString(Unit* pItem) const override; 73 | void SetVariables(uint32_t nLineNumber, const utility::string_umap& variables) override; 74 | }; 75 | 76 | class Binary : public Expression { 77 | protected: 78 | Token m_Operator; 79 | std::unique_ptr m_Left; 80 | std::unique_ptr m_Right; 81 | public: 82 | Binary(std::unique_ptr left = nullptr, std::unique_ptr right = nullptr, Token op = Token::NONE) : m_Left(std::move(left)), m_Right(std::move(right)), m_Operator(op) {}; 83 | int32_t Evaluate(Unit* pItem) const override; 84 | std::wstring ToString(Unit* pItem) const override; 85 | void SetVariables(uint32_t nLineNumber, const utility::string_umap& variables) override; 86 | }; 87 | 88 | class In : public Expression { 89 | protected: 90 | Token m_Operator; 91 | std::unique_ptr m_Left; 92 | std::unique_ptr m_Min; 93 | std::unique_ptr m_Max; 94 | public: 95 | In(std::unique_ptr left = nullptr, std::unique_ptr min = nullptr, std::unique_ptr max = nullptr, Token op = Token::NONE) : m_Left(std::move(left)), m_Min(std::move(min)), m_Max(std::move(max)), m_Operator(op) {}; 96 | int32_t Evaluate(Unit* pItem) const override; 97 | std::wstring ToString(Unit* pItem) const override; 98 | void SetVariables(uint32_t nLineNumber, const utility::string_umap& variables) override; 99 | }; 100 | 101 | class Unary : public Expression { 102 | protected: 103 | Token m_Operator; 104 | std::unique_ptr m_Right; 105 | public: 106 | Unary(std::unique_ptr right = nullptr, Token op = Token::NONE) : m_Right(std::move(right)), m_Operator(op) {}; 107 | int32_t Evaluate(Unit* pItem) const override; 108 | std::wstring ToString(Unit* pItem) const override; 109 | void SetVariables(uint32_t nLineNumber, const utility::string_umap& variables) override; 110 | }; 111 | 112 | class Literal : public Expression { 113 | protected: 114 | int32_t m_Value; 115 | public: 116 | Literal(std::wstring_view value) : m_Value(std::stoi(std::wstring(value))) {}; 117 | int32_t Evaluate(Unit* pItem) const override; 118 | std::wstring ToString(Unit* pItem) const override; 119 | void SetVariables(uint32_t nLineNumber, const utility::string_umap& variables) override; 120 | }; 121 | 122 | class Boolean : public Expression { 123 | protected: 124 | int32_t m_Value; 125 | public: 126 | Boolean(int32_t value) : m_Value(value) {}; 127 | int32_t Evaluate(Unit* pItem) const override; 128 | std::wstring ToString(Unit* pItem) const override; 129 | void SetVariables(uint32_t nLineNumber, const utility::string_umap& variables) override; 130 | }; 131 | 132 | typedef int32_t(*GlobalVariableFunction)(Unit* pUnit); 133 | 134 | class Variable : public Expression { 135 | protected: 136 | std::wstring m_Variable; 137 | std::optional m_Value; 138 | GlobalVariableFunction m_Func; 139 | public: 140 | Variable(std::wstring_view variable = {}); 141 | int32_t Evaluate(Unit* pItem) const override; 142 | std::wstring ToString(Unit* pItem) const override; 143 | void SetValue(int32_t v); 144 | void SetVariables(uint32_t nLineNumber, const utility::string_umap& variables) override; 145 | }; 146 | 147 | class VarReference : public Expression { 148 | protected: 149 | Variable& m_Variable; 150 | public: 151 | VarReference(Variable& variable) : m_Variable(variable) {} 152 | int32_t Evaluate(Unit* pItem) const override { return m_Variable.Evaluate(pItem); } 153 | std::wstring ToString(Unit* pItem) const override { return m_Variable.ToString(pItem); } 154 | void SetValue(int32_t v) { m_Variable.SetValue(v); } 155 | void SetVariables(uint32_t nLineNumber, const utility::string_umap& variable) override { m_Variable.SetVariables(nLineNumber, variable); } 156 | }; 157 | 158 | class Call : public Expression { 159 | protected: 160 | Token m_Func; 161 | std::vector> m_Args; 162 | 163 | int32_t EvaluateClass(Unit* pItem, const std::vector& args) const; 164 | int32_t EvaluateChargedSkill(Unit* pItem, Stat stat, const std::vector& args) const; 165 | int32_t EvaluateStat(Unit* pItem, Stat stat, const std::vector& args) const; 166 | public: 167 | Call(Token func, std::vector> args) : m_Func(func), m_Args(std::move(args)) {}; 168 | int32_t Evaluate(Unit* pItem) const override; 169 | std::wstring ToString(Unit* pItem) const override; 170 | void SetVariables(uint32_t nLineNumber, const utility::string_umap& variables) override; 171 | }; 172 | 173 | class ListExpression : public Expression { 174 | protected: 175 | std::vector> m_List; 176 | public: 177 | void Push(std::unique_ptr expression) { m_List.push_back(std::move(expression)); } 178 | int32_t Evaluate(Unit* pItem) const override; 179 | std::wstring ToString(Unit* pItem) const override; 180 | void SetVariables(uint32_t nLineNumber, const utility::string_umap& variables) override; 181 | }; 182 | 183 | class TokenizerToken { 184 | protected: 185 | Token m_TokenType; 186 | std::wstring_view m_Value; 187 | public: 188 | TokenizerToken(Token type = Token::NONE, std::wstring_view value = {}) : m_TokenType(type), m_Value(value) {}; 189 | Token GetTokenType() const { return m_TokenType; } 190 | std::wstring_view GetValue() const { return m_Value; } 191 | }; 192 | 193 | //https://github.com/munificent/craftinginterpreters/blob/master/java/com/craftinginterpreters/lox/Scanner.java 194 | class Tokenizer { 195 | protected: 196 | int m_Current = 0; 197 | std::vector m_Tokens; 198 | 199 | bool IsNull(wchar_t c) const; 200 | bool IsOperator(const wchar_t*& expression) const; 201 | std::wstring_view ParseVariable(const wchar_t*& expression) const; 202 | std::wstring_view ParseQuotedVariable(const wchar_t*& expression) const; 203 | std::wstring_view ParseDigit(const wchar_t*& expression) const; 204 | void Tokenize(const wchar_t*& expression); 205 | public: 206 | Tokenizer(const wchar_t* expression) { 207 | Tokenize(expression); 208 | }; 209 | 210 | const std::vector& GetTokens() const { return m_Tokens; } 211 | bool Match(Token t); 212 | bool Check(Token t) const; 213 | const TokenizerToken& Previous() const; 214 | const TokenizerToken& Peek() const; 215 | void Reset(); 216 | void Insert(const TokenizerToken& t); 217 | }; 218 | 219 | //https://en.wikipedia.org/wiki/Recursive_descent_parser 220 | class Parser { 221 | protected: 222 | static std::unique_ptr _finishCall(Tokenizer& t, Token call); 223 | static std::unique_ptr _primary(Tokenizer& t, Variable* lhs = nullptr); 224 | static std::unique_ptr _unary(Tokenizer& t, Variable* lhs = nullptr); 225 | static std::unique_ptr _factor(Tokenizer& t, Variable* lhs = nullptr); 226 | static std::unique_ptr _term(Tokenizer& t, Variable* lhs = nullptr); 227 | static std::unique_ptr _comparison(Tokenizer& t, Variable* lhs = nullptr); 228 | static std::unique_ptr _equality(Tokenizer& t, Variable* lhs = nullptr); 229 | static std::unique_ptr _and(Tokenizer& t, Variable* lhs = nullptr); 230 | static std::unique_ptr _or(Tokenizer& t, Variable* lhs = nullptr); 231 | static std::unique_ptr _expression(Tokenizer& t, Variable* lhs = nullptr); 232 | public: 233 | static std::unique_ptr Parse(const wchar_t* expression); 234 | static std::unique_ptr Parse(const wchar_t* expression, Variable* lhs); 235 | static std::unique_ptr ParseCall(const wchar_t* args, Token func); 236 | }; 237 | -------------------------------------------------------------------------------- /Globals.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "Action.h" 7 | #include "D2Constants.h" 8 | #include "Utils.h" 9 | 10 | //Globals 11 | extern const utility::string_umap ItemTypes; 12 | extern const std::unordered_map ItemTypesLookup; 13 | 14 | extern const utility::string_umap Rarities; 15 | extern const std::unordered_map RaritiesLookup; 16 | 17 | extern const utility::string_umap Qualities; 18 | extern const std::unordered_map QualitiesLookup; 19 | 20 | extern const utility::string_umap CustomStats; 21 | 22 | extern std::unordered_map ITEM_ACTIONS; -------------------------------------------------------------------------------- /Hooking.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "Hooking.h" 4 | 5 | //https://stackoverflow.com/a/54661410/597419 6 | bool Hooking::Hook(void* src, void* dst, int len) { 7 | if (len < 5) return false; 8 | 9 | DWORD curProtection; 10 | VirtualProtect(src, len, PAGE_EXECUTE_READWRITE, &curProtection); 11 | 12 | memset(src, 0x90, len); 13 | uintptr_t relativeAddress = ((uintptr_t)dst - (uintptr_t)src) - 5; 14 | 15 | *(BYTE*)(src) = 0xE9; 16 | *(uintptr_t*)((uintptr_t)src + 1) = relativeAddress; 17 | 18 | VirtualProtect(src, len, curProtection, &curProtection); 19 | return true; 20 | } 21 | 22 | void Hooking::TrampolineHook(void* src, void* dst, void** orig, int len) { 23 | // Make sure the length is greater than 5 24 | if (len < 5) { 25 | return; 26 | } 27 | 28 | // Create the gateway (len + 5 for the overwritten bytes + the jmp) 29 | void* gateway = VirtualAlloc(0, len + 5, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 30 | 31 | //Write the stolen bytes into the gateway 32 | memcpy(gateway, src, len); 33 | 34 | // Get the gateway to destination addy 35 | uintptr_t gatewayRelativeAddr = ((uintptr_t)src - (uintptr_t)gateway) - 5; 36 | 37 | // Add the jmp opcode to the end of the gateway 38 | *(BYTE*)((uintptr_t)gateway + len) = 0xE9; 39 | 40 | // Add the address to the jmp 41 | *(uintptr_t*)((uintptr_t)gateway + len + 1) = gatewayRelativeAddr; 42 | 43 | // Place the hook at the destination 44 | Hook(src, dst, len); 45 | 46 | *orig = gateway; 47 | } 48 | 49 | //todo: refactor combine call/jmp fncs 50 | void Hooking::SetCall(void* address, void* function, size_t size) { 51 | DWORD old_protect; 52 | VirtualProtect(address, size, PAGE_EXECUTE_READWRITE, &old_protect); 53 | 54 | memset(address, 0x90, size); 55 | *static_cast(address) = 0xE8; 56 | *reinterpret_cast(static_cast(address) + 1) = reinterpret_cast(function) - reinterpret_cast(address) - 5; 57 | 58 | VirtualProtect(address, size, old_protect, &old_protect); 59 | } 60 | 61 | void Hooking::SetJmp(void* address, void* function, size_t size) { 62 | DWORD old_protect; 63 | VirtualProtect(address, size, PAGE_EXECUTE_READWRITE, &old_protect); 64 | 65 | memset(address, 0x90, size); 66 | *static_cast(address) = 0xE9; 67 | *reinterpret_cast(static_cast(address) + 1) = reinterpret_cast(function) - reinterpret_cast(address) - 5; 68 | 69 | VirtualProtect(address, size, old_protect, &old_protect); 70 | } -------------------------------------------------------------------------------- /Hooking.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | class Hooking 3 | { 4 | public: 5 | static bool Hook(void* src, void* dst, int len); 6 | static void TrampolineHook(void* src, void* dst, void** orig, int len); 7 | static void SetCall(void* address, void* function, size_t size); 8 | static void SetJmp(void* address, void* function, size_t size); 9 | }; 10 | 11 | -------------------------------------------------------------------------------- /ItemFilter.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "D2Structs.h" 5 | #include "Rule.h" 6 | #include "Configuration.h" 7 | 8 | typedef void(__fastcall* D2GSServerToClientPacketHandlerFn)(uint8_t* pBitstream); 9 | 10 | class ItemFilter 11 | { 12 | private: 13 | Configuration m_Config; 14 | 15 | ItemFilter(); 16 | 17 | inline static bool DebugFlag = false; 18 | inline static std::unique_ptr Instance = nullptr; 19 | public: 20 | ItemFilter(const ItemFilter&) = delete; 21 | ItemFilter& operator=(const ItemFilter&) = delete; 22 | 23 | static void Initialize(); 24 | 25 | static void ReloadFilter(); 26 | static void RunRules(Unit* pItem); 27 | 28 | static bool IsLoaded() { return Instance->m_Config.IsLoaded(); } 29 | static void LoadSettings() { Instance->m_Config.LoadSettings(); } 30 | static void SaveSettings() { Instance->m_Config.SaveSettings(); } 31 | 32 | static int32_t& FilterLevel() { return Instance->m_Config.m_Settings.nFilterLevel; } 33 | static int32_t& PingLevel() { return Instance->m_Config.m_Settings.nPingLevel; } 34 | 35 | static void ToggleDebug(); 36 | static void DebugRule(uint32_t nLineNumber); 37 | static bool IsFilterDebug() { return DebugFlag; } 38 | 39 | static void Clip(); 40 | 41 | static void DoChatAlert(Unit* pUnit); 42 | 43 | static bool IsItem(Unit* pUnit); 44 | static bool HasActions(Unit* pUnit); 45 | static POINT ScreenToAutomap(int nX, int nY); 46 | 47 | static BOOL IsTxtDataLoaded() { return *D2CLIENT_UIVar_Game; } 48 | 49 | #pragma region Hooks 50 | //Hooked Methods 51 | static LRESULT WndProc(HWND hWnd, int msg, WPARAM wParam, LPARAM lParam); 52 | 53 | static void __fastcall ItemActionOwned(uint8_t* pBitstream); 54 | static void __fastcall ItemActionWorld(uint8_t* pBitstream); 55 | static void HandlePacket(uint8_t* pBitstream, D2GSServerToClientPacketHandlerFn pHandler); 56 | static void __stdcall DrawDebugInfo(Unit* pItem, uint32_t nXStart, uint32_t nYStart, uint32_t nXEnd, uint32_t nYEnd); 57 | static void __stdcall DrawGroundItemRect(DWORD retAddress, BOOL isHovered, Unit* pItem, uint32_t nXStart, uint32_t nYStart, uint32_t nXEnd, uint32_t nYEnd, uint8_t nPaletteIndex, DrawMode eDrawMode); 58 | static void __stdcall DrawInventoryItemRect(Unit* pItem, uint32_t nXStart, uint32_t nYStart, uint32_t nXEnd, uint32_t nYEnd, uint8_t nPaletteIndex, DrawMode eDrawMode); 59 | 60 | static BOOL __fastcall GetItemName_114d(Unit* pItem, wchar_t* pBuffer, uint32_t dwSize); 61 | static BOOL __stdcall GetItemName(Unit* pItem, wchar_t* pBuffer, uint32_t dwSize); 62 | static void __stdcall HandleItemName(Unit* pItem, wchar_t* pBuffer, uint32_t dwSize); 63 | 64 | static void __stdcall GetItemDesc(wchar_t* pBuffer); 65 | static void __stdcall DrawGameUI(); 66 | 67 | static void __stdcall UNITS_FreeUnit(Unit* pUnit); 68 | 69 | static void __stdcall AUTOMAP_Draw(); 70 | static BOOL __fastcall UNITDRAW_DrawUnit(Unit* pUnit, uint32_t dwColorTint, int nXpos, int nYpos, BOOL bFade, BOOL bDrawOverlays); 71 | static BOOL __fastcall IsUnitNoDraw(Unit* pUnit); 72 | 73 | static BOOL __stdcall CallCommand(char* wCmd); 74 | 75 | #pragma endregion 76 | 77 | #pragma region Stubs 78 | //Stubs 79 | static void __stdcall DrawAltDownItemRect_STUB_110f(); 80 | static void __stdcall DrawAltDownItemRect_STUB(); 81 | static void __stdcall DrawAltDownItemRect_STUB_114d(); 82 | 83 | static void __stdcall DrawHoverItemRect_STUB(); 84 | 85 | static void __stdcall DrawInventoryItemRect_STUB_110f(); 86 | static void __stdcall DrawInventoryItemRect_STUB(); 87 | static void __stdcall DrawInventoryItemRect_STUB_114d(); 88 | 89 | static void __stdcall CheckUnitNoDraw1_STUB(); 90 | static void __stdcall CheckUnitNoDraw2_STUB(); 91 | static void __stdcall CheckUnitNoDraw2_STUB_114d(); 92 | 93 | static void __stdcall GetItemDesc_STUB(); 94 | static void __stdcall GetItemDesc_STUB_114d(); 95 | 96 | static void __stdcall CallCommand_STUB(); 97 | static void __stdcall CallCommand_STUB_114d(); 98 | #pragma endregion 99 | 100 | }; 101 | 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 dschu012 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### d2lootfilter 2 | 3 | d2lootfilter is a plugin that can be used with [PlugY](http://plugy.free.fr/en/index.html) or other mod systems to filter loot on 1.10f, 1.13c, and 1.14d. The syntax for filtering loot was made to be similar to [Path of Exile](https://pathofexile.fandom.com/wiki/Item_filter). 4 | 5 | * [Sample Filter](doc/ENG/item.filter) 6 | * [Features](#Features) 7 | * [Installing](#Installing) 8 | * [Basic Syntax](#Basic-Syntax) 9 | * [Operators](#Operators) 10 | * [Conditions](#Conditions) 11 | * [Stats](#Stats) 12 | * [Actions](#Actions) 13 | * [Styles](#Styles) 14 | * [Settings](#Settings) 15 | * [In Game Commands](#In-Game-Commands) 16 | * [Credits](#Credits) 17 | 18 | **This has not been heavily tested, crashes may happen, and items may be lost** 19 | 20 | ### Features 21 | 22 | * Filter loot by various attributes (still a work in progress) 23 | * Notifications for loot 24 | * Minimap icons for loot 25 | * Custom name/descriptions/background colors for ground and inventory loot. 26 | 27 | Still todo: 28 | 29 | * Error handling. Filter just silently ignores errors right now. 30 | * Disable sound for hidden drops 31 | * Tier system to change filter levels in game. 32 | 33 | Example with custom item name/border/background color/inventory background color and chat notification. 34 | 35 | ![image](https://user-images.githubusercontent.com/1458109/114068726-33ed1f00-986c-11eb-8cca-686efb629e8e.png) 36 | 37 | See what rules in your config caused the item to be shown. Set background colors for charms/items to quickly find charms that need to be replaced etc... 38 | 39 | ![image](https://user-images.githubusercontent.com/1458109/114068812-4cf5d000-986c-11eb-9795-fd7e1d6a8683.png) 40 | 41 | ### Installing 42 | * Download the [latest dll](https://github.com/dschu012/d2lootfilter/releases/latest/download/d2lootfilter.dll) and [item.filter](https://github.com/dschu012/d2lootfilter/blob/master/doc/ENG/item.filter) 43 | * Follow instructions below, depending on your DLL mod loader 44 | 45 | #### For PlugY: 46 | * Copy both files to your PlugY directory. 47 | * Change `DllToLoad=` to `DllToLoad=d2lootfilter.dll` in PlugY.ini 48 | 49 | #### For BaseMod: 50 | Tested on BaseMod 1.13.9, Windows 10 and Diablo 2 LOD 1.14d 51 | 52 | * Copy both files to your Diablo 2 directory, where you installed `BaseMod.ini` . 53 | * In `BaseMod.ini`, in `ExtraDll2` section change `LoadDll2=` to `LoadDll2=d2lootfilter.dll` and `Enabled=0` to `Enabled=1` 54 | 55 | ### Basic Syntax 56 | 57 | The basic syntax is a collection of `Show` and `Hide` condition blocks. If an item matches **all conditions** on the block, the item will respectively be hidden or shown. `Continue` may be added to the end of a condition block if you would like the item to continue trying to match other condition blocks, otherwise processing stops after the first block is matched. A sample filter can be found [here](doc/ENG/item.filter). 58 | 59 | ``` 60 | # hides all inferior items 61 | Hide 62 | Rarity Inferior 63 | 64 | # append the rune number to all runes 65 | Show 66 | Class Rune 67 | SetName {Name} {Red}{Rune Number} 68 | Continue 69 | 70 | # since continue was specified on the last condiition block 71 | # this one will also match. the name carries over from the 72 | # previous condiition block 73 | Show 74 | Rune >= Vex 75 | ChatNotify True 76 | MinimapIcon Purple 77 | ``` 78 | 79 | ### Operators 80 | 81 | The operators are as followed. If no operator is specified `=` is implied. 82 | 83 | | Operator | Description | 84 | |-|-| 85 | | `=` | Equals | 86 | | `!=` | Not Equals | 87 | | `<` | Less Than | 88 | | `<=` | Less Than Equals | 89 | | `>` | Greater Than | 90 | | `>=` | Greater Than Equals | 91 | | `in X-Y` | Between values X and Y | 92 | 93 | ### Conditions 94 | 95 | * `,` essentially works as an `or` operator. i.e. `Type Swirling Crystal, Dimensional Shard` would match either item. 96 | 97 | | Name | Valid Values | 98 | |-|-| 99 | | Type ` ` | Name of item in `strings.txt` lookup from `weapons.txt`, `armor.txt`, or `misc.txt` . i.e. `Swirling Crystal`. For 1.13c and 1.14d these can be found [here](doc/ENG/Types.md). | 100 | | Code ` ` | 3 character item code found in `weapons.txt`, `armor.txt`, or `misc.txt`. For 1.13c and 1.14d these can be found [here](doc/ENG/Types.md). | 101 | | Class ` ` | ItemType column from `itemtypes.txt`. For 1.13c and 1.14d these can be found [here](doc/ENG/Class.md). | 102 | | Ethereal `` | Boolean True or False | 103 | | Rarity `` | Inferior, Normal, Superior, Magic, Set, Rare, Unique, Crafted | 104 | | Runeword `` | Boolean True or False | 105 | | Prefix ` ` | Prefix ID from `magicprefix.txt` (todo... human readable name) | 106 | | Suffix ` ` | Suffix ID from `magicsuffix.txt` (todo... human readable name) | 107 | | ItemLevel ` ` | Number | 108 | | Quality ` ` | Normal, Exceptional, Elite | 109 | | CharacterLevel ` ` | Number | 110 | | Difficulty ` ` | Normal, Nightmare, Hell | 111 | | Rune ` ` | Rune Name or Number. For 1.13c and 1.14d these can be found [here](doc/ENG/Runes.md). | 112 | | Id ` ` | Unique or Set ID from `sets.txt` or `uniques.txt` | 113 | | Gold ` ` | Gold value | 114 | | Defense ` ` | Defense | 115 | | Price ` ` | Price when vendoring item | 116 | | Identified `` | Boolean True or False | 117 | | Armor `` | Boolean True or False. True if item is any armor. | 118 | | Weapon `` | Boolean True or False. True if item is any weapon. | 119 | | Sockets ` ` | Number of sockets | 120 | | Width ` ` | Width of item in inventory | 121 | | Height ` ` | Height of item in inventory | 122 | | Stats `` | Expression that evaluates to true or false to filter an item based on stats. More details can be found in [Stats](#Stats) | 123 | | HasWeight `` | Expression that evaluates to true or false to filter an item based "Weight" (see [Actions](#Actions)) | 124 | 125 | 126 | ### Stats 127 | 128 | Stats are expressions that evaluate to true or false. i.e `"All Resists > 0 and "Life" > 0` would match items with both all resists and life. A list of keywords that can be used in stats expressions can be found here. [Stats](doc/ENG/Stats.md), [Skills](doc/ENG/Skills.md). These keywords must be quoted `"`. 129 | 130 | | Functions | Description | 131 | |-|-| 132 | | `Stat` | Use other stats that are not in [Stats](doc/ENG/Stats.md). 2 arg function. The first arg is the stat id from `itemcoststats.txt`. The second arg is optional layer (used for skill stats). Returns the unsigned value of the stat. | 133 | | `ChargedSkill` | Check the skill level of a charged skill. i.e. `ChargedSkill(54) > 0` will check if an item has charges of teleport | 134 | | `ClassSkill` | Check if an item has a certain class skill. i.e. `ClassSkill(1) > 0` will check if an item has +To All Sorc skills. | 135 | | `TabSkill` | Check if an item has a certain tab skill. i.e. `TabSkill(34) > 0` will check if an item has +To to Warcries skills. | 136 | | `Class` | Check if an item is a certain type/class. 1 arg function of the class from `itemtypes.txt`. | 137 | | `Min` | varargs. returns the minimum (non-zero, exclude stats that don't exist) value from a list | 138 | | `MinIn` | varargs. returns the minimum (including zeros, i.e. stats that don't exist on the item) value from a list | 139 | | `Max` | varargs. returns the maximum (non-zero, exclude stats that don't exist) value from a list | 140 | 141 | e.x. `Max(Stat(39), Stat(43), Stat(41), Stat(45)) > 0` can be used to filter the existance of any resistance and `MinIn(Stat(39), Stat(43), Stat(41), Stat(45)) > 0` for all resistances 142 | 143 | ### Actions 144 | 145 | * Name and Description can use the following color tokens. `{White}`, `{Red}`, `{Green}`, `{Blue}`, `{Gold}`, `{Gray}`, `{Black}`, `{Tan}`, `{Orange}`, `{Yellow}`, `{Purple}`, `{Dark Green}`. 146 | * Name and Description can use the following special tokens: `{Price}` (Item Price), `{Sockets}` (Number of sockets) 147 | 148 | | Name | Valid Values | 149 | |-|-| 150 | | SetStyle `` | Sets the styling for an item. A style is a group of actions that will be applied. See [Styles](#Styles). | 151 | | SetName `` | Sets the name for an item. Special token `{Name}` is the base name for the item. When using continue it will append from the previous condition block. | 152 | | SetDescription `` | Sets the description for an item. Special token `{Description}` is the base name for the item. When using continue it will append from the previous condition block. | 153 | | SetBackgroundColor `` | Sets the background color of the item when on the ground. Pallette index color or White, Red, Green, Blue, Gold, Gray, Black, Tan, Orange, Yellow, Purple, Dark Green | 154 | | SetInventoryColor `` | Sets the background color of the item when in your inventory. Value is a pallette index color. | 155 | | SetBorderColor `` | Sets the border color of the item when on the ground. Value is a pallette index color. | 156 | | ChatNotify `` | Notify when the item drops in chat. True or False | 157 | | MinimapIcon `` | Sets the color of the item on your minimap when on the ground. Value is a pallette index color. | 158 | | Weight `` | Add's a "Weight" to an item. Let's you filter items based on combination of stats. (i.e. `Weight "Barbarian +%d to Warcries" * 100` would make a Warcry weapon w/ +3 warcry's have a weight of 300 while a +2 would have 200). All weights are added together and then can be filtered on w/ `HasWeight` | 159 | 160 | ### Styles 161 | 162 | Lets you apply a group of actions to an item. i.e. 163 | 164 | ``` 165 | Style Tier 1 Items 166 | SetName {Purple}T1 {Name} 167 | ChatNotify True 168 | MinimapIcon Purple 169 | SetInventoryColor Purple 170 | 171 | Show 172 | Type Diadem 173 | Rarity Unique 174 | SetStyle Tier 1 Items 175 | 176 | Show 177 | Type Unearthed Wand 178 | Rarity Unique 179 | SetStyle Tier 1 Items 180 | ``` 181 | 182 | will apply all of the `Tier 1 Items` styles to the items. 183 | 184 | ### Settings 185 | 186 | The first time you join a game with the plugin loaded it should create a `d2lootfilter.ini` settings file in your Diablo II directory. These are the following settings that can be changed. 187 | 188 | | Setting | Description | 189 | |-|-| 190 | | Path | Path to your filter file. Default: `./item.filter` | 191 | | FilterLevel | Used to dynamically change how strict your filter while playing. (Currently unused, still a planned feature) Default: `6` | 192 | | PingLevel | Used to dynamically change how strict drop notification are while playing. Default: `6` | 193 | 194 | 195 | ### In Game Commands 196 | 197 | The filter has a few in-game commands for changing settings. 198 | 199 | | Command | Description | 200 | |-|-| 201 | | `/reload` | Reloads your filter. | 202 | | `/debug` | Toggles debugging. | 203 | | `/test ` | Tests a specific rule (by line number) against your currently hovered mouse item. | 204 | | `/filterlevel ` | Change the filter level. | 205 | | `/pinglevel ` | Change the ping/notification level. | 206 | 207 | 208 | ### Credits 209 | 210 | Special thanks to everyone that has shared their work at [Phrozen-Keep](https://d2mods.info/forum/viewforum.php?f=8) [(Discord)](https://discord.gg/NvfftHY). 211 | 212 | To name a few Necrolis, Lectem, Kingpin, whist, Revan, etc... 213 | 214 | Thanks to coffin_spirit on Discord for the 1.10f implementation. 215 | 216 | + Swine-Flu for his refactoring/performance enhancements. 217 | -------------------------------------------------------------------------------- /Rule.cpp: -------------------------------------------------------------------------------- 1 | #include "Rule.h" 2 | 3 | bool Rule::Evaluate(Unit* pItem) { 4 | for (auto& condition : m_Conditions) { 5 | if (!condition->Evaluate(pItem)) { 6 | return false; 7 | } 8 | } 9 | return true; 10 | } 11 | 12 | void Rule::EvaluateActionResult(ActionResult& actionResult, Unit* pItem) const { 13 | actionResult.bContinue = false; 14 | for (auto& action : m_Actions) { 15 | action->SetResult(actionResult, pItem); 16 | } 17 | } 18 | 19 | uint32_t Rule::GetLineNumber() const { 20 | return m_LineNumber; 21 | } 22 | 23 | const std::vector>& Rule::GetConditions() { 24 | return m_Conditions; 25 | } 26 | 27 | const std::vector>& Rule::GetActions() { 28 | return m_Actions; 29 | } 30 | 31 | void Rule::AddAction(std::unique_ptr action) { 32 | m_Actions.push_back(std::move(action)); 33 | } 34 | 35 | void Rule::AddActions(std::vector> actions) { 36 | std::move(actions.begin(), actions.end(), std::back_inserter(m_Actions)); 37 | } 38 | 39 | void Rule::AddCondition(std::unique_ptr condition) { 40 | m_Conditions.push_back(std::move(condition)); 41 | } 42 | 43 | void Rule::AddConditions(std::vector> conditions) { 44 | std::move(conditions.begin(), conditions.end(), std::back_inserter(m_Conditions)); 45 | } -------------------------------------------------------------------------------- /Rule.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "D2Structs.h" 6 | #include "Condition.h" 7 | #include "Action.h" 8 | 9 | class Rule { 10 | private: 11 | uint32_t m_LineNumber; 12 | std::vector> m_Conditions; 13 | std::vector> m_Actions; 14 | public: 15 | Rule(uint32_t lineNumber) : m_LineNumber{ lineNumber } {} 16 | bool Evaluate(Unit* pUnit); 17 | void EvaluateActionResult(ActionResult& actionResult, Unit* pItem) const; 18 | uint32_t GetLineNumber() const; 19 | const std::vector>& GetConditions(); 20 | const std::vector>& GetActions(); 21 | void AddAction(std::unique_ptr action); 22 | void AddActions(std::vector> actions); 23 | void AddCondition(std::unique_ptr condition); 24 | void AddConditions(std::vector> conditions); 25 | }; -------------------------------------------------------------------------------- /Utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Utils.h" 7 | #include 8 | #include 9 | #include "D2Tables.h" 10 | #include "D2Structs.h" 11 | #include "D2Ptrs.h" 12 | #include "mINI.h" 13 | 14 | #pragma comment(lib, "Version.Lib") 15 | 16 | #define D2SE_SETUP_FILE "D2SE_SETUP.ini" 17 | 18 | D2Version InitGameVersion(LPCVOID pVersionResource) { 19 | UINT uLen; 20 | VS_FIXEDFILEINFO* ptFixedFileInfo; 21 | if (!VerQueryValue(pVersionResource, L"\\", (LPVOID*)&ptFixedFileInfo, &uLen)) 22 | return D2Version::NONE; 23 | 24 | if (uLen == 0) 25 | return D2Version::ERR; 26 | 27 | WORD major = HIWORD(ptFixedFileInfo->dwFileVersionMS); 28 | WORD minor = LOWORD(ptFixedFileInfo->dwFileVersionMS); 29 | WORD revision = HIWORD(ptFixedFileInfo->dwFileVersionLS); 30 | WORD subrevision = LOWORD(ptFixedFileInfo->dwFileVersionLS); 31 | 32 | if (major != 1) 33 | return D2Version::ERR; 34 | if (minor == 0 && revision == 13 && subrevision == 60) return D2Version::V113c; 35 | if (minor == 14 && revision == 3 && subrevision == 71) return D2Version::V114d; 36 | if (minor == 0 && revision == 10 && subrevision == 39) return D2Version::V110f; 37 | return D2Version::ERR; 38 | } 39 | 40 | D2Version InitGameVersion() { 41 | mINI::INIFile file(D2SE_SETUP_FILE); 42 | mINI::INIStructure coredata; 43 | bool exists = std::filesystem::exists(D2SE_SETUP_FILE); 44 | 45 | if (!exists) { 46 | HMODULE hModule = GetModuleHandle(NULL); 47 | HRSRC hResInfo = FindResource(hModule, MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); 48 | if (!hResInfo) { 49 | return D2Version::ERR; 50 | } 51 | HGLOBAL hResData = LoadResource(hModule, hResInfo); 52 | if (!hResData) { 53 | return D2Version::ERR; 54 | } 55 | LPVOID pVersionResource = LockResource(hResData); 56 | D2Version version = InitGameVersion(pVersionResource); 57 | FreeResource(hResData); 58 | return version; 59 | } 60 | else { 61 | file.read(coredata); 62 | const auto& core = coredata["Protected"]["D2Core"]; 63 | if (core == "1.10f") return D2Version::V110f; 64 | if (core == "1.13c") return D2Version::V113c; 65 | return D2Version::ERR; 66 | } 67 | } 68 | 69 | D2Version GetGameVersion() { 70 | static D2Version GameVersion = InitGameVersion(); 71 | return GameVersion; 72 | } 73 | 74 | void PrintGameString(const std::wstring& wStr, TextColor color) { 75 | if (GetGameVersion() == D2Version::V114d || GetGameVersion() == D2Version::V110f) { 76 | D2CLIENT_PrintGameStringe_114d(wStr.c_str(), color); 77 | } else { 78 | D2CLIENT_PrintGameString(wStr.c_str(), color); 79 | } 80 | } 81 | 82 | Unit* FindUnitFromTable(uint32_t unitId, UnitType type) { 83 | UnitHashTable serverSideUnitTable = D2CLIENT_ServerSideUnitHashTables[static_cast(type)]; 84 | Unit* unit = serverSideUnitTable.table[unitId]; 85 | if (unit == NULL) { 86 | UnitHashTable clientSideUnitTable = D2CLIENT_ClientSideUnitHashTables[static_cast(type)]; 87 | unit = clientSideUnitTable.table[unitId]; 88 | } 89 | return unit; 90 | } 91 | 92 | Unit* FindUnit(uint32_t unitId, UnitType type) { 93 | Unit* unit = D2CLIENT_FindServerSideUnit(unitId, type); 94 | if (unit == NULL) { 95 | unit = D2CLIENT_FindClientSideUnit(unitId, type); 96 | } 97 | return unit; 98 | } 99 | 100 | uint32_t __fastcall GetDllOffset(uint32_t baseAddress, int offset) { 101 | DEBUG_LOG(std::format(L"baseAddress: {}\n", baseAddress)); 102 | DEBUG_LOG(std::format(L"offset: {}\n", offset)); 103 | DEBUG_LOG(std::format(L"return: {}\n", baseAddress + offset)); 104 | if (offset < 0) 105 | return (uint32_t)GetProcAddress((HMODULE)baseAddress, (LPCSTR)(-offset)); 106 | return baseAddress + offset; 107 | } 108 | 109 | std::wstring_view ltrim(std::wstring_view s) { 110 | s.remove_prefix(std::distance(s.cbegin(), std::find_if(s.cbegin(), s.cend(), 111 | [](wchar_t c) {return !iswspace(c); }))); 112 | 113 | return s; 114 | } 115 | 116 | std::wstring_view rtrim(std::wstring_view s) { 117 | s.remove_suffix(std::distance(s.crbegin(), std::find_if(s.crbegin(), s.crend(), 118 | [](wchar_t c) {return !iswspace(c); }))); 119 | 120 | return s; 121 | } 122 | 123 | std::wstring_view trim(std::wstring_view s) { 124 | return ltrim(rtrim(s)); 125 | } 126 | 127 | std::wstring ltrim_copy(std::wstring_view s) { 128 | return std::wstring(ltrim(s)); 129 | } 130 | 131 | // trim from end (copying) 132 | std::wstring rtrim_copy(std::wstring_view s) { 133 | return std::wstring(rtrim(s)); 134 | } 135 | 136 | // trim from both ends (copying) 137 | std::wstring trim_copy(std::wstring_view s) { 138 | return std::wstring(trim(s)); 139 | } 140 | 141 | void replace(std::wstring& subject, std::wstring_view search, std::wstring_view replace) { 142 | size_t pos = 0; 143 | while ((pos = subject.find(search, pos)) != std::wstring_view::npos) { 144 | subject.replace(pos, search.length(), replace); 145 | pos += replace.length(); 146 | } 147 | } 148 | 149 | std::vector split(std::wstring_view stringToSplit, std::wstring_view regexPattern) 150 | { 151 | std::vector result; 152 | 153 | const std::wregex rgx(regexPattern.begin(), regexPattern.end()); 154 | std::regex_token_iterator iter(stringToSplit.begin(), stringToSplit.end(), rgx, -1); 155 | 156 | for (std::regex_token_iterator end; iter != end; ++iter) 157 | { 158 | result.emplace_back(iter->str()); 159 | } 160 | 161 | return result; 162 | } 163 | 164 | ItemsTxt* GetItemsTxt(Unit* pUnit) { 165 | return &D2COMMON_ItemDataTbl->pItemsTxt[pUnit->dwLineId]; 166 | } 167 | 168 | std::wstring GetItemCode(Unit* pUnit) { 169 | ItemsTxt* pItemTxt = GetItemsTxt(pUnit); 170 | std::wstring wCode = std::wstring(4, L' '); 171 | mbstowcs(&wCode[0], pItemTxt->szCode, 4); 172 | wCode = trim(wCode); 173 | return wCode; 174 | } 175 | 176 | int32_t GetQualityLevel(Unit* pItem) { 177 | ItemsTxt* pItemTxt = GetItemsTxt(pItem); 178 | int32_t quality = -1; 179 | if (pItemTxt->dwCode == pItemTxt->dwUltraCode) { 180 | quality = 2; 181 | } 182 | else if (pItemTxt->dwCode == pItemTxt->dwUberCode) { 183 | quality = 1; 184 | } 185 | else if (pItemTxt->dwCode == pItemTxt->dwNormCode) { 186 | quality = 0; 187 | } 188 | return quality; 189 | } 190 | 191 | -------------------------------------------------------------------------------- /Utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "D2Structs.h" 8 | #include "D2Tables.h" 9 | #include 10 | #include 11 | #include 12 | 13 | #ifdef _DEBUG 14 | #define DEBUG_LOG(f) std::wprintf(f.c_str()); //PrintGameString(f, TextColor::YELLOW); 15 | #else 16 | #define DEBUG_LOG(f) ; 17 | #endif 18 | 19 | #define INFO_LOG(f) PrintGameString(f, TextColor::YELLOW); 20 | #define WARN_LOG(f) PrintGameString(f, TextColor::ORANGE); 21 | #define ERROR_LOG(f) PrintGameString(f, TextColor::RED); 22 | 23 | 24 | //Hooking 25 | uint32_t __fastcall GetDllOffset(uint32_t baseAddress, int offset); 26 | 27 | D2Version GetGameVersion(); 28 | void PrintGameString(const std::wstring& wStr, TextColor color); 29 | 30 | Unit* FindUnitFromTable(uint32_t unitId, UnitType type); 31 | Unit* FindUnit(uint32_t unitId, UnitType type); 32 | 33 | //String funcs 34 | std::wstring_view ltrim(std::wstring_view s); 35 | std::wstring_view rtrim(std::wstring_view s); 36 | std::wstring_view trim(std::wstring_view s); 37 | std::wstring ltrim_copy(std::wstring_view s); 38 | std::wstring rtrim_copy(std::wstring_view s); 39 | std::wstring trim_copy(std::wstring_view s); 40 | 41 | void replace(std::wstring& subject, std::wstring_view search, std::wstring_view replace); 42 | std::vector split(std::wstring_view stringToSplit, std::wstring_view regexPattern); 43 | 44 | //Utility D2 Methods 45 | ItemsTxt* GetItemsTxt(Unit* pUnit); 46 | std::wstring GetItemCode(Unit* pUnit); 47 | int32_t GetQualityLevel(Unit* pItem); 48 | 49 | // Heterogeneous lookup for std::unordered_map 50 | namespace utility { 51 | namespace detail { 52 | template 53 | struct char_type_helper { 54 | using type = std::remove_cv_t>; 55 | }; 56 | 57 | template 58 | struct char_type_helper> { 59 | using type = CharT; 60 | }; 61 | 62 | template 63 | struct char_type_helper> { 64 | using type = CharT; 65 | }; 66 | 67 | template 68 | using char_type_helper_t = char_type_helper::type; 69 | 70 | template 71 | struct string_equal { 72 | using is_transparent = void; 73 | 74 | bool operator()(std::basic_string_view> lhs, 75 | std::basic_string_view> rhs) const noexcept { 76 | return lhs == rhs; 77 | } 78 | }; 79 | 80 | template 81 | struct string_hash { 82 | using is_transparent = void; 83 | 84 | auto operator()(std::basic_string_view> str) const noexcept { 85 | return std::hash>>{}(str); 86 | } 87 | }; 88 | 89 | template 90 | struct string_iequal { 91 | using is_transparent = void; 92 | 93 | auto operator()(std::wstring_view lhs, std::wstring_view rhs) const noexcept { 94 | return std::ranges::equal(lhs, rhs, [](wchar_t a, wchar_t b) { 95 | return std::towlower(a) == std::towlower(b); 96 | }); 97 | } 98 | 99 | }; 100 | 101 | constexpr size_t FNV_Offset_Basis = 2166136261u; 102 | constexpr size_t FNV_Prime = 16777619u; 103 | 104 | template 105 | struct string_ihash { 106 | using is_transparent = void; 107 | 108 | size_t operator()(std::wstring_view str) const noexcept { 109 | static_assert(sizeof(decltype(str)::traits_type::char_type) == 2); 110 | 111 | size_t hash{ FNV_Offset_Basis }; 112 | for (auto ch : str) { 113 | ch = std::towlower(ch); 114 | hash ^= static_cast(ch & 0xFF); 115 | hash *= FNV_Prime; 116 | hash ^= static_cast(ch >> 8); 117 | hash *= FNV_Prime; 118 | } 119 | return hash; 120 | } 121 | }; 122 | } // namespace detail 123 | 124 | template 125 | struct unordered_string_map_helper; 126 | 127 | template 128 | struct unordered_string_map_helper, T, Hash, KeyEqual, Allocator> { 129 | using type = std::unordered_map, T, Hash, KeyEqual, Allocator>; 130 | }; 131 | 132 | template>> 133 | using string_umap = unordered_string_map_helper, detail::string_equal, Allocator>::type; 134 | 135 | template>> 136 | using string_umap_icase = unordered_string_map_helper, detail::string_iequal, Allocator>::type; 137 | } // namespace utility 138 | -------------------------------------------------------------------------------- /d2lootfilter.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30711.63 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "d2lootfilter", "d2lootfilter.vcxproj", "{9824E123-2431-4197-8558-F30F76901D4F}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {9824E123-2431-4197-8558-F30F76901D4F}.Debug|x64.ActiveCfg = Debug|Win32 17 | {9824E123-2431-4197-8558-F30F76901D4F}.Debug|x86.ActiveCfg = Debug|Win32 18 | {9824E123-2431-4197-8558-F30F76901D4F}.Debug|x86.Build.0 = Debug|Win32 19 | {9824E123-2431-4197-8558-F30F76901D4F}.Release|x64.ActiveCfg = Release|Win32 20 | {9824E123-2431-4197-8558-F30F76901D4F}.Release|x64.Build.0 = Release|Win32 21 | {9824E123-2431-4197-8558-F30F76901D4F}.Release|x86.ActiveCfg = Release|Win32 22 | {9824E123-2431-4197-8558-F30F76901D4F}.Release|x86.Build.0 = Release|Win32 23 | EndGlobalSection 24 | GlobalSection(SolutionProperties) = preSolution 25 | HideSolutionNode = FALSE 26 | EndGlobalSection 27 | GlobalSection(ExtensibilityGlobals) = postSolution 28 | SolutionGuid = {C8B12A2B-7D9A-40FF-8BF2-DE5F15C59F1B} 29 | EndGlobalSection 30 | EndGlobal 31 | -------------------------------------------------------------------------------- /d2lootfilter.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | 16.0 15 | Win32Proj 16 | {9824e123-2431-4197-8558-f30f76901d4f} 17 | d2lootfilter 18 | 10.0 19 | x86-windows-static 20 | 21 | 22 | 23 | DynamicLibrary 24 | true 25 | v143 26 | Unicode 27 | 28 | 29 | DynamicLibrary 30 | false 31 | v143 32 | true 33 | Unicode 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | true 55 | d2lootfilter 56 | 57 | 58 | true 59 | d2lootfilter 60 | 61 | 62 | 63 | Level3 64 | true 65 | WIN32;_DEBUG;_WINDOWS;_USRDLL;_NO_ASYNCRTIMP;_CRT_SECURE_NO_WARNINGS;_ITERATOR_DEBUG_LEVEL=0;%(PreprocessorDefinitions) 66 | true 67 | MultiThreadedDebug 68 | true 69 | stdcpp20 70 | 71 | 72 | Windows 73 | true 74 | false 75 | 76 | 77 | set "D2Path=$(D2Path)" 78 | if defined D2Path ( 79 | xcopy "$(SolutionDir)$(Configuration)" "$(D2Path)" /h /i /c /k /e /r /y 80 | ) 81 | 82 | 83 | 84 | 85 | 86 | Level3 87 | true 88 | true 89 | true 90 | WIN32;NDEBUG;_WINDOWS;_USRDLL;_NO_ASYNCRTIMP;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) 91 | true 92 | MultiThreaded 93 | stdcpp20 94 | 95 | 96 | Windows 97 | true 98 | true 99 | true 100 | false 101 | UseFastLinkTimeCodeGeneration 102 | 103 | 104 | set "D2Path=$(D2Path)" 105 | if defined D2Path ( 106 | xcopy "$(SolutionDir)$(Configuration)" "$(D2Path)" /h /i /c /k /e /r /y 107 | ) 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /d2lootfilter.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | Source Files 47 | 48 | 49 | 50 | 51 | Header Files 52 | 53 | 54 | Header Files 55 | 56 | 57 | Header Files 58 | 59 | 60 | Header Files 61 | 62 | 63 | Header Files 64 | 65 | 66 | Header Files 67 | 68 | 69 | Header Files 70 | 71 | 72 | Header Files 73 | 74 | 75 | Header Files 76 | 77 | 78 | Header Files 79 | 80 | 81 | Header Files 82 | 83 | 84 | Header Files 85 | 86 | 87 | Header Files 88 | 89 | 90 | Header Files 91 | 92 | 93 | -------------------------------------------------------------------------------- /d2lootfilter.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(D2Path)\Game.exe 5 | -w 6 | $(D2Path) 7 | WindowsLocalDebugger 8 | 9 | 10 | $(D2Path)\Game.exe 11 | -w 12 | $(D2Path) 13 | WindowsLocalDebugger 14 | 15 | -------------------------------------------------------------------------------- /dllmain.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ItemFilter.h" 3 | 4 | void Error(const wchar_t* wszMessage) { 5 | MessageBox(nullptr, wszMessage, L"Error", MB_OK | MB_ICONSTOP); 6 | exit(0); 7 | } 8 | 9 | BOOL DllAttach() { 10 | 11 | #ifdef _DEBUG 12 | AllocConsole(); 13 | AttachConsole(GetCurrentProcessId()); 14 | freopen_s(reinterpret_cast(stdin), "CONIN$", "r", stdin); 15 | freopen_s(reinterpret_cast(stdout), "CONOUT$", "w", stdout); 16 | freopen_s(reinterpret_cast(stderr), "CONOUT$", "w", stderr); 17 | #endif 18 | if (GetGameVersion() == D2Version::ERR) { 19 | Error(L"Could not determine the game version."); 20 | } 21 | 22 | ItemFilter::Initialize(); 23 | return TRUE; 24 | } 25 | 26 | BOOL APIENTRY DllMain( HMODULE hModule, 27 | DWORD ul_reason_for_call, 28 | LPVOID lpReserved 29 | ) 30 | { 31 | switch (ul_reason_for_call) 32 | { 33 | case DLL_PROCESS_ATTACH: 34 | return DllAttach(); 35 | case DLL_THREAD_ATTACH: 36 | case DLL_THREAD_DETACH: 37 | case DLL_PROCESS_DETACH: 38 | break; 39 | } 40 | return TRUE; 41 | } 42 | 43 | -------------------------------------------------------------------------------- /doc/ENG/Class.md: -------------------------------------------------------------------------------- 1 | |Class|Id| 2 | |-|-| 3 | |Amazon Bow|85| 4 | |Amazon Item|60| 5 | |Amazon Javelin|87| 6 | |Amazon Spear|86| 7 | |Amethyst|96| 8 | |Amulet|12| 9 | |Antidote Potion|80| 10 | |Any Armor|50| 11 | |Any Shield|51| 12 | |Armor|3| 13 | |Assassin Item|65| 14 | |Auric Shields|70| 15 | |Axe|28| 16 | |Barbarian Item|61| 17 | |Belt|19| 18 | |Blunt|57| 19 | |Body Part|40| 20 | |Book|18| 21 | |Boots|15| 22 | |Bow|27| 23 | |Bow Quiver|5| 24 | |Charm|13| 25 | |Chipped Gem|91| 26 | |Circlet|75| 27 | |Class Specific|59| 28 | |Cloak|73| 29 | |Club|29| 30 | |Combo Weapon|49| 31 | |Crossbow|35| 32 | |Crossbow Quiver|6| 33 | |Diamond|97| 34 | |Druid Item|66| 35 | |Elixir|11| 36 | |Emerald|98| 37 | |Flawed Gem|92| 38 | |Flawless Gem|94| 39 | |Gem|20| 40 | |Gloves|16| 41 | |Gold|4| 42 | |Hammer|31| 43 | |Hand to Hand|67| 44 | |Hand to Hand 2|88| 45 | |Healing Potion|76| 46 | |Helm|37| 47 | |Herb|8| 48 | |Javelin|44| 49 | |Jewel|58| 50 | |Key|41| 51 | |Knife|32| 52 | |Large Charm|84| 53 | |Mace|36| 54 | |Magic Bow Quiv|89| 55 | |Magic Xbow Quiv|90| 56 | |Mana Potion|77| 57 | |Medium Charm|83| 58 | |Melee Weapon|46| 59 | |Miscellaneous|52| 60 | |Missile|56| 61 | |Missile Potion|38| 62 | |Missile Weapon|47| 63 | |Necromancer Item|62| 64 | |None|0| 65 | |Not Used|14| 66 | |Orb|68| 67 | |Paladin Item|63| 68 | |Pelt|72| 69 | |Perfect Gem|95| 70 | |Player Body Part|7| 71 | |Polearm|34| 72 | |Potion|9| 73 | |Primal Helm|71| 74 | |Quest|39| 75 | |Rejuv Potion|78| 76 | |Ring|10| 77 | |Ruby|99| 78 | |Rune|74| 79 | |Sapphire|100| 80 | |Scepter|24| 81 | |Scroll|22| 82 | |Second Hand|54| 83 | |Shield|2| 84 | |Skull|102| 85 | |Small Charm|82| 86 | |Socket Filler|53| 87 | |Sorceress Item|64| 88 | |Spear|33| 89 | |Staff|26| 90 | |Stamina Potion|79| 91 | |Standard Gem|93| 92 | |Staves And Rods|55| 93 | |Sword|30| 94 | |Thawing Potion|81| 95 | |Throwing Axe|43| 96 | |Throwing Knife|42| 97 | |Thrown Weapon|48| 98 | |Topaz|101| 99 | |Torch|21| 100 | |Voodoo Heads|69| 101 | |Wand|25| 102 | |Weapon|45| -------------------------------------------------------------------------------- /doc/ENG/Runes.md: -------------------------------------------------------------------------------- 1 | |Rune|Stat| 2 | |-|-| 3 | |Amn|11| 4 | |Amn Rune|11| 5 | |Ber|30| 6 | |Ber Rune|30| 7 | |Cham|32| 8 | |Cham Rune|32| 9 | |Dol|14| 10 | |Dol Rune|14| 11 | |El|1| 12 | |El Rune|1| 13 | |Eld|2| 14 | |Eld Rune|2| 15 | |Eth|5| 16 | |Eth Rune|5| 17 | |Fal|19| 18 | |Fal Rune|19| 19 | |Gul|25| 20 | |Gul Rune|25| 21 | |Hel|15| 22 | |Hel Rune|15| 23 | |Io|16| 24 | |Io Rune|16| 25 | |Ist|24| 26 | |Ist Rune|24| 27 | |Ith|6| 28 | |Ith Rune|6| 29 | |Jah|31| 30 | |Jah Rune|31| 31 | |Ko|18| 32 | |Ko Rune|18| 33 | |Lem|20| 34 | |Lem Rune|20| 35 | |Lo|28| 36 | |Lo Rune|28| 37 | |Lum|17| 38 | |Lum Rune|17| 39 | |Mal|23| 40 | |Mal Rune|23| 41 | |Nef|4| 42 | |Nef Rune|4| 43 | |Ohm|27| 44 | |Ohm Rune|27| 45 | |Ort|9| 46 | |Ort Rune|9| 47 | |Pul|21| 48 | |Pul Rune|21| 49 | |Ral|8| 50 | |Ral Rune|8| 51 | |Shael|13| 52 | |Shael Rune|13| 53 | |Sol|12| 54 | |Sol Rune|12| 55 | |Sur|29| 56 | |Sur Rune|29| 57 | |Tal|7| 58 | |Tal Rune|7| 59 | |Thul|10| 60 | |Thul Rune|10| 61 | |Tir|3| 62 | |Tir Rune|3| 63 | |Um|22| 64 | |Um Rune|22| 65 | |Vex|26| 66 | |Vex Rune|26| 67 | |Zod|33| 68 | |Zod Rune|33| -------------------------------------------------------------------------------- /doc/ENG/Skills.md: -------------------------------------------------------------------------------- 1 | |Skill|Stat| 2 | |-|-| 3 | |"Amazon Skills"|Stat(83,0))| 4 | |"Amazon +%d to Bow and Crossbow Skills"|Stat(188,0))| 5 | |"Amazon +%d to Passive and Magic Skills"|Stat(188,1))| 6 | |"Amazon +%d to Javelin and Spear Skills"|Stat(188,2))| 7 | |"Sorceress Skills"|Stat(83,1))| 8 | |"Sorceress +%d to Fire Skills"|Stat(188,8))| 9 | |"Sorceress +%d to Lightning Skills"|Stat(188,9))| 10 | |"Sorceress +%d to Cold Skills"|Stat(188,10))| 11 | |"Necromancer Skills"|Stat(83,2))| 12 | |"Necromancer +%d to Curses"|Stat(188,16))| 13 | |"Necromancer +%d to Poison and Bone Skills"|Stat(188,17))| 14 | |"Necromancer +%d to Summoning Skills"|Stat(188,18))| 15 | |"Paladin Skills"|Stat(83,3))| 16 | |"Paladin +%d to Combat Skills"|Stat(188,24))| 17 | |"Paladin +%d to Offensive Auras"|Stat(188,33))| 18 | |"Paladin +%d to Defensive Auras"|Stat(188,26))| 19 | |"Barbarian Skills"|Stat(83,4))| 20 | |"Barbarian +%d to Combat Skills"|Stat(188,32))| 21 | |"Barbarian +%d to Masteries"|Stat(188,33))| 22 | |"Barbarian +%d to Warcries"|Stat(188,34))| 23 | |"Druid Skills"|Stat(83,5))| 24 | |"Druid +%d to Summoning Skills"|Stat(188,40))| 25 | |"Druid +%d to Shape Shifting Skills"|Stat(188,41))| 26 | |"Druid +%d to Elemental Skills"|Stat(188,42))| 27 | |"Assassin Skills"|Stat(83,6))| 28 | |"Assassin +%d to Traps"|Stat(188,48))| 29 | |"Assassin +%d to Shadow Disciplines"|Stat(188,49))| 30 | |"Assassin +%d to Martial Arts"|Stat(188,50))| 31 | |"Attack"|Stat(107,0))| 32 | |"Kick"|Stat(107,1))| 33 | |"Throw"|Stat(107,2))| 34 | |"Unsummon"|Stat(107,3))| 35 | |"Left Hand Throw"|Stat(107,4))| 36 | |"Left Hand Swing"|Stat(107,5))| 37 | |"Magic Arrow"|Stat(107,6))| 38 | |"Fire Arrow"|Stat(107,7))| 39 | |"Inner Sight"|Stat(107,8))| 40 | |"Critical Strike"|Stat(107,9))| 41 | |"Jab"|Stat(107,10))| 42 | |"Cold Arrow"|Stat(107,11))| 43 | |"Multiple Shot"|Stat(107,12))| 44 | |"Dodge"|Stat(107,13))| 45 | |"Power Strike"|Stat(107,14))| 46 | |"Poison Javelin"|Stat(107,15))| 47 | |"Exploding Arrow"|Stat(107,16))| 48 | |"Slow Missiles"|Stat(107,17))| 49 | |"Avoid"|Stat(107,18))| 50 | |"Impale"|Stat(107,19))| 51 | |"Lightning Bolt"|Stat(107,20))| 52 | |"Ice Arrow"|Stat(107,21))| 53 | |"Guided Arrow"|Stat(107,22))| 54 | |"Penetrate"|Stat(107,23))| 55 | |"Charged Strike"|Stat(107,24))| 56 | |"Plague Javelin"|Stat(107,25))| 57 | |"Strafe"|Stat(107,26))| 58 | |"Immolation Arrow"|Stat(107,27))| 59 | |"Dopplezon"|Stat(107,28))| 60 | |"Evade"|Stat(107,29))| 61 | |"Fend"|Stat(107,30))| 62 | |"Freezing Arrow"|Stat(107,31))| 63 | |"Valkyrie"|Stat(107,32))| 64 | |"Pierce"|Stat(107,33))| 65 | |"Lightning Strike"|Stat(107,34))| 66 | |"Lightning Fury"|Stat(107,35))| 67 | |"Fire Bolt"|Stat(107,36))| 68 | |"Warmth"|Stat(107,37))| 69 | |"Charged Bolt"|Stat(107,38))| 70 | |"Ice Bolt"|Stat(107,39))| 71 | |"Frozen Armor"|Stat(107,40))| 72 | |"Inferno"|Stat(107,41))| 73 | |"Static Field"|Stat(107,42))| 74 | |"Telekinesis"|Stat(107,43))| 75 | |"Frost Nova"|Stat(107,44))| 76 | |"Ice Blast"|Stat(107,45))| 77 | |"Blaze"|Stat(107,46))| 78 | |"Fire Ball"|Stat(107,47))| 79 | |"Nova"|Stat(107,48))| 80 | |"Lightning"|Stat(107,49))| 81 | |"Shiver Armor"|Stat(107,50))| 82 | |"Fire Wall"|Stat(107,51))| 83 | |"Enchant"|Stat(107,52))| 84 | |"Chain Lightning"|Stat(107,53))| 85 | |"Teleport"|Stat(107,54))| 86 | |"Glacial Spike"|Stat(107,55))| 87 | |"Meteor"|Stat(107,56))| 88 | |"Thunder Storm"|Stat(107,57))| 89 | |"Energy Shield"|Stat(107,58))| 90 | |"Blizzard"|Stat(107,59))| 91 | |"Chilling Armor"|Stat(107,60))| 92 | |"Fire Mastery"|Stat(107,61))| 93 | |"Hydra"|Stat(107,62))| 94 | |"Lightning Mastery"|Stat(107,63))| 95 | |"Frozen Orb"|Stat(107,64))| 96 | |"Cold Mastery"|Stat(107,65))| 97 | |"Amplify Damage"|Stat(107,66))| 98 | |"Teeth"|Stat(107,67))| 99 | |"Bone Armor"|Stat(107,68))| 100 | |"Skeleton Mastery"|Stat(107,69))| 101 | |"Raise Skeleton"|Stat(107,70))| 102 | |"Dim Vision"|Stat(107,71))| 103 | |"Weaken"|Stat(107,72))| 104 | |"Poison Dagger"|Stat(107,73))| 105 | |"Corpse Explosion"|Stat(107,74))| 106 | |"Clay Golem"|Stat(107,75))| 107 | |"Iron Maiden"|Stat(107,76))| 108 | |"Terror"|Stat(107,77))| 109 | |"Bone Wall"|Stat(107,78))| 110 | |"Golem Mastery"|Stat(107,79))| 111 | |"Raise Skeletal Mage"|Stat(107,80))| 112 | |"Confuse"|Stat(107,81))| 113 | |"Life Tap"|Stat(107,82))| 114 | |"Poison Explosion"|Stat(107,83))| 115 | |"Bone Spear"|Stat(107,84))| 116 | |"BloodGolem"|Stat(107,85))| 117 | |"Attract"|Stat(107,86))| 118 | |"Decrepify"|Stat(107,87))| 119 | |"Bone Prison"|Stat(107,88))| 120 | |"Summon Resist"|Stat(107,89))| 121 | |"IronGolem"|Stat(107,90))| 122 | |"Lower Resist"|Stat(107,91))| 123 | |"Poison Nova"|Stat(107,92))| 124 | |"Bone Spirit"|Stat(107,93))| 125 | |"FireGolem"|Stat(107,94))| 126 | |"Revive"|Stat(107,95))| 127 | |"Sacrifice"|Stat(107,96))| 128 | |"Smite"|Stat(107,97))| 129 | |"Might"|Stat(107,98))| 130 | |"Prayer"|Stat(107,99))| 131 | |"Resist Fire"|Stat(107,100))| 132 | |"Holy Bolt"|Stat(107,101))| 133 | |"Holy Fire"|Stat(107,102))| 134 | |"Thorns"|Stat(107,103))| 135 | |"Defiance"|Stat(107,104))| 136 | |"Resist Cold"|Stat(107,105))| 137 | |"Zeal"|Stat(107,106))| 138 | |"Charge"|Stat(107,107))| 139 | |"Blessed Aim"|Stat(107,108))| 140 | |"Cleansing"|Stat(107,109))| 141 | |"Resist Lightning"|Stat(107,110))| 142 | |"Vengeance"|Stat(107,111))| 143 | |"Blessed Hammer"|Stat(107,112))| 144 | |"Concentration"|Stat(107,113))| 145 | |"Holy Freeze"|Stat(107,114))| 146 | |"Vigor"|Stat(107,115))| 147 | |"Conversion"|Stat(107,116))| 148 | |"Holy Shield"|Stat(107,117))| 149 | |"Holy Shock"|Stat(107,118))| 150 | |"Sanctuary"|Stat(107,119))| 151 | |"Meditation"|Stat(107,120))| 152 | |"Fist of the Heavens"|Stat(107,121))| 153 | |"Fanaticism"|Stat(107,122))| 154 | |"Conviction"|Stat(107,123))| 155 | |"Redemption"|Stat(107,124))| 156 | |"Salvation"|Stat(107,125))| 157 | |"Bash"|Stat(107,126))| 158 | |"Sword Mastery"|Stat(107,127))| 159 | |"Axe Mastery"|Stat(107,128))| 160 | |"Mace Mastery"|Stat(107,129))| 161 | |"Howl"|Stat(107,130))| 162 | |"Find Potion"|Stat(107,131))| 163 | |"Leap"|Stat(107,132))| 164 | |"Double Swing"|Stat(107,133))| 165 | |"Pole Arm Mastery"|Stat(107,134))| 166 | |"Throwing Mastery"|Stat(107,135))| 167 | |"Spear Mastery"|Stat(107,136))| 168 | |"Taunt"|Stat(107,137))| 169 | |"Shout"|Stat(107,138))| 170 | |"Stun"|Stat(107,139))| 171 | |"Double Throw"|Stat(107,140))| 172 | |"Increased Stamina"|Stat(107,141))| 173 | |"Find Item"|Stat(107,142))| 174 | |"Leap Attack"|Stat(107,143))| 175 | |"Concentrate"|Stat(107,144))| 176 | |"Iron Skin"|Stat(107,145))| 177 | |"Battle Cry"|Stat(107,146))| 178 | |"Frenzy"|Stat(107,147))| 179 | |"Increased Speed"|Stat(107,148))| 180 | |"Battle Orders"|Stat(107,149))| 181 | |"Grim Ward"|Stat(107,150))| 182 | |"Whirlwind"|Stat(107,151))| 183 | |"Berserk"|Stat(107,152))| 184 | |"Natural Resistance"|Stat(107,153))| 185 | |"War Cry"|Stat(107,154))| 186 | |"Battle Command"|Stat(107,155))| 187 | |"Fire Hit"|Stat(107,156))| 188 | |"UnHolyBolt"|Stat(107,157))| 189 | |"SkeletonRaise"|Stat(107,158))| 190 | |"MaggotEgg"|Stat(107,159))| 191 | |"ShamanFire"|Stat(107,160))| 192 | |"MagottUp"|Stat(107,161))| 193 | |"MagottDown"|Stat(107,162))| 194 | |"MagottLay"|Stat(107,163))| 195 | |"AndrialSpray"|Stat(107,164))| 196 | |"Jump"|Stat(107,165))| 197 | |"Swarm Move"|Stat(107,166))| 198 | |"Nest"|Stat(107,167))| 199 | |"Quick Strike"|Stat(107,168))| 200 | |"VampireFireball"|Stat(107,169))| 201 | |"VampireFirewall"|Stat(107,170))| 202 | |"VampireMeteor"|Stat(107,171))| 203 | |"GargoyleTrap"|Stat(107,172))| 204 | |"SpiderLay"|Stat(107,173))| 205 | |"VampireHeal"|Stat(107,174))| 206 | |"VampireRaise"|Stat(107,175))| 207 | |"Submerge"|Stat(107,176))| 208 | |"FetishAura"|Stat(107,177))| 209 | |"FetishInferno"|Stat(107,178))| 210 | |"ZakarumHeal"|Stat(107,179))| 211 | |"Emerge"|Stat(107,180))| 212 | |"Resurrect"|Stat(107,181))| 213 | |"Bestow"|Stat(107,182))| 214 | |"MissileSkill1"|Stat(107,183))| 215 | |"MonTeleport"|Stat(107,184))| 216 | |"PrimeLightning"|Stat(107,185))| 217 | |"PrimeBolt"|Stat(107,186))| 218 | |"PrimeBlaze"|Stat(107,187))| 219 | |"PrimeFirewall"|Stat(107,188))| 220 | |"PrimeSpike"|Stat(107,189))| 221 | |"PrimeIceNova"|Stat(107,190))| 222 | |"PrimePoisonball"|Stat(107,191))| 223 | |"PrimePoisonNova"|Stat(107,192))| 224 | |"DiabLight"|Stat(107,193))| 225 | |"DiabCold"|Stat(107,194))| 226 | |"DiabFire"|Stat(107,195))| 227 | |"FingerMageSpider"|Stat(107,196))| 228 | |"DiabWall"|Stat(107,197))| 229 | |"DiabRun"|Stat(107,198))| 230 | |"DiabPrison"|Stat(107,199))| 231 | |"PoisonBallTrap"|Stat(107,200))| 232 | |"AndyPoisonBolt"|Stat(107,201))| 233 | |"HireableMissile"|Stat(107,202))| 234 | |"DesertTurret"|Stat(107,203))| 235 | |"ArcaneTower"|Stat(107,204))| 236 | |"MonBlizzard"|Stat(107,205))| 237 | |"Mosquito"|Stat(107,206))| 238 | |"CursedBallTrapRight"|Stat(107,207))| 239 | |"CursedBallTrapLeft"|Stat(107,208))| 240 | |"MonFrozenArmor"|Stat(107,209))| 241 | |"MonBoneArmor"|Stat(107,210))| 242 | |"MonBoneSpirit"|Stat(107,211))| 243 | |"MonCurseCast"|Stat(107,212))| 244 | |"HellMeteor"|Stat(107,213))| 245 | |"RegurgitatorEat"|Stat(107,214))| 246 | |"MonFrenzy"|Stat(107,215))| 247 | |"QueenDeath"|Stat(107,216))| 248 | |"Scroll of Identify"|Stat(107,217))| 249 | |"Book of Identify"|Stat(107,218))| 250 | |"Scroll of Townportal"|Stat(107,219))| 251 | |"Book of Townportal"|Stat(107,220))| 252 | |"Raven"|Stat(107,221))| 253 | |"Plague Poppy"|Stat(107,222))| 254 | |"Wearwolf"|Stat(107,223))| 255 | |"Shape Shifting"|Stat(107,224))| 256 | |"Firestorm"|Stat(107,225))| 257 | |"Oak Sage"|Stat(107,226))| 258 | |"Summon Spirit Wolf"|Stat(107,227))| 259 | |"Wearbear"|Stat(107,228))| 260 | |"Molten Boulder"|Stat(107,229))| 261 | |"Arctic Blast"|Stat(107,230))| 262 | |"Cycle of Life"|Stat(107,231))| 263 | |"Feral Rage"|Stat(107,232))| 264 | |"Maul"|Stat(107,233))| 265 | |"Eruption"|Stat(107,234))| 266 | |"Cyclone Armor"|Stat(107,235))| 267 | |"Heart of Wolverine"|Stat(107,236))| 268 | |"Summon Fenris"|Stat(107,237))| 269 | |"Rabies"|Stat(107,238))| 270 | |"Fire Claws"|Stat(107,239))| 271 | |"Twister"|Stat(107,240))| 272 | |"Vines"|Stat(107,241))| 273 | |"Hunger"|Stat(107,242))| 274 | |"Shock Wave"|Stat(107,243))| 275 | |"Volcano"|Stat(107,244))| 276 | |"Tornado"|Stat(107,245))| 277 | |"Spirit of Barbs"|Stat(107,246))| 278 | |"Summon Grizzly"|Stat(107,247))| 279 | |"Fury"|Stat(107,248))| 280 | |"Armageddon"|Stat(107,249))| 281 | |"Hurricane"|Stat(107,250))| 282 | |"Fire Trauma"|Stat(107,251))| 283 | |"Claw Mastery"|Stat(107,252))| 284 | |"Psychic Hammer"|Stat(107,253))| 285 | |"Tiger Strike"|Stat(107,254))| 286 | |"Dragon Talon"|Stat(107,255))| 287 | |"Shock Field"|Stat(107,256))| 288 | |"Blade Sentinel"|Stat(107,257))| 289 | |"Quickness"|Stat(107,258))| 290 | |"Fists of Fire"|Stat(107,259))| 291 | |"Dragon Claw"|Stat(107,260))| 292 | |"Charged Bolt Sentry"|Stat(107,261))| 293 | |"Wake of Fire Sentry"|Stat(107,262))| 294 | |"Weapon Block"|Stat(107,263))| 295 | |"Cloak of Shadows"|Stat(107,264))| 296 | |"Cobra Strike"|Stat(107,265))| 297 | |"Blade Fury"|Stat(107,266))| 298 | |"Fade"|Stat(107,267))| 299 | |"Shadow Warrior"|Stat(107,268))| 300 | |"Claws of Thunder"|Stat(107,269))| 301 | |"Dragon Tail"|Stat(107,270))| 302 | |"Lightning Sentry"|Stat(107,271))| 303 | |"Inferno Sentry"|Stat(107,272))| 304 | |"Mind Blast"|Stat(107,273))| 305 | |"Blades of Ice"|Stat(107,274))| 306 | |"Dragon Flight"|Stat(107,275))| 307 | |"Death Sentry"|Stat(107,276))| 308 | |"Blade Shield"|Stat(107,277))| 309 | |"Venom"|Stat(107,278))| 310 | |"Shadow Master"|Stat(107,279))| 311 | |"Royal Strike"|Stat(107,280))| 312 | |"Wake Of Destruction Sentry"|Stat(107,281))| 313 | |"Imp Inferno"|Stat(107,282))| 314 | |"Imp Fireball"|Stat(107,283))| 315 | |"Baal Taunt"|Stat(107,284))| 316 | |"Baal Corpse Explode"|Stat(107,285))| 317 | |"Baal Monster Spawn"|Stat(107,286))| 318 | |"Catapult Charged Ball"|Stat(107,287))| 319 | |"Catapult Spike Ball"|Stat(107,288))| 320 | |"Suck Blood"|Stat(107,289))| 321 | |"Cry Help"|Stat(107,290))| 322 | |"Healing Vortex"|Stat(107,291))| 323 | |"Teleport 2"|Stat(107,292))| 324 | |"Self-resurrect"|Stat(107,293))| 325 | |"Vine Attack"|Stat(107,294))| 326 | |"Overseer Whip"|Stat(107,295))| 327 | |"Barbs Aura"|Stat(107,296))| 328 | |"Wolverine Aura"|Stat(107,297))| 329 | |"Oak Sage Aura"|Stat(107,298))| 330 | |"Imp Fire Missile"|Stat(107,299))| 331 | |"Impregnate"|Stat(107,300))| 332 | |"Siege Beast Stomp"|Stat(107,301))| 333 | |"MinionSpawner"|Stat(107,302))| 334 | |"CatapultBlizzard"|Stat(107,303))| 335 | |"CatapultPlague"|Stat(107,304))| 336 | |"CatapultMeteor"|Stat(107,305))| 337 | |"BoltSentry"|Stat(107,306))| 338 | |"CorpseCycler"|Stat(107,307))| 339 | |"DeathMaul"|Stat(107,308))| 340 | |"Defense Curse"|Stat(107,309))| 341 | |"Blood Mana"|Stat(107,310))| 342 | |"mon inferno sentry"|Stat(107,311))| 343 | |"mon death sentry"|Stat(107,312))| 344 | |"sentry lightning"|Stat(107,313))| 345 | |"fenris rage"|Stat(107,314))| 346 | |"Baal Tentacle"|Stat(107,315))| 347 | |"Baal Nova"|Stat(107,316))| 348 | |"Baal Inferno"|Stat(107,317))| 349 | |"Baal Cold Missiles"|Stat(107,318))| 350 | |"MegademonInferno"|Stat(107,319))| 351 | |"EvilHutSpawner"|Stat(107,320))| 352 | |"CountessFirewall"|Stat(107,321))| 353 | |"ImpBolt"|Stat(107,322))| 354 | |"Horror Arctic Blast"|Stat(107,323))| 355 | |"death sentry ltng"|Stat(107,324))| 356 | |"VineCycler"|Stat(107,325))| 357 | |"BearSmite"|Stat(107,326))| 358 | |"Resurrect2"|Stat(107,327))| 359 | |"BloodLordFrenzy"|Stat(107,328))| 360 | |"Baal Teleport"|Stat(107,329))| 361 | |"Imp Teleport"|Stat(107,330))| 362 | |"Baal Clone Teleport"|Stat(107,331))| 363 | |"ZakarumLightning"|Stat(107,332))| 364 | |"VampireMissile"|Stat(107,333))| 365 | |"MephistoMissile"|Stat(107,334))| 366 | |"DoomKnightMissile"|Stat(107,335))| 367 | |"RogueMissile"|Stat(107,336))| 368 | |"HydraMissile"|Stat(107,337))| 369 | |"NecromageMissile"|Stat(107,338))| 370 | |"MonBow"|Stat(107,339))| 371 | |"MonFireArrow"|Stat(107,340))| 372 | |"MonColdArrow"|Stat(107,341))| 373 | |"MonExplodingArrow"|Stat(107,342))| 374 | |"MonFreezingArrow"|Stat(107,343))| 375 | |"MonPowerStrike"|Stat(107,344))| 376 | |"SuccubusBolt"|Stat(107,345))| 377 | |"MephFrostNova"|Stat(107,346))| 378 | |"MonIceSpear"|Stat(107,347))| 379 | |"ShamanIce"|Stat(107,348))| 380 | |"Diablogeddon"|Stat(107,349))| 381 | |"Delerium Change"|Stat(107,350))| 382 | |"NihlathakCorpseExplosion"|Stat(107,351))| 383 | |"SerpentCharge"|Stat(107,352))| 384 | |"Trap Nova"|Stat(107,353))| 385 | |"UnHolyBoltEx"|Stat(107,354))| 386 | |"ShamanFireEx"|Stat(107,355))| 387 | |"Imp Fire Missile Ex"|Stat(107,356))| 388 | |"Interact"|Stat(107,357))| 389 | |"Loot"|Stat(107,358))| 390 | |"TownPortal"|Stat(107,359)) -------------------------------------------------------------------------------- /doc/ENG/Stats.md: -------------------------------------------------------------------------------- 1 | |Stat|Stat| 2 | |-|-| 3 | |"All Resist"|MinIn(Stat(39),Stat(43),Stat(41),Stat(45))| 4 | |"Any Resist"|Max(Stat(39),Stat(43),Stat(41),Stat(45))| 5 | |"Attack Rating"|Stat(19)| 6 | |"Cold Resist"|Stat(43)| 7 | |"Defense"|Stat(31)| 8 | |"Dexterity"|Stat(2)| 9 | |"Durability"|Stat(75)| 10 | |"Energy"|Stat(1)| 11 | |"Enhanced Damage"|Stat(17)| 12 | |"Enhanced Defense"|Stat(16)| 13 | |"Faster Cast Rate"|Stat(105)| 14 | |"Faster Hit Recovery"|Stat(99)| 15 | |"Faster Run Walk"|Stat(96)| 16 | |"Fire Resist"|Stat(39)| 17 | |"Gold Find"|Stat(79)| 18 | |"Increased Attack Speed"|Stat(93)| 19 | |"Life"|Stat(7)| 20 | |"Lightning Resist"|Stat(41)| 21 | |"Magic Find"|Stat(80)| 22 | |"Mana After Each Kill"|Stat(138)| 23 | |"Mana"|Stat(9)| 24 | |"Maximum Damage"|Stat(22)| 25 | |"Minimum Damage"|Stat(21)| 26 | |"Poison Resist"|Stat(45)| 27 | |"Repairs Durability"|Stat(252)| 28 | |"Required Level"|Stat(92)| 29 | |"Strength"|Stat(0)| 30 | |"Vitality"|Stat(3)| -------------------------------------------------------------------------------- /doc/ENG/Types.md: -------------------------------------------------------------------------------- 1 | |Type|Code| 2 | |-|-| 3 | |Hand Axe|hax | 4 | |Axe|axe | 5 | |Double Axe|2ax | 6 | |Military Pick|mpi | 7 | |War Axe|wax | 8 | |Large Axe|lax | 9 | |Broad Axe|bax | 10 | |Battle Axe|btx | 11 | |Great Axe|gax | 12 | |Giant Axe|gix | 13 | |Wand|wnd | 14 | |Yew Wand|ywn | 15 | |Bone Wand|bwn | 16 | |Grim Wand|gwn | 17 | |Club|clb | 18 | |Scepter|scp | 19 | |Grand Scepter|gsc | 20 | |War Scepter|wsp | 21 | |Spiked Club|spc | 22 | |Mace|mac | 23 | |Morning Star|mst | 24 | |Flail|fla | 25 | |War Hammer|whm | 26 | |Maul|mau | 27 | |Great Maul|gma | 28 | |Short Sword|ssd | 29 | |Scimitar|scm | 30 | |Sabre|sbr | 31 | |Falchion|flc | 32 | |Crystal Sword|crs | 33 | |Broad Sword|bsd | 34 | |Long Sword|lsd | 35 | |War Sword|wsd | 36 | |Two-Handed Sword|2hs | 37 | |Claymore|clm | 38 | |Giant Sword|gis | 39 | |Bastard Sword|bsw | 40 | |Flamberge|flb | 41 | |Great Sword|gsd | 42 | |Dagger|dgr | 43 | |Dirk|dir | 44 | |Kris|kri | 45 | |Blade|bld | 46 | |Throwing Knife|tkf | 47 | |Throwing Axe|tax | 48 | |Balanced Knife|bkf | 49 | |Balanced Axe|bal | 50 | |Javelin|jav | 51 | |Pilum|pil | 52 | |Short Spear|ssp | 53 | |Glaive|glv | 54 | |Throwing Spear|tsp | 55 | |Spear|spr | 56 | |Trident|tri | 57 | |Brandistock|brn | 58 | |Spetum|spt | 59 | |Pike|pik | 60 | |Bardiche|bar | 61 | |Voulge|vou | 62 | |Scythe|scy | 63 | |Poleaxe|pax | 64 | |Halberd|hal | 65 | |War Scythe|wsc | 66 | |Short Staff|sst | 67 | |Long Staff|lst | 68 | |Gnarled Staff|cst | 69 | |Battle Staff|bst | 70 | |War Staff|wst | 71 | |Short Bow|sbw | 72 | |Hunter's Bow|hbw | 73 | |Long Bow|lbw | 74 | |Composite Bow|cbw | 75 | |Short Battle Bow|sbb | 76 | |Long Battle Bow|lbb | 77 | |Short War Bow|swb | 78 | |Long War Bow|lwb | 79 | |Light Crossbow|lxb | 80 | |Crossbow|mxb | 81 | |Heavy Crossbow|hxb | 82 | |Repeating Crossbow|rxb | 83 | |Rancid Gas Potion|gps | 84 | |Oil Potion|ops | 85 | |Choking Gas Potion|gpm | 86 | |Exploding Potion|opm | 87 | |Strangling Gas Potion|gpl | 88 | |Fulminating Potion|opl | 89 | |Decoy Gidbinn|d33 | 90 | |The Gidbinn|g33 | 91 | |Wirt's Leg|leg | 92 | |Horadric Malus|hdm | 93 | |Hell Forge Hammer|hfh | 94 | |Horadric Staff|hst | 95 | |Shaft of the Horadric Staff|msf | 96 | |Hatchet|9ha | 97 | |Cleaver|9ax | 98 | |Twin Axe|92a | 99 | |Crowbill|9mp | 100 | |Naga|9wa | 101 | |Military Axe|9la | 102 | |Bearded Axe|9ba | 103 | |Tabar|9bt | 104 | |Gothic Axe|9ga | 105 | |Ancient Axe|9gi | 106 | |Burnt Wand|9wn | 107 | |Petrified Wand|9yw | 108 | |Tomb Wand|9bw | 109 | |Grave Wand|9gw | 110 | |Cudgel|9cl | 111 | |Rune Scepter|9sc | 112 | |Holy Water Sprinkler|9qs | 113 | |Divine Scepter|9ws | 114 | |Barbed Club|9sp | 115 | |Flanged Mace|9ma | 116 | |Jagged Star|9mt | 117 | |Knout|9fl | 118 | |Battle Hammer|9wh | 119 | |War Club|9m9 | 120 | |Martel de Fer|9gm | 121 | |Gladius|9ss | 122 | |Cutlass|9sm | 123 | |Shamshir|9sb | 124 | |Tulwar|9fc | 125 | |Dimensional Blade|9cr | 126 | |Battle Sword|9bs | 127 | |Rune Sword|9ls | 128 | |Ancient Sword|9wd | 129 | |Espandon|92h | 130 | |Dacian Falx|9cm | 131 | |Tusk Sword|9gs | 132 | |Gothic Sword|9b9 | 133 | |Zweihander|9fb | 134 | |Executioner Sword|9gd | 135 | |Poignard|9dg | 136 | |Rondel|9di | 137 | |Cinquedeas|9kr | 138 | |Stiletto|9bl | 139 | |Battle Dart|9tk | 140 | |Francisca|9ta | 141 | |War Dart|9bk | 142 | |Hurlbat|9b8 | 143 | |War Javelin|9ja | 144 | |Great Pilum|9pi | 145 | |Simbilan|9s9 | 146 | |Spiculum|9gl | 147 | |Harpoon|9ts | 148 | |War Spear|9sr | 149 | |Fuscina|9tr | 150 | |War Fork|9br | 151 | |Yari|9st | 152 | |Lance|9p9 | 153 | |Lochaber Axe|9b7 | 154 | |Bill|9vo | 155 | |Battle Scythe|9s8 | 156 | |Partizan|9pa | 157 | |Bec-de-Corbin|9h9 | 158 | |Grim Scythe|9wc | 159 | |Jo Staff|8ss | 160 | |Quarterstaff|8ls | 161 | |Cedar Staff|8cs | 162 | |Gothic Staff|8bs | 163 | |Rune Staff|8ws | 164 | |Edge Bow|8sb | 165 | |Razor Bow|8hb | 166 | |Cedar Bow|8lb | 167 | |Double Bow|8cb | 168 | |Short Siege Bow|8s8 | 169 | |Large Siege Bow|8l8 | 170 | |Rune Bow|8sw | 171 | |Gothic Bow|8lw | 172 | |Arbalest|8lx | 173 | |Siege Crossbow|8mx | 174 | |Ballista|8hx | 175 | |Chu-Ko-Nu|8rx | 176 | |Khalim's Flail|qf1 | 177 | |Khalim's Will|qf2 | 178 | |Katar|ktr | 179 | |Wrist Blade|wrb | 180 | |Hatchet Hands|axf | 181 | |Cestus|ces | 182 | |Claws|clw | 183 | |Blade Talons|btl | 184 | |Scissors Katar|skr | 185 | |Quhab|9ar | 186 | |Wrist Spike|9wb | 187 | |Fascia|9xf | 188 | |Hand Scythe|9cs | 189 | |Greater Claws|9lw | 190 | |Greater Talons|9tw | 191 | |Scissors Quhab|9qr | 192 | |Suwayyah|7ar | 193 | |Wrist Sword|7wb | 194 | |War Fist|7xf | 195 | |Battle Cestus|7cs | 196 | |Feral Claws|7lw | 197 | |Runic Talons|7tw | 198 | |Scissors Suwayyah|7qr | 199 | |Tomahawk|7ha | 200 | |Small Crescent|7ax | 201 | |Ettin Axe|72a | 202 | |War Spike|7mp | 203 | |Berserker Axe|7wa | 204 | |Feral Axe|7la | 205 | |Silver-edged Axe|7ba | 206 | |Decapitator|7bt | 207 | |Champion Axe|7ga | 208 | |Glorious Axe|7gi | 209 | |Polished Wand|7wn | 210 | |Ghost Wand|7yw | 211 | |Lich Wand|7bw | 212 | |Unearthed Wand|7gw | 213 | |Truncheon|7cl | 214 | |Mighty Scepter|7sc | 215 | |Seraph Rod|7qs | 216 | |Caduceus|7ws | 217 | |Tyrant Club|7sp | 218 | |Reinforced Mace|7ma | 219 | |Devil Star|7mt | 220 | |Scourge|7fl | 221 | |Legendary Mallet|7wh | 222 | |Ogre Maul|7m7 | 223 | |Thunder Maul|7gm | 224 | |Falcata|7ss | 225 | |Ataghan|7sm | 226 | |Elegant Blade|7sb | 227 | |Hydra Edge|7fc | 228 | |Phase Blade|7cr | 229 | |Conquest Sword|7bs | 230 | |Cryptic Sword|7ls | 231 | |Mythical Sword|7wd | 232 | |Legend Sword|72h | 233 | |Highland Blade|7cm | 234 | |Balrog Blade|7gs | 235 | |Champion Sword|7b7 | 236 | |Colossus Sword|7fb | 237 | |Colossus Blade|7gd | 238 | |Bone Knife|7dg | 239 | |Mithril Point|7di | 240 | |Fanged Knife|7kr | 241 | |Legend Spike|7bl | 242 | |Flying Knife|7tk | 243 | |Flying Axe|7ta | 244 | |Winged Knife|7bk | 245 | |Winged Axe|7b8 | 246 | |Hyperion Javelin|7ja | 247 | |Stygian Pilum|7pi | 248 | |Balrog Spear|7s7 | 249 | |Ghost Glaive|7gl | 250 | |Winged Harpoon|7ts | 251 | |Hyperion Spear|7sr | 252 | |Stygian Pike|7tr | 253 | |Mancatcher|7br | 254 | |Ghost Spear|7st | 255 | |War Pike|7p7 | 256 | |Ogre Axe|7o7 | 257 | |Colossus Voulge|7vo | 258 | |Thresher|7s8 | 259 | |Cryptic Axe|7pa | 260 | |Great Poleaxe|7h7 | 261 | |Giant Thresher|7wc | 262 | |Walking Stick|6ss | 263 | |Stalagmite|6ls | 264 | |Elder Staff|6cs | 265 | |Shillelagh|6bs | 266 | |Archon Staff|6ws | 267 | |Spider Bow|6sb | 268 | |Blade Bow|6hb | 269 | |Shadow Bow|6lb | 270 | |Great Bow|6cb | 271 | |Diamond Bow|6s7 | 272 | |Crusader Bow|6l7 | 273 | |Ward Bow|6sw | 274 | |Hydra Bow|6lw | 275 | |Pellet Bow|6lx | 276 | |Gorgon Crossbow|6mx | 277 | |Colossus Crossbow|6hx | 278 | |Demon Crossbow|6rx | 279 | |Eagle Orb|ob1 | 280 | |Sacred Globe|ob2 | 281 | |Smoked Sphere|ob3 | 282 | |Clasped Orb|ob4 | 283 | |Jared's Stone|ob5 | 284 | |Stag Bow|am1 | 285 | |Reflex Bow|am2 | 286 | |Maiden Spear|am3 | 287 | |Maiden Pike|am4 | 288 | |Maiden Javelin|am5 | 289 | |Glowing Orb|ob6 | 290 | |Crystalline Globe|ob7 | 291 | |Cloudy Sphere|ob8 | 292 | |Sparkling Ball|ob9 | 293 | |Swirling Crystal|oba | 294 | |Ashwood Bow|am6 | 295 | |Ceremonial Bow|am7 | 296 | |Ceremonial Spear|am8 | 297 | |Ceremonial Pike|am9 | 298 | |Ceremonial Javelin|ama | 299 | |Heavenly Stone|obb | 300 | |Eldritch Orb|obc | 301 | |Demon Heart|obd | 302 | |Vortex Orb|obe | 303 | |Dimensional Shard|obf | 304 | |Matriarchal Bow|amb | 305 | |Grand Matron Bow|amc | 306 | |Matriarchal Spear|amd | 307 | |Matriarchal Pike|ame | 308 | |Matriarchal Javelin|amf | 309 | |Cap|cap | 310 | |Skull Cap|skp | 311 | |Helm|hlm | 312 | |Full Helm|fhl | 313 | |Great Helm|ghm | 314 | |Crown|crn | 315 | |Mask|msk | 316 | |Quilted Armor|qui | 317 | |Leather Armor|lea | 318 | |Hard Leather Armor|hla | 319 | |Studded Leather|stu | 320 | |Ring Mail|rng | 321 | |Scale Mail|scl | 322 | |Chain Mail|chn | 323 | |Breast Plate|brs | 324 | |Splint Mail|spl | 325 | |Plate Mail|plt | 326 | |Field Plate|fld | 327 | |Gothic Plate|gth | 328 | |Full Plate Mail|ful | 329 | |Ancient Armor|aar | 330 | |Light Plate|ltp | 331 | |Buckler|buc | 332 | |Small Shield|sml | 333 | |Large Shield|lrg | 334 | |Kite Shield|kit | 335 | |Tower Shield|tow | 336 | |Gothic Shield|gts | 337 | |Leather Gloves|lgl | 338 | |Heavy Gloves|vgl | 339 | |Chain Gloves|mgl | 340 | |Light Gauntlets|tgl | 341 | |Gauntlets|hgl | 342 | |Boots|lbt | 343 | |Heavy Boots|vbt | 344 | |Chain Boots|mbt | 345 | |Light Plated Boots|tbt | 346 | |Greaves|hbt | 347 | |Sash|lbl | 348 | |Light Belt|vbl | 349 | |Belt|mbl | 350 | |Heavy Belt|tbl | 351 | |Plated Belt|hbl | 352 | |Bone Helm|bhm | 353 | |Bone Shield|bsh | 354 | |Spiked Shield|spk | 355 | |War Hat|xap | 356 | |Sallet|xkp | 357 | |Casque|xlm | 358 | |Basinet|xhl | 359 | |Winged Helm|xhm | 360 | |Grand Crown|xrn | 361 | |Death Mask|xsk | 362 | |Ghost Armor|xui | 363 | |Serpentskin Armor|xea | 364 | |Demonhide Armor|xla | 365 | |Trellised Armor|xtu | 366 | |Linked Mail|xng | 367 | |Tigulated Mail|xcl | 368 | |Mesh Armor|xhn | 369 | |Cuirass|xrs | 370 | |Russet Armor|xpl | 371 | |Templar Coat|xlt | 372 | |Sharktooth Armor|xld | 373 | |Embossed Plate|xth | 374 | |Chaos Armor|xul | 375 | |Ornate Plate|xar | 376 | |Mage Plate|xtp | 377 | |Defender|xuc | 378 | |Round Shield|xml | 379 | |Scutum|xrg | 380 | |Dragon Shield|xit | 381 | |Pavise|xow | 382 | |Ancient Shield|xts | 383 | |Demonhide Gloves|xlg | 384 | |Sharkskin Gloves|xvg | 385 | |Heavy Bracers|xmg | 386 | |Battle Gauntlets|xtg | 387 | |War Gauntlets|xhg | 388 | |Demonhide Boots|xlb | 389 | |Sharkskin Boots|xvb | 390 | |Mesh Boots|xmb | 391 | |Battle Boots|xtb | 392 | |War Boots|xhb | 393 | |Demonhide Sash|zlb | 394 | |Sharkskin Belt|zvb | 395 | |Mesh Belt|zmb | 396 | |Battle Belt|ztb | 397 | |War Belt|zhb | 398 | |Grim Helm|xh9 | 399 | |Grim Shield|xsh | 400 | |Barbed Shield|xpk | 401 | |Wolf Head|dr1 | 402 | |Hawk Helm|dr2 | 403 | |Antlers|dr3 | 404 | |Falcon Mask|dr4 | 405 | |Spirit Mask|dr5 | 406 | |Jawbone Cap|ba1 | 407 | |Fanged Helm|ba2 | 408 | |Horned Helm|ba3 | 409 | |Assault Helmet|ba4 | 410 | |Avenger Guard|ba5 | 411 | |Targe|pa1 | 412 | |Rondache|pa2 | 413 | |Heraldic Shield|pa3 | 414 | |Aerin Shield|pa4 | 415 | |Crown Shield|pa5 | 416 | |Preserved Head|ne1 | 417 | |Zombie Head|ne2 | 418 | |Unraveller Head|ne3 | 419 | |Gargoyle Head|ne4 | 420 | |Demon Head|ne5 | 421 | |Circlet|ci0 | 422 | |Coronet|ci1 | 423 | |Tiara|ci2 | 424 | |Diadem|ci3 | 425 | |Shako|uap | 426 | |Hydraskull|ukp | 427 | |Armet|ulm | 428 | |Giant Conch|uhl | 429 | |Spired Helm|uhm | 430 | |Corona|urn | 431 | |Demonhead|usk | 432 | |Dusk Shroud|uui | 433 | |Wyrmhide|uea | 434 | |Scarab Husk|ula | 435 | |Wire Fleece|utu | 436 | |Diamond Mail|ung | 437 | |Loricated Mail|ucl | 438 | |Boneweave|uhn | 439 | |Great Hauberk|urs | 440 | |Balrog Skin|upl | 441 | |Hellforge Plate|ult | 442 | |Kraken Shell|uld | 443 | |Lacquered Plate|uth | 444 | |Shadow Plate|uul | 445 | |Sacred Armor|uar | 446 | |Archon Plate|utp | 447 | |Heater|uuc | 448 | |Luna|uml | 449 | |Hyperion|urg | 450 | |Monarch|uit | 451 | |Aegis|uow | 452 | |Ward|uts | 453 | |Bramble Mitts|ulg | 454 | |Vampirebone Gloves|uvg | 455 | |Vambraces|umg | 456 | |Crusader Gauntlets|utg | 457 | |Ogre Gauntlets|uhg | 458 | |Wyrmhide Boots|ulb | 459 | |Scarabshell Boots|uvb | 460 | |Boneweave Boots|umb | 461 | |Mirrored Boots|utb | 462 | |Myrmidon Greaves|uhb | 463 | |Spiderweb Sash|ulc | 464 | |Vampirefang Belt|uvc | 465 | |Mithril Coil|umc | 466 | |Troll Belt|utc | 467 | |Colossus Girdle|uhc | 468 | |Bone Visage|uh9 | 469 | |Troll Nest|ush | 470 | |Blade Barrier|upk | 471 | |Alpha Helm|dr6 | 472 | |Griffon Headdress|dr7 | 473 | |Hunter's Guise|dr8 | 474 | |Sacred Feathers|dr9 | 475 | |Totemic Mask|dra | 476 | |Jawbone Visor|ba6 | 477 | |Lion Helm|ba7 | 478 | |Rage Mask|ba8 | 479 | |Savage Helmet|ba9 | 480 | |Slayer Guard|baa | 481 | |Akaran Targe|pa6 | 482 | |Akaran Rondache|pa7 | 483 | |Protector Shield|pa8 | 484 | |Gilded Shield|pa9 | 485 | |Royal Shield|paa | 486 | |Mummified Trophy|ne6 | 487 | |Fetish Trophy|ne7 | 488 | |Sexton Trophy|ne8 | 489 | |Cantor Trophy|ne9 | 490 | |Hierophant Trophy|nea | 491 | |Blood Spirit|drb | 492 | |Sun Spirit|drc | 493 | |Earth Spirit|drd | 494 | |Sky Spirit|dre | 495 | |Dream Spirit|drf | 496 | |Carnage Helm|bab | 497 | |Fury Visor|bac | 498 | |Destroyer Helm|bad | 499 | |Conqueror Crown|bae | 500 | |Guardian Crown|baf | 501 | |Sacred Targe|pab | 502 | |Sacred Rondache|pac | 503 | |Kurast Shield|pad | 504 | |Zakarum Shield|pae | 505 | |Vortex Shield|paf | 506 | |Minion Skull|neb | 507 | |Hellspawn Skull|neg | 508 | |Overseer Skull|ned | 509 | |Succubus Skull|nee | 510 | |Bloodlord Skull|nef | 511 | |Elixir|elx | 512 | |an evil force|hpo | 513 | |an evil force|mpo | 514 | |an evil force|hpf | 515 | |an evil force|mpf | 516 | |Stamina Potion|vps | 517 | |Antidote Potion|yps | 518 | |Rejuvenation Potion|rvs | 519 | |Full Rejuvenation Potion|rvl | 520 | |Thawing Potion|wms | 521 | |Tome of Town Portal|tbk | 522 | |Tome of Identify|ibk | 523 | |Amulet|amu | 524 | |Top of the Horadric Staff|vip | 525 | |Ring|rin | 526 | |Gold|gld | 527 | |Scroll of Inifuss|bks | 528 | |Key to the Cairn Stones|bkd | 529 | |Arrows|aqv | 530 | |Torch|tch | 531 | |Bolts|cqv | 532 | |Scroll of Town Portal|tsc | 533 | |Scroll of Identify|isc | 534 | |Heart|hrt | 535 | |Brain|brz | 536 | |Jawbone|jaw | 537 | |Eye|eyz | 538 | |Horn|hrn | 539 | |Tail|tal | 540 | |Flag|flg | 541 | |Fang|fng | 542 | |Quill|qll | 543 | |Soul|sol | 544 | |Scalp|scz | 545 | |Spleen|spe | 546 | |Key|key | 547 | |The Black Tower Key|luv | 548 | |Right Click to permanently add 20 to Life 549 | Potion of Life|xyz | 550 | |A Jade Figurine|j34 | 551 | |The Golden Bird|g34 | 552 | |Lam Esen's Tome|bbb | 553 | |Horadric Cube|box | 554 | |Horadric Scroll|tr1 | 555 | |Mephisto's Soulstone|mss | 556 | |Right Click to learn skill of your choice 557 | Book of Skill|ass | 558 | |Khalim's Eye|qey | 559 | |Khalim's Heart|qhr | 560 | |Khalim's Brain|qbr | 561 | |Ear|ear | 562 | |Chipped Amethyst|gcv | 563 | |Flawed Amethyst|gfv | 564 | |Amethyst|gsv | 565 | |Flawless Amethyst|gzv | 566 | |Perfect Amethyst|gpv | 567 | |Chipped Topaz|gcy | 568 | |Flawed Topaz|gfy | 569 | |Topaz|gsy | 570 | |Flawless Topaz|gly | 571 | |Perfect Topaz|gpy | 572 | |Chipped Sapphire|gcb | 573 | |Flawed Sapphire|gfb | 574 | |Sapphire|gsb | 575 | |Flawless Sapphire|glb | 576 | |Perfect Sapphire|gpb | 577 | |Chipped Emerald|gcg | 578 | |Flawed Emerald|gfg | 579 | |Emerald|gsg | 580 | |Flawless Emerald|glg | 581 | |Perfect Emerald|gpg | 582 | |Chipped Ruby|gcr | 583 | |Flawed Ruby|gfr | 584 | |Ruby|gsr | 585 | |Flawless Ruby|glr | 586 | |Perfect Ruby|gpr | 587 | |Chipped Diamond|gcw | 588 | |Flawed Diamond|gfw | 589 | |Diamond|gsw | 590 | |Flawless Diamond|glw | 591 | |Perfect Diamond|gpw | 592 | |Minor Healing Potion|hp1 | 593 | |Light Healing Potion|hp2 | 594 | |Healing Potion|hp3 | 595 | |Greater Healing Potion|hp4 | 596 | |Super Healing Potion|hp5 | 597 | |Minor Mana Potion|mp1 | 598 | |Light Mana Potion|mp2 | 599 | |Mana Potion|mp3 | 600 | |Greater Mana Potion|mp4 | 601 | |Super Mana Potion|mp5 | 602 | |Chipped Skull|skc | 603 | |Flawed Skull|skf | 604 | |Skull|sku | 605 | |Flawless Skull|skl | 606 | |Perfect Skull|skz | 607 | |Herb|hrb | 608 | |Small Charm|cm1 | 609 | |Large Charm|cm2 | 610 | |Grand Charm|cm3 | 611 | |an evil force|rps | 612 | |an evil force|rpl | 613 | |an evil force|bps | 614 | |an evil force|bpl | 615 | |El Rune|r01 | 616 | |Eld Rune|r02 | 617 | |Tir Rune|r03 | 618 | |Nef Rune|r04 | 619 | |Eth Rune|r05 | 620 | |Ith Rune|r06 | 621 | |Tal Rune|r07 | 622 | |Ral Rune|r08 | 623 | |Ort Rune|r09 | 624 | |Thul Rune|r10 | 625 | |Amn Rune|r11 | 626 | |Sol Rune|r12 | 627 | |Shael Rune|r13 | 628 | |Dol Rune|r14 | 629 | |Hel Rune|r15 | 630 | |Io Rune|r16 | 631 | |Lum Rune|r17 | 632 | |Ko Rune|r18 | 633 | |Fal Rune|r19 | 634 | |Lem Rune|r20 | 635 | |Pul Rune|r21 | 636 | |Um Rune|r22 | 637 | |Mal Rune|r23 | 638 | |Ist Rune|r24 | 639 | |Gul Rune|r25 | 640 | |Vex Rune|r26 | 641 | |Ohm Rune|r27 | 642 | |Lo Rune|r28 | 643 | |Sur Rune|r29 | 644 | |Ber Rune|r30 | 645 | |Jah Rune|r31 | 646 | |Cham Rune|r32 | 647 | |Zod Rune|r33 | 648 | |Jewel|jew | 649 | |Keep it to thaw Anya 650 | Malah's Potion|ice | 651 | |Scroll of Knowledge|0sc | 652 | |Right Click to Cast 653 | Scroll of Resistance|tr2 | 654 | |Key of Terror|pk1 | 655 | |Key of Hate|pk2 | 656 | |Key of Destruction|pk3 | 657 | |Diablo's Horn|dhn | 658 | |Baal's Eye|bey | 659 | |Mephisto's Brain|mbr | 660 | |Right-click to reset Stat/Skill Points 661 | Token of Absolution|toa | 662 | |Twisted Essence of Suffering|tes | 663 | |Charged Essence of Hatred|ceh | 664 | |Burning Essence of Terror|bet | 665 | |Festering Essence of Destruction|fed | 666 | |Standard of Heroes|std | -------------------------------------------------------------------------------- /mINI.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define MINI_CASE_SENSITIVE 3 | 4 | /* 5 | * The MIT License (MIT) 6 | * Copyright (c) 2018 Danijel Durakovic 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | * this software and associated documentation files (the "Software"), to deal in 10 | * the Software without restriction, including without limitation the rights to 11 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 | * of the Software, and to permit persons to whom the Software is furnished to do 13 | * so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 20 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 22 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * 25 | */ 26 | 27 | /////////////////////////////////////////////////////////////////////////////// 28 | // 29 | // /mINI/ v0.9.10 30 | // An INI file reader and writer for the modern age. 31 | // 32 | /////////////////////////////////////////////////////////////////////////////// 33 | // 34 | // A tiny utility library for manipulating INI files with a straightforward 35 | // API and a minimal footprint. It conforms to the (somewhat) standard INI 36 | // format - sections and keys are case insensitive and all leading and 37 | // trailing whitespace is ignored. Comments are lines that begin with a 38 | // semicolon. Trailing comments are allowed on section lines. 39 | // 40 | // Files are read on demand, upon which data is kept in memory and the file 41 | // is closed. This utility supports lazy writing, which only writes changes 42 | // and updates to a file and preserves custom formatting and comments. A lazy 43 | // write invoked by a write() call will read the output file, find what 44 | // changes have been made and update the file accordingly. If you only need to 45 | // generate files, use generate() instead. Section and key order is preserved 46 | // on read, write and insert. 47 | // 48 | /////////////////////////////////////////////////////////////////////////////// 49 | // 50 | // /* BASIC USAGE EXAMPLE: */ 51 | // 52 | // /* read from file */ 53 | // mINI::INIFile file("myfile.ini"); 54 | // mINI::INIStructure ini; 55 | // file.read(ini); 56 | // 57 | // /* read value; gets a reference to actual value in the structure. 58 | // if key or section don't exist, a new empty value will be created */ 59 | // std::string& value = ini["section"]["key"]; 60 | // 61 | // /* read value safely; gets a copy of value in the structure. 62 | // does not alter the structure */ 63 | // std::string value = ini.get("section").get("key"); 64 | // 65 | // /* set or update values */ 66 | // ini["section"]["key"] = "value"; 67 | // 68 | // /* set multiple values */ 69 | // ini["section2"].set({ 70 | // {"key1", "value1"}, 71 | // {"key2", "value2"} 72 | // }); 73 | // 74 | // /* write updates back to file, preserving comments and formatting */ 75 | // file.write(ini); 76 | // 77 | // /* or generate a file (overwrites the original) */ 78 | // file.generate(ini); 79 | // 80 | /////////////////////////////////////////////////////////////////////////////// 81 | // 82 | // Long live the INI file!!! 83 | // 84 | /////////////////////////////////////////////////////////////////////////////// 85 | 86 | #ifndef MINI_INI_H_ 87 | #define MINI_INI_H_ 88 | 89 | #include 90 | #include 91 | #include 92 | #include 93 | #include 94 | #include 95 | #include 96 | #include 97 | #include 98 | #include 99 | 100 | namespace mINI 101 | { 102 | namespace INIStringUtil 103 | { 104 | const char* const whitespaceDelimiters = " \t\n\r\f\v"; 105 | inline void trim(std::string& str) 106 | { 107 | str.erase(str.find_last_not_of(whitespaceDelimiters) + 1); 108 | str.erase(0, str.find_first_not_of(whitespaceDelimiters)); 109 | } 110 | #ifndef MINI_CASE_SENSITIVE 111 | inline void toLower(std::string& str) 112 | { 113 | std::transform(str.begin(), str.end(), str.begin(), [](const char c) { 114 | return static_cast(std::tolower(c)); 115 | }); 116 | } 117 | #endif 118 | inline void replace(std::string& str, std::string const& a, std::string const& b) 119 | { 120 | if (!a.empty()) 121 | { 122 | std::size_t pos = 0; 123 | while ((pos = str.find(a, pos)) != std::string::npos) 124 | { 125 | str.replace(pos, a.size(), b); 126 | pos += b.size(); 127 | } 128 | } 129 | } 130 | #ifdef _WIN32 131 | const char* const endl = "\r\n"; 132 | #else 133 | const char* const endl = "\n"; 134 | #endif 135 | }; 136 | 137 | template 138 | class INIMap 139 | { 140 | private: 141 | using T_DataIndexMap = std::unordered_map; 142 | using T_DataItem = std::pair; 143 | using T_DataContainer = std::vector; 144 | using T_MultiArgs = typename std::vector>; 145 | 146 | T_DataIndexMap dataIndexMap; 147 | T_DataContainer data; 148 | 149 | inline std::size_t setEmpty(std::string& key) 150 | { 151 | std::size_t index = data.size(); 152 | dataIndexMap[key] = index; 153 | data.emplace_back(key, T()); 154 | return index; 155 | } 156 | 157 | public: 158 | using const_iterator = typename T_DataContainer::const_iterator; 159 | 160 | INIMap() { } 161 | 162 | INIMap(INIMap const& other) 163 | { 164 | std::size_t data_size = other.data.size(); 165 | for (std::size_t i = 0; i < data_size; ++i) 166 | { 167 | auto const& key = other.data[i].first; 168 | auto const& obj = other.data[i].second; 169 | data.emplace_back(key, obj); 170 | } 171 | dataIndexMap = T_DataIndexMap(other.dataIndexMap); 172 | } 173 | 174 | T& operator[](std::string key) 175 | { 176 | INIStringUtil::trim(key); 177 | #ifndef MINI_CASE_SENSITIVE 178 | INIStringUtil::toLower(key); 179 | #endif 180 | auto it = dataIndexMap.find(key); 181 | bool hasIt = (it != dataIndexMap.end()); 182 | std::size_t index = (hasIt) ? it->second : setEmpty(key); 183 | return data[index].second; 184 | } 185 | T get(std::string key) const 186 | { 187 | INIStringUtil::trim(key); 188 | #ifndef MINI_CASE_SENSITIVE 189 | INIStringUtil::toLower(key); 190 | #endif 191 | auto it = dataIndexMap.find(key); 192 | if (it == dataIndexMap.end()) 193 | { 194 | return T(); 195 | } 196 | return T(data[it->second].second); 197 | } 198 | bool has(std::string key) const 199 | { 200 | INIStringUtil::trim(key); 201 | #ifndef MINI_CASE_SENSITIVE 202 | INIStringUtil::toLower(key); 203 | #endif 204 | return (dataIndexMap.count(key) == 1); 205 | } 206 | void set(std::string key, T obj) 207 | { 208 | INIStringUtil::trim(key); 209 | #ifndef MINI_CASE_SENSITIVE 210 | INIStringUtil::toLower(key); 211 | #endif 212 | auto it = dataIndexMap.find(key); 213 | if (it != dataIndexMap.end()) 214 | { 215 | data[it->second].second = obj; 216 | } 217 | else 218 | { 219 | dataIndexMap[key] = data.size(); 220 | data.emplace_back(key, obj); 221 | } 222 | } 223 | void set(T_MultiArgs const& multiArgs) 224 | { 225 | for (auto const& it : multiArgs) 226 | { 227 | auto const& key = it.first; 228 | auto const& obj = it.second; 229 | set(key, obj); 230 | } 231 | } 232 | bool remove(std::string key) 233 | { 234 | INIStringUtil::trim(key); 235 | #ifndef MINI_CASE_SENSITIVE 236 | INIStringUtil::toLower(key); 237 | #endif 238 | auto it = dataIndexMap.find(key); 239 | if (it != dataIndexMap.end()) 240 | { 241 | std::size_t index = it->second; 242 | data.erase(data.begin() + index); 243 | dataIndexMap.erase(it); 244 | for (auto& it2 : dataIndexMap) 245 | { 246 | auto& vi = it2.second; 247 | if (vi > index) 248 | { 249 | vi--; 250 | } 251 | } 252 | return true; 253 | } 254 | return false; 255 | } 256 | void clear() 257 | { 258 | data.clear(); 259 | dataIndexMap.clear(); 260 | } 261 | std::size_t size() const 262 | { 263 | return data.size(); 264 | } 265 | const_iterator begin() const { return data.begin(); } 266 | const_iterator end() const { return data.end(); } 267 | }; 268 | 269 | using INIStructure = INIMap>; 270 | 271 | namespace INIParser 272 | { 273 | using T_ParseValues = std::pair; 274 | 275 | enum class PDataType : char 276 | { 277 | PDATA_NONE, 278 | PDATA_COMMENT, 279 | PDATA_SECTION, 280 | PDATA_KEYVALUE, 281 | PDATA_UNKNOWN 282 | }; 283 | 284 | inline PDataType parseLine(std::string line, T_ParseValues& parseData) 285 | { 286 | parseData.first.clear(); 287 | parseData.second.clear(); 288 | INIStringUtil::trim(line); 289 | if (line.empty()) 290 | { 291 | return PDataType::PDATA_NONE; 292 | } 293 | char firstCharacter = line[0]; 294 | if (firstCharacter == ';') 295 | { 296 | return PDataType::PDATA_COMMENT; 297 | } 298 | if (firstCharacter == '[') 299 | { 300 | auto commentAt = line.find_first_of(';'); 301 | if (commentAt != std::string::npos) 302 | { 303 | line = line.substr(0, commentAt); 304 | } 305 | auto closingBracketAt = line.find_last_of(']'); 306 | if (closingBracketAt != std::string::npos) 307 | { 308 | auto section = line.substr(1, closingBracketAt - 1); 309 | INIStringUtil::trim(section); 310 | parseData.first = section; 311 | return PDataType::PDATA_SECTION; 312 | } 313 | } 314 | auto lineNorm = line; 315 | INIStringUtil::replace(lineNorm, "\\=", " "); 316 | auto equalsAt = lineNorm.find_first_of('='); 317 | if (equalsAt != std::string::npos) 318 | { 319 | auto key = line.substr(0, equalsAt); 320 | INIStringUtil::trim(key); 321 | INIStringUtil::replace(key, "\\=", "="); 322 | auto value = line.substr(equalsAt + 1); 323 | INIStringUtil::trim(value); 324 | parseData.first = key; 325 | parseData.second = value; 326 | return PDataType::PDATA_KEYVALUE; 327 | } 328 | return PDataType::PDATA_UNKNOWN; 329 | } 330 | }; 331 | 332 | class INIReader 333 | { 334 | public: 335 | using T_LineData = std::vector; 336 | using T_LineDataPtr = std::shared_ptr; 337 | 338 | private: 339 | std::ifstream fileReadStream; 340 | T_LineDataPtr lineData; 341 | 342 | T_LineData readFile() 343 | { 344 | std::string fileContents; 345 | fileReadStream.seekg(0, std::ios::end); 346 | fileContents.resize(fileReadStream.tellg()); 347 | fileReadStream.seekg(0, std::ios::beg); 348 | std::size_t fileSize = fileContents.size(); 349 | fileReadStream.read(&fileContents[0], fileSize); 350 | fileReadStream.close(); 351 | T_LineData output; 352 | if (fileSize == 0) 353 | { 354 | return output; 355 | } 356 | std::string buffer; 357 | buffer.reserve(50); 358 | for (std::size_t i = 0; i < fileSize; ++i) 359 | { 360 | char& c = fileContents[i]; 361 | if (c == '\n') 362 | { 363 | output.emplace_back(buffer); 364 | buffer.clear(); 365 | continue; 366 | } 367 | if (c != '\0' && c != '\r') 368 | { 369 | buffer += c; 370 | } 371 | } 372 | output.emplace_back(buffer); 373 | return output; 374 | } 375 | 376 | public: 377 | INIReader(std::string const& filename, bool keepLineData = false) 378 | { 379 | fileReadStream.open(filename, std::ios::in | std::ios::binary); 380 | if (keepLineData) 381 | { 382 | lineData = std::make_shared(); 383 | } 384 | } 385 | ~INIReader() { } 386 | 387 | bool operator>>(INIStructure& data) 388 | { 389 | if (!fileReadStream.is_open()) 390 | { 391 | return false; 392 | } 393 | T_LineData fileLines = readFile(); 394 | std::string section; 395 | bool inSection = false; 396 | INIParser::T_ParseValues parseData; 397 | for (auto const& line : fileLines) 398 | { 399 | auto parseResult = INIParser::parseLine(line, parseData); 400 | if (parseResult == INIParser::PDataType::PDATA_SECTION) 401 | { 402 | inSection = true; 403 | data[section = parseData.first]; 404 | } 405 | else if (inSection && parseResult == INIParser::PDataType::PDATA_KEYVALUE) 406 | { 407 | auto const& key = parseData.first; 408 | auto const& value = parseData.second; 409 | data[section][key] = value; 410 | } 411 | if (lineData && parseResult != INIParser::PDataType::PDATA_UNKNOWN) 412 | { 413 | if (parseResult == INIParser::PDataType::PDATA_KEYVALUE && !inSection) 414 | { 415 | continue; 416 | } 417 | lineData->emplace_back(line); 418 | } 419 | } 420 | return true; 421 | } 422 | T_LineDataPtr getLines() 423 | { 424 | return lineData; 425 | } 426 | }; 427 | 428 | class INIGenerator 429 | { 430 | private: 431 | std::ofstream fileWriteStream; 432 | 433 | public: 434 | bool prettyPrint = false; 435 | 436 | INIGenerator(std::string const& filename) 437 | { 438 | fileWriteStream.open(filename, std::ios::out | std::ios::binary); 439 | } 440 | ~INIGenerator() { } 441 | 442 | bool operator<<(INIStructure const& data) 443 | { 444 | if (!fileWriteStream.is_open()) 445 | { 446 | return false; 447 | } 448 | if (!data.size()) 449 | { 450 | return true; 451 | } 452 | auto it = data.begin(); 453 | for (;;) 454 | { 455 | auto const& section = it->first; 456 | auto const& collection = it->second; 457 | fileWriteStream 458 | << "[" 459 | << section 460 | << "]"; 461 | if (collection.size()) 462 | { 463 | fileWriteStream << INIStringUtil::endl; 464 | auto it2 = collection.begin(); 465 | for (;;) 466 | { 467 | auto key = it2->first; 468 | INIStringUtil::replace(key, "=", "\\="); 469 | auto value = it2->second; 470 | INIStringUtil::trim(value); 471 | fileWriteStream 472 | << key 473 | << ((prettyPrint) ? " = " : "=") 474 | << value; 475 | if (++it2 == collection.end()) 476 | { 477 | break; 478 | } 479 | fileWriteStream << INIStringUtil::endl; 480 | } 481 | } 482 | if (++it == data.end()) 483 | { 484 | break; 485 | } 486 | fileWriteStream << INIStringUtil::endl; 487 | if (prettyPrint) 488 | { 489 | fileWriteStream << INIStringUtil::endl; 490 | } 491 | } 492 | return true; 493 | } 494 | }; 495 | 496 | class INIWriter 497 | { 498 | private: 499 | using T_LineData = std::vector; 500 | using T_LineDataPtr = std::shared_ptr; 501 | 502 | std::string filename; 503 | 504 | T_LineData getLazyOutput(T_LineDataPtr const& lineData, INIStructure& data, INIStructure& original) 505 | { 506 | T_LineData output; 507 | INIParser::T_ParseValues parseData; 508 | std::string sectionCurrent; 509 | bool parsingSection = false; 510 | bool continueToNextSection = false; 511 | bool discardNextEmpty = false; 512 | bool writeNewKeys = false; 513 | std::size_t lastKeyLine = 0; 514 | for (auto line = lineData->begin(); line != lineData->end(); ++line) 515 | { 516 | if (!writeNewKeys) 517 | { 518 | auto parseResult = INIParser::parseLine(*line, parseData); 519 | if (parseResult == INIParser::PDataType::PDATA_SECTION) 520 | { 521 | if (parsingSection) 522 | { 523 | writeNewKeys = true; 524 | parsingSection = false; 525 | --line; 526 | continue; 527 | } 528 | sectionCurrent = parseData.first; 529 | if (data.has(sectionCurrent)) 530 | { 531 | parsingSection = true; 532 | continueToNextSection = false; 533 | discardNextEmpty = false; 534 | output.emplace_back(*line); 535 | lastKeyLine = output.size(); 536 | } 537 | else 538 | { 539 | continueToNextSection = true; 540 | discardNextEmpty = true; 541 | continue; 542 | } 543 | } 544 | else if (parseResult == INIParser::PDataType::PDATA_KEYVALUE) 545 | { 546 | if (continueToNextSection) 547 | { 548 | continue; 549 | } 550 | if (data.has(sectionCurrent)) 551 | { 552 | auto& collection = data[sectionCurrent]; 553 | auto const& key = parseData.first; 554 | auto const& value = parseData.second; 555 | if (collection.has(key)) 556 | { 557 | auto outputValue = collection[key]; 558 | if (value == outputValue) 559 | { 560 | output.emplace_back(*line); 561 | } 562 | else 563 | { 564 | INIStringUtil::trim(outputValue); 565 | auto lineNorm = *line; 566 | INIStringUtil::replace(lineNorm, "\\=", " "); 567 | auto equalsAt = lineNorm.find_first_of('='); 568 | auto valueAt = lineNorm.find_first_not_of( 569 | INIStringUtil::whitespaceDelimiters, 570 | equalsAt + 1 571 | ); 572 | std::string outputLine = line->substr(0, valueAt); 573 | if (prettyPrint && equalsAt + 1 == valueAt) 574 | { 575 | outputLine += " "; 576 | } 577 | outputLine += outputValue; 578 | output.emplace_back(outputLine); 579 | } 580 | lastKeyLine = output.size(); 581 | } 582 | } 583 | } 584 | else 585 | { 586 | if (discardNextEmpty && line->empty()) 587 | { 588 | discardNextEmpty = false; 589 | } 590 | else if (parseResult != INIParser::PDataType::PDATA_UNKNOWN) 591 | { 592 | output.emplace_back(*line); 593 | } 594 | } 595 | } 596 | if (writeNewKeys || std::next(line) == lineData->end()) 597 | { 598 | T_LineData linesToAdd; 599 | if (data.has(sectionCurrent) && original.has(sectionCurrent)) 600 | { 601 | auto const& collection = data[sectionCurrent]; 602 | auto const& collectionOriginal = original[sectionCurrent]; 603 | for (auto const& it : collection) 604 | { 605 | auto key = it.first; 606 | if (collectionOriginal.has(key)) 607 | { 608 | continue; 609 | } 610 | auto value = it.second; 611 | INIStringUtil::replace(key, "=", "\\="); 612 | INIStringUtil::trim(value); 613 | linesToAdd.emplace_back( 614 | key + ((prettyPrint) ? " = " : "=") + value 615 | ); 616 | } 617 | } 618 | if (!linesToAdd.empty()) 619 | { 620 | output.insert( 621 | output.begin() + lastKeyLine, 622 | linesToAdd.begin(), 623 | linesToAdd.end() 624 | ); 625 | } 626 | if (writeNewKeys) 627 | { 628 | writeNewKeys = false; 629 | --line; 630 | } 631 | } 632 | } 633 | for (auto const& it : data) 634 | { 635 | auto const& section = it.first; 636 | if (original.has(section)) 637 | { 638 | continue; 639 | } 640 | if (prettyPrint && output.size() > 0 && !output.back().empty()) 641 | { 642 | output.emplace_back(); 643 | } 644 | output.emplace_back("[" + section + "]"); 645 | auto const& collection = it.second; 646 | for (auto const& it2 : collection) 647 | { 648 | auto key = it2.first; 649 | auto value = it2.second; 650 | INIStringUtil::replace(key, "=", "\\="); 651 | INIStringUtil::trim(value); 652 | output.emplace_back( 653 | key + ((prettyPrint) ? " = " : "=") + value 654 | ); 655 | } 656 | } 657 | return output; 658 | } 659 | 660 | public: 661 | bool prettyPrint = false; 662 | 663 | INIWriter(std::string const& filename) 664 | : filename(filename) 665 | { 666 | } 667 | ~INIWriter() { } 668 | 669 | bool operator<<(INIStructure& data) 670 | { 671 | struct stat buf; 672 | bool fileExists = (stat(filename.c_str(), &buf) == 0); 673 | if (!fileExists) 674 | { 675 | INIGenerator generator(filename); 676 | generator.prettyPrint = prettyPrint; 677 | return generator << data; 678 | } 679 | INIStructure originalData; 680 | T_LineDataPtr lineData; 681 | bool readSuccess = false; 682 | { 683 | INIReader reader(filename, true); 684 | if ((readSuccess = reader >> originalData)) 685 | { 686 | lineData = reader.getLines(); 687 | } 688 | } 689 | if (!readSuccess) 690 | { 691 | return false; 692 | } 693 | T_LineData output = getLazyOutput(lineData, data, originalData); 694 | std::ofstream fileWriteStream(filename, std::ios::out | std::ios::binary); 695 | if (fileWriteStream.is_open()) 696 | { 697 | if (output.size()) 698 | { 699 | auto line = output.begin(); 700 | for (;;) 701 | { 702 | fileWriteStream << *line; 703 | if (++line == output.end()) 704 | { 705 | break; 706 | } 707 | fileWriteStream << INIStringUtil::endl; 708 | } 709 | } 710 | return true; 711 | } 712 | return false; 713 | } 714 | }; 715 | 716 | class INIFile 717 | { 718 | private: 719 | std::string filename; 720 | 721 | public: 722 | INIFile(std::string const& filename) 723 | : filename(filename) 724 | { } 725 | 726 | ~INIFile() { } 727 | 728 | bool read(INIStructure& data) const 729 | { 730 | if (data.size()) 731 | { 732 | data.clear(); 733 | } 734 | if (filename.empty()) 735 | { 736 | return false; 737 | } 738 | INIReader reader(filename); 739 | return reader >> data; 740 | } 741 | bool generate(INIStructure const& data, bool pretty = false) const 742 | { 743 | if (filename.empty()) 744 | { 745 | return false; 746 | } 747 | INIGenerator generator(filename); 748 | generator.prettyPrint = pretty; 749 | return generator << data; 750 | } 751 | bool write(INIStructure& data, bool pretty = false) const 752 | { 753 | if (filename.empty()) 754 | { 755 | return false; 756 | } 757 | INIWriter writer(filename); 758 | writer.prettyPrint = pretty; 759 | return writer << data; 760 | } 761 | }; 762 | } 763 | 764 | #endif // MINI_INI_H_ 765 | --------------------------------------------------------------------------------