Retrobat – 건콘 자동 설정

라이트건 (LightGun, 건콘) 게임을 위해 Retrobat으로 게임과 에뮬레이터를 관리하고 있습니다.
라이트건 종류가 많아지다 보니 기기에 따른 설정이 복잡해졌습니다.
 
좀 더 손쉽게 게임환경을 설정할 수 있도록 AutoHotKey (이하 AHK)를 사용해 보았습니다.
Requirement는 다음과 같습니다.

  • 연결된 건콘을 수동 또는 자동으로 선택해야 합니다.
  • 자동으로 기기를 인식할때는 음성으로 선택된 기기를 알려줘야 합니다.
  • Teknoparrot과 DemulShooter는 기기의 HID에 따라 설정을 자동으로 바꿔줘야 합니다.
  • Retrobat이 실행될때 기기별 전용 프로그램을 실행해야 합니다. 
  • Retrobat이 종료될때 기기별 전용 프로그램을 종료해야 합니다.
  • 마우스 커서를 조준선으로 변경하고, Retrobat 종료시 원복해야 합니다.

설정 파일 변경

방법1 – 심볼릭 링크

기기에 따른 Teknoparrot과 DemulShooter를 구분하여 설치하고, AHK를 통해 심볼릭 링크를 생성하는 방법을 사용할 수 있습니다.
심볼릭링크를 사용시 teknoparrot - Sinden 폴더가 teknoparrot 폴더로 인식됩니다.
단점은 심볼릭 링크를 생성하기 위해서는 관리자 권한으로 AHK를 실행해야 합니다.

방법 2 – 설정파일 복사

기기에 따른 설정파일만 따로 만들고 AHK를 통해 원하는 설정들을 덮어쓸 수 있습니다.
teknoparrot - Sinden\UserProfiles\*.* 파일을 teknoparrot\UserProfiles\*.* 에 덮어쓰는 방식입니다.

기기별 전용 프로그램

Key Manager

현재 X-Gunner는 사용자 버튼 설정 기능이 없기 때문에 Key Manager를 통해 버튼을 변경하여 사용하고 있습니다.
저는 MP5와 Pistol의 매핑을 다르게 설정하였기 때문에 AHK를 통해 프로필을 변경하고 있습니다.

SindenLightgun

Sinden Lightgun을 사용하기 위해서는 전용 프로그램을 꼭 사용해야 합니다.

Retrobat Launcher

완성된 AHK 스크립트와 실행 예시는 다음과 같습니다.

#SingleInstance, Force

autoDetect := A_Args[1] == "auto"
global deviceIds := Object("Sinden", "HID\VID_16C0&PID_0F38&MI_02&Col02\a&325b90b&0&0001"
, "Gun4IR", "HID\VID_2341&PID_8042&MI_02&Col03\8&3a338b46&0&0002"
, "XGunner MP5", "HID\VID_1209&PID_0001&MI_02&Col02\8&231fee9e&0&0001"
, "XGunner Pistol", "HID\VID_1209&PID_0002&MI_02&Col02\8&335f4b7e&0&0001")
global cursorFile := "E:\Lightgun\crosshair\ico\blue_white_128.ico"

; -----------------------------------

if (autoDetect == false) {
	OpenGui()
	return
}

hid := FindGunName()
if (hid == "") {
	OpenGui()
}
else {
	Notify(hid)
	Process(hid)
}
return

; -----------------------------------

FindGunName() {
	PnPSignedDriver := FindPnPSignedDriver()
	for i, v in PnPSignedDriver {
		for k, v in deviceIds {
			if (InStr(PnPSignedDriver[i].DeviceID, v)) {
				; MsgBox, % "Id: " . PnPSignedDriver[i].DeviceID . "`rstatus: " . PnPSignedDriver[i].Status "`rk: " . k . "`rv: " . v
				return k
			}
		}
	}
	MsgBox, "No guns detected, select one manually."
	return ""
}

; ref: https://www.autohotkey.com/boards/viewtopic.php?t=90238
FindPnPSignedDriver()
{
	try
	{
		PnPSignedDriver := []
		for objItem in ComObjGet("winmgmts:").ExecQuery("SELECT * FROM Win32_PnPSignedDriver")
		{
			if (InStr(objItem.DeviceID, "HID\VID_")) {
				PnPSignedDriver[A_Index, "Description"] := objItem.Description
				PnPSignedDriver[A_Index, "DeviceID"] := objItem.DeviceID
				PnPSignedDriver[A_Index, "Status"] := objItem.Status
			}
		}
		return PnPSignedDriver
	}
	return ""
}

; ref : https://www.autohotkey.com/boards/viewtopic.php?t=114512
Notify(hid) {
	pronunciation := StrReplace(hid, "Gun4IR", "Gun for IR")
	sv := ComObjCreate("SAPI.SpVoice")
	sv.Voice  := sv.GetVoices().Item(1)
	sv.Speak(pronunciation . " detected")
}

; -----------------------------------

OpenGui() {
	Gui, Add, Button, w100 gOnPress, Sinden
	Gui, Add, Button, x+5 yp wp gOnPress, Gun4IR
	Gui, Add, Button, x+5 yp wp gOnPress, XGunner MP5
	Gui, Add, Button, x+5 yp wp gOnPress, XGunner Pistol
	Gui, Add, Button, x+5 yp wp gOnPress, None
	Gui, Show,,Choose a lightgun to use.
}

GuiClose()  {
	ExitApp
}

OnPress()  {
	Gui, Hide
	Process(A_GuiControl)
}

Process(gunName) {
	switch
	{
	case InStr(gunName, "Sinden"): 
		PrepareTeknoparrot(gunName)
		PrepareDemulShooter(gunName)
		ExcuteSinden()
		ExcuteRetrobat()
		SetCrosshairCursor()
		MonitorRetrobat()
		return

	case InStr(gunName, "XGunner"): 
		PrepareTeknoparrot(gunName)
		PrepareDemulShooter(gunName)
		ExcuteKeyManager(gunName)
		ExcuteRetrobat()
		SetCrosshairCursor()
		MonitorRetrobat()
		return

	case InStr(gunName, "Gun4IR"): 
		PrepareTeknoparrot(gunName)
		PrepareDemulShooter(gunName)
		ExcuteRetrobat()
		SetCrosshairCursor()
		MonitorRetrobat()
		return

	default: 
		ExcuteRetrobat()
		ExitApp
	}
}

; -----------------------------------

ExcuteSinden() {
	Run, E:\Lightgun\SindenLightgun V2.07\Lightgun.exe
}

ExcuteKeyManager(gunName) {
	; app := % "E:\Lightgun\Key Manager - " . gunName . "\keymanager.exe"
	; Run, %app%

	Run, E:\Lightgun\Key Manager\keymanager.exe
	sleep,1000

	switch
	{
	case InStr(gunName, "XGunner MP5"): 
		SendRaw ^!1
		return

	case InStr(gunName, "XGunner Pistol"): 
		SendRaw ^!2
		return
	}
}

ExcuteRetrobat() {
	Run, D:\RetroBat_Gun\retrobat.exe
	sleep,1000
	WinActivate, EmulationStation
}

MonitorRetrobat() {
	WinTitle:= "EmulationStation"
	loop, {
	    if not WinExist(WinTitle) {
		Process, Close, keymanager.exe
		Run, taskkill /im "keymanager.exe" /F

		Process, Close, Lightgun.exe
		Run, taskkill /im "Lightgun.exe" /F

		RestoreCursors()

		ExitApp
	    }
	    Sleep, 1000
	}
}

; -----------------------------------

; Symbolic links - requires administrator privileges
/*
PrepareTeknoparrot(gunName) {
	run, %comspec% /c rmdir "D:\RetroBat_Gun\emulators\teknoparrot",,hide
	cmd := "mklink /d ""D:\RetroBat_Gun\emulators\teknoparrot"" ""D:\RetroBat_Gun\emulators\teknoparrot - " . gunName . """"
	run, %comspec% /c %cmd%,,hide
}

PrepareDemulShooter(gunName) {
	run, %comspec% /c rmdir "E:\Lightgun\DemulShooter",,hide
	cmd := "mklink /d ""E:\Lightgun\DemulShooter"" ""E:\Lightgun\DemulShooter - " . gunName . """"
	run, %comspec% /c %cmd%,,hide
}
*/

; Copy the configuration file
PrepareTeknoparrot(gunName) {
	cmd := "copy ""E:\Lightgun\DemulShooter - " . gunName . "\config.ini"" ""E:\Lightgun\DemulShooter\"" /Y"
	run, %comspec% /c %cmd%,,hide

	cmd := "echo " . gunName . " > ""E:\Lightgun\DemulShooter\__marker__.txt"""
	run, %comspec% /c %cmd%,,hide
}

PrepareDemulShooter(gunName) {
	cmd := "xcopy ""D:\RetroBat_Gun\emulators\teknoparrot - " . gunName . "\UserProfiles"" ""D:\RetroBat_Gun\emulators\teknoparrot\UserProfiles"" /e /h /k /y"
	run, %comspec% /c %cmd%,,hide

	cmd := "echo " . gunName . " > ""D:\RetroBat_Gun\emulators\teknoparrot\__marker__.txt"""
	run, %comspec% /c %cmd%,,hide
}

; -----------------------------------

; ref : https://www.autohotkey.com/board/topic/32608-changing-the-system-cursor/
SetCrosshairCursor() {
	Cursors = 32512,32513,32514,32515,32516,32640,32641,32642,32643,32644,32645,32646,32648,32649,32650,32651
	Loop, Parse, Cursors, `,
	{
		DllCall( "SetSystemCursor", Uint, DllCall("LoadCursorFromFile", Str, cursorFile), Int, A_Loopfield )
		; DllCall("SystemParametersInfo", "Int", 0x2029, "Int", 0, "Ptr", 48, "Int", 0x01)
	}
}

RestoreCursors() {
	SPI_SETCURSORS := 0x57
	DllCall("SystemParametersInfo", UInt, SPI_SETCURSORS, UInt, 0, UInt, 0, UInt, 0 )
	; DllCall("SystemParametersInfo", "Int", 0x2029, "Int", 0, "Ptr", 30, "Int", 0x01)
}

댓글 남기기