win32-captureie 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +3 -0
- data/License.txt +20 -0
- data/Manifest.txt +45 -0
- data/README.txt +49 -0
- data/Rakefile +4 -0
- data/TODO.txt +78 -0
- data/bin/prtie +62 -0
- data/config/hoe.rb +72 -0
- data/config/requirements.rb +17 -0
- data/lib/win32/capture_ie/base.rb +4 -0
- data/lib/win32/capture_ie/bitmap.rb +115 -0
- data/lib/win32/capture_ie/browser.rb +119 -0
- data/lib/win32/capture_ie/cli/base.rb +7 -0
- data/lib/win32/capture_ie/cli/prt_ie.rb +100 -0
- data/lib/win32/capture_ie/commands/base.rb +39 -0
- data/lib/win32/capture_ie/commands/prt_ie.rb +66 -0
- data/lib/win32/capture_ie/ffi/base.rb +22 -0
- data/lib/win32/capture_ie/ffi/gdi32.rb +165 -0
- data/lib/win32/capture_ie/ffi/struct.rb +246 -0
- data/lib/win32/capture_ie/ffi/user32.rb +101 -0
- data/lib/win32/capture_ie/ffi.rb +3 -0
- data/lib/win32/capture_ie/screen_captor.rb +131 -0
- data/lib/win32/capture_ie/version.rb +11 -0
- data/lib/win32/capture_ie/window.rb +47 -0
- data/lib/win32/capture_ie.rb +92 -0
- data/script/destroy +14 -0
- data/script/destroy.cmd +1 -0
- data/script/generate +14 -0
- data/script/generate.cmd +1 -0
- data/script/rdoc_filter.rb +75 -0
- data/script/txt2html +74 -0
- data/script/txt2html.cmd +1 -0
- data/setup.rb +1585 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/win32/capture_ie_spec.rb +11 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/deployment2.rake +90 -0
- data/tasks/environment.rake +7 -0
- data/tasks/helper/rake.rb +58 -0
- data/tasks/helper/rake_sh_filter.rb +23 -0
- data/tasks/helper/util.rb +19 -0
- data/tasks/helper.rb +3 -0
- data/tasks/rspec.rake +21 -0
- data/tasks/website.rake +17 -0
- metadata +95 -0
@@ -0,0 +1,100 @@
|
|
1
|
+
require "optparse"
|
2
|
+
|
3
|
+
require "win32/capture_ie/cli/base"
|
4
|
+
require "win32/capture_ie/commands/prt_ie"
|
5
|
+
|
6
|
+
|
7
|
+
module Win32::CaptureIE::CLI
|
8
|
+
class PrtIE #:nodoc:
|
9
|
+
|
10
|
+
DEFAULT_ARGUMENTS = %W(--output-digest=MD5)
|
11
|
+
|
12
|
+
def self.start(args)
|
13
|
+
self.new.perform(args)
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def arguable(arr_or_arguable)
|
18
|
+
case arr_or_arguable
|
19
|
+
when OptionParser::Arguable
|
20
|
+
arr_or_arguable
|
21
|
+
when Array
|
22
|
+
arr_or_arguable.dup.extend(OptionParser::Arguable)
|
23
|
+
else
|
24
|
+
raise ArgumentError, "Invalid option `#{arr_or_arguable.inspect}:#{arr_or_arguable.class}'" +
|
25
|
+
" (expected Array or OptionParser::Arguable)"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def usage_and_exit(exit_code, message, parser, out=$stdout)
|
30
|
+
m = message.sub(/\.?\z/, ".")
|
31
|
+
out.puts "Error: #{m}"
|
32
|
+
out.puts parser
|
33
|
+
exit(2)
|
34
|
+
end
|
35
|
+
|
36
|
+
def perform(args)
|
37
|
+
opt, parser = parse_options(arguable(args))
|
38
|
+
act = Win32::CaptureIE::Commands::PrtIE::Action.new
|
39
|
+
begin
|
40
|
+
act.perform(opt)
|
41
|
+
rescue ArgumentError => e
|
42
|
+
usage_and_exit(2, e.message, parser)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def option_parser(opt)
|
47
|
+
Proc.new do |parser|
|
48
|
+
parser.program_name = "prtie"
|
49
|
+
parser.version = Win32::CaptureIE::VERSION::STRING
|
50
|
+
parser.banner = "Usage: #{parser.program_name} [options] URL"
|
51
|
+
parser.separator ""
|
52
|
+
parser.on("-w", "--wait=SECONDS", Float, "wait SECONDS between retrievals") {|v|
|
53
|
+
opt.wait = v
|
54
|
+
}
|
55
|
+
parser.on("-d", "--output-directory=DIRECTORY", "save capture to DIRECTORY/...") {|v|
|
56
|
+
opt.outdir = v
|
57
|
+
}
|
58
|
+
parser.on("-o", "--output=FILE", "save capture to FILE") {|v|
|
59
|
+
opt.output = v
|
60
|
+
}
|
61
|
+
parser.on("-D", "--output-digest=ALGORITHM",
|
62
|
+
"use output filename as hex-encoded version",
|
63
|
+
"of a given URL",
|
64
|
+
"ALGORITHM: MD5, SHA1, SHA512 ...") {|v|
|
65
|
+
opt.output_digest = v
|
66
|
+
}
|
67
|
+
|
68
|
+
parser.separator ""
|
69
|
+
parser.on("-v", "--version", "print the version"){
|
70
|
+
puts parser.ver
|
71
|
+
exit 0
|
72
|
+
}
|
73
|
+
parser.on("-h", "--help", "print this message"){
|
74
|
+
puts parser
|
75
|
+
exit 0
|
76
|
+
}
|
77
|
+
|
78
|
+
parser.separator ""
|
79
|
+
parser.separator " Default options:"
|
80
|
+
parser.separator " #{DEFAULT_ARGUMENTS * ' '}"
|
81
|
+
parser
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def parse_options(args)
|
86
|
+
opt = Win32::CaptureIE::Commands::PrtIE::Option.new
|
87
|
+
proc = option_parser(opt)
|
88
|
+
arguable(DEFAULT_ARGUMENTS).options(&proc).parse!
|
89
|
+
parser = args.options(&proc)
|
90
|
+
begin
|
91
|
+
parser.parse!
|
92
|
+
rescue OptionParser::ParseError => e
|
93
|
+
usage_and_exit(2, e.message, parser)
|
94
|
+
end
|
95
|
+
opt.url = ARGV.shift
|
96
|
+
[opt, parser]
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Win32
|
2
|
+
module CaptureIE
|
3
|
+
module Commands #:nodoc:
|
4
|
+
end
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module Win32::CaptureIE::Commands
|
9
|
+
class OptionBase #:nodoc:
|
10
|
+
def self.define_option_category(name)
|
11
|
+
class_eval %Q{
|
12
|
+
@@#{name} = Hash.new{|h,k| h[k] = []}
|
13
|
+
def self.#{name}(*names)
|
14
|
+
attr_accessor *names
|
15
|
+
@@#{name}[self].concat(names)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.#{name}s
|
19
|
+
@@#{name}[self]
|
20
|
+
end
|
21
|
+
|
22
|
+
def init_#{name}s
|
23
|
+
self.class.#{name}s.each do |e|
|
24
|
+
instance_variable_set("@\#{e}", nil)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
define_option_category(:local_option)
|
31
|
+
define_option_category(:global_option)
|
32
|
+
|
33
|
+
def initialize
|
34
|
+
init_local_options
|
35
|
+
init_global_options
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "digest/md5"
|
3
|
+
|
4
|
+
require "win32/capture_ie"
|
5
|
+
require "win32/capture_ie/commands/base"
|
6
|
+
|
7
|
+
|
8
|
+
module Win32::CaptureIE::Commands
|
9
|
+
module PrtIE #:nodoc:
|
10
|
+
|
11
|
+
class Option < OptionBase #:nodoc:
|
12
|
+
global_option :outdir, :wait, :output_digest
|
13
|
+
local_option :output, :url
|
14
|
+
attr_reader :ext
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@ext = "bmp"
|
18
|
+
end
|
19
|
+
|
20
|
+
def check_options
|
21
|
+
raise_arg_error(url, "missing url argument")
|
22
|
+
raise_arg_error(output || output_digest, "missing output or output-digest argument")
|
23
|
+
if output_digest
|
24
|
+
begin
|
25
|
+
Digest(output_digest)
|
26
|
+
rescue => e
|
27
|
+
raise ArgumentError, e.message
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def raise_arg_error(ok, msg)
|
33
|
+
raise ArgumentError, msg unless ok
|
34
|
+
end
|
35
|
+
|
36
|
+
def digest_url
|
37
|
+
Digest(output_digest).hexdigest(url)
|
38
|
+
end
|
39
|
+
|
40
|
+
def fixup!
|
41
|
+
self.wait = 0 if wait.nil? or wait < 0
|
42
|
+
end
|
43
|
+
|
44
|
+
def outfile
|
45
|
+
base = output || "#{digest_url}.#{ext}"
|
46
|
+
r = File.expand_path(base, outdir)
|
47
|
+
dir = File.dirname(r)
|
48
|
+
::FileUtils.mkdir_p(dir) unless File.exist?(dir)
|
49
|
+
r
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class Action #:nodoc:
|
54
|
+
def perform(opt)
|
55
|
+
opt.fixup!
|
56
|
+
opt.check_options
|
57
|
+
Win32::CaptureIE.start do |ie|
|
58
|
+
ie.navigate(opt.url, true)
|
59
|
+
sleep(opt.wait) if opt.wait > 0
|
60
|
+
ie.capture_page(opt.outfile)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "Win32API"
|
2
|
+
|
3
|
+
module Win32::CaptureIE
|
4
|
+
module FFI #:nodoc:
|
5
|
+
end
|
6
|
+
|
7
|
+
class Win32APIError < StandardError #:nodoc:
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module Win32::CaptureIE::FFI
|
12
|
+
module Base #:nodoc:
|
13
|
+
def define_ffi_entry(const, args, ret, lib, entry=const.to_s)
|
14
|
+
api = ::Win32API.new(lib, entry, args, ret)
|
15
|
+
const_set(const, api)
|
16
|
+
define_method(const) do |*args|
|
17
|
+
api.call(*args)
|
18
|
+
end
|
19
|
+
module_function const
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require "win32/capture_ie/ffi/base"
|
2
|
+
require "win32/capture_ie/ffi/struct"
|
3
|
+
|
4
|
+
module Win32::CaptureIE::FFI
|
5
|
+
module GDI32 #:nodoc:
|
6
|
+
extend Win32::CaptureIE::FFI::Base
|
7
|
+
extend Win32::CaptureIE::FFI::CStruct
|
8
|
+
|
9
|
+
define_c_type(:uint8, :BYTE)
|
10
|
+
define_c_type(:uint16, :WORD)
|
11
|
+
define_c_type(:uint32, :DWORD)
|
12
|
+
define_c_type(:int32, :LONG)
|
13
|
+
|
14
|
+
define_c_struct(:RGBQUAD) {
|
15
|
+
BYTE :rgbBlue
|
16
|
+
BYTE :rgbGreen
|
17
|
+
BYTE :rgbRed
|
18
|
+
BYTE :rgbReserved
|
19
|
+
}
|
20
|
+
|
21
|
+
define_c_struct(:BITMAPINFOHEADER) {
|
22
|
+
DWORD :biSize
|
23
|
+
LONG :biWidth
|
24
|
+
LONG :biHeight
|
25
|
+
WORD :biPlanes
|
26
|
+
WORD :biBitCount
|
27
|
+
DWORD :biCompression
|
28
|
+
DWORD :biSizeImage
|
29
|
+
LONG :biXPelsPerMeter
|
30
|
+
LONG :biYPelsPerMeter
|
31
|
+
DWORD :biClrUsed
|
32
|
+
DWORD :biClrImportant
|
33
|
+
}
|
34
|
+
|
35
|
+
define_c_struct(:BITMAPINFO) {
|
36
|
+
BITMAPINFOHEADER :bmiHeader
|
37
|
+
RGBQUAD :bmiColors, :dim => 1
|
38
|
+
}
|
39
|
+
|
40
|
+
define_c_struct(:BITMAPFILEHEADER) {
|
41
|
+
WORD :bfType
|
42
|
+
DWORD :bfSize, :offset => 2
|
43
|
+
WORD :bfReserved1
|
44
|
+
WORD :bfReserved2
|
45
|
+
DWORD :bfOffBits, :offset => 10
|
46
|
+
}
|
47
|
+
|
48
|
+
DRIVERVERSION = 0
|
49
|
+
TECHNOLOGY = 2
|
50
|
+
HORZSIZE = 4
|
51
|
+
VERTSIZE = 6
|
52
|
+
HORZRES = 8
|
53
|
+
VERTRES = 10
|
54
|
+
BITSPIXEL = 12
|
55
|
+
PLANES = 14
|
56
|
+
NUMBRUSHES = 16
|
57
|
+
NUMPENS = 18
|
58
|
+
NUMMARKERS = 20
|
59
|
+
NUMFONTS = 22
|
60
|
+
NUMCOLORS = 24
|
61
|
+
PDEVICESIZE = 26
|
62
|
+
CURVECAPS = 28
|
63
|
+
LINECAPS = 30
|
64
|
+
POLYGONALCAPS = 32
|
65
|
+
TEXTCAPS = 34
|
66
|
+
CLIPCAPS = 36
|
67
|
+
RASTERCAPS = 38
|
68
|
+
ASPECTX = 40
|
69
|
+
ASPECTY = 42
|
70
|
+
ASPECTXY = 44
|
71
|
+
|
72
|
+
DIB_RGB_COLORS = 0
|
73
|
+
DIB_PAL_COLORS = 1
|
74
|
+
|
75
|
+
# Raster operations
|
76
|
+
SRCCOPY = 0x00CC0020
|
77
|
+
SRCPAINT = 0x00EE0086
|
78
|
+
SRCAND = 0x008800C6
|
79
|
+
SRCINVERT = 0x00660046
|
80
|
+
SRCERASE = 0x00440328
|
81
|
+
NOTSRCCOPY = 0x00330008
|
82
|
+
NOTSRCERASE = 0x001100A6
|
83
|
+
MERGECOPY = 0x00C000CA
|
84
|
+
MERGEPAINT = 0x00BB0226
|
85
|
+
PATCOPY = 0x00F00021
|
86
|
+
PATPAINT = 0x00FB0A09
|
87
|
+
PATINVERT = 0x005A0049
|
88
|
+
DSTINVERT = 0x00550009
|
89
|
+
BLACKNESS = 0x00000042
|
90
|
+
WHITENESS = 0x00FF0062
|
91
|
+
|
92
|
+
# constants for the biCompression field
|
93
|
+
BI_RGB = 0
|
94
|
+
BI_RLE8 = 1
|
95
|
+
BI_RLE4 = 2
|
96
|
+
BI_BITFIELDS = 3
|
97
|
+
BI_JPEG = 4
|
98
|
+
BI_PNG = 5
|
99
|
+
|
100
|
+
define_ffi_entry(:BitBlt, "LIIIILIIL", "B", "gdi32")
|
101
|
+
define_ffi_entry(:CreateCompatibleDC, "L", "L", "gdi32")
|
102
|
+
define_ffi_entry(:CreateCompatibleBitmap, "LII", "L", "gdi32")
|
103
|
+
define_ffi_entry(:DeleteDC, "L", "B", "gdi32")
|
104
|
+
define_ffi_entry(:DeleteObject, "L", "B", "gdi32")
|
105
|
+
define_ffi_entry(:GetDIBits, "LLIIPPI", "I", "gdi32")
|
106
|
+
define_ffi_entry(:SelectObject, "LL", "L", "gdi32")
|
107
|
+
|
108
|
+
module_function
|
109
|
+
|
110
|
+
def with_delete_dc(hdc)
|
111
|
+
begin
|
112
|
+
yield hdc
|
113
|
+
ensure
|
114
|
+
DeleteDC(hdc)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def with_delete_object(obj)
|
119
|
+
begin
|
120
|
+
yield obj
|
121
|
+
ensure
|
122
|
+
DeleteObject(obj)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
__END__
|
131
|
+
from WinGDI.h
|
132
|
+
|
133
|
+
typedef struct tagBITMAPINFOHEADER{
|
134
|
+
DWORD biSize;
|
135
|
+
LONG biWidth;
|
136
|
+
LONG biHeight;
|
137
|
+
WORD biPlanes;
|
138
|
+
WORD biBitCount;
|
139
|
+
DWORD biCompression;
|
140
|
+
DWORD biSizeImage;
|
141
|
+
LONG biXPelsPerMeter;
|
142
|
+
LONG biYPelsPerMeter;
|
143
|
+
DWORD biClrUsed;
|
144
|
+
DWORD biClrImportant;
|
145
|
+
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
|
146
|
+
|
147
|
+
typedef struct tagRGBQUAD {
|
148
|
+
BYTE rgbBlue;
|
149
|
+
BYTE rgbGreen;
|
150
|
+
BYTE rgbRed;
|
151
|
+
BYTE rgbReserved;
|
152
|
+
} RGBQUAD;
|
153
|
+
|
154
|
+
typedef struct tagBITMAPINFO {
|
155
|
+
BITMAPINFOHEADER bmiHeader;
|
156
|
+
RGBQUAD bmiColors[1];
|
157
|
+
} BITMAPINFO, FAR *LPBITMAPINFO, *PBITMAPINFO;
|
158
|
+
|
159
|
+
typedef struct tagBITMAPFILEHEADER {
|
160
|
+
WORD bfType;
|
161
|
+
DWORD bfSize;
|
162
|
+
WORD bfReserved1;
|
163
|
+
WORD bfReserved2;
|
164
|
+
DWORD bfOffBits;
|
165
|
+
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;
|
@@ -0,0 +1,246 @@
|
|
1
|
+
require "win32/capture_ie/ffi/base"
|
2
|
+
|
3
|
+
module Win32::CaptureIE::FFI
|
4
|
+
|
5
|
+
class Type #:nodoc:
|
6
|
+
attr_reader :name
|
7
|
+
def initialize(name)
|
8
|
+
@name = name
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class PrimitiveType < Type #:nodoc:
|
13
|
+
BASE_TYPES = {
|
14
|
+
:int8 => ["c", 1],
|
15
|
+
:uint8 => ["C", 1],
|
16
|
+
:int16 => ["s", 2],
|
17
|
+
:uint16 => ["S", 2],
|
18
|
+
:int32 => ["l", 4],
|
19
|
+
:uint32 => ["L", 4],
|
20
|
+
:float => ["f", 4],
|
21
|
+
:double => ["d", 8],
|
22
|
+
:string => ["p", 4],
|
23
|
+
:pointer => ["P", 4],
|
24
|
+
}
|
25
|
+
|
26
|
+
attr_reader :size, :format
|
27
|
+
def initialize(name, size=guess_size(name), format=guess_format(name))
|
28
|
+
super(name)
|
29
|
+
@size = size
|
30
|
+
@format = format
|
31
|
+
end
|
32
|
+
|
33
|
+
def pack(value)
|
34
|
+
[value].pack(format)
|
35
|
+
end
|
36
|
+
|
37
|
+
def guess_format(name)
|
38
|
+
BASE_TYPES[name][0]
|
39
|
+
end
|
40
|
+
private :guess_format
|
41
|
+
|
42
|
+
def guess_size(name)
|
43
|
+
BASE_TYPES[name][1]
|
44
|
+
end
|
45
|
+
private :guess_size
|
46
|
+
end
|
47
|
+
|
48
|
+
class StructureType < Type #:nodoc:
|
49
|
+
PACKING_ALIGN = 4
|
50
|
+
|
51
|
+
attr_reader :name, :size, :ruby_class, :fields
|
52
|
+
def initialize(name, size=nil)
|
53
|
+
super(name)
|
54
|
+
@ruby_class = nil
|
55
|
+
@fields = []
|
56
|
+
@size = size
|
57
|
+
end
|
58
|
+
|
59
|
+
def calc_offsets
|
60
|
+
offset = 0
|
61
|
+
@fields.each do |f|
|
62
|
+
align = [f.type.size, PACKING_ALIGN].min
|
63
|
+
offset = (offset.to_f / align).ceil * align
|
64
|
+
size = f.size
|
65
|
+
if f.offset.nil?
|
66
|
+
f.offset = offset
|
67
|
+
offset += size
|
68
|
+
else
|
69
|
+
offset = f.offset
|
70
|
+
offset += size
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def calc_size
|
76
|
+
return @size if @size
|
77
|
+
if @fields.empty?
|
78
|
+
@size = 0
|
79
|
+
return
|
80
|
+
end
|
81
|
+
align = [@fields.first.type.size, PACKING_ALIGN].min
|
82
|
+
offset = @fields.last.offset + @fields.last.size
|
83
|
+
@size = (offset.to_f / align).ceil * align
|
84
|
+
end
|
85
|
+
|
86
|
+
def pack(value)
|
87
|
+
packed = @fields.zip(value.values).map{|f,v| [f.offset, f.pack(v)] }
|
88
|
+
r = ""
|
89
|
+
packed.each do |offset,c|
|
90
|
+
align!(r, offset)
|
91
|
+
r << c
|
92
|
+
end
|
93
|
+
align!(r, size)
|
94
|
+
r
|
95
|
+
end
|
96
|
+
|
97
|
+
def align!(r, offset)
|
98
|
+
if offset < r.length
|
99
|
+
raise "buffer over flow: expected offset #{offset}, but was #{r.length}"
|
100
|
+
end
|
101
|
+
if r.length < offset
|
102
|
+
r << "\0" * (offset - r.length)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def define!(ns)
|
107
|
+
calc_offsets
|
108
|
+
calc_size
|
109
|
+
define_ruby_class(ns)
|
110
|
+
end
|
111
|
+
|
112
|
+
def define_ruby_class(ns)
|
113
|
+
types = self
|
114
|
+
name = @name
|
115
|
+
clazz = Class.new
|
116
|
+
clazz.instance_eval {
|
117
|
+
types.fields.each {|f|
|
118
|
+
attr_accessor f.name
|
119
|
+
}
|
120
|
+
define_method(:initialize) {|*args|
|
121
|
+
types.fields.zip(args).each {|f,arg|
|
122
|
+
instance_variable_set(f.var, arg || f.init)
|
123
|
+
}
|
124
|
+
}
|
125
|
+
define_method(:pack) {
|
126
|
+
types.pack(self)
|
127
|
+
}
|
128
|
+
}
|
129
|
+
clazz.class_eval %Q{
|
130
|
+
def names
|
131
|
+
[#{types.fields.map{|f| ":#{f.name}"} * ','}]
|
132
|
+
end
|
133
|
+
def values
|
134
|
+
[#{types.fields.map{|f| f.var} * ','}]
|
135
|
+
end
|
136
|
+
}
|
137
|
+
(class <<clazz; self; end).instance_eval {
|
138
|
+
define_method(:packed_size) {
|
139
|
+
CStruct.sizeof(name)
|
140
|
+
}
|
141
|
+
define_method(:c_type) {
|
142
|
+
CStruct.c_type(name)
|
143
|
+
}
|
144
|
+
}
|
145
|
+
@ruby_class = clazz
|
146
|
+
ns.const_set(@name, clazz)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
class Field #:nodoc:
|
151
|
+
attr_reader :type, :name, :count, :var
|
152
|
+
attr_accessor :offset
|
153
|
+
def initialize(type, name, opts=nil)
|
154
|
+
@type = type
|
155
|
+
@name = name
|
156
|
+
@var = "@#{name}".to_sym
|
157
|
+
@opts = opts || {}
|
158
|
+
@offset = @opts[:offset] || nil
|
159
|
+
@count = @opts[:dim] || 1
|
160
|
+
end
|
161
|
+
|
162
|
+
def array?
|
163
|
+
count > 0
|
164
|
+
end
|
165
|
+
|
166
|
+
def init
|
167
|
+
if array?
|
168
|
+
init_one
|
169
|
+
else
|
170
|
+
(0...count).map{ init_one }
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def size
|
175
|
+
@type.size * count
|
176
|
+
end
|
177
|
+
|
178
|
+
def pack(value)
|
179
|
+
if count == 1
|
180
|
+
@type.pack(value)
|
181
|
+
else
|
182
|
+
raise "hoge" unless Array === value
|
183
|
+
raise "foo" unless value.length == count
|
184
|
+
value.map{|e| @type.pack(e) }.join
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def init_one
|
189
|
+
if PrimitiveType === @type
|
190
|
+
0
|
191
|
+
else
|
192
|
+
@type.ruby_class.new
|
193
|
+
end
|
194
|
+
end
|
195
|
+
private :init_one
|
196
|
+
end
|
197
|
+
|
198
|
+
module CStruct #:nodoc:
|
199
|
+
TYPES = PrimitiveType::BASE_TYPES.inject({}){|h,t|
|
200
|
+
h[t[0]] = PrimitiveType.new(t[0])
|
201
|
+
h
|
202
|
+
}
|
203
|
+
|
204
|
+
def define_c_struct(name, size=nil, &block)
|
205
|
+
define_c_struct_under(self, name, size, &block)
|
206
|
+
end
|
207
|
+
|
208
|
+
module_function
|
209
|
+
|
210
|
+
def define_c_struct_under(ns, name, size=nil, &block)
|
211
|
+
dsl = DSL.new(name, size)
|
212
|
+
dsl.instance_eval(&block)
|
213
|
+
dsl.struct.define!(ns)
|
214
|
+
TYPES[name] = dsl.struct
|
215
|
+
dsl.struct
|
216
|
+
end
|
217
|
+
|
218
|
+
def sizeof(type)
|
219
|
+
c_type(type).size
|
220
|
+
end
|
221
|
+
|
222
|
+
def c_type(type)
|
223
|
+
TYPES[type]
|
224
|
+
end
|
225
|
+
|
226
|
+
def define_c_type(type, decl)
|
227
|
+
TYPES[decl] = TYPES[type]
|
228
|
+
end
|
229
|
+
|
230
|
+
class DSL #:nodoc:
|
231
|
+
attr_reader :struct
|
232
|
+
|
233
|
+
def initialize(name, size)
|
234
|
+
@struct = StructureType.new(name, size)
|
235
|
+
end
|
236
|
+
|
237
|
+
def method_missing(name, *args)
|
238
|
+
type = CStruct.c_type(name)
|
239
|
+
raise "unknown type: #{name}" unless type
|
240
|
+
name = args.shift
|
241
|
+
@struct.fields << Field.new(type, name, *args)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
end
|