PageRenderTime 67ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/tools/register_vpcom.py

https://github.com/mypinballs/pyprocgame
Python | 333 lines | 223 code | 62 blank | 48 comment | 41 complexity | 77ba68ae021fbdec58b56017b00652d1 MD5 | raw file
  1. import os
  2. import sys
  3. sys.path.append(sys.path[0]+'/..') # Set the path so we can find procgame. We are assuming (stupidly?) that the first member is our directory.
  4. #from procgame import fakepinproc
  5. import pinproc
  6. import win32com
  7. import pythoncom
  8. import win32com.server.util
  9. from win32com.server.util import wrap, unwrap
  10. import thread
  11. import yaml
  12. from procgame import *
  13. class ISettings:
  14. _public_methods_ = []
  15. _public_attrs_ = [ 'Value']
  16. def Value(self, item, item2):
  17. return True
  18. def SetValue(self, item, item2):
  19. return True
  20. class IGames:
  21. _public_methods_ = []
  22. _public_attrs_ = [ 'Settings']
  23. def Settings(self):
  24. settings = ISettings()
  25. Settings = wrap( settings )
  26. return Settings
  27. def SetSettings(self):
  28. settings = ISettings()
  29. Settings = wrap( settings )
  30. return Settings
  31. IID_IController = pythoncom.MakeIID('{CE9ECC7C-960F-407E-B27B-62E39AB1E30F}')
  32. class Controller:
  33. """Main Visual Pinball COM interface class."""
  34. _public_methods_ = [ 'Run',
  35. 'Stop',
  36. 'PrintGlobal']
  37. _reg_progid_ = "VPinMAME.Controller"
  38. _reg_clsid_ = "{F389C8B7-144F-4C63-A2E3-246D168F9D39}"
  39. _public_attrs_ = [ 'Version',
  40. 'GameName',
  41. 'Games',
  42. 'SplashInfoLine',
  43. 'ShowTitle',
  44. 'ShowFrame',
  45. 'ShowDMDOnly',
  46. 'HandleMechanics',
  47. 'HandleKeyboard',
  48. 'DIP',
  49. 'Switch',
  50. 'Mech',
  51. 'Pause',
  52. 'ChangedSolenoids',
  53. 'ChangedGIStrings',
  54. 'ChangedLamps',
  55. 'GetMech']
  56. _readonly_attrs_ = [ 'Version',
  57. 'ChangedSolenoids',
  58. 'ChangedLamps',
  59. 'ChangedGIStrings',
  60. 'GetMech']
  61. Version = "22222222"
  62. ShowTitle = None
  63. ShowFrame = False
  64. ShowDMDOnly = False
  65. HandleKeyboard = False
  66. DIP = False
  67. GameName = "Game name"
  68. switch = [True]*128
  69. lastSwitch = None
  70. Pause = None
  71. game = None
  72. last_lamp_states = []
  73. last_coil_states = []
  74. last_gi_states = []
  75. HandleMechanics = True
  76. # Need to overload this method to tell that we support IID_IServerWithEvents
  77. def _query_interface_(self, iid):
  78. """ Return this main interface if the IController class is queried. """
  79. if iid == IID_IController:
  80. return win32com.server.util.wrap(self)
  81. def PrintGlobal(self):
  82. """ Unused by pyprocgame. """
  83. return True
  84. def Run(self):
  85. """ Figure out which game to play based on the contents of the
  86. vp_game_map_file. """
  87. vp_game_map_file = config.value_for_key_path(keypath='vp_game_map_file', default='/.')
  88. vp_game_map = yaml.load(open(vp_game_map_file, 'r'))
  89. game_class = vp_game_map[self.GameName]['kls']
  90. game_path = vp_game_map[self.GameName]['path']
  91. yamlpath = vp_game_map[self.GameName]['yaml']
  92. rundir = vp_game_map['rundir']
  93. os.chdir(rundir)
  94. #game_config = yaml.load(open(yamlpath, 'r'))
  95. #machine_type = game_config['PRGame']['machineType']
  96. #self.game = None
  97. klass = util.get_class(game_class,game_path)
  98. #self.game = klass(machine_type)
  99. self.game = klass()
  100. self.game.log("GameName: " + str(self.GameName))
  101. self.game.log("SplashInfoLine: " + str(self.SplashInfoLine))
  102. self.game.yamlpath = yamlpath
  103. self.last_lamp_states = self.getLampStates()
  104. self.last_coil_states = self.getCoilStates()
  105. self.game.setup()
  106. # Initialize switches. Call SetSwitch so it can invert
  107. # normally closed switches as appropriate.
  108. for i in range(0,120):
  109. self.SetSwitch(i, False)
  110. thread.start_new_thread(self.game.run_loop,())
  111. return True
  112. def Stop(self):
  113. """ Currently unused. """
  114. #TODO: Figure out a way to kill the run_loop thread.
  115. return True
  116. def Games(self, rom_name):
  117. """ Return the IGames interface, by wrapping the object. """
  118. games = IGames()
  119. wrapped_games = wrap (games)
  120. return wrapped_games
  121. def SetGames(self, rom_name):
  122. """ Return the IGames interface, by wrapping the object. """
  123. games = IGames()
  124. wrapped_games = wrap (games)
  125. return wrapped_games
  126. def Switch(self, number):
  127. """ Return the current value of the requested switch. """
  128. if number != None: self.lastSwitch = number
  129. return self.switch[self.lastSwitch]
  130. def SetSwitch(self, number, value):
  131. """ Set the value of the requested switch. """
  132. # All of the 'None' logic is error handling for unexpected
  133. # cases when None is passed in as a parameter. This seems to
  134. # only happen with the original VP scripts when the switch data
  135. # is corrupted by making COM calls into this object. This
  136. # appears to be a pywin32 bug.
  137. if value == None: return self.Switch(number)
  138. if number == None: return self.Switch(number)
  139. if number != None: self.lastSwitch = number
  140. self.switch[self.lastSwitch] = value
  141. if self.lastSwitch < 10:
  142. prNumber = self.VPSwitchDedToPRSwitch(self.lastSwitch)
  143. elif self.lastSwitch < 110:
  144. prNumber = self.VPSwitchMatrixToPRSwitch(self.lastSwitch)
  145. elif self.lastSwitch < 120:
  146. prNumber = self.VPSwitchFlipperToPRSwitch(self.lastSwitch)
  147. else: prNumber = 0
  148. if not self.game.switches.has_key(prNumber): return False
  149. if self.game.switches[prNumber].type == 'NC':
  150. self.AddSwitchEvent(prNumber, not value)
  151. else: self.AddSwitchEvent(prNumber, value)
  152. return True
  153. def AddSwitchEvent(self, prNumber, value):
  154. """ Add the incoming VP switch event into the p-roc emulator. """
  155. # VP doesn't have a concept of bouncing switches; so send
  156. # both nondebounced and debounced for each event to ensure
  157. # switch rules for either event type will be processed.
  158. if value:
  159. self.game.proc.add_switch_event(prNumber, pinproc.EventTypeSwitchClosedNondebounced)
  160. self.game.proc.add_switch_event(prNumber, pinproc.EventTypeSwitchClosedDebounced)
  161. else:
  162. self.game.proc.add_switch_event(prNumber, pinproc.EventTypeSwitchOpenNondebounced)
  163. self.game.proc.add_switch_event(prNumber, pinproc.EventTypeSwitchOpenDebounced)
  164. def VPSwitchMatrixToPRSwitch(self, number):
  165. """ Helper method to find the P-ROC number of a matrix switch. """
  166. vpNumber = ((number / 10)*8) + ((number%10) - 1)
  167. vpIndex = vpNumber / 8
  168. vpOffset = vpNumber % 8 + 1
  169. if vpIndex < 10:
  170. switch = 'S' + str(vpIndex) + str(vpOffset)
  171. return pinproc.decode(self.game.machine_type,switch)
  172. else: return number
  173. def VPSwitchFlipperToPRSwitch(self, number):
  174. """ Helper method to find the P-ROC number of a flipper switch. """
  175. vpNumber = number - 110
  176. switch = 'SF' + str(vpNumber)
  177. return pinproc.decode(self.game.machine_type, switch)
  178. def VPSwitchDedToPRSwitch(self, number):
  179. """ Helper method to find the P-ROC number of a direct switch. """
  180. vpNumber = number
  181. switch = 'SD' + str(vpNumber)
  182. return pinproc.decode(self.game.machine_type, switch)
  183. def Mech(self, number):
  184. """ Currently unused. Game specific mechanism handling will
  185. be called through this method. """
  186. return True
  187. def SetMech(self, number):
  188. """ Currently unused. Game specific mechanism handling will
  189. be called through this method. """
  190. return True
  191. def GetMech(self, number):
  192. """ Currently unused. Game specific mechanism handling will
  193. be called through this method. """
  194. return 0
  195. def ChangedSolenoids(self):
  196. """ Return a list of changed coils. """
  197. coils = self.getCoilStates()
  198. changedCoils = []
  199. already=False
  200. if len(self.last_coil_states) > 0:
  201. for i in range(0,len(coils)):
  202. if coils[i] != self.last_coil_states[i]:
  203. if not already:
  204. changedCoils += [(0,True)]
  205. already = True
  206. changedCoils += [(i,coils[i])]
  207. self.last_coil_states = coils
  208. return changedCoils
  209. def ChangedLamps(self):
  210. """ Return a list of changed lamps. """
  211. lamps = self.getLampStates()
  212. changedLamps = []
  213. if len(self.last_lamp_states) > 0:
  214. for i in range(0,len(lamps)):
  215. if lamps[i] != self.last_lamp_states[i]:
  216. changedLamps += [(i,lamps[i])]
  217. self.last_lamp_states = lamps
  218. return changedLamps
  219. def ChangedGIStrings(self):
  220. """ Return a list of changed GI strings. """
  221. gi = self.getGIStates()
  222. changedGI = []
  223. if len(self.last_gi_states) > 0:
  224. for i in range(0,len(gi)):
  225. if gi[i] != self.last_gi_states[i]:
  226. changedGI += [(i,gi[i])]
  227. self.last_gi_states = gi
  228. return changedGI
  229. def getGIStates(self):
  230. """ Gets the current state of the GI strings. """
  231. vpgi = [False]*5
  232. for i in range(0,5):
  233. numStr = 'G0' + str(i+1)
  234. prNumber = pinproc.decode(self.game.machine_type, numStr)
  235. vpgi[i] = self.game.proc.drivers[prNumber].curr_state
  236. return vpgi
  237. def getLampStates(self):
  238. """ Gets the current state of the lamps. """
  239. vplamps = [False]*90
  240. for i in range(0,64):
  241. vpNum = (((i/8)+1)*10) + (i%8) + 1
  242. vplamps[vpNum] = self.game.proc.drivers[i+80].curr_state
  243. return vplamps
  244. def getCoilStates(self):
  245. """ Gets the current state of the coils. """
  246. pycoils = self.game.proc.drivers
  247. vpcoils = [False]*64
  248. for i in range(0,len(vpcoils)):
  249. if i<=28: vpcoils[i] = pycoils[i+39].curr_state
  250. elif i<33: vpcoils[i] = False # Unused?
  251. # Use the machine's Hold coils for the VP flippers
  252. # since they stay on until the button is released
  253. elif i == 34: vpcoils[i] = pycoils[pinproc.decode(self.game.machine_type, "FURH")].curr_state
  254. elif i == 36: vpcoils[i] = pycoils[pinproc.decode(self.game.machine_type, "FULH")].curr_state
  255. elif i<44:
  256. if self.game.machine_type == pinproc.MachineTypeWPC95:
  257. vpcoils[i] = pycoils[i+31].curr_state
  258. else: vpcoils[i] = pycoils[i+107].curr_state
  259. elif i == 46: vpcoils[i] = pycoils[pinproc.decode(self.game.machine_type, "FLRH")].curr_state
  260. elif i == 48: vpcoils[i] = pycoils[pinproc.decode(self.game.machine_type, "FLLH")].curr_state
  261. else: vpcoils[i] = pycoils[i+108].curr_state
  262. return vpcoils
  263. def Register(pyclass=Controller, p_game=None):
  264. """ Registration code for the Visual Pinball COM interface for pyprocgame."""
  265. pythoncom.CoInitialize()
  266. from win32com.server.register import UseCommandLine
  267. UseCommandLine(pyclass)
  268. # Run the registration code by default. Using the commandline param
  269. # "--unregister" will unregister this COM object.
  270. if __name__=='__main__':
  271. Register(Controller)