|
| 1 | +#include "../DevTools.hpp" |
| 2 | +#include "../ImGui.hpp" |
| 3 | +#include <misc/cpp/imgui_stdlib.h> |
| 4 | +#include <Geode/modify/AppDelegate.hpp> |
| 5 | +#include <Geode/loader/Index.hpp> |
| 6 | + |
| 7 | +using namespace geode::prelude; |
| 8 | + |
| 9 | +void DevTools::drawAdvancedSettings() { |
| 10 | + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 1.f, 1.f }); |
| 11 | + ImGui::Checkbox("Show Mod Graph", &m_showModGraph); |
| 12 | + ImGui::Checkbox("Show Mod Index", &m_showModIndex); |
| 13 | + ImGui::PopStyleVar(); |
| 14 | +} |
| 15 | + |
| 16 | +void DevTools::drawModGraph() { |
| 17 | + // TODO: function to get loader mod |
| 18 | + this->drawModGraphNode(Mod::get()->getMetadata().getDependencies()[0].mod); |
| 19 | +} |
| 20 | + |
| 21 | +namespace { |
| 22 | + std::string inputText(const char* label, std::string x) { |
| 23 | + ImGui::InputText(label, &x); |
| 24 | + return x; |
| 25 | + } |
| 26 | + std::optional<std::string> inputText(const char* label, std::optional<std::string> x) { |
| 27 | + std::string str = x ? *x : ""; |
| 28 | + return ImGui::InputText(label, &str) ? str : x; |
| 29 | + } |
| 30 | + std::optional<std::string> inputTextMultiline(const char* label, std::optional<std::string> x) { |
| 31 | + std::string str = x ? *x : ""; |
| 32 | + return ImGui::InputTextMultiline(label, &str) ? str : x; |
| 33 | + } |
| 34 | + bool inputBool(const char* label, bool x) { |
| 35 | + ImGui::Checkbox(label, &x); |
| 36 | + return x; |
| 37 | + } |
| 38 | + |
| 39 | + VersionInfo inputVersion(VersionInfo version) { |
| 40 | + int major = (int)version.getMajor(); |
| 41 | + int minor = (int)version.getMinor(); |
| 42 | + int patch = (int)version.getPatch(); |
| 43 | + std::optional<VersionTag> tag = version.getTag(); |
| 44 | + ImGui::InputInt("version.major", &major); |
| 45 | + ImGui::InputInt("version.minor", &minor); |
| 46 | + ImGui::InputInt("version.patch", &patch); |
| 47 | + int tagSel = tag ? tag->value + 1 : 0; |
| 48 | + ImGui::Combo("version.tag.value", &tagSel, "std::monostate\0Alpha\0Beta\0Prerelease\0\0"); |
| 49 | + if (tagSel == 0) |
| 50 | + tag.reset(); |
| 51 | + else { |
| 52 | + int tagNum = tag && tag->number ? (int)*tag->number : -1; |
| 53 | + ImGui::InputInt("version.tag.number", &tagNum); |
| 54 | + auto t = (VersionTag::Type)(tagSel - 1); |
| 55 | + if (tagNum < 0) |
| 56 | + tag = VersionTag(t); |
| 57 | + else |
| 58 | + tag = VersionTag(t, tagNum); |
| 59 | + } |
| 60 | + return {(size_t)major, (size_t)minor, (size_t)patch, tag}; |
| 61 | + } |
| 62 | + |
| 63 | + std::optional<ModMetadata::IssuesInfo> inputIssues(std::optional<ModMetadata::IssuesInfo> x) { |
| 64 | + ModMetadata::IssuesInfo a = x ? *x : ModMetadata::IssuesInfo{"", std::nullopt}; |
| 65 | + std::string url = a.url ? *a.url : ""; |
| 66 | + bool inputInfo = ImGui::InputText("issues.info", &a.info); |
| 67 | + bool inputUrl = ImGui::InputText("issues.url", &url); |
| 68 | + if (inputUrl) |
| 69 | + a.url = url; |
| 70 | + return inputInfo || inputUrl ? a : x; |
| 71 | + } |
| 72 | +} |
| 73 | + |
| 74 | +ModMetadata DevTools::inputMetadata(void* treePtr, ModMetadata metadata) { |
| 75 | + metadata.setVersion(inputVersion(metadata.getVersion())); |
| 76 | + metadata.setName(inputText("name", metadata.getName())); |
| 77 | + metadata.setDeveloper(inputText("developer", metadata.getDeveloper())); |
| 78 | + metadata.setDescription(inputTextMultiline("description", metadata.getDescription())); |
| 79 | + metadata.setDetails(inputTextMultiline("details", metadata.getDetails())); |
| 80 | + metadata.setChangelog(inputTextMultiline("changelog", metadata.getChangelog())); |
| 81 | + metadata.setSupportInfo(inputTextMultiline("supportInfo", metadata.getSupportInfo())); |
| 82 | + metadata.setRepository(inputTextMultiline("repository", metadata.getRepository())); |
| 83 | + metadata.setIssues(inputIssues(metadata.getIssues())); |
| 84 | + metadata.setNeedsEarlyLoad(inputBool("needsEarlyLoad", metadata.needsEarlyLoad())); |
| 85 | + metadata.setIsAPI(inputBool("isAPI", metadata.isAPI())); |
| 86 | + |
| 87 | + if (ImGui::TreeNode(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(treePtr) + 1), "dependencies")) { |
| 88 | + for (auto const& item : metadata.getDependencies()) { |
| 89 | + if (item.mod) { |
| 90 | + if (!ImGui::TreeNode(item.mod, "%s", item.id.c_str())) |
| 91 | + continue; |
| 92 | + } |
| 93 | + else { |
| 94 | + if (!ImGui::TreeNode(item.id.data(), "%s", item.id.c_str())) |
| 95 | + continue; |
| 96 | + } |
| 97 | + ImGui::Text("version: %s", item.version.toString().c_str()); |
| 98 | + const char* importance = ""; |
| 99 | + switch (item.importance) { |
| 100 | + case geode::ModMetadata::Dependency::Importance::Required: importance = "required"; break; |
| 101 | + case geode::ModMetadata::Dependency::Importance::Recommended: importance = "recommended"; break; |
| 102 | + case geode::ModMetadata::Dependency::Importance::Suggested: importance = "suggested"; break; |
| 103 | + } |
| 104 | + ImGui::Text("importance: %s", importance); |
| 105 | + ImGui::Text("isResolved: %s", item.isResolved() ? "true" : "false"); |
| 106 | + if (item.mod) |
| 107 | + drawModGraphNode(item.mod); |
| 108 | + ImGui::TreePop(); |
| 109 | + } |
| 110 | + ImGui::TreePop(); |
| 111 | + } |
| 112 | + |
| 113 | + if (ImGui::TreeNode(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(treePtr) + 2), "incompatibilities")) { |
| 114 | + for (auto const& item : metadata.getIncompatibilities()) { |
| 115 | + if (item.mod) { |
| 116 | + if (!ImGui::TreeNode(item.mod, "%s", item.id.c_str())) |
| 117 | + continue; |
| 118 | + } |
| 119 | + else { |
| 120 | + if (!ImGui::TreeNode(item.id.data(), "%s", item.id.c_str())) |
| 121 | + continue; |
| 122 | + } |
| 123 | + ImGui::Text("version: %s", item.version.toString().c_str()); |
| 124 | + const char* importance = ""; |
| 125 | + switch (item.importance) { |
| 126 | + case geode::ModMetadata::Incompatibility::Importance::Breaking: importance = "breaking"; break; |
| 127 | + case geode::ModMetadata::Incompatibility::Importance::Conflicting: importance = "conflicting"; break; |
| 128 | + } |
| 129 | + ImGui::Text("importance: %s", importance); |
| 130 | + ImGui::Text("isResolved: %s", item.isResolved() ? "true" : "false"); |
| 131 | + if (item.mod) |
| 132 | + drawModGraphNode(item.mod); |
| 133 | + ImGui::TreePop(); |
| 134 | + } |
| 135 | + ImGui::TreePop(); |
| 136 | + } |
| 137 | + |
| 138 | + if (ImGui::TreeNode(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(treePtr) + 3), "spritesheets")) { |
| 139 | + for (auto const& item : metadata.getSpritesheets()) { |
| 140 | + ImGui::Text("%s", item.c_str()); |
| 141 | + } |
| 142 | + ImGui::TreePop(); |
| 143 | + } |
| 144 | + |
| 145 | + if (ImGui::TreeNode(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(treePtr) + 4), "settings")) { |
| 146 | + for (auto const& [id, setting] : metadata.getSettings()) { |
| 147 | + if (!ImGui::TreeNode(id.data(), "%s", id.c_str())) |
| 148 | + continue; |
| 149 | + ImGui::Text("displayName: %s", setting.getDisplayName().c_str()); |
| 150 | + if (setting.getDescription()) |
| 151 | + ImGui::Text("description: %s", setting.getDescription()->c_str()); |
| 152 | + ImGui::Text("isCustom: %s", setting.isCustom() ? "true" : "false"); |
| 153 | + ImGui::TreePop(); |
| 154 | + } |
| 155 | + ImGui::TreePop(); |
| 156 | + } |
| 157 | + |
| 158 | + return metadata; |
| 159 | +} |
| 160 | + |
| 161 | +void DevTools::drawModGraphNode(Mod* node) { |
| 162 | + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_None; |
| 163 | + |
| 164 | + ImColor color = ImColor(1.f, 1.f, 1.f); |
| 165 | + if (node->isUninstalled()) |
| 166 | + color = ImColor(0.1f, 0.1f, 0.1f); |
| 167 | + else if (!node->isLoaded()) |
| 168 | + color = ImColor(1.f, 0.f, 0.f); |
| 169 | + else if (!node->isEnabled()) |
| 170 | + color = ImColor(0.7f, 0.7f, 0.7f); |
| 171 | + |
| 172 | + ImGui::PushStyleColor(ImGuiCol_Text, (ImU32)color); |
| 173 | + auto treeNode = ImGui::TreeNodeEx(node, flags, "%s", node->getID().c_str()); |
| 174 | + ImGui::PopStyleColor(); |
| 175 | + |
| 176 | + if (!treeNode) |
| 177 | + return; |
| 178 | + |
| 179 | + node->setMetadata(this->inputMetadata(node, node->getMetadata())); |
| 180 | + |
| 181 | + ImGui::Text("supportsDisabling: %s", node->supportsDisabling() ? "true" : "false"); |
| 182 | + ImGui::Text("early: %s", node->needsEarlyLoad() ? "true" : "false"); |
| 183 | + ImGui::Text("canEnable: %s", node->canEnable() ? "true" : "false"); |
| 184 | + ImGui::Text("canDisable: %s", node->canDisable() ? "true" : "false"); |
| 185 | + ImGui::Text("hasUnresolvedDependencies: %s", node->hasUnresolvedDependencies() ? "true" : "false"); |
| 186 | + ImGui::Text("hasUnresolvedIncompatibilities: %s", node->hasUnresolvedIncompatibilities() ? "true" : "false"); |
| 187 | + |
| 188 | + for (auto& dep : node->getDependants()) { |
| 189 | + this->drawModGraphNode(dep); |
| 190 | + } |
| 191 | + |
| 192 | + ImGui::TreePop(); |
| 193 | +} |
| 194 | + |
| 195 | +void DevTools::drawModIndex() { |
| 196 | + for (auto const& item : Index::get()->getItems()) { |
| 197 | + drawIndexItem(item); |
| 198 | + } |
| 199 | +} |
| 200 | + |
| 201 | +void DevTools::drawIndexItem(IndexItemHandle const& node) { |
| 202 | + auto* item = node.get(); |
| 203 | + if (!item || !ImGui::TreeNode(item, "%s", item->getMetadata().getID().c_str())) |
| 204 | + return; |
| 205 | + item->setMetadata(this->inputMetadata(item, item->getMetadata())); |
| 206 | + item->setDownloadURL(inputText("downloadURL", item->getDownloadURL())); |
| 207 | + item->setPackageHash(inputText("packageHash", item->getPackageHash())); |
| 208 | + if (ImGui::TreeNode(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(item) + 5), "availablePlatforms")) { |
| 209 | + auto platforms = item->getAvailablePlatforms(); |
| 210 | + for (PlatformID::Type type = PlatformID::Type::Unknown; type <= PlatformID::Type::Linux; (*(int*)&type)++) { |
| 211 | + bool contains = platforms.contains({type}); |
| 212 | + if (!ImGui::Checkbox(PlatformID::toString(type), &contains)) |
| 213 | + continue; |
| 214 | + if (contains) |
| 215 | + platforms.insert({type}); |
| 216 | + else |
| 217 | + platforms.erase({type}); |
| 218 | + } |
| 219 | + item->setAvailablePlatforms(platforms); |
| 220 | + ImGui::TreePop(); |
| 221 | + } |
| 222 | + item->setIsFeatured(inputBool("isFeatured", item->isFeatured())); |
| 223 | + if (ImGui::TreeNode(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(item) + 6), "tags")) { |
| 224 | + auto tags = item->getTags(); |
| 225 | + static std::string current; |
| 226 | + ImGui::InputText("", ¤t); |
| 227 | + ImGui::SameLine(); |
| 228 | + if (ImGui::Button("Add")) { |
| 229 | + tags.insert(current); |
| 230 | + current = ""; |
| 231 | + } |
| 232 | + for (auto const& tag : item->getTags()) { |
| 233 | + ImGui::Text("%s", tag.c_str()); |
| 234 | + ImGui::SameLine(); |
| 235 | + if (ImGui::Button("Remove")) |
| 236 | + tags.erase(tag); |
| 237 | + } |
| 238 | + item->setTags(tags); |
| 239 | + ImGui::TreePop(); |
| 240 | + } |
| 241 | + ImGui::Text("isInstalled: %s", item->isInstalled() ? "true" : "false"); |
| 242 | + ImGui::TreePop(); |
| 243 | +} |
0 commit comments