Utility to create direct-launch EXE files for RPCS3 PS3 games

Cross posted from the RPCS3 forums.

I’ve written a small utility that may prove useful to other users: It parses the games.yml along with the params.sfo files on your virtual HDD and creates an EXE with the correct name and icon.

rpcs3-stub-generator on GitHub
(The above link will take you straight to the release page where you can download binaries)

The reason it was written was simply that now that I wanted different Steam controller configs for different games (in particular, it’s great to play lightgun games with a gyroscope)… and Steam needs different EXE files for that.

Usage is simple:
Extract, run the GUI application, point it to your RPCS3 folder and the place where you want the EXE files created. If you don’t want it to create stubs for all files select the ones you want in the list, press Generate, done.
If you want to update with your last settings just run the commandline application.

Features:

  • Creates EXE files for PS3 games
  • Finds disc games added to RPCS3 from games.yml
  • Finds installed games by looking through the emulated HDD (SFO parsing included)
  • Creates correct icon for each game
  • Uses game title for filename
  • Includes GUI application for setup and filtering
  • Commandline application for automatic creation

Hope it’s useful to others as well.

Simple tools are sometimes the most useful

With a kid in the house during the holidays fighting the volume of various devices has become one of my main struggles.

This has actually lead to all kinds of strange tools, but one of the most effective is also one of the simplest. A tiny little C# program which polls the volume of our FireTV and adjusts the volume if it exceeds a certain limit, by default 0.5.

If you’re a parent and comfortable using ADB, you can get the binary and source at https://github.com/hansschmucker/FireTV-VolumeLimiter/releases

I have to be brutally honest here… I’m at a total loss why no OS manufacturer includes this out of the box. We have iOS, macOS, Windows, Android, FireOS and not a single one allows setting the maximum volume level for restricted users.

Commandline: Changing resolution

Just had a common issue this morning that would usually require installing an application, but is very easy to solve using the batch file (GIST) from Thursday’s post:

Changing the resolution from a batch file. Specifically, I wanted to lower my display’s resolution whenever I connect via VNC. The first part is simple: Attach a task to the System Event generated by TightVNC Server (Ok, not that easy… this actually involves using Microsoft’s bizarre XPath subset, since TightVNC’s events are not quite as specific as they should be), then set this task to run a batch file.

Now, for some reason, Microsoft doesn’t include anything to do something as simple as setting the resolution by any other means than calling into USER32.DLL directly… and that call is too complex for little old RunDLL32.exe. .NET can’t do it either without calling into USER32.dll. But at least it makes doing so pretty straightforward.

Declare a struct that matches Windows display properties (no need to declare all fields, I just use dummy byte arrays for any fields that I’m not interested in), then call EnumDisplaySettings to retrieve the current settings into that struct. Change the resolution of the retrieved information and pass it back to ChangeDisplaySettings and voilà.

This is also a good example of how to use arguments with C#.CMD. Just don’t. Save them to environment variables instead and retrieve them via System.Environment.GetEnvironmentVariable . SETLOCAL/ENDLOCAL will keep these environment variables from leaking into other parts of your script.

(GIST)

@ECHO OFF
SETLOCAL
SET RES_X=%1
SET RES_Y=%2
echo @^
    using System.Runtime.InteropServices;^
    [StructLayout(LayoutKind.Sequential)]^
    public struct DispSet {^
        [MarshalAs(UnmanagedType.ByValArray,SizeConst=106)]^
            byte[] padding0;^
        public int width, height;^
        [MarshalAs(UnmanagedType.ByValArray,SizeConst=40)]^
            byte[] padding1;^
    };^
    public class App {^
        [DllImport("user32.dll")] public static extern^
        int EnumDisplaySettings(string a, int b, ref DispSet c);^
        [DllImport("user32.dll")] public static extern^
        int ChangeDisplaySettings(ref DispSet a, int b);^
        public static void Main() {^
            var disp = new DispSet();^
            if ( EnumDisplaySettings(null, -1, ref disp) == 0)^
                return;^
            disp.width=int.Parse(System.Environment^
                .GetEnvironmentVariable("RES_X"));^
            disp.height=int.Parse(System.Environment.^
                GetEnvironmentVariable("RES_Y"));^
            ChangeDisplaySettings(ref disp, 1);^
        }^
    }^
 |c#
ENDLOCAL

 

Assuming you have C#.CMD somewhere in your path, you can now simply call this batch file with horizontal resolution as first argument and vertical as second.

Using C# in Batch files

Batch Files

People usually smile when I say that some parts of our network like the filtering of system events are held together by batch files. It just seems so arcane, but there are some big benefits:

You don’t have to compile anything, you always know where the source code is and you can simply copy them between machines without having to set up anything. And since it’s more or less a legacy technology, Microsoft isn’t really changing a lot anymore, so there’s little chance of an upgrade breaking a batch file.

The only problem is: cmd.exe shell syntax is a horrible, horrible mess and even the most basic string functions can take ages to implement… plus, the code you’ll write look like gibberish to anybody else no matter what you do. Plus there’s the horrible, horrible string escaping behavior and the very strange behavior of variables.

 

PowerShell

So, Microsoft started developing a replacement: PowerShell.exe . And functionality-wise it’s wonderful… it can be run interactively, it doesn’t need compilation, it has useful variables, it can access the system’s .NET libraries… it all sounds wonderful… until you try to run the darn thing. Let’s just say: The syntax is frighteningly bad, never mind the documentation plus the fact that for some bizarre reason you’re allowed to run batch files or EXE files, but you need to set an additional policy before you’re allowed to run PowerShell scripts!

 

The C# Compiler

But enough ranting. Thankfully there’s an alternative that’s preinstalled on all modern Windows System: The C# compiler. Yes, it’s there, even if you don’t have VisualStudio installed. Just enter

dir “%WINDIR%\Microsoft.NET\Framework\v4*”

on the commandline and you’ll see the directory of all installed .NET 4 frameworks, each containing CSC.EXE, which is the C# compiler.

Now, you could just use that, but that means a whole lot of temp files since you can’t pipe to CSC.EXE and you can’t run the code immediately. However there’s another way to access it: Through .NET itself via System.CodeDom.Compiler.CodeDomProvider .

 

Using PowerShell to access the C# Compiler

Thankfully, there’s one thing that PowerShell gets right: Giving you access to .NET . It’s not a pleasant experience, but it is possible. And there’s another thing PowerShell gets right: it allows piping anything to it. So we can use a little PowerShell script that invokes CodeDomProvider.CreateProvider to compile our code on the fly and run it immediately.

It’s really pretty simple:

GIST

$opt = New-Object System.CodeDom.Compiler.CompilerParameters;
$opt.GenerateInMemory = $true;
$cr = [System.CodeDom.Compiler.CodeDomProvider]::CreateProvider
   ("CSharp").CompileAssemblyFromSource($opt,
   "public class App { public static void Main() { "+ $input+" } }");
if($cr.CompiledAssembly){
    $obj = $cr.CompiledAssembly.CreateInstance("App");
    $obj.GetType().GetMethod("Main").Invoke($obj, $null);
}else{
    $cr.errors;
}

It’s really very straight forward. Take STDIN, wrap it in a Main function, compile it, run it, report error if there was one during compilation.Through the magic of horrible cmd.exe paramter escaping, this looks a bit differently when passed directly to PowerShell.exe (3 quotes), but you should still be able to recognize it. Just put it in any old batch file (I’m using c#.cmd which I also added to my system’s PATH variable so that I don’t have to enter the whole path each time), but be sure to put it in a single line, because even escaping the linebreak with “^” won’t work for arguments of PowerShell.exe :

GIST

@PowerShell -Command " $opt = New-Object System.CodeDom.Compiler.
 CompilerParameters; $opt.GenerateInMemory = $true; $cr = [System.
 CodeDom.Compiler.CodeDomProvider]::CreateProvider("""CSharp""").
 CompileAssemblyFromSource($opt,"""public class App { public
 static void Main() { """+ $input+""" } }"""); if(
 $cr.CompiledAssembly) {$obj = $cr.CompiledAssembly.
 CreateInstance("""App"""); $obj.GetType().GetMethod("""Main""").
 Invoke($obj, $null);}else{ $cr.errors; } "

Horrible, I know. But it works.

 

Including C# inline in batch files

Now, if you want to actually include any C# in your batch file, it’s surprisingly straight-forward since the cmd.exe ECHO command actually has very straight forward escaping rules. Well, except for | and & , which you best avoid by using .Equals() . But new lines just need to be escaped with a “^” at the end of the line and a space before the final pipe character. OK, that sounds way worse than it actually is:

@echo ^
var a="Hello";^
var b="World";^
var foo=(a+" "+b).ToUpper();^
System.Console.WriteLine(foo);^
if(System.IO.File.Exists("C#.cmd")){^
    System.Console.WriteLine("Hey, you named it C#.cmd too :)");^
}^
 |c#

That’s what a typical call would look like. Again, note the “^” at the end of each line and the space before “|c#”. Remember this and you will be fine. Of course, you can also put the CSharp code in a separate file and use @TYPE to pipe it directly to C#.CMD, so it won’t need any escaping.

 

Issues

Well, there’s obviously the issue of escaping your code if you use ECHO to include it inline, but I really don’t think there’s any way to avoid it.

There are some issues which are mostly due to the C# code running inside the PowerShell process, rather than the CMD.EXE process. Most importantly: You cannot set environment variables without setting them user- or system-wide. You can set the environment variables of the PowerShell process, but these won’t be visible to the parent CMD.EXE process either. Your only way out is to use STDOUT and STDERR and FOR /F to move it to a variable. If that doesn’t work (which may be the case if you want to include the code inline, because escaping inside a CMD.EXE FOR call is incredibly difficult), you’ll need to transport the information using the filesystem.

And since we’re piping the code to PowerShell, STDIN will obviously not be available… so no ReadLine().

 

TODO

Well, obviously support for commandline arguments would be nice at some point, but I haven’t needed it so far.

It would also be nice if the PowerShell could add the class/main wrapper only if there is no method given in the source code. For now I’m simply using two different batch files, c#.cmd and c#full.cmd

 

Hopefully this will make your life a bit easier 🙂