The file-preserving repack landed with the restore before the zip and skipped entirely under -Zip, to keep somebody's callsign and key bindings out of a release. It worked, but at the price of -Zip quietly wiping the settings out of dist\ - captured, then discarded. Both properties are available at once by moving the restore after the archive is taken: the zip is built from a folder with none of the player's files in it, and they go back into dist\ immediately afterwards. A fresh unzip still looks like a first run, and cutting a release costs the person cutting it nothing. Verified: edited all four files, packed with -Zip, and confirmed the archive contains none of them - 1003 entries, nothing loose at the root - while all four are still in dist\ with their edits intact. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
325 lines
15 KiB
PowerShell
325 lines
15 KiB
PowerShell
# ============================================================================
|
|
# pack-dist.ps1 - assemble a runnable Red Planet 4.12 package into dist\
|
|
# ============================================================================
|
|
#
|
|
# Collects everything the game needs at run time:
|
|
# - Release\rpl4opt.exe (+ .pdb for crash debugging)
|
|
# - game data from assets\RP411 (AUDIO, GAUGE, VIDEO, INIs, RPL4.RES,
|
|
# TEST.EGG) - but not the arcade launch scripts or the old 4.10 exe
|
|
# - libsndfile-1.dll beside the exe; OpenAL32.dll copied from the system
|
|
# when installed, with oalinst.exe included as the fallback installer
|
|
# (environ.ini is NOT shipped - the exe writes it on first run)
|
|
# - start/joyconfig scripts, HANDBOOK.html, CONTROLS.txt and a README
|
|
#
|
|
# Usage: powershell -ExecutionPolicy Bypass -File pack-dist.ps1 [-Zip] [-Fresh]
|
|
#
|
|
param(
|
|
[switch]$Zip,
|
|
# Wipe the player's files too, for testing what a first run does.
|
|
[switch]$Fresh
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$root = Split-Path -Parent $MyInvocation.MyCommand.Path
|
|
$dist = Join-Path $root 'dist'
|
|
$assets = Join-Path $root 'assets\RP411'
|
|
$exe = Join-Path $root 'Release\rpl4opt.exe'
|
|
|
|
if (-not (Test-Path $exe)) {
|
|
throw "Release\rpl4opt.exe not found - build first (see BUILD.md 2)."
|
|
}
|
|
|
|
# --- version ---------------------------------------------------------------
|
|
# Read the stamp the exe was BUILT with rather than asking git again: a
|
|
# commit between the build and the pack would otherwise have the package
|
|
# claiming a version the binary inside it does not report.
|
|
$buildHeader = Join-Path $root 'RP_L4\rpl4build.h'
|
|
if (-not (Test-Path $buildHeader)) {
|
|
throw "RP_L4\rpl4build.h not found - build first, or run stamp-version.ps1."
|
|
}
|
|
$stamp = Get-Content $buildHeader -Raw
|
|
$version = ([regex]::Match($stamp, '#define\s+RP412_VERSION\s+"([^"]+)"')).Groups[1].Value
|
|
$versionLong = ([regex]::Match($stamp, '#define\s+RP412_VERSION_LONG\s+"([^"]+)"')).Groups[1].Value
|
|
if (-not $version) { throw "could not read RP412_VERSION from $buildHeader" }
|
|
if ($stamp -match '#define\s+RP412_BUILD_DIRTY\s+1') {
|
|
Write-Warning "packing a build made from a modified tree ($versionLong)"
|
|
}
|
|
Write-Host "Version $versionLong"
|
|
|
|
# Refuse to touch a dist the game is currently running from.
|
|
$running = Get-Process rpl4opt -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.Path -like "$dist\*" }
|
|
if ($running) {
|
|
throw "rpl4opt.exe is running from $dist (PID $($running.Id)) - close the game first."
|
|
}
|
|
|
|
Write-Host "Packing into $dist"
|
|
|
|
# --- the player's own files ------------------------------------------------
|
|
# None of these ship: the game writes each one the first time it needs it and
|
|
# then leaves it alone, so a new build dropped over a folder keeps every
|
|
# setting. This script rebuilds dist\ from scratch, which would throw exactly
|
|
# those away - the one place the promise did not hold, and it is the folder we
|
|
# do most of our own testing in. Carry them across. -Fresh to start over.
|
|
$keepFiles = @('environ.ini', 'bindings.txt', 'pilot.cfg', 'mfd_layout.cfg')
|
|
$kept = @{}
|
|
if (-not $Fresh) {
|
|
foreach ($name in $keepFiles) {
|
|
$path = Join-Path $dist $name
|
|
if (Test-Path $path) { $kept[$name] = [System.IO.File]::ReadAllBytes($path) }
|
|
}
|
|
if ($kept.Count -gt 0) {
|
|
Write-Host " keeping $($kept.Keys -join ', ')"
|
|
}
|
|
} elseif (Test-Path $dist) {
|
|
Write-Host " -Fresh: the player's files go too"
|
|
}
|
|
|
|
if (Test-Path $dist) { Remove-Item -Recurse -Force $dist }
|
|
New-Item -ItemType Directory -Force "$dist\SPOOLS" | Out-Null
|
|
|
|
# --- game binary -----------------------------------------------------------
|
|
Copy-Item $exe $dist
|
|
$pdb = Join-Path $root 'Release\rpl4opt.pdb'
|
|
if (Test-Path $pdb) { Copy-Item $pdb $dist }
|
|
|
|
# steam_api.dll: the exe imports it (RP412_STEAM build). The Steam wire
|
|
# only activates with RP412STEAM=1; plain desktop runs never touch it.
|
|
Copy-Item (Join-Path $root 'extern\steamworks_sdk_164\sdk\redistributable_bin\steam_api.dll') $dist
|
|
|
|
# steam_appid.txt: until RP412 has its own AppID, Steam testing runs
|
|
# under Spacewar (480). Without this file SteamAPI_Init fails and the
|
|
# game falls back to plain TCP - which is exactly the confusing symptom
|
|
# testers hit when the file goes missing. Delete it once we ship under
|
|
# our own AppID (the Steam client provides it then).
|
|
Set-Content -Path "$dist\steam_appid.txt" -Encoding ascii -Value '480'
|
|
|
|
# --- game data -------------------------------------------------------------
|
|
foreach ($dir in 'AUDIO', 'GAUGE', 'VIDEO') {
|
|
Write-Host " copying $dir..."
|
|
Copy-Item -Recurse (Join-Path $assets $dir) $dist
|
|
}
|
|
foreach ($file in 'RPDPL.INI', 'JOYSTICK.INI', 'RPL4.RES', 'TEST.EGG',
|
|
'libsndfile-1.dll', 'oalinst.exe') {
|
|
Copy-Item (Join-Path $assets $file) $dist
|
|
}
|
|
|
|
# The controls half as plain text, for Notepad. Flattened to ASCII so it
|
|
# reads correctly there - the markdown source keeps its typography.
|
|
$controls = Get-Content (Join-Path $root 'docs\CONTROLS.md') -Raw -Encoding UTF8
|
|
foreach ($pair in @(
|
|
@([char]0x2014, '-'), @([char]0x2013, '-'), @([char]0x2018, "'"),
|
|
@([char]0x2019, "'"), @([char]0x201C, '"'), @([char]0x201D, '"'),
|
|
@([char]0x00D7, 'x'), @([char]0x2192, '->'), @([char]0x2026, '...'))) {
|
|
$controls = $controls.Replace([string]$pair[0], [string]$pair[1])
|
|
}
|
|
Set-Content -Path "$dist\CONTROLS.txt" -Encoding ascii -Value $controls
|
|
|
|
# The handbook as a page: the controls map with the diagrams, plus the
|
|
# joystick setup and what every file in the folder is for. Its source is
|
|
# the published artifact, which is a fragment - the publisher supplies
|
|
# the document shell - so wrap it to
|
|
# stand alone: without a doctype the browser drops into quirks mode, and
|
|
# without a charset the typography arrives as mojibake. Written without a
|
|
# BOM so the charset declaration is the only thing speaking.
|
|
$handbookPage = Get-Content (Join-Path $root 'docs\rp412-handbook.html') -Raw -Encoding UTF8
|
|
# Stamp the shipped copy with the build's own version. The source keeps a
|
|
# readable one for publishing; only "4.12.<n>" is touched, which on this
|
|
# page is always the version and never anything else.
|
|
$handbookPage = [regex]::Replace($handbookPage, '4\.12\.\d+', $version)
|
|
$page = @"
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
</head>
|
|
<body>
|
|
$handbookPage
|
|
</body>
|
|
</html>
|
|
"@
|
|
[System.IO.File]::WriteAllText(
|
|
"$dist\HANDBOOK.html", $page, (New-Object System.Text.UTF8Encoding $false))
|
|
|
|
# --- OpenAL runtime --------------------------------------------------------
|
|
# The exe links OpenAL32.dll (32-bit). Prefer shipping the already-installed
|
|
# runtime beside the exe; oalinst.exe covers machines where that misses.
|
|
$openal = "$env:WINDIR\SysWOW64\OpenAL32.dll"
|
|
if (-not (Test-Path $openal)) { $openal = "$env:WINDIR\System32\OpenAL32.dll" }
|
|
if (Test-Path $openal) {
|
|
Copy-Item $openal $dist
|
|
$wrap = Join-Path (Split-Path $openal) 'wrap_oal.dll'
|
|
if (Test-Path $wrap) { Copy-Item $wrap $dist }
|
|
Write-Host " OpenAL runtime copied from $(Split-Path $openal)"
|
|
} else {
|
|
Write-Warning "OpenAL32.dll not found on this system - dist relies on oalinst.exe"
|
|
}
|
|
|
|
# --- desktop configuration -------------------------------------------------
|
|
# --- desktop configuration -------------------------------------------------
|
|
# environ.ini is NOT shipped. The exe carries the template and writes it on
|
|
# first run (RPL4ENVIRON.cpp), the same way it writes bindings.txt - so a
|
|
# tester can drop a new build over an old folder and keep every setting they
|
|
# have changed. Laying one down here would overwrite their file on every
|
|
# unzip, which is the whole problem.
|
|
|
|
Set-Content -Path "$dist\start-windowed.bat" -Encoding ascii -Value @"
|
|
@echo off
|
|
rem Red Planet 4.12 - desktop prototype. Boots into the game-setup
|
|
rem front end. The cockpit fits itself to the window and keeps 16:9,
|
|
rem so a wider screen letterboxes rather than stretching. -res sets the
|
|
rem 3D render size - raise it to match a big screen for sharper pixels,
|
|
rem e.g. -res 2560 1440.
|
|
rem (Add -egg TEST.EGG to skip the menu and run the canned mission.)
|
|
cd /d "%~dp0"
|
|
start rpl4opt.exe -windowed -res 1920 1080
|
|
"@
|
|
|
|
Set-Content -Path "$dist\start-fullscreen.bat" -Encoding ascii -Value @"
|
|
@echo off
|
|
rem Red Planet 4.12 - borderless over the whole monitor. -fit sizes the
|
|
rem window to the panel AND picks the render size to match the cockpit
|
|
rem canvas it lands in, so no -res guessing: the 3D arrives 1:1 instead
|
|
rem of being stretched. Pass -res yourself to override it.
|
|
rem (Add -egg TEST.EGG to skip the menu and run the canned mission.)
|
|
cd /d "%~dp0"
|
|
start rpl4opt.exe -fit
|
|
"@
|
|
|
|
Set-Content -Path "$dist\joyconfig.bat" -Encoding ascii -Value @"
|
|
@echo off
|
|
rem Red Planet 4.12 - one-time JOYSTICK / HOTAS / rudder-pedal setup.
|
|
rem A console wizard asks you to move each control (stick, twist or
|
|
rem rudder, throttle lever, fire buttons); it works out what you moved
|
|
rem and which way it reads, and writes the joystick section of
|
|
rem bindings.txt. When it finishes the game carries on into the setup
|
|
rem screen so you can try the bindings straight away.
|
|
rem Xbox-class controllers need NO setup - they work out of the box.
|
|
rem Re-run this any time to redo the bindings; anything you have edited
|
|
rem yourself in bindings.txt is kept.
|
|
cd /d "%~dp0"
|
|
set RP412JOYCONFIG=1
|
|
start /wait rpl4opt.exe -windowed -res 1920 1080
|
|
set RP412JOYCONFIG=
|
|
"@
|
|
|
|
Set-Content -Path "$dist\README.txt" -Encoding ascii -Value @"
|
|
Red Planet $version
|
|
=================
|
|
|
|
Run start-fullscreen.bat for borderless over the whole monitor, or
|
|
start-windowed.bat to keep a title bar. No cockpit hardware needed.
|
|
If there is no sound, run oalinst.exe once.
|
|
|
|
Got a flight stick, HOTAS or rudder pedals? Run joyconfig.bat once. It
|
|
asks you to move each control, works out which axis you moved and which
|
|
way round it reads, and writes the joystick rows of bindings.txt. Xbox-
|
|
class controllers need none of that - they work out of the box.
|
|
|
|
Controls (XInput controller and/or keyboard) - EVERY input is
|
|
rebindable: edit bindings.txt beside the exe (written with the full
|
|
documented default layout on first run; delete it to restore).
|
|
|
|
Left stick / NumPad 8462 joystick (8=fwd 2=back 4=left 6=right)
|
|
LT / RT or NumPad 7,9 left / right pedal
|
|
Right stick Y throttle (holds position)
|
|
Shift / Ctrl throttle up / down
|
|
A / Space / NumPad 0 joystick trigger
|
|
RB / Alt reverse thrust
|
|
DPad / arrow keys joystick hat (look)
|
|
9 / 0 keys config buttons (pad Start/Back left free)
|
|
Number+letter rows MFD bank buttons (as printed on the panel)
|
|
F1-F12 secondary / screen columns
|
|
Alt+Q abort the mission (score banked)
|
|
|
|
The cockpit scales to whatever window you give it - maximise it and it
|
|
grows, keeping its 16:9 shape (an ultrawide gets black bars rather than
|
|
a stretched cockpit).
|
|
|
|
The 3D itself renders at whatever -res says and is then stretched onto
|
|
the cockpit's viewscreen, so a mismatched -res costs sharpness. -fit
|
|
takes care of that for you: it measures the monitor, works out the
|
|
canvas the cockpit will settle on, and asks for exactly that render
|
|
size, so the picture arrives 1:1.
|
|
|
|
rpl4opt.exe -fit borderless, res chosen for you
|
|
rpl4opt.exe -fit -res 1280 720 same window, lighter render
|
|
rpl4opt.exe -windowed -res 2560 1440 pick both yourself
|
|
|
|
(-windowed-fullscreen is accepted as a long spelling of -fit.)
|
|
|
|
The full pod cockpit comes up in a single window: three green MFDs
|
|
across the top, the 3D viewscreen centered with the orange plasma glass
|
|
at its left, and the lower MFDs flanking the portrait map. The red
|
|
buttons around each MFD and the amber buttons beside the map are the
|
|
pod's real button banks: click them with the mouse, and they light up
|
|
as the game commands their lamps.
|
|
environ.ini is self-documenting: the game writes it on first run with
|
|
every option in it and a comment on each (Steam networking, keyboard
|
|
lighting, stick inversion, LAN hosting, developer keys, display scaling
|
|
and radar placement, and more).
|
|
|
|
HANDBOOK.html is the full manual - open it in a browser for the pad,
|
|
keyboard and pod-panel diagrams, the joystick setup, and what every file
|
|
in this folder is for. CONTROLS.txt is the controls half as plain text.
|
|
|
|
Four files here are yours. None of them ship - the game writes each one
|
|
the first time it needs it and then leaves it alone, so a new build
|
|
unzipped over this folder keeps everything you have set. Delete any of
|
|
them to start that part over:
|
|
|
|
environ.ini every engine option, commented in place
|
|
bindings.txt every key, pad button, axis and joystick row
|
|
pilot.cfg your callsign and loadout
|
|
mfd_layout.cfg where you dragged the windows (RP412MFDLAYOUT)
|
|
|
|
Two that catch people out: environ.ini is applied OVER the environment,
|
|
so a variable set in a shell loses to an uncommented line in the file;
|
|
and none of these four is ever overwritten once it exists, which is what
|
|
lets you keep a folder across builds. The trade is that a file carried
|
|
through several updates stops being offered new options - rpl4.log names
|
|
any it has not heard of, and deleting the file brings back the fully
|
|
documented current one.
|
|
|
|
Known prototype notes: pods race untextured (the player1-8 skins come
|
|
from the presets system, not shipped data), and text drawn on the plasma
|
|
glass may appear rotated.
|
|
|
|
Source: https://gitea.mysticmachines.com/VWE/RP412
|
|
"@
|
|
|
|
# --- summary / optional zip ------------------------------------------------
|
|
$size = (Get-ChildItem $dist -Recurse | Measure-Object Length -Sum).Sum
|
|
Write-Host ("dist ready: {0:N1} MB" -f ($size / 1MB))
|
|
|
|
if ($Zip) {
|
|
$zipPath = Join-Path $root "RedPlanet-$version.zip"
|
|
Write-Host "zipping to $zipPath..."
|
|
|
|
# Taken BEFORE the player's files go back, so a release never carries
|
|
# somebody's callsign, key bindings or window positions to everyone who
|
|
# downloads it. A fresh unzip must look like a first run.
|
|
#
|
|
# Everything lives under a single RP412\ folder inside the zip, so
|
|
# unpacking anywhere gives one self-contained game directory instead
|
|
# of scattering files into the extraction folder.
|
|
$stage = Join-Path ([System.IO.Path]::GetTempPath()) 'rp412-zipstage'
|
|
if (Test-Path $stage) { Remove-Item -Recurse -Force $stage }
|
|
New-Item -ItemType Directory -Force "$stage\RP412" | Out-Null
|
|
Copy-Item "$dist\*" "$stage\RP412" -Recurse -Force
|
|
Compress-Archive -Path "$stage\RP412" -DestinationPath $zipPath -Force
|
|
Remove-Item -Recurse -Force $stage
|
|
}
|
|
|
|
# --- the player's own files, back where they were --------------------------
|
|
# Last of all: after the rebuilt tree, so nothing the pack writes can land on
|
|
# top of them, and after the zip, so the release stays clean. Zipping should
|
|
# not cost you your own settings.
|
|
if ($kept.Count -gt 0) {
|
|
foreach ($name in $kept.Keys) {
|
|
[System.IO.File]::WriteAllBytes((Join-Path $dist $name), $kept[$name])
|
|
}
|
|
Write-Host " restored $($kept.Keys -join ', ')"
|
|
}
|