查看: 72|回复: 0

[转载发布] 【RGD/三版通用】SINVideo for RGD(视频功能增强·拓展改造)

[复制链接]
  • TA的每日心情
    开心
    7 天前
  • 签到天数: 37 天

    连续签到: 3 天

    [LV.5]常住居民I

    2028

    主题

    32

    回帖

    7260

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    VIP
    0
    卡币
    5184
    OK点
    16
    积分
    7260
    发表于 同元一千年二月十七日(春) | 显示全部楼层 |阅读模式
    起因是前段时间看到有人整理“使用RGD后的播放视频解决方案”,结果还是没什么稳定的替代方案
    我就想起了SINVideo这个用DirectShow写入Bitmap内存来实现的老物(原贴:https://rpg. blue/thread-306831-1-1.html
    不知是帖子标题难以搜索、效率或兼容存在问题还是单纯年代过于久远的原因,一直都没什么讨论度,感到颇为可惜
    试着用RGD的Bitmap#process_color改造了一下发现效率还算可以,800x600+60FPS的视频基本也运行能稳60帧

    本来到这里就算完了,然后我就发现SINVideo实现的功能太少了...基本上除了播放和停止,只有个变更音量和重播,甚至连暂停都没有
    于是去翻阅了文档把DirectShow本来有的几个方法加上了,因为没dll实际源码也没编译环境,还是采用的内存修改的方式做的拓展
    由于对汇编不怎么了解实在是苦战了一番,不过多亏原贴有附同种实现的源码,花了一周多时间也终于还是搞定了
    我个人没有放视频的需求,囤手里烂了也不应当,分享出来看看能不能作为一种解决方案吧
    为了方便演示,顺带研究了一下截获窗口标题的FPS值,搞了一些基于RGD和RGSS300/301.dll的内核修改,大概过些日子也会发布【当然这个是后话了



    已在Win10/Win7环境下,使用RGD(v1.3.2/1.5.4/1.6.1)/VA/XP测试通过,暂未发现问题
    【人家dll都现成的 就算有问题我也没(bu)法(fang)修(bian)x
    视频文件基本只支持AVI格式和MPEG系列编码,可以用格式工厂之类的工具查看和转换
    以及理所当然的,视频文件不支持放入加密档

    由于使用起来需要一定的代码基础,推荐下载范例工程
    原版演示视频无声,Video文件夹下另行准备了名为title1.avi的视频文件,将其替换title.avi可用于测试音频相关的功能
    按ESC/X键暂停继续,左右键变速,上下键调整音量,A/S键调整声道
    进入标题画面并开始游戏后,与NPC交互以演示在地图上播放视频(故意将视频坐标调整到了玩家脚下)

    截图(GIF警告)







    范例工程(推荐


    蓝奏云(密码:lei)
    文件体积较大,不方便以附件形式上传,如果实在无法访问我再考虑其他方式
    SINVideo.dll 核心



    SINVideo for RGD 本体脚本
    SINVideo_RGD
    [Ruby] 纯文本查看 复制代码
    #encoding: utf-8
    #
    # ■ [SIN]Video(RGD兼容)
    #        MPEG Player for RPG Maker / RGDirect (RGD)
    #
    #  使用你的精灵来播放 MPEG 视频(RGD/XP/VX/VA通用)
    #
    # [更新记录]
    #    - 2013.04.21 By Shy07, Syalon(真正的作者)
    #      * 初版亦终版
    #
    #    - 2022.07.27 By 岚风 雷(gqxastg)
    #      * 优化代码,改造为RGD兼容版
    #
    # [使用方法]
    #    - SIN::PLAYER.play("Video/title.avi")  #=> 加载并开始播放视频(可追加音量)
    #    - SIN::PLAYER.update                   #=> 更新视频画面,原则上必须每帧调用
    #    - SIN::PLAYER.stop                     #=> 停止播放并释放视频
    #
    #    - SIN::PLAYER.playing?                 #=> 获取视频是否播放中
    #    - SIN::PLAYER.rewind                   #=> 从头重新开始播放
    #    - SIN::PLAYER.replay_at_finish         #=> 播放完成后则重新播放
    #    - SIN::PLAYER.volume                   #=> 获取播放音量(-10000~0)
    #    - SIN::PLAYER.volume = 0               #=> 设置播放音量
    #    - SIN::Video.regen                     #=> 重新生成播放器精灵(可追加参数)
    #
    #    - 其余使用方法请参考下面的代码以及范例工程中的 SINVideoDemo
    #
    #
    module SIN
      module VideoConfig
        #------------------------------------------------------------------------
        # ■ 以下为设定部分
        #------------------------------------------------------------------------
     
        # SINVideo.dll的存放路径
     
        DLLPATH = 'System/SINVideo.dll'
     
        # 使用原版时的视频更新模式
        # 该项为true时: 效率优先,直接修改位图数据
        # 该项为false时:稳定优先,通过缓存中转修改
        # 默认为true,如使用RGD则无须修改此项
     
        ORIMODE = true
     
        # 使用RGD时的视频显示模式
        # RGD因位图存储格式不同,相对原版从下往上显示将导致颠倒
        # 该项为0时:初始会自动将mirror和angle分别设为true和180以矫正颠倒
        # 该项为1时:每帧更新视频时,自动翻转位图数据以矫正颠倒
        # 该项为nil时则不作任何处理
        # 默认为0,如使用原版则无须修改此项
     
        RGDFlip = 0
     
        #------------------------------------------------------------------------
        # ■ 设定部分到此结束,使用者无须修改以下内容
        #------------------------------------------------------------------------
        ORIMODE = false if defined?(RGD)
        RGDFlip = nil unless defined?(RGD)
      end
      #
      class ::Bitmap
        # API
        @@RtlMoveMemory_pl = Win32API.new('kernel32', 'RtlMoveMemory', 'pll', 'l')
        @@RtlMoveMemory_lp = Win32API.new('kernel32', 'RtlMoveMemory', 'lpl', 'l')
        @@RtlMoveMemory_ll = Win32API.new('kernel32', 'RtlMoveMemory', 'lll', 'l')##
        def _dump(limit) ############
          data = "rgba" * width * height
          if respond_to?(:process_color)
            process_color {|color_arr| color_arr.save_data(data, width * height) }
          else
            @@RtlMoveMemory_pl.call(data, address, width * height * 4)
          end
          [width, height, Zlib::Deflate.deflate(data)].pack("LLa*")
        end
        def self._load(str) #############
          w, h, zdata = str.unpack("LLa*")
          data = Zlib::Inflate.inflate(zdata)
          bmp = self.new(w, h)
          if method_defined?(:process_color)
            bmp.process_color {|color_arr| color_arr.load_data(data, w * h) }
          else
            @@RtlMoveMemory_lp.call(bmp.address, Zlib::Inflate.inflate(zdata), 
            w * h * 4)
          end
          return bmp
        end
        def direct_load(str, rev = false) #############
          if rev
            buf = "\0" * 4 * width * height # str.bytesize
            len = width * 4
            src = [str].pack('p').unpack('L').first + width * height * 4
            dst = [buf].pack('p').unpack('L').first
            height.times do |i|
              src -= len
              @@RtlMoveMemory_ll.call(dst, src, len)
              dst += len
            end
            str = buf
          end
          #str = str.scan(/.{#{width * 4}}/m).reverse.join if rev
          if respond_to?(:process_color)
            process_color {|color_arr| color_arr.load_data(str, width * height) }
          else
            @@RtlMoveMemory_lp.call(address, str, width * height * 4)
          end
          self
        end
        # [[[bitmap.object_id * 2 + 16] + 8] + 16] == header
        def address
          buffer, ad = "rgba", object_id * 2 + 16
          @@RtlMoveMemory_pl.call(buffer, ad, 4)
          ad = buffer.unpack("L")[0] + 8
          @@RtlMoveMemory_pl.call(buffer, ad, 4)
          ad = buffer.unpack("L")[0] + 16
          @@RtlMoveMemory_pl.call(buffer, ad, 4)
          return buffer.unpack("L")[0]
        end
        #
        def g_fill_rect(*arg)
          if arg[0].is_a? Rect
            col1, col2 = 1, 2
          else
            col1, col2 = 4, 5
          end
          color1, color2 = arg[col1], arg[col2]
          arg[col1] = Color.new((color1 >> 16) & 0xFF,
                          (color1 >> 8) & 0xFF, (color1 & 0xFF), (color1 >> 24))
          arg[col2] = Color.new((color2 >> 16) & 0xFF,
                          (color2 >> 8) & 0xFF, (color2 & 0xFF), (color2 >> 24))
          gradient_fill_rect(*arg)
        end
      end
      #
      class Video < Sprite
     
        DLLPATH = VideoConfig::DLLPATH ############# 'System/SINVideo.dll'
     
        @@__video__ = nil
     
        def self.new(viewport = nil)
          return @@__video__ ||= super(viewport)
        end
     
        def self.regen(*args) #############
          @@__video__.dispose if @@__video__ && !@@__video__.disposed?
          @@__video__ = nil
          ::SIN.const_set(:PLAYER, Video.new(*args))
        end
     
        undef clone rescue nil
        undef dup rescue nil
     
        VideoLoad       = Win32API.new(DLLPATH, "__rgssx_video_load", "ppp", "i")
        VideoPlay       = Win32API.new(DLLPATH, "__rgssx_video_play", "ll", "v")
        VideoPlay_p     = Win32API.new(DLLPATH, "__rgssx_video_play", "pl", "v")###
        VideoPlaying    = Win32API.new(DLLPATH, "__rgssx_video_is_playing", "v", "i")
        VideoUpdate     = Win32API.new(DLLPATH, "__rgssx_video_update", "v", "v")
        VideoStop       = Win32API.new(DLLPATH, "__rgssx_video_stop", "v", "v")
        VideoRewind     = Win32API.new(DLLPATH, "__rgssx_video_rewind", "v", "v")
        VideoGetVolume  = Win32API.new(DLLPATH, "__rgssx_video_get_volume", "v", "l")
        VideoSetVolume  = Win32API.new(DLLPATH, "__rgssx_video_set_volume", "l", "v")
     
        def initialize(*args) ############## viewport = nil
          super(*args) ############## viewport
          @__playing = false
          ##############
          return unless VideoConfig::RGDFlip == 0
          self.angle = 180
          self.mirror = true
        end
     
        def dispose
          stop
          super
        end
     
        def volume
          return VideoGetVolume.call
        end
     
        def volume=(v)
          VideoSetVolume.call(v)
        end  
     
        def playing?
          return false unless @__playing
          return true if VideoPlaying.call != 0
          false #stop ############## 取消自动停止,避免出错
        end
     
        def replay_at_finish
          return unless @__playing
          return if VideoPlaying.call != 0 
          VideoRewind.call
        end
     
        def stop
          return unless @__playing
          VideoStop.call
          bitmap.dispose
          self.bitmap = nil
          @bmpbuf = nil ##############
          @__playing = false
        end
     
        def rewind
          VideoRewind.call
        end
     
        def play(filename, volume = 0)
          return Video.regen.play(filename, volume) if disposed? ##############
          w = [0].pack("L")
          h = [0].pack("L")
          return false if VideoLoad.call(filename, w, h) == 0
          w = w.unpack("L").first
          h = h.unpack("L").first
          bitmap.dispose if bitmap
          self.bitmap = Bitmap.new(w, h)
          self.ox = w / 2
          self.oy = h / 2
          self.x = Graphics.width / 2
          self.y = Graphics.height / 2
     
          @bmpbuf = "\0" * w * h * 4 ##############
          #@bmpbuf.force_encoding('ASCII-8BIT')
     
          ##############
          if VideoConfig::ORIMODE
            VideoPlay.call(bitmap.address, volume)
          else
            VideoPlay_p.call(@bmpbuf, volume) ##############
          end
          ##############
     
          #self.bitmap.direct_load(@bmpbuf, VideoConfig::RGDFlip == 1)##############
     
          @__playing = true
          return true
        end
     
        def update
          return unless @__playing
          VideoUpdate.call
          self.bitmap.direct_load(@bmpbuf, VideoConfig::RGDFlip == 1) unless
          VideoConfig::ORIMODE ##############
          super
        end
     
      end
     
      PLAYER.dispose if const_defined?(:PLAYER) && !PLAYER.disposed? ##############
      PLAYER = Video.new # ||=
    end


    SINVideo 拓展插件(请置于本体下方)
    SINVideo_Hacker
    [Ruby] 纯文本查看 复制代码
    #encoding: utf-8
    #
    # ■ [SIN]Video Hacker
    #        Extension Plugin for SINVideo
    #
    #  SINVideo(视频功能增强)拓展插件
    #
    # [更新记录]
    #    - 2022.07.27 By 岚风 雷(gqxastg)
    #      * 初版
    #
    # [使用方法]
    #    - 首先请确保安装了SINVideo脚本(原版或RGD兼容版皆可),并将此脚本置于其下
    #
    #    - SIN::PLAYER.convert_time(1, :SECOND, :MEDIA) #=>
    #      转换播放时间单位(:SECOND为秒,:MEDIA为默认单位)
    #
    #    - SIN::PLAYER.getDuration        #=> 获取视频的完整长度(不受结束位置影响)
    #    - SIN::PLAYER.getCurrentPosition #=> 获取播放的当前位置
    #    - SIN::PLAYER.getStopPosition    #=> 获取播放的结束位置
    #    - SIN::PLAYER.getPositions       #=> 获取播放的当前位置和结束位置
    #    - SIN::PLAYER.setPositions(0)    #=> 设置播放的当前位置和结束位置
    #      可使用 :AbsolutePositioning 和 :NoPositioning 标志来分别指定是否要设置
    #      如:(0, :NoPositioning, 100, :AbsolutePositioning) 则只设置结束位置
    #
    #    - SIN::PLAYER.getCurFile         #=> 获取当前播放视频的文件名
    #    - SIN::PLAYER.getState           #=> 获取当前播放状态(0结束、1暂停、2播放)
    #    - SIN::PLAYER.pausing?           #=> 获取视频是否暂停中
    #    - SIN::PLAYER.pause              #=> 暂停播放
    #    - SIN::PLAYER.run                #=> 继续播放(暂停时)
    #    - SIN::PLAYER.getRate            #=> 获取播放倍速(正常速度为1.0)
    #    - SIN::PLAYER.setRate(2.0)       #=> 设置播放倍速
    #
    #    - SIN::PLAYER.get_Balance        #=> 获取播放声道平衡(-100~100)
    #    - SIN::PLAYER.put_Balance(-5.0)  #=> 设置播放声道平衡
    #    - SIN::PLAYER.get_Volume         #=> 获取播放音量(0~100)
    #    - SIN::PLAYER.put_Volume(50.0)   #=> 设置播放音量
    #
    #    - SIN::PLAYER.playing? 判断播放中包括暂停状态
    #    - SIN::PLAYER.getPositions 等方法涉及时间的数值请用convert_time进行转换
    #
    #    - 其余使用方法请参考下面的代码以及范例工程中的 SINVideoDemo
    #
    #
    module SIN
      raise '未检测到SINVideo,请将此脚本置于其下' unless defined?(SIN::Video)
      # 兼容XP/VX
      class ::String
        def bytesize; size; end unless method_defined?(:bytesize)
        def start_with?(*args)
          args.any? {|s| self[0, s.size] == s.to_s }
        end unless method_defined?(:start_with?)
      end
      class ::Symbol
        def upcase;  self.to_s.upcase.to_sym; end unless method_defined?(:upcase)
        def to_proc; proc {|a| a.send(self) }; end unless method_defined?(:to_proc)
      end
      class ::Array
        alias videohacker_flatten flatten unless ([].flatten(0) rescue false)
        def flatten(level = nil)
          return self.videohacker_flatten if !level || (level < 0)
          return self if level == 0
          from, to = nil, self
          level.times do
            from, to = to, []
            from.each {|a| a.is_a?(Array) ? to.push(*a) : to.push(a) }
          end
          to
        end if method_defined?(:videohacker_flatten)
      end
      #----------------------------------------------------------------------------
      module VideoHacker
        MultiByteToWideChar = 
        Win32API.new('kernel32', 'MultiByteToWideChar', 'iipipi', 'i')
        WideCharToMultiByte = 
        Win32API.new('kernel32', 'WideCharToMultiByte', 'iipipipp', 'i')
        #--------------------------------------------------------------------------
        def self.u2w(str)
          buf = "\0" * 2 *
          MultiByteToWideChar.call(65001, 0, str, -1, nil, 0)
          MultiByteToWideChar.call(65001, 0, str, -1, buf, buf.bytesize / 2)
          buf
        end
        #--------------------------------------------------------------------------
        def self.w2u(str, zero = true)
          buf = "\0" *
          WideCharToMultiByte.call(65001, 0, str, -1, nil, 0, nil, nil)
          WideCharToMultiByte.call(65001, 0, str, -1, buf, buf.bytesize, nil, nil)
          buf.sub!(/\0+$/, '') if zero
          buf
        end
        #--------------------------------------------------------------------------
     
        RtlMoveMemory_pl = Win32API.new('kernel32', 'RtlMoveMemory', 'pll', 'l')
        RtlMoveMemory_lp = Win32API.new('kernel32', 'RtlMoveMemory', 'lpl', 'l')
        RtlMoveMemory_ll = Win32API.new('kernel32', 'RtlMoveMemory', 'lll', 'l')
        #RtlMoveMemory_pp = Win32API.new('kernel32', 'RtlMoveMemory', 'ppl', 'l')
        GetModuleHandle  = Win32API.new('kernel32', 'GetModuleHandle', 'p', 'l')
     
        CallWindowProc_lplll = 
        Win32API.new('user32', 'CallWindowProc', 'lpLLL', 'i')
        CallWindowProc_lllll = 
        Win32API.new('user32', 'CallWindowProc', 'llLLL', 'i')
        CallWindowProc_lpppp = 
        Win32API.new('user32', 'CallWindowProc', 'lpppp', 'i')
        #CallWindowProc_lplpl = 
        #Win32API.new('user32', 'CallWindowProc', 'lpLpL', 'i')
     
        MEM_COMMIT  = 0x00001000
        MEM_RESERVE = 0x00002000
        MEM_RELEASE = 0x00008000
        PAGE_EXECUTE_READWRITE = 0x40
     
        VirtualAlloc = Win32API.new('kernel32', 'VirtualAlloc', 'llll', 'l')
        VirtualFree  = Win32API.new('kernel32', 'VirtualFree', 'lll', 'l')
        #VirtualProtect = Win32API.new('kernel32', 'VirtualProtect', 'llll', 'l')
     
        #--------------------------------------------------------------------------
     
        S_OK = 0x00000000
        S_FALSE = 0x00000001
        E_INVALIDARG = 0x80070057
        E_OUTOFMEMORY = 0x8007000E
        E_UNEXPECTED = 0x8000FFFF
        E_NOTIMPL = 0x80004001
        E_FAIL = 0x80004005
        E_POINTER = 0x80004003
        E_HANDLE = 0x80070006
        E_ABORT = 0x80004004
        E_ACCESSDENIED = 0x80070005
        E_NOINTERFACE = 0x80004002
     
        VFW_S_STATE_INTERMEDIATE = 0x00040237
        VFW_S_CANT_CUE = 0x00040268
        VFW_E_NOT_CONNECTED = 0x80040209
        VFW_E_WRONG_STATE = 0x80040227
        VFW_E_UNSUPPORTED_AUDIO = 0x8004025C
        VFW_E_UNSUPPORTED_VIDEO = 0x8004025D
     
        ReturnValueList = constants.select {|sym|
        sym.to_s.start_with?('S_', 'E_', 'VFW_S_', 'VFW_E_') }.collect(&:to_sym)
     
        AM_SEEKING_NoPositioning = 0x00
        AM_SEEKING_AbsolutePositioning = 0x01
        AM_SEEKING_RelativePositioning = 0x02
        AM_SEEKING_IncrementalPositioning = 0x03
        AM_SEEKING_PositioningBitsMask = 0x03
        AM_SEEKING_SeekToKeyFrame = 0x04
        AM_SEEKING_ReturnTime = 0x08
        AM_SEEKING_Segment = 0x10
        AM_SEEKING_NoFlush = 0x20
     
        ALL_TIME_FORMAT = %w{TIME_FORMAT_NONE TIME_FORMAT_FRAME TIME_FORMAT_SAMPLE 
        TIME_FORMAT_FIELD TIME_FORMAT_BYTE TIME_FORMAT_MEDIA_TIME}.collect(&:to_sym)
     
        ConvertTimeList = %w{SECOND FRAME MEDIA}.collect(&:to_sym)
     
        TIME_FORMAT_NONE       = "\0" * 16
        TIME_FORMAT_FRAME      = [0x7b785570, 0x8c82, 0x11cf, 
        0xbc, 0x0c, 0x00, 0xaa, 0x00, 0xac, 0x74, 0xf6].pack('LSSC8')
        TIME_FORMAT_SAMPLE     = [0x7b785572, 0x8c82, 0x11cf, 
        0xbc, 0x0c, 0x00, 0xaa, 0x00, 0xac, 0x74, 0xf6].pack('LSSC8')
        TIME_FORMAT_FIELD      = [0x7b785573, 0x8c82, 0x11cf, 
        0xbc, 0x0c, 0x00, 0xaa, 0x00, 0xac, 0x74, 0xf6].pack('LSSC8')
        TIME_FORMAT_BYTE       = [0x7b785571, 0x8c82, 0x11cf, 
        0xbc, 0x0c, 0x00, 0xaa, 0x00, 0xac, 0x74, 0xf6].pack('LSSC8')
        TIME_FORMAT_MEDIA_TIME = [0x7b785574, 0x8c82, 0x11cf, 
        0xbc, 0x0c, 0x00, 0xaa, 0x00, 0xac, 0x74, 0xf6].pack('LSSC8')
     
        #--------------------------------------------------------------------------
     
        DSHOWprocList = {
          :m_pBasicAudio => [
            [:put_Volume,  0x1C, 'put_Volume'],
            [:get_Volume,  0x20, 'get_Volume'],
            [:put_Balance, 0x24, 'put_Balance'],
            [:get_Balance, 0x28, 'get_Balance'],
          ],
          :m_pSampleGrabber => [
            [:GetCurrentBuffer, 0x1C, 'getCurrentBuffer'],
          ],
          :m_pMediaSeeking => [
            [:SetTimeFormat,      0x18, 'setTimeFormat'],
            [:GetTimeFormat,      0x1C, 'getTimeFormat'],
            [:IsUsingTimeFormat,  0x20, 'isUsingTimeFormat'],
            [:IsFormatSupported,  0x24, 'isFormatSupported'],
            [:GetDuration,        0x28, 'getDuration'],
            [:GetStopPosition,    0x2C, 'getStopPosition'],
            [:GetCurrentPosition, 0x30, 'getCurrentPosition'],
            [:ConvertTimeFormat,  0x34, 'convertTimeFormat'],
            [:SetPositions,       0x38, 'setPositions'],
            [:GetPositions,       0x3C, 'getPositions'],
            [:GetAvailable,       0x40, 'getAvailable'],
            [:SetRate,            0x44, 'setRate'],
            [:GetRate,            0x48, 'getRate'],
          ],
          :m_pMediaControl => [
            [:Run,      0x1C, 'run'], 
            [:Pause,    0x20, 'pause'], 
            [:Stop,     0x24, nil], 
            [:GetState, 0x28, 'getState'],
          ],
          :m_pMediaEvent => [
          ],
          :pFileSourceFilter => [
            [:Load,       0x0C, nil],
            [:GetCurFile, 0x10, 'getCurFile'],
          ],
        }
     
        #--------------------------------------------------------------------------
     
        arr = DSHOWprocList.values.flatten(1)
        ProcSym = arr.collect {|a| a[0] }
        MethodSym = arr.collect {|a| a[2] }.compact
     
        DLLmodule = GetModuleHandle.call(File.basename(::SIN::Video::DLLPATH))
        DLLproc = {}
        DSHOWproc = {}
        ProcSym.each {|key| DSHOWproc[key] = 0 }
     
        #DSHOWproc_p = DSHOWproc.clone
        #DSHOWproc_p.each {|key, value| DSHOWproc_p[key] = [value].pack('L') }
     
        #--------------------------------------------------------------------------
     
        if const_defined?(:MACHproc) && (ptr = MACHproc.values[0]) && ptr != 0
          VirtualFree.call(ptr, 0, MEM_RELEASE)
        end
     
        MACHcode = {
          :BASE =>
          [0x8B, 0x44, 0x24, 0x04, 0x8B, 0x4C, 0x24, 0x08, 0xFF, 0x34, 0x88, 0xE2, 
           0xFB, 0xFF, 0x10, 0xC3].pack('C*'),
          :GetCurrentBuffer =>
          [0x8B, 0x44, 0x24, 0x04, 0x8B, 0x4C, 0x24, 0x08, 0xB2, 0xFF, 0x83, 0xC0, 
           0x03, 0x88, 0x10, 0x83, 0xC0, 0x04, 0xE2, 0xF9, 0xC2, 0x10, 0x00].
           pack('C*'),
        }
    #~     [0x8B, 0x44, 0x24, 0x04, 0xFF, 0x70, 0x14, 0xFF, 0x70, 0x10, 0xFF, 0x70, 
    #~      0x0C, 0xFF, 0x70, 0x08, 0xFF, 0x70, 0x04, 0xFF, 0x10, 0xC3].pack('C*'),
    #~     [0x89, 0xE0, 0xFF, 0x70, 0x10, 0xFF, 0x70, 0x0C, 0xFF, 0x70, 0x08, 0xFF, 
    #~     0x70, 0x04, 0x68].pack('C*') + 
    #~     [DLLproc[1]].pack('L') + 
    #~     [0xFF, 0x15].pack('C*') + 
    #~     [DSHOWproc_p[:SetPositions]].pack('p') + 
    #~     [0xC3].pack('C*')
        MACHproc = {}
     
        hash = MACHcode#.clone
        #hash.each {|key, value| hash[key] = value.call }
        all = hash.values.join
     
        ptr = VirtualAlloc.call(0, all.bytesize, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
        raise '无法申请内存' if ptr == 0
        RtlMoveMemory_lp.call(ptr, all, all.bytesize)
     
        hash.each do |key, value|
          MACHproc[key] = ptr
          ptr += value.bytesize
        end
     
        #--------------------------------------------------------------------------
     
        buf = "\0" * 4
        RtlMoveMemory_pl.call(buf, DLLmodule + 0xD8AC, 4)
        DLLproc[:BASE] = buf.unpack('L').first
     
        #--------------------------------------------------------------------------
        def self.recalc_DSHOWproc#(resetMACH = true)
          buf = "\0" * 4
     
          # mov esi,[SINVideo.dll+D8AC]
     
          # m_pBasicAudio
          # mov eax,[esi+14]
          RtlMoveMemory_pl.call(buf, DLLproc[:BASE] + 0x14, 4)
          DLLproc[:m_pBasicAudio] = buf.unpack('L').first
     
          # m_pSampleGrabber
          # mov eax,[esi+10]
          RtlMoveMemory_pl.call(buf, DLLproc[:BASE] + 0x10, 4)
          DLLproc[:m_pSampleGrabber] = buf.unpack('L').first
     
          # m_pMediaSeeking->SetPositions
          # mov eax,[esi+0C]
          RtlMoveMemory_pl.call(buf, DLLproc[:BASE] + 0x0C, 4)
          DLLproc[:m_pMediaSeeking] = buf.unpack('L').first
     
          # m_pMediaControl->Stop
          # mov eax,[esi+04]
          RtlMoveMemory_pl.call(buf, DLLproc[:BASE] + 0x04, 4)
          DLLproc[:m_pMediaControl] = buf.unpack('L').first
     
          # m_pMediaEvent
          # mov eax,[esi+08]
          RtlMoveMemory_pl.call(buf, DLLproc[:BASE] + 0x08, 4)
          DLLproc[:m_pMediaEvent] = buf.unpack('L').first
     
          # m_pGraphBuilder
          # mov eax,[esi]
          RtlMoveMemory_pl.call(buf, DLLproc[:BASE], 4)
          DLLproc[:m_pGraphBuilder] = buf.unpack('L').first
     
          # pFileSourceFilter
          # [[[[[SINVideo.dll+D8AC]]+BC]+8]]+44
          adr = DLLproc[:BASE]
          [0, 0xBC, 8, 0].each do |off|
            buf = "\0" * 4
            RtlMoveMemory_pl.call(buf, adr + off, 4)
            adr = buf.unpack('L').first
            break if adr == 0
          end
          adr += 0x44 unless adr == 0
          DLLproc[:pFileSourceFilter] = adr
     
          #------------------------------------------------------------------------
     
          DSHOWprocList.each do |key, value|
            value.each do |arr|
              sym, off = arr
              # mov eax,[SINVideo.dll+D8AC]
              # mov eax,[eax+**]
              adr = DLLproc[key]
              break value.each {|a| DSHOWproc[a[0]] = 0 } if adr == 0
              buf = "\0" * 4
              # mov eax,[eax]
              RtlMoveMemory_pl.call(buf, adr, 4)
              adr = buf.unpack('L').first
              next DSHOWproc[sym] = 0 if adr == 0
              # mov eax,[eax+**]
              RtlMoveMemory_pl.call(buf, adr + off, 4)
              DSHOWproc[sym] = buf.unpack('L').first
            end
          end
     
          #DSHOWproc_p.each {|key, value| 
          #RtlMoveMemory_pp.call(value, [DSHOWproc[key]].pack('L'), 4) }
     
          #reset_MACHcode if resetMACH
        end
        #--------------------------------------------------------------------------
        def self.clear_DSHOWproc
          DSHOWproc.each_key {|key| DSHOWproc[key] = 0 }
        end
        #--------------------------------------------------------------------------
    #~     def self.reset_MACHcode
    #~       return if MACHproc.values[0] == 0
    #~       all = MACHcode.values.collect(&:call).join
    #~       RtlMoveMemory_lp.call(MACHproc.values[0], all, all.bytesize)
    #~     end
        #--------------------------------------------------------------------------
        def self.convert_ret(ret)
          ret = ret + 0x100000000 if ret < 0
          sym = ReturnValueList.find {|s| const_get(s) == ret }
          sym ? sym : ret
        end
        #--------------------------------------------------------------------------
        def self.convert_time(time, from = :SECOND, to = :MEDIA)
          2.times do |i|
            format = (i == 0) ? from : to
            format = (format.is_a?(Symbol) || format.is_a?(String)) ? 
                      format.to_sym.upcase : ConvertTimeList[format.to_i]
            (i == 0) ? from = format : to = format
          end
          case from
          when :SECOND
          when :FRAME then time = time / Graphics.frame_rate.to_f
          when :MEDIA then time = time / 10000000.0
          end
          case to
          when :SECOND
          when :FRAME then time = (time * Graphics.frame_rate).to_i
          when :MEDIA then time = (time * 10000000).to_i
          end
          time
        end
        #--------------------------------------------------------------------------
        # m_pBasicAudio
        #--------------------------------------------------------------------------
        def self.put_Volume(lVolume = 100)
          key, sym = :m_pBasicAudio, :put_Volume
          return false if DSHOWproc[sym] == 0
          lVolume = (lVolume * 100 - 10000).to_i
          params = [DSHOWproc[sym], DLLproc[key], lVolume].pack('LLl')
          ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
          convert_ret(ret)
        end
        #--------------------------------------------------------------------------
        def self.get_Volume
          key, sym = :m_pBasicAudio, :get_Volume
          return false if DSHOWproc[sym] == 0
          plVolume = "\0" * 4 #[0].pack('L')
          params = [DSHOWproc[sym], DLLproc[key], plVolume].pack('LLp')
          ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
          (plVolume.unpack('l').first + 10000) / 100.0
        end
        #--------------------------------------------------------------------------
        def self.put_Balance(lBalance = 0)
          key, sym = :m_pBasicAudio, :put_Balance
          return false if DSHOWproc[sym] == 0
          lBalance = (lBalance * 100).to_i
          params = [DSHOWproc[sym], DLLproc[key], lBalance].pack('LLl')
          ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
          convert_ret(ret)
        end
        #--------------------------------------------------------------------------
        def self.get_Balance
          key, sym = :m_pBasicAudio, :get_Balance
          return false if DSHOWproc[sym] == 0
          plBalance = "\0" * 4 #[0].pack('L')
          params = [DSHOWproc[sym], DLLproc[key], plBalance].pack('LLp')
          ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
          plBalance.unpack('l').first / 100.0
        end
        #--------------------------------------------------------------------------
        # m_pSampleGrabber
        #--------------------------------------------------------------------------
        def self.getCurrentBuffer(dst = nil)
          key, sym = :m_pSampleGrabber, :GetCurrentBuffer
          return false if DSHOWproc[sym] == 0
          pBufferSize = "\0" * 4 #[0].pack('L')
          pBuffer = nil
          params = [DSHOWproc[sym], DLLproc[key], pBufferSize, pBuffer].pack('LLpp')
          ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 3, 0, 0)
          size = pBufferSize.unpack('L').first
          return false if size == 0
     
          if dst.is_a?(Integer)
            pBuffer = true
            params = [DSHOWproc[sym], DLLproc[key], pBufferSize, dst].pack('LLpL')
          else
            pBuffer = dst ? dst : ("\0" * size)
            params = 
            [DSHOWproc[sym], DLLproc[key], pBufferSize, pBuffer].pack('LLpp')
          end
          ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 3, 0, 0)
          return false if ret != 0
          pBuffer
        end
        #--------------------------------------------------------------------------
        # m_pMediaSeeking
        #--------------------------------------------------------------------------
        def self.setTimeFormat(pFormat = :TIME_FORMAT_MEDIA_TIME)
          key, sym = :m_pMediaSeeking, :SetTimeFormat
          return false if DSHOWproc[sym] == 0
          pFormat = const_get(pFormat)
          params = [DSHOWproc[sym], DLLproc[key], pFormat].pack('LLp')
          CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
        end
        #--------------------------------------------------------------------------
        def self.getTimeFormat
          key, sym = :m_pMediaSeeking, :GetTimeFormat
          return false if DSHOWproc[sym] == 0
          pFormat = "\0" * 16
          params = [DSHOWproc[sym], DLLproc[key], pFormat].pack('LLp')
          ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
          ALL_TIME_FORMAT.find {|key| const_get(key) == pFormat }
        end
        #--------------------------------------------------------------------------
        def self.isUsingTimeFormat(pFormat)
          key, sym = :m_pMediaSeeking, :IsUsingTimeFormat
          return false if DSHOWproc[sym] == 0
          pFormat = const_get(pFormat)
          params = [DSHOWproc[sym], DLLproc[key], pFormat].pack('LLp')
          ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
          ret == 0
        end
        #--------------------------------------------------------------------------
        def self.isFormatSupported(pFormat)
          key, sym = :m_pMediaSeeking, :IsFormatSupported
          return false if DSHOWproc[sym] == 0
          pFormat = const_get(pFormat)
          params = [DSHOWproc[sym], DLLproc[key], pFormat].pack('LLp')
          CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
        end
        #--------------------------------------------------------------------------
        def self.getDuration
          key, sym = :m_pMediaSeeking, :GetDuration
          return false if DSHOWproc[sym] == 0
          pDuration = "\0" * 8 #[0].pack('q')
          params = [DSHOWproc[sym], DLLproc[key], pDuration].pack('LLp')
          ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
          pDuration.unpack('q').first
        end
        #--------------------------------------------------------------------------
        def self.getStopPosition
          key, sym = :m_pMediaSeeking, :GetStopPosition
          return false if DSHOWproc[sym] == 0
          pStop = "\0" * 8 #[0].pack('q')
          params = [DSHOWproc[sym], DLLproc[key], pStop].pack('LLp')
          ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
          pStop.unpack('q').first
        end
        #--------------------------------------------------------------------------
        def self.getCurrentPosition
          key, sym = :m_pMediaSeeking, :GetCurrentPosition
          return false if DSHOWproc[sym] == 0
          pCurrent = "\0" * 8 #[0].pack('q')
          params = [DSHOWproc[sym], DLLproc[key], pCurrent].pack('LLp')
          ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
          pCurrent.unpack('q').first
        end
        #--------------------------------------------------------------------------
        def self.convertTimeFormat(pTargetFormat, source, pSourceFormat = nil)
          key, sym = :m_pMediaSeeking, :ConvertTimeFormat
          return false if DSHOWproc[sym] == 0
          pTarget = "\0" * 8 #[0].pack('q')
          pTargetFormat = const_get(pTargetFormat) if pTargetFormat
          pSourceFormat = const_get(pSourceFormat) if pSourceFormat
          params = [DSHOWproc[sym], DLLproc[key], 
          pTarget, pTargetFormat, source, pSourceFormat].pack('LLppqp')
          # 因为传的是longlong(8字节)所以多占一个参数
          ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 6, 0, 0)
          pTarget.unpack('q').first
        end
        #--------------------------------------------------------------------------
        def self.setPositions(pCurrent = 0, dwCurrentFlags = 1, pStop = 0, 
          dwStopFlags = 0)
          key, sym = :m_pMediaSeeking, :SetPositions
          return false if DSHOWproc[sym] == 0
          pCurrent = [pCurrent].pack('q')
          pStop = [pStop].pack('q')
     
          2.times do |i|
            flags = (i == 0) ? dwCurrentFlags : dwStopFlags
            flags = flags.is_a?(Array) ? 
            flags.inject(0) {|r, m| r | const_get("AM_SEEKING_#{m}") } : 
            (flags.is_a?(Symbol) || flags.is_a?(String)) ? 
            const_get("AM_SEEKING_#{flags}") : flags.to_i
            (i == 0) ? dwCurrentFlags = flags : dwStopFlags = flags
          end
     
          params = [DSHOWproc[sym], DLLproc[key], 
          pCurrent, dwCurrentFlags, pStop, dwStopFlags].pack('LLpLpL')
          ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 5, 0, 0)
          #CallWindowProc_lplpl.call(MACHproc[:SetPositions], 
          #pCurrent, dwCurrentFlags, pStop, dwStopFlags)
     
          ret = convert_ret(ret)
          ret = [ret, pCurrent.unpack('q').first, pStop.unpack('q').first] if 
          (dwCurrentFlags & AM_SEEKING_ReturnTime != 0) || 
          (dwStopFlags & AM_SEEKING_ReturnTime != 0)
          ret
        end
        #--------------------------------------------------------------------------
        def self.getPositions
          key, sym = :m_pMediaSeeking, :GetPositions
          return false if DSHOWproc[sym] == 0
          pCurrent = "\0" * 8 #[0].pack('q')
          pStop = "\0" * 8 #[0].pack('q')
          params = [DSHOWproc[sym], DLLproc[key], pCurrent, pStop].pack('LLpp')
          ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 3, 0, 0)
          [pCurrent.unpack('q').first, pStop.unpack('q').first]
        end
        #--------------------------------------------------------------------------
        def self.getAvailable
          key, sym = :m_pMediaSeeking, :GetAvailable
          return false if DSHOWproc[sym] == 0
          pEarliest = "\0" * 8 #[0].pack('q')
          pLatest = "\0" * 8 #[0].pack('q')
          params = [DSHOWproc[sym], DLLproc[key], pEarliest, pLatest].pack('LLpp')
          ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 3, 0, 0)
          [pEarliest.unpack('q').first, pLatest.unpack('q').first]
        end
        #--------------------------------------------------------------------------
        def self.setRate(dRate = 1)
          key, sym = :m_pMediaSeeking, :SetRate
          return false if DSHOWproc[sym] == 0
          params = [DSHOWproc[sym], DLLproc[key], dRate].pack('LLD')
          # 因为传的是double(8字节)所以多占一个参数
          ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 3, 0, 0)
          convert_ret(ret)
        end
        #--------------------------------------------------------------------------
        def self.getRate
          key, sym = :m_pMediaSeeking, :GetRate
          return false if DSHOWproc[sym] == 0
          pdRate = "\0" * 8 #[0].pack('D')
          params = [DSHOWproc[sym], DLLproc[key], pdRate].pack('LLp')
          ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 2, 0, 0)
          pdRate.unpack('D').first
        end
        #--------------------------------------------------------------------------
        # m_pMediaControl
        #--------------------------------------------------------------------------
        def self.run
          key, sym = :m_pMediaControl, :Run
          return false if DSHOWproc[sym] == 0
          params = [DSHOWproc[sym], DLLproc[key]].pack('LL')
          CallWindowProc_lplll.call(MACHproc[:BASE], params, 1, 0, 0)
        end
        #--------------------------------------------------------------------------
        def self.pause
          key, sym = :m_pMediaControl, :Pause
          return false if DSHOWproc[sym] == 0
          params = [DSHOWproc[sym], DLLproc[key]].pack('LL')
          CallWindowProc_lplll.call(MACHproc[:BASE], params, 1, 0, 0)
        end
        #--------------------------------------------------------------------------
        def self.getState(msTimeout = 0)
          key, sym = :m_pMediaControl, :GetState
          return false if DSHOWproc[sym] == 0
          params = [DSHOWproc[sym], DLLproc[key],
          msTimeout, state = "\0" * 4].pack('LLlp')
          ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 3, 0, 0)
          # State_Stopped = 0, State_Paused = 1, State_Running = 2
          state.unpack('L').first
        end
        #--------------------------------------------------------------------------
        # pFileSourceFilter
        #--------------------------------------------------------------------------
        def self.getCurFile
          key, sym = :pFileSourceFilter, :GetCurFile
          return false if DSHOWproc[sym] == 0
          ppszFileName = "\0" * 4 #[0].pack('L')
          pmt = nil #"\0" * (32 + 2 + 4 + 16 + 4 + 4 + 1)
          params = [DSHOWproc[sym], DLLproc[key], ppszFileName, pmt].pack('LLpp')
          ret = CallWindowProc_lplll.call(MACHproc[:BASE], params, 3, 0, 0)
     
          adr = ppszFileName.unpack('L').first
          if adr == 0
            name = nil
          else
            name, buf = '', "\0" * 2
            begin
              RtlMoveMemory_pl.call(buf, adr, 2)
              name << buf
              adr += 2
            end until buf == "\0\0"
            name = w2u(name)
          end
          #[name, pmt.unpack('a16a16C2La16LLC')]
          name
        end
      end
      class Video < Sprite
        class VideoUpdate_caller
          CallWindowProc_lplll = ::SIN::VideoHacker::CallWindowProc_lplll
          attr_accessor :mode, :default
          #------------------------------------------------------------------------
          def initialize(default)
            @default, @mode = default, false
          end
          #------------------------------------------------------------------------
          def call
            return @default.call unless @mode
            ::SIN::PLAYER.instance_eval do
              return 1 unless @bmpbuf
              getCurrentBuffer(@bmpbuf)
              return 1 unless @bmpbuf
              CallWindowProc_lplll.call(
              ::SIN::VideoHacker::MACHproc[:GetCurrentBuffer], 
              @bmpbuf, width * height, 0, 0)
            end
            0
          end
        end
    #~     VideoUpdate = VideoUpdate_caller.new(VideoUpdate) unless
    #~                   VideoUpdate.is_a?(VideoUpdate_caller)
    #~     VideoUpdate.mode = true
        #--------------------------------------------------------------------------
        alias hackerold_play play
        def play(*args)
          return hackerold_play(*args) if disposed?
          hackerold_play(*args)
          ::SIN::VideoHacker.recalc_DSHOWproc if @__playing
        end
        #--------------------------------------------------------------------------
        alias hackerold_stop stop
        def stop
          hackerold_stop
          ::SIN::VideoHacker.clear_DSHOWproc if 
          ::SIN::VideoHacker::DSHOWproc.values[0] != 0
        end
        #--------------------------------------------------------------------------
        alias hackerold_playing? playing?
        def playing?
          return false unless @__playing
          return true if getState == 1 # 避免暂停状态被误判
          hackerold_playing?
        end
        #--------------------------------------------------------------------------
        alias hackerold_replay_at_finish replay_at_finish
        def replay_at_finish
          return unless @__playing
          return unless (s = getState) && s != 1 # 避免暂停状态被误判
          hackerold_replay_at_finish
        end
        #--------------------------------------------------------------------------
        def pausing?
          return false unless @__playing
          return true if getState == 1
          false
        end
        #--------------------------------------------------------------------------
        def convert_time(*args)
          ::SIN::VideoHacker.convert_time(*args)
        end
        #--------------------------------------------------------------------------
        ::SIN::VideoHacker::MethodSym.each do |name|
          define_method(name) do |*args|
            return unless @__playing
            ::SIN::VideoHacker.send(name, *args)
          end
        end
      end
    end


    附:演示代码(RGD/VA用)
    SINVideoDemo
    [Ruby] 纯文本查看 复制代码
    #------------------------------------------------------------------------------
    # ○ 改造自原版范例,仅供参考不建议直接使用
    #------------------------------------------------------------------------------
    Graphics.resize_screen(640, 480)
     
    @viewport = Viewport.new ##########
    $data_system ||= load_data("Data/System.rvdata2") ##########
     
    SIN::PLAYER.viewport = @viewport ##########
    SIN::PLAYER.play("Video/title.avi")
    SIN::PLAYER.z = 50
     
    #################################################################
    @fps_spr = Sprite.new
    @fps_spr.z = 0xFFFF
    @fps_spr.bitmap = Bitmap.new(56, 40)
    @fps_spr.bitmap.font.size = 32
    lastfps = (Graphics.fps rescue '')
    @fps_spr.bitmap.draw_text(@fps_spr.bitmap.rect, lastfps, 1)
    updatefps = -> do
      if (n = (Graphics.fps rescue '')) != lastfps
        lastfps = n
        @fps_spr.bitmap.clear
        @fps_spr.bitmap.draw_text(@fps_spr.bitmap.rect, lastfps, 1)
      end
    end
    #################################################################
     
    #################################################################
    class << SIN::PLAYER
      alias videodemo_update update
      def update
        videodemo_update
        return unless @__playing
        begin
          @tt ||= 0
          p [::SIN::VideoHacker::MACHproc[:BASE], 
          ::SIN::VideoHacker::DLLproc[:m_pMediaSeeking], 
          ::SIN::VideoHacker::DSHOWproc[:SetPositions]].collect {|n|
          n.to_s(16) } if @tt == 0
          if @tt == 0
            s = convert_time(11) # * 10000000
            p setPositions(0, :NoPositioning, s, :AbsolutePositioning)
            #setTimeFormat(:TIME_FORMAT_FRAME)
            #p isUsingTimeFormat(:TIME_FORMAT_FRAME)
            #p isUsingTimeFormat(:TIME_FORMAT_MEDIA_TIME)
            #p isFormatSupported(:TIME_FORMAT_FRAME)
            #p isFormatSupported(:TIME_FORMAT_MEDIA_TIME)
            p getTimeFormat
            #p [getCurrentPosition, getStopPosition]
            p [t = getPositions, getDuration]
            p [t.collect {|n| convert_time(n, -1, 0) }, 
               convert_time(getDuration, -1, 0)]
            #p getAvailable
            #p setRate(2)
            #p getRate
            #p convertTimeFormat(:TIME_FORMAT_FRAME, t[0], :TIME_FORMAT_MEDIA_TIME)
            #p getState
            #p pause
            p getCurFile
          end
          #p run if @tt == 29
          @tt = (@tt + 1) % 60
        end
      end
    end
    #################################################################
     
    @spt1 = Sprite.new
    @spt1.bitmap = Bitmap.new(Graphics.width, Graphics.height) # 640, 480
    @spt1.bitmap.font.size = 24
    @spt1.bitmap.font.shadow = true
    @spt1.bitmap.font.outline = false
    @spt1.z = 100
     
    @spt2 = Sprite.new(@viewport) ###########
    @spt2.bitmap = Bitmap.new(Graphics.width, Graphics.height) # 640, 480
    @spt2.bitmap.g_fill_rect(@spt2.bitmap.rect, 0xffff0000, 0xff0000ff)
    @spt2.visible = false
    @spt2.z = 0
     
    #################################################################
    if defined?(RGD) # RGD中Sprite的波效果无法显示位图尺寸外的部分 此处特别处理
      @display = Sprite.new(@viewport)
      @display.visible = false
      @display.bitmap = Bitmap.new(Graphics.width, Graphics.height)
      @display.ox = @display.bitmap.width / 2
      @display.oy = @display.bitmap.height / 2
      @display.x = Graphics.width / 2
      @display.y = Graphics.height / 2
      @display.z = 50
     
      rect = Rect.new((@display.bitmap.width - SIN::PLAYER.bitmap.width * 0.5) / 2,
      (@display.bitmap.height - SIN::PLAYER.bitmap.height * 0.5) / 2,
      SIN::PLAYER.bitmap.width * 0.5, SIN::PLAYER.bitmap.height * 0.5)
      @display.bitmap.stretch_blt(rect, SIN::PLAYER.bitmap, SIN::PLAYER.src_rect)
     
      class << SIN::PLAYER
        def bind_flash_sprite(sprite); @flash_sprite = sprite; end
        alias oldtest_flash flash
        def flash(*args) # color, duration
          @flash_sprite.flash(*args) if @flash_sprite && !@flash_sprite.disposed?
          oldtest_flash(*args)
        end
      end
     
      SIN::PLAYER.bind_flash_sprite(@display)
    end
    #################################################################
     
    def show_msg(argh)
      return @spt1.bitmap.clear if argh[:clear]
      x = argh[:x].nil? ? 0 : argh[:x]
      y = argh[:y].nil? ? 0 : argh[:y]
      width = argh[:width].nil? ? 640 : argh[:width]
      height = argh[:height].nil? ? 480 : argh[:height]
      y = (Graphics.height - height) / 2 if y == :middle
      str = argh[:str].nil? ? "" : argh[:str]
      align = argh[:align].nil? ? 0 : argh[:align]
      align = 0 if align == :left
      align = 1 if align == :middle
      align = 2 if align == :right
      @spt1.bitmap.draw_text(x, y, width, height, str, align)
    end
     
    @step = 0
    @wait = 30
    @rotating = false
     
    while true
      Graphics.update
      Input.update
      SIN::PLAYER.update
      SIN::PLAYER.replay_at_finish
      @spt1.update
      SIN::PLAYER.angle += 1 if @rotating
     
      updatefps.call ##########
     
      if Input.trigger?(:C) && @wait == -7
        show_msg({ :clear => true })
        case @step
        when 0
          SIN::PLAYER.zoom_x = 0.5
          SIN::PLAYER.zoom_y = 0.5
        when 1
          SIN::PLAYER.zoom_x = (Graphics.width * 1.0) / SIN::PLAYER.width
          SIN::PLAYER.zoom_y = (Graphics.height * 1.0) / SIN::PLAYER.height
        when 2
          SIN::PLAYER.mirror = SIN::VideoConfig::RGDFlip != 0 ########## true
        when 3
          SIN::PLAYER.zoom_x = 0.5
          SIN::PLAYER.zoom_y = 0.5
          @rotating = true
        when 4
          @spt2.visible = true
          SIN::PLAYER.blend_type = 2
        when 5
          SIN::PLAYER.blend_type = 0
          SIN::PLAYER.tone = Tone.new(0, 0, 0, 255)
        when 6
          SIN::PLAYER.tone = Tone.new(0, 0, 0, 0)
          SIN::PLAYER.flash(Color.new(0, 255, 0), 30)
        when 7
          SIN::PLAYER.wave_amp = 8
          SIN::PLAYER.wave_length = 240
          SIN::PLAYER.wave_speed = 360
        when 8
          #疑似开始旋转后角度置0以外的值会导致图片无法更新 原版就存在这个问题(RGD没事)
          SIN::PLAYER.angle = SIN::VideoConfig::RGDFlip == 0 ? 180 : 0 ##########
          @rotating = false
        when 9
          @spt2.visible = false
          SIN::PLAYER.mirror = SIN::VideoConfig::RGDFlip == 0 ########## false
          SIN::PLAYER.wave_amp = 0
          #SIN::PLAYER.wave_length = 0 # RGD里这样设置会崩溃(预设是360) ##########
          SIN::PLAYER.wave_speed = 0
          SIN::PLAYER.zoom_x = (Graphics.width * 1.0) / SIN::PLAYER.width
          SIN::PLAYER.zoom_y = (Graphics.height * 1.0) / SIN::PLAYER.height
        when 10
          rate = 255 / 60
          90.times do |i|
            Graphics.update
            updatefps.call ##########
            SIN::PLAYER.update
            SIN::PLAYER.opacity -= rate
          end
          SIN::PLAYER.stop
          break
        end
        @step += 1
        @wait = 30
      #################################################################
      elsif Input.trigger?(:B)
        p t = SIN::PLAYER.getPositions
        p t.collect {|n| SIN::PLAYER.convert_time(n, :MEDIA, :SECOND) }
        SIN::PLAYER.getState != 2 ? 
        (Sound.play_ok rescue nil; p SIN::PLAYER.run) : 
        (Sound.play_cancel rescue nil; p SIN::PLAYER.pause)
      elsif key = [:LEFT, :RIGHT].find {|k| Input.repeat?(k) }
        if key == :LEFT
          a = SIN::PLAYER.getRate - 0.25
          a = -0.25 if a == 0
        else
          a = SIN::PLAYER.getRate + 0.25
          a = 0.25 if a == 0
        end
        Sound.play_cursor rescue nil
        p SIN::PLAYER.setRate(a)
        p SIN::PLAYER.getRate
      elsif key = [:DOWN, :UP].find {|k| Input.repeat?(k) }
        if key == :DOWN
          a = SIN::PLAYER.get_Volume - 1
        else
          a = SIN::PLAYER.get_Volume + 1
        end
        Sound.play_cursor rescue nil
        p SIN::PLAYER.put_Volume(a)
        p SIN::PLAYER.get_Volume
      elsif key = [:X, :Y].find {|k| Input.repeat?(k) }
        if key == :X
          a = SIN::PLAYER.get_Balance - 1
        else
          a = SIN::PLAYER.get_Balance + 1
        end
        Sound.play_cursor rescue nil
        p SIN::PLAYER.put_Balance(a)
        p SIN::PLAYER.get_Balance
      end
      #################################################################
     
      #################################################################
      if SIN::PLAYER.zoom_x == 0.5
        @display.bitmap.clear
        rect = Rect.new((@display.bitmap.width - SIN::PLAYER.bitmap.width * 0.5) / 2,
        (@display.bitmap.height - SIN::PLAYER.bitmap.height * 0.5) / 2,
        SIN::PLAYER.bitmap.width * 0.5, SIN::PLAYER.bitmap.height * 0.5)
        @display.bitmap.stretch_blt(rect, SIN::PLAYER.bitmap, SIN::PLAYER.src_rect)
     
        %w{angle wave_amp wave_length wave_speed mirror opacity blend_type
        color tone}.each {|var| 
        @display.send("#{var}=", SIN::PLAYER.send(var)) }
        @display.update
        @display.wave_phase = SIN::PLAYER.wave_phase
        @display.visible, SIN::PLAYER.visible = true, false
      else
        @display.visible, SIN::PLAYER.visible = false, true
      end if @display
      #################################################################
     
      next if @wait == -7
      if @wait.zero?
        case @step
        when 0
          show_msg({  :y      => :middle,
                      :height => 24,
                      :str    => "窗口太小,看不到全部画面?按下空格试试",
                      :align  => :middle
                    })
        when 1
          show_msg({  :y      => 32,
                      :height => 24,
                      :str    => "画面太小,黑边难看?按下空格试试",
                      :align  => :middle
                    })
        when 2
          show_msg({  :y      => :middle,
                      :height => 24,
                      :str    => "这样可以吧?但是再按下空格试试",
                      :align  => :middle
                    })
        when 3
          show_msg({  :y      => 32,
                      :height => 24,
                      :str    => "再按下空格试试",
                      :align  => :middle
                    })
        when 4
          show_msg({  :y      => 32,
                      :height => 24,
                      :str    => "再按下空格试试",
                      :align  => :middle
                    })
        when 5
          show_msg({  :y      => 32,
                      :height => 24,
                      :str    => "再按……累死了,您自便吧,等结束了,我再回来",
                      :align  => :middle
                    })
        when 10
          show_msg({  :y      => :middle,
                      :height => 24,
                      :str    => "演示就到这里,最后再按下空格,转到标题吧",
                      :align  => :middle
                    })
        end
        @wait = -7
        next
      end
      @wait -= 1
    end
     
    #################################################################
    @fps_spr.bitmap.dispose
    @fps_spr.dispose
    @spt1.bitmap.dispose
    @spt2.bitmap.dispose
    @spt1.dispose
    @spt2.dispose
    SIN::PLAYER.viewport = nil
    @viewport.dispose
     
    if @display
      @display.bitmap.dispose
      @display.dispose
      SIN::Video.regen
    end
    #################################################################


    附:游戏窗口失去/获得焦点时自动暂停/继续的小插件(RGD用)

    [Ruby] 纯文本查看 复制代码
    module RGD
      class << self
        alias videopause_focus_out focus_out if method_defined?(:focus_out)
        alias videopause_focus_in focus_in if method_defined?(:focus_in)
      end
      # 失去焦点
      def self.focus_out
        SIN::PLAYER.pause if SIN::PLAYER.getState == 2
        videopause_focus_out if respond_to?(:videopause_focus_out)
      end
      # 获得焦点
      def self.focus_in
        SIN::PLAYER.run if SIN::PLAYER.getState == 1
        videopause_focus_in if respond_to?(:videopause_focus_in)
      end
    end if defined?(RGD)


                 本帖来自P1论坛作者gqxastg,因Project1站服务器在国外有时候访问缓慢不方便作者交流学习,经联系P1站长fux2同意署名转载一起分享游戏制作经验,共同为国内独立游戏作者共同创造良好交流环境,原文地址:https://rpg. blue/forum.php?mod=viewthread&tid=490464  若有侵权,发帖作者可联系底部站长QQ在线咨询功能删除,谢谢。

    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有账号?立即注册

    x
    天天去同能,天天有童年!
    回复 论坛版权

    使用道具 举报

    ahome_bigavatar:guest
    ahome_bigavatar:welcomelogin
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|Archiver|手机版|小黑屋|同能RPG制作大师 ( 沪ICP备12027754号-3 )

    GMT+8, 2024-5-17 08:57 , Processed in 0.058710 second(s), 46 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2020, Tencent Cloud.

    快速回复 返回顶部 返回列表