# Custom Game Command Line for Media Capture

In some cases, you might want to use different command-line options when launching Visual Pinball (or another game system) for a media capture session, vs. the regular options for a regular game play session.

I know of one special case where this is particularly useful, which is VP's "true full screen" mode. Many people normally run VP in Exclusive Full Screen mode, because the version of DirectX that VP is based on can achieve better GPU performance in that mode. But this creates a problem for media capture operations: by its very nature, "exclusive" mode means that only one program can access the video display. So putting the display in "exclusive" mode means that FFMPEG can't capture the video, defeating the whole purpose of the media capture operation.

At least one person has proposed adding a special feature to PinballY to turn off VP's exclusive mode when running a capture. But I can't see a way to do that without building knowledge of that very specific feature of VP into PinballY. I try to avoid making PinballY know special things about VP, because I want PinballY to work with any game system you want to use it with. Special handling for VP violates that principle of generality.

So instead of building special knowledge of VP into PinballY, I'll provide this worked example showing how you can use Javascript to perform this kind of extremely specific customization. This is the only way I see that makes it general enough to apply the same idea to other systems that might need something similar.

## Launch events and command lines

The approach we're going to use is to listen for a prelaunch event. That event fires when a game is about to be launched, and it gives Javascript a chance to do all of the following:

- Determine which game is being launched
- Find out which game system is going to be used (VP, FP, etc)
- See whether we're launching for a regular game session or a media capture operation
- Change the command line as needed

So this lets us accomplish our goal here: when we see a VP X game being launched for a media capture operation, we'll sneak in and add the "disableTrueFullScreen" option to the VP command line.

This example uses a number of system objects, which you can find full details on in other chapters:

- The prelaunch event is part of the LaunchEvent group
- The GameInfo object contains information on the game being launched
- The GameSysInfo object contains information the game system (in other words, the program that we launch to play the game, such as Visual Pinball)
- Commands has a list of the command codes, which is what we use to distinguish regular "play" launches from media capture launches

Here's the script:

// Intercept the prelaunch event, which fires each time a game is // about to be launched. This fires BEFORE the launch process starts, // so it gives us an opportunity to edit the command line that will be // used to launch the program. mainWindow.on("prelaunch", ev => { // check to see if we're in Media Capture mode AND this is VP X if (ev.command == command.CaptureGo && game.system.systemClass == "VPX") { // We're launching a Visual Pinball X game in media capture mode. // Edit the command line to add the /disableTrueFullScreen option flag. ev.overrides.params = gameSysInfo.params + " /disableTrueFullScreen"; } });

The overall body of the code is simply an event listener for the prelaunch event. As mentioned above, a listener for this event has the ability to modify anything about the command line for the game that's about to be launched.

The first thing the event listener does is check the command to see if it's a media capture command (command.CaptureGo) and that the game system is Visual Pinball X. The special command-line option that we want to insert only applies to that particular situation - we wouldn't want to add it to a Future Pinball launch, for example.

Note that we detect that the game is a Visual Pinball X game by checking the "system class" of the game system that we're about to launch. The "game system" is simply PinballY's internal data record describing the program. This corresponds exactly to one of the game systems that you can edit in the Settings dialog. The "system class" is one of the game system properties that you can select in the dialog. Even though I made a big deal earlier about how PinballY tries to avoid special knowledge of Visual Pinball (or any other particular game system), there are a few special cases that PinballY does have to handle, which is why it lets you specify that a particular system is VP, VPX, FP, or Steam-based. The script takes advantage of the fact that we have this way to specially designate VPX among the system data records. If you wanted to create a similar script that detects launches for some other game system that doesn't have a special system class in PinballY, you could use one of the other GameSysInfo properties to detect the system. One way would be to identify it by name, using the displayName property of the GameSysInfo object. I consider that a little unreliable because you can really set it to anything you want to for any system; it's just there for display purposes, not for absolute identification. A better way might be to check the defExt property, which tells you the filename extension used by the system's games; that might be unique enough to be reliable. Another option would be to look at the exe property, which tells you the EXE program file's name; that might also be unique enough to reliably identify the system.

Once we've identified the system as VPX, and identified the command as a Media Capture operation, we spring into action. The goal here is to get VP not to use exclusive full-screen mode for this session. VP helpfully provides a command-line option that does exactly that, /disableTrueFullScreen. So all we have to do is append that option to the command line options that we were going to provide for a normal launch. To get the original options, we just look at the params property of the system's GameSysInfo object. That gives us the exact text that you typed into the Settings dialog for this system. In the case of VPX, we can add new option flags simply by stringing them along at the end of the rest of the command line, so we use the Javascript string "+" operator to append the option at the end.

Note that some other systems might need options to be inserted at the beginning of the command line, or pasted into the middle. This is one more reason that it's not easy to generalize this into something that we could have built into PinballY itself. Command-line syntax on Windows is inherently idiosyncratic, so we can't say in general that "new options always go at the end" or whatever else. New options go where the particular program says they go.

## Adapting the script to other game systems

The "Exclusive Full Screen" media capture issue will apply to any game system that uses Exclusive Full Screen mode. The same technique we used in the script above should be adaptable to other systems. Javascript lets you have as many prelaunch event listeners as you want, so you can just add another event listener based on the one above for each system that needs one.

The first step in adapting the event listener to a new system is to recognize the new system. If the system has a sufficiently unique filename extension that identifies its games, that should be a reliable way to identify it:

if (command == command.CaptureGo && game.system.defExt == ".somepin")

If the system doesn't use game files, or doesn't have a unique enough default extension, you could rely on the executable filename. The exe property of the GameSysInfo object gives us the full name, with drive letter and directory path prefix, so you might want to use a regular expression to just look for the main file name portion:

if (command == command.CaptureGo && /\\somepinplayer\.exe$/i.test(game.system.exe))

Once you've identified the system, all that remains is to tell the program being launched not to use Exclusive Full Screen mode for the current session. Exactly how you do that is up to the particular player program you're using:

- If the system has a command-line option similar to VPX's /disableTrueFullScreen, it's a simple matter of inserting the option into the default parameters using ev.overrides.params, just like the example above. You just have to replace VP's /disableTrueFullScreen with the corresponding option flag for the particular program. You might also have to change where it's inserted into the original parameters; VPX is happy to have the parameter tacked onto the end, but other systems might need it inserted at the beginning of the other parameters, or at some point in the middle. Each program defines its own command-line syntax, so there's no general rule to follow here; you just have to figure out how the program you're working with expects the parameter.
- Some programs might not have a command-line option for this at all, but might instead store the option in a settings file, for example, or in the Windows registry. In that case, you'd have to write Javascript code to update the program's setting file, registry keys, etc. PinballY's Javascript has access to all of the Windows API functions, so this is all doable: see Calling Native DLLs from Javascript for details on how to access native Windows functions. File system access is easiest through the Windows "Scripting.FileSystemObject" interface; see OLE Automation Objects for more on accessing that sort of thing.

  Note that if you do have to change the contents of a settings file, registry key, or something of that sort, you'll probably also want to restore the original settings after the game finishes. You can do that by setting up a separate event listener for the postlaunch event, which you can read about in the LaunchEvent chapter. That event is the end-of-game counterpart of prelaunch, so it gives you a chance to "clean up" anything that you did before launching the game. The most elegant way to handle this would be via a "once" event listener, since that sets up a handler that's called exactly once and then discarded. So the prelaunch handler would look something like this:

  mainWindow.on("prelaunch", ev => { if (command == command.CaptureGo && game.system.defExt == ".somepin") { // *** make the settings change here *** // ...and then set up an event listener to undo the settings // changes after the game exits mainWindow.once("postlaunch", ev => { // *** restore the original settings here *** }); });

  That will neatly install the cleanup event listener only when it's needed, and automatically remove the event listener after it runs. That eliminates any need for the event handler to check global variables or anything like that to see if it needs to do its work; the fact that it's running is enough to know that it should go ahead and undo the changes.