diff --git a/.gitignore b/.gitignore index bb8aeda..38df1ed 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,8 @@ build/ *build/ bin/ obj/ +**/bin/ +**/obj/ .vs/ .idea/ .vscode/ @@ -37,8 +39,14 @@ obj/ *.VC.db x64/ x86/ +**/x64/ +**/x86/ Debug/ Release/ +Release_codex/ +**/Debug/ +**/Release/ +**/Release_codex/ *.exe *.dll *.obj @@ -48,6 +56,8 @@ Release/ *.exp *.iobj *.ipdb +docs/archive/ + # Local bot state nxrth_config.ini diff --git a/HD/BotEngine.cpp b/HD/BotEngine.cpp index ac7db77..008d523 100644 --- a/HD/BotEngine.cpp +++ b/HD/BotEngine.cpp @@ -4,6 +4,8 @@ #include "Language.h" #include "Discord.h" #include "tesseract_ocr.h" +#include "cpp/src/infra/AhjieCrypto.h" +#include "cpp/src/infra/ActionDebugLog.h" #include #include #include "imgui.h" @@ -1207,11 +1209,16 @@ static bool TryOpenOccupiedCrateFallbackGrid(int instanceId, int cropMode, int t for (const auto& slot : kFallbackCrateCenters) { if (!bot.isRunning) return false; + cv::Mat curScreen = CaptureInstanceScreen(instanceId, kAdbPath, bot.adbSerial); + if (!IsMarketViewLikelyVisible(curScreen, cropMode)) { + AddLog(instanceId, "Shop is not visible. Aborting fallback crate probing.", ImVec4(1.0f, 0.4f, 0.2f, 1.0f)); + return false; + } AdbTap(instanceId, slot.x, slot.y); std::this_thread::sleep_for(std::chrono::milliseconds(effectiveTapWaitMs)); cv::Mat verifyScreen = CaptureInstanceScreen(instanceId, kAdbPath, bot.adbSerial); if (IsOccupiedCrateEditPanelVisible(verifyScreen) || IsSalePanelVisible(verifyScreen)) return true; - if (!IsMarketViewLikelyVisible(verifyScreen, cropMode)) return true; + if (!IsMarketViewLikelyVisible(verifyScreen, cropMode)) return false; } return false; } @@ -1756,11 +1763,17 @@ bool RunAdbCommand(int instanceId, std::string args) { std::string serial = g_Bots[instanceId].adbSerial; if (serial.empty()) return false; + if (args.find("input swipe") != std::string::npos) { + AppendActionDebugLog(instanceId, "SWIPE", "AdbSwipe", args); + } + std::string cmd = "cmd.exe /c \"\"" + kAdbPath + "\" -s " + serial + " " + args + "\""; return RunCmdHidden(cmd); } // TAP FUNCTION TO TAP ON THE SCREEN void AdbTap(int instanceId, int x, int y) { + std::string detail = "(" + std::to_string(x) + ", " + std::to_string(y) + ")"; + AppendActionDebugLog(instanceId, "TAP", "AdbTap", detail); RunAdbCommand(instanceId, "shell input tap " + std::to_string(x) + " " + std::to_string(y)); } @@ -2273,13 +2286,10 @@ void InjectImportantFiles(int instanceId) { std::thread([instanceId, exeDir, fontFile, zoomFile, minitouchFile]() { auto decryptNxrthToFile = [&](const std::string& nxPath, const std::string& tempPath) { - std::ifstream inFile(nxPath, std::ios::binary); - std::string encryptedData((std::istreambuf_iterator(inFile)), std::istreambuf_iterator()); - inFile.close(); - - std::string rawData = DecryptXORHex(encryptedData, "NXRTH_NATURE_KEY"); - if (rawData.empty()) { - AddLog(instanceId, "Error: Failed to decrypt " + nxPath, ImVec4(1, 0, 0, 1)); + std::string rawData; + std::string decryptError; + if (!ahjie::DecryptFileToBytes(nxPath, ahjie::Purpose::Asset, rawData, decryptError)) { + AddLog(instanceId, "Error: Failed to decrypt " + nxPath + " (" + decryptError + ")", ImVec4(1, 0, 0, 1)); return false; } @@ -2706,6 +2716,9 @@ bool AutoDetectTouchDevice(int instanceId) { void ExecuteDenseGridGesture(int instanceId, int startX, int startY, const std::vector& fields) { if (fields.empty()) return; + std::string gestureDetail = "Start=(" + std::to_string(startX) + "," + std::to_string(startY) + "), TargetFields=" + std::to_string(fields.size()); + AppendActionDebugLog(instanceId, "SWIPE_GRID", "MinitouchSweep", gestureDetail); + AddLog(instanceId, "[INFO] Executing Sweep...", ImVec4(0.8f, 0.4f, 1.0f, 1.0f)); // 1. FIND FIELD'S BORDER SO THE BOT CAN DRAG IT TO THE CORNER OF THE FIELDS. @@ -2779,12 +2792,19 @@ void ExecuteDenseGridGesture(int instanceId, int startX, int startY, const std:: int cx1 = x1 + (int)((tX1 - x1) * t); int cy1 = y1 + (int)((tY1 - y1) * t); - std::string mCmd = "m 0 " + std::to_string(cx0) + " " + std::to_string(cy0) + " 50\n" + - "m 1 " + std::to_string(cx1) + " " + std::to_string(cy1) + " 50\nc\n"; + // Add humanized micro-jitter to drag waypoints + int jx0 = cx0 + (rand() % 5 - 2); + int jy0 = cy0 + (rand() % 5 - 2); + int jx1 = cx1 + (rand() % 5 - 2); + int jy1 = cy1 + (rand() % 5 - 2); + + std::string mCmd = "m 0 " + std::to_string(jx0) + " " + std::to_string(jy0) + " 50\n" + + "m 1 " + std::to_string(jx1) + " " + std::to_string(jy1) + " 50\nc\n"; send(sock, mCmd.c_str(), mCmd.length(), 0); - - std::this_thread::sleep_for(std::chrono::milliseconds(20)); + // Micro-variable step delay (18ms - 23ms) + int stepDelay = 18 + (rand() % 6); + std::this_thread::sleep_for(std::chrono::milliseconds(stepDelay)); } }; @@ -3173,18 +3193,11 @@ void RunSalesCycle(int instanceId, int accountIndex, bool isEmergency) { if (!SmartSleep(g_Intervals.shopEnterWait)) return; cv::Mat verifyScreen = CaptureInstanceScreen(instanceId, kAdbPath, bot.adbSerial); - if (IsMarketViewLikelyVisible(verifyScreen, bot.testCropMode)) { + if (IsMarketViewLikelyVisible(verifyScreen, bot.testCropMode) || FindBestCrossAny(verifyScreen).found) { shopOpened = true; break; } - if (!FindImage(verifyScreen, shop_templatePath, g_Thresholds.shopThreshold, false, 1.0f, false).found) { - AddLog(instanceId, "Shop icon disappeared after tap. Assuming the roadside shop opened and continuing.", ImVec4(1.0f, 0.85f, 0.2f, 1.0f)); - shopOpened = true; - break; - } - else { - AddLog(instanceId, std::string(Tr("Shop click missed (Bug)! Retrying... (")) + std::to_string(tryCount) + "/3)", ImVec4(1, 0.5f, 0, 1)); - } + AddLog(instanceId, std::string(Tr("Shop click missed! Retrying... (")) + std::to_string(tryCount) + "/3)", ImVec4(1, 0.5f, 0, 1)); } if (!shopOpened) { @@ -4428,6 +4441,16 @@ static void RunAccountFarmCycle(int instanceId, int accountIndex) { AddLog(instanceId, Tr("Emergency sales pass finished. Resuming harvest after emergency actions..."), ImVec4(0.8f, 0.4f, 1.0f, 1.0f)); bot.statusText = Tr("Resuming Harvest..."); harvestStatus = TryHarvest(); + + if (harvestStatus == 2) { + AddLog(instanceId, "SILO STILL FULL! Emergency sales freed 0 items. Setting Silo-Full cooldown to rotate slots.", ImVec4(1.0f, 0.3f, 0.3f, 1.0f)); + account.isShopFullStuck = true; + account.lastShopCheckTime = std::chrono::steady_clock::now(); + SetFarmFlowState(instanceId, account, FarmFlowState::WaitingForSiloFull, "Silo space unresolved, slot on cooldown"); + state = FarmFlowState::CycleComplete; + break; + } + state = FarmFlowState::Planting; break; diff --git a/HD/Directory.Build.props b/HD/Directory.Build.props index ea1662e..b1c5d3f 100644 --- a/HD/Directory.Build.props +++ b/HD/Directory.Build.props @@ -1,7 +1,8 @@ - $(VCPKG_ROOT)\ - D:\vcpkg\ - E:\vcpkg\ + D:\01_Apps\DevTools\vcpkg\installed\x64-windows-static-md + D:\01_Apps\DevTools\vcpkg\installed\x64-windows + D:\vcpkg\installed\x64-windows + E:\vcpkg\installed\x64-windows diff --git a/HD/Premium bot/bot.vcxproj b/HD/Premium bot/bot.vcxproj index 685849d..c61ae08 100644 --- a/HD/Premium bot/bot.vcxproj +++ b/HD/Premium bot/bot.vcxproj @@ -61,6 +61,8 @@ + + @@ -201,7 +203,7 @@ Windows true - glfw3dll.lib;Gdi32.lib;User32.lib;opencv_calib3d4.lib;opencv_core4.lib;opencv_dnn4.lib;opencv_features2d4.lib;opencv_flann4.lib;opencv_highgui4.lib;opencv_imgcodecs4.lib;opencv_imgproc4.lib;opencv_ml4.lib;opencv_objdetect4.lib;opencv_photo4.lib;opencv_stitching4.lib;opencv_video4.lib;opencv_videoio4.lib + libcurl.lib;archive.lib;libcrypto.lib;libssl.lib;bz2.lib;lz4.lib;glfw3.lib;tesseract55.lib;leptonica-1.87.0.lib;libwebp.lib;libwebpmux.lib;libwebpdecoder.lib;libwebpdemux.lib;libsharpyuv.lib;libpng16.lib;zlib.lib;jpeg.lib;tiff.lib;gif.lib;openjp2.lib;lzma.lib;zstd.lib;Gdi32.lib;User32.lib;Ws2_32.lib;Wldap32.lib;Normaliz.lib;Crypt32.lib;Advapi32.lib;Iphlpapi.lib;Secur32.lib;XmlLite.lib;opencv_calib3d4.lib;opencv_core4.lib;opencv_dnn4.lib;opencv_features2d4.lib;opencv_flann4.lib;opencv_highgui4.lib;opencv_imgcodecs4.lib;opencv_imgproc4.lib;opencv_ml4.lib;opencv_objdetect4.lib;opencv_photo4.lib;opencv_stitching4.lib;opencv_video4.lib;opencv_videoio4.lib $(OutDir);$(HaydayVcpkgRoot)\lib;$(ProjectDir)discord-rpc;%(AdditionalLibraryDirectories) RequireAdministrator mainCRTStartup diff --git a/HD/bot_logic.cpp b/HD/bot_logic.cpp index 9e87f33..aeaea04 100644 --- a/HD/bot_logic.cpp +++ b/HD/bot_logic.cpp @@ -97,8 +97,142 @@ static bool RunCmdHiddenLogic(const std::string& command) { return true; } +int g_ScreenshotMode = SCREENSHOT_MODE_WINAPI; + +// PrintWindow flag (Windows 8.1+) +#ifndef PW_RENDERFULLCONTENT +#define PW_RENDERFULLCONTENT 0x00000002 +#endif + +struct WindowSearchCtx { + const char* sub; + HWND found; +}; + +static BOOL CALLBACK EnumWindowsPartialProc(HWND hwnd, LPARAM lParam) { + if (!IsWindowVisible(hwnd)) return TRUE; + char title[256] = {}; + GetWindowTextA(hwnd, title, sizeof(title)); + WindowSearchCtx* ctx = (WindowSearchCtx*)lParam; + if (title[0] != '\0' && ctx->sub && strstr(title, ctx->sub) != nullptr) { + ctx->found = hwnd; + return FALSE; + } + return TRUE; +} + +static HWND FindWindowPartial(const char* windowTitle) { + if (!windowTitle || windowTitle[0] == '\0') return NULL; + HWND hwnd = FindWindowA(NULL, windowTitle); + if (hwnd && IsWindow(hwnd)) return hwnd; + + WindowSearchCtx ctx{ windowTitle, NULL }; + EnumWindows(EnumWindowsPartialProc, (LPARAM)&ctx); + return ctx.found; +} + +// --- WINDOWS API (GDI) FAST SCREENSHOT CAPTURE --- +static cv::Mat CaptureWindowGDI(const char* windowTitle, int targetW, int targetH) { + if (!windowTitle || windowTitle[0] == '\0' || targetW <= 0 || targetH <= 0) + return cv::Mat(); + + HWND hwnd = FindWindowPartial(windowTitle); + if (!hwnd || !IsWindow(hwnd)) return cv::Mat(); + + RECT wr; + if (!GetWindowRect(hwnd, &wr)) return cv::Mat(); + int fullW = wr.right - wr.left; + int fullH = wr.bottom - wr.top; + if (fullW <= 0 || fullH <= 0) return cv::Mat(); + + HDC hdcScreen = GetDC(NULL); + HDC hdcMem = CreateCompatibleDC(hdcScreen); + HBITMAP hbm = CreateCompatibleBitmap(hdcScreen, fullW, fullH); + HGDIOBJ oldObj = SelectObject(hdcMem, hbm); + + BOOL ok = PrintWindow(hwnd, hdcMem, PW_RENDERFULLCONTENT); + if (!ok) ok = PrintWindow(hwnd, hdcMem, 0); + + SelectObject(hdcMem, oldObj); + + cv::Mat full; + if (ok) { + BITMAPINFOHEADER bi = {}; + bi.biSize = sizeof(BITMAPINFOHEADER); + bi.biWidth = fullW; + bi.biHeight = -fullH; // top-down + bi.biPlanes = 1; + bi.biBitCount = 32; + bi.biCompression = BI_RGB; + + cv::Mat bgra(fullH, fullW, CV_8UC4); + if (GetDIBits(hdcScreen, hbm, 0, fullH, bgra.data, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) { + cv::cvtColor(bgra, full, cv::COLOR_BGRA2BGR); + } + } + + DeleteObject(hbm); + DeleteDC(hdcMem); + ReleaseDC(NULL, hdcScreen); + + if (full.empty()) return cv::Mat(); + + // Client area crop (strip title bar & borders) + RECT cr; + POINT tl = { 0, 0 }; + if (GetClientRect(hwnd, &cr) && ClientToScreen(hwnd, &tl)) { + int offX = tl.x - wr.left; + int offY = tl.y - wr.top; + int cw = cr.right - cr.left; + int ch = cr.bottom - cr.top; + if (cw > 0 && ch > 0 && offX >= 0 && offY >= 0 && + offX + cw <= full.cols && offY + ch <= full.rows) { + full = full(cv::Rect(offX, offY, cw, ch)).clone(); + } + } + + // Render area crop (strip emulator sidebar toolbar) + double targetAR = (double)targetW / (double)targetH; + int cw = full.cols, ch = full.rows; + int renderW, renderH; + int fitW = (int)(ch * targetAR + 0.5); + if (fitW <= cw) { + renderW = fitW; + renderH = ch; + } + else { + renderW = cw; + renderH = (int)(cw / targetAR + 0.5); + } + if (renderW > 0 && renderH > 0 && renderW <= full.cols && renderH <= full.rows) { + full = full(cv::Rect(0, 0, renderW, renderH)); + } + + // Reject black frames (fall back to ADB) + cv::Scalar m = cv::mean(full); + if (m[0] + m[1] + m[2] < 3.0) return cv::Mat(); + + cv::Mat out; + cv::resize(full, out, cv::Size(targetW, targetH), 0, 0, cv::INTER_AREA); + return out; +} + +static constexpr int kMaxInstanceCount = 6; +static int g_RefCapW[kMaxInstanceCount] = { 0 }; +static int g_RefCapH[kMaxInstanceCount] = { 0 }; + // SCREEN CAPTURING cv::Mat CaptureInstanceScreen(int instanceId, const std::string& adbPath, const std::string& serial) { + if (g_ScreenshotMode == SCREENSHOT_MODE_WINAPI && + instanceId >= 0 && instanceId < kMaxInstanceCount) { + int tw = g_RefCapW[instanceId]; + int th = g_RefCapH[instanceId]; + if (tw > 0 && th > 0) { + cv::Mat win = CaptureWindowGDI(g_Bots[instanceId].vmName, tw, th); + if (!win.empty()) return win; + } + } + std::string tempFile = "C:\\Users\\Public\\adb_screen_" + std::to_string(instanceId) + ".png"; int maxRetries = 2; cv::Mat img; @@ -110,7 +244,13 @@ cv::Mat CaptureInstanceScreen(int instanceId, const std::string& adbPath, const if (RunCmdHiddenLogic(cmd)) { if (WaitForValidFile(tempFile)) { img = cv::imread(tempFile); - if (!img.empty()) return img; + if (!img.empty()) { + if (instanceId >= 0 && instanceId < kMaxInstanceCount) { + g_RefCapW[instanceId] = img.cols; + g_RefCapH[instanceId] = img.rows; + } + return img; + } } } // RETRY IF FAILED diff --git a/HD/bot_logic.h b/HD/bot_logic.h index 16c1066..10a3853 100644 --- a/HD/bot_logic.h +++ b/HD/bot_logic.h @@ -362,19 +362,31 @@ struct TemplateThresholds { float siloFullCrossThreshold = 0.70f; }; +constexpr int SCREENSHOT_MODE_WINAPI = 0; +constexpr int SCREENSHOT_MODE_ADB = 1; +extern int g_ScreenshotMode; + struct IntervalSettings { int gameLoadWait = 22; int afterHarvestWait = 2800; int afterPlantWait = 2600; + int fieldTapWait = 250; + int menuCloseWait = 500; + int tapResponseWait = 400; + int pageLoadWait = 1000; + int shopSearchWait = 500; int shopEnterWait = 2000; + int crateOpenWait = 600; int crateClickWait = 800; int nextAccountWait = 2000; int coinCollectWait = 700; int productSelectWait = 500; int createSaleWait = 1000; + int saleConfirmWait = 500; + int siloBarnWait = 1500; + int accountLoadWait = 2000; bool autoZoomOutAfterMailbox = false; int mailboxZoomDelayMs = 5000; - }; @@ -401,3 +413,4 @@ std::vector FindEmptyFields(const cv::Mat& screen, bool isRecheck = std::string EncryptXORHex(const std::string& text, const std::string& key = "ISpentDaysForThisApp"); std::string DecryptXORHex(const std::string& hexStr, const std::string& key = "ISpentDaysForThisApp"); + diff --git a/HD/cpp/src/infra/ActionDebugLog.cpp b/HD/cpp/src/infra/ActionDebugLog.cpp index a08cb4e..5235b0d 100644 --- a/HD/cpp/src/infra/ActionDebugLog.cpp +++ b/HD/cpp/src/infra/ActionDebugLog.cpp @@ -13,7 +13,7 @@ #include #include -bool g_EnableDebugActionLog = false; +bool g_EnableDebugActionLog = true; static std::mutex g_DebugActionLogMutex; static std::string g_DebugActionLogPath; static constexpr unsigned long long kMaxDebugActionLogBytes = 4ull * 1024ull * 1024ull; diff --git a/HD/cpp/src/infra/AhjieCrypto.cpp b/HD/cpp/src/infra/AhjieCrypto.cpp index 59d1bc5..d63cff5 100644 --- a/HD/cpp/src/infra/AhjieCrypto.cpp +++ b/HD/cpp/src/infra/AhjieCrypto.cpp @@ -31,9 +31,16 @@ constexpr size_t kNonceSize = 12; constexpr size_t kTagSize = 16; constexpr size_t kKeySize = 32; +constexpr unsigned char kYaJingMagic[] = { 'Y', 'a', 'J', 'i', 'n', 'g' }; +constexpr size_t kYaJingMagicSize = sizeof(kYaJingMagic); +constexpr size_t kYaJingHeaderSize = kYaJingMagicSize + 2; + const unsigned char kAssetKeySeed[] = "HaydayMod.Ahjie.asset-payloads.v1.NXRTH_NATURE_KEY.replacement"; +const unsigned char kYaJingAssetKeySeed[] = + "HaydayMod.YaJing.asset-payloads.v1.NXRTH_NATURE_KEY.replacement"; + struct BCryptAlgHandle { BCRYPT_ALG_HANDLE value = nullptr; ~BCryptAlgHandle() { @@ -248,6 +255,88 @@ std::array HeaderFor(Purpose purpose) { return header; } +bool IsYaJingBytes(const std::string& data) { + return data.size() >= kYaJingHeaderSize + && memcmp(data.data(), kYaJingMagic, kYaJingMagicSize) == 0 + && static_cast(data[kYaJingMagicSize]) == kVersion; +} + +bool DecryptYaJingBytes(const std::string& encrypted, Purpose expectedPurpose, std::string& plaintext, std::string& error) { + if (!IsYaJingBytes(encrypted) || encrypted.size() < kYaJingHeaderSize + kNonceSize + kTagSize) { + error = "Not a YaJing payload"; + return false; + } + + std::array key{}; + if (expectedPurpose == Purpose::Asset) { + if (!HashSha256(kYaJingAssetKeySeed, sizeof(kYaJingAssetKeySeed) - 1, key, error)) return false; + } else { + if (!LoadOrCreateAccountKey(key, error)) return false; + } + + BCryptAlgHandle alg; + BCryptKeyHandle keyHandle; + std::vector keyObject; + if (!OpenAesGcm(key, alg, keyHandle, keyObject, error)) return false; + + const unsigned char* header = reinterpret_cast(encrypted.data()); + const unsigned char* nonce = header + kYaJingHeaderSize; + const unsigned char* tag = nonce + kNonceSize; + const unsigned char* ciphertext = tag + kTagSize; + const size_t ciphertextSize = encrypted.size() - kYaJingHeaderSize - kNonceSize - kTagSize; + std::vector output(ciphertextSize); + + BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo; + BCRYPT_INIT_AUTH_MODE_INFO(authInfo); + authInfo.pbNonce = const_cast(nonce); + authInfo.cbNonce = static_cast(kNonceSize); + authInfo.pbAuthData = const_cast(header); + authInfo.cbAuthData = static_cast(kYaJingHeaderSize); + authInfo.pbTag = const_cast(tag); + authInfo.cbTag = static_cast(kTagSize); + + ULONG written = 0; + NTSTATUS status = BCryptDecrypt( + keyHandle.value, + const_cast(ciphertext), + static_cast(ciphertextSize), + &authInfo, + nullptr, + 0, + output.empty() ? nullptr : output.data(), + static_cast(output.size()), + &written, + 0); + if (status < 0) { + error = StatusError("BCryptDecrypt (YaJing)", status); + return false; + } + + plaintext.assign(reinterpret_cast(output.data()), written); + return true; +} + +bool DecryptNxrthXorBytes(const std::string& encrypted, const std::string& key, std::string& plaintext) { + if (encrypted.empty() || key.empty()) return false; + std::string cleanHex = encrypted; + if (cleanHex.length() > 32) { + cleanHex = cleanHex.substr(16, cleanHex.length() - 32); + } + + if (cleanHex.length() % 2 != 0) return false; + plaintext.clear(); + plaintext.reserve(cleanHex.length() / 2); + for (size_t i = 0; i < cleanHex.length(); i += 2) { + char* endPtr = nullptr; + std::string byteString = cleanHex.substr(i, 2); + long val = strtol(byteString.c_str(), &endPtr, 16); + if (endPtr == byteString.c_str()) return false; + char byte = static_cast(val); + plaintext.push_back(byte ^ key[(i / 2) % key.length()]); + } + return !plaintext.empty(); +} + } // namespace bool IsAhjieBytes(const std::string& data) { @@ -306,59 +395,73 @@ bool EncryptBytes(const std::string& plaintext, Purpose purpose, std::string& en } bool DecryptBytes(const std::string& encrypted, Purpose expectedPurpose, std::string& plaintext, std::string& error) { - if (encrypted.size() < kHeaderSize + kNonceSize + kTagSize || !IsAhjieBytes(encrypted)) { - error = "Not an Ahjie payload"; - return false; - } - const unsigned char purposeByte = static_cast(encrypted[kMagicSize + 1]); - if (purposeByte != static_cast(expectedPurpose)) { - error = "Ahjie purpose mismatch"; - return false; - } - - std::array key{}; - if (!KeyForPurpose(expectedPurpose, key, error)) return false; - - BCryptAlgHandle alg; - BCryptKeyHandle keyHandle; - std::vector keyObject; - if (!OpenAesGcm(key, alg, keyHandle, keyObject, error)) return false; + if (IsAhjieBytes(encrypted)) { + if (encrypted.size() < kHeaderSize + kNonceSize + kTagSize) { + error = "Truncated Ahjie payload"; + return false; + } + const unsigned char purposeByte = static_cast(encrypted[kMagicSize + 1]); + if (purposeByte != static_cast(expectedPurpose)) { + error = "Ahjie purpose mismatch"; + return false; + } - const unsigned char* header = reinterpret_cast(encrypted.data()); - const unsigned char* nonce = header + kHeaderSize; - const unsigned char* tag = nonce + kNonceSize; - const unsigned char* ciphertext = tag + kTagSize; - const size_t ciphertextSize = encrypted.size() - kHeaderSize - kNonceSize - kTagSize; - std::vector output(ciphertextSize); + std::array key{}; + if (!KeyForPurpose(expectedPurpose, key, error)) return false; + + BCryptAlgHandle alg; + BCryptKeyHandle keyHandle; + std::vector keyObject; + if (!OpenAesGcm(key, alg, keyHandle, keyObject, error)) return false; + + const unsigned char* header = reinterpret_cast(encrypted.data()); + const unsigned char* nonce = header + kHeaderSize; + const unsigned char* tag = nonce + kNonceSize; + const unsigned char* ciphertext = tag + kTagSize; + const size_t ciphertextSize = encrypted.size() - kHeaderSize - kNonceSize - kTagSize; + std::vector output(ciphertextSize); + + BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo; + BCRYPT_INIT_AUTH_MODE_INFO(authInfo); + authInfo.pbNonce = const_cast(nonce); + authInfo.cbNonce = static_cast(kNonceSize); + authInfo.pbAuthData = const_cast(header); + authInfo.cbAuthData = static_cast(kHeaderSize); + authInfo.pbTag = const_cast(tag); + authInfo.cbTag = static_cast(kTagSize); + + ULONG written = 0; + NTSTATUS status = BCryptDecrypt( + keyHandle.value, + const_cast(ciphertext), + static_cast(ciphertextSize), + &authInfo, + nullptr, + 0, + output.empty() ? nullptr : output.data(), + static_cast(output.size()), + &written, + 0); + if (status >= 0) { + plaintext.assign(reinterpret_cast(output.data()), written); + return true; + } + } - BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo; - BCRYPT_INIT_AUTH_MODE_INFO(authInfo); - authInfo.pbNonce = const_cast(nonce); - authInfo.cbNonce = static_cast(kNonceSize); - authInfo.pbAuthData = const_cast(header); - authInfo.cbAuthData = static_cast(kHeaderSize); - authInfo.pbTag = const_cast(tag); - authInfo.cbTag = static_cast(kTagSize); + // Fallback 1: Legacy YaJing format + std::string yjError; + if (IsYaJingBytes(encrypted) && DecryptYaJingBytes(encrypted, expectedPurpose, plaintext, yjError)) { + return true; + } - ULONG written = 0; - NTSTATUS status = BCryptDecrypt( - keyHandle.value, - const_cast(ciphertext), - static_cast(ciphertextSize), - &authInfo, - nullptr, - 0, - output.empty() ? nullptr : output.data(), - static_cast(output.size()), - &written, - 0); - if (status < 0) { - error = StatusError("BCryptDecrypt", status); - return false; + // Fallback 2: Legacy NXRTH XOR format + std::string xorKey = (expectedPurpose == Purpose::Asset) ? "NXRTH_NATURE_KEY" : "NXRTH_LOCAL_ACCOUNT_KEY"; + if (DecryptNxrthXorBytes(encrypted, xorKey, plaintext)) { + return true; } - plaintext.assign(reinterpret_cast(output.data()), written); - return true; + error = "Not an Ahjie payload and legacy decryption failed"; + return false; } bool EncryptFileFromBytes(const std::string& plaintext, const std::string& outputPath, Purpose purpose, std::string& error) { diff --git a/HD/premium gui.cpp b/HD/premium gui.cpp index 04dbe77..e3bec5f 100644 --- a/HD/premium gui.cpp +++ b/HD/premium gui.cpp @@ -925,16 +925,26 @@ void SaveConfig() { out << "EnableBarnWebhook=" << (g_EnableBarnWebhook ? "1" : "0") << "\n"; out << "EnableWebhookImage=" << (g_EnableWebhookImage ? "1" : "0") << "\n"; + out << "ScreenshotMode=" << g_ScreenshotMode << "\n"; out << "GameLoadWait=" << g_Intervals.gameLoadWait << "\n"; out << "AfterHarvestWait=" << g_Intervals.afterHarvestWait << "\n"; out << "AfterPlantWait=" << g_Intervals.afterPlantWait << "\n"; + out << "FieldTapWait=" << g_Intervals.fieldTapWait << "\n"; + out << "MenuCloseWait=" << g_Intervals.menuCloseWait << "\n"; + out << "TapResponseWait=" << g_Intervals.tapResponseWait << "\n"; + out << "PageLoadWait=" << g_Intervals.pageLoadWait << "\n"; + out << "ShopSearchWait=" << g_Intervals.shopSearchWait << "\n"; out << "ShopEnterWait=" << g_Intervals.shopEnterWait << "\n"; + out << "CrateOpenWait=" << g_Intervals.crateOpenWait << "\n"; out << "CrateClickWait=" << g_Intervals.crateClickWait << "\n"; out << "NextAccountWait=" << g_Intervals.nextAccountWait << "\n"; out << "CoinCollectWait=" << g_Intervals.coinCollectWait << "\n"; out << "ProductSelectWait=" << g_Intervals.productSelectWait << "\n"; out << "CreateSaleWait=" << g_Intervals.createSaleWait << "\n"; + out << "SaleConfirmWait=" << g_Intervals.saleConfirmWait << "\n"; + out << "SiloBarnWait=" << g_Intervals.siloBarnWait << "\n"; + out << "AccountLoadWait=" << g_Intervals.accountLoadWait << "\n"; out << "AutoZoomOutAfterMailbox=" << (g_Intervals.autoZoomOutAfterMailbox ? "1" : "0") << "\n"; out << "MailboxZoomDelayMs=" << g_Intervals.mailboxZoomDelayMs << "\n"; out << "OcrAnchorThreshold=" << g_OcrAnchorThreshold << "\n"; @@ -1090,10 +1100,20 @@ void LoadConfig() { else if (key == "EnableWebhookImage") g_EnableWebhookImage = (val == "1"); else if (key == "DiscordID") strncpy(g_DiscordID, val.c_str(), 64); + else if (key == "ScreenshotMode") { + g_ScreenshotMode = std::stoi(val); + if (g_ScreenshotMode != SCREENSHOT_MODE_ADB) g_ScreenshotMode = SCREENSHOT_MODE_WINAPI; + } else if (key == "GameLoadWait") g_Intervals.gameLoadWait = std::stoi(val); else if (key == "AfterHarvestWait") g_Intervals.afterHarvestWait = std::stoi(val); else if (key == "AfterPlantWait") g_Intervals.afterPlantWait = std::stoi(val); + else if (key == "FieldTapWait") g_Intervals.fieldTapWait = std::stoi(val); + else if (key == "MenuCloseWait") g_Intervals.menuCloseWait = std::stoi(val); + else if (key == "TapResponseWait") g_Intervals.tapResponseWait = std::stoi(val); + else if (key == "PageLoadWait") g_Intervals.pageLoadWait = std::stoi(val); + else if (key == "ShopSearchWait") g_Intervals.shopSearchWait = std::stoi(val); else if (key == "ShopEnterWait") g_Intervals.shopEnterWait = std::stoi(val); + else if (key == "CrateOpenWait") g_Intervals.crateOpenWait = std::stoi(val); else if (key == "CrateClickWait") g_Intervals.crateClickWait = std::stoi(val); else if (key == "NextAccountWait") g_Intervals.nextAccountWait = std::stoi(val); @@ -1101,6 +1121,9 @@ void LoadConfig() { else if (key == "CoinCollectWait") g_Intervals.coinCollectWait = std::stoi(val); else if (key == "ProductSelectWait") g_Intervals.productSelectWait = std::stoi(val); else if (key == "CreateSaleWait") g_Intervals.createSaleWait = std::stoi(val); + else if (key == "SaleConfirmWait") g_Intervals.saleConfirmWait = std::stoi(val); + else if (key == "SiloBarnWait") g_Intervals.siloBarnWait = std::stoi(val); + else if (key == "AccountLoadWait") g_Intervals.accountLoadWait = std::stoi(val); else if (key == "AutoZoomOutAfterMailbox") g_Intervals.autoZoomOutAfterMailbox = (val == "1"); else if (key == "MailboxZoomDelayMs") g_Intervals.mailboxZoomDelayMs = (std::max)(0, std::stoi(val)); else if (key == "OcrAnchorThreshold") g_OcrAnchorThreshold = std::stof(val); @@ -3081,7 +3104,22 @@ static void ClassicTemplateManagerTab() { if (ImGui::Button("Test Sickle", ImVec2(135, 28))) PerformTemplateTest(g_ClassicSelectedInstance, s_templatePath, "Sickle Check", g_Thresholds.sickleThreshold, false); ImGui::SameLine(); if (ImGui::Button("Test Cow Feed", ImVec2(135, 28))) PerformTemplateTest(g_ClassicSelectedInstance, feed_mill_templatePath, "Cow Feed Check", g_Thresholds.feedMillThreshold, false); + if (ImGui::Button("Test Dairy", ImVec2(135, 28))) PerformTemplateTest(g_ClassicSelectedInstance, dairy_templatePath, "Dairy Check", g_Thresholds.dairyThreshold, false); + ImGui::SameLine(); + if (ImGui::Button("Test Grow", ImVec2(135, 28))) PerformTemplateTest(g_ClassicSelectedInstance, "", "Grow Check", 0.0f, false); + + if (ImGui::Button("Test Harvest", ImVec2(135, 28))) PerformTemplateTest(g_ClassicSelectedInstance, "", "Harvest Check", 0.0f, false); + ImGui::SameLine(); + if (ImGui::Button("Test Sell", ImVec2(135, 28))) PerformTemplateTest(g_ClassicSelectedInstance, "", "Sell Check", 0.0f, false); + + if (ImGui::Button("Test Barn/Silo", ImVec2(135, 28))) PerformTemplateTest(g_ClassicSelectedInstance, "", "Barn/Silo Check", 0.0f, false); + ImGui::SameLine(); + if (ImGui::Button("Test Silo Cross", ImVec2(135, 28))) PerformTemplateTest(g_ClassicSelectedInstance, "", "Silo Full Cross Check", 0.0f, false); + + if (ImGui::Button("Test Reserve OCR", ImVec2(135, 28))) PerformTemplateTest(g_ClassicSelectedInstance, "", "Preserve OCR Check", 0.0f, false); + ImGui::SameLine(); + if (ImGui::Button("Test Sale Mode", ImVec2(135, 28))) PerformTemplateTest(g_ClassicSelectedInstance, "", "Sale Mode Check", 0.0f, false); ImGui::EndChild(); ImGui::NextColumn(); ClassicBeginPanel("Screenshot Display", ImVec2(0, 0)); @@ -5156,6 +5194,26 @@ void RenderApp() { ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); + // --- SCREENSHOT MODE (WinAPI / ADB) --- + ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f), Tr("SCREENSHOT MODE")); + ImGui::Separator(); + ImGui::Spacing(); + ImGui::TextDisabled(Tr("How the bot captures the emulator screen. WinAPI is the fast method (no disk writes). Switch to ADB if WinAPI shows a black screen or misbehaves.")); + { + const char* ssModes = "WinAPI (Fast)\0ADB API (Slow)\0"; + ImGui::PushItemWidth(220); + if (ImGui::Combo(Tr("Capture Method"), &g_ScreenshotMode, ssModes)) { + if (g_ScreenshotMode != SCREENSHOT_MODE_ADB) g_ScreenshotMode = SCREENSHOT_MODE_WINAPI; + SaveConfig(); + } + ImGui::PopItemWidth(); + } + if (g_ScreenshotMode == SCREENSHOT_MODE_WINAPI) + ImGui::TextColored(ImVec4(0.4f, 0.9f, 0.4f, 1.0f), Tr("WinAPI: fast host-side capture. Falls back to ADB automatically if a frame fails.")); + else + ImGui::TextColored(ImVec4(0.95f, 0.75f, 0.2f, 1.0f), Tr("ADB: slower legacy capture (writes each frame to disk).")); + ImGui::Spacing(); ImGui::Separator(); ImGui::Spacing(); + ImGui::Text(Tr("ADB Executable Path:")); float inputWidth = ImGui::GetContentRegionAvail().x - 130; ImGui::PushItemWidth(inputWidth); @@ -5199,19 +5257,38 @@ void RenderApp() { ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f), Tr("TIMING & DELAY SETTINGS (Advanced)")); ImGui::Separator(); ImGui::Spacing(); - ImGui::TextDisabled(Tr("Adjust these values if your emulator is lagging or running too fast.")); + ImGui::TextDisabled(Tr("Adjust each wait if your emulator is lagging or running too fast. Higher = slower but safer.")); + + ImGui::Spacing(); + ImGui::TextDisabled(Tr("Startup & Farming:")); bool timingChanged = false; timingChanged |= ImGui::SliderInt(Tr("Game Load Wait (Seconds)"), &g_Intervals.gameLoadWait, 5, 45); timingChanged |= ImGui::SliderInt(Tr("Harvest Cooldown (ms)"), &g_Intervals.afterHarvestWait, 300, 5000); timingChanged |= ImGui::SliderInt(Tr("Planting Cooldown (ms)"), &g_Intervals.afterPlantWait, 300, 5000); - timingChanged |= ImGui::SliderInt(Tr("Shop Open Wait (ms)"), &g_Intervals.shopEnterWait, 500, 5000); - timingChanged |= ImGui::SliderInt(Tr("Next Account Wait (ms)"), &g_Intervals.nextAccountWait, 500, 5000); + timingChanged |= ImGui::SliderInt(Tr("Field Tap Wait (ms)"), &g_Intervals.fieldTapWait, 50, 2000); + + ImGui::Spacing(); + ImGui::TextDisabled(Tr("Navigation:")); + timingChanged |= ImGui::SliderInt(Tr("Menu Close Wait (ms)"), &g_Intervals.menuCloseWait, 100, 3000); + timingChanged |= ImGui::SliderInt(Tr("Tap Response Wait (ms)"), &g_Intervals.tapResponseWait, 100, 3000); + timingChanged |= ImGui::SliderInt(Tr("Page Load Wait (ms)"), &g_Intervals.pageLoadWait, 300, 5000); + ImGui::Spacing(); - ImGui::TextDisabled(Tr("Shop Automation Speeds:")); + ImGui::TextDisabled(Tr("Shop & Sales:")); + timingChanged |= ImGui::SliderInt(Tr("Shop Search Wait (ms)"), &g_Intervals.shopSearchWait, 200, 3000); + timingChanged |= ImGui::SliderInt(Tr("Shop Open Wait (ms)"), &g_Intervals.shopEnterWait, 500, 5000); + timingChanged |= ImGui::SliderInt(Tr("Crate Open Wait (ms)"), &g_Intervals.crateOpenWait, 300, 3000); timingChanged |= ImGui::SliderInt(Tr("Crate Menu Wait (ms)"), &g_Intervals.crateClickWait, 300, 3000); timingChanged |= ImGui::SliderInt(Tr("Coin Collect Wait (ms)"), &g_Intervals.coinCollectWait, 200, 2000); timingChanged |= ImGui::SliderInt(Tr("Product Select Wait (ms)"), &g_Intervals.productSelectWait, 200, 2000); timingChanged |= ImGui::SliderInt(Tr("Create Sale Wait (ms)"), &g_Intervals.createSaleWait, 300, 3000); + timingChanged |= ImGui::SliderInt(Tr("Sale Confirm Wait (ms)"), &g_Intervals.saleConfirmWait, 200, 2000); + + ImGui::Spacing(); + ImGui::TextDisabled(Tr("Silo / Barn & Accounts:")); + timingChanged |= ImGui::SliderInt(Tr("Silo/Barn Check Wait (ms)"), &g_Intervals.siloBarnWait, 500, 5000); + timingChanged |= ImGui::SliderInt(Tr("Next Account Wait (ms)"), &g_Intervals.nextAccountWait, 500, 5000); + timingChanged |= ImGui::SliderInt(Tr("Account Load Wait (ms)"), &g_Intervals.accountLoadWait, 500, 6000); if (timingChanged) SaveConfig(); if (ImGui::Button("TURBO FARM", ImVec2(130, 28))) { g_Intervals.gameLoadWait = 8; diff --git a/README.md b/README.md index 1d4c5f3..bbbabbc 100644 --- a/README.md +++ b/README.md @@ -56,9 +56,10 @@ flowchart TD end subgraph Core ["Bot Engine & Recognition"] - ADB["Background ADB Pipe Wrapper"] + WINAPI["WinAPI GDI Capture (CaptureWindowGDI)"] + MINITOUCH["Minitouch Stealth Touch Agent"] OCR["Tesseract OCR Engine"] - COLOR["Direct Color-State Matcher"] + COLOR["Direct HSV Color Matcher"] end subgraph Emulator ["Android Emulator Pool"] @@ -68,23 +69,96 @@ flowchart TD UI --> CFG CFG <--> DPAPI - DPAPI --> AES + DPAPI <--> AES CFG --> Core - Core --> ADB + Core --> WINAPI Core --> OCR Core --> COLOR - ADB <--> E1 - ADB <--> E2 + Core --> MINITOUCH + MINITOUCH <--> E1 + MINITOUCH <--> E2 +``` + +--- + +## 🌾 Streamlined Core Workflow + +```mermaid +flowchart TD + Start["Start Bot Instance"] --> SelectSlot["1. Select Active Account Slot"] + + SelectSlot --> CheckStuck{"Shop Blocked?"} + CheckStuck -- Yes --> ClearAd["Clear Shop Ad Cooldown"] + CheckStuck -- No --> AutoTom["2. Run Auto Tom Helper"] + ClearAd --> AutoTom + + AutoTom --> Harvest["3. Harvest Crops"] + Harvest --> SiloCheck{"Silo 100% Full?"} + + SiloCheck -- Yes --> EmergencySale["Run Emergency Sales Pass"] + EmergencySale --> SpaceCheck{"Silo Space Freed?"} + SpaceCheck -- No --> SiloCooldown["Set Silo-Full Cooldown & Skip Slot"] + SpaceCheck -- Yes --> Plant["4. Plant Empty Fields"] + SiloCheck -- No --> Plant + + Plant --> SalesTrigger{"Run Sales?"} + SalesTrigger -- No --> NextSlot + SalesTrigger -- Yes --> OpenShop["5. Open Roadside Shop"] + + OpenShop --> CollectCoins["Collect Gold & Free Crates"] + CollectCoins --> CrateCheck{"Empty Crate Available?"} + + CrateCheck -- Yes --> ListCrop["List Crop at Configured Price"] + CrateCheck -- No --> AdCheck{"Ad Available?"} + + AdCheck -- Yes --> PublishAd["Publish Newspaper Ad"] + AdCheck -- No --> ShopCooldown["Set Shop-Full Cooldown & Skip Slot"] + + ListCrop --> NextSlot["6. Advance to Next Account Slot"] + PublishAd --> NextSlot + SiloCooldown --> NextSlot + ShopCooldown --> NextSlot +``` + +--- + +## πŸ‘¦ Auto Tom Helper Workflow + +```mermaid +flowchart TD + Start["Account Slot Start"] --> CheckEnabled{"Auto Tom Enabled?"} + CheckEnabled -- No --> SkipTom["Proceed to Harvest & Plant"] + CheckEnabled -- Yes --> CheckCooldown{"Tom Awake? (nextTomTime <= now)"} + + CheckCooldown -- Sleeping --> SkipTom + CheckCooldown -- Awake --> OpenTom["Tap Tom Crate on Farm"] + + OpenTom --> SelectCategory["Select Barn or Silo Tab"] + SelectCategory --> InputItem["Type Item Name via ADB"] + InputItem --> ConfirmSearch["Confirm Search"] + + ConfirmSearch --> WaitReturn["Wait 30s for Tom Search"] + WaitReturn --> ChooseStack["Select Max Quantity Crate"] + ChooseStack --> CoinCheck{"Coins Sufficient?"} + + CoinCheck -- Low Coins --> FallbackStack["Fallback to Mid / Min Stack"] + CoinCheck -- Ok --> ConfirmPurchase["Confirm Purchase"] + FallbackStack --> ConfirmPurchase + + ConfirmPurchase --> WaitDelivery["Wait 30s for Tom Delivery"] + WaitDelivery --> CollectItems["Tap Balloons to Collect to Barn/Silo"] + CollectItems --> SetCooldown["Set 2-Hour Cooldown"] + SetCooldown --> SkipTom ``` --- ## πŸš€ Key Features -* **⚑ Ultra-Low CPU Footprint**: Built entirely in native C++20 with DirectX/ImGui renderingβ€”no heavy Python runtimes or Electron wrappers. +* **⚑ Zero-Disk-Write WinAPI Engine**: Captures host emulator window framebuffers directly via Windows GDI (`CaptureWindowGDI`) without slow disk PNG writes or ADB delays. * **πŸ” Hardware-Protected Credentials (`.Ahjie`)**: Stores account profiles in AES-256-GCM encrypted format tied to machine identity via Windows DPAPI. -* **πŸ‘οΈ Precision OCR & Visual State Detection**: Integrated Tesseract OCR reads level numbers, coin balances, diamond counts, and item quantities accurately. -* **πŸ”„ Multi-Instance Background Rotation**: Rotates through minimized MEmu or LDPlayer instances without taking over user mouse or keyboard. +* **πŸ‘οΈ Precision OCR & HSV Color Detection**: Uses fast HSV color segmentation for crops (Cyan/Blue/Pink/Mint/Purple) and empty soil (Magenta), combined with Tesseract OCR for coin, diamond, and storage level tracking. +* **πŸ”„ Anti-Deadlock Multi-Instance Rotation**: Features non-blocking Silo-Full recovery, shop ad cooldown management, and automated background account slot rotation. --- @@ -94,10 +168,10 @@ For optimal OCR accuracy and pixel state matching, ensure your emulator matches | Parameter | Recommended Setting | Note | | :--- | :--- | :--- | -| **Resolution** | `640 x 480` | Required for OCR alignment | +| **Resolution** | `640 x 480` | Strictly required for coordinate & OCR alignment | | **DPI** | `100 DPI` | Required for coordinate mapping | | **Graphics Engine** | `DirectX` | Prevents frame buffer tearing | -| **Permissions** | `Root Enabled` | Allows background touch routing | +| **Permissions** | `Root Enabled` | Allows background touch routing via Minitouch | | **In-Game Language**| `English` | Required for Tesseract traineddata | --- diff --git a/quickstart.bat b/quickstart.bat index 9076db3..84a592e 100644 --- a/quickstart.bat +++ b/quickstart.bat @@ -1,4 +1,5 @@ @echo off +setlocal EnableDelayedExpansion TITLE "Hayday-Bot-Pro Quickstart Launcher" echo ======================================================== @@ -8,8 +9,8 @@ echo. :: Check for ADB where adb >nul 2>&1 -if %ERRORLEVEL% NEQ 0 ( - echo [!] WARNING: adb command not found in system PATH. +if !ERRORLEVEL! NEQ 0 ( + echo [-] WARNING: adb command not found in system PATH. echo Please make sure Android SDK Platform-Tools or your emulator's ADB is installed. ) else ( echo [OK] ADB found in PATH. @@ -18,6 +19,10 @@ if %ERRORLEVEL% NEQ 0 ( :: Check for compiled executable in HD\x64\Release\premium.exe if exist "HD\x64\Release\premium.exe" ( echo [OK] Compiled binary found: HD\x64\Release\premium.exe + echo Copying runtime dependencies if needed... + if not exist "HD\x64\Release\injecthacks" xcopy /Y /S /I "runtime\injecthacks" "HD\x64\Release\injecthacks" >nul + if not exist "HD\x64\Release\templates" xcopy /Y /S /I "runtime\templates" "HD\x64\Release\templates" >nul + if not exist "HD\x64\Release\tessdata" xcopy /Y /S /I "runtime\tessdata" "HD\x64\Release\tessdata" >nul echo. echo Launching Hayday-Bot-Pro GUI... start "" "HD\x64\Release\premium.exe" @@ -27,29 +32,33 @@ if exist "HD\x64\Release\premium.exe" ( :: Check for compiled executable in x64\Release\premium.exe if exist "x64\Release\premium.exe" ( echo [OK] Compiled binary found: x64\Release\premium.exe + echo Copying runtime dependencies if needed... + if not exist "x64\Release\injecthacks" xcopy /Y /S /I "runtime\injecthacks" "x64\Release\injecthacks" >nul + if not exist "x64\Release\templates" xcopy /Y /S /I "runtime\templates" "x64\Release\templates" >nul + if not exist "x64\Release\tessdata" xcopy /Y /S /I "runtime\tessdata" "x64\Release\tessdata" >nul echo. echo Launching Hayday-Bot-Pro GUI... start "" "x64\Release\premium.exe" exit /b 0 ) -echo [!] Pre-compiled binary not found at HD\x64\Release\premium.exe +echo [-] Pre-compiled binary not found at HD\x64\Release\premium.exe echo Attempting to locate Visual Studio Build Tools / MSBuild... -:: Search for vswhere -set "VSWHERE=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -if exist "%VSWHERE%" ( - for /f "usebackq tokens=*" %%i in (`"%VSWHERE%" -latest -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe`) do ( - set "MSBUILD=%%i" - ) -) +:: Search for MSBuild via vswhere +for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "& { if (Test-Path '${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe') { & '${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe' -latest -requires Microsoft.Component.MSBuild -find 'MSBuild\**\Bin\MSBuild.exe' } }"`) do set "MSBUILD=%%i" + if defined MSBUILD ( - echo [OK] MSBuild found at: "%MSBUILD%" - echo Building HD/Premium bot.sln (Release^|x64)... - "%MSBUILD%" "HD\Premium bot.sln" /p:Configuration=Release /p:Platform=x64 - if %ERRORLEVEL% EQU 0 ( - echo [OK] Build succeeded! Launching bot... + echo [OK] MSBuild found at: "!MSBUILD!" + echo Building HD/Premium bot.sln Release x64... + "!MSBUILD!" "HD\Premium bot.sln" /p:Configuration=Release /p:Platform=x64 + if !ERRORLEVEL! EQU 0 ( + echo [OK] Build succeeded! Copying runtime dependencies... + xcopy /Y /S /I "runtime\injecthacks" "HD\x64\Release\injecthacks" >nul + xcopy /Y /S /I "runtime\templates" "HD\x64\Release\templates" >nul + xcopy /Y /S /I "runtime\tessdata" "HD\x64\Release\tessdata" >nul + echo [OK] Dependencies copied. Launching bot... start "" "HD\x64\Release\premium.exe" exit /b 0 ) else ( @@ -58,8 +67,11 @@ if defined MSBUILD ( exit /b 1 ) ) else ( - echo [!] MSBuild not detected automatically. - echo Please open 'HD\Premium bot.sln' in Visual Studio 2022 and build 'Release^|x64'. + echo [-] MSBuild not detected automatically. + echo Please open HD\Premium bot.sln in Visual Studio 2022 and build Release x64. pause exit /b 1 ) + + + diff --git a/runtime/injecthacks/extra_visuals/rejected_wrong_targets/inject_extra_buildings_new_2.nxrth b/runtime/injecthacks/extra_visuals/rejected_wrong_targets/inject_extra_buildings_new_2.nxrth index 4d95650..cf37cf5 100644 Binary files a/runtime/injecthacks/extra_visuals/rejected_wrong_targets/inject_extra_buildings_new_2.nxrth and b/runtime/injecthacks/extra_visuals/rejected_wrong_targets/inject_extra_buildings_new_2.nxrth differ diff --git a/runtime/injecthacks/extra_visuals/rejected_wrong_targets/inject_extra_map_2.nxrth b/runtime/injecthacks/extra_visuals/rejected_wrong_targets/inject_extra_map_2.nxrth index 9928622..67543cc 100644 Binary files a/runtime/injecthacks/extra_visuals/rejected_wrong_targets/inject_extra_map_2.nxrth and b/runtime/injecthacks/extra_visuals/rejected_wrong_targets/inject_extra_map_2.nxrth differ diff --git a/runtime/injecthacks/inject.nxrth b/runtime/injecthacks/inject.nxrth index 317d411..efb0cf1 100644 Binary files a/runtime/injecthacks/inject.nxrth and b/runtime/injecthacks/inject.nxrth differ diff --git a/runtime/injecthacks/inject2.nxrth b/runtime/injecthacks/inject2.nxrth index 41b5243..504a147 100644 Binary files a/runtime/injecthacks/inject2.nxrth and b/runtime/injecthacks/inject2.nxrth differ diff --git a/runtime/injecthacks/inject3.nxrth b/runtime/injecthacks/inject3.nxrth index 934de79..e910036 100644 Binary files a/runtime/injecthacks/inject3.nxrth and b/runtime/injecthacks/inject3.nxrth differ diff --git a/runtime/injecthacks/inject4.nxrth b/runtime/injecthacks/inject4.nxrth index b8d140f..c0defc4 100644 Binary files a/runtime/injecthacks/inject4.nxrth and b/runtime/injecthacks/inject4.nxrth differ diff --git a/runtime/injecthacks/inject5.nxrth b/runtime/injecthacks/inject5.nxrth index a43d0b0..73067f3 100644 Binary files a/runtime/injecthacks/inject5.nxrth and b/runtime/injecthacks/inject5.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animal_accessories_0.nxrth b/runtime/injecthacks/inject_extra_animal_accessories_0.nxrth index ccc1f57..b833ada 100644 Binary files a/runtime/injecthacks/inject_extra_animal_accessories_0.nxrth and b/runtime/injecthacks/inject_extra_animal_accessories_0.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animal_accessories_1.nxrth b/runtime/injecthacks/inject_extra_animal_accessories_1.nxrth index 1086731..ce7004c 100644 Binary files a/runtime/injecthacks/inject_extra_animal_accessories_1.nxrth and b/runtime/injecthacks/inject_extra_animal_accessories_1.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals02_0.nxrth b/runtime/injecthacks/inject_extra_animals02_0.nxrth index 38f6adf..079cd50 100644 Binary files a/runtime/injecthacks/inject_extra_animals02_0.nxrth and b/runtime/injecthacks/inject_extra_animals02_0.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals02_1.nxrth b/runtime/injecthacks/inject_extra_animals02_1.nxrth index e7ae637..4e6c2d2 100644 Binary files a/runtime/injecthacks/inject_extra_animals02_1.nxrth and b/runtime/injecthacks/inject_extra_animals02_1.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals02_2.nxrth b/runtime/injecthacks/inject_extra_animals02_2.nxrth index 8f00985..752db07 100644 Binary files a/runtime/injecthacks/inject_extra_animals02_2.nxrth and b/runtime/injecthacks/inject_extra_animals02_2.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals02_3.nxrth b/runtime/injecthacks/inject_extra_animals02_3.nxrth index 4e4e297..d3a4f15 100644 Binary files a/runtime/injecthacks/inject_extra_animals02_3.nxrth and b/runtime/injecthacks/inject_extra_animals02_3.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals02_4.nxrth b/runtime/injecthacks/inject_extra_animals02_4.nxrth index 71d340d..ed12662 100644 Binary files a/runtime/injecthacks/inject_extra_animals02_4.nxrth and b/runtime/injecthacks/inject_extra_animals02_4.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals03_0.nxrth b/runtime/injecthacks/inject_extra_animals03_0.nxrth index e007743..259aeec 100644 Binary files a/runtime/injecthacks/inject_extra_animals03_0.nxrth and b/runtime/injecthacks/inject_extra_animals03_0.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals03_1.nxrth b/runtime/injecthacks/inject_extra_animals03_1.nxrth index 223f222..a878d11 100644 Binary files a/runtime/injecthacks/inject_extra_animals03_1.nxrth and b/runtime/injecthacks/inject_extra_animals03_1.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals03_2.nxrth b/runtime/injecthacks/inject_extra_animals03_2.nxrth index 030eee8..6951d09 100644 Binary files a/runtime/injecthacks/inject_extra_animals03_2.nxrth and b/runtime/injecthacks/inject_extra_animals03_2.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals03_3.nxrth b/runtime/injecthacks/inject_extra_animals03_3.nxrth index 9ab5251..6e29eb2 100644 Binary files a/runtime/injecthacks/inject_extra_animals03_3.nxrth and b/runtime/injecthacks/inject_extra_animals03_3.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals03_4.nxrth b/runtime/injecthacks/inject_extra_animals03_4.nxrth index 39067ae..dc5bc75 100644 Binary files a/runtime/injecthacks/inject_extra_animals03_4.nxrth and b/runtime/injecthacks/inject_extra_animals03_4.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals03_5.nxrth b/runtime/injecthacks/inject_extra_animals03_5.nxrth index 2283cfd..85dc8b6 100644 Binary files a/runtime/injecthacks/inject_extra_animals03_5.nxrth and b/runtime/injecthacks/inject_extra_animals03_5.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals03_6.nxrth b/runtime/injecthacks/inject_extra_animals03_6.nxrth index 4729620..dc390ba 100644 Binary files a/runtime/injecthacks/inject_extra_animals03_6.nxrth and b/runtime/injecthacks/inject_extra_animals03_6.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals04_0.nxrth b/runtime/injecthacks/inject_extra_animals04_0.nxrth index 35c119c..89370c6 100644 Binary files a/runtime/injecthacks/inject_extra_animals04_0.nxrth and b/runtime/injecthacks/inject_extra_animals04_0.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals04_1.nxrth b/runtime/injecthacks/inject_extra_animals04_1.nxrth index 7dc2419..94eab3f 100644 Binary files a/runtime/injecthacks/inject_extra_animals04_1.nxrth and b/runtime/injecthacks/inject_extra_animals04_1.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals04_2.nxrth b/runtime/injecthacks/inject_extra_animals04_2.nxrth index ba82ae6..9e823c7 100644 Binary files a/runtime/injecthacks/inject_extra_animals04_2.nxrth and b/runtime/injecthacks/inject_extra_animals04_2.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals04_3.nxrth b/runtime/injecthacks/inject_extra_animals04_3.nxrth index 90beb66..a6eb5ae 100644 Binary files a/runtime/injecthacks/inject_extra_animals04_3.nxrth and b/runtime/injecthacks/inject_extra_animals04_3.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals04_4.nxrth b/runtime/injecthacks/inject_extra_animals04_4.nxrth index 5d44e79..33bddf6 100644 Binary files a/runtime/injecthacks/inject_extra_animals04_4.nxrth and b/runtime/injecthacks/inject_extra_animals04_4.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals04_5.nxrth b/runtime/injecthacks/inject_extra_animals04_5.nxrth index 9c2a6a6..64ca61d 100644 Binary files a/runtime/injecthacks/inject_extra_animals04_5.nxrth and b/runtime/injecthacks/inject_extra_animals04_5.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals04_6.nxrth b/runtime/injecthacks/inject_extra_animals04_6.nxrth index 39d01b8..894552c 100644 Binary files a/runtime/injecthacks/inject_extra_animals04_6.nxrth and b/runtime/injecthacks/inject_extra_animals04_6.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals04_7.nxrth b/runtime/injecthacks/inject_extra_animals04_7.nxrth index 29f56ad..1d32779 100644 Binary files a/runtime/injecthacks/inject_extra_animals04_7.nxrth and b/runtime/injecthacks/inject_extra_animals04_7.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals04_8.nxrth b/runtime/injecthacks/inject_extra_animals04_8.nxrth index bf79bf7..630480b 100644 Binary files a/runtime/injecthacks/inject_extra_animals04_8.nxrth and b/runtime/injecthacks/inject_extra_animals04_8.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals04_9.nxrth b/runtime/injecthacks/inject_extra_animals04_9.nxrth index 9d98256..1175a02 100644 Binary files a/runtime/injecthacks/inject_extra_animals04_9.nxrth and b/runtime/injecthacks/inject_extra_animals04_9.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals05_0.nxrth b/runtime/injecthacks/inject_extra_animals05_0.nxrth index df125ad..4c4c27d 100644 Binary files a/runtime/injecthacks/inject_extra_animals05_0.nxrth and b/runtime/injecthacks/inject_extra_animals05_0.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals05_1.nxrth b/runtime/injecthacks/inject_extra_animals05_1.nxrth index bcfb8f0..6aabf55 100644 Binary files a/runtime/injecthacks/inject_extra_animals05_1.nxrth and b/runtime/injecthacks/inject_extra_animals05_1.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals05_2.nxrth b/runtime/injecthacks/inject_extra_animals05_2.nxrth index e3befe3..184cc5c 100644 Binary files a/runtime/injecthacks/inject_extra_animals05_2.nxrth and b/runtime/injecthacks/inject_extra_animals05_2.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals05_3.nxrth b/runtime/injecthacks/inject_extra_animals05_3.nxrth index d114794..244707f 100644 Binary files a/runtime/injecthacks/inject_extra_animals05_3.nxrth and b/runtime/injecthacks/inject_extra_animals05_3.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals05_4.nxrth b/runtime/injecthacks/inject_extra_animals05_4.nxrth index b6c1340..4f7bc3e 100644 Binary files a/runtime/injecthacks/inject_extra_animals05_4.nxrth and b/runtime/injecthacks/inject_extra_animals05_4.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals05_5.nxrth b/runtime/injecthacks/inject_extra_animals05_5.nxrth index 093cf6b..8ce1339 100644 Binary files a/runtime/injecthacks/inject_extra_animals05_5.nxrth and b/runtime/injecthacks/inject_extra_animals05_5.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals05_6.nxrth b/runtime/injecthacks/inject_extra_animals05_6.nxrth index bbf41b1..d8301a3 100644 Binary files a/runtime/injecthacks/inject_extra_animals05_6.nxrth and b/runtime/injecthacks/inject_extra_animals05_6.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals05_7.nxrth b/runtime/injecthacks/inject_extra_animals05_7.nxrth index f5d5efd..06ac3e1 100644 Binary files a/runtime/injecthacks/inject_extra_animals05_7.nxrth and b/runtime/injecthacks/inject_extra_animals05_7.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals05_8.nxrth b/runtime/injecthacks/inject_extra_animals05_8.nxrth index 5608706..8945032 100644 Binary files a/runtime/injecthacks/inject_extra_animals05_8.nxrth and b/runtime/injecthacks/inject_extra_animals05_8.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals05_9.nxrth b/runtime/injecthacks/inject_extra_animals05_9.nxrth index 00698e9..f19738b 100644 Binary files a/runtime/injecthacks/inject_extra_animals05_9.nxrth and b/runtime/injecthacks/inject_extra_animals05_9.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals_0.nxrth b/runtime/injecthacks/inject_extra_animals_0.nxrth index dbdf165..844ec61 100644 Binary files a/runtime/injecthacks/inject_extra_animals_0.nxrth and b/runtime/injecthacks/inject_extra_animals_0.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals_1.nxrth b/runtime/injecthacks/inject_extra_animals_1.nxrth index df15ab9..6018f9e 100644 Binary files a/runtime/injecthacks/inject_extra_animals_1.nxrth and b/runtime/injecthacks/inject_extra_animals_1.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals_10.nxrth b/runtime/injecthacks/inject_extra_animals_10.nxrth index 15c16f5..ef78de0 100644 Binary files a/runtime/injecthacks/inject_extra_animals_10.nxrth and b/runtime/injecthacks/inject_extra_animals_10.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals_11.nxrth b/runtime/injecthacks/inject_extra_animals_11.nxrth index 927cce8..07db609 100644 Binary files a/runtime/injecthacks/inject_extra_animals_11.nxrth and b/runtime/injecthacks/inject_extra_animals_11.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals_12.nxrth b/runtime/injecthacks/inject_extra_animals_12.nxrth index 34b10b2..c961f93 100644 Binary files a/runtime/injecthacks/inject_extra_animals_12.nxrth and b/runtime/injecthacks/inject_extra_animals_12.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals_13.nxrth b/runtime/injecthacks/inject_extra_animals_13.nxrth index 3145440..aa534a7 100644 Binary files a/runtime/injecthacks/inject_extra_animals_13.nxrth and b/runtime/injecthacks/inject_extra_animals_13.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals_14.nxrth b/runtime/injecthacks/inject_extra_animals_14.nxrth index 251b6ba..cbe24f7 100644 Binary files a/runtime/injecthacks/inject_extra_animals_14.nxrth and b/runtime/injecthacks/inject_extra_animals_14.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals_15.nxrth b/runtime/injecthacks/inject_extra_animals_15.nxrth index 5a92b6a..101b4cb 100644 Binary files a/runtime/injecthacks/inject_extra_animals_15.nxrth and b/runtime/injecthacks/inject_extra_animals_15.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals_16.nxrth b/runtime/injecthacks/inject_extra_animals_16.nxrth index 5df7141..1ea5b22 100644 Binary files a/runtime/injecthacks/inject_extra_animals_16.nxrth and b/runtime/injecthacks/inject_extra_animals_16.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals_2.nxrth b/runtime/injecthacks/inject_extra_animals_2.nxrth index ed8c9f8..814e549 100644 Binary files a/runtime/injecthacks/inject_extra_animals_2.nxrth and b/runtime/injecthacks/inject_extra_animals_2.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals_3.nxrth b/runtime/injecthacks/inject_extra_animals_3.nxrth index 64489fb..12c7fe6 100644 Binary files a/runtime/injecthacks/inject_extra_animals_3.nxrth and b/runtime/injecthacks/inject_extra_animals_3.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals_4.nxrth b/runtime/injecthacks/inject_extra_animals_4.nxrth index 5ad9cc7..d9fee9b 100644 Binary files a/runtime/injecthacks/inject_extra_animals_4.nxrth and b/runtime/injecthacks/inject_extra_animals_4.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals_5.nxrth b/runtime/injecthacks/inject_extra_animals_5.nxrth index e6193ea..7859b9f 100644 Binary files a/runtime/injecthacks/inject_extra_animals_5.nxrth and b/runtime/injecthacks/inject_extra_animals_5.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals_6.nxrth b/runtime/injecthacks/inject_extra_animals_6.nxrth index ead142f..14acf47 100644 Binary files a/runtime/injecthacks/inject_extra_animals_6.nxrth and b/runtime/injecthacks/inject_extra_animals_6.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals_7.nxrth b/runtime/injecthacks/inject_extra_animals_7.nxrth index 431ccb8..8e0e609 100644 Binary files a/runtime/injecthacks/inject_extra_animals_7.nxrth and b/runtime/injecthacks/inject_extra_animals_7.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals_8.nxrth b/runtime/injecthacks/inject_extra_animals_8.nxrth index 246fb9e..c77cec9 100644 Binary files a/runtime/injecthacks/inject_extra_animals_8.nxrth and b/runtime/injecthacks/inject_extra_animals_8.nxrth differ diff --git a/runtime/injecthacks/inject_extra_animals_9.nxrth b/runtime/injecthacks/inject_extra_animals_9.nxrth index 00d0987..534f2ec 100644 Binary files a/runtime/injecthacks/inject_extra_animals_9.nxrth and b/runtime/injecthacks/inject_extra_animals_9.nxrth differ diff --git a/runtime/injecthacks/inject_extra_buildings_new_0.nxrth b/runtime/injecthacks/inject_extra_buildings_new_0.nxrth index 94e4646..4ca96fe 100644 Binary files a/runtime/injecthacks/inject_extra_buildings_new_0.nxrth and b/runtime/injecthacks/inject_extra_buildings_new_0.nxrth differ diff --git a/runtime/injecthacks/inject_extra_buildings_new_2.nxrth b/runtime/injecthacks/inject_extra_buildings_new_2.nxrth index 36830a2..28611e0 100644 Binary files a/runtime/injecthacks/inject_extra_buildings_new_2.nxrth and b/runtime/injecthacks/inject_extra_buildings_new_2.nxrth differ diff --git a/runtime/injecthacks/inject_extra_buildings_new_6.nxrth b/runtime/injecthacks/inject_extra_buildings_new_6.nxrth index 5bd1e0c..f2e6ff7 100644 Binary files a/runtime/injecthacks/inject_extra_buildings_new_6.nxrth and b/runtime/injecthacks/inject_extra_buildings_new_6.nxrth differ diff --git a/runtime/injecthacks/inject_extra_trees.nxrth b/runtime/injecthacks/inject_extra_trees.nxrth index 00533c5..9b42302 100644 Binary files a/runtime/injecthacks/inject_extra_trees.nxrth and b/runtime/injecthacks/inject_extra_trees.nxrth differ diff --git a/runtime/injecthacks/inject_extra_trees_0.nxrth b/runtime/injecthacks/inject_extra_trees_0.nxrth index 7a37059..1b6eb8f 100644 Binary files a/runtime/injecthacks/inject_extra_trees_0.nxrth and b/runtime/injecthacks/inject_extra_trees_0.nxrth differ diff --git a/runtime/injecthacks/inject_extra_trees_1.nxrth b/runtime/injecthacks/inject_extra_trees_1.nxrth index 9694364..f89f017 100644 Binary files a/runtime/injecthacks/inject_extra_trees_1.nxrth and b/runtime/injecthacks/inject_extra_trees_1.nxrth differ