[
  {
    "path": ".clangd",
    "content": "CompileFlags:\n    Add: [\"-std:latest\", \"/clang:-std=c++23\"]"
  },
  {
    "path": ".github/workflows/xmake.yml",
    "content": "name: Build\npermissions: write-all\n\non:\n  push:\n    branches: [ \"chromatic\" ]\n  pull_request:\n    branches: [ \"chromatic\" ]\n  release:\n    types: [created]\n\njobs:\n  build:\n    runs-on: windows-2025\n\n    steps:\n    - uses: actions/checkout@v4\n      with:\n        submodules: recursive\n\n    - uses: xmake-io/github-action-setup-xmake@v1\n      with:\n        xmake-version: latest\n        actions-cache-folder: '.xmake-cache'\n        actions-cache-key: 'ci'\n        package-cache: true\n        package-cache-key: windows-2025\n        # build-cache: true\n        # build-cache-key: ${{ matrix.os }}-${{ matrix.build_type }}\n\n    - name: Xmake configure\n      run: |\n        xmake config -v --yes --toolchain=clang-cl --mode=releasedbg\n\n    - name: build-releasedbg\n      run: |\n        xmake b --yes --verbose\n\n    - name: Upload Artifacts\n      uses: actions/upload-artifact@v4.6.0\n      with:\n        path: ./build/windows/x64/\n        name: windows-build\n\n    - name: Create Archive\n      if: github.event_name == 'release'\n      run: |\n        Compress-Archive -Path ./build/windows/* -DestinationPath windows-build-pdb.zip\n        Remove-Item -Path ./build/windows/x64/releasedbg/*.pdb -Force\n        Remove-Item -Path ./build/windows/x64/releasedbg/*.lib -Force\n        Compress-Archive -Path ./build/windows/* -DestinationPath windows-build.zip\n\n    - name: Upload Release Assets\n      if: github.event_name == 'release'\n      uses: softprops/action-gh-release@v1\n      with:\n        files: |\n          windows-build.zip\n          windows-build-pdb.zip\n        token: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "build\n.xmake\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"deps/blook\"]\n\tpath = deps/blook\n\turl = https://github.com/std-microblock/blook\n[submodule \"deps/cpp-ipc\"]\n\tpath = deps/cpp-ipc\n\turl = https://github.com/std-microblock/cpp-ipc\n"
  },
  {
    "path": "README.md",
    "content": "<div align=center>\n<img src=https://github.com/user-attachments/assets/4e0d0de3-c117-44e0-aed5-f95eaac33ba7 width=200/>\n<h1>chromatic</h1>\n<h5>Universal modifier for Chromium/V8 | 广谱注入 Chromium/V8 的通用修改器</h5>  \n</div>\n\n> [!NOTE]\n> 在找 BetterNCM ？\n> \n> 由于作者迁移至 QQ 音乐，BetterNCM 疏于维护，以及其年代已久，现将相关代码整体重写并支持极多其它软件，改名为 chromatic。\n>\n> 点击以查看 [BetterNCM 相关代码存档](https://github.com/std-microblock/chromatic/tree/v2) 或 [最后一版 BetterNCM Release](https://github.com/std-microblock/chromatic/releases/tag/1.3.4)\n\n> [!WARNING]\n> This project is still in active development. File a bug report if you meet\n> any!\\\n> 此项目仍在开发阶段，如果遇到问题请发送 Issue\n>\n> Both English and Chinese issues are accepted.\n> Issue 中使用中文或英文均可\n\n## 文档/使用方法\n\n待完善\n\n## Showcase\n\n```javascript\nimport { chrome } from \"chromatic\"\n\nchrome.blink.add_blink_parse_html_manipulator(html => {\n    if (html.includes('<body')) {\n        return html.replace(\"<body\", `\n           <div style=\"\n          position: fixed;\n          left: 13px;\n          top: 11px;\n          background: #00000022;\n          color: white;\n          z-index: 9999;\n          backdrop-filter: blur(20px);\n          padding: 10px 20px;\n          font-size: 15px;\n          border-radius: 100px;\n          overflow: hidden;\n          border: 1px solid #00000038;\n          font-family: Consolas;\n          cursor: pointer;\n      \" onclick=\"location.reload()\">Chromatic</div>\n      <body\n            `)\n    }\n})\n```\n\n![image](https://github.com/user-attachments/assets/6d72958e-d673-4c80-bcd3-e7da743479e3)\n"
  },
  {
    "path": "deps/blook.lua",
    "content": "package(\"blook\")\n    add_deps(\"cmake\")\n    add_syslinks(\"advapi32\")\n    set_sourcedir(path.join(os.scriptdir(), \"blook\"))\n    on_install(function (package)\n        local fcdir = package:cachedir() .. \"/fetchcontent\"\n        import(\"package.tools.cmake\").install(package, {\n                \"-DCMAKE_INSTALL_PREFIX=\" .. package:installdir(),\n                \"-DCMAKE_PREFIX_PATH=\" .. package:installdir(),\n                \"-DFETCHCONTENT_QUIET=OFF\",\n                \"-DFETCHCONTENT_BASE_DIR=\" .. fcdir,\n        })\n        \n        os.cp(\"include/blook/**\", package:installdir(\"include/blook/\"))\n        os.cp(\"external/zasm/zasm/include/**\", package:installdir(\"include/zasm/\"))\n        os.cp(fcdir .. \"/zydis-src/dependencies/zycore/include/**\", package:installdir(\"include/zycore/\"))\n        os.cp(package:buildir() .. \"/blook.lib\", package:installdir(\"lib\"))\n        os.cp(package:buildir() .. \"/external/zasm/zasm.lib\", package:installdir(\"lib\"))\n    end)\npackage_end()"
  },
  {
    "path": "deps/breeze-js.lua",
    "content": "package(\"breeze-js\")\n    set_description(\"A lightweight and modern JavaScript runtime built on QuickJS for desktop applications.\")\n    set_license(\"AGPL-3.0\")\n\n    add_urls(\"https://github.com/breeze-shell/breeze-js.git\")\n\n    add_versions(\"20250621.3\", \"4a1556efe18e7afb4a13c3b25abe54080577cae7\")\n    add_deps(\"yalantinglibs 0c98464dd202aaa6275a8da3297719a436b8a51a\", {\n        configs = {\n            ssl = true\n        }\n    })\n\n    add_configs(\"shared\", {description = \"Build shared library.\", default = false, type = \"boolean\", readonly = true})\n\n    on_install(\"windows|!arm\", \"linux\", \"macosx\", function (package)\n        io.replace(\"xmake.lua\", [[set_languages(\"c89\", \"c++20\")]], [[set_languages(\"c11\", \"c++20\")]], {plain = true})\n        io.replace(\"xmake.lua\", [[set_kind(\"binary\")]], [[set_kind(\"binary\")\nset_enabled(false)]], {plain = true})\n        io.replace(\"xmake.lua\", [[set_kind(\"static\")]], [[set_kind(\"static\")\nadd_defines(\"NDEBUG\")\nif is_plat(\"macosx\") then\n    add_cxxflags(\"-fexperimental-library\")\nend]], {plain = true})\n        import(\"package.tools.xmake\").install(package)\n    end)\n\n    on_test(function (package)\n        assert(package:check_cxxsnippets({test = [[\n            #include <breeze-js/quickjs.h>\n            #include <breeze-js/script.h>\n            void test() {\n                auto ctx = std::make_shared<breeze::script_context>();\n                ctx->reset_runtime();\n            }\n        ]]}, {configs = {languages = \"c++23\"}}))\n    end)"
  },
  {
    "path": "ipc/ipc.cc",
    "content": "#include \"ipc.h\"\n#include <chrono>\n#include <print>\n#include <stdexcept>\n#include <string>\n\n#include \"shared_memory_ipc.h\"\n#include \"ylt/easylog.hpp\"\n\nnamespace chromatic {\nvoid breeze_ipc::connect(std::string_view name) {\n  channel.connect(name.data());\n  ipc_thread = std::thread([this]() {\n    ELOGFMT(INFO, \"IPC thread started, listening for packets...\");\n    while (!exit_signal) {\n      poll();\n    }\n  });\n}\n\nbreeze_ipc::~breeze_ipc() {\n  exit_signal = true;\n  if (ipc_thread.joinable()) {\n    ipc_thread.join();\n  }\n}\n\nbool breeze_ipc::poll() {\n  std::string data;\n  channel.try_receive(data);\n  if (!data.empty()) {\n    auto pkt = deserialize<breeze_ipc::packet>(data);\n    if (pkt.has_value()) {\n      // 检查是否是发给自己的包\n      if (pkt->to_pid != 0 && pkt->to_pid != current_pid_) {\n        return true; // 不是给自己的包，跳过\n      }\n\n      if (pkt->is_fragment) {\n        process_fragment(*pkt);\n      } else {\n        process_packet(std::move(*pkt));\n      }\n    }\n    return true;\n  }\n  return false;\n}\n\nvoid breeze_ipc::process_packet(packet &&pkt) {\n  if (pkt.return_for_call != 0) {\n    auto it = call_handlers.find(pkt.return_for_call);\n    if (it != call_handlers.end()) {\n      it->second(pkt);\n      call_handlers.erase(it->first);\n    }\n  } else {\n    auto &handler_list = handlers[pkt.name];\n    for (auto &handler : handler_list) {\n      handler(pkt);\n    }\n  }\n}\n\nvoid breeze_ipc::process_fragment(const packet &frag) {\n  std::lock_guard lock(fragment_mutex_);\n\n  auto &cache = fragment_cache_[frag.fragment_id];\n  if (cache.fragments.empty()) {\n    cache.created_time = std::chrono::steady_clock::now();\n    cache.base_packet = frag;\n    cache.base_packet.is_fragment = false;\n    cache.base_packet.data.clear();\n    cache.fragments.resize(frag.fragment_total);\n  }\n\n  if (frag.fragment_index < frag.fragment_total) {\n    cache.fragments[frag.fragment_index] = frag.data;\n  }\n\n  bool complete = true;\n  for (const auto &fragment : cache.fragments) {\n    if (fragment.empty()) {\n      complete = false;\n      break;\n    }\n  }\n\n  if (complete) {\n    reassemble_and_process(frag.fragment_id);\n  }\n}\n\nvoid breeze_ipc::reassemble_and_process(size_t fragment_id) {\n  auto it = fragment_cache_.find(fragment_id);\n  if (it == fragment_cache_.end())\n    return;\n\n  auto &cache = it->second;\n  for (const auto &frag : cache.fragments) {\n    cache.base_packet.data += frag;\n  }\n\n  process_packet(\n      std::move(deserialize<packet>(cache.base_packet.data).value()));\n\n  fragment_cache_.erase(it);\n}\n} // namespace chromatic"
  },
  {
    "path": "ipc/ipc.h",
    "content": "#pragma once\n#include \"./shared_memory_ipc.h\"\n#include <atomic>\n#include <condition_variable>\n#include <deque>\n#include <expected>\n#include <functional>\n#include <future>\n#include <iostream>\n#include <mutex>\n#include <print>\n#include <sstream>\n#include <string>\n#include <unordered_map>\n\n#include \"rfl.hpp\"\n#include \"rfl/json.hpp\"\n#include \"ylt/easylog.hpp\"\n#include \"ylt/struct_pack.hpp\"\n\n#define WIN32_LEAN_AND_MEAN\n#define NOMINMAX\n#include \"Windows.h\"\n\nnamespace chromatic {\nconstexpr static bool use_struct_pack = false;\nconstexpr static bool print_packages = false;\nconstexpr static size_t MAX_PACKET_SIZE = 1024;\n\nauto serialize = [](const auto &data) {\n  if constexpr (use_struct_pack) {\n    return struct_pack::serialize<std::string>(data);\n  } else {\n    return rfl::json::write(data);\n  }\n};\n\ntemplate <typename T> auto deserialize(const std::string &data) {\n  if constexpr (use_struct_pack) {\n    return struct_pack::deserialize<T>(data);\n  } else {\n    return rfl::json::read<T, rfl::NoExtraFields, rfl::DefaultIfMissing>(data);\n  }\n}\n\ntemplate <typename T>\nconcept StructPackSerializable = requires(T t) {\n  { serialize(t) } -> std::same_as<std::string>;\n  deserialize<T>(std::declval<std::string>());\n};\n\nstruct test_serializable_struct {\n  int a;\n  float b;\n  std::vector<char> c;\n};\n\nstatic_assert(StructPackSerializable<test_serializable_struct>,\n              \"test_struct should be StructPackSerializable\");\n\nstruct breeze_ipc {\n  struct packet {\n    size_t seq;\n    size_t return_for_call = 0;\n    std::string name;\n    std::string data;\n    DWORD from_pid = 0;        // 发送方进程ID\n    DWORD to_pid = 0;          // 接收方进程ID\n    bool is_fragment = false;  // 是否是分包\n    size_t fragment_id = 0;    // 分包ID\n    size_t fragment_index = 0; // 分包索引\n    size_t fragment_total = 0; // 总分包数\n  };\n\n  // 分包重组缓存\n  struct fragment_cache {\n    std::vector<std::string> fragments;\n    std::chrono::steady_clock::time_point created_time;\n    packet base_packet;\n  };\n\n  void connect(std::string_view name);\n\n  inline size_t inc_seq() { return seq++; }\n  inline size_t next_fragment_id() { return next_fragment_id_++; }\n\n  // 发送包（可选择目标PID）\n  void send(packet &&pkt, DWORD target_pid = 0) {\n    pkt.to_pid = target_pid;\n\n    pkt.from_pid = current_pid_;\n\n    auto serialized = serialize(pkt);\n\n    // 分包处理\n    if (serialized.size() > MAX_PACKET_SIZE) {\n      send_fragmented(pkt, serialized);\n      return;\n    }\n\n    send_impl(serialized);\n  }\n\n  // 实现分包发送\n  void send_fragmented(const packet &base_pkt, const std::string &full_data) {\n    const size_t fragment_id = next_fragment_id();\n    const size_t total_fragments =\n        (full_data.size() + MAX_PACKET_SIZE - 1) / MAX_PACKET_SIZE;\n\n    for (size_t i = 0; i < total_fragments; ++i) {\n      const size_t start = i * MAX_PACKET_SIZE;\n      const size_t end = std::min(start + MAX_PACKET_SIZE, full_data.size());\n\n      packet fragment = base_pkt;\n      fragment.is_fragment = true;\n      fragment.fragment_id = fragment_id;\n      fragment.fragment_index = i;\n      fragment.fragment_total = total_fragments;\n      fragment.data = full_data.substr(start, end - start);\n\n      auto serialized_fragment = serialize(fragment);\n      send_impl(serialized_fragment);\n    }\n  }\n\n  // 实际发送实现\n  void send_impl(const std::string &data) {\n\n    if (!data.empty()) {\n      if constexpr (print_packages) {\n        if constexpr (use_struct_pack) {\n          for (size_t i = 0; i < data.size(); ++i) {\n            if (i % 16 == 0 && i != 0) {\n              printf(\"\\n\");\n            }\n            char buf[3];\n            snprintf(buf, sizeof(buf), \"%02x \",\n                     static_cast<unsigned char>(data[i]));\n            WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), buf, 2, nullptr,\n                          nullptr);\n          }\n        } else {\n          if (GetConsoleWindow())\n            WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), data.data(),\n                          static_cast<DWORD>(data.size()), nullptr, nullptr);\n        }\n      }\n      channel.send(data);\n    }\n  }\n\n  bool poll();\n\n  // 发送给特定PID\n  template <StructPackSerializable T>\n  void send(const std::string &name, const T &data, DWORD target_pid = 0) {\n    send(\n        packet{\n            .seq = inc_seq(),\n            .return_for_call = 0,\n            .name = name,\n            .data = serialize(data),\n        },\n        target_pid);\n  }\n\n  template <StructPackSerializable T>\n  void broadcast(const std::string &name, const T &data) {\n    send(packet{\n        .seq = inc_seq(),\n        .return_for_call = 0,\n        .name = name,\n        .data = serialize(data),\n    });\n  }\n\n  struct listener_remover {\n    breeze_ipc &ipc_instance;\n    std::string name;\n    std::function<void(const packet &)> &handler;\n    void remove() {\n      auto &handlers = ipc_instance.handlers;\n      auto it = handlers.find(name);\n      if (it != handlers.end()) {\n        auto &handler_list = it->second;\n\n        handler_list.remove_if(\n            [&](const std::function<void(const packet &)> &h) {\n              return &h == &handler;\n            });\n      }\n    }\n  };\n\n  listener_remover add_listener(const std::string &name,\n                                std::function<void(const packet &)> &&handler) {\n    auto &h = handlers[name];\n    h.emplace_back(std::move(handler));\n\n    return listener_remover{*this, name, h.back()};\n  }\n\n  template <StructPackSerializable T>\n  listener_remover add_listener(const std::string &name,\n                                std::function<void(const T &)> &&handler) {\n    return add_listener(\n        name, [handler = std::move(handler)](const packet &pkt) {\n          auto data = deserialize<T>(pkt.data);\n          if (data.has_value()) {\n            handler(data.value());\n          } else {\n            throw std::runtime_error(\"Failed to deserialize packet data\");\n          }\n        });\n  }\n\n  template <StructPackSerializable RetVal, StructPackSerializable Arg>\n  listener_remover\n  add_call_handler(const std::string &name,\n                   std::function<RetVal(const Arg &)> &&handler) {\n    return add_listener(\"call_\" + name, [this, handler = std::move(handler),\n                                         name](const packet &pkt) {\n      auto data = deserialize<Arg>(pkt.data);\n      if (!data.has_value()) {\n        throw std::runtime_error(\"Failed to deserialize call data for \" + name);\n      }\n      auto result = handler(data.value());\n      send(packet{.seq = inc_seq(),\n                  .return_for_call = pkt.seq,\n                  .name = \"call_result_\" + name,\n                  .data = serialize(result),\n                  .to_pid = pkt.from_pid});\n    });\n  }\n\n  template <StructPackSerializable RetVal>\n  listener_remover add_call_handler(const std::string &name,\n                                    std::function<RetVal()> &&handler) {\n    return add_call_handler<RetVal, bool>(\n        name, [handler = std::move(handler)](bool) { return handler(); });\n  }\n\n  template <StructPackSerializable RetVal, StructPackSerializable R>\n  std::future<RetVal> call(const std::string &name, const R &data,\n                           DWORD target_pid = 0) {\n    auto seq = inc_seq();\n    auto promise = std::make_shared<std::promise<RetVal>>();\n    call_handlers[seq] = [promise](const packet &pkt) {\n      auto result = deserialize<RetVal>(pkt.data);\n      if (result.has_value()) {\n        promise->set_value(std::move(result.value()));\n      } else {\n        promise->set_exception(\n            std::make_exception_ptr(std::runtime_error(\"Call failed\")));\n      }\n    };\n\n    send(packet{\n        .seq = seq,\n        .return_for_call = 0,\n        .name = \"call_\" + name,\n        .data = serialize(data),\n        .to_pid = target_pid // 定向发送\n    });\n\n    return promise->get_future();\n  }\n\n  template <StructPackSerializable RetVal>\n  std::future<RetVal> call(const std::string &name, DWORD target_pid = 0) {\n    return call<RetVal, bool>(name, true, target_pid);\n  }\n\n  template <StructPackSerializable RetVal, StructPackSerializable R>\n  std::optional<RetVal>\n  call_and_poll(const std::string &name, const R &data, DWORD target_pid = 0,\n                std::chrono::milliseconds timeout = std::chrono::seconds(5)) {\n    auto future = call<RetVal, R>(name, data, target_pid);\n    auto start = std::chrono::steady_clock::now();\n\n    while (std::chrono::steady_clock::now() - start < timeout) {\n      if (future.valid() && future.wait_for(std::chrono::milliseconds(0)) ==\n                                std::future_status::ready) {\n        return future.get();\n      }\n      poll();\n    }\n    return std::nullopt;\n  }\n\n  template <StructPackSerializable RetVal>\n  std::optional<RetVal>\n  call_and_poll(const std::string &name, DWORD target_pid = 0,\n                std::chrono::milliseconds timeout = std::chrono::seconds(5)) {\n    return call_and_poll<RetVal, bool>(name, true, target_pid, timeout);\n  }\n\n  ~breeze_ipc();\n\nprivate:\n  void process_packet(packet &&pkt);\n  void process_fragment(const packet &frag);\n  void reassemble_and_process(size_t fragment_id);\n\n  std::unordered_map<std::string,\n                     std::list<std::function<void(const packet &)>>>\n      handlers;\n  std::unordered_map<size_t, std::function<void(const packet &)>> call_handlers;\n  ::ipc::Channel<> channel;\n  std::atomic_size_t seq =\n      1 + std::chrono::system_clock::now().time_since_epoch().count() / 1000 %\n              1000000;\n  std::atomic_size_t next_fragment_id_ = 1;\n  std::atomic_bool exit_signal = false;\n  std::thread ipc_thread;\n\n  DWORD current_pid_ = ::GetCurrentProcessId();\n\n  // 分包重组相关\n  std::mutex fragment_mutex_;\n  std::unordered_map<size_t, fragment_cache> fragment_cache_;\n};\n} // namespace chromatic"
  },
  {
    "path": "ipc/shared_memory_ipc.h",
    "content": "#pragma once\n\n#include <print>\n#if defined(_MSC_VER)\n#pragma warning(push)\n#pragma warning(disable : 4200)\n#endif\n\n#ifndef NOMINMAX\n#define NOMINMAX\n#endif\n\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif\n\n#include <aclapi.h>\n#include <memoryapi.h>\n#include <processthreadsapi.h>\n#include <sddl.h>\n#include <windows.h>\n#include <winnt.h>\n\n#include <atomic>\n#include <chrono>\n#include <memory>\n#include <stdexcept>\n#include <string>\n#include <string_view>\n#include <system_error>\n#include <thread>\n#include <vector>\n\nnamespace ipc {\n\nclass Exception : public std::runtime_error {\npublic:\n  explicit Exception(const std::string &message)\n      : std::runtime_error(message) {}\n  explicit Exception(const char *message) : std::runtime_error(message) {}\n};\n\nnamespace detail {\n\ninline void throw_windows_error(const std::string &message) {\n  DWORD error_code = ::GetLastError();\n  throw ipc::Exception(message +\n                       \" (Windows Error: \" + std::to_string(error_code) + \")\");\n}\n\nclass SidHolder {\npublic:\n  SidHolder() : sid_buffer_() {}\n  ~SidHolder() = default;\n  bool CreateEveryone() {\n    DWORD sid_size = 0;\n    CreateWellKnownSid(WinWorldSid, nullptr, nullptr, &sid_size);\n    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)\n      return false;\n    sid_buffer_.resize(sid_size);\n    return !!CreateWellKnownSid(WinWorldSid, nullptr, Get(), &sid_size);\n  }\n  bool CreateUntrusted() {\n    SID_IDENTIFIER_AUTHORITY mandatory_label_authority =\n        SECURITY_MANDATORY_LABEL_AUTHORITY;\n    DWORD sid_size = GetSidLengthRequired(1);\n    sid_buffer_.resize(sid_size);\n    if (!InitializeSid(Get(), &mandatory_label_authority, 1))\n      return false;\n    *(GetSidSubAuthority(Get(), 0)) = SECURITY_MANDATORY_UNTRUSTED_RID;\n    return true;\n  }\n  PSID Get() {\n    return sid_buffer_.empty() ? nullptr\n                               : reinterpret_cast<PSID>(sid_buffer_.data());\n  }\n  DWORD GetLength() const { return static_cast<DWORD>(sid_buffer_.size()); }\n\nprivate:\n  std::vector<BYTE> sid_buffer_;\n  SidHolder(const SidHolder &) = delete;\n  SidHolder &operator=(const SidHolder &) = delete;\n};\n\ninline LPSECURITY_ATTRIBUTES get_sa() {\n  static struct initiator {\n    SECURITY_ATTRIBUTES sa_{};\n    std::vector<BYTE> sd_buffer_;\n    bool succ_ = false;\n    initiator() {\n      SidHolder everyone_sid;\n      if (!everyone_sid.CreateEveryone())\n        return;\n      SidHolder untrusted_il_sid;\n      if (!untrusted_il_sid.CreateUntrusted())\n        return;\n      const DWORD dacl_size =\n          sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + everyone_sid.GetLength();\n      std::vector<BYTE> dacl_buffer(dacl_size);\n      PACL dacl = reinterpret_cast<PACL>(dacl_buffer.data());\n      if (!InitializeAcl(dacl, dacl_size, ACL_REVISION))\n        return;\n      if (!AddAccessAllowedAce(dacl, ACL_REVISION,\n                               SYNCHRONIZE | SEMAPHORE_ALL_ACCESS |\n                                   EVENT_ALL_ACCESS | FILE_MAP_ALL_ACCESS,\n                               everyone_sid.Get()))\n        return;\n      const DWORD sacl_size = sizeof(ACL) + sizeof(SYSTEM_MANDATORY_LABEL_ACE) +\n                              untrusted_il_sid.GetLength();\n      std::vector<BYTE> sacl_buffer(sacl_size);\n      PACL sacl = reinterpret_cast<PACL>(sacl_buffer.data());\n      if (!InitializeAcl(sacl, sacl_size, ACL_REVISION))\n        return;\n      if (!AddMandatoryAce(sacl, ACL_REVISION, 0,\n                           SYSTEM_MANDATORY_LABEL_NO_WRITE_UP,\n                           untrusted_il_sid.Get()))\n        return;\n      SECURITY_DESCRIPTOR sd_absolute = {0};\n      if (!InitializeSecurityDescriptor(&sd_absolute,\n                                        SECURITY_DESCRIPTOR_REVISION))\n        return;\n      if (!SetSecurityDescriptorDacl(&sd_absolute, TRUE, dacl, FALSE))\n        return;\n      if (!SetSecurityDescriptorSacl(&sd_absolute, TRUE, sacl, FALSE))\n        return;\n      DWORD sd_buffer_size = 0;\n      MakeSelfRelativeSD(&sd_absolute, nullptr, &sd_buffer_size);\n      if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)\n        return;\n      sd_buffer_.resize(sd_buffer_size);\n      PSECURITY_DESCRIPTOR sd_relative =\n          reinterpret_cast<PSECURITY_DESCRIPTOR>(sd_buffer_.data());\n      if (!MakeSelfRelativeSD(&sd_absolute, sd_relative, &sd_buffer_size))\n        return;\n      sa_.nLength = sizeof(sa_);\n      sa_.lpSecurityDescriptor = sd_relative;\n      sa_.bInheritHandle = FALSE;\n      succ_ = true;\n    }\n  } handle;\n  return handle.succ_ ? &handle.sa_ : nullptr;\n}\n\nstruct MessageHeader {\n  std::atomic<uint64_t> sequence{0};\n  uint32_t data_size;\n  bool is_first_fragment;\n  bool is_last_fragment;\n};\n\nstruct MessageSlot {\n  MessageHeader header;\n  char data[0];\n};\n\nstruct ReaderSlot {\n\n  std::atomic<DWORD> pid{0};\n\n  std::atomic<uint64_t> sequence{0};\n};\n\nstruct SharedMemoryHeader {\n\n  std::atomic_flag write_lock = ATOMIC_FLAG_INIT;\n  uint32_t capacity;\n  uint32_t max_payload_size;\n  uint32_t max_readers;\n\n  std::atomic<uint64_t> write_index{0};\n\n  std::atomic<uint64_t> sequence_generator{0};\n};\n\n} // namespace detail\n\ntemplate <size_t MaxReaders = 16> class Channel {\npublic:\n  struct Config {\n    size_t capacity = 128;\n    size_t max_message_payload_size = 4096;\n  };\n\n  Channel() = default;\n  ~Channel() { disconnect(); }\n  Channel(const Channel &) = delete;\n  Channel &operator=(const Channel &) = delete;\n  Channel(Channel &&) = delete;\n  Channel &operator=(Channel &&) = delete;\n\n  void connect(const std::string &name, const Config &config = {}) {\n    if (is_connected())\n      throw Exception(\"Channel is already connected.\");\n\n    config_ = config;\n    name_ = name;\n    std::wstring shm_name =\n        L\"IPC_Channel_SHM_\" + std::wstring(name.begin(), name.end());\n    std::wstring event_name =\n        L\"IPC_Channel_EVT_\" + std::wstring(name.begin(), name.end());\n    LPSECURITY_ATTRIBUTES sa = detail::get_sa();\n    if (!sa)\n      throw Exception(\"Failed to get security attributes.\");\n\n    const size_t header_size = get_header_size();\n    const size_t slot_size =\n        sizeof(detail::MessageSlot) + config_.max_message_payload_size;\n    const size_t total_shm_size = header_size + config_.capacity * slot_size;\n\n    bool is_creator = false;\n    h_map_file_ = ::CreateFileMappingW(\n        INVALID_HANDLE_VALUE, sa, PAGE_READWRITE,\n        static_cast<DWORD>(total_shm_size >> 32),\n        static_cast<DWORD>(total_shm_size & 0xFFFFFFFF), shm_name.c_str());\n\n    if (h_map_file_ == NULL)\n      detail::throw_windows_error(\"CreateFileMappingW failed for \" + name);\n\n    if (::GetLastError() != ERROR_ALREADY_EXISTS)\n      is_creator = true;\n\n    p_shared_mem_ =\n        ::MapViewOfFile(h_map_file_, FILE_MAP_ALL_ACCESS, 0, 0, total_shm_size);\n    if (p_shared_mem_ == NULL) {\n      disconnect();\n      detail::throw_windows_error(\"MapViewOfFile failed for \" + name);\n    }\n\n    p_header_ = static_cast<detail::SharedMemoryHeader *>(p_shared_mem_);\n\n    p_buffer_start_ = reinterpret_cast<char *>(p_shared_mem_) + header_size;\n\n    if (is_creator) {\n\n      new (p_header_) detail::SharedMemoryHeader();\n      p_header_->capacity = static_cast<uint32_t>(config_.capacity);\n      p_header_->max_payload_size =\n          static_cast<uint32_t>(config_.max_message_payload_size);\n      p_header_->max_readers = static_cast<uint32_t>(MaxReaders);\n\n      for (size_t i = 0; i < MaxReaders; ++i) {\n        new (get_reader_slot(i)) detail::ReaderSlot();\n      }\n\n    } else {\n\n      if (p_header_->max_readers != MaxReaders) {\n        disconnect();\n        throw Exception(\n            \"Connection failed: Mismatched MaxReaders configuration.\");\n      }\n      if (p_header_->capacity != config_.capacity ||\n          p_header_->max_payload_size != config_.max_message_payload_size) {\n\n        disconnect();\n        throw Exception(\"Connection failed: Mismatched capacity or \"\n                        \"max_payload_size configuration.\");\n      }\n    }\n\n    if (is_creator) {\n      h_data_ready_event_ = ::CreateEventW(sa, TRUE, FALSE, event_name.c_str());\n      if (h_data_ready_event_ == NULL) {\n        disconnect();\n        detail::throw_windows_error(\"CreateEventW failed for \" + name);\n      }\n    } else {\n      h_data_ready_event_ = ::OpenEventW(SYNCHRONIZE | EVENT_MODIFY_STATE,\n                                         FALSE, event_name.c_str());\n      if (h_data_ready_event_ == NULL) {\n        disconnect();\n        detail::throw_windows_error(\"OpenEventW failed for \" + name);\n      }\n    }\n\n    register_reader();\n  }\n\n  void disconnect() {\n\n    if (is_connected()) {\n      deregister_reader();\n    }\n\n    if (p_shared_mem_ != nullptr) {\n      ::UnmapViewOfFile(p_shared_mem_);\n      p_shared_mem_ = nullptr;\n    }\n    if (h_map_file_ != NULL) {\n      ::CloseHandle(h_map_file_);\n      h_map_file_ = NULL;\n    }\n    if (h_data_ready_event_ != NULL) {\n      ::CloseHandle(h_data_ready_event_);\n      h_data_ready_event_ = NULL;\n    }\n    p_header_ = nullptr;\n    p_buffer_start_ = nullptr;\n    my_reader_slot_index_ = -1;\n    name_.clear();\n  }\n\n  bool is_connected() const { return p_shared_mem_ != nullptr; }\n\n  void send(const std::string &message) {\n    if (!is_connected()) {\n      throw Exception(\"Channel is not connected.\");\n    }\n\n    const size_t max_payload = p_header_->max_payload_size;\n    const size_t num_fragments =\n        message.empty() ? 1\n                        : (message.length() + max_payload - 1) / max_payload;\n    const uint32_t capacity = p_header_->capacity;\n\n    if (num_fragments > capacity) {\n      throw Exception(\n          \"Message is too large to fit in the channel buffer capacity.\");\n    }\n\n    std::string_view message_view(message);\n\n    while (true) {\n\n      while (p_header_->write_lock.test_and_set(std::memory_order_acquire)) {\n        std::this_thread::yield();\n      }\n\n      const uint64_t write_idx =\n          p_header_->write_index.load(std::memory_order_relaxed);\n\n      uint64_t slowest_reader_seq = get_slowest_reader_sequence();\n\n      if ((write_idx - slowest_reader_seq) + num_fragments > capacity) {\n\n        p_header_->write_lock.clear(std::memory_order_release);\n        std::this_thread::sleep_for(std::chrono::milliseconds(1));\n        continue;\n      }\n\n      try {\n        const uint64_t seq_gen_at_write_time =\n            p_header_->sequence_generator.load(std::memory_order_relaxed);\n\n        for (size_t i = 0; i < num_fragments; ++i) {\n          uint64_t current_write_slot_idx = (write_idx + i) % capacity;\n          detail::MessageSlot *slot = get_message_slot(current_write_slot_idx);\n\n          size_t offset = i * max_payload;\n          size_t chunk_size =\n              message.empty()\n                  ? 0\n                  : std::min(message_view.length() - offset, max_payload);\n          uint64_t new_sequence = seq_gen_at_write_time + i + 1;\n\n          slot->header.data_size = static_cast<uint32_t>(chunk_size);\n          slot->header.is_first_fragment = (i == 0);\n          slot->header.is_last_fragment = (i == num_fragments - 1);\n          if (chunk_size > 0) {\n            memcpy(&slot->data, message_view.data() + offset, chunk_size);\n          }\n\n          slot->header.sequence.store(new_sequence, std::memory_order_release);\n        }\n\n        p_header_->write_index.fetch_add(num_fragments,\n                                         std::memory_order_relaxed);\n        p_header_->sequence_generator.fetch_add(num_fragments,\n                                                std::memory_order_relaxed);\n\n      } catch (...) {\n        p_header_->write_lock.clear(std::memory_order_release);\n        throw;\n      }\n\n      p_header_->write_lock.clear(std::memory_order_release);\n      ::SetEvent(h_data_ready_event_);\n      return;\n    }\n  }\n\n  void receive(std::string &message) {\n    if (!internal_try_receive(message, INFINITE)) {\n      throw Exception(\n          \"receive failed. The wait handle may be invalid or closed.\");\n    }\n  }\n\n  bool try_receive(std::string &message) {\n    return internal_try_receive(message, 0);\n  }\n\nprivate:\n  bool internal_try_receive(std::string &message, DWORD timeout_ms) {\n    if (!is_connected())\n      throw Exception(\"Channel is not connected.\");\n    if (my_reader_slot_index_ < 0)\n      throw Exception(\"Reader is not registered.\");\n\n    std::string reassembly_buffer;\n    bool in_reassembly = false;\n\n    while (true) {\n\n      while (true) {\n        uint64_t next_expected_seq = local_read_sequence_ + 1;\n        detail::MessageSlot *found_slot = nullptr;\n\n        uint64_t probable_idx =\n            (p_header_->write_index.load(std::memory_order_relaxed) - 1) %\n            p_header_->capacity;\n        probable_idx = (local_read_sequence_) % p_header_->capacity;\n\n        detail::MessageSlot *candidate_slot = get_message_slot(probable_idx);\n        if (candidate_slot->header.sequence.load(std::memory_order_acquire) ==\n            next_expected_seq) {\n          found_slot = candidate_slot;\n        } else {\n\n          for (uint32_t i = 0; i < p_header_->capacity; ++i) {\n            detail::MessageSlot *current_slot = get_message_slot(i);\n            uint64_t slot_sequence =\n                current_slot->header.sequence.load(std::memory_order_relaxed);\n            if (slot_sequence == next_expected_seq) {\n              found_slot = current_slot;\n              break;\n            }\n          }\n        }\n\n        if (found_slot) {\n\n          if (found_slot->header.is_first_fragment) {\n            if (in_reassembly)\n              reassembly_buffer.clear();\n            in_reassembly = true;\n          } else if (!in_reassembly) {\n            local_read_sequence_++;\n            update_reader_progress();\n            continue;\n          }\n\n          reassembly_buffer.append(found_slot->data,\n                                   found_slot->header.data_size);\n          local_read_sequence_++;\n\n          if (found_slot->header.is_last_fragment) {\n            message = std::move(reassembly_buffer);\n            in_reassembly = false;\n            update_reader_progress();\n            return true;\n          }\n        } else {\n\n          break;\n        }\n      }\n\n      ::ResetEvent(h_data_ready_event_);\n\n      if (p_header_->sequence_generator.load(std::memory_order_acquire) >\n          local_read_sequence_) {\n        continue;\n      }\n\n      DWORD wait_result =\n          ::WaitForSingleObject(h_data_ready_event_, timeout_ms);\n      if (wait_result == WAIT_OBJECT_0) {\n        continue;\n      } else if (wait_result == WAIT_TIMEOUT) {\n        return false;\n      } else {\n        detail::throw_windows_error(\n            \"WaitForSingleObject failed on ipc::Channel '\" + name_ + \"'\");\n      }\n    }\n  }\n\n  static constexpr size_t get_header_size() {\n    return sizeof(detail::SharedMemoryHeader) +\n           MaxReaders * sizeof(detail::ReaderSlot);\n  }\n\n  detail::ReaderSlot *get_reader_slot(size_t index) const {\n    char *base = static_cast<char *>(p_shared_mem_);\n    return reinterpret_cast<detail::ReaderSlot *>(\n        base + sizeof(detail::SharedMemoryHeader) +\n        index * sizeof(detail::ReaderSlot));\n  }\n\n  detail::MessageSlot *get_message_slot(uint64_t index) const {\n    const size_t slot_size =\n        sizeof(detail::MessageSlot) + config_.max_message_payload_size;\n    return reinterpret_cast<detail::MessageSlot *>(p_buffer_start_ +\n                                                   index * slot_size);\n  }\n\n  inline static std::atomic_int instance_count;\n  int current_instance_index_ = instance_count++;\n  int current_instance_id_ =\n      (::GetCurrentProcessId() << 16) + current_instance_index_;\n\n  static bool is_pid_alive(DWORD pid) {\n    HANDLE h_process = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);\n    if (h_process == NULL) {\n      return false;\n    }\n    ::CloseHandle(h_process);\n    return true;\n  }\n\n  void register_reader() {\n    uint64_t current_global_seq =\n        p_header_->sequence_generator.load(std::memory_order_relaxed);\n\n    for (size_t i = 0; i < MaxReaders; ++i) {\n      detail::ReaderSlot *slot = get_reader_slot(i);\n      DWORD expected_pid = 0;\n\n      if (slot->pid.compare_exchange_strong(expected_pid, current_instance_id_,\n                                            std::memory_order_acq_rel)) {\n        my_reader_slot_index_ = static_cast<int>(i);\n\n        local_read_sequence_ = current_global_seq;\n        slot->sequence.store(current_global_seq, std::memory_order_release);\n        return;\n      }\n    }\n\n    disconnect();\n    throw Exception(\"Failed to register reader: Maximum number of concurrent \"\n                    \"readers reached for channel '\" +\n                    name_ + \"'.\");\n  }\n\n  void deregister_reader() {\n    if (my_reader_slot_index_ >= 0) {\n      detail::ReaderSlot *slot = get_reader_slot(my_reader_slot_index_);\n      slot->pid.store(0, std::memory_order_release);\n      my_reader_slot_index_ = -1;\n    }\n  }\n\n  void update_reader_progress() {\n    if (my_reader_slot_index_ >= 0) {\n      get_reader_slot(my_reader_slot_index_)\n          ->sequence.store(local_read_sequence_, std::memory_order_release);\n    }\n\n    uint64_t now = std::chrono::system_clock::now().time_since_epoch().count();\n    if (now - last_clean_time_ > clean_interval_ms) {\n      clean_dead_readers();\n      last_clean_time_ = now;\n    }\n  }\n\n  uint64_t get_slowest_reader_sequence() const {\n    uint64_t slowest_seq =\n        p_header_->write_index.load(std::memory_order_relaxed);\n    bool reader_found = false;\n\n    for (size_t i = 0; i < MaxReaders; ++i) {\n      const detail::ReaderSlot *slot = get_reader_slot(i);\n      auto pid = slot->pid.load(std::memory_order_acquire);\n      if (pid != 0 && pid != current_instance_id_) {\n        uint64_t reader_seq = slot->sequence.load(std::memory_order_acquire);\n        if (!reader_found) {\n          slowest_seq = reader_seq;\n          reader_found = true;\n        } else {\n          slowest_seq = std::min(slowest_seq, reader_seq);\n        }\n      }\n    }\n\n    return slowest_seq;\n  }\n\n  void clean_dead_readers() {\n    for (size_t i = 0; i < MaxReaders; ++i) {\n      detail::ReaderSlot *slot = get_reader_slot(i);\n      DWORD pid = slot->pid.load(std::memory_order_acquire);\n      if (pid != 0 && !is_pid_alive(pid)) {\n\n        slot->pid.store(0, std::memory_order_release);\n        slot->sequence.store(0, std::memory_order_release);\n      }\n    }\n  }\n\n  uint64_t last_clean_time_ = 0;\n  static constexpr uint64_t clean_interval_ms = 100;\n  Config config_;\n  std::string name_;\n  HANDLE h_map_file_ = NULL;\n  HANDLE h_data_ready_event_ = NULL;\n  void *p_shared_mem_ = nullptr;\n  detail::SharedMemoryHeader *p_header_ = nullptr;\n  char *p_buffer_start_ = nullptr;\n\n  uint64_t local_read_sequence_ = 0;\n  int my_reader_slot_index_ = -1;\n};\n\n} // namespace ipc\n\n#if defined(_MSC_VER)\n#pragma warning(pop)\n#endif\n"
  },
  {
    "path": "scripts/bindgen.bat",
    "content": "npx breeze-bindgen@latest -i src/script/bindings/binding_types.h --nameFilter chromatic::js -o src/script/bindings --tsModuleName chromatic"
  },
  {
    "path": "scripts/rebuild.ps1",
    "content": "$targetPath = \"C:\\Program Files\\Tencent\\QQNT-dev\\QQ.exe\"\n\n#  kill the process if it is running\nforeach ($process in Get-Process -ErrorAction SilentlyContinue) {\n    if ($process.Path -eq $targetPath) {\n        Stop-Process -Id $process.Id -Force || Write-Host \"Failed to stop process: $($process.Id) - $($process.Name)\"\n        Write-Host \"Killed process: $($process.Id) - $($process.Name)\"\n    }\n}\n\nxmake b chromatic\nStart-Process -FilePath $targetPath -WorkingDirectory \"C:\\Program Files\\Tencent\\QQNT-dev\" -NoNewWindow"
  },
  {
    "path": "src/config.cc",
    "content": "#include \"config.h\"\n#include <chrono>\n#include <filesystem>\n#include <fstream>\n#include <mutex>\n#include <thread>\n\n#include \"rfl.hpp\"\n#include \"rfl/DefaultIfMissing.hpp\"\n#include \"rfl/json.hpp\"\n#include \"ylt/easylog.hpp\"\n\n\n#include \"utils.h\"\n#include \"windows.h\"\n\nnamespace chromatic {\nstd::unique_ptr<config> config::current;\nstd::vector<std::function<void()>> config::on_reload;\n\nvoid config::write_config() {\n  auto config_file = data_directory() / \"config.json\";\n  std::ofstream ofs(config_file);\n  if (!ofs) {\n    std::cerr << \"Failed to write config file.\" << std::endl;\n    return;\n  }\n\n  ofs << rfl::json::write(*config::current);\n}\nvoid config::read_config() {\n  auto config_file = data_directory() / \"config.json\";\n\n#ifdef __llvm__\n  std::ifstream ifs(config_file);\n  if (!std::filesystem::exists(config_file)) {\n    auto config_file = data_directory() / \"config.json\";\n    std::ofstream ofs(config_file);\n    if (!ofs) {\n      std::cerr << \"Failed to write config file.\" << std::endl;\n    }\n\n    ofs << R\"({\n})\";\n  }\n  if (!ifs) {\n    std::cerr\n        << \"Config file could not be opened. Using default config instead.\"\n        << std::endl;\n    config::current = std::make_unique<config>();\n    config::current->debug_console = true;\n  } else {\n    std::string json_str;\n    std::copy(std::istreambuf_iterator<char>(ifs),\n              std::istreambuf_iterator<char>(), std::back_inserter(json_str));\n\n    if (auto json =\n            rfl::json::read<config, rfl::NoExtraFields, rfl::DefaultIfMissing>(\n                json_str)) {\n      config::current = std::make_unique<config>(json.value());\n    } else {\n      std::cerr << \"Failed to read config file: \" << json.error().what()\n                << \"\\nUsing default config instead.\" << std::endl;\n      config::current = std::make_unique<config>();\n      config::current->debug_console = true;\n    }\n  }\n\n  for (auto &fn : config::on_reload) {\n    try {\n      fn();\n    } catch (const std::exception &e) {\n      ELOGFMT(WARN, \"Failed to run on_reload function: {}\", e.what());\n    }\n  }\n#else\n#pragma message                                                                \\\n    \"We don't support loading config file on MSVC because of a bug in MSVC.\"\n  dbgout(\"We don't support loading config file when compiled with MSVC \"\n         \"because of a bug in MSVC.\");\n  config::current = std::make_unique<config>();\n  config::current->debug_console = true;\n#endif\n\n  if (config::current->debug_console) {\n    ShowWindow(GetConsoleWindow(), SW_SHOW);\n  } else {\n    ShowWindow(GetConsoleWindow(), SW_HIDE);\n  }\n}\n\nstd::filesystem::path config::data_directory() {\n  static std::optional<std::filesystem::path> path;\n  static std::mutex mtx;\n  std::lock_guard lock(mtx);\n\n  if (!path) {\n    path = std::filesystem::path(utils::env(\"USERPROFILE\").value()) /\n           \".chromatic\" / utils::current_executable_path().filename().string();\n  }\n\n  if (!std::filesystem::exists(*path)) {\n    std::filesystem::create_directories(*path);\n  }\n\n  return path.value();\n}\nvoid config::run_config_loader() {\n  auto config_path = config::data_directory() / \"config.json\";\n  ELOGFMT(INFO, \"config file: {}\", config_path.string());\n  config::read_config();\n  std::thread([config_path]() {\n    auto last_mod = std::filesystem::last_write_time(config_path);\n    while (true) {\n      if (std::filesystem::last_write_time(config_path) != last_mod) {\n        last_mod = std::filesystem::last_write_time(config_path);\n        config::read_config();\n      }\n      std::this_thread::sleep_for(std::chrono::milliseconds(100));\n    }\n  }).detach();\n}\nstd::string config::dump_config() {\n  if (!current) {\n    return \"{}\";\n  }\n  return rfl::json::write(*current);\n}\n} // namespace chromatic\n"
  },
  {
    "path": "src/config.h",
    "content": "#pragma once\n\n#include <filesystem>\n#include <functional>\n#include <memory>\n#include <numbers>\n#include <vector>\n\nnamespace chromatic {\n\nstruct config {\n  bool debug_console = true;\n\n  struct detector {\n    struct chrome {\n      bool enable = true;\n      std::string chrome_module_name = \"\";\n    } chrome;\n  } detector;\n\n  std::string $schema;\n\n  static std::unique_ptr<config> current;\n  static void read_config();\n  static void write_config();\n  static void run_config_loader();\n  static std::string dump_config();\n  static std::vector<std::function<void()>> on_reload;\n\n  static std::filesystem::path data_directory();\n};\n} // namespace chromatic"
  },
  {
    "path": "src/context.cc",
    "content": "#include \"context.h\"\n\n#include \"config.h\"\n#include \"cpptrace/basic.hpp\"\n#include \"utils.h\"\n#include \"ylt/easylog.hpp\"\n\n#include \"cpptrace/cpptrace.hpp\"\n#include \"cpptrace/from_current.hpp\"\n\n#include \"Windows.h\"\n#include \"blook/blook.h\"\n#include <debugapi.h>\n#include <fstream>\n#include <thread>\n#include <unordered_map>\n\n#include \"rfl.hpp\"\n#include \"rfl/json.hpp\"\n\n#include \"./hooks/blink_parse_html_manipulator.h\"\n#include \"./hooks/disable-integrity.h\"\n#include \"./hooks/wait_for_module_load.h\"\n\nnamespace chromatic {\nstd::unique_ptr<context> context::current = nullptr;\n\nvoid context::init_singleton() {\n  SetUnhandledExceptionFilter(+[](EXCEPTION_POINTERS *ep) -> long {\n    ELOGFMT(FATAL, \"Unhandled exception: {}\", cpptrace::stacktrace::current());\n    Sleep(1000);\n    return EXCEPTION_CONTINUE_SEARCH;\n  });\n\n  CPPTRACE_TRY {\n    if (!current) {\n      current = std::make_unique<context>();\n      current->init_context();\n    }\n  }\n  CPPTRACE_CATCH(const std::exception &e) {\n    ELOGFMT(ERROR, \"Failed to initialize context: {} {}\", e.what(),\n            cpptrace::from_current_exception());\n    Sleep(1000);\n  }\n  catch (...) {\n    ELOGFMT(ERROR, \"Failed to initialize context: unknown exception: {}\",\n            cpptrace::from_current_exception());\n    Sleep(1000);\n  }\n}\ncontext::context() {}\n\nvoid context::detect_process_type() {\n  auto cmdline = std::wstring(GetCommandLineW());\n\n  auto &cfg = *config::current.get();\n  if (cfg.detector.chrome.enable) {\n    auto chrome_module = config::current->detector.chrome.chrome_module_name;\n\n    if (cmdline.find(L\"--type=gpu\") != std::wstring::npos) {\n      type.chrome_type = process_type::chrome_type::gpu;\n    } else if (cmdline.find(L\"--type=renderer\") != std::wstring::npos) {\n      type.chrome_type = process_type::chrome_type::renderer;\n    } else if (cmdline.find(L\"--type=utility\") != std::wstring::npos) {\n      type.chrome_type = process_type::chrome_type::utility;\n    } else if (cmdline.find(L\"--type=network\") != std::wstring::npos) {\n      type.chrome_type = process_type::chrome_type::network;\n    } else if (cmdline.find(L\"--type=\") == std::wstring::npos) {\n      type.chrome_type = process_type::chrome_type::main;\n    }\n\n    if (type.chrome_type) {\n      ELOGFMT(INFO, \"Detected Chrome process type: {}\",\n              static_cast<int>(type.chrome_type.value()));\n    } else {\n      ELOGFMT(WARN, \"Failed to detect Chrome process type.\");\n    }\n\n    auto proc = blook::Process::self();\n    if (chrome_module == \"\") {\n      std::shared_ptr<blook::Module> chrome_mod;\n      if (auto mod = proc->module(\"chrome.dll\")) {\n        chrome_mod = mod.value();\n      } else if (auto mod = proc->module(\"edge.dll\")) {\n        chrome_mod = mod.value();\n      } else if (auto mod = proc->module(\"libcef.dll\")) {\n        chrome_mod = mod.value();\n      } else if (auto mod = proc->process_module()) {\n        chrome_mod = mod.value();\n      }\n\n      // verify if the module is actually chrome\n      constexpr auto chrome_signature =\n          \"\\\\content\\\\browser\\\\renderer_host\\\\debug_urls.cc\";\n      if (chrome_mod && chrome_mod->section(\".rdata\") &&\n          chrome_mod->section(\".rdata\")->find_one(chrome_signature)) {\n        type.chrome_module = chrome_mod;\n      } else {\n        type.chrome_module = {};\n      }\n\n      if (type.chrome_module) {\n        on_before_chrome_startup();\n      }\n    } else {\n      if (GetModuleHandleW(utils::utf8_to_wstring(chrome_module).c_str())) {\n        type.chrome_module =\n            proc->module(\n                    std::filesystem::path(chrome_module).filename().string())\n                .value();\n\n        on_before_chrome_startup();\n      } else {\n        ELOGFMT(WARN, \"Chrome module {} not found, waiting for it to load...\",\n                chrome_module);\n\n        hooks::wait_for_module_load::wait_for_module(\n            chrome_module,\n            [this](void *mod) {\n              if (mod) {\n                ELOGFMT(INFO, \"Chrome module {} loaded\", mod);\n                type.chrome_module =\n                    blook::Process::self()\n                        ->module(\n                            utils::get_module_path(mod).filename().string())\n                        .value();\n\n                auto entry_hook =\n                    type.chrome_module->entry_point()->inline_hook();\n                entry_hook->install(\n                    [this, entry_hook](size_t a, size_t b, size_t c) {\n                      on_before_chrome_startup();\n                      return entry_hook->call_trampoline<size_t>(a, b, c);\n                    });\n              } else {\n                ELOGFMT(ERROR, \"Failed to load Chrome module\");\n              }\n            })\n            .wait();\n      }\n    }\n\n    if (type.chrome_module) {\n      ELOGFMT(\n          INFO, \"Detected Chrome module: {}\",\n          utils::get_module_path(type.chrome_module->base().data()).string());\n    } else {\n      ELOGFMT(WARN, \"Chrome module not found, some features may not work.\");\n    }\n  }\n}\nvoid context::init_ipc() {\n  //   process_ipc.connect(std::format(\n  //       \"chromatic://process/{}\",\n  //       std::hash<std::string>{}(utils::current_executable_path().string())));\n  process_ipc.connect(\"chromatic://process/\");\n}\nvoid context::on_before_chrome_startup() {\n  ELOGFMT(INFO, \"on_before_chrome_startup called\");\n  blink_parse_html_manipulator::install();\n}\nvoid context::init_context() {\n  auto cmdline = std::wstring(GetCommandLineW());\n\n  ELOGFMT(INFO, \"Command line: {}\", utils::wstring_to_utf8(cmdline));\n  bool is_probably_main = cmdline.find(L\"--type=\") == std::wstring::npos,\n       is_renderer = cmdline.find(L\"--type=renderer\") != std::wstring::npos;\n  if (!is_probably_main && !is_renderer) {\n    ELOGFMT(INFO, \"Chromatic is not running in this process.\");\n    return;\n  }\n\n  init_ipc();\n\n  if (is_probably_main) {\n    DWORD mode;\n    AllocConsole();\n    freopen(\"CONOUT$\", \"w\", stdout);\n    freopen(\"CONOUT$\", \"w\", stderr);\n\n    static HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);\n    GetConsoleMode(h, &mode);\n    SetConsoleMode(h, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);\n\n    static auto log_msg_raw = [](const auto &msg) {\n      std::string msg_formatted =\n          std::format(\"\\033[47;30m chromatic \\033[0m {}\", msg);\n\n      msg_formatted.erase(\n          std::find_if(msg_formatted.rbegin(), msg_formatted.rend(),\n                       [](unsigned char ch) { return !std::isspace(ch); })\n              .base(),\n          msg_formatted.end());\n      msg_formatted += \"\\n\";\n      WriteConsoleA(h, msg_formatted.c_str(),\n                    static_cast<DWORD>(msg_formatted.size()), nullptr, nullptr);\n    };\n\n    easylog::add_appender(log_msg_raw);\n\n    hooks::windows::disable_integrity();\n\n    config::run_config_loader();\n    config::on_reload.push_back([this]() {\n      ELOGFMT(INFO, \"Config reloaded, broadcasting to other processes.\");\n      process_ipc.send(\"config_reload\", *config::current);\n    });\n\n    ELOGFMT(INFO, \"Chromatic v0.0.0, initialized as main process.\");\n\n    process_ipc.send(\"config_reload\", *config::current);\n\n    process_ipc.add_call_handler<config>(\"get_config\",\n                                         []() { return *config::current; });\n\n    process_ipc.add_call_handler<bool, std::string>(\n        \"log\", [](const std::string &msg) {\n          log_msg_raw(\"[other_proc] \" + msg);\n          return true;\n        });\n\n    std::thread([this]() {\n      detect_process_type();\n      script = std::make_unique<script_engine>();\n\n      static std::unordered_map<std::string, std::pair<size_t, int32_t>>\n          symbol_cache;\n\n      auto symbols_file = config::data_directory() / \"symbols.json\";\n      if (std::filesystem::exists(symbols_file)) {\n        try {\n          std::ifstream ifs(symbols_file, std::ios::binary);\n          if (ifs) {\n            symbol_cache = rfl::json::read<std::unordered_map<\n                std::string, std::pair<size_t, int32_t>>>(ifs)\n                               .value();\n          }\n        } catch (const std::exception &e) {\n          ELOGFMT(ERROR, \"Failed to read symbols from {}: {}\",\n                  symbols_file.string(), e.what());\n        }\n      }\n\n      process_ipc.add_call_handler<\n          bool, std::pair<std::string, std::pair<size_t, int32_t>>>(\n          \"set_symbol\", [](auto &symbol) {\n            symbol_cache[symbol.first] = symbol.second;\n            std::ofstream ofs(config::data_directory() / \"symbols.json\",\n                              std::ios::binary | std::ios::trunc);\n            if (ofs) {\n              ofs << rfl::json::write(symbol_cache);\n            }\n            return true;\n          });\n\n      process_ipc.add_call_handler<std::pair<size_t, int32_t>, std::string>(\n          \"get_symbol\", [](const std::string &name) {\n            auto it = symbol_cache.find(name);\n            if (it != symbol_cache.end()) {\n              return it->second;\n            }\n            return std::pair<size_t, int32_t>{0, 0};\n          });\n\n      blink_parse_html_manipulator::register_js();\n    }).detach();\n  } else if (cmdline.find(L\"--type=renderer\") != std::wstring::npos) {\n    easylog::add_appender([this](std::string_view msg) {\n      process_ipc.call<bool, std::string>(\"log\", std::string(msg));\n    });\n\n    ELOGFMT(INFO, \"requesting config from main process.\");\n    process_ipc.add_listener<config>(\"config_reload\", [](const config &cfg) {\n      ELOGFMT(INFO, \"Received config_reload\");\n      config::current = std::make_unique<config>(cfg);\n    });\n    process_ipc.call<config>(\"get_config\");\n    process_ipc.send(breeze_ipc::packet {\n      .seq = 1,\n      .return_for_call = 0,\n      .name = \"call_get_config\",\n      .data = \"true\"\n    });\n    auto p = process_ipc.call_and_poll<config>(\"get_config\");\n    if (p) {\n      config::current = std::make_unique<config>(p.value());\n    } else {\n      ELOGFMT(WARN, \"Failed to get config from main process, using default.\");\n      config::current = std::make_unique<config>();\n    }\n\n    ELOGFMT(INFO, \"Chromatic v0.0.0, initialized as renderer process.\");\n\n    ELOGFMT(INFO, \"Config loaded: {}\", config::current->dump_config());\n\n    detect_process_type();\n  }\n}\n} // namespace chromatic"
  },
  {
    "path": "src/context.h",
    "content": "#pragma once\n\n#include \"blook/module.h\"\n#include \"config.h\"\n#include \"script/script.h\"\n#include <memory>\n#include <string>\n\n#include \"ipc.h\"\n\nnamespace chromatic {\nstruct context {\n  static std::unique_ptr<context> current;\n\n  static void init_singleton();\n\n  struct process_type {\n    enum chrome_type { main, renderer, gpu, utility, network };\n\n    std::optional<chrome_type> chrome_type = {};\n    std::shared_ptr<blook::Module> chrome_module = {};\n  };\n\n  process_type type = {};\n  \n  breeze_ipc process_ipc;\n  std::unique_ptr<script_engine> script = nullptr;\n\n  void init_ipc();\n  void init_context();\n\n  context();\n\n  void on_before_chrome_startup();\n\nprivate:\n  void detect_process_type();\n};\n} // namespace chromatic"
  },
  {
    "path": "src/entry.cc",
    "content": "#include <algorithm>\n#include <print>\n#include <ranges>\n#include <string>\n#include <thread>\n\n#include \"blook/blook.h\"\n\n#include \"config.h\"\n#include \"context.h\"\n#include \"ylt/easylog.hpp\"\n\n#include \"utils.h\"\n\n#define NOMINMAX\n#define WIN32_LEAN_AND_MEAN\n#include \"Windows.h\"\n\nnamespace chromatic {\nint main() {\n  easylog::init_log(easylog::Severity::INFO, \"\", false, false);\n\n  easylog::add_appender(\n      [](std::string_view msg) { OutputDebugStringA(msg.data()); });\n\n  context::init_singleton();\n  return 0;\n}\n\n} // namespace chromatic\n\nint APIENTRY DllMain(HINSTANCE hInstance, DWORD fdwReason, LPVOID lpvReserved) {\n  switch (fdwReason) {\n  case DLL_PROCESS_ATTACH: {\n    auto cmdline = std::string(GetCommandLineA());\n\n    if (cmdline.contains(\"--type=gpu\")) {\n      return 1; // Skip if this is a gpu process\n    }\n\n    chromatic::main();\n    break;\n  }\n  }\n  return 1;\n}"
  },
  {
    "path": "src/hooks/blink_parse_html_manipulator.cc",
    "content": "#include \"blink_parse_html_manipulator.h\"\n\n#include \"../context.h\"\n#include \"../utils.h\"\n#include \"blook/function.h\"\n#include \"ylt/easylog.hpp\"\n#include <chrono>\n#include <debugapi.h>\n#include <future>\n#include <thread>\n\n#include \"blook/blook.h\"\n#define NOMINMAX\n#define WIN32_LEAN_AND_MEAN\n#include \"Windows.h\"\n\n#include \"../script/bindings/binding_types.h\"\n\nextern std::list<std::function<std::string(std::string)>>\n    blink_parse_html_manipulators;\n\nnamespace chromatic {\nstruct BlinkHTMLData {\n  const std::span<char> &data;\n  std::optional<std::vector<char>> replacement;\n};\n\nstruct BlinkUtilSpan {\n  char *data;\n  size_t size;\n};\n\nvoid blink_parse_html_manipulator::install() {\n  if (context::current->type.chrome_type != context::process_type::renderer ||\n      !context::current->type.chrome_module) {\n    return;\n  }\n\n  ELOGFMT(INFO, \"BlinkParseHTMLManipulator: Checking for cached symbol \"\n                \"HTMLParser__AppendBytes\");\n\n  auto &chrome = context::current->type.chrome_module;\n\n  auto rdata = chrome->section(\".rdata\").value();\n  auto text = chrome->section(\".text\").value();\n\n  auto crc32 = text.crc32();\n\n  static std::optional<blook::Function> HTMLParser__AppendBytes = {};\n  if (auto res = context::current->process_ipc\n                     .call_and_poll<std::pair<size_t, int32_t>>(\n                         \"get_symbol\", std::string(\"HTMLParser__AppendBytes\"));\n      res && res->first != 0 && res->second == crc32) {\n    HTMLParser__AppendBytes = text.add(res->first).as_function();\n    ELOGFMT(INFO,\n            \"BlinkParseHTMLManipulator: Using cached symbol \"\n            \"HTMLParser__AppendBytes at {}\",\n            HTMLParser__AppendBytes->data());\n  } else {\n    auto appendBytesText = rdata.find_one(\"HTMLDocumentParser::appendBytes\");\n    if (!appendBytesText) {\n      ELOGFMT(WARN,\n              \"BlinkParseHTMLManipulator: HTMLDocumentParser::appendBytes not \"\n              \"found in rdata section\");\n      return;\n    }\n\n    ELOGFMT(INFO,\n            \"BlinkParseHTMLManipulator: Found HTMLDocumentParser::appendBytes \"\n            \"string at {} in rdata section\",\n            appendBytesText.value().data());\n\n    auto xref = text.find_xref(appendBytesText.value());\n\n    if (!xref) {\n      ELOGFMT(WARN,\n              \"BlinkParseHTMLManipulator: HTMLDocumentParser::appendBytes xref \"\n              \"not found in text section\");\n      return;\n    }\n\n    ELOGFMT(INFO,\n            \"BlinkParseHTMLManipulator: Found HTMLDocumentParser::appendBytes \"\n            \"function at {} in text section\",\n            xref->data());\n\n    HTMLParser__AppendBytes =\n        xref->find_upwards({0x56, 0x57}).value().as_function();\n\n    context::current->process_ipc\n        .call<bool, std::pair<std::string, std::pair<size_t, int32_t>>>(\n            \"set_symbol\",\n            std::pair(\n                std::string(\"HTMLParser__AppendBytes\"),\n                std::pair(\n                    (size_t)(HTMLParser__AppendBytes->pointer() - text).data(),\n                    crc32)));\n  }\n\n  static auto HTMLParser__AppendBytes_Hook =\n      HTMLParser__AppendBytes->inline_hook();\n\n  const auto pFunc = (uint8_t *)HTMLParser__AppendBytes->data();\n\n  if (pFunc[0] == 0x56 && pFunc[1] == 0x57 && pFunc[2] == 0x53) {\n    ELOGFMT(INFO, \"BlinkParseHTMLManipulator: Using older function signature\");\n    HTMLParser__AppendBytes_Hook->install(+[](void *self, uint8_t *data,\n                                              size_t size) {\n      ELOGFMT(DEBUG, \"BlinkParseHTMLManipulator: Hook called with {} bytes\",\n              size);\n      auto span_data = std::span<char>(reinterpret_cast<char *>(data), size);\n      std::vector<std::shared_ptr<std::any>> contexts;\n      BlinkHTMLData html_data{.data = span_data, .replacement = {}};\n\n      auto res = context::current->process_ipc.call_and_poll<std::string>(\n          \"on_blink_parse_html_manipulate\",\n          std::string(html_data.data.data(), html_data.data.size()));\n\n      if (res.has_value() &&\n          res.value() !=\n              std::string_view(html_data.data.data(), html_data.data.size())) {\n        ELOGFMT(\n            DEBUG,\n            \"BlinkParseHTMLManipulator: HTML content modified, new size: {}\",\n            res.value().size());\n        html_data.replacement =\n            std::vector<char>(res.value().begin(), res.value().end());\n      }\n\n      auto ret = html_data.replacement.has_value()\n                     ? HTMLParser__AppendBytes_Hook->call_trampoline<void *>(\n                           self, html_data.replacement->data(),\n                           html_data.replacement->size())\n                     : HTMLParser__AppendBytes_Hook->call_trampoline<void *>(\n                           self, data, size);\n      return ret;\n    });\n  } else {\n    ELOGFMT(INFO, \"BlinkParseHTMLManipulator: Using newer function signature\");\n    HTMLParser__AppendBytes_Hook->install(+[](void *self, BlinkUtilSpan &data) {\n      ELOGFMT(DEBUG, \"BlinkParseHTMLManipulator: Hook called with {} bytes\",\n              data.size);\n      auto span_data =\n          std::span<char>(reinterpret_cast<char *>(data.data), data.size);\n      std::vector<std::shared_ptr<std::any>> contexts;\n      BlinkHTMLData html_data{.data = span_data, .replacement = {}};\n\n      auto html = std::string(html_data.data.data(), html_data.data.size());\n      if (auto res = context::current->process_ipc\n                         .call<std::string, std::string>(\n                             \"on_blink_parse_html_manipulate\", html)\n                         .get();\n          res != html) {\n        ELOGFMT(\n            DEBUG,\n            \"BlinkParseHTMLManipulator: HTML content modified, new size: {}\",\n            res.size());\n        html_data.replacement = std::vector<char>(res.begin(), res.end());\n      }\n\n      auto blink_span =\n          html_data.replacement.has_value()\n              ? BlinkUtilSpan{reinterpret_cast<char *>(\n                                  html_data.replacement->data()),\n                              html_data.replacement->size()}\n              : BlinkUtilSpan{reinterpret_cast<char *>(span_data.data()),\n                              span_data.size()};\n\n      auto ret = HTMLParser__AppendBytes_Hook->call_trampoline<void *>(\n          self, blink_span);\n      return ret;\n    });\n  }\n\n  context::current->process_ipc.add_call_handler<bool>(\n      \"is_blink_parse_html_manipulator_available\", []() { return true; });\n\n  ELOGFMT(INFO,\n          \"BlinkParseHTMLManipulator: Installation completed successfully\");\n}\n\nbool blink_parse_html_manipulator::is_available() {\n  auto result = context::current->process_ipc.call<bool>(\n      \"is_blink_parse_html_manipulator_available\");\n  if (result.valid() && result.wait_for(std::chrono::milliseconds(20)) ==\n                            std::future_status::ready) {\n    return true;\n  } else {\n    ELOGFMT(\n        DEBUG,\n        \"BlinkParseHTMLManipulator: Availability check failed or timed out\");\n    return false;\n  }\n}\n\nvoid blink_parse_html_manipulator::register_js() {\n  ELOGFMT(INFO, \"BlinkParseHTMLManipulator: Registering JavaScript handlers\");\n\n  context::current->process_ipc.add_call_handler<\n      std::string,\n      std::string>(\"on_blink_parse_html_manipulate\", [](const std::string\n                                                            &_html) {\n    auto html = _html;\n    size_t manipulator_count = 0;\n\n    for (auto &manipulator : blink_parse_html_manipulators) {\n      try {\n        if (auto res = manipulator(html); !res.empty()) {\n          html = res;\n        }\n      } catch (const std::exception &e) {\n        ELOGFMT(ERROR,\n                \"BlinkParseHTMLManipulator: Exception in manipulator {}: {}\",\n                manipulator_count, e.what());\n      } catch (...) {\n        ELOGFMT(\n            ERROR,\n            \"BlinkParseHTMLManipulator: Unknown exception in manipulator {}\",\n            manipulator_count);\n      }\n      manipulator_count++;\n    }\n\n    if (manipulator_count > 0) {\n      ELOGFMT(INFO, \"BlinkParseHTMLManipulator: Processed {} manipulators\",\n              manipulator_count);\n    }\n\n    return html;\n  });\n\n  context::current->script->ctx.on_bind.push_back([]() {\n    ELOGFMT(DEBUG,\n            \"BlinkParseHTMLManipulator: Clearing manipulators on script bind\");\n    blink_parse_html_manipulators.clear();\n  });\n}\n} // namespace chromatic\n"
  },
  {
    "path": "src/hooks/blink_parse_html_manipulator.h",
    "content": "#pragma once\n#include <functional>\n#include <list>\n#include <memory>\n\nnamespace chromatic {\n    struct blink_parse_html_manipulator {\n      static void install();\n      static bool is_available();\n      static void register_js();\n    };\n}"
  },
  {
    "path": "src/hooks/disable-integrity.cc",
    "content": "#include \"disable-integrity.h\"\n#include \"blook/blook.h\"\n\n#include \"Windows.h\"\n#include <memory>\n\n#include \"processthreadsapi.h\"\nbool chromatic::hooks::windows::disable_integrity() {\n  static std::shared_ptr<blook::InlineHook> disable_integrity_hook;\n  auto func = (blook::Pointer)(void *)GetProcAddress(\n      LoadLibraryA(\"Kernel32.dll\"), \"UpdateProcThreadAttribute\");\n\n  if (!func) {\n    return false;\n  }\n\n  disable_integrity_hook = func.as_function().inline_hook();\n  disable_integrity_hook->install((\n      void *)+[](LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, DWORD dwFlags,\n                 DWORD_PTR Attribute, PVOID lpValue, SIZE_T cbSize,\n                 PVOID lpPreviousValue, PSIZE_T lpReturnSize) {\n    if (Attribute == PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY &&\n        cbSize >= sizeof(DWORD64)) {\n      PDWORD64 old_policy = &((PDWORD64)lpValue)[0];\n      *old_policy &= ~(\n          DWORD64)(PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON);\n      *old_policy &=\n          (DWORD64)(PROCESS_CREATION_MITIGATION_POLICY_WIN32K_SYSTEM_CALL_DISABLE_ALWAYS_ON);\n    }\n    return disable_integrity_hook\n        ->trampoline_t<int64_t(LPPROC_THREAD_ATTRIBUTE_LIST, DWORD, DWORD_PTR,\n                               PVOID, SIZE_T, PVOID, PSIZE_T)>()(\n            lpAttributeList, dwFlags, Attribute, lpValue, cbSize,\n            lpPreviousValue, lpReturnSize);\n  });\n  return true;\n}\n"
  },
  {
    "path": "src/hooks/disable-integrity.h",
    "content": "#pragma once\n\nnamespace chromatic::hooks::windows {\n    bool disable_integrity();\n}"
  },
  {
    "path": "src/hooks/wait_for_module_load.cc",
    "content": "#include \"wait_for_module_load.h\"\n#include \"../utils.h\"\n#include \"blook/blook.h\"\n#include <algorithm>\n#include <atomic>\n#include <functional>\n#include <list>\n\n#include \"Windows.h\"\n#include \"blook/process.h\"\nnamespace chromatic::hooks::wait_for_module_load {\nstruct WaitTask {\n  std::function<bool(std::filesystem::path)> verifier;\n  std::promise<std::shared_ptr<blook::Module>> promise;\n\n  WaitTask(std::function<bool(std::filesystem::path)> v,\n           std::promise<std::shared_ptr<blook::Module>> p)\n      : verifier(std::move(v)), promise(std::move(p)) {}\n};\n\nstatic std::list<std::unique_ptr<WaitTask>> wait_tasks;\n\nstatic void process_module_load(HMODULE mod) {\n  auto module_path = utils::get_module_path(mod);\n  for (auto it = wait_tasks.begin(); it != wait_tasks.end();) {\n    if ((*it)->verifier(module_path)) {\n      (*it)->promise.set_value(blook::Process::self()\n                                   ->module(module_path.filename().string())\n                                   .value());\n      it = wait_tasks.erase(it);\n    } else {\n      ++it;\n    }\n  }\n}\n\nstatic void ensure_loadlibrary_hooks() {\n  static std::atomic_bool installed;\n  if (installed.exchange(true))\n    return;\n\n  auto self = blook::Process::self();\n  auto kernel32 = self->module(\"kernel32.dll\").value();\n\n  static auto LoadLibraryExAHook =\n                  kernel32->exports(\"LoadLibraryExA\")->inline_hook(),\n              LoadLibraryExWHook =\n                  kernel32->exports(\"LoadLibraryExW\")->inline_hook();\n\n  LoadLibraryExAHook->install(+[]([in] LPCSTR lpLibFileName,\n                                  HANDLE hFile, [in] DWORD dwFlags) -> HMODULE {\n    auto mod = LoadLibraryExAHook->call_trampoline<HMODULE>(lpLibFileName,\n                                                            hFile, dwFlags);\n    process_module_load(mod);\n    return mod;\n  });\n\n  LoadLibraryExWHook->install(+[]([in] LPCWSTR lpLibFileName,\n                                  HANDLE hFile, [in] DWORD dwFlags) -> HMODULE {\n    auto mod = LoadLibraryExWHook->call_trampoline<HMODULE>(lpLibFileName,\n                                                            hFile, dwFlags);\n    process_module_load(mod);\n    return mod;\n  });\n}\n\nstd::future<std::shared_ptr<blook::Module>>\nwait_for_module(std::function<bool(std::filesystem::path)> verifier) {\n  ensure_loadlibrary_hooks();\n\n  std::promise<std::shared_ptr<blook::Module>> promise;\n  auto future = promise.get_future();\n\n  wait_tasks.emplace_back(\n      std::make_unique<WaitTask>(std::move(verifier), std::move(promise)));\n\n  return future;\n}\n\nstatic std::future<std::shared_ptr<blook::Module>>\nwait_for_module(std::string_view module_name,\n                std::function<void(void *)> callback) {\n  if (module_name.empty()) {\n    throw std::invalid_argument(\"Module name cannot be empty\");\n  }\n\n  if (GetModuleHandleA(module_name.data()) != nullptr) {\n    return std::async(std::launch::deferred, [&, module_name = std::string(\n                                                     module_name)]() {\n      callback(GetModuleHandleA(module_name.c_str()));\n      return blook::Process::self()->module(std::string(module_name)).value();\n    });\n  }\n  return wait_for_module([module_name, callback = std::move(callback)](\n                             std::filesystem::path path) {\n    callback(GetModuleHandleW(path.filename().c_str()));\n    return path.filename() == module_name;\n  });\n};\n} // namespace chromatic::hooks::wait_for_module_load"
  },
  {
    "path": "src/hooks/wait_for_module_load.h",
    "content": "#pragma once\n\n#include \"blook/module.h\"\n#include <functional>\n#include <future>\nnamespace chromatic::hooks::wait_for_module_load {\nstd::future<std::shared_ptr<blook::Module>>\nwait_for_module(std::function<bool(std::filesystem::path)> verifier);\nstd::future<std::shared_ptr<blook::Module>>\nwait_for_module(std::string_view module_name, std::function<void(void*)> callback = [](void*) {});\n} // namespace chromatic::hooks::wait_for_module_load\n"
  },
  {
    "path": "src/script/bindings/binding_qjs.h",
    "content": "// This file is generated by Breeze.JS Bindgen (https://github.com/breeze-shell/breeze-js-bindgen)\n// Do not modify this file manually!\n\n#pragma once\n#include \"binding_types.h\"\n#include \"quickjspp.hpp\"\n\ntemplate <typename T>\nstruct js_bind {\n    static void bind(qjs::Context::Module &mod) {}\n};\n\ntemplate <> struct qjs::js_traits<chromatic::js::chrome> {\n    static chromatic::js::chrome unwrap(JSContext *ctx, JSValueConst v) {\n        chromatic::js::chrome obj;\n\n        return obj;\n    }\n\n    static JSValue wrap(JSContext *ctx, const chromatic::js::chrome &val) noexcept {\n        JSValue obj = JS_NewObject(ctx);\n\n        return obj;\n    }\n};\ntemplate<> struct js_bind<chromatic::js::chrome> {\n    static void bind(qjs::Context::Module &mod) {\n        mod.class_<chromatic::js::chrome>(\"chrome\")\n            .constructor<>()\n            ;\n    }\n};\n\ntemplate <> struct qjs::js_traits<chromatic::js::chrome::blink> {\n    static chromatic::js::chrome::blink unwrap(JSContext *ctx, JSValueConst v) {\n        chromatic::js::chrome::blink obj;\n\n        return obj;\n    }\n\n    static JSValue wrap(JSContext *ctx, const chromatic::js::chrome::blink &val) noexcept {\n        JSValue obj = JS_NewObject(ctx);\n\n        return obj;\n    }\n};\ntemplate<> struct js_bind<chromatic::js::chrome::blink> {\n    static void bind(qjs::Context::Module &mod) {\n        mod.class_<chromatic::js::chrome::blink>(\"chrome::blink\")\n            .constructor<>()\n                .static_fun<&chromatic::js::chrome::blink::add_blink_parse_html_manipulator>(\"add_blink_parse_html_manipulator\")\n                .static_fun<&chromatic::js::chrome::blink::is_parse_html_manipulator_available>(\"is_parse_html_manipulator_available\")\n            ;\n    }\n};\n\ntemplate <> struct qjs::js_traits<chromatic::js::chrome::blink::blink_parse_manipulate_context> {\n    static chromatic::js::chrome::blink::blink_parse_manipulate_context unwrap(JSContext *ctx, JSValueConst v) {\n        chromatic::js::chrome::blink::blink_parse_manipulate_context obj;\n\n        obj.html = js_traits<std::string>::unwrap(ctx, JS_GetPropertyStr(ctx, v, \"html\"));\n\n        obj.url = js_traits<std::string>::unwrap(ctx, JS_GetPropertyStr(ctx, v, \"url\"));\n\n        return obj;\n    }\n\n    static JSValue wrap(JSContext *ctx, const chromatic::js::chrome::blink::blink_parse_manipulate_context &val) noexcept {\n        JSValue obj = JS_NewObject(ctx);\n\n        JS_SetPropertyStr(ctx, obj, \"html\", js_traits<std::string>::wrap(ctx, val.html));\n\n        JS_SetPropertyStr(ctx, obj, \"url\", js_traits<std::string>::wrap(ctx, val.url));\n\n        return obj;\n    }\n};\ntemplate<> struct js_bind<chromatic::js::chrome::blink::blink_parse_manipulate_context> {\n    static void bind(qjs::Context::Module &mod) {\n        mod.class_<chromatic::js::chrome::blink::blink_parse_manipulate_context>(\"chrome::blink::blink_parse_manipulate_context\")\n            .constructor<>()\n                .fun<&chromatic::js::chrome::blink::blink_parse_manipulate_context::html>(\"html\")\n                .fun<&chromatic::js::chrome::blink::blink_parse_manipulate_context::url>(\"url\")\n            ;\n    }\n};\n\ntemplate <> struct qjs::js_traits<chromatic::js::infra> {\n    static chromatic::js::infra unwrap(JSContext *ctx, JSValueConst v) {\n        chromatic::js::infra obj;\n\n        return obj;\n    }\n\n    static JSValue wrap(JSContext *ctx, const chromatic::js::infra &val) noexcept {\n        JSValue obj = JS_NewObject(ctx);\n\n        return obj;\n    }\n};\ntemplate<> struct js_bind<chromatic::js::infra> {\n    static void bind(qjs::Context::Module &mod) {\n        mod.class_<chromatic::js::infra>(\"infra\")\n            .constructor<>()\n                .static_fun<&chromatic::js::infra::log>(\"log\")\n            ;\n    }\n};\n\ninline void bindAll(qjs::Context::Module &mod) {\n\n    js_bind<chromatic::js::chrome>::bind(mod);\n\n    js_bind<chromatic::js::chrome::blink>::bind(mod);\n\n    js_bind<chromatic::js::chrome::blink::blink_parse_manipulate_context>::bind(mod);\n\n    js_bind<chromatic::js::infra>::bind(mod);\n\n}\n"
  },
  {
    "path": "src/script/bindings/binding_types.cc",
    "content": "#include \"binding_types.h\"\n#include <list>\n\n#include \"breeze-js/quickjspp.hpp\"\n#include \"ylt/easylog.hpp\"\n\n#include \"../../context.h\"\n\nstd::list<std::function<std::string(std::string)>>\n    blink_parse_html_manipulators;\nnamespace chromatic::js {\nvoid chrome::blink::add_blink_parse_html_manipulator(\n    std::function<std::string(std::string)> manipulator) {\n\n  blink_parse_html_manipulators.push_back([manipulator](std::string ctx) {\n    try {\n      return manipulator(ctx);\n    } catch (const std::exception &e) {\n      ELOGFMT(ERROR, \"Exception in blink parse HTML manipulator: {}\", e.what());\n      return std::string();\n    } catch (...) {\n      ELOGFMT(ERROR, \"Unknown exception in blink parse HTML manipulator\");\n      return std::string();\n    }\n  });\n}\nvoid infra::log(qjs::rest<std::string> msg) {\n  std::string log_msg;\n  for (const auto &m : msg) {\n    log_msg += m + \" \";\n  }\n  log_msg.pop_back();\n\n  ELOGFMT(INFO, \"{}\", log_msg);\n}\n} // namespace chromatic::js"
  },
  {
    "path": "src/script/bindings/binding_types.d.ts",
    "content": "// This file is generated by bindgen\n// Do not modify this file manually!\n\ndeclare module 'chromatic' {\n\nexport class chrome {\n}\nnamespace chrome {\nexport class blink {\n\t/**\n     * \n     * @param arg0: ((arg1: string) => string)\n     * @returns void\n     */\n    static add_blink_parse_html_manipulator(arg0: ((arg1: string) => string)): void\n\tstatic is_parse_html_manipulator_available(): boolean\n}\n}\nnamespace chrome.blink {\nexport class blink_parse_manipulate_context {\n\thtml: string\n\turl: string\n}\n}\nexport class infra {\n\t/**\n     * \n     * @param msg: qjs.rest<string>\n     * @returns void\n     */\n    static log(msg: qjs.rest<string>): void\n}\n}\n\n"
  },
  {
    "path": "src/script/bindings/binding_types.h",
    "content": "#pragma once\n\n#include <functional>\n#include <memory>\n#include <optional>\n#include <string>\n\n#include \"../../hooks/blink_parse_html_manipulator.h\"\n\nnamespace qjs {\ntemplate <typename T> struct rest;\n}\n\nnamespace chromatic::js {\nstruct chrome {\n  struct blink {\n    struct blink_parse_manipulate_context {\n      std::string html;\n      std::string url;\n    };\n    static void add_blink_parse_html_manipulator(\n        std::function<std::string(std::string)>);\n\n    static bool is_parse_html_manipulator_available() {\n      return chromatic::blink_parse_html_manipulator::is_available();\n    }\n  };\n};\n\nstruct infra {\n  static void log(qjs::rest<std::string> msg);\n};\n} // namespace chromatic::js"
  },
  {
    "path": "src/script/bindings/quickjspp.hpp",
    "content": "#pragma once\n#include \"breeze-js/quickjspp.hpp\""
  },
  {
    "path": "src/script/script.cc",
    "content": "#include \"script.h\"\n\n#include \"../config.h\"\n\n#include \"bindings/binding_qjs.h\"\n#include \"ylt/easylog.hpp\"\n\nnamespace chromatic {\n\nscript_engine::script_engine() {\n\n  js_watch_thread = std::thread([this]() {\n\n    auto script_dir = config::data_directory() / \"scripts\";\n    if (!std::filesystem::exists(script_dir)) {\n      std::filesystem::create_directories(script_dir);\n    }\n\n    ctx.on_bind.push_back([this]() {\n      auto &mod = ctx.js->addModule(\"chromatic\");\n\n      bindAll(mod);\n    });\n\n    ELOGFMT(INFO, \"Script engine initialized.\");\n    ctx.watch_folder(script_dir);\n  });\n}\n\nscript_engine::~script_engine() {\n  if (js_watch_thread.joinable()) {\n    ctx.stop_signal = std::make_shared<int>(1);\n    js_watch_thread.join();\n  }\n}\n} // namespace chromatic"
  },
  {
    "path": "src/script/script.h",
    "content": "#pragma once\n#include \"breeze-js/script.h\"\n\nnamespace chromatic {\nstruct script_engine {\n  breeze::script_context ctx;\n\n  script_engine();\n  ~script_engine();\n\nprivate:\n  std::thread js_watch_thread;\n};\n} // namespace chromatic"
  },
  {
    "path": "src/utils.cc",
    "content": "#include \"utils.h\"\n\n#include \"Windows.h\"\n\nnamespace chromatic::utils {\nstd::optional<std::string> env(const std::string &name) {\n  wchar_t buffer[32767];\n  GetEnvironmentVariableW(utf8_to_wstring(name).c_str(), buffer, 32767);\n  if (buffer[0] == 0) {\n    return std::nullopt;\n  }\n  return wstring_to_utf8(buffer);\n}\n\nstd::string wstring_to_utf8(std::wstring const &str) {\n  int size_needed = WideCharToMultiByte(CP_UTF8, 0, str.c_str(),\n                                        (int)str.size(), NULL, 0, NULL, NULL);\n  std::string result(size_needed, 0);\n  WideCharToMultiByte(CP_UTF8, 0, str.c_str(), (int)str.size(), &result[0],\n                      size_needed, NULL, NULL);\n  return result;\n}\n\nstd::wstring utf8_to_wstring(std::string const &str) {\n  int size_needed =\n      MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), NULL, 0);\n  std::wstring result(size_needed, 0);\n  MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), &result[0],\n                      size_needed);\n  return result;\n}\nstd::filesystem::path current_executable_path() {\n  static std::filesystem::path path = chromatic::utils::get_module_path();\n  return path;\n}\nstd::filesystem::path get_module_path(void *module_handle) {\n  HMODULE hModule = static_cast<HMODULE>(module_handle);\n  wchar_t buffer[MAX_PATH];\n  if (GetModuleFileNameW(hModule, buffer, MAX_PATH) == 0) {\n    return {};\n  }\n\n  return std::filesystem::path(buffer);\n}\n\ntask_queue::task_queue() : stop(false) {\n  worker = std::thread(&task_queue::run, this);\n}\ntask_queue::~task_queue() {\n  {\n    std::lock_guard<std::mutex> lock(queue_mutex);\n    stop = true;\n  }\n  condition.notify_all();\n  if (worker.joinable()) {\n    worker.join();\n  }\n}\nvoid task_queue::run() {\n  while (true) {\n    std::function<void()> task;\n    {\n      std::unique_lock<std::mutex> lock(queue_mutex);\n      condition.wait(lock, [this]() { return stop || !tasks.empty(); });\n\n      if (stop && tasks.empty()) {\n        return;\n      }\n\n      task = std::move(tasks.front());\n      tasks.pop();\n    }\n\n    task();\n  }\n}\n} // namespace chromatic::utils"
  },
  {
    "path": "src/utils.h",
    "content": "#pragma once\n#include <filesystem>\n#include <future>\n#include <optional>\n#include <queue>\n#include <string>\n\n\nnamespace chromatic {\nnamespace utils {\nstd::optional<std::string> env(const std::string &name);\nstd::string wstring_to_utf8(std::wstring const &str);\nstd::wstring utf8_to_wstring(std::string const &str);\nstd::filesystem::path current_executable_path();\nstd::filesystem::path get_module_path(void *module_handle = nullptr);\nstruct task_queue {\npublic:\n  task_queue();\n\n  ~task_queue();\n\n  template <typename F, typename... Args>\n  auto add_task(F &&f, Args &&...args)\n      -> std::future<std::invoke_result_t<F, Args...>> {\n    using return_type = std::invoke_result_t<F, Args...>;\n\n    if (stop) {\n      throw std::runtime_error(\"add_task called on stopped task_queue\");\n    }\n\n    auto task = std::make_shared<std::packaged_task<return_type()>>(\n        std::bind(std::forward<F>(f), std::forward<Args>(args)...));\n\n    std::future<return_type> res = task->get_future();\n\n    {\n      std::lock_guard<std::mutex> lock(queue_mutex);\n      tasks.emplace([task]() { (*task)(); });\n    }\n\n    condition.notify_one();\n    return res;\n  }\n\nprivate:\n  void run();\n\n  std::thread worker;\n  std::queue<std::function<void()>> tasks;\n  std::mutex queue_mutex;\n  std::condition_variable condition;\n  bool stop;\n};\n\n} // namespace utils\n} // namespace chromatic"
  },
  {
    "path": "test/ipc_test.cc",
    "content": "#include \"ipc.h\"\n#include <chrono>\n#include <gtest/gtest.h>\n#include <print>\n#include <thread>\n\nusing namespace chromatic;\n\nTEST(IPCTest, BasicMessageSendReceive) {\n  breeze_ipc ipc1, ipc2;\n  ipc1.connect(\"chromatic://process/1\");\n  ipc2.connect(\"chromatic://process/1\");\n\n  bool received = false;\n  auto remover = ipc2.add_listener(\n      \"test_msg\", [&](const breeze_ipc::packet &pkt) { received = true; });\n\n  ipc1.send(\"test_msg\", test_serializable_struct{1, 2.0f, {'a', 'b', 'c'}});\n\n  std::this_thread::sleep_for(\n      std::chrono::milliseconds(100)); // Give time for IPC to process\n  EXPECT_TRUE(received);\n}\n\nTEST(IPCTest, RPCCall) {\n  breeze_ipc server, client;\n  server.connect(\"chromatic://process/\");\n  client.connect(\"chromatic://process/\");\n\n  auto remover =\n      server.add_call_handler<int, int>(\"add_one\", [](int x) { return x + 1; });\n\n  auto future = client.call<int, int>(\"add_one\", 5);\n\n  EXPECT_EQ(future.get(), 6);\n}\n\nTEST(IPCTest, StringReturnRPC) {\n  breeze_ipc server, client;\n  server.connect(\"chromatic://process/\");\n  client.connect(\"chromatic://process/\");\n\n  auto remover = server.add_call_handler<std::string, std::string>(\n      \"echo\", [](const std::string &s) { return s; });\n\n  auto future = client.call<std::string, std::string>(\"echo\", \"hello world\");\n\n  EXPECT_EQ(future.get(), \"hello world\");\n}\n\nTEST(IPCTest, PairReturnRPC) {\n  breeze_ipc server, client;\n  server.connect(\"chromatic://process/\");\n  client.connect(\"chromatic://process/\");\n\n  auto remover = server.add_call_handler<std::pair<int, std::string>, int>(\n      \"make_pair\", [](int x) { return std::make_pair(x, std::to_string(x)); });\n\n  auto future = client.call<std::pair<int, std::string>, int>(\"make_pair\", 42);\n\n  auto result = future.get();\n  EXPECT_EQ(result.first, 42);\n  EXPECT_EQ(result.second, \"42\");\n}\n\nTEST(IPCTest, StringPairRPC) {\n  breeze_ipc server, client;\n  server.connect(\"chromatic://process/\");\n  client.connect(\"chromatic://process/\");\n\n  auto remover = server.add_call_handler<std::pair<std::string, std::string>,\n                                         std::pair<std::string, std::string>>(\n      \"concat_pair\", [](const auto &p) {\n        return std::make_pair(p.first + p.second, p.second + p.first);\n      });\n\n  auto future = client.call<std::pair<std::string, std::string>,\n                            std::pair<std::string, std::string>>(\n      \"concat_pair\", std::make_pair(\"hello\", \"world\"));\n\n  auto result = future.get();\n  EXPECT_EQ(result.first, \"helloworld\");\n  EXPECT_EQ(result.second, \"worldhello\");\n}\n\nstruct blink_parse_manipulate_context {\n  std::string html;\n  std::string url;\n};\n\nTEST(IPCTest, BlinkContextRPC) {\n  breeze_ipc server, client;\n  server.connect(\"chromatic://process/\");\n  client.connect(\"chromatic://process/\");\n\n  auto remover = server.add_call_handler<blink_parse_manipulate_context,\n                                         blink_parse_manipulate_context>(\n      \"process_html\", [](const auto &ctx) {\n        blink_parse_manipulate_context result = ctx;\n        result.html += \"<!-- processed -->\";\n        return result;\n      });\n\n  blink_parse_manipulate_context original{.html = \"<html>test</html>\",\n                                          .url = \"http://example.com\"};\n\n  auto future =\n      client\n          .call<blink_parse_manipulate_context, blink_parse_manipulate_context>(\n              \"process_html\", original);\n\n  auto result = future.get();\n  EXPECT_EQ(result.html, \"<html>test</html><!-- processed -->\");\n  EXPECT_EQ(result.url, \"http://example.com\");\n}\n\nTEST(IPCTest, Serialization) {\n  test_serializable_struct original{42, 3.14f, {'x', 'y', 'z'}};\n  auto serialized = struct_pack::serialize(original);\n  auto deserialized =\n      struct_pack::deserialize<test_serializable_struct>(serialized);\n\n  ASSERT_TRUE(deserialized.has_value());\n  EXPECT_EQ(deserialized->a, 42);\n  EXPECT_FLOAT_EQ(deserialized->b, 3.14f);\n  auto vec = std::vector<char>{'x', 'y', 'z'};\n  EXPECT_EQ(deserialized->c, vec);\n}\n\nTEST(IPCTest, ALotOfPackets) {\n  breeze_ipc server, client;\n  server.connect(\"chromatic://process/\");\n  client.connect(\"chromatic://process/\");\n\n  int count = 1000;\n  int received = 0;\n\n  auto remover = server.add_listener(\n      \"test_msg\", [&](const breeze_ipc::packet &pkt) { received++; });\n\n  for (int i = 0; i < count; ++i) {\n    client.send(\"test_msg\", test_serializable_struct{i, i * 1.1f, {'a', 'b'}});\n  }\n\n  std::this_thread::sleep_for(\n      std::chrono::seconds(1)); // Give time for IPC to process\n  EXPECT_EQ(received, count);\n}\n\nTEST(IPCTest, LargePacket) {\n  breeze_ipc server, client;\n  server.connect(\"chromatic://process/\");\n  client.connect(\"chromatic://process/\");\n\n  // ~1MB of data\n  std::string large_data(1024 * 1024, 'x');\n\n  auto remover = server.add_call_handler<std::string, std::string>(\n      \"echo_large\", [](const std::string &data) { \n        std::println(\"Received large data of size: {}\", data.size());\n        return data;\n       });\n\n  auto future = client.call<std::string, std::string>(\"echo_large\", large_data);\n\n  EXPECT_EQ(future.get(), large_data);\n}\n\nint main(int argc, char **argv) {\n\n  // auto channel = breeze_ipc{};\n  // channel.connect(\"chromatic://process/\");\n\n  // channel.add_call_handler<std::string, std::string>(\n  //     \"on_blink_parse_html_manipulate\",\n  //     [](const std::string &ctx) {\n  //       std::println(\"on_blink_parse_html_manipulate called with: {}\", ctx);\n  //       return \"Processed: \" + ctx;\n  //     });\n\n  // while (1)\n  //   ;\n\n  std::string arg(argc > 1 ? argv[1] : \"\");\n  if (arg == \"inspect\") {\n    ipc::Channel channel;\n    channel.connect(\"chromatic://process/\");\n\n    std::cout << \"Connected to IPC channel.\" << std::endl;\n\n    std::thread input_thread([&channel]() {\n      while (true) {\n        std::string input;\n        std::getline(std::cin, input);\n        channel.send(input);\n      }\n    });\n\n    while (true) {\n      std::string data;\n      channel.try_receive(data);\n      if (!data.empty()) {\n        std::cout << \"\\033[47;30m[\"\n                  << std::chrono::system_clock::now().time_since_epoch().count()\n                  << \"] \\033[47;0m\";\n\n        if (*(char *)data.data() == '{') {\n          // Assuming it's a JSON string\n          std::string json_str((char *)data.data(), data.size());\n          std::println(\"JSON: {}\", json_str);\n        } else {\n          // Print as hex\n          std::cout << \"Hex: \";\n          for (size_t i = 0; i < data.size(); ++i) {\n            if (i % 16 == 0 && i != 0) {\n              std::cout << \"\\n\";\n            }\n            std::cout << std::hex << static_cast<int>(((char *)data.data())[i])\n                      << \" \";\n          }\n          std::cout << std::dec << \"\\n\";\n        }\n      }\n    }\n  } else {\n    try {\n      testing::InitGoogleTest(&argc, argv);\n      return RUN_ALL_TESTS();\n    } catch (const std::exception &e) {\n      std::cerr << \"Exception during test initialization: \" << e.what()\n                << std::endl;\n      return 1;\n    } catch (...) {\n      std::cerr << \"Unknown exception during test initialization.\" << std::endl;\n      return 1;\n    }\n  }\n}"
  },
  {
    "path": "xmake.lua",
    "content": "set_project(\"chromatic\")\nset_policy(\"compatibility.version\", \"3.0\")\n\nset_languages(\"c++23\")\nset_warnings(\"all\")\nadd_rules(\"plugin.compile_commands.autoupdate\", {outputdir = \"build\"})\n\nadd_rules(\"mode.releasedbg\")\n\nincludes(\"deps/blook.lua\")\nincludes(\"deps/breeze-js.lua\")\nincludes(\"deps/cpp-ipc.lua\")\n\nadd_requires(\"yalantinglibs 0c98464dd202aaa6275a8da3297719a436b8a51a\", {\n    configs = {\n        ssl = true\n    }\n})\n\nadd_requireconfs(\"**.cinatra\", {\n    override = true,\n    version = \"e329293f6705649a6f1e8847ec845a7631179bb8\"\n})\n\nadd_requireconfs(\"**.async_simple\", {\n    override = true,\n    version = \"18f3882be354d407af0f0674121dcddaeff36e26\"\n})\n\nadd_requires(\"blook\", \"breeze-js\", \"reflect-cpp\", \"cpptrace v0.8.3\", \"gtest\")\nset_runtimes(\"MT\")\n\ntarget(\"chromatic_ipc\")\n    set_kind(\"static\")\n    add_defines(\"NOMINMAX\")\n    add_packages(\"yalantinglibs\", \"reflect-cpp\", {\n        public = true,\n    })\n    add_files(\"ipc/ipc.cc\")\n    add_headerfiles(\"ipc/ipc.h\")\n    add_includedirs(\"ipc\", {public = true})\n    set_encodings(\"utf-8\")\n\ntarget(\"chromatic\")\n    set_kind(\"shared\")\n    add_defines(\"NOMINMAX\")\n    add_packages(\"blook\", \"breeze-js\", \"reflect-cpp\", \"yalantinglibs\", \"cpptrace\")\n    add_syslinks(\"oleacc\", \"ole32\", \"oleaut32\", \"uuid\", \"comctl32\", \"comdlg32\", \"gdi32\", \"user32\", \"shell32\", \"kernel32\", \"advapi32\", \"psapi\")\n    add_files(\"src/**/*.cc\", \"src/*.cc\")\n    remove_files(\"src/ipc.cc\")\n    add_deps(\"chromatic_ipc\")\n    set_encodings(\"utf-8\")\n\ntarget(\"chromatic_ipc_test\")\n    set_kind(\"binary\")\n    add_deps(\"chromatic_ipc\")\n    add_packages(\"gtest\", \"yalantinglibs\")\n    add_files(\"test/ipc_test.cc\")\n    add_includedirs(\"src\")\n    set_encodings(\"utf-8\")"
  }
]