#include "KernelDynDataProfiles.h"

#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <Windows.h>

#include <algorithm>
#include <array>
#include <cctype>
#include <charconv>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cwctype>
#include <filesystem>
#include <fstream>
#include <iterator>
#include <limits>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <vector>

namespace Ksword::Features::Kernel {
namespace {

// JsonValue is a deliberately small DOM that covers the profile-pack grammar.
// Inputs are produced by JsonParser below; processing code reads object members,
// arrays, strings, and numbers; return behavior is value-only and local to this
// file so KswordARKLight does not grow a third-party JSON dependency.
struct JsonValue {
    enum class Type { Null, Bool, Number, String, Array, Object };

    Type type = Type::Null;
    bool boolValue = false;
    double numberValue = 0.0;
    std::string stringValue;
    std::vector<JsonValue> arrayValue;
    std::map<std::string, JsonValue> objectValue;

    bool isObject() const noexcept { return type == Type::Object; }
    bool isArray() const noexcept { return type == Type::Array; }
    bool isString() const noexcept { return type == Type::String; }
    bool isNumber() const noexcept { return type == Type::Number; }

    const JsonValue* member(const std::string& name) const {
        const auto found = objectValue.find(name);
        return found == objectValue.end() ? nullptr : &found->second;
    }
};

// AppendUtf8Codepoint writes one \u escape code point as UTF-8. Input is a
// Unicode scalar value; processing intentionally replaces invalid scalar ranges
// with '?' because pack keys are ASCII and diagnostics should never throw;
// output is appended to the destination string.
void AppendUtf8Codepoint(std::string& out, const std::uint32_t codepoint) {
    if (codepoint <= 0x7FU) {
        out.push_back(static_cast<char>(codepoint));
    } else if (codepoint <= 0x7FFU) {
        out.push_back(static_cast<char>(0xC0U | (codepoint >> 6U)));
        out.push_back(static_cast<char>(0x80U | (codepoint & 0x3FU)));
    } else if (codepoint <= 0xFFFFU) {
        out.push_back(static_cast<char>(0xE0U | (codepoint >> 12U)));
        out.push_back(static_cast<char>(0x80U | ((codepoint >> 6U) & 0x3FU)));
        out.push_back(static_cast<char>(0x80U | (codepoint & 0x3FU)));
    } else if (codepoint <= 0x10FFFFU) {
        out.push_back(static_cast<char>(0xF0U | (codepoint >> 18U)));
        out.push_back(static_cast<char>(0x80U | ((codepoint >> 12U) & 0x3FU)));
        out.push_back(static_cast<char>(0x80U | ((codepoint >> 6U) & 0x3FU)));
        out.push_back(static_cast<char>(0x80U | (codepoint & 0x3FU)));
    } else {
        out.push_back('?');
    }
}

// JsonParser is a bounded recursive-descent parser for the compact profile
// packs generated by the existing release tooling. Input is UTF-8 JSON text;
// processing accepts standard objects/arrays/strings/numbers/literals; return
// behavior is a bool plus an error string instead of exceptions.
class JsonParser {
public:
    explicit JsonParser(std::string_view text) : text_(text) {}

    bool parse(JsonValue& valueOut, std::string& errorOut) {
        skipWhitespace();
        if (!parseValue(valueOut, errorOut)) {
            return false;
        }
        skipWhitespace();
        if (pos_ != text_.size()) {
            errorOut = "unexpected trailing JSON data";
            return false;
        }
        return true;
    }

private:
    void skipWhitespace() {
        while (pos_ < text_.size()) {
            const unsigned char ch = static_cast<unsigned char>(text_[pos_]);
            if (ch != ' ' && ch != '\t' && ch != '\r' && ch != '\n') {
                break;
            }
            ++pos_;
        }
    }

    bool parseValue(JsonValue& valueOut, std::string& errorOut) {
        skipWhitespace();
        if (pos_ >= text_.size()) {
            errorOut = "unexpected end of JSON";
            return false;
        }

        const char ch = text_[pos_];
        if (ch == '{') {
            return parseObject(valueOut, errorOut);
        }
        if (ch == '[') {
            return parseArray(valueOut, errorOut);
        }
        if (ch == '"') {
            valueOut.type = JsonValue::Type::String;
            return parseString(valueOut.stringValue, errorOut);
        }
        if (ch == '-' || (ch >= '0' && ch <= '9')) {
            return parseNumber(valueOut, errorOut);
        }
        if (consumeLiteral("true")) {
            valueOut.type = JsonValue::Type::Bool;
            valueOut.boolValue = true;
            return true;
        }
        if (consumeLiteral("false")) {
            valueOut.type = JsonValue::Type::Bool;
            valueOut.boolValue = false;
            return true;
        }
        if (consumeLiteral("null")) {
            valueOut.type = JsonValue::Type::Null;
            return true;
        }

        errorOut = "unexpected JSON token";
        return false;
    }

    bool parseObject(JsonValue& valueOut, std::string& errorOut) {
        valueOut = JsonValue{};
        valueOut.type = JsonValue::Type::Object;
        ++pos_;
        skipWhitespace();
        if (consume('}')) {
            return true;
        }

        while (pos_ < text_.size()) {
            std::string key;
            if (!parseString(key, errorOut)) {
                return false;
            }
            skipWhitespace();
            if (!consume(':')) {
                errorOut = "object member missing ':'";
                return false;
            }

            JsonValue memberValue;
            if (!parseValue(memberValue, errorOut)) {
                return false;
            }
            valueOut.objectValue[std::move(key)] = std::move(memberValue);
            skipWhitespace();
            if (consume('}')) {
                return true;
            }
            if (!consume(',')) {
                errorOut = "object member missing ','";
                return false;
            }
            skipWhitespace();
        }

        errorOut = "unterminated JSON object";
        return false;
    }

    bool parseArray(JsonValue& valueOut, std::string& errorOut) {
        valueOut = JsonValue{};
        valueOut.type = JsonValue::Type::Array;
        ++pos_;
        skipWhitespace();
        if (consume(']')) {
            return true;
        }

        while (pos_ < text_.size()) {
            JsonValue item;
            if (!parseValue(item, errorOut)) {
                return false;
            }
            valueOut.arrayValue.push_back(std::move(item));
            skipWhitespace();
            if (consume(']')) {
                return true;
            }
            if (!consume(',')) {
                errorOut = "array item missing ','";
                return false;
            }
            skipWhitespace();
        }

        errorOut = "unterminated JSON array";
        return false;
    }

    bool parseString(std::string& valueOut, std::string& errorOut) {
        valueOut.clear();
        if (!consume('"')) {
            errorOut = "expected JSON string";
            return false;
        }

        while (pos_ < text_.size()) {
            const char ch = text_[pos_++];
            if (ch == '"') {
                return true;
            }
            if (ch != '\\') {
                valueOut.push_back(ch);
                continue;
            }
            if (pos_ >= text_.size()) {
                errorOut = "unterminated JSON escape";
                return false;
            }
            const char escaped = text_[pos_++];
            switch (escaped) {
            case '"': valueOut.push_back('"'); break;
            case '\\': valueOut.push_back('\\'); break;
            case '/': valueOut.push_back('/'); break;
            case 'b': valueOut.push_back('\b'); break;
            case 'f': valueOut.push_back('\f'); break;
            case 'n': valueOut.push_back('\n'); break;
            case 'r': valueOut.push_back('\r'); break;
            case 't': valueOut.push_back('\t'); break;
            case 'u': {
                std::uint32_t codepoint = 0;
                if (!parseHex4(codepoint)) {
                    errorOut = "invalid JSON unicode escape";
                    return false;
                }
                AppendUtf8Codepoint(valueOut, codepoint);
                break;
            }
            default:
                errorOut = "invalid JSON escape";
                return false;
            }
        }

        errorOut = "unterminated JSON string";
        return false;
    }

    bool parseNumber(JsonValue& valueOut, std::string& errorOut) {
        const std::size_t start = pos_;
        if (text_[pos_] == '-') {
            ++pos_;
        }
        if (pos_ >= text_.size()) {
            errorOut = "invalid JSON number";
            return false;
        }
        if (text_[pos_] == '0') {
            ++pos_;
        } else if (text_[pos_] >= '1' && text_[pos_] <= '9') {
            while (pos_ < text_.size() && text_[pos_] >= '0' && text_[pos_] <= '9') {
                ++pos_;
            }
        } else {
            errorOut = "invalid JSON number";
            return false;
        }
        if (pos_ < text_.size() && text_[pos_] == '.') {
            ++pos_;
            if (pos_ >= text_.size() || text_[pos_] < '0' || text_[pos_] > '9') {
                errorOut = "invalid JSON number fraction";
                return false;
            }
            while (pos_ < text_.size() && text_[pos_] >= '0' && text_[pos_] <= '9') {
                ++pos_;
            }
        }
        if (pos_ < text_.size() && (text_[pos_] == 'e' || text_[pos_] == 'E')) {
            ++pos_;
            if (pos_ < text_.size() && (text_[pos_] == '+' || text_[pos_] == '-')) {
                ++pos_;
            }
            if (pos_ >= text_.size() || text_[pos_] < '0' || text_[pos_] > '9') {
                errorOut = "invalid JSON exponent";
                return false;
            }
            while (pos_ < text_.size() && text_[pos_] >= '0' && text_[pos_] <= '9') {
                ++pos_;
            }
        }

        const std::string numberText(text_.substr(start, pos_ - start));
        char* end = nullptr;
        const double parsed = std::strtod(numberText.c_str(), &end);
        if (end == nullptr || *end != '\0') {
            errorOut = "number conversion failed";
            return false;
        }
        valueOut = JsonValue{};
        valueOut.type = JsonValue::Type::Number;
        valueOut.numberValue = parsed;
        return true;
    }

    bool parseHex4(std::uint32_t& codepointOut) {
        if (pos_ + 4U > text_.size()) {
            return false;
        }
        std::uint32_t result = 0;
        for (int i = 0; i < 4; ++i) {
            const char ch = text_[pos_++];
            result <<= 4U;
            if (ch >= '0' && ch <= '9') {
                result |= static_cast<std::uint32_t>(ch - '0');
            } else if (ch >= 'a' && ch <= 'f') {
                result |= static_cast<std::uint32_t>(10 + ch - 'a');
            } else if (ch >= 'A' && ch <= 'F') {
                result |= static_cast<std::uint32_t>(10 + ch - 'A');
            } else {
                return false;
            }
        }
        codepointOut = result;
        return true;
    }

    bool consume(const char ch) {
        if (pos_ < text_.size() && text_[pos_] == ch) {
            ++pos_;
            return true;
        }
        return false;
    }

    bool consumeLiteral(std::string_view literal) {
        if (text_.substr(pos_, literal.size()) == literal) {
            pos_ += literal.size();
            return true;
        }
        return false;
    }

    std::string_view text_;
    std::size_t pos_ = 0;
};

// WideToUtf8 converts Win32/NT diagnostic text into narrow profile metadata.
// Input is a UTF-16 string; processing uses Windows UTF-8 conversion and falls
// back to byte truncation for impossible input; output is safe for client model
// metadata fields that are transported as char arrays.
std::string WideToUtf8(const std::wstring& value) {
    if (value.empty()) {
        return {};
    }
    const int needed = ::WideCharToMultiByte(CP_UTF8, 0, value.c_str(), static_cast<int>(value.size()), nullptr, 0, nullptr, nullptr);
    if (needed <= 0) {
        std::string fallback;
        fallback.reserve(value.size());
        for (const wchar_t ch : value) {
            fallback.push_back(ch <= 0x7F ? static_cast<char>(ch) : '?');
        }
        return fallback;
    }
    std::string result(static_cast<std::size_t>(needed), '\0');
    ::WideCharToMultiByte(CP_UTF8, 0, value.c_str(), static_cast<int>(value.size()), result.data(), needed, nullptr, nullptr);
    return result;
}

// Utf8ToWide converts profile-pack text into Win32 text. Input is UTF-8 stored
// in JSON; processing falls back to byte widening on invalid sequences; output
// can be displayed directly in KernelPage rows.
std::wstring Utf8ToWideLocal(const std::string& value) {
    if (value.empty()) {
        return {};
    }
    const int needed = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, value.c_str(), static_cast<int>(value.size()), nullptr, 0);
    if (needed <= 0) {
        std::wstring fallback;
        fallback.reserve(value.size());
        for (const unsigned char ch : value) {
            fallback.push_back(static_cast<wchar_t>(ch));
        }
        return fallback;
    }
    std::wstring result(static_cast<std::size_t>(needed), L'\0');
    ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, value.c_str(), static_cast<int>(value.size()), result.data(), needed);
    return result;
}

// HexTextLocal formats values without depending on KernelFacade internals. Input
// is a numeric diagnostic; output is uppercase hex text for match/detail rows.
std::wstring HexTextLocal(const std::uint64_t value) {
    std::wostringstream stream;
    stream << L"0x" << std::hex << std::uppercase << value;
    return stream.str();
}

// JoinWide concatenates short diagnostics. Input is a vector of display strings;
// processing inserts a separator between non-empty values; output is one detail
// line suitable for KernelOperationResult::message or row detail text.
std::wstring JoinWide(const std::vector<std::wstring>& values, const std::wstring& separator) {
    std::wstring result;
    for (const std::wstring& value : values) {
        if (value.empty()) {
            continue;
        }
        if (!result.empty()) {
            result += separator;
        }
        result += value;
    }
    return result;
}

// LowerAscii lowercases stable pack names/kinds. Input is ASCII-ish JSON text;
// processing avoids locale; output is used for case-insensitive maps.
std::string LowerAscii(std::string value) {
    for (char& ch : value) {
        ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
    }
    return value;
}

// ParseUInt32 reads compact pack numbers that may be JSON numbers or decimal/hex
// strings. Input is a JSON value; processing checks unsigned 32-bit range;
// output is true with valueOut set only when parsing is exact enough for driver
// protocol fields.
bool ParseUInt32(const JsonValue* value, std::uint32_t& valueOut) {
    valueOut = 0;
    if (value == nullptr) {
        return false;
    }
    if (value->isNumber()) {
        if (value->numberValue < 0.0 || value->numberValue > static_cast<double>(std::numeric_limits<std::uint32_t>::max())) {
            return false;
        }
        const double truncated = static_cast<double>(static_cast<std::uint32_t>(value->numberValue));
        if (truncated != value->numberValue) {
            return false;
        }
        valueOut = static_cast<std::uint32_t>(value->numberValue);
        return true;
    }
    if (!value->isString()) {
        return false;
    }

    std::string text = value->stringValue;
    text.erase(text.begin(), std::find_if(text.begin(), text.end(), [](unsigned char ch) { return !std::isspace(ch); }));
    text.erase(std::find_if(text.rbegin(), text.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), text.end());
    if (text.empty()) {
        return false;
    }

    int base = 10;
    if (text.size() > 2 && text[0] == '0' && (text[1] == 'x' || text[1] == 'X')) {
        text.erase(0, 2);
        base = 16;
    }

    std::uint64_t parsed = 0;
    const auto result = std::from_chars(text.data(), text.data() + text.size(), parsed, base);
    if (result.ec != std::errc{} || result.ptr != text.data() + text.size() || parsed > std::numeric_limits<std::uint32_t>::max()) {
        return false;
    }
    valueOut = static_cast<std::uint32_t>(parsed);
    return true;
}

// ParseString extracts an optional string member. Input is a JSON object and key;
// processing validates the type; output is empty on missing/non-string values.
std::string ParseString(const JsonValue& object, const std::string& key) {
    const JsonValue* value = object.member(key);
    return value != nullptr && value->isString() ? value->stringValue : std::string{};
}

// AppendUniquePath adds a readable candidate path once. Input is a path list and
// candidate; processing normalizes absolute paths when possible and compares
// case-insensitively; output is an updated list used by profile-pack discovery.
void AppendUniquePath(std::vector<std::filesystem::path>& paths, std::filesystem::path candidate) {
    if (candidate.empty()) {
        return;
    }

    std::error_code ec;
    candidate = std::filesystem::absolute(candidate, ec);
    if (ec) {
        ec.clear();
    }
    const std::wstring key = candidate.wstring();
    const auto samePath = [&](const std::filesystem::path& existing) {
        return ::CompareStringOrdinal(existing.wstring().c_str(), -1, key.c_str(), -1, TRUE) == CSTR_EQUAL;
    };
    if (std::find_if(paths.begin(), paths.end(), samePath) == paths.end()) {
        paths.push_back(std::move(candidate));
    }
}

// ApplicationDirectory returns the running executable directory. Input is none;
// processing asks Win32 for the module path; output is empty only on unexpected
// API failure, in which case callers still try current-directory candidates.
std::filesystem::path ApplicationDirectory() {
    std::wstring buffer(MAX_PATH, L'\0');
    DWORD copied = ::GetModuleFileNameW(nullptr, buffer.data(), static_cast<DWORD>(buffer.size()));
    while (copied == buffer.size() && ::GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
        buffer.resize(buffer.size() * 2U);
        copied = ::GetModuleFileNameW(nullptr, buffer.data(), static_cast<DWORD>(buffer.size()));
    }
    if (copied == 0) {
        return {};
    }
    buffer.resize(copied);
    return std::filesystem::path(buffer).parent_path();
}

// ProfilePackSearchPaths mirrors the original KernelDock search behavior with
// plain Win32/runtime helpers. Input is process environment/runtime paths;
// processing checks env override, executable profiles, and current-directory
// profiles; output is an ordered list of candidate pack paths from most explicit
// to least explicit.
std::vector<std::filesystem::path> ProfilePackSearchPaths() {
    std::vector<std::filesystem::path> paths;

    std::wstring envValue(32768, L'\0');
    const DWORD envCopied = ::GetEnvironmentVariableW(L"KSWORD_ARK_PROFILE_PACK", envValue.data(), static_cast<DWORD>(envValue.size()));
    if (envCopied > 0 && envCopied < envValue.size()) {
        envValue.resize(envCopied);
        AppendUniquePath(paths, envValue);
    }

    const std::filesystem::path appDir = ApplicationDirectory();
    const std::filesystem::path currentDir = std::filesystem::current_path();
    for (const std::filesystem::path& base : { appDir, currentDir }) {
        if (base.empty()) {
            continue;
        }
        AppendUniquePath(paths, base / L"profiles" / L"ark_dyndata_pack_v4.json");
        AppendUniquePath(paths, base / L"profiles" / L"ark_dyndata_pack_v3.json");
        AppendUniquePath(paths, base / L"profiles" / L"ark_dyndata_pack_v2.json");
        AppendUniquePath(paths, base / L"profiles" / L"ark_dyndata_pack_v1.json");
    }
    return paths;
}

// ReadFileBytes reads a JSON pack into memory. Input is a filesystem path;
// processing uses binary ifstream and preserves bytes; output is true with UTF-8
// text when the file can be opened and read completely.
bool ReadFileBytes(const std::filesystem::path& path, std::string& textOut, std::wstring& errorOut) {
    std::ifstream file(path, std::ios::binary);
    if (!file) {
        errorOut = L"无法打开 pack";
        return false;
    }
    textOut.assign(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
    if (file.bad()) {
        errorOut = L"读取 pack 失败";
        return false;
    }
    if (textOut.size() >= 3 &&
        static_cast<unsigned char>(textOut[0]) == 0xEF &&
        static_cast<unsigned char>(textOut[1]) == 0xBB &&
        static_cast<unsigned char>(textOut[2]) == 0xBF) {
        textOut.erase(0, 3);
    }
    return true;
}

// Row constructs a KernelResultRow locally. Input is key/value pairs plus detail;
// processing copies them into the common row model; output is a value row.
KernelResultRow Row(std::initializer_list<std::pair<std::wstring, std::wstring>> columns, const std::wstring& detail = {}) {
    KernelResultRow row;
    row.columns.assign(columns.begin(), columns.end());
    row.detailText = detail;
    return row;
}

// FieldNameToId maps compact pack field names onto the shared DynData protocol
// ids. Input is one fieldDictionary/item name; processing accepts both short
// release names and dotted structure aliases; output is 0 for unknown fields so
// unsupported pack entries can be skipped safely.
std::uint32_t FieldNameToId(const std::string& rawName) {
    static const std::unordered_map<std::string, std::uint32_t> kMap = {
        { "epobjecttable", KSW_DYN_FIELD_ID_EP_OBJECT_TABLE },
        { "_eprocess.objecttable", KSW_DYN_FIELD_ID_EP_OBJECT_TABLE },
        { "epsectionobject", KSW_DYN_FIELD_ID_EP_SECTION_OBJECT },
        { "_eprocess.sectionobject", KSW_DYN_FIELD_ID_EP_SECTION_OBJECT },
        { "hthandlecontentionevent", KSW_DYN_FIELD_ID_HT_HANDLE_CONTENTION_EVENT },
        { "_handle_table.handlecontentionevent", KSW_DYN_FIELD_ID_HT_HANDLE_CONTENTION_EVENT },
        { "otname", KSW_DYN_FIELD_ID_OT_NAME },
        { "_object_type.name", KSW_DYN_FIELD_ID_OT_NAME },
        { "otindex", KSW_DYN_FIELD_ID_OT_INDEX },
        { "_object_type.index", KSW_DYN_FIELD_ID_OT_INDEX },
        { "obdecodeshift", KSW_DYN_FIELD_ID_OB_DECODE_SHIFT },
        { "obattributesshift", KSW_DYN_FIELD_ID_OB_ATTRIBUTES_SHIFT },
        { "ktinitialstack", KSW_DYN_FIELD_ID_KT_INITIAL_STACK },
        { "_kthread.initialstack", KSW_DYN_FIELD_ID_KT_INITIAL_STACK },
        { "ktstacklimit", KSW_DYN_FIELD_ID_KT_STACK_LIMIT },
        { "_kthread.stacklimit", KSW_DYN_FIELD_ID_KT_STACK_LIMIT },
        { "ktstackbase", KSW_DYN_FIELD_ID_KT_STACK_BASE },
        { "_kthread.stackbase", KSW_DYN_FIELD_ID_KT_STACK_BASE },
        { "ktkernelstack", KSW_DYN_FIELD_ID_KT_KERNEL_STACK },
        { "_kthread.kernelstack", KSW_DYN_FIELD_ID_KT_KERNEL_STACK },
        { "ktreadoperationcount", KSW_DYN_FIELD_ID_KT_READ_OPERATION_COUNT },
        { "ktwriteoperationcount", KSW_DYN_FIELD_ID_KT_WRITE_OPERATION_COUNT },
        { "ktotheroperationcount", KSW_DYN_FIELD_ID_KT_OTHER_OPERATION_COUNT },
        { "ktreadtransfercount", KSW_DYN_FIELD_ID_KT_READ_TRANSFER_COUNT },
        { "ktwritetransfercount", KSW_DYN_FIELD_ID_KT_WRITE_TRANSFER_COUNT },
        { "ktothertransfercount", KSW_DYN_FIELD_ID_KT_OTHER_TRANSFER_COUNT },
        { "mmsectioncontrolarea", KSW_DYN_FIELD_ID_MM_SECTION_CONTROL_AREA },
        { "mmcontrolarealisthead", KSW_DYN_FIELD_ID_MM_CONTROL_AREA_LIST_HEAD },
        { "mmcontrolarealock", KSW_DYN_FIELD_ID_MM_CONTROL_AREA_LOCK },
        { "alpccommunicationinfo", KSW_DYN_FIELD_ID_ALPC_COMMUNICATION_INFO },
        { "alpcownerprocess", KSW_DYN_FIELD_ID_ALPC_OWNER_PROCESS },
        { "alpcconnectionport", KSW_DYN_FIELD_ID_ALPC_CONNECTION_PORT },
        { "alpcservercommunicationport", KSW_DYN_FIELD_ID_ALPC_SERVER_COMMUNICATION_PORT },
        { "alpcclientcommunicationport", KSW_DYN_FIELD_ID_ALPC_CLIENT_COMMUNICATION_PORT },
        { "alpchandletable", KSW_DYN_FIELD_ID_ALPC_HANDLE_TABLE },
        { "alpchandletablelock", KSW_DYN_FIELD_ID_ALPC_HANDLE_TABLE_LOCK },
        { "alpcattributes", KSW_DYN_FIELD_ID_ALPC_ATTRIBUTES },
        { "alpcattributesflags", KSW_DYN_FIELD_ID_ALPC_ATTRIBUTES_FLAGS },
        { "alpcportcontext", KSW_DYN_FIELD_ID_ALPC_PORT_CONTEXT },
        { "alpcportobjectlock", KSW_DYN_FIELD_ID_ALPC_PORT_OBJECT_LOCK },
        { "alpcsequenceno", KSW_DYN_FIELD_ID_ALPC_SEQUENCE_NO },
        { "alpcstate", KSW_DYN_FIELD_ID_ALPC_STATE },
        { "lxpico_proc", KSW_DYN_FIELD_ID_LX_PICO_PROC },
        { "lxpico_procinfo", KSW_DYN_FIELD_ID_LX_PICO_PROC_INFO },
        { "lxpico_procinfo_pid", KSW_DYN_FIELD_ID_LX_PICO_PROC_INFO_PID },
        { "lxpico_thrdinfo", KSW_DYN_FIELD_ID_LX_PICO_THRD_INFO },
        { "lxpico_thrdinfo_tid", KSW_DYN_FIELD_ID_LX_PICO_THRD_INFO_TID },
        { "ep_protection", KSW_DYN_FIELD_ID_EP_PROTECTION },
        { "epprotection", KSW_DYN_FIELD_ID_EP_PROTECTION },
        { "_eprocess.protection", KSW_DYN_FIELD_ID_EP_PROTECTION },
        { "ep_signaturelevel", KSW_DYN_FIELD_ID_EP_SIGNATURE_LEVEL },
        { "epsignaturelevel", KSW_DYN_FIELD_ID_EP_SIGNATURE_LEVEL },
        { "_eprocess.signaturelevel", KSW_DYN_FIELD_ID_EP_SIGNATURE_LEVEL },
        { "ep_sectionsignaturelevel", KSW_DYN_FIELD_ID_EP_SECTION_SIGNATURE_LEVEL },
        { "epsectionsignaturelevel", KSW_DYN_FIELD_ID_EP_SECTION_SIGNATURE_LEVEL },
        { "_eprocess.sectionsignaturelevel", KSW_DYN_FIELD_ID_EP_SECTION_SIGNATURE_LEVEL },
        { "egeguidentry", KSW_DYN_FIELD_ID_EGE_GUID },
        { "egeguid", KSW_DYN_FIELD_ID_EGE_GUID },
        { "ereguidentry", KSW_DYN_FIELD_ID_ERE_GUID_ENTRY },
        { "pseventguidentry", KSW_DYN_FIELD_ID_ERE_GUID_ENTRY },
        { "pspcreateprocessnotifyroutine", KSW_DYN_FIELD_ID_CB_PSP_CREATE_PROCESS_NOTIFY_ROUTINE },
        { "cbpspcreateprocessnotifyroutine", KSW_DYN_FIELD_ID_CB_PSP_CREATE_PROCESS_NOTIFY_ROUTINE },
        { "pspcreatethreadnotifyroutine", KSW_DYN_FIELD_ID_CB_PSP_CREATE_THREAD_NOTIFY_ROUTINE },
        { "cbpspcreatethreadnotifyroutine", KSW_DYN_FIELD_ID_CB_PSP_CREATE_THREAD_NOTIFY_ROUTINE },
        { "psploadimagenotifyroutine", KSW_DYN_FIELD_ID_CB_PSP_LOAD_IMAGE_NOTIFY_ROUTINE },
        { "cbpsploadimagenotifyroutine", KSW_DYN_FIELD_ID_CB_PSP_LOAD_IMAGE_NOTIFY_ROUTINE },
        { "pspnotifyenablemask", KSW_DYN_FIELD_ID_CB_PSP_NOTIFY_ENABLE_MASK },
        { "cbpspnotifyenablemask", KSW_DYN_FIELD_ID_CB_PSP_NOTIFY_ENABLE_MASK },
        { "cmcallbacklisthead", KSW_DYN_FIELD_ID_CB_CM_CALLBACK_LIST_HEAD },
        { "cbcmcallbacklisthead", KSW_DYN_FIELD_ID_CB_CM_CALLBACK_LIST_HEAD },
        { "_object_type.callbacklist", KSW_DYN_FIELD_ID_CB_OBJECT_TYPE_CALLBACK_LIST },
        { "objecttypecallbacklist", KSW_DYN_FIELD_ID_CB_OBJECT_TYPE_CALLBACK_LIST },
        { "cbobjecttypecallbacklist", KSW_DYN_FIELD_ID_CB_OBJECT_TYPE_CALLBACK_LIST },
        { "callbackentryitementrylist", KSW_DYN_FIELD_ID_CB_CALLBACK_ENTRY_ITEM_ENTRY_LIST },
        { "cbcallbackentryitementrylist", KSW_DYN_FIELD_ID_CB_CALLBACK_ENTRY_ITEM_ENTRY_LIST },
        { "callbackentryitempreoperation", KSW_DYN_FIELD_ID_CB_CALLBACK_ENTRY_ITEM_PRE_OPERATION },
        { "cbcallbackentryitempreoperation", KSW_DYN_FIELD_ID_CB_CALLBACK_ENTRY_ITEM_PRE_OPERATION },
        { "callbackentryitempostoperation", KSW_DYN_FIELD_ID_CB_CALLBACK_ENTRY_ITEM_POST_OPERATION },
        { "cbcallbackentryitempostoperation", KSW_DYN_FIELD_ID_CB_CALLBACK_ENTRY_ITEM_POST_OPERATION },
        { "callbackentryitemoperations", KSW_DYN_FIELD_ID_CB_CALLBACK_ENTRY_ITEM_OPERATIONS },
        { "cbcallbackentryitemoperations", KSW_DYN_FIELD_ID_CB_CALLBACK_ENTRY_ITEM_OPERATIONS },
        { "callbackentryitemcallbackentry", KSW_DYN_FIELD_ID_CB_CALLBACK_ENTRY_ITEM_CALLBACK_ENTRY },
        { "cbcallbackentryitemcallbackentry", KSW_DYN_FIELD_ID_CB_CALLBACK_ENTRY_ITEM_CALLBACK_ENTRY },
        { "callbackentryaltitude", KSW_DYN_FIELD_ID_CB_CALLBACK_ENTRY_ALTITUDE },
        { "cbcallbackentryaltitude", KSW_DYN_FIELD_ID_CB_CALLBACK_ENTRY_ALTITUDE },
        { "callbackentryregistrationcontext", KSW_DYN_FIELD_ID_CB_CALLBACK_ENTRY_REGISTRATION_CONTEXT },
        { "cbcallbackentryregistrationcontext", KSW_DYN_FIELD_ID_CB_CALLBACK_ENTRY_REGISTRATION_CONTEXT },
        { "epuniqueprocessid", KSW_DYN_FIELD_ID_EP_UNIQUE_PROCESS_ID },
        { "_eprocess.uniqueprocessid", KSW_DYN_FIELD_ID_EP_UNIQUE_PROCESS_ID },
        { "epactiveprocesslinks", KSW_DYN_FIELD_ID_EP_ACTIVE_PROCESS_LINKS },
        { "_eprocess.activeprocesslinks", KSW_DYN_FIELD_ID_EP_ACTIVE_PROCESS_LINKS },
        { "epthreadlisthead", KSW_DYN_FIELD_ID_EP_THREAD_LIST_HEAD },
        { "_eprocess.threadlisthead", KSW_DYN_FIELD_ID_EP_THREAD_LIST_HEAD },
        { "epimagefilename", KSW_DYN_FIELD_ID_EP_IMAGE_FILE_NAME },
        { "_eprocess.imagefilename", KSW_DYN_FIELD_ID_EP_IMAGE_FILE_NAME },
        { "eptoken", KSW_DYN_FIELD_ID_EP_TOKEN },
        { "_eprocess.token", KSW_DYN_FIELD_ID_EP_TOKEN },
        { "etcid", KSW_DYN_FIELD_ID_ET_CID },
        { "_ethread.cid", KSW_DYN_FIELD_ID_ET_CID },
        { "etthreadlistentry", KSW_DYN_FIELD_ID_ET_THREAD_LIST_ENTRY },
        { "_ethread.threadlistentry", KSW_DYN_FIELD_ID_ET_THREAD_LIST_ENTRY },
        { "etstartaddress", KSW_DYN_FIELD_ID_ET_START_ADDRESS },
        { "_ethread.startaddress", KSW_DYN_FIELD_ID_ET_START_ADDRESS },
        { "etwin32startaddress", KSW_DYN_FIELD_ID_ET_WIN32_START_ADDRESS },
        { "_ethread.win32startaddress", KSW_DYN_FIELD_ID_ET_WIN32_START_ADDRESS },
        { "ktprocess", KSW_DYN_FIELD_ID_KT_PROCESS },
        { "_kthread.process", KSW_DYN_FIELD_ID_KT_PROCESS },
        { "httablecode", KSW_DYN_FIELD_ID_HT_TABLE_CODE },
        { "_handle_table.tablecode", KSW_DYN_FIELD_ID_HT_TABLE_CODE },
        { "hthandlecount", KSW_DYN_FIELD_ID_HT_HANDLE_COUNT },
        { "_handle_table.handlecount", KSW_DYN_FIELD_ID_HT_HANDLE_COUNT },
        { "htelowvalue", KSW_DYN_FIELD_ID_HTE_LOW_VALUE },
        { "_handle_table_entry.lowvalue", KSW_DYN_FIELD_ID_HTE_LOW_VALUE },
        { "kldrinloadorderlinks", KSW_DYN_FIELD_ID_KLDR_IN_LOAD_ORDER_LINKS },
        { "_kl_data_table_entry.inloadorderlinks", KSW_DYN_FIELD_ID_KLDR_IN_LOAD_ORDER_LINKS },
        { "kldrdllbase", KSW_DYN_FIELD_ID_KLDR_DLL_BASE },
        { "kldrbasedllname", KSW_DYN_FIELD_ID_KLDR_BASE_DLL_NAME },
        { "kldrfulldllname", KSW_DYN_FIELD_ID_KLDR_FULL_DLL_NAME },
        { "kldrsizeofimage", KSW_DYN_FIELD_ID_KLDR_SIZE_OF_IMAGE },
        { "kldrflags", KSW_DYN_FIELD_ID_KLDR_FLAGS },
        { "dodriverstart", KSW_DYN_FIELD_ID_DO_DRIVER_START },
        { "_driver_object.driverstart", KSW_DYN_FIELD_ID_DO_DRIVER_START },
        { "dodriversize", KSW_DYN_FIELD_ID_DO_DRIVER_SIZE },
        { "_driver_object.driversize", KSW_DYN_FIELD_ID_DO_DRIVER_SIZE },
        { "dodriversection", KSW_DYN_FIELD_ID_DO_DRIVER_SECTION },
        { "_driver_object.driversection", KSW_DYN_FIELD_ID_DO_DRIVER_SECTION },
        { "domajorfunction", KSW_DYN_FIELD_ID_DO_MAJOR_FUNCTION },
        { "_driver_object.majorfunction", KSW_DYN_FIELD_ID_DO_MAJOR_FUNCTION },
        { "dofastiodispatch", KSW_DYN_FIELD_ID_DO_FAST_IO_DISPATCH },
        { "_driver_object.fastiodispatch", KSW_DYN_FIELD_ID_DO_FAST_IO_DISPATCH },
        { "dodriverunload", KSW_DYN_FIELD_ID_DO_DRIVER_UNLOAD },
        { "_driver_object.driverunload", KSW_DYN_FIELD_ID_DO_DRIVER_UNLOAD },
        { "uldname", KSW_DYN_FIELD_ID_ULD_NAME },
        { "_unloaded_drivers.name", KSW_DYN_FIELD_ID_ULD_NAME },
        { "uldstartaddress", KSW_DYN_FIELD_ID_ULD_START_ADDRESS },
        { "_unloaded_drivers.startaddress", KSW_DYN_FIELD_ID_ULD_START_ADDRESS },
        { "uldendaddress", KSW_DYN_FIELD_ID_ULD_END_ADDRESS },
        { "_unloaded_drivers.endaddress", KSW_DYN_FIELD_ID_ULD_END_ADDRESS },
        { "uldcurrenttime", KSW_DYN_FIELD_ID_ULD_CURRENT_TIME },
        { "_unloaded_drivers.currenttime", KSW_DYN_FIELD_ID_ULD_CURRENT_TIME },
        { "uldtypesize", KSW_DYN_FIELD_ID_ULD_TYPE_SIZE },
        { "_unloaded_drivers.typesize", KSW_DYN_FIELD_ID_ULD_TYPE_SIZE },
        { "rtlavlbalancedroot", KSW_DYN_FIELD_ID_RTL_AVL_BALANCED_ROOT },
        { "_rtl_avl_table.balancedroot", KSW_DYN_FIELD_ID_RTL_AVL_BALANCED_ROOT },
        { "rtlavlorderedpointer", KSW_DYN_FIELD_ID_RTL_AVL_ORDERED_POINTER },
        { "_rtl_avl_table.orderedpointer", KSW_DYN_FIELD_ID_RTL_AVL_ORDERED_POINTER },
        { "rtlavlwhichorderedelement", KSW_DYN_FIELD_ID_RTL_AVL_WHICH_ORDERED_ELEMENT },
        { "_rtl_avl_table.whichorderedelement", KSW_DYN_FIELD_ID_RTL_AVL_WHICH_ORDERED_ELEMENT },
        { "rtlavlnumbergenerictableelements", KSW_DYN_FIELD_ID_RTL_AVL_NUMBER_GENERIC_TABLE_ELEMENTS },
        { "_rtl_avl_table.numbergenerictableelements", KSW_DYN_FIELD_ID_RTL_AVL_NUMBER_GENERIC_TABLE_ELEMENTS },
        { "rtlavldepthoftree", KSW_DYN_FIELD_ID_RTL_AVL_DEPTH_OF_TREE },
        { "_rtl_avl_table.depthoftree", KSW_DYN_FIELD_ID_RTL_AVL_DEPTH_OF_TREE },
        { "rtlavlrestartkey", KSW_DYN_FIELD_ID_RTL_AVL_RESTART_KEY },
        { "_rtl_avl_table.restartkey", KSW_DYN_FIELD_ID_RTL_AVL_RESTART_KEY },
        { "rtlavldeletecount", KSW_DYN_FIELD_ID_RTL_AVL_DELETE_COUNT },
        { "_rtl_avl_table.deletecount", KSW_DYN_FIELD_ID_RTL_AVL_DELETE_COUNT },
        { "rtlavltypesize", KSW_DYN_FIELD_ID_RTL_AVL_TYPE_SIZE },
        { "_rtl_avl_table.typesize", KSW_DYN_FIELD_ID_RTL_AVL_TYPE_SIZE },
        { "pspcidtable", KSW_DYN_FIELD_ID_KG_PSP_CID_TABLE },
        { "kgpspcidtable", KSW_DYN_FIELD_ID_KG_PSP_CID_TABLE },
        { "psloadedmodulelist", KSW_DYN_FIELD_ID_KG_PS_LOADED_MODULE_LIST },
        { "kgpsloadedmodulelist", KSW_DYN_FIELD_ID_KG_PS_LOADED_MODULE_LIST },
        { "mmunloadeddrivers", KSW_DYN_FIELD_ID_KG_MM_UNLOADED_DRIVERS },
        { "kgmmunloadeddrivers", KSW_DYN_FIELD_ID_KG_MM_UNLOADED_DRIVERS },
        { "piddbcachetable", KSW_DYN_FIELD_ID_KG_PIDDB_CACHE_TABLE },
        { "kgpiddbcachetable", KSW_DYN_FIELD_ID_KG_PIDDB_CACHE_TABLE },
        { "keservicedescriptortableshadow", KSW_DYN_FIELD_ID_KG_KE_SERVICE_DESCRIPTOR_TABLE_SHADOW },
        { "kgkeservicedescriptortableshadow", KSW_DYN_FIELD_ID_KG_KE_SERVICE_DESCRIPTOR_TABLE_SHADOW },
        { "mmlastunloadeddriver", KSW_DYN_FIELD_ID_KG_MM_LAST_UNLOADED_DRIVER },
        { "kgmmlastunloadeddriver", KSW_DYN_FIELD_ID_KG_MM_LAST_UNLOADED_DRIVER },
    };

    std::string key = LowerAscii(rawName);
    key.erase(std::remove_if(key.begin(), key.end(), [](const unsigned char ch) { return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'; }), key.end());
    const auto found = kMap.find(key);
    return found == kMap.end() ? 0U : found->second;
}

// BuildFieldDictionary converts fieldDictionary strings into protocol ids. Input
// is the pack root; processing keeps positional alignment with fields entries;
// output is an id vector, with 0 marking unknown dictionary names.
std::vector<std::uint32_t> BuildFieldDictionary(const JsonValue& root) {
    std::vector<std::uint32_t> dictionary;
    const JsonValue* fieldDictionary = root.member("fieldDictionary");
    if (fieldDictionary == nullptr || !fieldDictionary->isArray()) {
        return dictionary;
    }
    dictionary.reserve(fieldDictionary->arrayValue.size());
    for (const JsonValue& item : fieldDictionary->arrayValue) {
        dictionary.push_back(item.isString() ? FieldNameToId(item.stringValue) : 0U);
    }
    return dictionary;
}

// ParseProfileFields expands legacy fields [dictionaryIndex, offset] pairs into
// ArkDriverClient apply fields. Inputs are a profile object and root dictionary;
// processing rejects malformed pairs and unknown field ids; output is appended to
// the legacy apply packet and counted for diagnostics.
void ParseProfileFields(
    const JsonValue& profileObject,
    const std::vector<std::uint32_t>& dictionary,
    ksword::ark::DynDataProfileApplyInput& profileOut) {
    const JsonValue* fields = profileObject.member("fields");
    if (fields == nullptr || !fields->isArray()) {
        return;
    }

    for (const JsonValue& pairValue : fields->arrayValue) {
        if (!pairValue.isArray() || pairValue.arrayValue.size() < 2U) {
            continue;
        }
        std::uint32_t fieldIndex = 0;
        std::uint32_t offset = 0;
        if (!ParseUInt32(&pairValue.arrayValue[0], fieldIndex) ||
            !ParseUInt32(&pairValue.arrayValue[1], offset) ||
            fieldIndex >= dictionary.size()) {
            continue;
        }
        const std::uint32_t fieldId = dictionary[fieldIndex];
        if (fieldId == 0U || fieldId > KSW_DYN_FIELD_ID_MAX) {
            continue;
        }
        profileOut.fields.push_back({ fieldId, offset });
        if (profileOut.fields.size() >= KSW_DYN_PROFILE_MAX_FIELDS) {
            break;
        }
    }
}

// ItemKindToProtocol maps textual item kind names onto EX protocol constants.
// Input is a pack item kind; processing is case-insensitive; output is 0 when
// the item cannot be represented by the current driver protocol.
std::uint32_t ItemKindToProtocol(const std::string& kind) {
    const std::string lower = LowerAscii(kind);
    if (lower == "structoffset" || lower == "struct_offset" || lower == "offset") {
        return KSW_DYN_PROFILE_EX_ITEM_KIND_STRUCT_OFFSET;
    }
    if (lower == "typesize" || lower == "type_size" || lower == "sizeof") {
        return KSW_DYN_PROFILE_EX_ITEM_KIND_STRUCT_OFFSET;
    }
    if (lower == "globalrva" || lower == "global_rva" || lower == "rva") {
        return KSW_DYN_PROFILE_EX_ITEM_KIND_GLOBAL_RVA;
    }
    return 0U;
}

// ParseBoolFlag reads optional boolean-ish item flags. Input is one JSON value;
// processing accepts true/false, 0/1, and yes/no strings; output defaults false
// when absent or unrecognized to preserve legacy pack behavior.
bool ParseBoolFlag(const JsonValue* value) {
    if (value == nullptr) {
        return false;
    }
    if (value->type == JsonValue::Type::Bool) {
        return value->boolValue;
    }
    std::uint32_t numeric = 0;
    if (ParseUInt32(value, numeric)) {
        return numeric != 0U;
    }
    if (value->isString()) {
        const std::string lower = LowerAscii(value->stringValue);
        return lower == "true" || lower == "yes" || lower == "required" || lower == "callback";
    }
    return false;
}

// ParseExplicitFlags reads optional numeric/string flags from a typed item.
// Input is one item object; processing accepts `flags`, `required`, and callback
// source context; output contains only shared protocol-supported flag bits.
std::uint32_t ParseExplicitFlags(const JsonValue& itemObject, const bool callbackSource) {
    std::uint32_t flags = callbackSource ? KSW_DYN_PROFILE_EX_ITEM_FLAG_CALLBACK : 0U;
    std::uint32_t numericFlags = 0;
    if (ParseUInt32(itemObject.member("flags"), numericFlags)) {
        flags |= numericFlags;
    }
    if (ParseBoolFlag(itemObject.member("required"))) {
        flags |= KSW_DYN_PROFILE_EX_ITEM_FLAG_REQUIRED;
    }
    if (ParseBoolFlag(itemObject.member("callback"))) {
        flags |= KSW_DYN_PROFILE_EX_ITEM_FLAG_CALLBACK;
    }
    return flags & (KSW_DYN_PROFILE_EX_ITEM_FLAG_REQUIRED | KSW_DYN_PROFILE_EX_ITEM_FLAG_CALLBACK);
}

// ParseProfileItems expands `items`/`typedItems` and `callbackItems` into EX
// protocol items. Inputs are one matched profile object; processing maps item
// names to field ids and kind strings to protocol kinds; output is appended to
// profileExOut and truncated at the shared protocol limit.
void ParseProfileItems(
    const JsonValue& profileObject,
    ksword::ark::DynDataProfileApplyExInput& profileExOut,
    const std::string& preferredItemKey = "items") {
    auto parseArray = [&](const JsonValue* arrayValue, const bool callbackSource) {
        if (arrayValue == nullptr || !arrayValue->isArray()) {
            return;
        }
        for (const JsonValue& itemValue : arrayValue->arrayValue) {
            if (!itemValue.isObject()) {
                continue;
            }
            const std::string name = ParseString(itemValue, "name");
            const std::uint32_t itemId = FieldNameToId(name);
            const std::uint32_t itemKind = ItemKindToProtocol(ParseString(itemValue, "kind"));
            std::uint32_t value = 0;
            if (itemId == 0U || itemKind == 0U || !ParseUInt32(itemValue.member("value"), value)) {
                continue;
            }
            profileExOut.items.push_back({ itemId, itemKind, value, ParseExplicitFlags(itemValue, callbackSource) });
            if (profileExOut.items.size() >= KSW_DYN_PROFILE_EX_MAX_ITEMS) {
                return;
            }
        }
    };

    const JsonValue* items = profileObject.member(preferredItemKey);
    if (items == nullptr || !items->isArray() || items->arrayValue.empty()) {
        items = profileObject.member("typedItems");
    }
    parseArray(items, false);
    if (profileExOut.items.size() < KSW_DYN_PROFILE_EX_MAX_ITEMS) {
        parseArray(profileObject.member("callbackItems"), true);
    }
}

// CopyV4Utf8 copies profile metadata into a fixed protocol buffer. Input is a
// UTF-8 profile string; output is always NUL terminated and never exposes a
// partially initialized packet to the driver.
template <std::size_t Size>
void CopyV4Utf8(char (&destination)[Size], const std::string& source) {
    static_assert(Size > 0U);
    std::memset(destination, 0, Size);
    const std::size_t bytes = std::min<std::size_t>(source.size(), Size - 1U);
    if (bytes != 0U) {
        std::memcpy(destination, source.data(), bytes);
    }
}

// CopyV4Wide copies the module name into the fixed shared-protocol identity.
// Input is local UTF-16 text; output is NUL terminated within the packet bound.
template <std::size_t Size>
void CopyV4Wide(wchar_t (&destination)[Size], const std::wstring& source) {
    static_assert(Size > 0U);
    std::fill(std::begin(destination), std::end(destination), L'\0');
    const std::size_t chars = std::min<std::size_t>(source.size(), Size - 1U);
    if (chars != 0U) {
        std::copy_n(source.begin(), chars, destination);
    }
}

// IsSupportedV4ItemId limits local packs to the stable shared DynData v4 item
// identifiers. It prevents malformed JSON from sending arbitrary ids to R0.
bool IsSupportedV4ItemId(const std::uint32_t itemId) {
    if (itemId >= 1U && itemId <= KSW_DYN_FIELD_ID_MAX) {
        return true;
    }
    switch (itemId) {
    case KSW_DYN_V4_ITEM_ID_ETH_ACTIVE_EX_WORKER:
    case KSW_DYN_V4_ITEM_ID_KPRCB_TIMER_TABLE:
    case KSW_DYN_V4_ITEM_ID_KTIMER_TABLE_TIMER_ENTRIES:
    case KSW_DYN_V4_ITEM_ID_KTIMER_TABLE_ENTRY_LOCK:
    case KSW_DYN_V4_ITEM_ID_KTIMER_TABLE_ENTRY_ENTRY:
    case KSW_DYN_V4_ITEM_ID_KTIMER_TABLE_ENTRY_TIME:
    case KSW_DYN_V4_ITEM_ID_KTIMER_TIMER_LIST_ENTRY:
    case KSW_DYN_V4_ITEM_ID_KTIMER_DUE_TIME:
    case KSW_DYN_V4_ITEM_ID_KTIMER_DPC:
    case KSW_DYN_V4_ITEM_ID_KTIMER_TIMER_TYPE:
    case KSW_DYN_V4_ITEM_ID_KTIMER_PERIOD:
    case KSW_DYN_V4_ITEM_ID_KDPC_DEFERRED_ROUTINE:
    case KSW_DYN_V4_ITEM_ID_KDPC_DEFERRED_CONTEXT:
    case KSW_DYN_V4_ITEM_ID_KTIMER_TABLE_TYPE_SIZE:
    case KSW_DYN_V4_ITEM_ID_KTIMER_TABLE_ENTRY_TYPE_SIZE:
    case KSW_DYN_V4_ITEM_ID_KTIMER_TYPE_SIZE:
    case KSW_DYN_V4_ITEM_ID_KDPC_TYPE_SIZE:
        return true;
    default:
        return false;
    }
}

// ParseV4Profile converts a version-4 pack entry to the typed packet accepted
// by applyDynDataProfileV4. Inputs are already identity-matched JSON and R0
// identity data; output is rejected unless every capability count is exact.
bool ParseV4Profile(
    const JsonValue& profileObject,
    const ksword::ark::ArkDynModuleIdentity& identity,
    const std::string& profileName,
    const std::string& pdbName,
    const std::string& pdbGuid,
    const std::uint32_t pdbAge,
    ksword::ark::DynDataV4ApplyInput& profileOut,
    std::wstring& errorOut) {
    const JsonValue* itemArray = profileObject.member("items");
    const JsonValue* groupArray = profileObject.member("capabilityGroups");
    if (itemArray == nullptr || !itemArray->isArray() || itemArray->arrayValue.empty() ||
        groupArray == nullptr || !groupArray->isArray() || groupArray->arrayValue.empty() ||
        itemArray->arrayValue.size() > KSW_DYN_V4_MAX_ITEMS_PER_MODULE ||
        groupArray->arrayValue.size() > KSW_DYN_V4_MAX_CAPABILITY_GROUPS_PER_MODULE) {
        errorOut = L"v4 DynData pack 的 items 或 capabilityGroups 无效。";
        return false;
    }

    profileOut = {};
    std::array<bool, KSW_DYN_V4_MAX_CAPABILITY_GROUPS_PER_MODULE + 1U> seenGroups{};
    std::unordered_map<std::uint32_t, std::pair<std::uint32_t, std::uint32_t>> expectedCounts;
    for (const JsonValue& groupValue : groupArray->arrayValue) {
        if (!groupValue.isObject()) {
            errorOut = L"v4 DynData capability group 不是对象。";
            return false;
        }
        std::uint32_t groupId = 0;
        std::uint32_t flags = 0;
        std::uint32_t requiredCount = 0;
        std::uint32_t optionalCount = 0;
        if (!ParseUInt32(groupValue.member("groupId"), groupId) ||
            !ParseUInt32(groupValue.member("flags"), flags) ||
            !ParseUInt32(groupValue.member("requiredItemCount"), requiredCount) ||
            !ParseUInt32(groupValue.member("optionalItemCount"), optionalCount) ||
            groupId == 0U || groupId > KSW_DYN_V4_MAX_CAPABILITY_GROUPS_PER_MODULE || seenGroups[groupId]) {
            errorOut = L"v4 DynData capability group 字段无效或重复。";
            return false;
        }
        seenGroups[groupId] = true;
        KSW_DYN_V4_CAPABILITY_GROUP_PACKET group{};
        group.groupId = groupId;
        group.flags = flags;
        group.requiredItemCount = requiredCount;
        group.optionalItemCount = optionalCount;
        CopyV4Utf8(group.groupName, ParseString(groupValue, "groupName"));
        profileOut.capabilityGroups.push_back(group);
        expectedCounts[groupId] = { requiredCount, optionalCount };
    }

    std::unordered_map<std::uint32_t, std::pair<std::uint32_t, std::uint32_t>> actualCounts;
    std::set<std::uint32_t> seenItems;
    for (const JsonValue& itemValue : itemArray->arrayValue) {
        if (!itemValue.isObject()) {
            errorOut = L"v4 DynData item 不是对象。";
            return false;
        }
        std::uint32_t itemId = 0;
        std::uint32_t itemKind = 0;
        std::uint32_t flags = 0;
        std::uint32_t groupId = 0;
        std::uint32_t valueLow = 0;
        std::uint32_t valueHigh = 0;
        std::uint32_t aux[4]{};
        if (!ParseUInt32(itemValue.member("itemId"), itemId) ||
            !ParseUInt32(itemValue.member("itemKind"), itemKind) ||
            !ParseUInt32(itemValue.member("flags"), flags) ||
            !ParseUInt32(itemValue.member("capabilityGroupId"), groupId) ||
            !ParseUInt32(itemValue.member("valueLow"), valueLow) ||
            !ParseUInt32(itemValue.member("valueHigh"), valueHigh) ||
            !ParseUInt32(itemValue.member("aux0"), aux[0]) ||
            !ParseUInt32(itemValue.member("aux1"), aux[1]) ||
            !ParseUInt32(itemValue.member("aux2"), aux[2]) ||
            !ParseUInt32(itemValue.member("aux3"), aux[3]) ||
            !IsSupportedV4ItemId(itemId) || !seenItems.insert(itemId).second ||
            itemKind < KSW_DYN_V4_ITEM_KIND_STRUCT_OFFSET || itemKind > KSW_DYN_V4_ITEM_KIND_LIST_HEAD_GLOBAL ||
            (flags != KSW_DYN_V4_ITEM_FLAG_REQUIRED && flags != KSW_DYN_V4_ITEM_FLAG_OPTIONAL) ||
            groupId == 0U || groupId >= seenGroups.size() || !seenGroups[groupId]) {
            errorOut = L"v4 DynData item 字段无效、未支持或重复。";
            return false;
        }
        KSW_DYN_V4_ITEM_PACKET item{};
        item.itemId = itemId;
        item.itemKind = itemKind;
        item.flags = flags;
        item.capabilityGroupId = groupId;
        item.valueLow = valueLow;
        item.valueHigh = valueHigh;
        item.aux0 = aux[0];
        item.aux1 = aux[1];
        item.aux2 = aux[2];
        item.aux3 = aux[3];
        profileOut.items.push_back(item);
        auto& count = actualCounts[groupId];
        if ((flags & KSW_DYN_V4_ITEM_FLAG_REQUIRED) != 0U) {
            ++count.first;
        } else {
            ++count.second;
        }
    }
    for (const auto& [groupId, expected] : expectedCounts) {
        if (actualCounts[groupId] != expected) {
            errorOut = L"v4 DynData capability group 的 required/optional 数量与 items 不一致。";
            return false;
        }
    }

    profileOut.module.image.present = identity.present ? 1UL : 0UL;
    profileOut.module.image.classId = identity.classId;
    profileOut.module.image.machine = identity.machine;
    profileOut.module.image.timeDateStamp = identity.timeDateStamp;
    profileOut.module.image.sizeOfImage = identity.sizeOfImage;
    profileOut.module.image.imageBase = identity.imageBase;
    CopyV4Wide(profileOut.module.image.moduleName, identity.moduleName);
    CopyV4Utf8(profileOut.module.profileName, profileName);
    CopyV4Utf8(profileOut.module.pdb.pdbName, pdbName);
    CopyV4Utf8(profileOut.module.pdb.pdbGuid, pdbGuid);
    profileOut.module.pdb.pdbAge = pdbAge;
    return true;
}

// ProfileArrayCount returns a compact count for diagnostics. Input is a JSON
// object and member key; processing checks array shape only; output is 0 when the
// member is missing or malformed.
std::uint32_t ProfileArrayCount(const JsonValue& profileObject, const std::string& key) {
    const JsonValue* value = profileObject.member(key);
    return value != nullptr && value->isArray() ? static_cast<std::uint32_t>(std::min<std::size_t>(value->arrayValue.size(), std::numeric_limits<std::uint32_t>::max())) : 0U;
}

// FillProfileMetadata copies identity and metadata into both apply packet shapes.
// Inputs are the current R0 identity and matched profile object; processing uses
// the same strings for legacy and EX APIs; output is ready for field/item append.
void FillProfileMetadata(
    const ksword::ark::ArkDynModuleIdentity& identity,
    const JsonValue& profileObject,
    ksword::ark::DynDataProfileApplyInput& profileOut,
    ksword::ark::DynDataProfileApplyExInput& profileExOut) {
    profileOut.ntoskrnl = identity;
    profileExOut.ntoskrnl = identity;
    profileOut.profileName = ParseString(profileObject, "profileName");
    profileOut.pdbName = ParseString(profileObject, "pdbName");
    profileOut.pdbGuid = ParseString(profileObject, "pdbGuid");
    ParseUInt32(profileObject.member("pdbAge"), profileOut.pdbAge);

    profileExOut.profileName = profileOut.profileName;
    profileExOut.pdbName = profileOut.pdbName;
    profileExOut.pdbGuid = profileOut.pdbGuid;
    profileExOut.pdbAge = profileOut.pdbAge;
}

// ProfileMatchesIdentity performs the exact kernel-image match required before
// applying PDB-derived offsets. Inputs are the current R0 identity and one JSON
// profile object; processing compares module class, machine, timestamp, and
// image size; output is true only for a safe exact match.
bool ProfileMatchesIdentity(const ksword::ark::ArkDynModuleIdentity& identity, const JsonValue& profileObject) {
    std::uint32_t moduleClassId = 0;
    std::uint32_t machine = 0;
    std::uint32_t timeDateStamp = 0;
    std::uint32_t sizeOfImage = 0;
    return ParseUInt32(profileObject.member("moduleClassId"), moduleClassId) &&
        ParseUInt32(profileObject.member("machine"), machine) &&
        ParseUInt32(profileObject.member("timeDateStamp"), timeDateStamp) &&
        ParseUInt32(profileObject.member("sizeOfImage"), sizeOfImage) &&
        identity.classId == moduleClassId &&
        identity.machine == machine &&
        identity.timeDateStamp == timeDateStamp &&
        identity.sizeOfImage == sizeOfImage;
}

// AppendDiagnostic records a bounded diagnostic line. Inputs are the output list
// and one message; processing prevents huge profile-scan summaries from flooding
// the UI; output is an updated diagnostics vector.
void AppendDiagnostic(std::vector<std::wstring>& diagnostics, std::wstring message) {
    constexpr std::size_t kMaxDiagnostics = 16;
    if (message.empty() || diagnostics.size() >= kMaxDiagnostics) {
        return;
    }
    diagnostics.push_back(std::move(message));
}

std::wstring ApplyModeText(const DynDataProfileMatch& match);

// BuildMatchedProfileResult fills the returned match after an identity hit.
// Inputs are the current identity, root dictionary, profile object, and path;
// processing parses both legacy fields and EX typed items; output is a complete
// match packet ready for display or ArkDriverClient apply.
DynDataProfileMatch BuildMatchedProfileResult(
    const ksword::ark::ArkDynModuleIdentity& identity,
    const JsonValue& rootObject,
    const JsonValue& profileObject,
    const std::filesystem::path& path,
    const std::uint32_t packVersion,
    const std::uint32_t existingPackCount,
    const std::uint32_t profileCount,
    const std::uint32_t scannedProfileCount) {
    DynDataProfileMatch result;
    result.scanned = true;
    result.matched = true;
    result.path = path.wstring();
    result.existingPackCount = existingPackCount;
    result.profileCount = profileCount;
    result.scannedProfileCount = scannedProfileCount;
    result.packVersion = packVersion;
    result.fieldCount = ProfileArrayCount(profileObject, "fields");
    result.typedItemCount = ProfileArrayCount(profileObject, packVersion == 4U ? "legacyItems" : "items");
    if (result.typedItemCount == 0U) {
        result.typedItemCount = ProfileArrayCount(profileObject, "typedItems");
    }
    result.callbackItemCount = ProfileArrayCount(profileObject, "callbackItems");
    result.v4ItemCount = packVersion == 4U ? ProfileArrayCount(profileObject, "items") : 0U;
    result.v4CapabilityGroupCount = packVersion == 4U ? ProfileArrayCount(profileObject, "capabilityGroups") : 0U;
    const JsonValue* coverage = profileObject.member("coveragePercent");
    result.coveragePercent = coverage != nullptr && coverage->isNumber() ? coverage->numberValue : -1.0;

    FillProfileMetadata(identity, profileObject, result.profile, result.profileEx);
    const std::vector<std::uint32_t> dictionary = BuildFieldDictionary(rootObject);
    ParseProfileFields(profileObject, dictionary, result.profile);
    ParseProfileItems(profileObject, result.profileEx, packVersion == 4U ? "legacyItems" : "items");

    if (packVersion == 4U) {
        std::wstring v4Error;
        if (!ParseV4Profile(
                profileObject,
                identity,
                result.profile.profileName,
                result.profile.pdbName,
                result.profile.pdbGuid,
                result.profile.pdbAge,
                result.profileV4,
                v4Error)) {
            result.message = L"本地 DynData v4 profile 命中 identity，但数据校验失败。 " + v4Error;
            return result;
        }
    }

    result.valid = !result.profile.fields.empty() || !result.profileEx.items.empty() || !result.profileV4.items.empty();

    std::wostringstream message;
    if (result.valid) {
        message << L"本地 DynData profile pack 命中；legacyFields=" << result.profile.fields.size()
            << L"，typedItems=" << result.profileEx.items.size()
            << L"，v4Items=" << result.profileV4.items.size()
            << L"，v4Groups=" << result.profileV4.capabilityGroups.size()
            << L"，packProfiles=" << result.profileCount
            << L"，scanned=" << result.scannedProfileCount
            << L"，applyMode=" << ApplyModeText(result) << L"。";
    } else {
        message << L"本地 DynData profile pack 命中 identity，但可应用 fields/items 均为空或无法映射到当前协议。";
    }
    result.message = message.str();
    return result;
}

// ContainsI checks filter membership across display fields. Inputs are one text
// and optional filter; processing is case-insensitive using towlower; output is
// true when the filter is empty or contained by text.
bool ContainsI(std::wstring text, std::wstring filter) {
    if (filter.empty()) {
        return true;
    }
    std::transform(text.begin(), text.end(), text.begin(), [](const wchar_t ch) { return static_cast<wchar_t>(::towlower(ch)); });
    std::transform(filter.begin(), filter.end(), filter.begin(), [](const wchar_t ch) { return static_cast<wchar_t>(::towlower(ch)); });
    return text.find(filter) != std::wstring::npos;
}

// PushFilteredRow appends a display row only when it matches the current UI
// filter. Inputs are the target result, row, and filter; processing scans all
// columns and detail text; no value is returned.
void PushFilteredRow(KernelOperationResult& result, KernelResultRow row, const std::wstring& filter) {
    std::wstring haystack = row.detailText;
    for (const auto& [key, value] : row.columns) {
        haystack += L" ";
        haystack += key;
        haystack += L"=";
        haystack += value;
    }
    if (ContainsI(haystack, filter)) {
        result.rows.push_back(std::move(row));
    }
}

// ApplyModeText returns every driver API layer supplied by parsed profile
// content. Input is one match packet; output follows the actual application
// order used by the main application and ArkLight.
std::wstring ApplyModeText(const DynDataProfileMatch& match) {
    if (!match.valid) {
        return L"Unavailable";
    }
    std::wstring layers;
    const auto appendLayer = [&layers](const wchar_t* layer) {
        if (!layers.empty()) {
            layers += L" -> ";
        }
        layers += layer;
    };
    if (!match.profile.fields.empty()) {
        appendLayer(L"Legacy");
    }
    if (!match.profileEx.items.empty()) {
        appendLayer(L"EX");
    }
    if (!match.profileV4.items.empty()) {
        appendLayer(L"V4");
    }
    return layers.empty() ? L"Unavailable" : layers;
}

// CoverageText formats optional coveragePercent. Input is the raw double from
// pack metadata; output is a user-readable percent or <未知>.
std::wstring CoverageText(const double coveragePercent) {
    if (coveragePercent < 0.0) {
        return L"<未知>";
    }
    std::wostringstream stream;
    stream.setf(std::ios::fixed);
    stream.precision(1);
    stream << coveragePercent << L"%";
    return stream.str();
}

} // namespace

DynDataProfileMatch FindMatchingDynDataProfile(const ksword::ark::ArkDynModuleIdentity& identity) {
    DynDataProfileMatch result;
    DynDataProfileMatch invalidMatchedResult;
    std::vector<std::wstring> diagnostics;

    if (!identity.present) {
        result.message = L"当前 ntoskrnl identity 不可用，无法匹配本地 DynData profile pack。";
        return result;
    }

    std::uint32_t existingPackCount = 0;
    std::uint32_t scannedProfileCount = 0;
    for (const std::filesystem::path& candidatePath : ProfilePackSearchPaths()) {
        std::error_code ec;
        if (!std::filesystem::exists(candidatePath, ec) || !std::filesystem::is_regular_file(candidatePath, ec)) {
            AppendDiagnostic(diagnostics, L"pack 不存在: " + candidatePath.wstring());
            continue;
        }

        result.scanned = true;
        existingPackCount += 1U;
        result.existingPackCount = existingPackCount;
        result.path = candidatePath.wstring();

        std::string jsonText;
        std::wstring readError;
        if (!ReadFileBytes(candidatePath, jsonText, readError)) {
            AppendDiagnostic(diagnostics, readError + L": " + candidatePath.wstring());
            continue;
        }

        JsonValue rootObject;
        std::string parseError;
        JsonParser parser(jsonText);
        if (!parser.parse(rootObject, parseError) || !rootObject.isObject()) {
            AppendDiagnostic(diagnostics, L"pack JSON 解析失败: " + candidatePath.wstring() + L" (" + Utf8ToWideLocal(parseError) + L")");
            continue;
        }

        std::uint32_t schemaVersion = 0;
        std::uint32_t packVersion = 0;
        if (!ParseUInt32(rootObject.member("schemaVersion"), schemaVersion)) {
            schemaVersion = 1U;
        }
        if (!ParseUInt32(rootObject.member("packVersion"), packVersion) ||
            schemaVersion != 1U ||
            (packVersion != 1U && packVersion != 2U && packVersion != 3U && packVersion != 4U)) {
            AppendDiagnostic(diagnostics, L"pack schemaVersion/packVersion 不支持: " + candidatePath.wstring());
            continue;
        }

        const JsonValue* profiles = rootObject.member("profiles");
        if (profiles == nullptr || !profiles->isArray() || profiles->arrayValue.empty()) {
            AppendDiagnostic(diagnostics, L"pack profile 列表为空: " + candidatePath.wstring());
            continue;
        }
        const std::uint32_t profileCount = static_cast<std::uint32_t>(std::min<std::size_t>(profiles->arrayValue.size(), std::numeric_limits<std::uint32_t>::max()));
        result.profileCount = profileCount;

        for (const JsonValue& profileObject : profiles->arrayValue) {
            if (!profileObject.isObject()) {
                continue;
            }
            scannedProfileCount += 1U;
            result.scannedProfileCount = scannedProfileCount;
            if (!ProfileMatchesIdentity(identity, profileObject)) {
                continue;
            }
            DynDataProfileMatch matched = BuildMatchedProfileResult(
                identity,
                rootObject,
                profileObject,
                candidatePath,
                packVersion,
                existingPackCount,
                profileCount,
                scannedProfileCount);
            if (matched.valid) {
                return matched;
            }
            invalidMatchedResult = std::move(matched);
            AppendDiagnostic(
                diagnostics,
                L"identity-matched pack entry was invalid");
        }
        AppendDiagnostic(diagnostics, L"pack 未命中: " + candidatePath.wstring() + L" (profiles=" + std::to_wstring(profileCount) + L")");
    }

    if (invalidMatchedResult.matched) {
        return invalidMatchedResult;
    }

    result.message = L"未找到匹配 DynData profile pack；存在 pack=" + std::to_wstring(existingPackCount) +
        L"，扫描 profile=" + std::to_wstring(scannedProfileCount) + L"。";
    const std::wstring detail = JoinWide(diagnostics, L" | ");
    if (!detail.empty()) {
        result.message += L" " + detail;
    }
    result.existingPackCount = existingPackCount;
    result.scannedProfileCount = scannedProfileCount;
    return result;
}

void AppendDynDataProfileRows(KernelOperationResult& result, const DynDataProfileMatch& match, const std::wstring& filterText) {
    PushFilteredRow(result, Row({
        { L"Source", match.source },
        { L"Scanned", match.scanned ? L"是" : L"否" },
        { L"Matched", match.matched ? L"是" : L"否" },
        { L"Valid", match.valid ? L"是" : L"否" },
        { L"ApplyMode", ApplyModeText(match) },
        { L"PackVersion", std::to_wstring(match.packVersion) },
        { L"Profile", Utf8ToWideLocal(match.profile.profileName) },
        { L"PdbName", Utf8ToWideLocal(match.profile.pdbName) },
        { L"PdbGuid", Utf8ToWideLocal(match.profile.pdbGuid) },
        { L"PdbAge", std::to_wstring(match.profile.pdbAge) },
        { L"Coverage", CoverageText(match.coveragePercent) },
        { L"Fields", std::to_wstring(match.profile.fields.size()) + L"/" + std::to_wstring(match.fieldCount) },
        { L"Items", std::to_wstring(match.profileEx.items.size()) + L"/" + std::to_wstring(match.typedItemCount) },
        { L"V4Items", std::to_wstring(match.profileV4.items.size()) + L"/" + std::to_wstring(match.v4ItemCount) },
        { L"V4Groups", std::to_wstring(match.profileV4.capabilityGroups.size()) + L"/" + std::to_wstring(match.v4CapabilityGroupCount) },
        { L"CallbackItems", std::to_wstring(match.callbackItemCount) },
        { L"PackProfiles", std::to_wstring(match.profileCount) },
        { L"ScannedProfiles", std::to_wstring(match.scannedProfileCount) },
        { L"ExistingPacks", std::to_wstring(match.existingPackCount) },
        { L"ProfilePath", match.path },
    }, match.message), filterText);

    PushFilteredRow(result, Row({
        { L"Source", L"Current ntoskrnl Identity" },
        { L"Module", match.profile.ntoskrnl.moduleName },
        { L"Class", std::to_wstring(match.profile.ntoskrnl.classId) },
        { L"Machine", HexTextLocal(match.profile.ntoskrnl.machine) },
        { L"TimeDateStamp", HexTextLocal(match.profile.ntoskrnl.timeDateStamp) },
        { L"SizeOfImage", HexTextLocal(match.profile.ntoskrnl.sizeOfImage) },
        { L"ImageBase", HexTextLocal(match.profile.ntoskrnl.imageBase) },
    }), filterText);

    const std::size_t fieldLimit = std::min<std::size_t>(match.profile.fields.size(), 32U);
    for (std::size_t index = 0; index < fieldLimit; ++index) {
        const ksword::ark::DynDataProfileField& field = match.profile.fields[index];
        PushFilteredRow(result, Row({
            { L"Source", L"Profile Legacy Field" },
            { L"Index", std::to_wstring(index) },
            { L"FieldId", std::to_wstring(field.fieldId) },
            { L"Offset", HexTextLocal(field.offset) },
            { L"ApplyMode", L"Legacy" },
            { L"Profile", Utf8ToWideLocal(match.profile.profileName) },
        }), filterText);
    }
    if (match.profile.fields.size() > fieldLimit) {
        PushFilteredRow(result, Row({
            { L"Source", L"Profile Legacy Field" },
            { L"Status", L"Truncated" },
            { L"Shown", std::to_wstring(fieldLimit) },
            { L"Total", std::to_wstring(match.profile.fields.size()) },
        }), filterText);
    }

    const std::size_t itemLimit = std::min<std::size_t>(match.profileEx.items.size(), 48U);
    for (std::size_t index = 0; index < itemLimit; ++index) {
        const ksword::ark::DynDataProfileExItem& item = match.profileEx.items[index];
        PushFilteredRow(result, Row({
            { L"Source", L"Profile EX Item" },
            { L"Index", std::to_wstring(index) },
            { L"FieldId", std::to_wstring(item.itemId) },
            { L"Kind", (item.itemId == KSW_DYN_FIELD_ID_ULD_TYPE_SIZE || item.itemId == KSW_DYN_FIELD_ID_RTL_AVL_TYPE_SIZE) ? L"TypeSize" : (item.itemKind == KSW_DYN_PROFILE_EX_ITEM_KIND_GLOBAL_RVA ? L"GlobalRva" : L"StructOffset") },
            { L"Value", HexTextLocal(item.value) },
            { L"Flags", HexTextLocal(item.flags) },
            { L"Callback", (item.flags & KSW_DYN_PROFILE_EX_ITEM_FLAG_CALLBACK) != 0U ? L"是" : L"否" },
            { L"Profile", Utf8ToWideLocal(match.profileEx.profileName) },
        }), filterText);
    }
    if (match.profileEx.items.size() > itemLimit) {
        PushFilteredRow(result, Row({
            { L"Source", L"Profile EX Item" },
            { L"Status", L"Truncated" },
            { L"Shown", std::to_wstring(itemLimit) },
            { L"Total", std::to_wstring(match.profileEx.items.size()) },
        }), filterText);
    }

    const std::size_t v4GroupLimit = std::min<std::size_t>(match.profileV4.capabilityGroups.size(), 16U);
    for (std::size_t index = 0; index < v4GroupLimit; ++index) {
        const KSW_DYN_V4_CAPABILITY_GROUP_PACKET& group = match.profileV4.capabilityGroups[index];
        PushFilteredRow(result, Row({
            { L"Source", L"Profile V4 Capability Group" },
            { L"Index", std::to_wstring(index) },
            { L"GroupId", std::to_wstring(group.groupId) },
            { L"Name", Utf8ToWideLocal(group.groupName) },
            { L"RequiredItems", std::to_wstring(group.requiredItemCount) },
            { L"OptionalItems", std::to_wstring(group.optionalItemCount) },
            { L"Flags", HexTextLocal(group.flags) },
        }), filterText);
    }

    const std::size_t v4ItemLimit = std::min<std::size_t>(match.profileV4.items.size(), 64U);
    for (std::size_t index = 0; index < v4ItemLimit; ++index) {
        const KSW_DYN_V4_ITEM_PACKET& item = match.profileV4.items[index];
        PushFilteredRow(result, Row({
            { L"Source", L"Profile V4 Item" },
            { L"Index", std::to_wstring(index) },
            { L"ItemId", std::to_wstring(item.itemId) },
            { L"Kind", std::to_wstring(item.itemKind) },
            { L"GroupId", std::to_wstring(item.capabilityGroupId) },
            { L"Required", (item.flags & KSW_DYN_V4_ITEM_FLAG_REQUIRED) != 0U ? L"是" : L"否" },
            { L"ValueLow", HexTextLocal(item.valueLow) },
            { L"ValueHigh", HexTextLocal(item.valueHigh) },
            { L"Aux0", HexTextLocal(item.aux0) },
            { L"Aux1", HexTextLocal(item.aux1) },
            { L"Aux2", HexTextLocal(item.aux2) },
            { L"Aux3", HexTextLocal(item.aux3) },
        }), filterText);
    }
    if (match.profileV4.items.size() > v4ItemLimit) {
        PushFilteredRow(result, Row({
            { L"Source", L"Profile V4 Item" },
            { L"Status", L"Truncated" },
            { L"Shown", std::to_wstring(v4ItemLimit) },
            { L"Total", std::to_wstring(match.profileV4.items.size()) },
        }), filterText);
    }
}

} // namespace Ksword::Features::Kernel
