· 6 years ago · Apr 15, 2020, 12:58 AM
1#==============================================================================
2# Win32Utils | v1.0.0 | por Brandt
3#
4# para RPG Maker VX Ace
5#------------------------------------------------------------------------------
6# Introduz recursos úteis na manipulação de funções da Win32API.
7#==============================================================================
8
9module Win32Utils
10 VERSION = [1, 0, 0]
11end
12
13#==============================================================================
14# ** Win32Utils::RawBuffer
15#------------------------------------------------------------------------------
16# Classe para manipulação de espaço em memória.
17#==============================================================================
18
19class Win32Utils::RawBuffer
20 include Enumerable
21
22 #--------------------------------------------------------------------------
23 # * Funções da Win32 API
24 #--------------------------------------------------------------------------
25 VirtualAlloc = Win32API.new('kernel32', 'VirtualAlloc', 'plll', 'l')
26 CopyMemory = Win32API.new('kernel32', 'RtlMoveMemory', 'ppl', 'v')
27 VirtualProtect = Win32API.new('kernel32', 'VirtualProtect', 'pllp', 'i')
28 VirtualFree = Win32API.new('kernel32', 'VirtualFree', 'pll', 'i')
29
30 #--------------------------------------------------------------------------
31 # * Constantes
32 #--------------------------------------------------------------------------
33 MEM_COMMIT = 0x00001000
34 MEM_RELEASE = 0x00008000
35 PAGE_READWRITE = 0x00000004
36 PAGE_EXECUTE_READ = 0x00000020
37
38 #--------------------------------------------------------------------------
39 # * Variáveis públicas
40 #--------------------------------------------------------------------------
41 attr_reader :address
42 attr_reader :size
43
44 #--------------------------------------------------------------------------
45 # * Inicialização do objeto
46 # pointer : Ponteiro do buffer
47 # size : Tamanho do buffer
48 #--------------------------------------------------------------------------
49 def initialize(pointer, size)
50 @address = pointer
51 @size = size
52 end
53
54 #--------------------------------------------------------------------------
55 # * Marca o buffer como executável
56 #--------------------------------------------------------------------------
57 def executable!
58 dummy = Win32Utils::RawBuffer.malloc(4)
59 VirtualProtect.call(@address, @size, PAGE_EXECUTE_READ, dummy)
60 dummy.dispose
61 end
62
63 #--------------------------------------------------------------------------
64 # * Libera o buffer
65 #--------------------------------------------------------------------------
66 def dispose
67 VirtualFree.call(@address, 0, MEM_RELEASE)
68 end
69
70 #--------------------------------------------------------------------------
71 # * Obtém um slice do buffer
72 # index : Índice inicial
73 # size : Tamanho
74 #--------------------------------------------------------------------------
75 def [](index, size = nil)
76 index %= @size
77 if size
78 Win32Utils::RawBuffer.new(@address + index, size)
79 else
80 to_s[index].ord
81 end
82 end
83
84 #--------------------------------------------------------------------------
85 # * Define um slice
86 # index : Índice
87 #--------------------------------------------------------------------------
88 def []=(index, value)
89 index %= @size
90 size = value.size
91 raise IndexError, 'Buffer overflow' if index + size > @size
92
93 CopyMemory.call(@address + index, value, size)
94 end
95
96 #--------------------------------------------------------------------------
97 # * Copia um buffer
98 # buffer
99 #--------------------------------------------------------------------------
100 def set(buffer)
101 raise IndexError, 'Buffer overflow' if buffer.size > @size
102
103 CopyMemory.call(@address, buffer, buffer.size)
104 end
105
106 #--------------------------------------------------------------------------
107 # * Converte o buffer em string
108 #--------------------------------------------------------------------------
109 def to_str
110 DL::CPtr.new(@address, @size).to_str
111 end
112
113 alias to_s to_str
114
115 #--------------------------------------------------------------------------
116 # * Converte o buffer em inteiro
117 #--------------------------------------------------------------------------
118 alias to_int address
119 alias to_i address
120
121 #--------------------------------------------------------------------------
122 # * Percorre o buffer
123 #--------------------------------------------------------------------------
124 def each(&block)
125 to_str.unpack('C*').each(&block)
126 end
127
128 #--------------------------------------------------------------------------
129 # * Aloca memória e cria um buffer
130 # size : Tamanho desejado
131 #--------------------------------------------------------------------------
132 def self.malloc(size)
133 pointer = alloc_memory(size)
134 new(pointer, size)
135 end
136
137 #--------------------------------------------------------------------------
138 # * Aloca um espaço de memória
139 # size : Tamanho desejado
140 #--------------------------------------------------------------------------
141 def self.alloc_memory(size)
142 VirtualAlloc.call(0, size, MEM_COMMIT, PAGE_READWRITE)
143 end
144
145 #--------------------------------------------------------------------------
146 # * Copia um buffer
147 # buffer : Buffer ou String
148 #--------------------------------------------------------------------------
149 def self.copy(buffer)
150 dest = malloc(buffer.size)
151 dest.set(buffer)
152 dest
153 end
154end
155
156#==============================================================================
157# ** Win32Utils::IntBuffer
158#------------------------------------------------------------------------------
159# Classe para manipulação de arrays de números inteiros de tamanho fixo em
160# memória.
161#==============================================================================
162
163class Win32Utils::IntBuffer < Win32Utils::RawBuffer
164 #--------------------------------------------------------------------------
165 # * Constantes (codificação)
166 #--------------------------------------------------------------------------
167 VALID_ENCODINGS = 'CcSsLlQqJjIi'.chars.map do |c|
168 [c, c + '_', c + '!', c + '>', c + '!>', c + '<', c + '!<']
169 end.flatten + 'NnVvUw'.chars.to_a
170
171 #--------------------------------------------------------------------------
172 # * Inicialização do objeto
173 # pointer : Ponteiro do buffer
174 # size : Tamanho do buffer
175 # encoding : Codificação dos valores (ver String#unpack)
176 #--------------------------------------------------------------------------
177 def initialize(pointer, size, encoding = 'L')
178 unless VALID_ENCODINGS.include?(encoding)
179 raise ArgumentError, "Unknown integer encoding `#{encoding}'"
180 end
181
182 @encoding = encoding
183 @item_size = [0].pack(@encoding).size
184 super(pointer, size * @item_size)
185 end
186
187 #--------------------------------------------------------------------------
188 # * Tamanho real do array
189 #--------------------------------------------------------------------------
190 def real_size
191 size / @item_size
192 end
193
194 #--------------------------------------------------------------------------
195 # * Obtém um slice do buffer
196 # index : Índice inicial
197 # size : Tamanho
198 #--------------------------------------------------------------------------
199 def [](index, size = nil)
200 index %= @size
201 index *= @item_size
202 if size
203 size *= @item_size
204 Win32Utils::RawBuffer.new(@address + index, size)
205 else
206 to_s[index].unpack(@encoding).first
207 end
208 end
209
210 #--------------------------------------------------------------------------
211 # * Define um slice
212 # index : Índice
213 #--------------------------------------------------------------------------
214 def []=(index, value)
215 index %= @size
216 index *= @item_size
217 raise IndexError, 'Buffer overflow' if index + @item_size > @size
218
219 CopyMemory.call(@address + index, [value].pack(@encoding), @item_size)
220 end
221
222 #--------------------------------------------------------------------------
223 # * Percorre o buffer
224 #--------------------------------------------------------------------------
225 def each(&block)
226 to_str.unpack("#{@encoding}*").each(&block)
227 end
228
229 #--------------------------------------------------------------------------
230 # * Aloca um buffer
231 # size : Tamanho desejado
232 # encoding : Codificação
233 #--------------------------------------------------------------------------
234 def self.malloc(size, encoding = 'L')
235 item_size = [0].pack(encoding).size
236 pointer = Win32Utils::RawBuffer.alloc_memory(size * item_size)
237 Win32Utils::IntBuffer.new(pointer, size, encoding)
238 end
239end
240
241#==============================================================================
242# ** Win32Utils::CString
243#------------------------------------------------------------------------------
244# Módulo de funções úteis para manipulação de strings estilo C.
245#==============================================================================
246
247module Win32Utils::CString
248 #--------------------------------------------------------------------------
249 # * Constantes (Code Pages)
250 #--------------------------------------------------------------------------
251 CP_ACP = 0
252 CP_UTF8 = 0xfde9
253
254 #--------------------------------------------------------------------------
255 # * Funções da Win32 API
256 #--------------------------------------------------------------------------
257 MultibyteToWideChar = Win32API.new('kernel32', 'MultiByteToWideChar',
258 'ilpipi', 'i')
259 WideCharToMultiByte = Win32API.new('kernel32', 'WideCharToMultiByte',
260 'ilpipill', 'i')
261
262 #--------------------------------------------------------------------------
263 # * Converte uma string multibyte em wide char
264 # string : String mutlibyte
265 #--------------------------------------------------------------------------
266 def multibyte_to_wide_char(string)
267 return string if string == 0
268
269 wide_char = Win32Utils::IntBuffer.malloc(string.size + 1, 'S')
270 MultibyteToWideChar.call(
271 CP_ACP,
272 0,
273 string.dup,
274 string.size,
275 wide_char.address,
276 string.size
277 )
278 wide_char.to_s[0...-1]
279 end
280
281 #--------------------------------------------------------------------------
282 # * Converte uma string wide char em utf-8
283 # string : String wide char
284 #--------------------------------------------------------------------------
285 def multibyte_to_utf8(string)
286 wide_char_to_utf8(multibyte_to_wide_char(string))
287 end
288
289 #--------------------------------------------------------------------------
290 # * Converte uma string wide char em multibyte
291 # string : String wide char
292 #--------------------------------------------------------------------------
293 def wide_char_to_multibyte(string)
294 return string if string == 0
295
296 multi_byte = Win32Utils::RawBuffer.malloc(string.size)
297 WideCharToMultiByte.call(
298 CP_UTF8,
299 0,
300 string,
301 string.size,
302 multi_byte.address,
303 string.size,
304 0,
305 0
306 )
307 multi_byte.to_s[0...-1]
308 end
309
310 alias wide_char_to_utf8 wide_char_to_multibyte
311end
312
313#==============================================================================
314# ** Win32Exception
315#------------------------------------------------------------------------------
316# Classe das exceções geradas por chamadas à Win32API.
317#==============================================================================
318
319class Win32Exception < RuntimeError
320 include Win32Utils::CString
321 #--------------------------------------------------------------------------
322 # * Constantes
323 #--------------------------------------------------------------------------
324 FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100
325 FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
326 FORMAT_MESSAGE_FROM_HMODULE = 0x00000800
327 FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200
328
329 #--------------------------------------------------------------------------
330 # * Funções da Win32 API
331 #--------------------------------------------------------------------------
332 GetLastError = Win32API.new('kernel32', 'GetLastError', 'v', 'l')
333
334 GetModuleHandle = Win32API.new('kernel32', 'GetModuleHandleA', 'p', 'l')
335
336 FormatMessage = Win32API.new('kernel32', 'FormatMessage', 'llllpll', 'l')
337
338 #--------------------------------------------------------------------------
339 # * Inicialização do objeto
340 # error_code : Código de erro (opcional)
341 #--------------------------------------------------------------------------
342 def initialize(error_code = nil)
343 @error_code = error_code || GetLastError.call
344 super(formatted_message)
345 end
346
347 #--------------------------------------------------------------------------
348 # * Mensagem da exceção
349 #--------------------------------------------------------------------------
350 def message
351 buffer = Array.new(1024, 0).pack('C*')
352 FormatMessage.call(
353 FORMAT_MESSAGE_FROM_SYSTEM,
354 0,
355 @error_code,
356 0,
357 buffer,
358 1024,
359 0
360 )
361 multibyte_to_utf8(buffer.rstrip)
362 end
363
364 #--------------------------------------------------------------------------
365 # * Mensagem da exceção formatada com o código de erro
366 #--------------------------------------------------------------------------
367 def formatted_message
368 format('%<message>s (0x%<error>08x)',
369 error: @error_code,
370 message: message)
371 end
372end
373
374#==============================================================================
375# ** Win32Utils::Lock
376#------------------------------------------------------------------------------
377# Trava de segurança para evitar problemas com paralelismo.
378#==============================================================================
379
380class Win32Utils::Lock
381 #--------------------------------------------------------------------------
382 # * Funções da Win32 API
383 #--------------------------------------------------------------------------
384 InitializeCriticalSection = Win32API.new('kernel32',
385 'InitializeCriticalSection',
386 'p', 'v')
387 EnterCriticalSection = Win32API.new('kernel32',
388 'InitializeCriticalSection',
389 'p', 'v')
390 LeaveCriticalSection = Win32API.new('kernel32',
391 'InitializeCriticalSection',
392 'p', 'v')
393
394 #--------------------------------------------------------------------------
395 # * Inicialização do objeto
396 #--------------------------------------------------------------------------
397 def initialize
398 @buffer = Win32Utils::RawBuffer.malloc(40)
399 InitializeCriticalSection.call(@buffer.address)
400 end
401
402 #--------------------------------------------------------------------------
403 # * Tranca o objeto
404 #--------------------------------------------------------------------------
405 def lock
406 EnterCriticalSection.call(@buffer.address)
407 end
408
409 #--------------------------------------------------------------------------
410 # * Libera o objeto
411 #--------------------------------------------------------------------------
412 def release
413 LeaveCriticalSection.call(@buffer.address)
414 end
415
416 #--------------------------------------------------------------------------
417 # * Ponteiro do objeto
418 #--------------------------------------------------------------------------
419 def address
420 @buffer.address
421 end
422end
423
424#==============================================================================
425# ** Win32Utils::ASMBuilder
426#------------------------------------------------------------------------------
427# Classe para construção de instruções nativas em memória.
428#==============================================================================
429
430class Win32Utils::ASMBuilder
431 #--------------------------------------------------------------------------
432 # * Constantes (Registradores)
433 #--------------------------------------------------------------------------
434 REGISTERS = {
435 :eax => 0x0,
436 :ecx => 0x1,
437 :edx => 0x2,
438 :ebx => 0x3,
439 :esp => 0x4,
440 :ebp => 0x5
441 }
442
443 #--------------------------------------------------------------------------
444 # * Constantes (Funções de seção crítica)
445 #--------------------------------------------------------------------------
446 EnterCriticalSection = DL.dlopen('kernel32')['InitializeCriticalSection']
447 LeaveCriticalSection = DL.dlopen('kernel32')['LeaveCriticalSection']
448
449 #--------------------------------------------------------------------------
450 # * Constantes
451 #--------------------------------------------------------------------------
452 def initialize
453 @buffer = ""
454 end
455
456 #--------------------------------------------------------------------------
457 # * Array
458 #--------------------------------------------------------------------------
459 def to_a
460 @buffer.unpack('C*')
461 end
462
463 #--------------------------------------------------------------------------
464 # * Comandos puros
465 #--------------------------------------------------------------------------
466 def raw(code)
467 @buffer << code
468 self
469 end
470
471 #--------------------------------------------------------------------------
472 # * PUSH <register>
473 #--------------------------------------------------------------------------
474 def push_r(register)
475 raw [0x50 | REGISTERS[register]].pack('C')
476 end
477
478 #--------------------------------------------------------------------------
479 # * POP <register>
480 #--------------------------------------------------------------------------
481 def pop_r(register)
482 raw [0x58 | REGISTERS[register]].pack('C')
483 end
484
485 #--------------------------------------------------------------------------
486 # * MOV <register>, <register>
487 #--------------------------------------------------------------------------
488 def mov_r2r(to, from)
489 from = REGISTERS[from]
490 to = REGISTERS[to]
491 raw [0x89, 0xC0 | (from << 3) | to].pack('CC')
492 end
493
494 #--------------------------------------------------------------------------
495 # * MOV <register>, [<register> + N]
496 #--------------------------------------------------------------------------
497 def mov_rptr2r(to, from, plus = 0)
498 to = REGISTERS[to]
499 if from == :esp
500 raw [0x8B, 0x44 | (to << 3), 0x24, plus].pack('CCCC')
501 else
502 from = REGISTERS[from]
503 raw [0x8B, 0x40 | (to << 3) | from, plus].pack('CCC')
504 end
505 end
506
507 #--------------------------------------------------------------------------
508 # * MOV <register>, <memory>
509 #--------------------------------------------------------------------------
510 def mov_m2r(to, from)
511 if to == :eax
512 raw [0xA1, from].pack('CL')
513 else
514 to = REGISTERS[to]
515 raw [0x8B, 0x05 | to << 3, from].pack('CCL')
516 end
517 end
518
519 #--------------------------------------------------------------------------
520 # * MOV <register>, <value>
521 #--------------------------------------------------------------------------
522 def mov_v2r(to, from)
523 to = REGISTERS[to]
524 raw [0xB8 | to, from].pack('CL')
525 end
526
527 #--------------------------------------------------------------------------
528 # * MOV <memory>, <register>
529 #--------------------------------------------------------------------------
530 def mov_r2m(to, from)
531 if from == :eax
532 raw [0xA3, to].pack('CL')
533 else
534 from = REGISTERS[from]
535 raw [0x89, 0x05 | from << 3, to].pack('CCL')
536 end
537 end
538
539 #--------------------------------------------------------------------------
540 # * CALL <register>
541 #--------------------------------------------------------------------------
542 def call_r(reg)
543 raw [0xFF, 0xD0 | REGISTERS[reg]].pack('CC')
544 end
545
546 #--------------------------------------------------------------------------
547 # * ADD <register>, <register>
548 #--------------------------------------------------------------------------
549 def add_r2r(to, from)
550 raw [0x01, 0xC0 | (REGISTERS[from] << 3) | REGISTERS[to]].pack('CC')
551 end
552
553 #--------------------------------------------------------------------------
554 # * ADD <register>, <value>
555 #--------------------------------------------------------------------------
556 def add_v2r(to, from)
557 raw [0x83, 0xC0 | REGISTERS[to], from].pack('CCC')
558 end
559
560 #--------------------------------------------------------------------------
561 # * RET <N>
562 #--------------------------------------------------------------------------
563 def retn(n)
564 raw [0xC2, n].pack('CS')
565 end
566
567 #--------------------------------------------------------------------------
568 # * Remove argumentos da pilha para um array de buffers
569 #--------------------------------------------------------------------------
570 def pop_args(*buffers)
571 push_r :ebp
572 mov_r2r :ebp, :esp
573 buffers.each_with_index do |buffer, i|
574 mov_rptr2r :eax, :ebp, (i + 2) << 2
575 mov_r2m buffer.address, :eax
576 end
577 pop_r :ebp
578 end
579
580 #--------------------------------------------------------------------------
581 # * Push de string na pilha
582 #--------------------------------------------------------------------------
583 def push_string(str)
584 push_v DL::CPtr[str].to_i
585 end
586
587 #--------------------------------------------------------------------------
588 # * Push de valor na pilha
589 #--------------------------------------------------------------------------
590 def push_v(value)
591 mov_v2r :eax, value
592 push_r :eax
593 end
594
595 #--------------------------------------------------------------------------
596 # * Chamada de função
597 #--------------------------------------------------------------------------
598 def call_function(pointer)
599 mov_v2r :edx, pointer
600 call_r :edx
601 end
602
603 #--------------------------------------------------------------------------
604 # * Função
605 #--------------------------------------------------------------------------
606 def function(*arg_buffers)
607 pop_args *arg_buffers
608 yield self
609 retn 4 * arg_buffers.size if arg_buffers.size > 0
610 self
611 end
612
613 #--------------------------------------------------------------------------
614 # * Preserva um registrador usando a pilha
615 #--------------------------------------------------------------------------
616 def preserve(reg)
617 push_r reg
618 yield self
619 pop_r reg
620 end
621
622 #--------------------------------------------------------------------------
623 # * Travamento de sincronização
624 #--------------------------------------------------------------------------
625 def lock(lock)
626 push_v lock.address
627 call_function EnterCriticalSection
628 end
629
630 #--------------------------------------------------------------------------
631 # * Liberação de sincronização
632 #--------------------------------------------------------------------------
633 def release(lock)
634 push_v lock.address
635 call_function LeaveCriticalSection
636 end
637
638 #--------------------------------------------------------------------------
639 # * Constrói a função em memória
640 #--------------------------------------------------------------------------
641 def build
642 data = Win32Utils::RawBuffer.copy(@buffer)
643 data.executable!
644 data
645 end
646end
647
648#==============================================================================
649# ** Win32Utils::NativeFunction
650#------------------------------------------------------------------------------
651# Classe para gerenciamento de funções nativas.
652#==============================================================================
653
654class Win32Utils::NativeFunction
655 #--------------------------------------------------------------------------
656 # * Inicialização do objeto
657 #--------------------------------------------------------------------------
658 def initialize(return_type = DL::TYPE_VOID)
659 asm_builder = Win32Utils::ASMBuilder.new
660 yield asm_builder
661 @buffer = asm_builder.build
662 @cfunc = DL::CFunc.new(@buffer.address, return_type)
663 end
664
665 #--------------------------------------------------------------------------
666 # * Chama a função
667 #--------------------------------------------------------------------------
668 def call(*args)
669 @cfunc.call(args)
670 end
671
672 #--------------------------------------------------------------------------
673 # * Ponteiro da função
674 #--------------------------------------------------------------------------
675 def address
676 @buffer.address
677 end
678
679 #--------------------------------------------------------------------------
680 # * Libera a memória alocada para a função
681 #--------------------------------------------------------------------------
682 def dispose
683 @buffer.dispose
684 @return_buffer.dispose
685 @argument_buffers.each(&:dispose)
686 end
687end
688
689#==============================================================================
690# ** Ini
691#------------------------------------------------------------------------------
692# Este módulo implementa lógica de leitura de arquivos .ini.
693#==============================================================================
694
695module Ini
696 #--------------------------------------------------------------------------
697 # * Funções da Win32 API
698 #--------------------------------------------------------------------------
699 GetPrivateProfileString = Win32API.new('kernel32',
700 'GetPrivateProfileStringA',
701 'pppplp', 'l')
702
703 #--------------------------------------------------------------------------
704 # * Obtém um valor do arquivo
705 # file : Nome do arquivo
706 # section : Seção do valor no arquivo
707 # key : Chave do valor na seção
708 # default : Valor padrão (opcional)
709 #--------------------------------------------------------------------------
710 def self.get(file, section, key, default = nil)
711 unless FileTest.file?(file)
712 raise "`#{file}' does not exist or is not a file"
713 end
714
715 buffer = Array.new(256, 0).pack('C*')
716 length = Ini::GetPrivateProfileString.call(
717 section,
718 key,
719 default || 0,
720 buffer,
721 256,
722 file
723 )
724 buffer[0, length]
725 end
726end
727
728#==============================================================================
729# ** GameIni
730#------------------------------------------------------------------------------
731# Este módulo permite a leitura facilitada de valores do arquivo Game.ini.
732#==============================================================================
733
734module GameIni
735 #--------------------------------------------------------------------------
736 # * Lê um valor do arquivo Game.ini
737 # section : Seção do valor no arquivo
738 # key : Chave do valor na seção
739 # default : Valor padrão (opcional)
740 #--------------------------------------------------------------------------
741 def self.get(section, key, default = nil)
742 Ini.get('./Game.ini', section, key, default)
743 end
744
745 #--------------------------------------------------------------------------
746 # * Constantes
747 #--------------------------------------------------------------------------
748 RTP = get('Game', 'RTP')
749 LIBRARY = get('Game', 'Library')
750 SCRIPTS = get('Game', 'Scripts')
751 TITLE = get('Game', 'Title')
752 DESCRIPTION = get('Game', 'Description', '')
753end
754
755#==============================================================================
756# ** Win32Utils::Callback
757#------------------------------------------------------------------------------
758# Classe para uma função nativa com callback para Ruby.
759#==============================================================================
760
761class Win32Utils::Callback < Win32Utils::NativeFunction
762 #--------------------------------------------------------------------------
763 # * Constantes
764 #--------------------------------------------------------------------------
765 RGSSEval = DL.dlopen(GameIni::LIBRARY)['RGSSEval']
766
767 #--------------------------------------------------------------------------
768 # * Inicialização do objeto
769 #--------------------------------------------------------------------------
770 def initialize(args, lock = nil, return_type = DL::TYPE_LONG, &block)
771 @argument_types = args
772 @lock = lock
773 @callback = block
774 init_argument_buffers
775 init_return_buffer
776 super(return_type, &method(:compile))
777 end
778
779 #--------------------------------------------------------------------------
780 # * Inicialização dos buffers de argumento
781 #--------------------------------------------------------------------------
782 def init_argument_buffers
783 @argument_buffers = Array.new(@argument_types.size) do
784 Win32Utils::RawBuffer.malloc(4)
785 end
786 end
787
788 #--------------------------------------------------------------------------
789 # * Inicialização do buffer de retorno
790 #--------------------------------------------------------------------------
791 def init_return_buffer
792 @return_buffer = Win32Utils::RawBuffer.malloc(4)
793 end
794
795 #--------------------------------------------------------------------------
796 # * Compila a função
797 # asm_builder : ASMBuilder
798 #--------------------------------------------------------------------------
799 def compile(asm_builder)
800 @callback_script = "ObjectSpace._id2ref(#{object_id}).call"
801 asm_builder.function(*@argument_buffers) do |b|
802 b.lock @lock if @lock
803
804 b.push_string @callback_script
805 b.call_function RGSSEval
806 b.mov_m2r :eax, @return_buffer
807 b.add_v2r :esp, 4
808
809 b.preserve(:eax) { b.release @lock } if @lock
810 end
811 end
812
813 #--------------------------------------------------------------------------
814 # * Argumentos decodificados do callback
815 #--------------------------------------------------------------------------
816 def arguments
817 @argument_buffers.each_with_index.map do |buffer, i|
818 type = @argument_types[i]
819 case type.upcase
820 when 'L', 'I', 'N', 'S', 'C'
821 buffer.to_s.unpack(type).first
822 when 'P'
823 buffer
824 end
825 end
826 end
827
828 #--------------------------------------------------------------------------
829 # * Chama a função
830 #--------------------------------------------------------------------------
831 def call
832 begin
833 ret = @callback.call(*arguments)
834 ret = 1 if ret.is_a?(TrueClass)
835 ret = 0 if ret.is_a?(FalseClass)
836 ret = [ret].pack('L') if ret.is_a?(Integer)
837 @return_buffer.set(ret)
838 rescue Exception => e
839 puts "Exception happened during call to native function:"
840 puts "\t" + e.message
841 puts "\t" + e.backtrace.join("\n\t")
842 end
843 end
844
845 #--------------------------------------------------------------------------
846 # * Ponteiro da função
847 #--------------------------------------------------------------------------
848 def pointer
849 @buffer.address
850 end
851
852 #--------------------------------------------------------------------------
853 # * Libera a memória alocada para a função
854 #--------------------------------------------------------------------------
855 def dispose
856 @buffer.dispose
857 @return_buffer.dispose
858 @argument_buffers.each(&:dispose)
859 end
860end
861
862#==============================================================================
863# ** Win32Utils::Window
864#------------------------------------------------------------------------------
865# Esta classe gerencia janelas da API do windows.
866#==============================================================================
867
868class Win32Utils::Window
869 #--------------------------------------------------------------------------
870 # * Constantes
871 #--------------------------------------------------------------------------
872 GWL_WNDPROC = -4
873
874 #--------------------------------------------------------------------------
875 # * Funções da Win32 API
876 #--------------------------------------------------------------------------
877 GetCurrentThreadId = Win32API.new('kernel32', 'GetCurrentThreadId',
878 'v', 'l')
879 GetWindowThreadProcessId = Win32API.new('user32', 'GetWindowThreadProcessId',
880 'lp', 'l')
881 EnumWindows = Win32API.new('user32', 'EnumWindows', 'll', 'i')
882 SetWindowLong = Win32API.new('user32', 'SetWindowLongA', 'lil', 'l')
883 GetWindowLong = Win32API.new('user32', 'GetWindowLongA', 'li', 'l')
884 SetClassLong = Win32API.new('user32', 'SetClassLongA', 'lil', 'l')
885 GetClassLong = Win32API.new('user32', 'GetClassLongA', 'li', 'l')
886
887 CallWindowProc = Win32API.new('user32', 'CallWindowProc', 'lllll', 'l')
888
889 SwitchToThread = DL.dlopen('kernel32')['SwitchToThread']
890
891 GetMessage = DL.dlopen('user32')['GetMessage']
892 TranslateMessage = DL.dlopen('user32')['TranslateMessage']
893 DispatchMessage = DL.dlopen('user32')['DispatchMessage']
894
895 #--------------------------------------------------------------------------
896 # * Variáveis públicas
897 #--------------------------------------------------------------------------
898 attr_reader :handle
899
900 #--------------------------------------------------------------------------
901 # * Inicialização do objeto
902 # handle : Handle da janela (HWND)
903 #--------------------------------------------------------------------------
904 def initialize(handle)
905 @handle = handle
906 @lock = Win32Utils::Lock.new
907 @lock.lock
908 init_update_func
909 end
910
911 #--------------------------------------------------------------------------
912 # * Obtém o objeto da janela principal do jogo
913 #--------------------------------------------------------------------------
914 def self.game_window
915 return @game_window if @game_window
916
917 thread_id = GetCurrentThreadId.call
918 enum_windows_proc = Win32Utils::Callback.new('LL') do |hwnd, _lparam|
919 next true unless thread_id == GetWindowThreadProcessId.call(hwnd, 0)
920
921 @game_window = Win32Utils::Window.new(hwnd)
922 false
923 end
924 EnumWindows.call(enum_windows_proc.pointer, 0)
925 @game_window
926 end
927
928 #--------------------------------------------------------------------------
929 # * Adiciona um listener para um evento
930 # event : Evento
931 #--------------------------------------------------------------------------
932 def add_event_listener(event, &listener)
933 old_proc = self.window_proc
934 proc = Win32Utils::Callback.new('LLLL', @lock) do |hwnd, msg, wparam, lparam|
935 listener.call(wparam, lparam) if event == msg
936 CallWindowProc.call(old_proc, hwnd, msg, wparam, lparam)
937 end
938
939 set_window_long(GWL_WNDPROC, proc.pointer)
940 end
941
942 #--------------------------------------------------------------------------
943 # * Atualização da janela
944 #--------------------------------------------------------------------------
945 def update
946 @update_func.call([])
947 end
948
949 #--------------------------------------------------------------------------
950 # * Ponteiro da WNDPROC
951 #--------------------------------------------------------------------------
952 def window_proc
953 get_window_long(GWL_WNDPROC)
954 end
955
956 #--------------------------------------------------------------------------
957 # * Define um valor long da classe da janela
958 # index : Índice do valor a ser alterado
959 # value : Novo valor
960 #--------------------------------------------------------------------------
961 def set_class_long(index, value)
962 SetClassLong.call(@handle, index, value)
963 end
964
965 #--------------------------------------------------------------------------
966 # * Obtém um valor long da classe da janela
967 # index : Índice do valor
968 #--------------------------------------------------------------------------
969 def get_class_long(index)
970 GetClassLong.call(@handle, index)
971 end
972
973 #--------------------------------------------------------------------------
974 # * Define um valor long da janela
975 # index : Índice do valor a ser alterado
976 # value : Novo valor
977 #--------------------------------------------------------------------------
978 def set_window_long(index, value)
979 SetWindowLong.call(@handle, index, value)
980 end
981
982 #--------------------------------------------------------------------------
983 # * Define um valor long da janela
984 # index : Índice do valor
985 #--------------------------------------------------------------------------
986 def get_window_long(index)
987 GetWindowLong.call(@handle, index)
988 end
989
990 #--------------------------------------------------------------------------
991 # * Inicialização da função nativa de atualização
992 #--------------------------------------------------------------------------
993 def init_update_func
994 @msg = Array.new(40, 0).pack('C*')
995 ptr = Win32Utils::ASMBuilder.new.function do |b|
996 b.lock @lock
997
998 b.push_string @msg
999 b.push_v @handle
1000 b.push_v 0
1001 b.push_v 0
1002 b.call_function GetMessage
1003
1004 b.push_string @msg
1005 b.call_function TranslateMessage
1006
1007 b.push_string @msg
1008 b.call_function DispatchMessage
1009
1010 b.call_function SwitchToThread
1011
1012 b.release @lock
1013
1014 b.mov_v2r :eax, 0
1015 b.retn 4
1016 end.build
1017
1018 @update_func = DL::CFunc.new(ptr, DL::TYPE_LONG)
1019 end
1020end
1021
1022#==============================================================================
1023# ** Graphics
1024#------------------------------------------------------------------------------
1025# Módulo principal de gerenciamento dos gráficos.
1026#==============================================================================
1027
1028class << Graphics
1029 #--------------------------------------------------------------------------
1030 # * Atualziação da tela
1031 #--------------------------------------------------------------------------
1032 alias update_without_win32_hook update
1033 def update
1034 update_without_win32_hook
1035 Win32Utils::Window.game_window.update
1036 end
1037end