MCRC=93430C5C MVersion=1.4.0 StartModule(){ Global gameSectionStartTime,gameSectionStartHour,skipChecks,dbName,romPath,romPathOrig,romName,romExtension,systemName,MEmu,MEmuV,MURL,MAuthor,MVersion,MCRC,iCRC,MSystem,romMapTable,romMappingLaunchMenuEnabled,romMenuRomName,sevenZEnabled,hideCursor,toggleCursorKey,servoStikEnabled,ledblinkyEnabled,winVer,setResolution,zz Global mgEnabled,mgOnLaunch,mgCandidate,mgLaunchMenuActive,MultiGame_Running,keyboardEncoder,keyboardEncoderEnabled,ultraMapFullPath,winIPACFullPath Global rIniIndex,globalPluginsFile,sysPluginsFile Log("StartModule - Started") Log("StartModule - MEmu: " . MEmu . "`r`n`t`t`t`t`tMEmuV: " . MEmuV . "`r`n`t`t`t`t`tMURL: " . MURL . "`r`n`t`t`t`t`tMAuthor: " . MAuthor . "`r`n`t`t`t`t`tMVersion: " . MVersion . "`r`n`t`t`t`t`tMCRC: " . MCRC . "`r`n`t`t`t`t`tiCRC: " . iCRC . "`r`n`t`t`t`t`tMID: " . MID . "`r`n`t`t`t`t`tMSystem: " . MSystem) If InStr(MSystem,systemName) Log("StartModule - You have a supported System Name for this module: """ . systemName . """") Else Log("StartModule - You have an unsupported System Name for this module: """ . systemName . """. Only the following System Names are suppported: """ . MSystem . """",2) winVer := If A_Is64bitOS ? "64" : "32" ; get windows version ;----------------------------------------------------------------------------------------------------------------------------------------- ; Plugin Specific Settings from Settings \ Global Plugins.ini and Settings \ %systemName% \ Plugins.ini ;----------------------------------------------------------------------------------------------------------------------------------------- rIniIndex := {} ; initialize the RIni array globalPluginsFile := A_ScriptDir . "\Settings\Global Plugins.ini" If !FileExist(globalPluginsFile) FileAppend,, %globalPluginsFile% ; create blank ini RIni_Read(8,globalPluginsFile) rIniIndex[8] := globalPluginsFile ; assign to array sysPluginsFile := A_ScriptDir . "\Settings\" . systemName . "\Plugins.ini" If !FileExist(sysPluginsFile) FileAppend,, %sysPluginsFile% ; create blank ini RIni_Read(9,sysPluginsFile) rIniIndex[9] := sysPluginsFile ; assign to array Gosub, PluginInit ; initialize plugin vars ; Gosub, ReadFEGameInfo ; read plugin data ;----------------------------------------------------------------------------------------------------------------------------------------- If (mgEnabled = "true" && mgOnLaunch = "true" && mgCandidate) { ; only if user has mgOnLaunch enabled mgLaunchMenuActive := true Log("StartModule - MultiGame_On_Launch execution started.",4) Gosub, StartMulti Sleep, 200 Loop { If !MultiGame_Running Break } mgLaunchMenuActive := false Log("StartModule - MultiGame_On_Launch execution ended.",4) } If (romMappingLaunchMenuEnabled = "true" && romMapTable.MaxIndex()) ; && romMapMultiRomsFound) CreateRomMappingLaunchMenu%zz%(romMapTable) ; msgbox dbName: %dbName%`nromName: %romName%`nromMenuRomName: %romMenuRomName%`nsevenZEnabled: %sevenZEnabled%`nskipChecks: %skipChecks% If (skipChecks != "false" && romMenuRomName && sevenZEnabled = "false") ; this is to support the scenario where Rom Map Launch Menu can send a rom that does not exist on disk or in the archive (mame clones) { Log("StartModule - Setting romName to the game picked from the Launch Menu: " . romMenuRomName,4) romName := romMenuRomName } Else If romName && romMapTable.MaxIndex() { Log("StartModule - Leaving romName as is because Rom Mapping filled it with an Alternate_Rom_Name: " . romName,4) romName := romName ; When Rom Mapping is used but no Alternate_Archive_Name key exists yet Alternate_Rom_Name key(s) were used. } Else If romMapTable.MaxIndex() { Log("StartModule - Not setting romName because Launch Menu was used and 7z will take care of it.",4) romName := "" ; If a romMapTable exists with roms, do not fill romName yet as 7z will take care of that. } Else { Log("StartModule - Setting romName to the dbName sent to RocketLauncher: " . dbName,4) romName := dbName ; Use dbName if previous checks are false } romPathOrig := romPath ; storing original rom path for any modules or features that need this before 7z updates it. If (setResolution != "") SetDisplaySettings(setResolution) If RegExMatch(hideCursor,"i)true|do_not_restore") SystemCursor("Off") Else If RegExMatch(hideCursor,"i)custom|custom_do_not_restore") { cursor := GetRLMediaFiles("Cursors","cur|ani") ;load cursor file for the system if they exist If cursor SetSystemCursor(Cursor) ; replace system cursors } If toggleCursorKey XHotKeywrapper(toggleCursorKey,"ToggleCursor") If (ledblinkyEnabled = "All") LEDBlinky("START") KeymapperProfileSelect("START", keyboardEncoder, %keyboardEncoder%FullPath, "ipc", "keyboard") KeymapperProfileSelect("START", "UltraMap", ultraMapFullPath, "ugc") If (servoStikEnabled = 4 || servoStikEnabled = 8) ServoStik(servoStikEnabled) ; handle servostiks on start ; romName := If romName ? romName : If romMapTable.MaxIndex() ? "" : dbName ; OLD METHOD, keeping this here until the split apart conditionals have been tested enough. ; if romName was filled at some point, use it, Else If a romMapTable exists with roms, do not fill romName yet as 7z will take care of that. Use dbName if previous checks are false Gui, show,Hide,RocketLauncherMessageReceiver ; creating hidden GUI to receive RocketLauncher messages OnMessage(0x4a, "ReceiveMessage") BroadcastMessage("RocketLauncher Message: Welcome! :)") gameSectionStartTime := A_TickCount gameSectionStartHour := A_Now ; msgbox % "romPath: " . romPath . "`nromName: " . romName . "`nromExtension: " . romExtension . "`nromMenuRomName: " . romMenuRomName . "`nromMapTable.MaxIndex(): " . romMapTable.MaxIndex() Log("StartModule - Ended") } ; ExitModule function in case we need to call anything on the module's exit routine, like UpdateStatistics for Pause or UnloadKeymapper ExitModule(){ Global statisticsEnabled,keymapperEnabled,keymapper,keymapperAHKMethod,keymapperLoaded,logShowCommandWindow,pToken,cmdWindowObj,mouseCursorHidden,cursor,hideCursor,rocketLauncherIsExiting,servoStikExitMode,ledblinkyEnabled,zz Global JoyIDsEnabled,JoyIDsPreferredControllersOnExit,keyboardEncoder,keyboardEncoderEnabled,ultraMapFullPath,winIPACFullPath Log("ExitModule - Started") rocketLauncherIsExiting := 1 ; notifies rest of the thread that the exit routine was triggered If (statisticsEnabled = "true") Gosub, UpdateStatistics If (keymapperEnabled = "true" && keymapperLoaded) RunKeyMapper%zz%("unload",keymapper) If keymapperAHKMethod = External RunAHKKeymapper%zz%("unload") If (JoyIDsEnabled = "true" && JoyIDsPreferredControllersOnExit) LoadPreferredControllers%zz%(JoyIDsPreferredControllersOnExit) If (hideCursor = "custom" and cursor) RestoreCursors() ; retore default system cursors If mouseCursorHidden ; just in case SystemCursor("On") If (ledblinkyEnabled = "All") LEDBlinky("END") KeymapperProfileSelect("END", keyboardEncoder, %keyboardEncoder%FullPath, "ipc", "keyboard") KeymapperProfileSelect("END", "UltraMap", ultraMapFullPath, "ugc") If (servoStikExitMode = 4 || servoStikExitMode = 8) ServoStik(servoStikExitMode) ; handle servostiks on exit If logShowCommandWindow = true Loop { If !cmdWindowObj[A_Index,"Name"] Break Else { Log("ExitModule - Closing command window: " . cmdWindowObj[A_Index,"Name"] . " PID: " . cmdWindowObj[A_Index,"PID"],4) Process("Close", cmdWindowObj[A_Index,"Name"]) ; close each opened cmd.exe } } BroadcastMessage("RocketLauncher Message: Goodbye! :(") Gdip_Shutdown(pToken) ; gdi+ may now be shutdown on exiting the thread Log("ExitModule - Ended") Log("End of Module Logs",,,1) ExitApp } WinWait(winTitle,winText="",secondsToWait=30,excludeTitle="",excludeText=""){ Global detectFadeErrorEnabled, logLevel If logLevel > 3 GetActiveWindowStatus() Log("WinWait - Waiting for """ . winTitle . """") WinWait, %winTitle% ,%winText% , %secondsToWait% , %excludeTitle% ,%excludeText% curErr := ErrorLevel ; have to store this because GetActiveWindowStatus will reset it If logLevel > 3 GetActiveWindowStatus() If (curErr and detectFadeErrorEnabled = "true") ScriptError("There was an error waiting for the window """ . winTitle . """. Please check you have the correct version emulator installed for this module, followed any notes in the module, and have this emulator working outside your Frontend first. Also turn off Fade to see if you are hiding your problem.",10) Else If (curErr and detectFadeErrorEnabled != "true") Log("There was an error waiting for the window """ . winTitle . """. Please check you have the correct version emulator installed for this module, followed any notes in the module, and have this emulator working outside your Frontend first. Also turn off Fade to see if you are hiding your problem.",3) Return curErr } WinWaitActive(winTitle,winText="",secondsToWait=30,excludeTitle="",excludeText=""){ Global detectFadeErrorEnabled, logLevel, emulatorProcessID, emulatorVolumeObject, emulatorInitialMuteState, fadeMuteEmulator, fadeIn If (logLevel > 3) GetActiveWindowStatus() Log("WinWaitActive - Waiting for """ . winTitle . """") WinWaitActive, %winTitle% ,%winText% , %secondsToWait% , %excludeTitle% ,%excludeText% curErr := ErrorLevel ; have to store this because GetActiveWindowStatus will reset it If (logLevel > 3) GetActiveWindowStatus() If (curErr and detectFadeErrorEnabled = "true") ScriptError("There was an error waiting for the window """ . winTitle . """ to become active. Please check you have the correct version emulator installed for this module, followed any notes in the module, and have this emulator working outside your Frontend first. Also turn off Fade to see if you are hiding your problem.",10) Else If (curErr and detectFadeErrorEnabled != "true") Log("There was an error waiting for the window """ . winTitle . """ to become active. Please check you have the correct version emulator installed for this module, followed any notes in the module, and have this emulator working outside your Frontend first. Also turn off Fade to see if you are hiding your problem.",3) If !curErr { WinGet emulatorProcessID, PID, %winTitle% emulatorVolumeObject := GetVolumeObject(emulatorProcessID) If ((fadeMuteEmulator = "true") and (fadeIn = "true")){ getMute(emulatorInitialMuteState, emulatorVolumeObject) setMute(1, emulatorVolumeObject) } } Return curErr } WinWaitClose(winTitle,winText="",secondsToWait="",excludeTitle="",excludeText=""){ Log("WinWaitClose - Waiting for """ . winTitle . """ to close") WinWaitClose, %winTitle% ,%winText% , %secondsToWait% , %excludeTitle% ,%excludeText% Return ErrorLevel } WinClose(winTitle,winText="",secondsToWait="",excludeTitle="",excludeText=""){ Log("WinClose - Closing: " . winTitle) WinClose, %winTitle%, %winText% , %secondsToWait%, %excludeTitle%, %excludeText% If (secondsToWait = "" || !secondsToWait) secondsToWait := 2 ; need to always have some timeout for this command otherwise it will wait forever WinWaitClose, %winTitle%, %winText% , %secondsToWait%, %excludeTitle%, %excludeText% ; only WinWaitClose reports an ErrorLevel Return ErrorLevel } WinActivate(WinTitle,WinText="",ExcludeTitle="",ExcludeText="") { Log("WinActivate - Selecting " . Menu . " -> " . SubMenu1 . (SubMenu2 ? " -> " . SubMenu2 : "") . (SubMenu3 ? " -> " . SubMenu3 : "") . (SubMenu4 ? " -> " . SubMenu4 : "") . (SubMenu5 ? " -> " . SubMenu5 : "") . (SubMenu6 ? " -> " . SubMenu6 : ""),4) WinActivate,%WinTitle%,%WinText%,%Menu%,%SubMenu1%,%SubMenu2%,%SubMenu3%,%SubMenu4%,%SubMenu5%,%SubMenu6%,%ExcludeTitle%,%ExcludeText% Return ErrorLevel } WinGet(cmd,WinTitle,WinText="",ExcludeTitle="",ExcludeText="",log=1) { WinGet,OutputVar,%cmd%,%WinTitle%,%WinText%,%ExcludeTitle%,%ExcludeText% If log Log("WinGet - Retrieved """ . OutputVar . """ from " . WinTitle . A_Space . WinText,4) Return OutputVar } WinGetTitle(WinTitle,WinText="",ExcludeTitle="",ExcludeText="",log=1) { WinGetTitle,OutputVar,%WinTitle%,%WinText%,%ExcludeTitle%,%ExcludeText% If log Log("WinGetTitle - Retrieved """ . OutputVar . """ from " . WinTitle . A_Space . WinText,4) Return OutputVar } WinGetPos(ByRef x,ByRef y,ByRef w,ByRef h,WinTitle,WinText="",ExcludeTitle="",ExcludeText="",log=1) { WinGetPos,x,y,w,h,%WinTitle%,%WinText%,%ExcludeTitle%,%ExcludeText% If log Log("WinGetPos - Retrieved x:" . x . " y:" . y . " w: " . w . " h: " . h . " from " . WinTitle . A_Space . WinText,4) Return } WinHide(WinTitle,WinText="",ExcludeTitle="",ExcludeText="") { Log("WinHide - Hiding window " . WinTitle . A_Space . WinText,4) WinHide,%WinTitle%,%WinText%,%ExcludeTitle%,%ExcludeText% Return ErrorLevel } WinMenuSelectItem(WinTitle,WinText="",Menu="",SubMenu1="",SubMenu2="",SubMenu3="",SubMenu4="",SubMenu5="",SubMenu6="",ExcludeTitle="",ExcludeText="") { If (!Menu || !SubMenu1) ScriptError("Menu and SubMenu are required for RL.WinMenuSelectItem") Log("WinMenuSelectItem - Selecting " . Menu . " -> " . SubMenu1 . (SubMenu2 ? " -> " . SubMenu2 : "") . (SubMenu3 ? " -> " . SubMenu3 : "") . (SubMenu4 ? " -> " . SubMenu4 : "") . (SubMenu5 ? " -> " . SubMenu5 : "") . (SubMenu6 ? " -> " . SubMenu6 : ""),4) WinMenuSelectItem,%WinTitle%,%WinText%,%Menu%,%SubMenu1%,%SubMenu2%,%SubMenu3%,%SubMenu4%,%SubMenu5%,%SubMenu6%,%ExcludeTitle%,%ExcludeText% Return ErrorLevel } WinMove(x,y,w="",h="",WinTitle="",WinText="",ExcludeTitle="",ExcludeText="",log=1) { If WinTitle WinMove,%WinTitle%,%WinText%,%x%,%y%,%w%,%h%,%ExcludeTitle%,%ExcludeText% Else WinMove,%x%,%y% If log Log("WinMove - Moved " . WinTitle . A_Space . WinText . " to x:" . x . " y:" . y . " w: " . w . " h: " . h,4) Return } WinRestore(WinTitle,WinText="",ExcludeTitle="",ExcludeText="") { Log("WinRestore - Restoring window " . WinTitle . A_Space . WinText,4) WinRestore,%WinTitle%,%WinText%,%ExcludeTitle%,%ExcludeText% Return ErrorLevel } WinSet(Attribute,Value,WinTitle,WinText="",ExcludeTitle="",ExcludeText="") { Log("WinSet - Setting " . Attribute . " to " . Value . " on window " . WinTitle . A_Space . WinText,4) WinSet,%Attribute%,%Value%,%WinTitle%,%WinText%,%ExcludeTitle%,%ExcludeText% Return ErrorLevel } WinShow(WinTitle,WinText="",ExcludeTitle="",ExcludeText="") { Log("WinShow - Unhiding window " . WinTitle . A_Space . WinText,4) WinShow,%WinTitle%,%WinText%,%ExcludeTitle%,%ExcludeText% Return ErrorLevel } RunWait(target,workingDir="",options=0,ByRef outputVarPID=""){ Global errorLevelReporting options := If options = 1 ? "useErrorLevel" : options ; enable or disable error level Log("RunWait - Started - running: " . workingDir . "\" . target) RunWait, %target%, %workingDir%, %options%, outputVarPID curErr := ErrorLevel ; store error level immediately Log("RunWait - """ . target . """ Process ID: " . outputVarPID . " and ErrorLevel reported as: " . curErr, 4) Return curErr } ; To disable inputBlocker on a specific Run call, set inputBlocker to 0, or to force it a specified amount of seconds (upto 30), set it to that amount. ; By default, options will enable all calls of Run() to return errorlevel within the function. However, it will only be returned if errorLevelReporting is true ; bypassCmdWindow - some apps will never work with the command window, like xpadder. enable this argument on these Run calls so it doesn't get caught here Run(target,workingDir="",options=0,ByRef outputVarPID="",inputBlocker=1,bypassCmdWindow=0,disableLogging=0){ Static cmdWindowCount Global logShowCommandWindow,logCommandWindow,cmdWindowObj,blockInputTime,blockInputFile,errorLevelReporting options := If options = 1 ? "useErrorLevel" : options ; enable or disable error level If disableLogging Log("Run - Running hidden executable in " . workingDir) Else Log("Run - Running: " . workingDir . "\" . target) If (blockInputTime && inputBlocker = 1) ; if user set a block time, use the user set length blockTime := blockInputTime Else If (inputBlocker > 1) ; if module called for a block, use that amount blockTime := inputBlocker Else ; do not block input blockTime := "" If blockTime { Log("Run - Blocking Input for: " . blockTime . " seconds") Run, %blockInputFile% %blockTime% } If !cmdWindowObj cmdWindowObj := Object() ; initialize object, this is used so all the command windows can be properly closed on exit If (logShowCommandWindow = "true" && !bypassCmdWindow) { Run, %ComSpec% /k, %workingDir%, %options%, outputVarPID ; open a command window (cmd.exe), starting in the directory of the target executable curErr := ErrorLevel ; store error level immediately If errorLevelReporting = true { Log("Run - Error Level for " . ComSpec . " reported as: " . curErr, 4) errLvl := curErr ; allows the module to handle the error level } Log("Run - Showing Command Window to troubleshoot launching. ProcessID: " . outputVarPID) WinWait, ahk_pid %outputVarPID% WinActivate, ahk_pid %outputVarPID% WinWaitActive, ahk_pid %outputVarPID%,,2 If ErrorLevel { WinSet, AlwaysOnTop, On, ahk_pid %outputVarPID% WinActivate, ahk_pid %outputVarPID% WinWaitActive, ahk_pid %outputVarPID%,,2 If ErrorLevel ScriptError("Could not put focus onto the command window. Please try turning off Fade In if you have it enabled in order to see it") } WinGet, procName, ProcessName, ahk_pid %outputVarPID% ; get the name of the process (which should usually be cmd.exe) mapObjects[currentObj,"type"] := "database" cmdWindowCount++ cmdWindowObj[cmdWindowCount,"Name"] := procName ; store the ProcessName being ran cmdWindowObj[cmdWindowCount,"PID"] := outputVarPID ; store the PID of the application being ran foundPos := RegExMatch(target,".*\.(exe|bat)",targetExeOnly) ; grab only the exe or bat file from the supplied target exeLen := StrLen(targetExeOnly) ; get length of exe name params := SubStr(target, exeLen+1) ; grab optional params out of supplied target If InStr(targetExeOnly, A_Space) target := """" . targetExeOnly . """" . params If (logCommandWindow = "true") SendInput, {Raw}%target% 1>"%A_ScriptDir%\command_%cmdWindowCount%_output.log" 2>"%A_ScriptDir%\command_%cmdWindowCount%_error.log" ; send the text to the command window and log the output to file Else SendInput, {Raw}%target% ; send the text to the command window and run it Send, {Enter} } Else { Run, %target%, %workingDir%, %options%, outputVarPID curErr := ErrorLevel ; store error level immediately If errorLevelReporting = true { Log("Run - Error Level for " . target . " reported as: " . curErr, 4) errLvl := curErr ; allows the module to handle the error level } } If disableLogging Log("Run - ""Hidden executable"" Process ID: " . outputVarPID, 4) Else Log("Run - """ . target . """ Process ID: " . outputVarPID, 4) Return errLvl } ; Replaces standard ahk IniRead calls ; Will not let ERROR be returned, returns no value instead ; If errormsg is set, will trigger ScriptError instead of returning no or default value IniRead(file,section,key,defaultvalue="",errorMsg="") { IniRead, v, %file%, %section%, %key%, %defaultvalue% ; msgbox % v Log("IniRead - SECTION: [" . section . "] - KEY: " . key . " - VALUE: " . v . " - FILE: " . file,4) If (v = "ERROR" || v = A_Space) { ; if key does not exist or is a space, delete ERROR as the value If errorMsg ScriptError(errorMsg) Else { If (defaultValue := A_Space) ; this prevents the var from existing when it's actually blank defaultValue := "" Return defaultValue } } Return v } ; Replaces standard ahk IniWrite calls ; compare = if used, only writes new value if existing value differs IniWrite(value,file,section,key,compare="") { If compare { IniRead, v, %file%, %section%, %key% If (v != value) { IniWrite, %value%, %file%, %section%, %key% err := ErrorLevel Log("IniWrite - ini updated due to differed value. SECTION: [" . section . "] - KEY: " . key . " - Old: " . v . " | New: " . value) } Else Log("IniWrite - ini value already correct. SECTION: [" . section . "] - KEY: " . key . " - Value: " . value,4) } Else { IniWrite, %value%, %file%, %section%, %key% err := ErrorLevel Log("IniWrite - SECTION: [" . section . "] - KEY: " . key . " - VALUE: " . value . " - FILE: " . file) } If err Log("IniWrite - There was an error writing to the ini",3) Return err ; returns 1 if there was an error } Process(cmd,name,param=""){ Log("Process - " . cmd . A_Space . name . A_Space . param) Process, %cmd%, %name%, %param% Return ErrorLevel } SetKeyDelay(delay="",pressDur="",play="") { Global pressDuration If (delay = "") ; -1 is the default delay for play mode and 10 for event mode when none is supplied delay := (If play = "" ? 10 : -1) If (pressDur = "") ; -1 is the default pressDur when none is supplied pressDur := -1 Log("SetKeyDelay - Current delay is " . A_KeyDelay . ". Current press duration is " . pressDuration . ". Delay will now be set to """ . delay . """ms for a press duration of """ . pressDur . """", 4) SetKeyDelay, %delay%, %pressDur%, %play% pressDuration := pressDur ; this is so the current pressDuration can be monitored outside the function } ; Mainly used in modules to read module.ini settings so multiple sections of an ini can be read of the same key name ; section: Allows | separated values so multiple sections can be checked. IniReadCheck(file,section,key,defaultvalue="",errorMsg="",logType="") { Loop, Parse, section, | { section%A_Index% := A_LoopField ; keep each parsed section in its own var If iniVar != "" ; if last loop's iniVar has a value, update this loop's default value with it defaultValue := If A_Index = 1 ? defaultValue : iniVar ; on first loop, default value will be the one sent to the function, on following loops it gets the value from the previous loop IniRead, iniVar, %file%, % section%A_Index%, %key%, %defaultvalue% If (IniVar = "ERROR" || iniVar = A_Space) ; if key does not exist or is a space, delete ERROR as the value iniVar := "" If (A_Index = 1 && iniVar = "" and !logType) { If errorMsg ScriptError(errorMsg) Else IniWrite, %defaultValue%, %file%, % section%A_Index%, %key% Return defaultValue } If logType ; only log if logType set { logAr := ["Module","Bezel"] Log(logAr[logType] . " Setting - [" . section%A_Index% . "] - " . key . ": " . iniVar) } If (iniVar != "") ; if IniVar contains a value, update the lastIniVar lastIniVar := iniVar } If defaultValue = %A_Space% ; this prevents the var from existing when it's actually blank defaultValue := "" Return If A_Index = 1 ? iniVar : If lastIniVar != "" ? lastIniVar : defaultValue ; if this is the first loop, always return the iniVar. If any other loop, return the lastinivar if it was filled, otherwise send the last updated defaultvalue } MaximizeWindow(WinTitle, keepAspectRatio="true", removeTitle="true", removeBorder="true", removeToggleMenu="true") { Log("MaximizeWindow - Started to process window """ . winTitle . """", 4) If (removeTitle = "true") RL.WinSet("Style", "-0xC00000", WinTitle) ;Removes the titlebar of the game window If (removeBorder = "true") RL.WinSet("Style", "-0x40000", WinTitle) ;Removes the border of the game window If (removeToggleMenu = "true") { WinID := RL.WinGet( "ID", WinTitle) ToggleMenu(WinID) } If (keepAspectRatio = "true") { RL.WinGetPos(appX, appY, appWidth, appHeight, WinTitle) widthMaxPercenty := ( A_ScreenWidth / appWidth ) heightMaxPercenty := ( A_ScreenHeight / appHeight ) If ( widthMaxPercenty < heightMaxPercenty ) percentToEnlarge := widthMaxPercenty Else percentToEnlarge := heightMaxPercenty appWidthNew := appWidth * percentToEnlarge appHeightNew := appHeight * percentToEnlarge appY := RL.Transform("Round", appY) appWidthNew := RL.Transform("Round", appWidthNew, 2) appHeightNew := RL.Transform("Round", appHeightNew, 2) appXPos := ( A_ScreenWidth / 2 ) - ( appWidthNew / 2 ) appYPos := ( A_ScreenHeight / 2 ) - ( appHeightNew / 2 ) } Else { appXPos := 0 appYPos := 0 appWidthNew := A_ScreenWidth appHeightNew := A_ScreenHeight } MoveWindow(WinTitle,appXPos,appYPos,appWidthNew,appHeightNew) Log("MaximizeWindow - Ended", 4) } MoveWindow(winTitle,X,Y,W,H,timeLimit=2000,ignoreWin=""){ ; assures that a window is moved to the desired position within a timeout Log("MoveWindow - Moving window " . winTitle . " to X=" . X . ", Y=" . Y . ", W=" . W . " H=" . H, 4) RL.WinMove(winTitle,"", X, Y, W, H, ignoreWin) ;check If window moved timeout := A_TickCount Loop { RL.WinGetPos(Xgot, Ygot, Wgot, Hgot, winTitle,"", ignoreWin) If ((Xgot=X) and (Ygot=Y) and (Wgot=W) and (Hgot=H)){ Log("MoveWindow - Successful: Window " . winTitle . " moved to X=" . X . ", Y=" . Y . ", W=" . W . " H=" . H, 4) error := 0 Break } If (timeout A_screenHeight) and ((screenRotationAngle=90) or (screenRotationAngle=270)))) XBaseRes := 1080, YBaseRes := 1920 If !errorXScale errorXScale := baseScreenWidth/XBaseRes If !errorYScale errorYScale := baseScreenHeight/YBaseRes Error_Warning_Width := w Error_Warning_Height := h Error_Warning_Pen_Width := 7 Error_Warning_Rounded_Corner := 30 Error_Warning_Margin := 30 Error_Warning_Bitmap_Size := 125 Error_Warning_Text_Size := txt OptionScale(Error_Warning_Width, errorXScale) OptionScale(Error_Warning_Height, errorYScale) OptionScale(Error_Warning_Pen_Width, errorXScale) OptionScale(Error_Warning_Rounded_Corner, errorXScale) OptionScale(Error_Warning_Margin, errorXScale) OptionScale(Error_Warning_Bitmap_Size, errorXScale) OptionScale(Error_Warning_Text_Size, errorYScale) ;Create error GUI Gui, Error_GUI: +Disabled -Caption +E0x80000 +OwnDialogs +LastFound +ToolWindow +AlwaysOnTop Gui, Error_GUI: Margin,0,0 Gui, Error_GUI: Show,, ErrorLayer Error_hwnd := WinExist() Error_hbm := CreateDIBSection(A_ScreenWidth, A_ScreenHeight) Error_hdc := CreateCompatibleDC() Error_obm := SelectObject(Error_hdc, Error_hbm) Error_G := Gdip_GraphicsFromhdc(Error_hdc) Gdip_SetSmoothingMode(Error_G, 4) Gdip_TranslateWorldTransform(Error_G, xTranslation, yTranslation) Gdip_RotateWorldTransform(Error_G, screenRotationAngle) pGraphUpd(Error_G,baseScreenWidth, baseScreenHeight) ;Create GUI elements pBrush := Gdip_BrushCreateSolid("0xFF000000") ; Painting the background color Gdip_Alt_FillRectangle(Error_G, pBrush, -1, -1, baseScreenWidth+1, baseScreenHeight+1) ; draw the background first on layer 1 first, layer order matters!! brushWarningBackground := Gdip_CreateLineBrushFromRect(0, 0, Error_Warning_Width, Error_Warning_Height, 0xff555555, 0xff050505) penWarningBackground := Gdip_CreatePen(0xffffffff, Error_Warning_Pen_Width) Gdip_Alt_FillRoundedRectangle(Error_G, brushWarningBackground, (baseScreenWidth - Error_Warning_Width)//2, (baseScreenHeight - Error_Warning_Height)//2, Error_Warning_Width, Error_Warning_Height, Error_Warning_Rounded_Corner) Gdip_Alt_DrawRoundedRectangle(Error_G, penWarningBackground, (baseScreenWidth - Error_Warning_Width)//2, (baseScreenHeight - Error_Warning_Height)//2, Error_Warning_Width, Error_Warning_Height, Error_Warning_Rounded_Corner) WarningBitmap := Gdip_CreateBitmapFromFile(RLMediaPath . "\Menu Images\RocketLauncher\Warning.png") Gdip_Alt_DrawImage(Error_G,WarningBitmap, round((baseScreenWidth - Error_Warning_Width)//2 + Error_Warning_Margin),round(baseScreenHeight/2 - Error_Warning_Bitmap_Size/2),Error_Warning_Bitmap_Size,Error_Warning_Bitmap_Size) Gdip_Alt_TextToGraphics(Error_G, error, "x" round((baseScreenWidth-Error_Warning_Width)//2+Error_Warning_Bitmap_Size+Error_Warning_Margin) " y" round((baseScreenHeight-Error_Warning_Height)//2+Error_Warning_Margin) " Left vCenter cffffffff r4 s" Error_Warning_Text_Size " Bold",, round((Error_Warning_Width - 2*Error_Warning_Margin - Error_Warning_Bitmap_Size)) , round((Error_Warning_Height - 2*Error_Warning_Margin))) startTime := A_TickCount Loop{ ; fade in t := ((TimeElapsed := A_TickCount-startTime) < 300) ? (255*(timeElapsed/300)) : 255 Alt_UpdateLayeredWindow(Error_hwnd,Error_hdc, 0, 0, baseScreenWidth, baseScreenHeight,t) If (t >= 255) Break } ; Generate a random sound to play on a script error erSoundsAr:=[] ; initialize the array to store error sounds Loop, %RLErrSoundPath%\error*.mp3 erSoundsAr.Insert(A_LoopFileName) ; insert each found error sound into an array Random, erRndmSound, 1, % erSoundsAr.MaxIndex() ; randomize what sound to play Log("ScriptError - Playing error sound: " . erSoundsAr[erRndmSound],4) setMute(0,emulatorVolumeObject) SoundPlay % If erSoundsAr.MaxIndex() ? (RLErrSoundPath . "\" . erSoundsAr[erRndmSound]):("*-64"), wait ; play the random sound if any exist, or default to the Asterisk windows sound If noexit { ; do not close thread, continue with script and let it handle the exiting scriptErrorTriggered := 1 Return } 7zCleanUp() ; clean up 7z if necessary Sleep, %timeout% CloseError: endTime := A_TickCount Loop { ; fade out t := ((TimeElapsed := A_TickCount-endTime) < 300) ? (255*(1-timeElapsed/300)) : 0 Alt_UpdateLayeredWindow(Error_hwnd,Error_hdc, 0, 0, baseScreenWidth, baseScreenHeight,t) If (t <= 0) Break } XHotKeywrapper(exitEmulatorKey,"CloseError","OFF") XHotKeywrapper(exitEmulatorKey,"CloseProcess","ON") Gdip_DeleteBrush(pBrush) Gdip_DisposeImage(WarningBitmap), SelectObject(Error_hdc, Error_obm), DeleteObject(Error_hbm), DeleteDC(Error_hdc), Gdip_DeleteGraphics(Error_G) Gui, ErrorGUI_10: Destroy Log("ScriptError - " . error,3) ExitModule() ; attempting to use this method which has the small chance to cause an infinite ScriptError loop, but no need to duplicate code to clean up on errors ; Below cleanup exists because we can't call other functions that may cause additional scripterrors and put the thread in an infinite loop ; If logShowCommandWindow = true ; { for index, element in cmdWindowObj ; Process, Close, % cmdWindowObj[A_Index,1] ; close each opened cmd.exe ; } ; ExitApp } ; Log usage: ; text = text I want to log ; lvl = the lvl to log the text at ; notime = only used for 1st and last lines of the log so a time is not inserted when I inset the BBCode [code] tags. Do not use this param ; dump = tells the function to write the log file at the end. Do not use this param ; firstLog = tells the function to not insert a time when the first log is made, instead puts an N/A. Do not use this param ; Log() in the module thread requires `r`n at the end of each line, where it's not needed in the RocketLauncher thread Log(text,lvl=1,notime="",dump="",firstLog=""){ Static log Static lastLog Global logFile,logLevel,logLabel,logShowDebugConsole ; Global executable If (logLevel > 0) { If (lvl<=logLevel || lvl=3){ ; ensures errors are always logged logDiff := A_TickCount - lastLog lastLog := A_TickCount log := log . (If notime?"" : A_Hour . ":" . A_Min ":" . A_Sec ":" . A_MSec . " | MD | " . logLabel[lvl] . A_Space . " | +" . AlignColumn(If firstLog ? "N/A" : logDiff) . "" . " | ") . text . "`r`n" } If (logShowDebugConsole = "true") DebugMessage(log) If (logLevel>=10 || dump){ FileAppend,%log%,%logFile% log := "" } ; Process, Exist, %executable% ; If ErrorLevel ; Log .= "mame exists`r`n" Return log } } ; Inserts extra characters/spaces into sections of the Log file to keep it aligned. ; Usage: inserts char x number of times on the end of txt until pad is reached. AlignColumn(txt,pad=9,char=" "){ x := If char=" "?2:1 ; if char is a space, let's only insert half as many so it looks slightly more even in notepad++ Loop { n := StrLen(txt) If (n*x >= pad) Break txt := txt . char } Return txt } ; Rini returns -2 if section does not exist ; Rini returns -3 if key does not exist ; Rini returns -10 if an invalid reference var for the ini file was used ; Rini returns empty value if key exists with no value ; rIniIndex := Object(1,globalRLFile,2,sysRLFile,3,globalEmuFile,4,sysEmuFile,5,RLFile,6,gamesFile) ; preferDefault - On rare occasions we may want to set a default value w/o wanting rini to return an error value of -2 or -3. Used for JoyIDs_Preferred_Controllers RIniLoadVar(gRIniVar,sRIniVar,section,key,gdefaultvalue="",sdefaultvalue="use_global",preferDefault="") { Global rIniIndex If (gRIniVar != 6) ; do not create missing sections or keys for games.ini { gValue := RIni_GetKeyValue(gRIniVar,section,key,If preferDefault ? gdefaultvalue : "") gValue := gValue ; trims whitespace If RegExMatch(gValue,"-2|-3") ; if global ini key does not exist, create the key { RIni_SetKeyValue(gRIniVar,section,key,gdefaultvalue) RIni_Write(gRIniVar,rIniIndex[gRIniVar],"`r`n",1,1,1) gValue := gdefaultvalue ; set to default value because it did not exist Log("RIniLoadVar - Created missing Global ini key: """ . key . """ in section: """ . section . """ in """ . rIniIndex[gRIniVar] . """",2) } If sRIniVar ; != "" ; only create system sections or keys for inis that use them { sValue := RIni_GetKeyValue(sRIniVar,section,key,If preferDefault ? sdefaultvalue : "") sValue := sValue ; trims whitespace If RegExMatch(sValue,"-2|-3") ; if system ini key does not exist, create the key { RIni_SetKeyValue(sRIniVar,section,key,sdefaultvalue) RIni_Write(sRIniVar,rIniIndex[sRIniVar],"`r`n",1,1,1) sValue := sdefaultvalue ; set to default value because it did not exist Log("RIniLoadVar - Created missing System ini key: """ . key . """ in section: """ . section . """ in """ . rIniIndex[sRIniVar] . """",2) } Return If sValue = "use_global" ? gValue : sValue ; now compare global & system keys to get final value } Return gValue ; return gValue when not using globa/system inis, like RLFile (rIniIndex 5) } iniVar := RIni_GetKeyValue(gRIniVar,section,key,gdefaultvalue) ; lookup key from ini and return it iniVar := iniVar ; trims whitespace Return iniVar } RIniReadCheck(rIniVar,section,key,defaultvalue="",errorMsg="") { Global rIniIndex iniVar := RIni_GetKeyValue(rIniVar,section,key) ; lookup key from ini and return it iniVar := iniVar ; trims whitespace If (iniVar = -2 or iniVar = -3 or iniVar = "") { If (iniVar != "") { ; with rini, no need write to ini file if value is returned empty, we already know the section\key exists with no value Log("RIniReadCheck - Created missing RocketLauncher ini key: """ . key . """ in section: """ . section . """ in """ . rIniIndex[rIniVar] . """",2) RIni_SetKeyValue(rIniVar,section,key,defaultvalue) RIni_Write(rIniVar,rIniIndex[rIniVar],"`r`n",1,1,1) ; write blank section, blank key, and space between sections } If errorMsg ScriptError(errorMsg) Return defaultValue } Return iniVar } CheckFont(font) { If !(Gdip_FontFamilyCreate(font)) ScriptError("The Font """ . font . """ is not installed on your system. Please install the font or change it in RocketLauncherUI.") } ; Toggles hiding/showing a MenuBar ; Usage: Provide the window's PID of the window you want to toggle the MenuBar ; used in nulldc module and bezel ToggleMenu(hWin){ Static hMenu, visible If (hMenu = "") hMenu := DllCall("GetMenu", "uint", hWin) ; store the menubar ID so it can be restored later hMenuCur := DllCall("GetMenu", "uint", hWin) timeout := A_TickCount If !hMenuCur Loop { ;ToolTip, menubar is hidden`, bringing it back`nhMenuCur: %hMenuCur%`n%A_Index% hMenuCur := DllCall("GetMenu", "uint", hWin) If hMenuCur { Log("ToggleMenu - MenuBar is now visible for " . hWin,4) Break ; menubar is now visible, break out } DllCall("SetMenu", "uint", hWin, "uint", hMenu) If (timeout < A_TickCount - 500) ; prevents an infinite loop and breaks after 2 seconds Break } Else Loop { ; menubar is visible ;ToolTip, menubar is visible`, hiding it`nhMenuCur: %hMenuCur%`n%A_Index% hMenuCur := DllCall("GetMenu", "uint", hWin) If !hMenuCur { Log("ToggleMenu - MenuBar is now hidden for " . hWin,4) Break ; menubar is now hidden, break out } DllCall("SetMenu", "uint", hWin, "uint", 0) If (timeout < A_TickCount - 500) ; prevents an infinite loop and breaks after 2 seconds Break } } ; Original function but somestimes does not work, which is why the new function loops above ToggleMenuOld(hWin){ Static hMenu, visible If (hMenu = "") hMenu := DllCall("GetMenu", "uint", hWin) If !visible DllCall("SetMenu", "uint", hWin, "uint", hMenu) Else DllCall("SetMenu", "uint", hWin, "uint", 0) visible := !visible } ; Function to pause and wait for a user to press any key to continue. ; IdleCheck usage: ; t = timeout in ms to break out of function ; m = the method - can be "P" (physical) or "L" (logical) ; s = sleep or how fast the function checks for idle state ; Exits when state is no longer idle or times out IdleCheck(t="",m="L",s=200){ timeIdlePrev := 0 startTime := A_TickCount While timeIdlePrev < (If m = "L" ? A_TimeIdle : A_TimeIdlePhysical){ timeIdlePrev := If m = "L" ? A_TimeIdle : A_TimeIdlePhysical If (t && A_TickCount-startTime >= t) Return "Timed Out" Sleep s } Return A_PriorKey } ; This function looks through all defined romPaths and romExtensions for the provided rom file ; Returns a path to the rom where it was found ; Returns nothing if not found RomNameExistCheck(file,archivesOnly="") { Global romPathFromIni,romExtensions,sevenZFormats Loop, Parse, romPathFromIni, | ; for each rom path defined { tempRomPath := A_LoopField ; assigning this to a var so it can be accessed in the next loop Loop, Parse, romExtensions, | ; for each extension defined { If (archivesOnly != "") If !InStr(sevenZFormats,A_LoopField) ; if rom extension is not an archive type, skip this rom Continue ; msgbox % tempRomPath . "\" . file . "." . tempRomExtension Log("RomNameExistCheck - Looking for rom: " . tempRomPath . "\" . file . "." . A_LoopField,4) If FileExist( tempRomPath . "\" . file . "." . A_LoopField ) { Log("RomNameExistCheck - Found rom: " . tempRomPath . "\" . file . "." . A_LoopField) Return tempRomPath . "\" . file . "." . A_LoopField ; return path if file exists } Log("RomNameExistCheck - Looking for rom: " . tempRomPath . "\" . file . "\" . file . "." . A_LoopField,4) If FileExist( tempRomPath . "\" . file . "\" . file . "." . A_LoopField ) { ; check one folder deep of the rom's name in case user keeps each rom in a folder Log("RomNameExistCheck - Found rom: " . tempRomPath . "\" . file . "\" . file . "." . A_LoopField) Return tempRomPath . "\" . file . "\" . file . "." . A_LoopField ; return path if file exists } } } Log("RomNameExistCheck - Could not find """ . file . """ in any of your Rom Paths with any defined Rom Extensions",2) Return } ; Shared romTable function and label for Pause and MG which calculates what roms have multiple discs. Now available on every launch to support some custom uses for loading multiple disks on some older computer systems CreateMGRomTable: Log("CreateMGRomTable - Started") If !mgCandidate { Log("CreateMGRomTable - Ended - This rom does not qualify for MultiGame") Return } If !IsObject(romTable) { Log("CreateMGRomTable - romTable does not exist, creating one for """ . dbName . """",4) romTable := CreateRomTable(dbName) } Else Log("CreateMGRomTable - romTable already exists, skipping table creation.",4) Log("CreateMGRomTable - Ended") Return CreateRomTable(table) { Global romPathFromIni,dbName,romExtensionOrig,sevenZEnabled,romTableStarted,romTableComplete,romTableCanceled,rocketLauncherIsExiting,mgCandidate romTableStarted := 1 romTableCanceled := "" romTableComplete := "" If rocketLauncherIsExiting { romTableCanceled := 1 ; set this so the RomTableCheck is canceled and doesn't get stuck in an infinite loop Log("CreateRomTable - RocketLauncher is currently exiting, skipping romTable creation") Return } If !mgCandidate { romTableCanceled := 1 ; set this so the RomTableCheck is canceled and doesn't get stuck in an infinite loop Log("CreateRomTable - This rom does not qualify for MultiGame") Return } Log("CreateRomTable - Started") romCount := 0 ; initialize the var and reset it, needed in case GUI is used more then once in a session table := [] ; initialize and empty the table typeArray := ["(Disc","(Disk","(Cart","(Tape","(Cassette","(Part","(Side"] regExCheck = i)\s\(Disc\s[^/]*|\s\(Disk\s[^/]*|\s\(Cart\s[^/]*|\s\(Tape\s[^/]*|\s\(Cassette\s[^/]*|\s\(Part\s[^/]*|\s\(Side\s[^/]* dbNamePre := RegExReplace(dbName, regExCheck) ; removes the last set of parentheses if Disc,Tape, etc is in them. A Space must exist before the "(" and after the word Disc or Tape, followed by the number. This is the HS2 standard Loop % typeArray.MaxIndex() ; loop each item in our array { If matchedRom ; Once we matched our game to the typeArray, no need to search for another. This allows the loop to break out. Break indexTotal ++ Log("CreateRomTable - Checking for match: """ . dbName . """ and """ . typeArray[A_Index] . """",4) If dbName contains % typeArray[A_Index] ; find the item in our array that matches our rom { Log("CreateRomTable - """ . dbName . """ contains """ . typeArray[A_Index] . """",4) typeArrayIndex := A_Index Loop, Parse, romPathFromIni, | { indexTotal ++ currentPath := A_LoopField Log("CreateRomTable - Checking New Rom path: " . currentPath,4) Log("CreateRomTable - Now looping in: " . currentPath . "\" . dbNamePre . A_Space . typeArray[typeArrayIndex] . "*",4) Loop, % currentPath . "\" . dbNamePre . A_Space . typeArray[typeArrayIndex] . "*", 1,1 ; we now know to only look for files & folders that have our rom & media type in them. { indexTotal ++ Log(A_LoopFileFullPath,4) Log("CreateRomTable - Looking for: " . currentPath . "\" . dbNamePre . A_Space . typeArray[typeArrayIndex] . "*." . A_LoopFileExt,4) If romExtensionOrig contains % A_LoopFileExt ; Now we narrow down to all matching files using our original extension. Next we use this data to build an array of our files to populate the GUI. { romCount += 1 matchedRom := 1 ; Allows to break out of the loops once we matched our rom table[romCount,1] := A_LoopFileFullPath ; Store A_LoopFileFullPath (full file path and file) in column 1 table[romCount,2] := A_LoopFileName ; Store A_LoopFileName (the full filename and extension) in column 2 table[romCount,3] := RegExReplace(table[romCount, 2], "\..*") ; Store the filename with media type # but w/o an extension in column 3 pos := RegExMatch(table[romCount,2], regExCheck) ; finds position of our multi media type so we can trim away and generate the imageText and check if rom is part of a set. This pulls only the filenames out of the table in column 2. uncleanTxt:= SubStr(table[romCount,2], pos + 1) ; remove everything but the media type and # and ext from our file name table[romCount,4] := dbNamePre ; store dbName w/o the media type and #, used for Pause and updating statistics in column 4 table[romCount,5] := RegExReplace(uncleanTxt, "\(|\)|\..*") ; clean the remainder, removing () and ext, then store it as column 5 in our table to be used for our imageText, this is the media type and # table[romCount,6] := SubStr(table[romCount,5],1,4) ; copies just the media type to column 6 Log("CreateRomTable - Adding found game to Rom Table: " . A_LoopFileFullPath,4) } } } } } romTableComplete := 1 ; flag to tell the RomTableCheck the function is complete in case no romTable was created for non-MG games romTableStarted := "" Log("CreateRomTable - Ended`, " . IndexTotal . " Loops to create table.") Return table } ; Function that gets called in some modules to wait for romTable creation if the module bases some conditionals off whether this table exists or not RomTableCheck() { Global systemName,mgEnabled,pauseEnabled,romTable,romTableStarted,romTableComplete,romTableCanceled,mgCandidate,dbName If mgCandidate { ; && (pauseEnabled = "true" || mgEnabled = "true")) { ; If (!romTableStarted && !IsObject(romTable)) ; romTable := CreateRomTable(dbName) Log("RomTableCheck - Started") ; PauseGlobalIni := A_ScriptDir . "\Settings\Global Pause.ini" ; Pause keys have not been read into memory yet, so they must be read here so RocketLauncher knows whether to run the below loop or not ; PauseSystemIni := A_ScriptDir . "\Settings\" . systemName . "\Pause.ini" ; IniRead, changeDiscMenuG, %PauseGlobalIni%, General Options, ChangeDisc_Menu_Enabled ; IniRead, changeDiscMenuS, %PauseSystemIni%, General Options, ChangeDisc_Menu_Enabled ; changeDiscMenu := If changeDiscMenuS = "use_global" ? changeDiscMenuG : changeDiscMenuS ; calculate to use system or global setting ; If (mgEnabled = "true" || changeDiscMenu = "true") { ; Log("RomTableCheck - MultiGame and/or Pause's Change Disc Menu is enabled so checking if romTable exists yet.",4) If !romTable.MaxIndex() Log("RomTableCheck - romTable does not exist yet, waiting until it does to continue loading the module.",4) Loop { If romTableComplete { ; this var gets created when CreateRomTable is complete in case this is not an MG game Log("RomTableCheck - Detected CreateRomTable is finished processing. Continuing with module thread.",4) Break } Else If romTableCanceled { ; this var gets created when CreateRomTable is cancelled in cases it is no longer needed Log("RomTableCheck - Detected CreateRomTable is no longer needed. Continuing with module thread.",4) Break } Else If (A_Index > 200) { ; if 20 seconds pass by, log there was an issue and continue w/o romTable Log("RomTableCheck - Creating the romTable took longer than 20 seconds. Continuing with module thread without waiting for the table's creation.",3) Break } Else Sleep, 100 } ; } Log("RomTableCheck - Ended") } Else Log("RomTableCheck - This game is not a candidate for MG or Change DIsc menu.") } ; Allows changing LEDBlinky's active profile ; mode can be RL or Rom which tells LEDBlinky what profile to load ; Ledblinky's ini gets loaded on start, so this function will never touch it LEDBlinky(mode) { Global ledblinkyEnabled,ledblinkySystemName,ledblinkyFullPath,ledblinkyProfilePath,ledblinkyRLProfile,dbName,systemName,romName Static ledblinkyExists,ledblinkyExe,ledblinkyPath If (ledblinkyEnabled != "false") { If !ledblinkyExists { ; Make sure LEDBlinky exists first before trying to use it If FileExist(ledblinkyFullPath) { ledblinkyExists := 1 SplitPath,ledblinkyFullPath,ledblinkyExe,ledblinkyPath } Else ScriptError("You are trying to use LEDBlinky support but could not locate it here: " . ledblinkyFullPath) } Log("LEDBlinky - Started, sending mode " . mode) If (mode = "START") Run(ledblinkyExe . " """ . (If romName ? romName : dbName) . """ """ . (If ledblinkySystemName ? ledblinkySystemName : systemName) . """", ledblinkyPath) ; Game Start Event Else If (mode = "END") Run(ledblinkyExe . " 4" , ledblinkyPath) ; Game Stop Event Else If (mode = "RL") Run(ledblinkyExe . " 15 RocketLauncher RocketLauncher", ledblinkyPath) ; Load RocketLauncher profile. "15" Tells ledblinky to skip all game start options and only light the controls. Else If (mode = "ROM") Run(ledblinkyExe . " 15 """ . (If romName ? romName : dbName) . """ """ . (If ledblinkySystemName ? ledblinkySystemName : systemName) . """", ledblinkyPath) ; return to rom profile. If within the module, romName can be used, otherwise default to dbName. Else Log("LEDBlinky - Unsupported use of LEDBlinky - UNKNOWN MODE SUPPLIED: """ . mode . """",3) Log("LEDBlinky - Ended") } } ; Allows changing WinIPAC's and UltraMap's active profile, among any other tools thrown at it that would utilize similar profile structure ; mode = can be START, END, RL or RESUME which represents what part of RL is called the function and what profiles will be loaded ; tool = the name of the tool or folder that will be searched in and checked if that setting is enabled. Ex: WinIPAC or UltraMap ; path = because of how the function supports multiple scenarios, the fullpath to the exe must be provided. Not through making it Global as variables like %prefix%FullPath cannot be made global ; ext = extension of the profiles to look for ; type = used to basically force special modes (only keyboard for now) so the function can support multiple scenarios that all use the same folder structures for profiles KeymapperProfileSelect(mode,tool,path,ext,type="") { Global keyboardEncoderEnabled,ultraMapEnabled,profilePath,keymapperFrontEndProfileName,systemName,emuName,dbName Global keymapperProfiles If !keymapperProfiles keymapperProfiles := {} ; create initial object If (type = "keyboard") prefix := "keyboardEncoder" Else prefix := tool If (%prefix%Enabled = "true") { keymapperProfiles[tool,"Enabled"] := "true" Log(tool . " - Started with mode: " . mode) If !keymapperProfiles[tool,"Exist"] { ; Make sure the tool exists first before trying to use it If FileExist(path) { keymapperProfiles[tool,"Exist"] := 1 keymapperProfiles[tool,"FullPath"] := path SplitPath,path,exe,path keymapperProfiles[tool,"Exe"] := exe keymapperProfiles[tool,"Path"] := path keymapperProfiles[tool,"Ext"] := "." . ext keymapperProfiles[tool,"ProfilePath"] := profilePath . "\" . tool keymapperProfiles[tool,"RLProfile"] := keymapperProfiles[tool,"ProfilePath"] . "\RocketLauncher" keymapperProfiles[tool,"FEProfile"] := keymapperProfiles[tool,"ProfilePath"] . "\" . keymapperFrontEndProfileName keymapperProfiles[tool,"DefaultProfile"] := keymapperProfiles[tool,"ProfilePath"] . "\_Default" keymapperProfiles[tool,"SystemProfile"] := keymapperProfiles[tool,"ProfilePath"] . "\" . systemName keymapperProfiles[tool,"EmuProfile"] := keymapperProfiles[tool,"ProfilePath"] . "\" . emuName keymapperProfiles[tool,"RomProfile"] := keymapperProfiles[tool,"ProfilePath"] . "\" . systemName . "\" . dbName ; msgbox % "fullPath: " . keymapperProfiles[tool,"FullPath"] . "`nexe: " . keymapperProfiles[tool,"Exe"] . "`npath: " . keymapperProfiles[tool,"Path"] . "`nExt: " . keymapperProfiles[tool,"Ext"] . "`nProfilePath: " . keymapperProfiles[tool,"ProfilePath"] . "`nRLProfile: " . keymapperProfiles[tool,"RLProfile"] . "`nFEProfile: " . keymapperProfiles[tool,"FEProfile"] . "`nDefaultProfile: " . keymapperProfiles[tool,"DefaultProfile"] . "`nSystemProfile: " . keymapperProfiles[tool,"SystemProfile"] . "`nEmuProfile: " . keymapperProfiles[tool,"EmuProfile"] . "`nRomProfile: " . keymapperProfiles[tool,"RomProfile"] } Else { Log(tool . " - You have your path set to " . %prefix% . " defined, but it could not be found here: " . path,2) keymapperProfiles[tool,"Enabled"] := "false" } } If (mode = "START") { Log(tool . " - Searching for profiles",4) If FileExist(keymapperProfiles[tool,"RomProfile"] . keymapperProfiles[tool,"Ext"]) { profile := keymapperProfiles[tool,"RomProfile"] . keymapperProfiles[tool,"Ext"] } Else If FileExist(keymapperProfiles[tool,"EmuProfile"] . keymapperProfiles[tool,"Ext"]) { profile := keymapperProfiles[tool,"EmuProfile"] . keymapperProfiles[tool,"Ext"] } Else If FileExist(keymapperProfiles[tool,"SystemProfile"] . keymapperProfiles[tool,"Ext"]) { profile := keymapperProfiles[tool,"SystemProfile"] . keymapperProfiles[tool,"Ext"] } Else If FileExist(keymapperProfiles[tool,"DefaultProfile"] . keymapperProfiles[tool,"Ext"]) { profile := keymapperProfiles[tool,"DefaultProfile"] . keymapperProfiles[tool,"Ext"] } Else { Log(tool . " - No profiles found",4) Return } keymapperProfiles[tool,"LastProfile"] := profile } Else If (mode = "END") { Log(tool . " - Searching for your Front End profile",4) If FileExist(keymapperProfiles[tool,"FEProfile"] . keymapperProfiles[tool,"Ext"]) { profile := keymapperProfiles[tool,"FEProfile"] . keymapperProfiles[tool,"Ext"] } Else { Log(tool . " - Profile not found",4) Return } } Else If (mode = "RL") { Log(tool . " - Searching for your RocketLauncher profile",4) If FileExist(keymapperProfiles[tool,"RLProfile"] . keymapperProfiles[tool,"Ext"]) { profile := keymapperProfiles[tool,"RLProfile"] . keymapperProfiles[tool,"Ext"] } Else { Log(tool . " - Profile not found",4) Return } } Else If (mode = "RESUME") { Log(tool . " - Restoring to your last profile",4) If keymapperProfiles[tool,"LastProfile"] ; only restore to a previous profile if one was found on start profile := keymapperProfiles[tool,"LastProfile"] Else { Log(tool . " - A profile was not loaded on start, skipping any profile loading",4) Return } } Else Log(tool . " - Unsupported mode: " . mode,2) Log(tool . " - Loading found profile: " . profile,4) Run, % keymapperProfiles[tool,"Exe"] . " " . profile . (If tool = "UltraMap" ? "/logerrors " . keymapperProfiles[tool,"Path"] . "\UltraMapLog.log" : ""), % keymapperProfiles[tool,"Path"] ; If there was a problem loading a profile, WinIPAC will pop up with a box saying so with this title/class: "WinIPAC - Downloading ahk_class ThunderRT6FormDC". It does not return any error codes unfortunately. UltraMap requires errors to be logged otherwise it pops up with an error dialog. Log(tool . " - Ended") } } ; Function to measure the size of an text MeasureText(Text,Options,Font="Arial",Width="", Height="", ReturnMode="W", ByRef H="", ByRef W="", ByRef X="", ByRef Y="", ByRef Chars="", ByRef Lines=""){ hdc_MeasureText := GetDC("MeasureText_hwnd") G_MeasureText := Gdip_GraphicsFromHDC(hdc_MeasureText) RECTF_STR := Gdip_TextToGraphics(G_MeasureText, Text, Options, Font, Width, Height, 1) StringSplit,RCI,RECTF_STR, | W := Ceil(RCI3) H := Ceil(RCI4) X := Ceil(RCI1) Y := Ceil(RCI2) Chars := Ceil(RCI5) Lines := Ceil(RCI6) DeleteDC(hdc_MeasureText), Gdip_DeleteGraphics(G_MeasureText) Return (ReturnMode="X") ? X : (ReturnMode="Y") ? Y :(ReturnMode="W") ? W :(ReturnMode="H") ? H : (ReturnMode="Chars") ? Chars : Lines } ; Function that allows making applications transparent so they can be hidden completely w/o moving them FadeApp(title,direction,time=0){ startTime := A_TickCount Loop{ t := ((TimeElapsed := A_TickCount-startTime) < time) ? (If direction="in" ? 255*(timeElapsed/time) : 255*(1-(timeElapsed/time))) : (If direction="in" ? 255 : 0) WinSet, Transparent, %t%, %title% If (direction = "in" && t >= 255) or (direction = "out" && t <= 0) { If (direction = "in") WinSet, Transparent, Off, %title% Break } } Log("HideFE - " . (If direction = "out" ? "Hiding Frontend by making it transparent" : "Showing Frontend and removing transparency")) } ; SplitPath function with support for roms that contain multiple periods in their name. AHK SplitPath does not support this. SplitPath(in,Byref outFileName,Byref outPath,Byref outExt,Byref outNameNoExt) { regx := "(\\{2}|(^[\w]:\\))([\w].+\w\\)" ; return path on regexmath and file on regexreplace regext := "((\.[^.\s]+)+)$" ; return extension with period (match literal period, match one or more at beginning any character and white space, one or more of all previous, and entire match must appear at end) in := RegExReplace(in,"/","\") ; replace all occurences of / with \ RegExMatch(in, regx, outPathBSlash) ; path with backslash pathLen := StrLen(outPathBSlash) ; get length of path with slash outPath := SubStr(outPathBSlash, 1, pathLen - 1) ; grab path w/o slash RegExMatch(in, regext, outExtP) ; get ext with period outExt := SubStr(outExtP, 2) ; get ext and remove period outFileName := RegExReplace(in, regx) ; get name with ext nameLen := StrLen(outFileName) ; get length of name extLen := StrLen(outExt) ; get length of ext outNameNoExt := SubStr(outFileName, 1, nameLen - extLen - 1) ; get name w/o ext } ; This function converts a relative path to absolute GetFullName( fn ) { ; http://msdn.microsoft.com/en-us/library/Aa364963 Static buf ;, i ; removing i from static because it needs to be reset from one call to the next ; If !i i := VarSetCapacity(buf, 512) DllCall("GetFullPathNameA", "str", fn, "uint", 512, "str", buf, "str*", 0) Return buf } ; Converts a relative path to an absolute one after providing the base path AbsoluteFromRelative(MasterPath, RelativePath) { VarSetCapacity(AbsP,260,0) DllCall( "shlwapi\PathCombineA", Str,AbsP, Str,MasterPath, Str,RelativePath ) Return AbsP } ; FileGetVersionInfo_AW which gets file attributes FileGetVersionInfo_AW( peFile="", StringFileInfo="", Delimiter="|") { Static CS, HexVal, Sps=" ", DLL="Version\" If ( CS = "" ) CS := A_IsUnicode ? "W" : "A", HexVal := "msvcrt\s" (A_IsUnicode ? "w": "" ) "printf" If ! FSz := DllCall( DLL "GetFileVersionInfoSize" CS , Str,peFile, UInt,0 ) Return "", DllCall( "SetLastError", UInt,1 ) VarSetCapacity( FVI, FSz, 0 ), VarSetCapacity( Trans,8 * ( A_IsUnicode ? 2 : 1 ) ) DllCall( DLL "GetFileVersionInfo" CS, Str,peFile, Int,0, UInt,FSz, UInt,&FVI ) If ! DllCall( DLL "VerQueryValue" CS, UInt,&FVI, Str,"\VarFileInfo\Translation", UIntP,Translation, UInt,0 ) Return "", DllCall( "SetLastError", UInt,2 ) If ! DllCall( HexVal, Str,Trans, Str,"%08X", UInt,NumGet(Translation+0) ) Return "", DllCall( "SetLastError", UInt,3 ) Loop, Parse, StringFileInfo, %Delimiter% { subBlock := "\StringFileInfo\" SubStr(Trans,-3) SubStr(Trans,1,4) "\" A_LoopField If ! DllCall( DLL "VerQueryValue" CS, UInt,&FVI, Str,SubBlock, UIntP,InfoPtr, UInt,0 ) Continue Value := DllCall( "MulDiv", UInt,InfoPtr, Int,1, Int,1, "Str" ) Info .= Value ? ( ( InStr( StringFileInfo,Delimiter ) ? SubStr( A_LoopField Sps,1,24 ) . A_Tab : "" ) . Value . Delimiter ) : "" } StringTrimRight, Info, Info, 1 Return Info } GetOSVersion() { VarSetCapacity(v,148), NumPut(148,v) DllCall("GetVersionEx", "uint", &v) ; Return formatted version string similar to A_AhkVersion. ; Assume build number will never be more than 4 characters. return NumGet(v,4) ; major . "." NumGet(v,8) ; minor . "." SubStr("0000" NumGet(v,12), -3) ; build } ; Returns system paths ; For example, GetCommonPath("LOCAL_APPDATA") will return the full path to yout local appdata folder: C:\Users\NAME\AppData\Local GetCommonPath(csidl) { Static init If !init { CSIDL_APPDATA := 0x001A ; Application Data, new for NT4 CSIDL_COMMON_APPDATA := 0x0023 ; All Users\Application Data CSIDL_COMMON_DOCUMENTS := 0x002e ; All Users\Documents CSIDL_DESKTOP := 0x0010 ; C:\Documents and Settings\username\Desktop CSIDL_FONTS := 0x0014 ; C:\Windows\Fonts CSIDL_LOCAL_APPDATA := 0x001C ; non roaming, user\Local Settings\Application Data CSIDL_MYMUSIC := 0x000d ; "My Music" folder CSIDL_MYPICTURES := 0x0027 ; My Pictures, new for Win2K CSIDL_PERSONAL := 0x0005 ; My Documents CSIDL_PROGRAM_FILES_COMMON := 0x002b ; C:\Program Files\Common CSIDL_PROGRAM_FILES := 0x0026 ; C:\Program Files CSIDL_PROGRAMS := 0x0002 ; C:\Documents and Settings\username\Start Menu\Programs CSIDL_RESOURCES := 0x0038 ; %windir%\Resources\, For theme and other windows resources. CSIDL_STARTMENU := 0x000b ; C:\Documents and Settings\username\Start Menu CSIDL_STARTUP := 0x0007 ; C:\Documents and Settings\username\Start Menu\Programs\Startup. CSIDL_SYSTEM := 0x0025 ; GetSystemDirectory() CSIDL_WINDOWS := 0x0024 ; GetWindowsDirectory() } val := CSIDL_%csidl% VarSetCapacity(fpath, 256) DllCall("shell32\SHGetFolderPathA", "uint", 0, "int", val, "uint", 0, "int", 0, "str", fpath) Return %fpath% } ; StrX function because some modules use it and Pause needs it for XML reading StrX( H, BS="",BO=0,BT=1, ES="",EO=0,ET=1, ByRef N="" ) { Return SubStr(H,P:=(((Z:=StrLen(ES))+(X:=StrLen(H))+StrLen(BS)-Z-X)?((T:=InStr(H,BS,0,((BO <0)?(1):(BO))))?(T+BT):(X+1)):(1)),(N:=P+((Z)?((T:=InStr(H,ES,0,((EO)?(P+1):(0))))?(T-P+Z +(0-ET)):(X+P)):(X)))-P) } HexCompareWrite(file,Pos,Value){ If (value = "") { Log("HexCompareWrite - NULL value supplied",2) Return } curBin := BinRead(file,nvramData,1,Pos) ; read binary Bin2Hex(curHex,nvramData,curBin) ; convert to hex If (curHex != value) { Log("HexCompareWrite - Changing " . curHex . " to " . value . " in: " . file,4) Hex2Bin(binData,value) BinWrite(file,binData,1,Pos) } } i18n(key, defaultLocale = "English_United_States", p0 = "-0", p1 = "-0", p2 = "-0", p3 = "-0", p4 = "-0", p5 = "-0", p6 = "-0", p7 = "-0", p8 = "-0", p9 = "-0") { Global sysLang,langFile Log("i18n - Started",4) IniRead, phrase, %langFile%, %sysLang%, %key% If (phrase = "ERROR" || phrase = "") { ; Nothing found, test with generic language StringSplit, keyArray, sysLang, _ Log("i18n - Section """ . sysLang . """ & key """ . key . """ not found, trying section """ . keyArray1 . """",4) IniRead, phrase, %langFile% , %keyArray1%, %key% If (phrase = "ERROR" || phrase = "") { Log("i18n - Section """ . keyArray1 . """ & key """ . key . """ not found, trying section """ . defaultLocale . """",4) ; Nothing found, test with default locale if one is provided If (defaultLocale != "") { IniRead, phrase, %langFile% , %defaultLocale%, %key% If (phrase = "ERROR" || phrase = "") { ; Nothing found, test with generic language for default locale as well StringSplit, keyArray, defaultLocale, _ Log("i18n - Section """ . defaultLocale . """ & key """ . key . """ not found, trying section """ . keyArray1 . """",4) IniRead, phrase, %langFile% , %keyArray1%, %key% } } ; Nothing found return original value If (defaultLocale = "" || phrase = "ERROR" || phrase = "") { Log("i18n - Ended, no phrase found for """ . key . """ in language """ . sysLang . """. Using default """ . key . """",2) Return % key } } } StringReplace, phrase, phrase, `\n, `r`n, ALL StringReplace, phrase, phrase, `\t, % A_Tab, ALL Loop 10 { idx := A_Index - 1 IfNotEqual, p%idx%, -0 phrase := RegExReplace(phrase, "\{" . idx . "\}", p%idx%) } Log("i18n - Ended, using """ . phrase . """ for """ . key . """",4) Return % phrase } ; Debug console handler DebugMessage(str) { Global rlTitle,rlVersion,rlDebugConsoleStdout If !rlDebugConsoleStdout DebugConsoleInitialize(rlDebugConsoleStdout, rlTitle . " v" . rlVersion . " Debug Console") ; start console window if not yet started str .= "`n" ; add line feed FileAppend %str%, CONOUT$ ; FileAppend %str%`n, * ; Works with SciTE and similar editors. ; OutputDebug %str%`n ; Works with Visual Studio and DbgView. WinSet, Bottom,, ahk_id %rlDebugConsoleStdout% ; keep console on bottom } DebugConsoleInitialize(ByRef handle, title="") { ; two calls to open, no error check (it's debug, so you know what you are doing) DllCall("AttachConsole", int, -1, int) DllCall("AllocConsole", int) DllCall("SetConsoleTitle", "str", (If title ? title : a_scriptname)) ; Set the title handle := DllCall("GetStdHandle", "int", -11) ; get the handle WinSet, Bottom,, ahk_id %handle% ; make sure it's on the bottom Return } ;Sends a command to the active window using AHK key names. It will always send down/up keypresses for better compatibility ;A special command {Wait} can be used to force a sleep of the time defined by WaitTime ;WaitCommandOffset will affect all Wait events passed in the Command string by this amount SendCommand(Command, SendCommandDelay=2000, WaitTime=500, WaitBetweenSends=0, Delay=50, PressDuration=-1, WaitCommandOffset=0) { Log("SendCommand - Started") Log("SendCommand - Command: " . Command . "`r`n`t`t`t`t`tSendCommandDelay: " . SendCommandDelay . "`r`n`t`t`t`t`tWaitTime: " . WaitTime . "`r`n`t`t`t`t`tWaitBetweenSends: " . WaitBetweenSends . "`r`n`t`t`t`t`tDelay: " . Delay . "`r`n`t`t`t`t`tPressDuration: " . PressDuration . "`r`n`t`t`t`t`tWaitCommandOffset: " . WaitCommandOffset,4) ArrayCount := 0 ;Keeps track of how many items are in the array. InsideBrackets := 0 ;If 1 it means the current array item starts with { SavedKeyDelay := A_KeyDelay ;Saving previous key delay and setting the new one SetKeyDelay, %Delay%, %PressDuration% Sleep, %SendCommandDelay% ;Wait before starting to send any command ;Create an array with each command as an array element Loop, % StrLen(Command) { StrValue := SubStr(Command,A_Index,1) ; { StringMid, StrValue, Command, A_Index, 1 If (StrValue != A_Space || InsideBrackets = 1) ; Spaces must be allowed when inside brackets so we can issue {Shift Down} for instance { If (InsideBrackets = 0) ArrayCount += 1 If (StrValue = "{") { If (InsideBrackets = 1) ScriptError("Non-Matching brackets detected in the SendCommand parameter, please correct it") Else InsideBrackets := 1 } Else If (StrValue = "}") { If (InsideBrackets = 0) ScriptError("Non-Matching brackets detected in the SendCommand parameter, please correct it") Else InsideBrackets := 0 } Array%ArrayCount% := Array%ArrayCount% . StrValue ;Update the array data } } ;Loop through the array and send the commands Loop %ArrayCount% { element := Array%A_Index% If (WaitBetweenSends = 1) Sleep, %WaitTime% ;Particular cases check if the commands already come with down or up suffixes on them and if so send the commands directly without appending Up/Down If RegExMatch(element,"i)Down}") { If (element != "{Down}") { Send, %element% continue } } Else If RegExMatch(element,"i)Up}") { If (element != "{Up}") { Send, %element% Continue } } Else If (element = "{Wait}") ;Special non-ahk tag to issue a sleep { NewWaitTime := WaitTime + WaitCommandOffset Sleep, %NewWaitTime% Continue } Else If RegExMatch(element,"i)\{Wait:") { ;Wait for a specified amount of time {Wait:xxx} ; StringMid, NewWaitTime, element, 7, StrLen(element) - 7 NewWaitTime := SubStr(element,7,StrLen(element) - 7) NewWaitTime := NewWaitTime + WaitCommandOffset Sleep, %NewWaitTime% Continue } ;the rest of the commands, send a keypress with down and up suffixes If RegExMatch(element,"}") { StrElement := SubStr(element,1,StrLen(element) - 1) ; { StringLeft, StrElement, element, StrLen(element) - 1 Send, %StrElement% down}%StrElement% up} } Else Send, {%element% down}{%element% up} } ;Restore key delay values SetKeyDelay(SavedKeyDelay, -1) Log("SendCommand - Ended") } ; Purpose: Tell a ServoStik to transition to 4 or 8-way mode ; Parameters: ; direction = Can be 4 or 8, self-explanatory ServoStik(direction) { Log("ServoStik - Started") Global PacDriveDllFile,servoStikEnabled Static dllExists If !RegExMatch(direction,"4|8") { Log("ServoStik - """ . direction . """ is not a supported direction for ServoSticks. Only 4 and 8 are supported. Leaving your ServoStik as is.",2) Return } If !dllExists { CheckFile(pacDrivedllFile, "Following file is required for RocketLauncher ServoStik support, but could not be found:`n" . pacDrivedllFile) dllExists := 1 ; do not run this check again } pacDriveLoadModule := DllCall("LoadLibrary", "Str", PacDriveDllFile) ; Avoids the need for ahk to load and free the dll's library multiple times pacInitialize := DllCall(PacDriveDllFile . "\PacInitialize") ; Initialize all PacDrive, PacLED64 and U-HID Devices and return the amount connected to system If !pacInitialize { Log("ServoStik - No devices found on system",2) Log("ServoStik - Ended") Return } Else Log("ServoStik - " . pacInitialize . " devices found on system. If you have multiple devices, this should list more than one and may not specifically mean a ServoStik was found") result := DllCall(PacDriveDllFile . "\PacSetServoStik" . direction . "Way") ; Tell ServoStiks to change to desired direction If !result Log("ServoStik - There was a problem telling your ServoStik(s) to go " . direction . "-Way",3) Else Log("ServoStik - ServoStik(s) were told to go " . direction . "-Way") ; pacDriveUnloadModule := DllCall("FreeLibrary", "UInt", pacDriveLoadModule) ; To conserve memory, the DLL is unloaded after using it. Log("ServoStik - Ended") } GetTimeString(time) { If (time<0) Return time If time is not number Return time Days := time // 86400 Hours := Mod(time, 86400) // 3600 Minutes := Mod(time, 3600) // 60 Seconds := Mod(time, 60) If (Days<>0) { If Strlen(Hours) = 1 Hours = 0%Hours% If Strlen(Minutes) = 1 Minutes = 0%Minutes% If Strlen(Seconds) = 1 Seconds = 0%Seconds% TimeString = %Days%d %Hours%h %Minutes%m %Seconds%s } Else If (Hours<>0) { If Strlen(Minutes) = 1 Minutes = 0%Minutes% If Strlen(Seconds) = 1 Seconds = 0%Seconds% TimeString = %Hours%h %Minutes%m %Seconds%s } Else If (Minutes<>0) { If Strlen(Seconds) = 1 Seconds = 0%Seconds% TimeString = %Minutes%m %Seconds%s } Else If (Seconds<>0) TimeString = %Seconds%s Else TimeString := "" Return TimeString } ReplaceFileNameInvalidChar(ByRef hastack,list,replaceChar){ Loop, Parse, list, `, StringReplace, hastack, hastack, %a_loopfield%, %replaceChar%, All Return hastack } ;------------------------------------------------------------------------------------------------------------- ;-------------------------------------------- RL Media Functions --------------------------------------------- ;------------------------------------------------------------------------------------------------------------- rndRLMediaLogoPath(assetType){ Global RLMedia, feMedia, gameInfo If !(RLMedia) RLMedia := loadRLMediaLogos(gameInfo) LogoImageList := [] for index, element in RLMedia["Logos"] If (element.Label) If (element.AssetType=assetType) Loop, % element.TotalItems LogoImageList.Insert(element["Path" . a_index]) If !(LogoImageList[1]) for index, element in feMedia["Logos"] If (element.Label) If (element.AssetType=assetType) Loop, % element.TotalItems LogoImageList.Insert(element["Path" . a_index]) If (LogoImageList[1]) { Random, RndmLogoImage, 1, % LogoImageList.MaxIndex() Return LogoImageList[RndmLogoImage] } } LoadRLMediaLogos(gameInfoObj){ Global RLMediaPath, RLMedia, systemname, dbname, romTable, mgCandidate RLMedia := {} LogoList := "Genre|Rating|Developer|Publisher|Year" Loop, Parse, LogoList, | { If (gameInfoObj[A_LoopField].Value){ %A_LoopField% := gameInfoObj[A_LoopField].Value If (A_LoopField = "Genre"){ ReplaceFileNameInvalidChar(%A_LoopField%,"/,|","\") ;Replacing invalid file name characters by folder separator ("\") ReplaceFileNameInvalidChar(%A_LoopField%,"*","-") ;Replacing invalid file name characters by hippens ("-") } Else ReplaceFileNameInvalidChar(%A_LoopField%,"/,*,|","-") ;Replacing invalid file name characters by hippens ("-") ReplaceFileNameInvalidChar(%A_LoopField%,":"," - ") ;Replacing invalid file name characters by hippens ("-") ReplaceFileNameInvalidChar(%A_LoopField%,"?," . """" . ",<,>","") ;Replacing invalid file name characters by hippens ("-") %A_LoopField% :=RegExReplace(%A_LoopField%,"^\s+|\s+(?=\s)|\s$") ;remove double white spaces If (FileExist(RLMediaPath . "\" . A_LoopField . "\" . systemname . "\" . dbname . "\" . %A_LoopField% . ".*")) %A_LoopField%Path := RLMediaPath . "\" . A_LoopField . "\" . systemname . "\" . dbname . "\" . %A_LoopField% . ".*" Else If (FileExist(RLMediaPath . "\" . A_LoopField . "\" . systemname . "\_Default\" . %A_LoopField% . ".*")) %A_LoopField%Path := RLMediaPath . "\" . A_LoopField . "\" . systemname . "\_Default\" . %A_LoopField% . ".*" Else %A_LoopField%Path := RLMediaPath . "\" . A_LoopField . "\_Default\" . %A_LoopField% . ".*" } } RLMedia.Logos := BuildAssetsTable(GenrePath . "|" . RatingPath . "|" . DeveloperPath . "|" . PublisherPath . "|" . YearPath,"Genre Logo|Rating Logo|Developer Logo|Publisher Logo|Year Logo","genre|rating|developer|publisher|year","png|bmp|gif|jpg|tif") systemLogoPath := RLMediaPath . "\Logos\" . systemname . "\_Default\" gameLogoPath1 := RLMediaPath . "\Logos\" . systemname . "\" . dbname . "\" ;Description name without (Disc X) If (!romTable && mgCandidate) romTable := CreateRomTable(dbName) Totaldiscsofcurrentgame := romTable.MaxIndex() If (Totaldiscsofcurrentgame > 1) { DescriptionNameWithoutDisc := romTable[1,4] gameLogoPath2 := RLMediaPath . "\Logos\" . systemname . "\" . DescriptionNameWithoutDisc . "\" } RLMedia.Logos := BuildAssetsTable(systemLogoPath . "|" . gameLogoPath1 . "|" . gameLogoPath2,"System Logo|Game Logo|Game Logo","system|game|game","png|bmp|gif|jpg|tif",RLMedia.Logos) Return RLMedia } ; Inject a shared function for Pause and Fade which adjusts the background image positioning ; Usage, params 1-4 are byref so supply the var you want to be filled with the calculated positions and size. Next 2 are the original pics width and height. Last is the position the user wants. GetBGPicPosition(ByRef retX,ByRef retY,ByRef retW,ByRef retH,w,h,pos){ Global baseScreenWidth, baseScreenHeight widthMaxPercent := ( baseScreenWidth / w ) ; get the percentage needed to maximumise the image so it reaches the screen's width heightMaxPercent := ( baseScreenHeight / h ) If (pos = "Stretch and Lose Aspect") { ; image is stretched to screen, loosing aspect retW := baseScreenWidth retH := baseScreenHeight retX := 0 retY := 0 } Else If (pos = "Stretch and Keep Aspect") { ; image is stretched to Center screen, keeping aspect percentToEnlarge := If (widthMaxPercent < heightMaxPercent) ? widthMaxPercent : heightMaxPercent ; this basicallys says if the width's max reaches the screen's width first, use the width's percentage instead of the height's retW := Round(w * percentToEnlarge) ; multiply width by the percentage from above to reach as close to the edge as possible retH := Round(h * percentToEnlarge) ; multiply height by the percentage from above to reach as close to the edge as possible retX := ( baseScreenWidth / 2 ) - ( retW / 2 ) ; find where to place the X of the image retY := ( baseScreenHeight / 2 ) - ( retH / 2 ) ; find where to place the Y of the image } Else If (pos = "Center Width") { ; image is stretched to Center screen's width, keeping aspect percentToEnlarge := widthMaxPercent ; increase the image size by the percentage it takes to reaches the screen's width, cropping may occur on top and bottom retW := Round(w * percentToEnlarge) ; multiply width by the percentage from above to reach as close to the edge as possible retH := Round(h * percentToEnlarge) ; multiply height by the percentage from above to reach as close to the edge as possible retX := ( baseScreenWidth / 2 ) - ( retW / 2 ) ; find where to place the X of the image retY := ( baseScreenHeight / 2 ) - ( retH / 2 ) ; find where to place the Y of the image } Else If (pos = "Center Height") { ; image is stretched to Center screen's height, keeping aspect percentToEnlarge := heightMaxPercent ; increase the image size by the percentage it takes to reaches the screen's height, cropping may occur on left and right retW := Round(w * percentToEnlarge) ; multiply width by the percentage from above to reach as close to the edge as possible retH := Round(h * percentToEnlarge) ; multiply height by the percentage from above to reach as close to the edge as possible retX := ( baseScreenWidth / 2 ) - ( retW / 2 ) ; find where to place the X of the image retY := ( baseScreenHeight / 2 ) - ( retH / 2 ) ; find where to place the Y of the image } Else If (pos = "Center") { ; original image size and aspect retX := ( baseScreenWidth / 2 ) - ( w / 2 ) ; find where to place the X of the image retY := ( baseScreenHeight / 2 ) - ( h / 2 ) ; find where to place the Y of the image } Else If (pos = "Align to Bottom Left") { ; place the pic so the bottom left corner matches the screen's bottom left corner retH := baseScreenHeight retW := Round( w / ( h / baseScreenHeight )) If ( retW < baseScreenWidth ){ retW := baseScreenWidth retH := Round( h / ( w / retW )) } retY := baseScreenHeight - retH } Else If (pos = "Align to Bottom Right") { ; place the pic so the bottom right corner matches the screen's bottom right corner retH := baseScreenHeight retW := Round( w / ( h / baseScreenHeight )) If ( retW < baseScreenWidth ){ retW := baseScreenWidth retH := Round( h / ( w / retW )) } retX := baseScreenWidth - retW retY := baseScreenHeight - retH } Else If (pos = "Align to Top Right") { ; place the pic so the top right corner matches the screen's top right corner retH := baseScreenHeight retW := Round( w / ( h / baseScreenHeight )) If ( retW < baseScreenWidth ){ retW := baseScreenWidth retH := Round( h / ( w / retW )) } retX := baseScreenWidth - retW } Else { ; place the pic so the top left corner matches the screen's top left corner, also the default retH := baseScreenHeight retW := Round( w / ( h / baseScreenHeight )) If ( retW < baseScreenWidth ){ retW := baseScreenWidth retH := Round( h / ( w / retW )) } } } ; Usage, params 1&2 are byref so supply the var you want to be filled with the calculated positions. Next 4 are the original pics xy,w,h. Last is the position the user wants. GetFadePicPosition(ByRef retX, ByRef retY,x,y,w,h,pos){ Global baseScreenWidth, baseScreenHeight If (pos = "Stretch and Lose Aspect"){ ; image is stretched to screen, loosing aspect retX := 0 retY := 0 } Else If (pos = "Stretch and Keep Aspect") { ; image is stretched to screen, keeping aspect retX := round(( baseScreenWidth / 2 ) - ( w / 2 )) retY := round(( baseScreenHeight / 2 ) - ( h / 2 )) } Else If (pos = "Center") { retX := round(( baseScreenWidth / 2 ) - ( w / 2 )) retY := round(( baseScreenHeight / 2 ) - ( h / 2 )) } Else If (pos = "Top Left Corner") { retX := 0 retY := 0 } Else If (pos = "Top Right Corner") { retX := baseScreenWidth - w retY := 0 } Else If (pos = "Bottom Left Corner") { retX := 0 retY := baseScreenHeight - h } Else If (pos = "Bottom Right Corner") { retX := baseScreenWidth - w retY := baseScreenHeight - h } Else If (pos = "Top Center") { retX := round(( baseScreenWidth / 2 ) - ( w / 2 )) retY := 0 } Else If (pos = "Bottom Center") { retX := round(( baseScreenWidth / 2 ) - ( w / 2 )) retY := baseScreenHeight - h } Else If (pos = "Left Center") { retX := 0 retY := round(( baseScreenHeight / 2 ) - ( h / 2 )) } Else If (pos = "Right Center") { retX := baseScreenWidth - w retY := round(( baseScreenHeight / 2 ) - ( h / 2 )) } Else { retX := x retY := y } } GetRLMediaFiles(mediaType,supportedFileTypes,returnArray=0) { Log("GetRLMediaFiles - Started",4) Global RLMediaPath,dbName,systemName,romTable,mgCandidate If (!romTable && mgCandidate) romTable:=CreateRomTable(dbName) DescriptionNameWithoutDisc := romTable[1,4] romFolder := RLMediaPath . "\" . mediaType . "\" . systemName . "\" . dbName . "\" romDisckLessFolder := RLMediaPath . "\" . mediaType . "\" . systemName . "\" . DescriptionNameWithoutDisc . "\" systemFolder := RLMediaPath . "\" . mediaType . "\" . systemName . "\_Default\" globalFolder := RLMediaPath . "\" . mediaType . "\_Default\" imagesArray := [] Loop, Parse, supportedFileTypes, | If FileExist(romFolder . "*." . A_LoopField) { Loop % romFolder . "*." . A_LoopField imagesArray[A_Index] := A_LoopFileFullPath } If imagesArray.MaxIndex() <= 0 Loop, Parse, supportedFileTypes, | If FileExist(romDisckLessFolder . "*." . A_LoopField) { Loop % romDisckLessFolder . "*." . A_LoopField imagesArray[A_Index] := A_LoopFileFullPath } If imagesArray.MaxIndex() <= 0 Loop, Parse, supportedFileTypes, | If FileExist(systemFolder . "*." . A_LoopField) { Loop % systemFolder . "*." . A_LoopField imagesArray[A_Index] := A_LoopFileFullPath } If imagesArray.MaxIndex() <= 0 Loop, Parse, supportedFileTypes, | If FileExist(globalFolder . "*." . A_LoopField) { Loop % globalFolder . "*." . A_LoopField imagesArray[A_Index] := A_LoopFileFullPath } If returnArray { Log("GetRLMediaFiles - Ended, returning array",4) Return imagesArray } Else { Random, RndmImagePic, 1, % imagesArray.MaxIndex() file := imagesArray[RndmImagePic] Log("GetRLMediaFiles - Ended, randomized RocketLauncher " . mediaType . " file selected: " . file) Return file } } ;------------------------------------------------------------------------------------------------------------- ;----------------------------------------- DXWnd Functions ----------------------------------------- ;------------------------------------------------------------------------------------------------------------- ; If you provide a value, DxwndIniRW assumes you want to write to the ini ; If no value is provided, DxwndIniRW assumes you want to read from the ini and returns the value ; DxwndIniRW is only used to read and write settings to dxwnd.ini ; DxwndRun is for launching dxwnd ; DxwndClose is for closing dxwnd ; DxwndUpdateIniPath is for updating the dxwndIni variable DxwndIniRW(sec="",key="",val="", default="", cTarget="") { Log("DxwndIniRW - Started") Global dxwndIni,romName Static pos,iniExists If !iniExists { CheckFile(dxwndIni) iniExists := 1 ; do not run this check again } If !pos { ; the current romName or cTarget position has not been found, loop through the ini to find it first targetGame := If cTarget ? cTarget : romName Loop { pos := a_index-1 IniRead, dxwndName, %dxwndIni%, target, title%pos% If (dxwndName = targetGame) Break If (dxwndName = "ERROR") ScriptError("There was a problem finding """ . targetGame . """ in the DXWnd Ini. Please make sure you have added this game to DXWnd before attempting to launch DXWnd through it.") } } errLvl := Process("Exist", "dxwnd.exe") ; Make sure dxwnd is not running first so settings don't get reverted If errLvl { DxwndClose() Process("WaitClose", "dxwnd.exe") } If val { IniWrite, %val%, %dxwndIni%, %sec%, %key%%pos% Log("DxwndIniRW - Wrote """ . val . """ to game #" . pos,4) } Else { IniRead, val, %dxwndIni%, %sec%, %key%%pos% Log("DxwndIniRW - Read """ . val . """",4) Log("DxwndIniRW - Ended") Return val } Log("DxwndIniRW - Ended") } DxwndRun(ByRef outPID="") { Log("DxwndRun - Started") Global dxwndFullPath,dxwndExe,dxwndPath,dxwndIni Static exeExists If !exeExists { CheckFile(dxwndFullPath, "Following file is required for DXWnd support, but its file could not be found:`n" . dxwndFullPath) exeExists := 1 ; do not run this check again } If !dxwndExe SplitPath, dxwndFullPath, dxwndExe, dxwndPath SplitPath, dxwndIni, dxwndIniFile Run(dxwndExe . " /T /C:""" . dxwndIniFile . """", dxwndPath, "Min", outPID) errLvl := Process("Wait", dxwndExe, 10) ; waiting 10 seconds for dxwnd to start If (errLvl = "") ScriptError("DXWnd did not start after waiting for 10 seconds. Please check you can run it manually and try again.") Else Log("DxwndRun - DxwndRun is now running") Log("DxwndRun - Ended") } DxwndClose() { Log("DxwndClose - Started") Global dxwndFullPath,dxwndExe,dxwndPath If !dxwndExe SplitPath, dxwndFullPath, dxwndExe, dxwndPath PostMessage, 0x111, 32810,,,ahk_exe %dxwndExe% ; this tells dxwnd to close itself Process("WaitClose", dxwndExe, 1) ; waits 1 second for dxwnd to close errLvl := Process("Exist", dxwndExe) ; checks if dxwnd is still running If errLvl Process("Close", dxwndExe) ; only needed when RocketLauncher is not ran as admin or RocketLauncher cannot close dxwnd for some reason Log("DxwndClose - Ended") } DxwndUpdateIniPath() { Global dxwndFullPath,dxwndPath,dxwndIni,systemName SplitPath,dxwndFullPath,,dxwndPath If FileExist( dxwndPath . "\" . systemName . ".ini" ) dxwndIni := dxwndPath . "\" . systemName . ".ini" Else dxwndIni := dxwndPath . "\dxwnd.ini" Log("DxwndUpdateIniPath - DxwndIni set to " . dxwndIni) } ;------------------------------------------------------------------------------------------------------------- ;----------------------------------- Cursor Control Functions ------------------------------------ ;------------------------------------------------------------------------------------------------------------- ToggleCursor: Log("ToggleCursor - Hotkey """ . toggleCursorKey . """ pressed, toggling cursor visibility") SystemCursor("Toggle") Return ; Function to hide/unhide the mouse cursor SystemCursor(OnOff=1) ; INIT = "I","Init"; OFF = 0,"Off"; TOGGLE = -1,"T","Toggle"; ON = others { Global mouseCursorHidden Static AndMask, XorMask, cursor, h_cursor ,c0,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11,c12,c13 ; system cursors , b1,b2,b3,b4,b5,b6,b7,b8,b9,b10,b11,b12,b13 ; blank cursors , h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11,h12,h13 ; handles of default cursors If (OnOff = "Init" or OnOff = "I" or cursor = "") ; init when requested or at first call { cursor := "h" ; active default cursors VarSetCapacity( h_cursor,4444, 1 ) VarSetCapacity( AndMask, 32*4, 0xFF ) VarSetCapacity( XorMask, 32*4, 0 ) system_cursors = 32512,32513,32514,32515,32516,32642,32643,32644,32645,32646,32648,32649,32650 StringSplit c, system_cursors, `, Loop %c0% { h_cursor := DllCall( "LoadCursor", "uint",0, "uint",c%A_Index% ) h%A_Index% := DllCall( "CopyImage", "uint",h_cursor, "uint",2, "int",0, "int",0, "uint",0 ) b%A_Index% := DllCall("CreateCursor","uint",0, "int",0, "int",0 , "int",32, "int",32, "uint",&AndMask, "uint",&XorMask ) } } If (OnOff = 0 or OnOff = "Off" or cursor = "h" and (OnOff < 0 or OnOff = "Toggle" or OnOff = "T")){ cursor := "b" ; use blank cursors Log("SystemCursor - Hiding mouse cursor") CoordMode, Mouse ; Also lets move it to the side since some emu's flash a cursor real quick even if we hide it. MouseMove, 0, 0, 0 } Else { cursor := "h" ; use the saved cursors SPI_SETCURSORS := 0x57 ; Emergency restore cursor, just in case something goes wrong DllCall( "SystemParametersInfo", UInt,SPI_SETCURSORS, UInt,0, UInt,0, UInt,0 ) mouseCursorHidden := "" Log("SystemCursor - Restoring mouse cursor") } Loop %c0% { h_cursor := DllCall( "CopyImage", "uint",%cursor%%A_Index%, "uint",2, "int",0, "int",0, "uint",0 ) DllCall( "SetSystemCursor", "uint",h_cursor, "uint",c%A_Index% ) } } SetSystemCursor( Cursor = "", cx = 0, cy = 0 ) { BlankCursor := 0, SystemCursor := 0, FileCursor := 0 ; init SystemCursors = 32512IDC_ARROW,32513IDC_IBEAM,32514IDC_WAIT,32515IDC_CROSS ,32516IDC_UPARROW,32640IDC_SIZE,32641IDC_ICON,32642IDC_SIZENWSE ,32643IDC_SIZENESW,32644IDC_SIZEWE,32645IDC_SIZENS,32646IDC_SIZEALL ,32648IDC_NO,32649IDC_HAND,32650IDC_APPSTARTING,32651IDC_HELP If (Cursor = "") ; empty, so create blank cursor { Log("SetSystemCursor - Creating blank cursor",4) VarSetCapacity( AndMask, 32*4, 0xFF ), VarSetCapacity( XorMask, 32*4, 0 ) BlankCursor := 1 ; flag for later } Else If SubStr( Cursor,1,4 ) = "IDC_" ; load system cursor { Log("SetSystemCursor - Loading system cursor",4) Loop, Parse, SystemCursors, `, { CursorName := SubStr( A_Loopfield, 6, 15 ) ; get the cursor name, no trailing space with substr CursorID := SubStr( A_Loopfield, 1, 5 ) ; get the cursor id SystemCursor := 1 If ( CursorName = Cursor ) { CursorHandle := DllCall( "LoadCursor", Uint,0, Int,CursorID ) Break } } If (CursorHandle = "") ; invalid cursor name given { Log("SetSystemCursor - Invalid cursor name supplied: """ . CursorHandle . """",2) ; Msgbox,, SetCursor, Error: Invalid cursor name CursorHandle := "Error" } } Else If FileExist( Cursor ) { Log("SetSystemCursor - Found this cursor: """ . Cursor . """",4) SplitPath, Cursor,,, Ext ; auto-detect type If (Ext = "ico") uType := 0x1 Else If RegExMatch("cur|ani","i)" . Ext) uType := 0x2 Else ; invalid file ext { Log("SetSystemCursor - Invalid cursor extension: """ . Ext . """",2) ; Msgbox,, SetCursor, Error: Invalid file type CursorHandle := "Error" } FileCursor := 1 } Else { Log("SetSystemCursor - Invalid cursor name or path: """ . Cursor . """",2) ; Msgbox,, SetCursor, Error: Invalid file path or cursor name CursorHandle := "Error" ; raise for later } If (CursorHandle != "Error") { Loop, Parse, SystemCursors, `, { If (BlankCursor = 1) { Type := "BlankCursor" %Type%%A_Index% := DllCall( "CreateCursor", Uint,0, Int,0, Int,0, Int,32, Int,32, Uint,&AndMask, Uint,&XorMask ) CursorHandle := DllCall( "CopyImage", Uint,%Type%%A_Index%, Uint,0x2, Int,0, Int,0, Int,0 ) DllCall( "SetSystemCursor", Uint,CursorHandle, Int,SubStr( A_Loopfield, 1, 5 ) ) } Else If (SystemCursor = 1) { Type := "SystemCursor" CursorHandle := DllCall( "LoadCursor", Uint,0, Int,CursorID ) %Type%%A_Index% := DllCall( "CopyImage", Uint,CursorHandle, Uint,0x2, Int,cx, Int,cy, Uint,0 ) CursorHandle := DllCall( "CopyImage", Uint,%Type%%A_Index%, Uint,0x2, Int,0, Int,0, Int,0 ) DllCall( "SetSystemCursor", Uint,CursorHandle, Int,SubStr( A_Loopfield, 1, 5 ) ) } Else If (FileCursor = 1) { Type := "FileCursor" %Type%%A_Index% := DllCall( "LoadImage", UInt,0, Str,Cursor, UInt,uType, Int,cx, Int,cy, UInt,0x10 ) DllCall( "SetSystemCursor", Uint,%Type%%A_Index%, Int,SubStr( A_Loopfield, 1, 5 ) ) } } } } RestoreCursors() { SPI_SETCURSORS := 0x57 DllCall( "SystemParametersInfo", UInt,SPI_SETCURSORS, UInt,0, UInt,0, UInt,0 ) } ;------------------------------------------------------------------------------------------------------------- ;------------------ Read and Write Wrapper Functions for IniFileEdit ------------------ ;------------------------------------------------------------------------------------------------------------- ; Usage - Read and Write to config files that are not valid inis with [sections], like RetroArch's cfg ; cfgFile - path to the file to read, only need to send this once, it stays in memory until SavePropertiesCfg is used ; Returns a reference number to the array where the cfg is stored in memory so multiple files can be edited at once LoadProperties(cfgFile) { Log("LoadProperties - Started and loading this cfg into memory: " . cfgFile,4) cfgtable := Object() Loop, Read, %cfgFile% ; This loop retrieves each line from the file, one at a time. cfgtable.Insert(A_LoopReadLine) ; Append this line to the array. Log("LoadProperties - Ended",4) Return cfgtable } ; cfgFile - path to the file to read, only need to send this once, it stays in memory until SavePropertiesCfg is used ; cfgArray - reference number of array in memory that should be saved to the cfgFile SaveProperties(cfgFile,cfgArray) { Log("SaveProperties - Started and saving this cfg to disk: " . cfgFile,4) FileDelete, %cfgFile% Loop % cfgArray.MaxIndex() { element := cfgArray[A_Index] trimmedElement := LTrim(element) finalCfg .= trimmedElement . "`n" } FileAppend, %finalCfg%, %cfgFile% Log("SaveProperties - Ended",4) } ; cfgArray - reference number of array in memory that you want to read ; keyName = key whose value you want to read ; Separator = the separator to use, defaults to = ReadProperty(cfgArray,keyName,Separator="=") { Log("ReadProperty - Started",4) Loop % cfgArray.MaxIndex() { element := cfgArray[A_Index] trimmedElement := Trim(element) ;MsgBox % "Element number " . A_Index . " is " . element StringGetPos, pos, trimmedElement, [ If (pos = 0) Break ; Section was found, do not search anymore, global section has ended If element contains %Separator% { StringSplit, keyValues, element, %Separator% CfgValue := Trim(keyValues1) If (CfgValue = keyName) Return Trim(keyValues2) ; Found it & trim any whitespace } } Log("ReadProperty - Ended",4) } ; cfgArray - reference number of array in memory that you want to read ; keyName = key whose value you want to write ; Value = value that you want to write to the keyName ; AddSpaces = If the seperator (=) has spaces on either side, set this parameter to 1 and it will wrap the seperator in spaces ; AddQuotes = If the Value needs to be wrapped in double quotes (like in retroarch's config), set this parameter to 1 ; Separator = the separator to use, defaults to = WriteProperty(cfgArray,keyName,Value,AddSpaces=0,AddQuotes=0,Separator="=") { added := 0 Loop % cfgArray.MaxIndex() { lastIndex := A_Index element := cfgArray[A_Index] trimmedElement := Trim(element) StringGetPos, pos, trimmedElement, [ If (pos = 0) { lastIndex := lastIndex - 1 ; Section was found, do not search anymore Break } If element contains %Separator% { StringSplit, keyValues, element, %Separator% CfgValue := Trim(keyValues1) If (CfgValue = keyName) { cfgArray[A_Index] := CfgValue . (If AddSpaces=1 ? (" " . Separator . " ") : Separator) . (If AddQuotes=1 ? ("""" . Value . """") : Value) ; Found it added := 1 Break } } } If (added = 0) cfgArray.Insert(lastIndex+1, keyName . (If AddSpaces=1 ? (" " . Separator . " ") : Separator) . (If AddQuotes=1 ? ("""" . Value . """") : Value)) ; Add the new entry to the file Log("WriteProperty - Writing - " . keyName . ": " . value,4) } ;------------------------------------------------------------------------------------------------------------- ;----------------------------------------- Player Select Menu -------------------------------------- ;------------------------------------------------------------------------------------------------------------- ; function to create a small menu with the number of players option NumberOfPlayersSelectionMenu(maxPlayers=4) { Global screenRotationAngle,baseScreenWidth,baseScreenHeight,xTranslation,yTranslation Global navSelectKey,navUpKey,navDownKey,navP2SelectKey,navP2UpKey,navP2DownKey,exitEmulatorKey,exitEmulatorKey Global keymapper,keymapperEnabled,keymapperRocketLauncherProfileEnabled If !pToken pToken := Gdip_Startup() Gdip_Alt_GetRotatedDimensions(A_ScreenWidth, A_ScreenHeight, screenRotationAngle, baseScreenWidth, baseScreenHeight) Gdip_GetRotatedTranslation(baseScreenWidth, baseScreenHeight, screenRotationAngle, xTranslation, yTranslation) xTranslation:=round(xTranslation), yTranslation:=round(yTranslation) Loop, 2 { Gui, playersMenu_GUI%A_Index%: +Disabled -Caption +E0x80000 +OwnDialogs +LastFound +ToolWindow +AlwaysOnTop Gui, playersMenu_GUI%A_Index%: Margin,0,0 Gui, playersMenu_GUI%A_Index%: Show,, playersMenuLayer%A_Index% playersMenu_hwnd%A_Index% := WinExist() playersMenu_hbm%A_Index% := CreateDIBSection(A_ScreenWidth, A_ScreenHeight) playersMenu_hdc%A_Index% := CreateCompatibleDC() playersMenu_obm%A_Index% := SelectObject(playersMenu_hdc%A_Index%, playersMenu_hbm%A_Index%) playersMenu_G%A_Index% := Gdip_GraphicsFromhdc(playersMenu_hdc%A_Index%) Gdip_SetSmoothingMode(playersMenu_G%A_Index%, 4) Gdip_TranslateWorldTransform(playersMenu_G%A_Index%, xTranslation, yTranslation) Gdip_RotateWorldTransform(playersMenu_G%A_Index%, screenRotationAngle) } ;Initializing parameters playersMenuTextFont := "Bebas Neue" CheckFont(playersMenuTextFont) playersMenuSelectedTextSize := 50 playersMenuSelectedTextColor := "FFFFFFFF" playersMenuDisabledTextColor := "FFAAAAAA" playersMenuDisabledTextSize := 30 playersMenuMargin := 50 playersMenuSpaceBtwText := 30 playersMenuCornerRadius := 10 ;menu scalling factor XBaseRes := 1920, YBaseRes := 1080 If (((A_screenWidth < A_screenHeight) and ((screenRotationAngle=0) or (screenRotationAngle=180))) or ((A_screenWidth > A_screenHeight) and ((screenRotationAngle=90) or (screenRotationAngle=270)))) XBaseRes := 1080, YBaseRes := 1920 If !playersMenuXScale playersMenuXScale := baseScreenWidth/XBaseRes If !playersMenuYScale playersMenuYScale := baseScreenHeight/YBaseRes OptionScale(playersMenuSelectedTextSize, playersMenuYScale) OptionScale(playersMenuDisabledTextSize, playersMenuYScale) OptionScale(playersMenuMargin, playersMenuXScale) OptionScale(playersMenuSpaceBtwText, playersMenuYScale) OptionScale(playersMenuCornerRadius, playersMenuXScale) playersMenuW := MeasureText("X Players", "Left r4 s" . playersMenuSelectedTextSize . " Bold",playersMenuTextFont) + 2*playersMenuMargin playersMenuH := maxPlayers*playersMenuSelectedTextSize + (maxPlayers-1)*playersMenuSpaceBtwText + 2*playersMenuMargin playersMenuX := (baseScreenWidth-playersMenuW)//2 playersMenuY := (baseScreenHeight-playersMenuH)//2 playersMenuBackgroundBrush := Gdip_BrushCreateSolid("0xDD000000") pGraphUpd(playersMenu_G1,playersMenuW,playersMenuH) pGraphUpd(playersMenu_G2,playersMenuW,playersMenuH) ;Drawing Background Gdip_Alt_FillRoundedRectangle(playersMenu_G1, playersMenuBackgroundBrush, 0, 0, playersMenuW, playersMenuH,playersMenuCornerRadius) Alt_UpdateLayeredWindow(playersMenu_hwnd1, playersMenu_hdc1, playersMenuX, playersMenuY, playersMenuW, playersMenuH) ;Drawing choice list SelectedNumberofPlayers := 1 gosub, DrawPlayersSelectionMenu ;Enabling Keys If (keymapperEnabled = "true") and (keymapperRocketLauncherProfileEnabled = "true") RunKeymapper%zz%("menu",keymapper) If keymapperAHKMethod = External RunAHKKeymapper%zz%("menu") Gosub, EnablePlayersMenuKeys LEDBlinky("RL") ; trigger ledblinky profile change if enabled ;Waiting for menu to exit Loop { If PlayersMenuExit Break Sleep, 100 } LEDBlinky("ROM") ; trigger ledblinky profile change if enabled Return SelectedNumberofPlayers ;labels to treat menu changes DrawPlayersSelectionMenu: currentY := 0 Gdip_GraphicsClear(playersMenu_G2) Loop, % maxPlayers { If (a_index=SelectedNumberofPlayers) { currentTextSize := playersMenuSelectedTextSize currentTextColor := playersMenuSelectedTextColor currentTextStyle := "bold" } Else { currentTextSize := playersMenuDisabledTextSize currentTextColor := playersMenuDisabledTextColor currentTextStyle := "normal" } If (a_index=1) currentText := "1 Player" Else currentText := a_index . " Players" currentY := playersMenuMargin + (a_index-1)*(playersMenuSelectedTextSize+playersMenuSpaceBtwText)+(playersMenuSelectedTextSize-currentTextSize)//2 Gdip_Alt_TextToGraphics(playersMenu_G2, currentText, "x0 y" . currentY . " Center c" . currentTextColor . " r4 s" . currentTextSize . " " . currentTextStyle, playersMenuTextFont, playersMenuW, playersMenuSelectedTextSize) } Alt_UpdateLayeredWindow(playersMenu_hwnd2, playersMenu_hdc2, playersMenuX, playersMenuY, playersMenuW, playersMenuH) Return EnablePlayersMenuKeys: XHotKeywrapper(navSelectKey,"PlayersMenuSelect","ON") XHotKeywrapper(navUpKey,"PlayersMenuUP","ON") XHotKeywrapper(navDownKey,"PlayersMenuDown","ON") XHotKeywrapper(navP2SelectKey,"PlayersMenuSelect","ON") XHotKeywrapper(navP2UpKey,"PlayersMenuUP","ON") XHotKeywrapper(navP2DownKey,"PlayersMenuDown","ON") XHotKeywrapper(exitEmulatorKey,"CloseProcess","OFF") XHotKeywrapper(exitEmulatorKey,"ClosePlayersMenu","ON") Return DisablePlayersMenuKeys: XHotKeywrapper(navSelectKey,"PlayersMenuSelect","OFF") XHotKeywrapper(navUpKey,"PlayersMenuUP","OFF") XHotKeywrapper(navDownKey,"PlayersMenuDown","OFF") XHotKeywrapper(navP2SelectKey,"PlayersMenuSelect","OFF") XHotKeywrapper(navP2UpKey,"PlayersMenuUP","OFF") XHotKeywrapper(navP2DownKey,"PlayersMenuDown","OFF") XHotKeywrapper(exitEmulatorKey,"ClosePlayersMenu","OFF") XHotKeywrapper(exitEmulatorKey,"CloseProcess","ON") Return PlayersMenuUP: SelectedNumberofPlayers-- If (SelectedNumberofPlayers<1) SelectedNumberofPlayers:=maxPlayers gosub, DrawPlayersSelectionMenu Return PlayersMenuDown: SelectedNumberofPlayers++ If (SelectedNumberofPlayers>maxPlayers) SelectedNumberofPlayers:=1 gosub, DrawPlayersSelectionMenu Return ClosePlayersMenu: ClosedPlayerMenu := true PlayersMenuSelect: Gosub, DisablePlayersMenuKeys Gdip_DeleteBrush(playersMenuBackgroundBrush) Loop, 2 { SelectObject(playersMenu_hdc%A_Index%, playersMenu_obm%A_Index%) DeleteObject(playersMenu_hbm%A_Index%) DeleteDC(playersMenu_hdc%A_Index%) Gdip_DeleteGraphics(playersMenu_G%A_Index%) Gui, playersMenu_GUI%A_Index%: Destroy } If ClosedPlayerMenu { Log("User cancelled the launch at the Player Select Menu") PlayersMenuExit := true ExitModule() } Else Log("Number of Players Selected: " . SelectedNumberofPlayers) If (keymapperEnabled = "true") and (keymapperRocketLauncherProfileEnabled = "true") RunKeymapper%zz%("load", keymapper) If keymapperAHKMethod = External RunAHKKeymapper%zz%("load") PlayersMenuExit := true Return } ;------------------------------------------------------------------------------------------------------------- ;----------------------------------------- HideEmu Functions --------------------------------------- ;------------------------------------------------------------------------------------------------------------- ; Default is 2ms so it picks up windows as soon as possible HideEmuStart(ms=2) { Global hideEmu Global hideEmuObj If (hideEmu = "true") { Log("HideEmuStart - Starting HideEmuTimer, scanning for windows defined in hideEmuObj every " . ms . "ms") ; First rebuild the single line object into a better one that's easier to track and work with newObject := Object() For key, value in hideEmuObj { currentObj++ newObject[currentObj,"window"] := key newObject[currentObj,"method"] := value newObject[currentObj,"status"] := "" ; default is 0 (0 = not hidden yet, 1 = hidden already) } hideEmuObj := newObject ; overwrite hideEmuObj with the updated one ; SetTimer, HideEmuTimer, %ms% SetTimerF("HideEmuTimer",ms,hideEmuObj,10) ;create a higher priority timer Log("HideEmuStart - Ended") } } HideEmuEnd() { Global hideEmu Global hideEmuObj If (hideEmu = "true") { Log("HideEmuEnd - Stopping HideEmuTimer and unhiding flagged windows",4) ; SetTimer, HideEmuTimer, Off SetTimerF("HideEmuTimer","off") For key, value in hideEmuObj If (hideEmuObj[A_Index,"method"] && hideEmuObj[A_Index,"status"]) { ; if one of the windows was hidden and needs to be unhidden WinSet, Transparent, Off, % hideEmuObj[A_Index,"window"] Log("HideEmu - Revealed window: " . hideEmuObj[A_Index,"window"],4) } Log("HideEmuEnd - Ended",4) } } HideEmuTimer(obj) { Log("HideEmuTimer",3) For key, value in obj { If !obj[A_Index,"status"] { If (A_DetectHiddenWindows != "On") DetectHiddenWindows, On ; msgbox % obj[A_Index,"window"] If WinExist(obj[A_Index,"window"]) { WinSet, Transparent, 0, % obj[A_Index,"window"] Log("HideEmu - Found a new window to hide: " . obj[A_Index,"window"],4) WinGet, currentTran, Transparent, % obj[A_Index,"window"] If (currentTran = 0) obj[A_Index,"status"] := 1 ; update object that this window is now hidden } } } Return } HideEmuTimer: For key, value in hideEmuObj { If !hideEmuObj[A_Index,"status"] { If (A_DetectHiddenWindows != "On") DetectHiddenWindows, On If WinExist(hideEmuObj[A_Index,"window"]) { WinSet, Transparent, 0, % hideEmuObj[A_Index,"window"] Log("HideEmu - Found a new window to hide: " . hideEmuObj[A_Index,"window"],4) WinGet, currentTran, Transparent, % hideEmuObj[A_Index,"window"] If (currentTran = 0) hideEmuObj[A_Index,"status"] := 1 ; update object that this window is now hidden } } } Return SetTimerF( Function, Period=0, ParmObject=0, Priority=0 ) { Static current,tmrs:=[] ;current will hold timer that is currently running If IsFunc( Function ) { If IsObject(tmr:=tmrs[Function]) ;destroy timer before creating a new one ret := DllCall( "KillTimer", UInt,0, PTR, tmr.tmr) , DllCall("GlobalFree", PTR, tmr.CBA) , tmrs.Remove(Function) If (Period = 0 || Period = "off") Return ret ;Return as we want to turn off timer ; create object that will hold information for timer, it will be passed trough A_EventInfo when Timer is launched tmr:=tmrs[Function]:={func:Function,Period:Period="on" ? 250 : Period,Priority:Priority ,OneTime:Period<0,params:IsObject(ParmObject)?ParmObject:Object() ,Tick:A_TickCount} tmr.CBA := RegisterCallback(A_ThisFunc,"F",4,&tmr) Return !!(tmr.tmr := DllCall("SetTimer", PTR,0, PTR,0, UInt , (Period && Period!="On") ? Abs(Period) : (Period := 250) , PTR,tmr.CBA,"PTR")) ;Create Timer and return true if a timer was created , tmr.Tick:=A_TickCount } tmr := Object(A_EventInfo) ;A_Event holds object which contains timer information If IsObject(tmr) { DllCall("KillTimer", PTR,0, PTR,tmr.tmr) ;deactivate timer so it does not run again while we are processing the function If (current && tmr.Priority tmr.Period) ? 0 : (tmr.Period-(A_TickCount-tmr.Tick)), PTR,tmr.CBA,"PTR") current= ;reset timer } } ;------------------------------------------------------------------------------------------------------------- ;---------------------------------------- Decryption Functions ------------------------------------- ;------------------------------------------------------------------------------------------------------------- Decrypt(T,key) ; Text, key-name { Local p, i, L, u, v, k5, a, c StringLeft p, T, 8 If p is not xdigit ; if no IV: Error { ErrorLevel := 1 Return } StringTrimLeft T, T, 8 ; remove IV from text (no separator) k5 = 0x%p% ; set new IV p := 0 ; counter to be Encrypted i := 9 ; pad-index, force restart L := "" ; processed text k0 := %key%0 k1 := %key%1 k2 := %key%2 k3 := %key%3 Loop % StrLen(T) { i++ IfGreater i,8, { ; all 9 pad values exhausted u := p v := k5 ; IV p++ ; increment counter TEA(u,v, k0,k1,k2,k3) Stream9(u,v) ; 9 pads from Encrypted counter i := 0 } StringMid c, T, A_Index, 1 a := Asc(c) if a between 32 and 126 { ; chars > 126 or < 31 unchanged a -= s%i% IfLess a, 32, SetEnv, a, % a+95 c := Chr(a) } L := L . c ; attach Encrypted character } Return L } TEA(ByRef y,ByRef z,k0,k1,k2,k3) ; (y,z) = 64-bit I/0 block { ; (k0,k1,k2,k3) = 128-bit key IntFormat := A_FormatInteger SetFormat Integer, D ; needed for decimal indices s := 0 d := 0x9E3779B9 Loop 32 { k := "k" . s & 3 ; indexing the key y := 0xFFFFFFFF & (y + ((z << 4 ^ z >> 5) + z ^ s + %k%)) s := 0xFFFFFFFF & (s + d) ; simulate 32 bit operations k := "k" . s >> 11 & 3 z := 0xFFFFFFFF & (z + ((y << 4 ^ y >> 5) + y ^ s + %k%)) } SetFormat Integer, %IntFormat% y += 0 z += 0 ; Convert to original ineger format } Stream9(x,y) ; Convert 2 32-bit words to 9 pad values { ; 0 <= s0, s1, ... s8 <= 94 Local z ; makes all s%i% global s0 := Floor(x*0.000000022118911147) ; 95/2**32 Loop 8 { z := (y << 25) + (x >> 7) & 0xFFFFFFFF y := (x << 25) + (y >> 7) & 0xFFFFFFFF x := z s%A_Index% := Floor(x*0.000000022118911147) } } ;------------------------------------------------------------------------------------------------------------- ;------------------------------------ Registry Access Functions ---------------------------------- ;------------------------------------------------------------------------------------------------------------- RegRead(RootKey, SubKey, ValueName = "", RegistryVersion="32") { Global winVer Log("RegRead - Reading from Registry : RootKey=" . RootKey . ", SubKey=" . SubKey . ", ValueName=" . ValueName . ",RegistryVersion=" . RegistryVersion, 4) If (RegistryVersion = "Auto") ;Try finding the correct registry reading based on the windows version { If (winVer = "64") If !OutputVar := RegRead(RootKey, SubKey, ValueName, "64") OutputVar := RegRead(RootKey, SubKey, ValueName, "32") Else OutputVar := RegRead(RootKey, SubKey, ValueName) } Else If (RegistryVersion = "32") RegRead, OutputVar, %RootKey%, %SubKey%, %ValueName% Else OutputVar := RegRead64(RootKey, SubKey, ValueName) Log("RegRead - Registry Read finished, returning " . OutputVar, 4) Return OutputVar } RegWrite(ValueType, RootKey, SubKey, ValueName = "", Value = "", RegistryVersion="32") { Log("RegWrite - Writing to Registry : RootKey=" . RootKey . ", SubKey=" . SubKey . ", ValueName=" . ValueName . ",Value=" . Value . ",ValueType=" . ValueType . ",RegistryVersion=" . RegistryVersion, 4) If (RegistryVersion = "32") RegWrite, %ValueType%, %RootKey%, %SubKey%, %ValueName%, %Value% Else RegWrite64(ValueType, RootKey, SubKey, ValueName, Value) Log("RegWrite - Registry Write finished", 4) } ;------------------------------------------------------------------------------------------------------------- ;----------------------------------------- Display Resolution Functions ---------------------------------------- ;------------------------------------------------------------------------------------------------------------- ; Grab current display settings for each monitor ; Returns object: ; obj[#] = Display Number ; obj[#].Name = Display's Name as known in windows. Ex: \\.\DISPLAY1 ; obj[#].Width = Display's Width ; obj[#].Height = Display's Height ; obj[#].BitDepth = Display's BitDepth ; obj[#].Frequency = Display's Frequency ; obj[#].Orientation = Display's Orientation ; obj[#].Left = Where this display's Left position starts ; obj[#].Right = Where this display's Right position ends ; obj[#].Top = Where this display's Top position starts ; obj[#].Bottom = Where this display's Bottom position ends ; obj[#].WorkingWidth = Display's Working Width. This is the usable space w/o the task bar ; obj[#].WorkingHeight = Display's Working Height GetDisplaySettings() { Global RLObject obj := Object() monOrientation := Object(0,"Landscape",1,"Portrait",2,"Landscape (Flipped)",3,"Portrait (Flipped)") getDisplaySettingsObj := Object(1,"Width",2,"Height",3,"BitDepth",4,"Frequency",5,"Orientation") SysGet, MonitorCount, 80 ; MonitorCount SysGet, MonitorPrimary, MonitorPrimary Loop, %MonitorCount% ; get each monitor's stats for the log { SysGet, MonitorName, MonitorName, %A_Index% SysGet, Monitor, Monitor, %A_Index% SysGet, MonitorWorkArea, MonitorWorkArea, %A_Index% currentobj:={} currentobj.Number := A_Index ; this is the monitor #, an integer currentobj.Name := MonitorName ; store monitor's name as it is known as in Windows monDetails := RL_getDisplaySettings(MonitorName) ; return 0 if something went wrong, like an invalid displayName is passed. Otherwise it returns a string with this format "width|height|bits|frequency|orientation" Loop, Parse, monDetails, | currentObj[getDisplaySettingsObj[A_Index]] := A_LoopField ; parse RLObject's return and store into the object currentobj.Orientation := monOrientation[currentobj.Orientation] ; replace orientation integer with name from object instead currentobj.Left := MonitorLeft ; store where this monitor's Left position starts currentobj.Right := MonitorRight ; store where this monitor's Right position ends currentobj.Top := MonitorTop ; store where this monitor's Top position starts currentobj.Bottom := MonitorBottom ; store where this monitor's Bottom position ends currentobj.WorkingWidth := MonitorWorkAreaRight - MonitorWorkAreaLeft ; store this monitor's working width currentobj.WorkingHeight := MonitorWorkAreaBottom - MonitorWorkAreaTop ; store this monitor's working height obj.Insert(currentobj["Number"], currentobj) Log("GetDisplaySettings - Monitor #" . A_Index . " (" . MonitorName . "): " . MonitorRight - MonitorLeft . "x" . MonitorBottom - MonitorTop . " (" . MonitorWorkAreaRight - MonitorWorkAreaLeft . "x" . MonitorWorkAreaBottom - MonitorWorkAreaTop . " work) [" . currentobj.BitDepth . "bit] [" . currentobj.Frequency . "hz] [" . currentobj.Orientation . "] " . (If MonitorPrimary = A_Index ? " (Primary)" : ""),4) } Return obj } ; This function will take the | delimited user settings from theses places: ; A) RLUI for all monitor/resolution settings and break it apart so it can be used in the various display functions then sent to the DLL ; B) Module when called to change the display setting(s) ; It supports multiple monitors and parameters for each ; array = | delimted string in this order where each monitor is delimted by ; monNumber|monWidth|monHeight|monBitDepth|monFrequency ; Example = 1|1600|1200|32|60&2|1024|768|32|60 ConvertToMonitorObject(array) { Global monitorTable displaySettingsObj := Object(1,"Name",2,"Width",3,"Height",4,"BitDepth",5,"Frequency") obj := Object() Loop, Parse, array, & { currentobj:={} currentobj.Number := A_Index ; this is the monitor #, an integer Loop, Parse, A_LoopField, | { If !A_LoopField { Log("ConvertToMonitorObject - " . displaySettingsObj[A_Index] . " not supplied in array. Object creation terminated.",3) Return } currentobj[displaySettingsObj[A_Index]] := If displaySettingsObj[A_Index] = "Name" && !InStr(A_LoopField,"DISPLAY") ? "\\.\DISPLAY" . A_LoopField : A_LoopField } obj.Insert(currentobj["Number"], currentobj) } Return obj } ; Handle the supplied object by first checking if parameters are supported by the monitor, then setting them. ; This is the function that will be called in features and modules to change display settings. SetDisplaySettings(monObj) { Global monitorTable Log("SetDisplaySettings - Started") currentMon := GetDisplaySettings() ; get current parameters to see if res needs to be changed or it can be skipped Loop % monObj.MaxIndex() { ; msgbox % "SetDisplaySettings`n`nobj[" . A_Index . "].Name: " . monObj[A_Index].Name . "`nobj.Width: " . monObj[A_Index].Width . "`nobj.Height: " . monObj[A_Index].Height . "`nobj.BitDepth: " . monObj[A_Index].BitDepth . "`nobj.Frequency: " . monObj[A_Index].Frequency x := CheckDisplaySettings(monObj[A_Index].Name,monObj[A_Index].Width,monObj[A_Index].Height,monObj[A_Index].BitDepth,monObj[A_Index].Frequency) If x { If ((monObj[A_Index].Width = currentMon[A_Index].Width) && (monObj[A_Index].Height = currentMon[A_Index].Height) && (monObj[A_Index].BitDepth = currentMon[A_Index].BitDepth) && (monObj[A_Index].Frequency = currentMon[A_Index].Frequency)) { Log("SetDisplaySettings - Not changing " . monObj[A_Index].Name . " as it is already set to " . monObj[A_Index].Width . "x" . monObj[A_Index].Height . " " . monObj[A_Index].BitDepth . "bit " . monObj[A_Index].Frequency . "hz") } Else { Log("SetDisplaySettings - Changing " . monObj[A_Index].Name . " to " . monObj[A_Index].Width . "x" . monObj[A_Index].Height . " " . monObj[A_Index].BitDepth . "bit " . monObj[A_Index].Frequency . "hz") ChangeDisplaySettings(monObj[A_Index].Name,monObj[A_Index].Width,monObj[A_Index].Height,monObj[A_Index].BitDepth,monObj[A_Index].Frequency) ; Update monitorTable with new settings so it stays current SysGet, Monitor, Monitor, %A_Index% SysGet, MonitorWorkArea, MonitorWorkArea, %A_Index% monitorTable[A_Index].Width := monObj[A_Index].Width monitorTable[A_Index].Height := monObj[A_Index].Height monitorTable[A_Index].BitDepth := monObj[A_Index].BitDepth monitorTable[A_Index].Frequency := monObj[A_Index].Frequency monitorTable[A_Index].Left := MonitorLeft monitorTable[A_Index].Right := MonitorRight monitorTable[A_Index].Top := MonitorTop monitorTable[A_Index].Bottom := MonitorBottom monitorTable[A_Index].WorkingWidth := MonitorWorkAreaRight - MonitorWorkAreaLeft monitorTable[A_Index].WorkingHeight := MonitorWorkAreaBottom - MonitorWorkAreaTop } } Else Log("SetDisplaySettings - " . monObj[A_Index].Name . " does not support " . monObj[A_Index].Width . "x" . monObj[A_Index].Height . " " . monObj[A_Index].BitDepth . "bit " . monObj[A_Index].Frequency . "hz",2) } Log("SetDisplaySettings - Ended") } ; Change screen resolution to supplied parameters ; Do not call this function directly, allow SetDisplaySettings to handle it ; n = Display Name ; w = Screen Width ; h = Screen Height ; b = Bit Depth ; f = Frequency ChangeDisplaySettings(n,w,h,b,f) { Global RLObject Return RL_changeDisplaySettings(n,w,h,b,f) } ; Check monitor if it can support the screen parameters supplied ; Do not call this function directly, allow SetDisplaySettings to handle it ; Use this before calling ChangeDisplaySettings ; n = Display Name ; w = Screen Width ; h = Screen Height ; b = Bit Depth ; f = Frequency CheckDisplaySettings(n,w,h,b,f) { Global RLObject Return RL_checkDisplaySettings(n,w,h,b,f) } ; http://www.autohotkey.com/forum/topic8355.html ; ChangeDisplaySettings( sW, sH, cD, rR ) { ; Change Screen Resolution ; VarSetCapacity(dM,156,0), NumPut(156,dM,36) ; DllCall( "EnumDisplaySettingsA", UInt,0, UInt,-1, UInt,&dM ), NumPut(0x5c0000,dM,40) ; NumPut(cD,dM,104), NumPut(sW,dM,108), NumPut(sH,dM,112), NumPut(rR,dM,120) ; Return DllCall( "ChangeDisplaySettingsA", UInt,&dM, UInt,0 ) ; } ; Acquire display "index" screen resolution (index=0,1,...) ; GetDisplaySettings(Index) { ; VarSetCapacity(device_mode,156,0) ; success:=DllCall("EnumDisplaySettings","uint",0,"uint",Index-1,"uint",&device_mode) ; If (ErrorLevel or !success) ; Return "Break" ; Out_1:=NumGet(&device_mode,108,"uint4") ;width ; Out_2:=NumGet(&device_mode,112,"uint4") ;height ; Out_3:=NumGet(&device_mode,104,"uint4") ;quality ; Out_4:=NumGet(&device_mode,120,"uint4") ;frequency ; Return Out_1 "|" Out_2 "|" Out_3 "|" Out_4 ; } ; out "Break" ; Acquire current display screen resolution (1=width 2=height 3=quality 4=frequency) ; CurrentDisplaySettings(in=0) { ; VarSetCapacity(device_mode,156,0),NumPut(156,2,&device_mode,36) ; success := DllCall("EnumDisplaySettings","uint",0,"uint",-1,"uint",&device_mode) ; Out_1:=NumGet(&device_mode,108,"uint4") ;width ; Out_2:=NumGet(&device_mode,112,"uint4") ;height ; Out_3:=NumGet(&device_mode,104,"uint4") ;quality ; Out_4:=NumGet(&device_mode,120,"uint4") ;frequency ; If in = 0 ; Return Out_1 "|" Out_2 "|" Out_3 "|" Out_4 ; Else Return (Out_%in%) ; } ; Check if a given screen resolution is supported by the monitor, and if not, choose the nearest one that is ; desiredResTable uses the object defined by ConvertToMonitorObject CheckForNearestSupportedRes(monNumber,desiredResTable){ Log("CheckForNearestSupportedRes - Started") If !desiredResTable { Log("CheckForNearestSupportedRes - Supplied desired resolution does not contain any data.",3) Return } listOfSupportedRes := EnumDisplaySettings(monNumber) If !listOfSupportedRes { Log("CheckForNearestSupportedRes - Ended, was supplied a monitor number that is not attached to this system: " . monNumber,3) Return } If RegExMatch(listOfSupportedRes, "i)" desiredResTable[monNumber].Width . "\|" . desiredResTable[monNumber].Height . "\|" . desiredResTable[monNumber].BitDepth . "\|" . desiredResTable[monNumber].Frequency) { Log("CheckForNearestSupportedRes - Ended, this resolution is already supported by this display: " . desiredResTable[monNumber].Width . "|" . desiredResTable[monNumber].Height . "|" . desiredResTable[monNumber].BitDepth . "|" . desiredResTable[monNumber].Frequency,4) Return desiredResTable } Log("CheckForNearestSupportedRes - This resolution is not directly supported by your monitor, finding the closest match.",4) SupportedResObj := {} nearestSupportedRes := {} Loop, Parse, listOfSupportedRes, `, { currentRes := A_Index SupportedResObj[currentRes] := {} StringSplit,curRes,A_LoopField,| SupportedResObj[currentRes].Width := curRes1 SupportedResObj[currentRes].Height := curRes2 SupportedResObj[currentRes].BitDepth := curRes3 SupportedResObj[currentRes].Frequency := curRes4 SupportedResObj[currentRes]["Distance"] := {} SupportedResObj[currentRes]["Distance"].Width := SupportedResObj[currentRes].Width - desiredResTable[monNumber].Width SupportedResObj[currentRes]["Distance"].Height := SupportedResObj[currentRes].Height - desiredResTable[monNumber].Height SupportedResObj[currentRes]["Distance"].BitDepth := SupportedResObj[currentRes].BitDepth - desiredResTable[monNumber].BitDepth SupportedResObj[currentRes]["Distance"].Frequency := SupportedResObj[currentRes].Frequency - desiredResTable[monNumber].Frequency } previousDeviation := 10**9 Loop, %currentRes% { currentDeviation := 100*SupportedResObj[A_Index]["Distance"].Width*SupportedResObj[A_Index]["Distance"].Width + 100*SupportedResObj[A_Index]["Distance"].Height*SupportedResObj[A_Index]["Distance"].Height + 10*SupportedResObj[A_Index]["Distance"].BitDepth*SupportedResObj[A_Index]["Distance"].BitDepth + SupportedResObj[A_Index]["Distance"].Frequency*SupportedResObj[A_Index]["Distance"].Frequency If (currentDeviation < previousDeviation) { previousDeviation := currentDeviation nearestSupportedRes.Width := SupportedResObj[A_Index].Width nearestSupportedRes.Height := SupportedResObj[A_Index].Height nearestSupportedRes.BitDepth := SupportedResObj[A_Index].BitDepth nearestSupportedRes.Frequency := SupportedResObj[A_Index].Frequency } } supportedRes := ConvertToMonitorObject(monNumber . "|" . nearestSupportedRes.Width . "|" . nearestSupportedRes.Height . "|" . nearestSupportedRes.BitDepth . "|" nearestSupportedRes.Frequency) ; convert to object Log("CheckForNearestSupportedRes - Ended, closest match found is: " . supportedRes[monNumber].Width . "|" . supportedRes[monNumber].Height . "|" . supportedRes[monNumber].BitDepth . "|" supportedRes[monNumber].Frequency,4) Return supportedRes } ; Enumerate Supported Screen Resolutions ; display = the number of the monitor to enumerate supported resolutions for. 1 = Display 1, 2 = display 2, etc ; Returns | delimited list of all supported settings EnumDisplaySettings(display) { VarSetCapacity(DM,156,0), NumPut(156,&DM,36, "UShort") DllCall( "EnumDisplaySettings", str,"\\.\DISPLAY" Display, UInt,-1, UInt,&DM ) CS:=NumGet(DM,108) "|" NumGet(DM,112) "|" NumGet(DM,104) "|" NumGet(DM,120) Loop If DllCall( "EnumDisplaySettings", str,"\\.\DISPLAY" Display, UInt,A_Index-1, UInt,&DM ) { EDS:=NumGet(DM,108) "|" NumGet(DM,112) "|" NumGet(DM,104) "|" NumGet(DM,120) DS.=(!InStr(DS,EDS) ? "," EDS : "") } Else Break StringReplace, DS, DS, %CS%|, %CS%||, All Return SubStr(DS,2) } OptionScale(ByRef option, scale){ ;selects portrait specifc option value if needed and scales variable to adjust to screen resolution Global screenRotationAngle If InStr(option,"|") { StringSplit, opt, option, | If ((opt2) and (((A_screenWidth < A_screenHeight) and ((screenRotationAngle=0) or (screenRotationAngle=180))) or ((A_screenWidth > A_screenHeight) and ((screenRotationAngle=90) or (screenRotationAngle=270))))) option := If (SubStr(opt2, 0)="p") ? opt2 : round(opt2 * scale) Else option := If (SubStr(opt1, 0)="p") ? opt1 : round(opt1 * scale) } Else option := If (SubStr(option, 0)="p") ? option : round(option * scale) } TextOptionScale(ByRef Option,XScale, YScale){ RegExMatch(Option, "i)X([\-\d\.]+)(p*)\|([\-\d\.]+)(p*)|X([\-\d\.]+)(p*)", xpos) RegExMatch(Option, "i)Y([\-\d\.]+)(p*)\|([\-\d\.]+)(p*)|Y([\-\d\.]+)(p*)", ypos) RegExMatch(Option, "i)W([\-\d\.]+)(p*)\|([\-\d\.]+)(p*)|W([\-\d\.]+)(p*)", Width) RegExMatch(Option, "i)H([\-\d\.]+)(p*)\|([\-\d\.]+)(p*)|H([\-\d\.]+)(p*)", Height) RegExMatch(Option, "i)S([\-\d\.]+)(p*)\|([\-\d\.]+)(p*)|S([\-\d\.]+)", Size) xposValue := SubStr(xpos, 2), yposValue := SubStr(ypos, 2), WidthValue := SubStr(Width, 2), HeightValue := SubStr(Height, 2), SizeValue := SubStr(Size, 2) OptionScale(xposValue, XScale) OptionScale(yposValue, YScale) OptionScale(WidthValue, XScale) OptionScale(HeightValue, YScale) OptionScale(SizeValue, YScale) Option := RegExReplace(Option, "i)X([\-\d\.]+)(p*)\|([\-\d\.]+)(p*)|X([\-\d\.]+)(p*)", "x" . xposValue) Option := RegExReplace(Option, "i)Y([\-\d\.]+)(p*)\|([\-\d\.]+)(p*)|Y([\-\d\.]+)(p*)", "y" . yposValue) Option := RegExReplace(Option, "i)W([\-\d\.]+)(p*)\|([\-\d\.]+)(p*)|W([\-\d\.]+)(p*)", "w" . WidthValue) Option := RegExReplace(Option, "i)H([\-\d\.]+)(p*)\|([\-\d\.]+)(p*)|H([\-\d\.]+)(p*)", "h" . HeightValue) Option := RegExReplace(Option, "i)S([\-\d\.]+)(p*)\|([\-\d\.]+)(p*)|S([\-\d\.]+)", "s" . SizeValue) } ;------------------------------------------------------------------------------------------------------------- ;---------------------------- Open And Close Process Functions ---------------------------- ;------------------------------------------------------------------------------------------------------------- ProcSus(PID_or_Name) { Log("ProcSus - Started",4) If InStr(PID_or_Name, ".") { Process, Exist, %PID_or_Name% PID_or_Name := ErrorLevel } If !(h := DllCall("OpenProcess", "uInt", 0x1F0FFF, "Int", 0, "Int", PID_or_Name)) { Log("ProcSus - Ended, process """ . PID_or_Name . """ not found") Return -1 } Log("ProcSus - Suspending Process: " . PID_or_Name) DllCall("ntdll.dll\NtSuspendProcess", "Int", h), DllCall("CloseHandle", "Int", h) Log("ProcSus - Ended",4) } ProcRes(PID_or_Name) { Log("ProcRes - Started",4) If InStr(PID_or_Name, ".") { Process, Exist, %PID_or_Name% PID_or_Name := ErrorLevel } If !(h := DllCall("OpenProcess", "uInt", 0x1F0FFF, "Int", 0, "Int", PID_or_Name)) { Log("ProcRes - Ended, process """ . PID_or_Name . """ not found") Return -1 } Log("ProcRes - Resuming Process: " . PID_or_Name) DllCall("ntdll.dll\NtResumeProcess", "Int", h), DllCall("CloseHandle", "Int", h) Log("ProcRes - Ended",4) } ;------------------------------------------------------------------------------------------------------------- ;--------------------------------------- Validate IP Functions --------------------------------------- ;------------------------------------------------------------------------------------------------------------- ValidIP(a) { Loop, Parse, a, . { If A_LoopField is digit If A_LoopField between 0 and 255 e++ c++ } Return, e = 4 AND c = 4 } ValidPort(a) { If a is digit If a between 0 and 65535 e++ Return e } GetPublicIP() { UrlDownloadToFile, http://www.rlauncher.com/ipcheck/myip.php, %A_Temp%\myip.txt FileRead, extIP, %A_Temp%\myip.txt Return extIP } GetLocalIP() { array := [] objWMIService := ComObjGet("winmgmts:{impersonationLevel = impersonate}!\\.\root\cimv2") colItems := objWMIService.ExecQuery("Select * from Win32_NetworkAdapterConfiguration WHERE IPEnabled = True")._NewEnum while colItems[objItem] { array[A_Index,1] := objItem.Description[0] array[A_Index,2] := objItem.IPAddress[0] array[A_Index,3] := objItem.IPSubnet[0] array[A_Index,4] := objItem.DefaultIPGateway[0] array[A_Index,5] := objItem.DNSServerSearchOrder[0] array[A_Index,6] := objItem.MACAddress[0] array[A_Index,7] := objItem.DHCPEnabled[0] } Return array } ;------------------------------------------------------------------------------------------------------------- ;------------------------------------------- Rotate Screen Functions ------------------------------------------- ;------------------------------------------------------------------------------------------------------------- ; Options: ; method = irotate, display, or shortcut ; degrees = 0, 90, 180, 270 Rotate(method="irotate", degrees=0) { Log("Rotate - Started") Global moduleExtensionsPath arrowKeys := { 0: "Up", 1: "Right", 2: "Down", 3: "Left" } If !RegExMatch(method,"i)irotate|display|shortcut") ScriptError("""" . method . """ is not a valid rotate method, Please choose either ""irotate"" or ""display""") If !RegExMatch(degrees,"0|90|180|270") ScriptError("""" . degrees . """ is not a valid degree to rotate to, Please choose either 0, 90, 180, or 270") rotateExe := CheckFile(moduleExtensionsPath . "\" . method . ".exe") ; check If the exe to our RotateMethod method exists If (method = "irotate") { Log("Rotate - Rotating display using irotate.exe to " . degrees . " degrees",4) Run(rotateExe . " /rotate=" degrees " /exit", moduleExtensionsPath) } Else If (method = "display") { Log("Rotate - Rotating display using display.exe to " . degrees . " degrees",4) Run(rotateExe . " /rotate:" degrees, moduleExtensionsPath) } Else If (method = "shortcut") { Log("Rotate - Rotating display using shortcut keys to " . degrees . " degrees",4) Send, % "{LControl Down}{LAlt Down}{" . arrowKeys[degrees // 90] . " Down}{LControl Up}{LAlt Up}{" . arrowKeys[degrees // 90] . " Up}" } Log("Rotate - Ended") } ;------------------------------------------------------------------------------------------------------------- ;-------------------------------------------- Database Asset Building ------------------------------------------ ;------------------------------------------------------------------------------------------------------------- ; Builds an object filled with the FE's assets BuildAssetsTable(list,label,AssetType,extensions="",obj=""){ Log("BuildAssetsTable - Started - Building Table for: " . label,4) Global logLevel StringReplace, extensionsReplaced, extensions, |, `,,All If !(obj) obj := {} StringSplit, labelArray, label, |, StringSplit, AssetTypeArray, AssetType, |, Loop, Parse, list,| { If !(labelArray%A_Index% = "#disabled#") { Log("BuildAssetsTable - Searching for a " . labelArray%A_Index% . ": " . A_LoopField,4) currentLabel := labelArray%A_Index% currentAssetType := AssetTypeArray%A_index% RASHNDOCT := FileExist(A_LoopField) If InStr(RASHNDOCT, "D") { ; it is a folder folderName := A_LoopFileName Loop, % A_LoopField . "\*.*" { If RegExMatch(extensions,"i)" . A_LoopFileExt) { currentobj := {} If (currentLabel="keepFileName") currentobj["Label"] := folderName Else currentobj["Label"] := currentLabel If obj[currentLabel].Label { currentobj := obj[currentLabel] currentobj.TotalItems := currentobj.TotalItems+1 } Else { currentobj.TotalItems := 1 obj.TotalLabels := if obj.TotalLabels ? obj.TotalLabels + 1 : 1 obj[obj.TotalLabels] := currentobj.Label } currentobj["Path" . currentobj.TotalItems] := A_LoopFileLongPath currentobj["Ext" . currentobj.TotalItems] := A_LoopFileExt currentobj["AssetType"] := currentAssetType currentobj["Type"] := "ImageGroup" obj.Insert(currentobj["Label"], currentobj) } } } Else If InStr(RASHNDOCT, "A") { ; it is a file SplitPath, A_LoopField, , currentDir,, FileNameWithoutExtension Loop, Parse, extensionsReplaced,`, { If FileExist(currentDir . "\" . FileNameWithoutExtension . "." . A_LoopField) { currentobj := {} If (currentLabel="keepFileName") currentobj["Label"] := FileNameWithoutExtension Else currentobj["Label"] := currentLabel If obj[FileNameWithoutExtension].Label { currentobj := obj[FileNameWithoutExtension] currentobj.TotalItems := currentobj.TotalItems+1 } Else { currentobj.TotalItems := 1 obj.TotalLabels := if obj.TotalLabels ? obj.TotalLabels + 1 : 1 obj[obj.TotalLabels] := currentobj.Label } currentobj["Path" . currentobj.TotalItems] := currentDir . "\" . FileNameWithoutExtension . "." . A_LoopField currentobj["Ext" . currentobj.TotalItems] := A_LoopField currentobj["AssetType"] := currentAssetType obj.Insert(currentobj["Label"], currentobj) } } } } Else Log("BuildAssetsTable - This asset has been disabled: " . labelArray%A_Index%,4) } If (logLevel>=5){ for index, element in obj { Loop, % obj[element.Label].TotalItems mediaAssetsLog := % mediaAssetsLog . "`r`n`t`t`t`t`tAsset Label: " . element.Label . " | Asset Path" . a_index . ": " . element["Path" . a_index] . " | Asset Extension" . a_index . ": " . element["Ext" . a_index] . " | Asset Type" . a_index . ": " . element["AssetType"] } If mediaAssetsLog Log("BuildAssetsTable - Media assets found: " . mediaAssetsLog,5) } Log("BuildAssetsTable - Ended",4) Return obj } BuildAssetsTableOld(list,label,AssetType,extensions=""){ Log("BuildAssetsTable - Started - Building Table for: " . label,4) Global logLevel StringReplace, extensionsReplaced, extensions, |, `,,All obj:={} StringSplit, labelArray, label, |, StringSplit, AssetTypeArray, AssetType, |, Loop, Parse, list,| { labelIndex++ If !(labelArray%labelIndex% = "#disabled#") { Log("BuildAssetsTable - Searching for a " . labelArray%labelIndex% . ": " . A_LoopField,4) currentLabel := labelArray%labelIndex% currentAssetType := AssetTypeArray%A_index% RASHNDOCT := FileExist(A_LoopField) If InStr(RASHNDOCT, "D") { ; it is a folder folderName := A_LoopFileName Loop, % A_LoopField . "\*.*" { If RegExMatch(extensions,"i)" . A_LoopFileExt) { currentobj := {} If (currentLabel="keepFileName") currentobj["Label"] := folderName Else currentobj["Label"] := currentLabel If obj[currentLabel].Label { currentobj := obj[currentLabel] currentobj.TotalItems := currentobj.TotalItems+1 } Else { currentobj.TotalItems := 1 obj.TotalLabels := if obj.TotalLabels ? obj.TotalLabels + 1 : 1 obj[obj.TotalLabels] := currentobj.Label } currentobj["Path" . currentobj.TotalItems] := A_LoopFileLongPath currentobj["Ext" . currentobj.TotalItems] := A_LoopFileExt currentobj["AssetType"] := currentAssetType currentobj["Type"] := "ImageGroup" obj.Insert(currentobj["Label"], currentobj) } } } Else If InStr(RASHNDOCT, "A") { ; it is a file SplitPath, A_LoopField, , currentDir,, FileNameWithoutExtension Loop, Parse, extensionsReplaced,`, { If (InStr(extensionsReplaced , ",") && A_Index >= 2) { labelIndex++ ; need to advance the label by one each time a new extension is used currentLabel := labelArray%labelIndex% } If FileExist(currentDir . "\" . FileNameWithoutExtension . "." . A_LoopField) { currentobj := {} If (currentLabel="keepFileName") currentobj["Label"] := FileNameWithoutExtension Else currentobj["Label"] := currentLabel If obj[FileNameWithoutExtension].Label { currentobj := obj[FileNameWithoutExtension] currentobj.TotalItems := currentobj.TotalItems+1 } Else { currentobj.TotalItems := 1 obj.TotalLabels := if obj.TotalLabels ? obj.TotalLabels + 1 : 1 obj[obj.TotalLabels] := currentobj.Label } currentobj["Path" . currentobj.TotalItems] := currentDir . "\" . FileNameWithoutExtension . "." . A_LoopField currentobj["Ext" . currentobj.TotalItems] := A_LoopField currentobj["AssetType"] := currentAssetType obj.Insert(currentobj["Label"], currentobj) } } } } Else Log("BuildAssetsTable - This asset has been disabled: " . labelArray%labelIndex%,4) } If (logLevel>=5){ for index, element in obj { Loop, % obj[element.Label].TotalItems mediaAssetsLog := % mediaAssetsLog . "`r`n`t`t`t`t`tAsset Label: " . element.Label . " | Asset Path" . a_index . ": " . element["Path" . a_index] . " | Asset Extension" . a_index . ": " . element["Ext" . a_index] . " | Asset Type" . a_index . ": " . element["AssetType"] } If mediaAssetsLog Log("BuildAssetsTable - Media assets found: " . mediaAssetsLog,5) } Log("BuildAssetsTable - Ended",4) Return obj } ;------------------------------------------------------------------------------------------------------------- ;-------------------------------------- Broadcast and Message Receiving -------------------------------------- ;------------------------------------------------------------------------------------------------------------- ReceiveMessage(wParam, lParam) ; receive messages from other programs { Global Pause_Active, Pause_Running, systemName, gameInfo ;Global ; it is necessary to use global if the var definition part is uncommented StringAddress := NumGet(lParam + 8) StringLength := DllCall("lstrlen", UInt, StringAddress) If (StringLength <= 0) Log("A blank string message was received by RocketLauncher or there was an error.") Else { VarSetCapacity(CopyOfData, StringLength) DllCall("lstrcpy", "str", CopyOfData, "uint", StringAddress) StringSplit, Data, CopyOfData, | If (Data1) { If (Data1="command") { ; predefined commands If (Data2="RLPause") { If (Pause_Active){ Gosub, TogglePauseMenuStatus Log("RocketLauncher received the windows message to unpause the game: " . Data2) } Else If !(Pause_Running) { Gosub, TogglePauseMenuStatus Log("RocketLauncher received the windows message to pause the game: " . Data2) } } Else If (Data2="RLSelect") { If (Pause_Active){ Gosub, ToggleItemSelectStatus } } Else If (Data2="RLUp") { If (Pause_Active){ Gosub, MoveUp } } Else If (Data2="RLDown") { If (Pause_Active){ Gosub, MoveDown } } Else If (Data2="RLLeft") { If (Pause_Active){ Gosub, MoveLeft } } Else If (Data2="RLRight") { If (Pause_Active){ Gosub, MoveRight } } Else If (Data2="RLExit") { Gosub, CloseProcess } Else { Log("RocketLauncher received the windows message to run a inexistent command: " . Data2) } } Else If (Data1="ping") { BroadcastMessage("RocketLauncher Message System is Available.") } Else If (Data1="Which system?") { BroadcastMessage("RocketLauncher Message. Current System: " . systemName) } Else If (Data1="Which game?") { BroadcastMessage("RocketLauncher Message. Current Game: " . gameInfo["Name"].Value) } /*} if (Data1="runlabel") { if IsLabel(Data2) { Log("RocketLauncher received the windows message to run the label: " . Data2) gosub, %Data2% } Else { Log("RocketLauncher received the windows message to run a inexistent label: " . Data2) } } Else If (Data1="setvar") { Log("RocketLauncher received the windows message to set the variable named " . Data2 . " to the value = " . Data3) %Data2% := Data3 */ } Else Log("RocketLauncher does not recognize the pure string message sent as a valid command: " . CopyOfData) } Return true } BroadcastMessage(StringToSend) { Global broadcastWindowTitle If broadcastWindowTitle { VarSetCapacity(CopyDataStruct, 12, 0) NumPut(StrLen(StringToSend) + 1, CopyDataStruct, 4) NumPut(&StringToSend, CopyDataStruct, 8) Prev_DetectHiddenWindows := A_DetectHiddenWindows Prev_TitleMatchMode := A_TitleMatchMode DetectHiddenWindows On SetTitleMatchMode 2 ; If (BroadcastWindowTitle="All"){ ; Log("BroadcastMessage - Sending message """ . StringToSend . """ to all windows.") ; SendMessage, 0x4a, 0, &CopyDataStruct,, ahk_id 0xFFFF If InStr(BroadcastWindowTitle, "|") { Loop, Parse, BroadcastWindowTitle, |, %A_Space% { Log("BroadcastMessage - Sending message """ . StringToSend . """ to " . A_LoopField . " window.") SendMessage, 0x4a, 0, &CopyDataStruct,, % BroadcastWindowTitle } } Else If (BroadcastWindowTitle){ If (WinExist(BroadcastWindowTitle) != "0x0") { Log("BroadcastMessage - Sending message """ . StringToSend . """ to " . BroadcastWindowTitle . " window.") SendMessage, 0x4a, 0, &CopyDataStruct,, % BroadcastWindowTitle } Else Log("BroadcastMessage - Could not broadcast message """ . StringToSend . """ to " . BroadcastWindowTitle . " because the window could not be found.",2) } Else Log("Message was not broadcasted because the lack of a valid window target.",4) DetectHiddenWindows %Prev_DetectHiddenWindows% SetTitleMatchMode %Prev_TitleMatchMode% Return ErrorLevel ; Return SendMessage's reply back to our caller. } } ;------------------------------------------------------------------------------------------------------------- ;----------------------------------------- Split Screen Module Support ------------------------------------------ ;------------------------------------------------------------------------------------------------------------- ; SplitScreenPos function ; Returns an object with the screen coordinates (posArray[screen number].x, posArray[screen number].y, posArray[screen number].w and posArray[screen number].h) for placing split screen emulator screens. ; Supports up to 8 screens ; splitScreen2PlayersMode can be Vertical or Horizontal ; splitScreen3PlayersMode can be P1top, P1left, P1right, P1bottom ; maxPlayersPerMonitor defines the maximun amount of player screens to be placed at each monitor. For example, "1|2|3" means that screen 1 will only have the player 1, screen 2 will have the player 2 and 3 and screen 3 will have the players 4, 5 and 6 screens. If the current available monitors is not able to show the selected number of players, the code will automatically increase the screen 1 amount of screens untill all players are displayed SplitScreenPos(numberofPlayers,splitScreen2PlayersMode="Horizontal",splitScreen3PlayersMode="P1top",maxPlayersPerMonitor="4|4|4"){ Global monitorTable Log("SplitScreenPos - Started") ;Making sure to distribute screens only on current available monitors and find out how many monitors are needed to show the selected number of players monitor := [] StringSplit,playerPerMonitor,maxPlayersPerMonitor,| Loop, % monitorTable.MaxIndex() { monitor[a_index] := {} currentNumberOfPlayers := playerPerMonitor%a_index% accumulatedPlayers += currentNumberOfPlayers If (accumulatedPlayers>numberofPlayers) currentNumberOfPlayers := numberofPlayers - (accumulatedPlayers-currentNumberOfPlayers) monitor[a_index].numberOfPlayers := currentNumberOfPlayers } If (accumulatedPlayers monitorTable.MaxIndex() ) count1 := 1 monitor[count1].numberOfPlayers += 1 } } ;Defining splitscreen positions posArray := [] Loop, % numberofPlayers posArray[a_index] := {} count := 0 Loop, % monitor.MaxIndex() { currentMonitor := a_index If (monitor[currentMonitor].numberOfPlayers = 1){ posArray[count+1].X := monitorTable[currentMonitor].Left , posArray[count+1].Y := monitorTable[currentMonitor].Top , posArray[count+1].W := monitorTable[currentMonitor].Width , posArray[count+1].H := monitorTable[currentMonitor].Height } Else If (monitor[currentMonitor].numberOfPlayers = 2){ If (splitScreen2PlayersMode = "Vertical"){ posArray[count+1].X := monitorTable[currentMonitor].Left , posArray[count+1].Y := monitorTable[currentMonitor].Top , posArray[count+1].W := monitorTable[currentMonitor].Width//2 , posArray[count+1].H := monitorTable[currentMonitor].Height posArray[count+2].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width//2 , posArray[count+2].Y := monitorTable[currentMonitor].Top , posArray[count+2].W := monitorTable[currentMonitor].Width//2 , posArray[count+2].H := monitorTable[currentMonitor].Height } Else { posArray[count+1].X := monitorTable[currentMonitor].Left , posArray[count+1].Y := monitorTable[currentMonitor].Top , posArray[count+1].W := monitorTable[currentMonitor].Width , posArray[count+1].H := monitorTable[currentMonitor].Height//2 posArray[count+2].X := monitorTable[currentMonitor].Left , posArray[count+2].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+2].W := monitorTable[currentMonitor].Width , posArray[count+2].H := monitorTable[currentMonitor].Height//2 } } Else If (monitor[currentMonitor].numberOfPlayers = 3){ If (splitScreen3PlayersMode = "P1left"){ posArray[count+1].X := monitorTable[currentMonitor].Left , posArray[count+1].Y := monitorTable[currentMonitor].Top , posArray[count+1].W := monitorTable[currentMonitor].Width//2 , posArray[count+1].H := monitorTable[currentMonitor].Height posArray[count+2].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width//2 , posArray[count+2].Y := monitorTable[currentMonitor].Top , posArray[count+2].W := monitorTable[currentMonitor].Width//2 , posArray[count+2].H := monitorTable[currentMonitor].Height//2 posArray[count+3].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width//2 , posArray[count+3].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+3].W := monitorTable[currentMonitor].Width//2 , posArray[count+3].H := monitorTable[currentMonitor].Height//2 } Else If (splitScreen3PlayersMode = "P1bottom") { posArray[count+1].X := monitorTable[currentMonitor].Left , posArray[count+1].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+1].W := monitorTable[currentMonitor].Width , posArray[count+1].H := monitorTable[currentMonitor].Height//2 posArray[count+2].X := monitorTable[currentMonitor].Left , posArray[count+2].Y := monitorTable[currentMonitor].Top , posArray[count+2].W := monitorTable[currentMonitor].Width//2 , posArray[count+2].H := monitorTable[currentMonitor].Height//2 posArray[count+3].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width//2 , posArray[count+3].Y := monitorTable[currentMonitor].Top , posArray[count+3].W := monitorTable[currentMonitor].Width//2 , posArray[count+3].H := monitorTable[currentMonitor].Height//2 } Else If (splitScreen3PlayersMode = "P1right") { posArray[count+1].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width//2 , posArray[count+1].Y := monitorTable[currentMonitor].Top , posArray[count+1].W := monitorTable[currentMonitor].Width//2 , posArray[count+1].H := monitorTable[currentMonitor].Height posArray[count+2].X := monitorTable[currentMonitor].Left , posArray[count+2].Y := monitorTable[currentMonitor].Top , posArray[count+2].W := monitorTable[currentMonitor].Width//2 , posArray[count+2].H := monitorTable[currentMonitor].Height//2 posArray[count+3].X := monitorTable[currentMonitor].Left , posArray[count+3].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+3].W := monitorTable[currentMonitor].Width//2 , posArray[count+3].H := monitorTable[currentMonitor].Height//2 } Else { ;top posArray[count+1].X := monitorTable[currentMonitor].Left , posArray[count+1].Y := monitorTable[currentMonitor].Top , posArray[count+1].W := monitorTable[currentMonitor].Width , posArray[count+1].H := monitorTable[currentMonitor].Height//2 posArray[count+2].X := monitorTable[currentMonitor].Left , posArray[count+2].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+2].W := monitorTable[currentMonitor].Width//2 , posArray[count+2].H := monitorTable[currentMonitor].Height//2 posArray[count+3].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width//2 , posArray[count+3].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+3].W := monitorTable[currentMonitor].Width//2 , posArray[count+3].H := monitorTable[currentMonitor].Height//2 } } Else If (monitor[currentMonitor].numberOfPlayers = 4){ posArray[count+1].X := monitorTable[currentMonitor].Left , posArray[count+1].Y := monitorTable[currentMonitor].Top , posArray[count+1].W := monitorTable[currentMonitor].Width//2 , posArray[count+1].H := monitorTable[currentMonitor].Height//2 posArray[count+2].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width//2 , posArray[count+2].Y := monitorTable[currentMonitor].Top , posArray[count+2].W := monitorTable[currentMonitor].Width//2 , posArray[count+2].H := monitorTable[currentMonitor].Height//2 posArray[count+3].X := monitorTable[currentMonitor].Left , posArray[count+3].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+3].W := monitorTable[currentMonitor].Width//2 , posArray[count+3].H := monitorTable[currentMonitor].Height//2 posArray[count+4].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width//2 , posArray[count+4].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+4].W := monitorTable[currentMonitor].Width//2 , posArray[count+4].H := monitorTable[currentMonitor].Height//2 } Else If (monitor[currentMonitor].numberOfPlayers = 5){ posArray[count+1].X := monitorTable[currentMonitor].Left , posArray[count+1].Y := monitorTable[currentMonitor].Top , posArray[count+1].W := monitorTable[currentMonitor].Width//2 , posArray[count+1].H := monitorTable[currentMonitor].Height//2 posArray[count+2].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width//2 , posArray[count+2].Y := monitorTable[currentMonitor].Top , posArray[count+2].W := monitorTable[currentMonitor].Width//2 , posArray[count+2].H := monitorTable[currentMonitor].Height//2 posArray[count+3].X := monitorTable[currentMonitor].Left , posArray[count+3].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+3].W := monitorTable[currentMonitor].Width//3 , posArray[count+3].H := monitorTable[currentMonitor].Height//2 posArray[count+4].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width//3 , posArray[count+4].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+4].W := monitorTable[currentMonitor].Width//3 , posArray[count+4].H := monitorTable[currentMonitor].Height//2 posArray[count+5].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width*2//3 , posArray[count+5].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+5].W := monitorTable[currentMonitor].Width//3 , posArray[count+5].H := monitorTable[currentMonitor].Height//2 } Else If (monitor[currentMonitor].numberOfPlayers = 6){ posArray[count+1].X := monitorTable[currentMonitor].Left , posArray[count+1].Y := monitorTable[currentMonitor].Top , posArray[count+1].W := monitorTable[currentMonitor].Width//3 , posArray[count+1].H := monitorTable[currentMonitor].Height//2 posArray[count+2].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width//3 , posArray[count+2].Y := monitorTable[currentMonitor].Top , posArray[count+2].W := monitorTable[currentMonitor].Width//3 , posArray[count+2].H := monitorTable[currentMonitor].Height//2 posArray[count+3].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width*2//3 , posArray[count+3].Y := monitorTable[currentMonitor].Top , posArray[count+3].W := monitorTable[currentMonitor].Width//3 , posArray[count+3].H := monitorTable[currentMonitor].Height//2 posArray[count+4].X := monitorTable[currentMonitor].Left , posArray[count+4].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+4].W := monitorTable[currentMonitor].Width//3 , posArray[count+4].H := monitorTable[currentMonitor].Height//2 posArray[count+5].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width//3 , posArray[count+5].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+5].W := monitorTable[currentMonitor].Width//3 , posArray[count+5].H := monitorTable[currentMonitor].Height//2 posArray[count+6].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width*2//3 , posArray[count+6].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+6].W := monitorTable[currentMonitor].Width//3 , posArray[count+6].H := monitorTable[currentMonitor].Height//2 } Else If (monitor[currentMonitor].numberOfPlayers = 7){ posArray[count+1].X := monitorTable[currentMonitor].Left , posArray[count+1].Y := monitorTable[currentMonitor].Top , posArray[count+1].W := monitorTable[currentMonitor].Width//3 , posArray[count+1].H := monitorTable[currentMonitor].Height//2 posArray[count+2].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width//3 , posArray[count+2].Y := monitorTable[currentMonitor].Top , posArray[count+2].W := monitorTable[currentMonitor].Width//3 , posArray[count+2].H := monitorTable[currentMonitor].Height//2 posArray[count+3].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width*2//3 , posArray[count+3].Y := monitorTable[currentMonitor].Top , posArray[count+3].W := monitorTable[currentMonitor].Width//3 , posArray[count+3].H := monitorTable[currentMonitor].Height//2 posArray[count+4].X := monitorTable[currentMonitor].Left , posArray[count+4].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+4].W := monitorTable[currentMonitor].Width//4 , posArray[count+4].H := monitorTable[currentMonitor].Height//2 posArray[count+5].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width//4 , posArray[count+5].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+5].W := monitorTable[currentMonitor].Width//4 , posArray[count+5].H := monitorTable[currentMonitor].Height//2 posArray[count+6].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width*2//4 , posArray[count+6].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+6].W := monitorTable[currentMonitor].Width//4 , posArray[count+6].H := monitorTable[currentMonitor].Height//2 posArray[count+7].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width*3//4 , posArray[count+7].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+7].W := monitorTable[currentMonitor].Width//4 , posArray[count+7].H := monitorTable[currentMonitor].Height//2 } Else If (monitor[currentMonitor].numberOfPlayers = 8){ posArray[count+1].X := monitorTable[currentMonitor].Left , posArray[count+1].Y := monitorTable[currentMonitor].Top , posArray[count+1].W := monitorTable[currentMonitor].Width//4 , posArray[count+1].H := monitorTable[currentMonitor].Height//2 posArray[count+2].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width//4 , posArray[count+2].Y := monitorTable[currentMonitor].Top , posArray[count+2].W := monitorTable[currentMonitor].Width//4 , posArray[count+2].H := monitorTable[currentMonitor].Height//2 posArray[count+3].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width*2//4 , posArray[count+3].Y := monitorTable[currentMonitor].Top , posArray[count+3].W := monitorTable[currentMonitor].Width//4 , posArray[count+3].H := monitorTable[currentMonitor].Height//2 posArray[count+4].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width*3//4 , posArray[count+4].Y := monitorTable[currentMonitor].Top , posArray[count+4].W := monitorTable[currentMonitor].Width//4 , posArray[count+4].H := monitorTable[currentMonitor].Height//2 posArray[count+5].X := monitorTable[currentMonitor].Left , posArray[count+5].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+5].W := monitorTable[currentMonitor].Width//4 , posArray[count+5].H := monitorTable[currentMonitor].Height//2 posArray[count+6].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width//4 , posArray[count+6].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+6].W := monitorTable[currentMonitor].Width//4 , posArray[count+6].H := monitorTable[currentMonitor].Height//2 posArray[count+7].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width*2//4 , posArray[count+7].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+7].W := monitorTable[currentMonitor].Width//4 , posArray[count+7].H := monitorTable[currentMonitor].Height//2 posArray[count+8].X := monitorTable[currentMonitor].Left+monitorTable[currentMonitor].Width*3//4 , posArray[count+8].Y := monitorTable[currentMonitor].Top+monitorTable[currentMonitor].Height//2 , posArray[count+8].W := monitorTable[currentMonitor].Width//4 , posArray[count+8].H := monitorTable[currentMonitor].Height//2 } Else { Log("SplitScreenPos - " . monitor[currentMonitor].numberOfPlayers . " is a nonsupported number of players for the split screen position calculation function.",2) } Loop, % monitor[currentMonitor].numberOfPlayers Log("SplitScreenPos - Player " . count+a_index . " window position: X=" . posArray[count+a_index].X . ", Y=" . posArray[count+a_index].Y . ", W=" . posArray[count+a_index].W . ", H=" . posArray[count+a_index].H,5) count += monitor[currentMonitor].numberOfPlayers If (count >= numberofPlayers) Break } Log("SplitScreenPos - Ended") Return posArray }