lib/types.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
using namespace System.Reflection
using namespace System.Reflection.Emit
using namespace System.Collections.Specialized
using namespace System.Runtime.InteropServices

function New-Enum {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory, Position=0)]
    [ValidateNotNullOrEmpty()]
    [String]$Name,

    [Parameter(Mandatory, Position=1)]
    [ValidateScript({![String]::IsNullOrEmpty($_)})]
    [ScriptBlock]$Definition,

    [Parameter()]
    [Type]$Type = [Int32],

    [Parameter()]
    [Switch]$Flags
  )

  end {
    if (!($pmb = Get-DynBuilder).GetType($Name)) {
      $enm = $pmb.DefineEnum($Name, 'Public', $Type)
      if ($Flags) {
        $enm.SetCustomAttribute((
          [CustomAttributeBuilder]::new([FlagsAttribute].GetConstructor(@()), @())
        ))
      }

      $i = 0
      $Definition.Ast.FindAll({$args[0].CommandElements}, $true).ToArray().ForEach{
        $fn, $$, $fv = $_.CommandElements
        $i = [BitConverter]::"To$($Type.Name)"([BitConverter]::GetBytes($fv.Value ?? $i))
        [void]$enm.DefineLiteral($fn.Value, $i)
        $i+=1
      }
      [void]$enm.CreateType()
    }
  }
}

function New-Structure {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory, Position=0)]
    [ValidateNotNullOrEmpty()]
    [String]$Name,

    [Parameter(Mandatory, Position=1)]
    [ValidateScript({![String]::IsNullOrEmpty($_)})]
    [ScriptBlock]$Definition,

    [Parameter()]
    [ValidateSet(
      'Unspecified', 'Size1', 'Size2', 'Size4', 'Size8', 'Size16', 'Size32', 'Size64', 'Size128'
    )]
    [ValidateNotNullOrEmpty()]
    [PackingSize]$PackingSize = 'Unspecified',

    [Parameter()]
    [ValidateSet('Ansi', 'Auto', 'Unicode')]
    [ValidateNotNullOrEmpty()]
    [CharSet]$CharSet = 'Ansi',

    [Parameter()]
    [Switch]$Explicit
  )

  begin {
    [TypeAttributes]$attr = 'BeforeFieldInit, Class, Public, Sealed'
    $attr = $attr -bor ($Explicit.IsPresent ? 'Explicit' : 'Sequential') -bor "$($CharSet)Class"
  }
  process {}
  end {
    if (!($pmb = Get-DynBuilder).GetType($Name)) {
      $type = $pmb.DefineType($Name, $attr, [ValueType], $PackingSize)
      $ctor = [MarshalAsAttribute].GetConstructor(
        [BindingFlags]'Instance, Public', $null, [Type[]]([UnmanagedType]), $null
      )
      $sc = [MarshalAsAttribute].GetField('SizeConst')

      $Definition.Ast.FindAll({$args[0].CommandElements}, $true).ToArray().ForEach{
        $ftype, $fdesc = $_.CommandElements.Value
        $ftype = $pmb.GetType($ftype) ?? [Type]$ftype
        $fdesc = @(($fdesc -split '\s+?').Where{$_}) # field, param ...
        switch ($fdesc.Length) {
          1 {[void]$type.DefineField($fdesc[0], $ftype, 'Public')}
          2 {
            [void]($Explicit.IsPresent ? $type.DefineField($fdesc[0], $ftype, 'Public'
            ).SetOffset([Int32]$fdesc[1]) : $type.DefineField(
              $fdesc[0], $ftype, 'Public, HasFieldMarshal'
            ).SetCustomAttribute(
              [CustomAttributeBuilder]::new($ctor, [Object]([UnmanagedType]$fdesc[1]))
            ))
          }
          3 {
            [void]$type.DefineField($fdesc[0], $ftype, 'Public, HasFieldMarshal'
            ).SetCustomAttribute(
              [CustomAttributeBuilder]::new($ctor, [UnmanagedType]$fdesc[1], $sc, ([Int32]$fdesc[2]))
            )
          }
        }
      }
      $il = $type.DefineMethod('GetSize', 'Public, Static', [Int32], [Type[]]@()).GetILGenerator()
      $il.Emit([OpCodes]::ldtoken, $type)
      $il.Emit([OpCodes]::call, [Type].GetMethod('GetTypeFromHandle'))
      $il.Emit([OpCodes]::call, [Marshal].GetMethod('SizeOf', [Type[]]([Type])))
      $il.Emit([OpCodes]::ret)
      $il = $type.DefineMethod('OfsOf', 'Public, Static', [Int32], [Type[]]@([String])).GetILGenerator()
      $local = $il.DeclareLocal([String])
      $il.Emit([OpCodes]::ldtoken, $type)
      $il.Emit([OpCodes]::call, [Type].GetMethod('GetTypeFromHandle'))
      $il.Emit([OpCodes]::ldarg_0)
      $il.Emit([OpCodes]::call, [Marshal].GetMethod('OffsetOf', [Type[]]([Type], [String])))
      $il.Emit([OpCodes]::stloc_0)
      $il.Emit([OpCodes]::ldloca_s, $local)
      $il.Emit([OpCodes]::call, [IntPtr].GetMethod('ToInt32', [Type[]]@()))
      $il.Emit([OpCodes]::ret)
      $il = $type.DefineMethod(
        'op_Implicit', 'PrivateScope, Public, Static, HideBySig, SpecialName', $type, [Type]([IntPtr])
      ).GetILGenerator()
      $il.Emit([OpCodes]::ldarg_0)
      $il.Emit([OpCodes]::ldtoken, $type)
      $il.Emit([OpCodes]::call, [Type].GetMethod('GetTypeFromHandle'))
      $il.Emit([OpCodes]::call, [Marshal].GetMethod('PtrToStructure', [Type[]]([IntPtr], [Type])))
      $il.Emit([OpCodes]::unbox_any, $type)
      $il.Emit([OpCodes]::ret)
      [void]$type.CreateType()
    }
  }
}

function ConvertTo-BitMap {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory, Position=0)]
    [ValidateNotNullOrEmpty()]
    [Object]$Value,

    [Parameter(Mandatory, Position=1)]
    [ValidateScript({![String]::IsNullOrEmpty($_)})]
    [ScriptBlock]$BitMap
  )

  end {
    $vtor = [BitVector32]::new($Value)
    [PSCustomObject](ConvertFrom-StringData (
      ($BitMap.Ast.FindAll({$args[0].CommandElements}, $true).ToArray().ForEach{
        $fname, $fbits = $_.CommandElements[0, 2]
        $mov = !$mov ? [BitVector32]::CreateSection($fbits.Value)
                     : [BitVector32]::CreateSection($fbits.Value, $mov)
        '{0} = {1}' -f $fname.Value, $vtor[$mov]
         }) | Out-String)
    )
  }
}

function ConvertTo-PointerOrStructure {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory, Position=0)]
    [ValidateNotNullOrEmpty()]
    [Byte[]]$Buffer,

    [Parameter(Position=1)]
    [ValidateNotNull()]
    [Type]$Type
  )

  end {
    try {
      $gch = [GCHandle]::Alloc($Buffer, [GCHandleType]::Pinned)
      if ($Type) { $gch.AddrOfPinnedObject() -as $Type }
      else { $gch.AddrOfPinnedObject() }
    }
    catch { Write-Verbose $_ }
    finally {
      if ($gch) { $gch.Free() }
    }
  }
}