Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions .github/workflows/build-v1.4-kfps-differential-decoder-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
name: Build FH6 Assistant v1.4 Decoder Differential Test

on:
workflow_dispatch:
push:
branches:
- v1.4-kfps-differential-decoder-test
paths:
- ".github/workflows/build-v1.4-kfps-differential-decoder-test.yml"
- "source-v1.2/**"
pull_request:
branches:
- v1.4-kfps-3.1.31-clean-decoder-test
paths:
- ".github/workflows/build-v1.4-kfps-differential-decoder-test.yml"
- "source-v1.2/**"

permissions:
contents: read

jobs:
build-and-test:
runs-on: windows-latest

steps:
- name: Checkout differential branch
uses: actions/checkout@v6

- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: "3.12"

- name: Vendor KFPS 3.1.31 current backend
shell: pwsh
run: |
$expected = "004b3b61a57d901e65957b6099805835f91e32f6"
$vendor = Join-Path $PWD "source-v1.2\vendor\kfps"
if (Test-Path $vendor) { Remove-Item $vendor -Recurse -Force }
New-Item -ItemType Directory -Force -Path (Split-Path $vendor) | Out-Null

git clone --filter=blob:none --no-checkout https://github.com/heyitshestia/kloudys-forza-painter-suite.git $vendor
git -C $vendor sparse-checkout init --no-cone
@'
/json_preview_renderer.py
/geometry_json.py
/kfps_shapes/
/LICENSE
/tools/cgroup/
/tools/livery/
/tools/fabric-editor/shape-words.json
/tools/fabric-editor/shape-names.json
/tools/fabric-editor/Resources/Vinyls/
'@ | Set-Content -Path (Join-Path $vendor ".git\info\sparse-checkout") -Encoding ascii
git -C $vendor fetch --depth 1 origin $expected
git -C $vendor checkout --detach FETCH_HEAD
$actual = (git -C $vendor rev-parse HEAD).Trim()
if ($actual -ne $expected) { throw "Unexpected current KFPS commit: $actual" }
Write-Host "Current KFPS backend: $actual"

- name: Vendor KFPS 3.1.27 legacy decoder
shell: pwsh
run: |
$expected = "8965780b8966e09d2f2a17e4d0684cdd44d7437c"
$vendor = Join-Path $PWD "source-v1.2\vendor\kfps_legacy"
if (Test-Path $vendor) { Remove-Item $vendor -Recurse -Force }
New-Item -ItemType Directory -Force -Path (Split-Path $vendor) | Out-Null

git clone --filter=blob:none --no-checkout https://github.com/heyitshestia/kloudys-forza-painter-suite.git $vendor
git -C $vendor sparse-checkout init --no-cone
@'
/LICENSE
/tools/cgroup/forza_source_decoder.py
/tools/cgroup/shape_identity.py
'@ | Set-Content -Path (Join-Path $vendor ".git\info\sparse-checkout") -Encoding ascii
git -C $vendor fetch --depth 1 origin $expected
git -C $vendor checkout --detach FETCH_HEAD
$actual = (git -C $vendor rev-parse HEAD).Trim()
if ($actual -ne $expected) { throw "Unexpected legacy KFPS commit: $actual" }
Write-Host "Legacy KFPS decoder: $actual"

- name: Install dependencies
shell: pwsh
run: |
Set-Location source-v1.2
python -m pip install --upgrade pip
python -m pip install -r requirements.txt
python -m pip install --upgrade pyinstaller

- name: Compile and run regression suite
shell: pwsh
env:
QT_QPA_PLATFORM: offscreen
run: |
Set-Location source-v1.2
python -m compileall -q .
python -m unittest discover -s tests -v

- name: Build differential EXE
shell: pwsh
run: |
Set-Location source-v1.2
python -m PyInstaller --clean --noconfirm .\FH6_Assistant_v1.4.spec
$exe = Join-Path $PWD "dist\FH6 Assistant v1.4.exe"
if (-not (Test-Path $exe)) { throw "FH6 Assistant v1.4.exe was not created." }
$size = (Get-Item $exe).Length
if ($size -lt 10000000) { throw "Built EXE is unexpectedly small: $size bytes" }
Write-Host "Built EXE bytes: $size"

- name: Prepare differential artifact
shell: pwsh
run: |
$source = Resolve-Path "source-v1.2\dist\FH6 Assistant v1.4.exe"
$name = "FH6 Assistant v1.4 Decoder Differential Test.exe"
New-Item -ItemType Directory -Force -Path artifact | Out-Null
$target = Join-Path $PWD "artifact\$name"
Copy-Item $source $target -Force
$hash = (Get-FileHash $target -Algorithm SHA256).Hash.ToLower()
"$hash $name" | Set-Content -Path "artifact\$name.sha256" -Encoding ascii
Write-Host "SHA-256: $hash"

- name: Upload differential build
uses: actions/upload-artifact@v6
with:
name: FH6_Assistant_v1.4_Decoder_Differential_Test
path: |
artifact/FH6 Assistant v1.4 Decoder Differential Test.exe
artifact/FH6 Assistant v1.4 Decoder Differential Test.exe.sha256
if-no-files-found: error
compression-level: 0
18 changes: 16 additions & 2 deletions source-v1.2/FH6_Assistant_v1.4.spec
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@ from pathlib import Path

project_root = Path(SPECPATH)
vendor_root = project_root / 'vendor' / 'kfps'
legacy_root = project_root / 'vendor' / 'kfps_legacy'

if not vendor_root.is_dir():
raise SystemExit(
'Pinned KFPS livery preview backend is missing. '
'Run the v1.4 build workflow or populate source-v1.2/vendor/kfps first.'
)
if not legacy_root.is_dir():
raise SystemExit(
'Legacy KFPS 3.1.27 decoder bundle is missing. '
'Run the differential build workflow or populate source-v1.2/vendor/kfps_legacy first.'
)

required_vendor_files = [
vendor_root / 'json_preview_renderer.py',
Expand All @@ -27,16 +33,24 @@ required_vendor_files = [
vendor_root / 'tools' / 'fabric-editor' / 'Resources' / 'Vinyls',
vendor_root / 'LICENSE',
]
missing = [str(path) for path in required_vendor_files if not path.exists()]
required_legacy_files = [
legacy_root / 'tools' / 'cgroup' / 'forza_source_decoder.py',
legacy_root / 'tools' / 'cgroup' / 'shape_identity.py',
legacy_root / 'LICENSE',
]
missing = [str(path) for path in [*required_vendor_files, *required_legacy_files] if not path.exists()]
if missing:
raise SystemExit('Incomplete KFPS preview backend:\n' + '\n'.join(missing))
raise SystemExit('Incomplete KFPS differential backend:\n' + '\n'.join(missing))

datas = [
(str(project_root / 'data' / 'car_names.json'), 'data'),
(str(project_root / 'icons' / 'FH6_Assistant.ico'), 'icons'),
(str(vendor_root / 'tools' / 'fabric-editor' / 'shape-words.json'), 'tools/fabric-editor'),
(str(vendor_root / 'tools' / 'fabric-editor' / 'Resources' / 'Vinyls'), 'tools/fabric-editor/Resources/Vinyls'),
(str(vendor_root / 'LICENSE'), 'vendor/kfps'),
(str(legacy_root / 'tools' / 'cgroup' / 'forza_source_decoder.py'), 'vendor/kfps_legacy/tools/cgroup'),
(str(legacy_root / 'tools' / 'cgroup' / 'shape_identity.py'), 'vendor/kfps_legacy/tools/cgroup'),
(str(legacy_root / 'LICENSE'), 'vendor/kfps_legacy'),
]
shape_names = vendor_root / 'tools' / 'fabric-editor' / 'shape-names.json'
if shape_names.is_file():
Expand Down
14 changes: 8 additions & 6 deletions source-v1.2/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from fh6garage.livery_render_integrity_patch import apply_livery_render_integrity_patch
from fh6garage.livery_preview_ui_polish import apply_livery_preview_ui_polish
from fh6garage.livery_kfps_3_1_31_clean_baseline import apply_kfps_3_1_31_clean_baseline
from fh6garage.livery_decoder_differential_test import apply_livery_decoder_differential_test


def resource_root() -> Path:
Expand All @@ -57,9 +58,9 @@ def main() -> int:
settings = QSettings()
set_language(settings.value("language", DEFAULT_LANGUAGE, str))

# Clean-decoder A/B test: rely on KFPS 3.1.31's upstream C_livery grammar.
# Do not install any FH6 Assistant decoder recovery, transform, section,
# compact-shape, structural-audit, or source-offset-order patch here.
# The visible preview stays on clean KFPS 3.1.31. The differential test
# separately loads the previous 3.1.27 decoder plus the proven FH6 Assistant
# decoder rules and compares both results without switching runtime decoder.

apply_v1_3_ui_patches(MainWindow)
apply_v1_3_1_patches(MainWindow)
Expand All @@ -70,16 +71,17 @@ def main() -> int:
apply_v1_4_quality_pipeline_patch(MainWindow)
apply_v1_4_preview_final_ui_patch(MainWindow)

# Keep the established v1.4 rendering/UI pipeline. Only the C_livery decoder
# source changes in this test; warning-only integrity and persistent quality
# scale remain enabled without rewriting decoded layer order.
apply_livery_tiled_runtime_patch()
apply_livery_render_acceleration_patch()
apply_livery_raster_runtime_patch()
apply_livery_render_integrity_patch()
apply_livery_preview_ui_polish()
apply_kfps_3_1_31_clean_baseline()

# Diagnostic-only wrapper. Opening a livery preview emits a JSON comparison
# under LocalAppData and then opens the normal clean 3.1.31 preview.
apply_livery_decoder_differential_test(MainWindow)

root = resource_root()
icon_path = root / "icons" / "FH6_Assistant.ico"
if icon_path.is_file():
Expand Down
Loading
Loading