When is pitool saved settings profiles coming along?

@anon74848233 @PimaxUSA is this feature still coming along soon? It’s tiring having to fiddle with settings every time opening a different game.
A button added under each game in pitool with preferences including individual game configuration settings seems like the easiest way to do this.

8 Likes

That has been in the roadmap for a while (and I agree completely it’s among the most important pieces) but we wanted to get the baseline control settings into PiTool first prior to profiles. There are already some structural pieces in there now for that.

We’ll be putting some more info out on this later this month.

10 Likes

@mojojojo do you think we might be able to do something like this via a batch/script file? Replacing Json file of pitool settings, restarting pitool service & Steamvr to push changes to headset?

If possible to have pitool execute batchfiles/scripts.

2 Likes

Creating a backup of the current configuration and keeping one for each game is easy, it’s just making a copy and you’re done.
All that is necessary is having the hmd reload the values via restarting the PiServiceLauncher.
It’s uncertain if the setting files would have to be recreated with PiTool upgrades.

%LOCALAPPDATA%\pimax\runtime\profile.json

But it would be much more preferable for PiTool to keep them in a local database.
There aren’t that many values to keep track of at the moment, so effort for Pimax would be limited.

Optionally saving the SteamVR SS settings would also be cool.

2 Likes

Agreed but this could be a good Diy solution in the meanwhile.

2 Likes

For what it’s worth, here is a crude implementation, totally without warranty and unmaintained.

Backup:

@echo off
xcopy "%LOCALAPPDATA%\pimax\runtime\profile.json" .

Will copy the current profile.json into the folder where the batch file is located.

For the restore scripts, I’d suggest using a folder structure like this:

pimax%20-%20backup%20restore

The leading numbers are optional.

Simple restore:

@echo off
SETLOCAL
SET "profile="
SET srcfile=%profile%\profile.json
IF exist "%srcfile%" (
	echo copying "%srcfile%" to "%LOCALAPPDATA%\pimax\runtime"
	xcopy /y "%srcfile%" "%LOCALAPPDATA%\pimax\runtime"
	PAUSE	
) ELSE (
	echo ERROR: "%srcfile%" does not exist
)
ENDLOCAL

All that is necessary is to create a batch file for each game and set the “profile” parameter to the directory containing the profile.json e.g.:

SET "profile=01 IL2"

Interactive restore:

@echo off
:while
SETLOCAL
SET "profile="
SET "source="
SET cnt=0
SET /p profile="Enter profile: "
IF "%profile%"=="" (
	echo ERROR: please enter a profile
	GOTO :while
)

FOR /f "delims=?" %%A IN ('dir /ad /b "*%profile%*"') DO (
	SET source=%%A
	SET /A cnt=cnt+1
)

SET srcfile=%source%\profile.json

IF "%cnt%"=="1" (
	IF exist "%srcfile%" (
		echo copying "%srcfile%" to "%LOCALAPPDATA%\pimax\runtime"
		xcopy /y "%srcfile%" "%LOCALAPPDATA%\pimax\runtime"
		PAUSE
		GOTO :end
	) ELSE (
		echo ERROR: %srcfile% does not exist
	)
)

IF "%cnt%"=="0" (
	echo ERROR: profile "%profile%" does not exist
)
IF %cnt% gtr 1 (
	echo ERROR: profile %profile% is not unique
)
ENDLOCAL
GOTO :while

:end
ENDLOCAL

This will prompt you for a profile and search for it.

pimax%20-%20restore%20interactive

All that is necessary now is to restart the PiServiceLauncher, must be run as administrator.

7 Likes

Because backing up of the whole settings file isn’t ideal in general and also not for sharing I’ve put some scripts together to make this more feasible. Configurations for software IPD, lighthouses, etc. aren’t exported so no need to worry about overwriting your personal settings. When importing settings, the current profile.json is only modified, other settings aren’t touched.

The local settings are transformed into performance and color files, it takes a few steps to get the scripts working and it’s nothing like the oculus store but that shouldn’t discourage anyone.

Following folder structure is recommended:

pimax%20-%20backup%20restore%20folder%20structure

Having the configuration files in the resources folder is necessary.

Configuration Files:
config.json

{
	"device": "Pimax P2 5K Formal",
	"blacklist": "fov,fov_adjust_degree"
}

device must be set once with the value found in
%LOCALAPPDATA%\pimax\runtime\profile.json
e.g.: Pimax P2 5K Formal for 5K+` ,please look it up for 8K or XR

blacklist: for values you don’t want to import, in my example FOV settings

mapping.json

{
	"color": {
		"brightness_left": "$device$/color_brightness_0",
		"brightness_left_red": "$device$/color_brightness_0_rgb/x",
		"brightness_left_green": "$device$/color_brightness_0_rgb/y",
		"brightness_left_blue": "$device$/color_brightness_0_rgb/z",
		"brightness_right": "$device$/color_brightness_1_rgb",
		"brightness_right_red": "$device$/color_brightness_1_rgb/x",
		"brightness_right_green": "$device$/color_brightness_1_rgb/y",
		"brightness_right_blue": "$device$/color_brightness_1_rgb/z",
		"contrast_left": "$device$/color_contrast_0",
		"contrast_left_red": "$device$/color_contrast_0_rgb/x",
		"contrast_left_green": "$device$/color_contrast_0_rgb/y",
		"contrast_left_blue": "$device$/color_contrast_0_rgb/z",
		"contrast_right": "$device$/color_contrast_1_rgb",
		"contrast_right_red": "$device$/color_contrast_1_rgb/x",
		"contrast_right_green": "$device$/color_contrast_1_rgb/y",
		"contrast_right_blue": "$device$/color_contrast_1_rgb/z"
	},
	"performance": {
		"render_quality": "pixels_per_display_pixel_rate",
		"refresh_rate": "$device$/display_timing_selection",
		"fov": "$device$/fov_level",
		"fov_adjust_degree": "$device$/fov_outer_adjust_degree",
		"parallel_projection_disabled": "steamvr_use_native_fov",
		"smart_smoothing": "dbg_asw_enable",
		"hidden_area_mask": "dbg_hidden_area_enable",
		"foveated_rendering": "enable_foveated_rendering",
		"vive_support": "support_vive_only_games"
	}
}

Please just copy as is.

Backup Script:

$profilefile = "$env:LOCALAPPDATA\pimax\runtime\profile.json"
$resourcesDir = "resources"
$config = Get-Content "$resourcesDir\config.json" | ConvertFrom-Json
$device = $config."device"
$mapping = Get-Content "$resourcesDir\mapping.json" | ConvertFrom-Json

$pathSettingsHash = @{}

foreach($categoryName in $mapping.psobject.properties.name) {
    $category = $mapping.$categoryName
    foreach($settingName in $category.psobject.properties.name) {
        $path = $category.$settingName -replace '\$device\$', $device
        $pathSettingsHash.add($path, $settingName)
    }
}

$settingsHash = @{}

function Extract-Settings-From-Profile-Node {
    Param ([PSCustomObject]$nodeObj, [string]$path)
    $nodeObj.psobject.Properties | ForEach-Object {
        $attributePath = $_.Name;
        if ($path) {
            $attributePath = "$path/$attributePath"
        }
        if ($_.TypeNameOfValue -eq 'System.Management.Automation.PSCustomObject') {
            Extract-Settings-From-Profile-Node -nodeObj ($nodeObj | Select -ExpandProperty $_.Name) -path $attributePath
        } elseif ($pathSettingsHash.ContainsKey($attributePath)) {
            $settingsHash.Add($pathSettingsHash.$attributePath, $_.Value)
        }
    }
}

$profileObj = (Get-Content $profilefile) `
	-replace '""\s*:','"a":' `
	| ConvertFrom-Json

Extract-Settings-From-Profile-Node -nodeObj $profileObj -path $null

foreach($categoryName in $mapping.psobject.properties.name) {
    $category = $mapping.$categoryName
    $settingsObject = [PSCustomObject]@{}
    foreach($settingName in $category.psobject.properties.name) {
        if ($settingsHash.ContainsKey($settingName)) {
            $settingsObject | Add-Member -MemberType NoteProperty -Name $settingName -Value $settingsHash.$settingName
        }
    }
    $settingsObject | ConvertTo-Json | Set-Content "$categoryName.json"
}

# cmd /c pause | out-null

Save as backup.ps1 and must be executed with right click on file: Run with PowerShell

param (
    [string]$dir = ""
)
$resourcesDir = "resources"
$sourceDir = $dir
$profilefile = "$env:LOCALAPPDATA\pimax\runtime\profile.json"
$config = Get-Content "$resourcesDir\config.json" | ConvertFrom-Json
$device = $config.'device'
$blacklistHash = @{}

if (Get-Member -inputobject $config -name 'blacklist' -Membertype NoteProperty) {
    $config.'blacklist'.Split(',') | ForEach {
        $key = $_.Trim()
        if ($key) {
            $blacklistHash.Add($key, $true);
        }
    }
}

$mapping = Get-Content "$resourcesDir\mapping.json" | ConvertFrom-Json

$pathSettingsHash = @{}
$categories = @()

foreach($categoryName in $mapping.psobject.properties.name) {
    $category = $mapping.$categoryName
    $categories += $categoryName
    foreach($settingName in $category.psobject.properties.name) {
        $path = $category.$settingName -replace '\$device\$', $device
        if (!$blacklistHash.ContainsKey($settingName)) {
            $pathSettingsHash.add($settingName, $path)
        }
    }
}

function Find-Settings-Directory {
    Param ([string]$dir)
    $count = 0
    $resultDir = ""
    Get-ChildItem . -Directory -Filter "*$dir*" | ForEach {
        $curDir = $_.Name
        $categories | Foreach-Object {
            $file = "$curDir\$_.json"
            if ([System.IO.File]::Exists($file)) {
                $resultDir = $curDir
            }
        }
        $count++
    }
    if ($count -gt 1) {
        Write-Host "ERROR: more than one matching directory found"
        return ""
    } elseif ($count -eq 0) {
        Write-Host "ERROR: no matching directory found"
    }
    return $resultDir
}


while (!$sourceDir) {
    $sourceDir = Read-Host -Prompt 'Enter profile'
    $sourceDir = $sourceDir.Trim()
    $sourceDir = Find-Settings-Directory -dir $sourceDir
}

function Convert-File-To-Profile {
    Param ([string]$file, [PSCustomObject]$targetObj)

    $hasConvertedProperties = $false
    $jsonObj = Get-Content $file | ConvertFrom-Json

    $jsonObj.psobject.Properties | ForEach-Object {
        $settingName = $_.Name;
        if ($pathSettingsHash.ContainsKey($settingName)) {
            $path = $pathSettingsHash.$settingName
            $settingsBranch = $path.Split('/')
            $currentNode = $targetObj
            for ($i=0; $i -lt $settingsBranch.Length - 1; $i++) {
                $nodeName = $settingsBranch[$i]
                if (!(Get-Member -inputobject $currentNode -name $nodeName -Membertype NoteProperty)) {
                    $newNode = [PSCustomObject]@{}
                    $currentNode | Add-Member -MemberType NoteProperty -Name $nodeName -Value $newNode
                }
                $currentNode = $currentNode | Select -ExpandProperty $nodeName
            }
            $nodeName = $settingsBranch[$settingsBranch.Length - 1]
            $currentNode | Add-Member -MemberType NoteProperty -Name $nodeName -Value $_.Value -Force
            $hasConvertedProperties = $true
        }
    }
    return $hasConvertedProperties
}

$profileJsonObj = (Get-Content $profilefile) `
	-replace '""\s*:','"a":' `
	| ConvertFrom-Json

$hasConvertedProperties = $false

$categories | Foreach-Object {
    $file = "$sourceDir\$_.json"
    if ([System.IO.File]::Exists($file)) {
        Write-Host "Reading settings file '$file'"
        $hasConvertedProperties = (Convert-File-To-Profile -file $file -targetObj $profileJsonObj) -or $hasConvertedProperties
    }
}

if (!$hasConvertedProperties) {
    Write-Host "No properties could be read, exiting"
    cmd /c pause | out-null
    exit
}

($profileJsonObj | ConvertTo-Json) `
	-replace '"a"\s*:','"":' `
	| Set-Content $profilefile

Write-Host "Profile has been updated"

cmd /c pause | out-null

Save as restore.ps1 and must be executed with right click on file: Run with PowerShell
This will bring up interactive mode and ask you for a directory which contains the exported setting files.

For convenience it also takes a -dir parameter to restore often used settings more quickly by creating a dedicated intermediate script for it.

Example

& .\restore.ps1 -dir "DCS"

Save as anything you like.ps1 and change DCS to the profile you want to restore, must be executed with right click on file: Run with PowerShell

Restart PiServiceLauncher after restoring settings and you’re done.

Only one of the backup files needs to be present for restoring settings.

Here are example files that the backup script created.

color.json

{
    "brightness_left":  -0.2,
    "brightness_left_red":  -0.2,
    "brightness_left_green":  -0.2,
    "brightness_left_blue":  -0.2,
    "brightness_right_red":  -0.2,
    "brightness_right_green":  -0.2,
    "brightness_right_blue":  -0.2,
    "contrast_left":  -0.1000000014901161,
    "contrast_left_red":  -0.2000000029802322,
    "contrast_left_green":  -0.2000000029802322,
    "contrast_left_blue":  -0.2000000029802322,
    "contrast_right_red":  -0.2000000029802322,
    "contrast_right_green":  -0.2000000029802322,
    "contrast_right_blue":  -0.2000000029802322
}

performance.json

{
    "render_quality":  1,
    "refresh_rate":  0,
    "fov":  2,
    "fov_adjust_degree":  20,
    "parallel_projection_disabled":  1,
    "smart_smoothing":  0,
    "hidden_area_mask":  1,
    "foveated_rendering":  1,
    "vive_support":  1
}

Note:
created for PiTool version .129
detailed FFR settings are excluded because I haven’t got access to them
Of course use at your own risk.

Edit:
shortened the restore script

6 Likes

Awesome work! :beers::sunglasses::+1::sparkles:

1 Like

Hope it’s swiftly made obsolete by amazing new PiTool features Pimax will add any day now :wink:

2 Likes

You’re a HACKER! :skull_and_crossbones::skull_and_crossbones::skull_and_crossbones:

2 Likes

Only rewriting configuration files for fun and not much profit in this case :crazy_face:

2 Likes