wdext 0.1.0

A DbgEng wrapper framework
// SPDX-FileCopyrightText: 2026 takubokudori
// SPDX-License-Identifier: MIT OR Apache-2.0
//! Rust-friendly wrapper for callback-provided TTD `IThreadView`.
//!
//! `IThreadView` is a non-owning native C++ interface. It has no UUID and does
//! not implement COM/IUnknown semantics.

use crate::{
    impl_ttd_view,
    ttd::{
        memory::{QueriedMemory, QueriedMemoryRange, QueriedMemoryWithRanges},
        raw::bindings::*,
    },
};
use std::ffi::c_void;

impl_ttd_view!(ThreadView, IThreadView);

// IThreadView
impl ThreadView {
    /// Returns a snapshot of the callback thread metadata.
    #[inline]
    pub fn get_thread_info(&self) -> ThreadInfo {
        unsafe { *self.0.GetThreadInfo() }
    }

    #[inline]
    pub fn get_teb_address(&self) -> GuestAddress {
        unsafe { self.0.GetTebAddress() }
    }

    /// Returns a snapshot of the current thread position.
    #[inline]
    pub fn get_position(&self) -> Position { unsafe { *self.0.GetPosition() } }

    /// Returns a snapshot of the previous valid position for this thread.
    #[inline]
    pub fn get_previous_position(&self) -> Position {
        unsafe { self.0.GetPreviousPosition() }
    }

    #[inline]
    pub fn get_program_counter(&self) -> GuestAddress {
        unsafe { self.0.GetProgramCounter() }
    }

    #[inline]
    pub fn get_stack_pointer(&self) -> GuestAddress {
        unsafe { self.0.GetStackPointer() }
    }

    #[inline]
    pub fn get_frame_pointer(&self) -> GuestAddress {
        unsafe { self.0.GetFramePointer() }
    }

    #[inline]
    pub fn get_basic_return_value(&self) -> u64 {
        unsafe { self.0.GetBasicReturnValue() }
    }

    #[inline]
    pub fn get_cross_platform_context(&self) -> RegisterContext {
        unsafe { self.0.GetCrossPlatformContext() }
    }

    #[inline]
    pub fn get_avx_extended_context(&self) -> ExtendedRegisterContext {
        unsafe { self.0.GetAvxExtendedContext() }
    }

    /// Queries a contiguous range backed by TTD-owned storage.
    ///
    /// The SDK states that the returned buffer is valid only until the next
    /// memory query. A mutable borrow prevents another safe memory query through
    /// this wrapper while the returned view is alive.
    #[inline]
    pub fn query_memory_range<'a>(
        &'a mut self,
        address: GuestAddress,
    ) -> QueriedMemoryRange<'a> {
        let result = unsafe { self.0.QueryMemoryRange(address) };
        unsafe { QueriedMemoryRange::from_ttd_owned(result) }
    }

    /// Queries memory into caller-owned storage.
    ///
    /// The returned memory slice borrows `buffer`, so it cannot outlive the
    /// storage populated by TTD.
    pub fn query_memory_buffer<'a>(
        &self,
        address: GuestAddress,
        buffer: &'a mut [u8],
    ) -> QueriedMemory<'a> {
        let raw_buffer = BufferView {
            BaseAddress: buffer.as_mut_ptr().cast(),
            Size: buffer.len(),
        };

        let result = unsafe { self.0.QueryMemoryBuffer(address, raw_buffer) };
        QueriedMemory::from_raw(buffer, result)
    }

    /// Queries memory and records the native source ranges in `ranges`.
    ///
    /// The returned slices borrow both caller-owned buffers and therefore
    /// cannot outlive the storage populated by TTD.
    pub fn query_memory_buffer_with_ranges<'a, 'r>(
        &self,
        address: GuestAddress,
        buffer: &'a mut [u8],
        ranges: &'r mut [MemoryRange],
    ) -> QueriedMemoryWithRanges<'a, 'r> {
        let raw_buffer = BufferView {
            BaseAddress: buffer.as_mut_ptr().cast(),
            Size: buffer.len(),
        };

        let result = unsafe {
            self.0
                .QueryMemoryBufferWithRanges(address, raw_buffer, ranges)
        };
        QueriedMemoryWithRanges::from_raw(buffer, ranges, result)
    }
}

// Non-interface helper.
impl ThreadView {
    /// Wraps the `IThreadView const*` supplied by TTD replay callbacks.
    ///
    /// # Safety
    ///
    /// `raw` must either be null or point to a live SDK-compatible
    /// `IThreadView` for every use of the returned wrapper. The wrapper does
    /// not own or extend the native object's lifetime. Callers should normally
    /// keep it within the callback invocation.
    pub unsafe fn _from_callback_raw(raw: *const c_void) -> Option<Self> {
        let raw = unsafe { IThreadView::from_raw(raw) }?;
        Some(Self::from(raw))
    }
}