Add RR3 Asset Extraction & Management System
Cross-Platform Scripts: - extract_z_asset.sh: Linux/Unix single file extraction - batch_extract_z_assets.sh: Linux/Unix batch extraction - pack_z_asset.sh: Linux/Unix asset packing - extract_z_asset.ps1: Windows PowerShell extraction Server Integration: - AssetExtractionService.cs: C# service for ZLIB extraction/packing - AssetManagementController.cs: API endpoints for asset management - POST /api/AssetManagement/extract - POST /api/AssetManagement/pack - POST /api/AssetManagement/batch-extract - GET /api/AssetManagement/list - Registered AssetExtractionService in Program.cs Features: - Extracts .z files (ZLIB compressed textures/data) - Packs files to .z format with ZLIB compression - Batch processing support - Cross-platform (Windows/Linux/macOS) - Server-side API for remote asset management - Path traversal protection Documentation: - ASSET_EXTRACTION_GUIDE.md: Complete integration guide - Tools/README.md: CLI tool documentation Based on: Tankonline/Real-Racing-3-Texture-Extraction-Tool Converted to cross-platform bash/PowerShell scripts + C# service Ready for .pak asset extraction when files arrive from community Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
106
Tools/extract_z_asset.ps1
Normal file
106
Tools/extract_z_asset.ps1
Normal file
@@ -0,0 +1,106 @@
|
||||
# RR3 Asset Extraction PowerShell Script
|
||||
# Extracts .z (ZLIB compressed) texture files from Real Racing 3
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]$InputFile,
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[string]$OutputDir
|
||||
)
|
||||
|
||||
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
|
||||
Write-Host " RR3 Asset Extraction Tool - Windows" -ForegroundColor Yellow
|
||||
Write-Host "════════════════════════════════════════════════════════════" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
# Validate input
|
||||
if (-not (Test-Path $InputFile)) {
|
||||
Write-Host "ERROR: Input file not found: $InputFile" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Default output dir
|
||||
if (-not $OutputDir) {
|
||||
$OutputDir = Split-Path -Parent $InputFile
|
||||
}
|
||||
|
||||
Write-Host "Input file: $InputFile" -ForegroundColor Green
|
||||
Write-Host "Output directory: $OutputDir" -ForegroundColor Green
|
||||
Write-Host ""
|
||||
|
||||
# Create output directory
|
||||
New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null
|
||||
|
||||
# Read file
|
||||
Write-Host "Reading file..." -ForegroundColor Cyan
|
||||
$data = [System.IO.File]::ReadAllBytes($InputFile)
|
||||
|
||||
$output = New-Object System.Collections.ArrayList
|
||||
$i = 0
|
||||
$found = 0
|
||||
|
||||
Write-Host "Scanning for ZLIB blocks..." -ForegroundColor Cyan
|
||||
|
||||
# Scan for zlib blocks (magic bytes 0x78 0x9C, 0xDA, or 0x01)
|
||||
while ($i -lt ($data.Length - 2)) {
|
||||
if ($data[$i] -eq 0x78 -and ($data[$i+1] -eq 0x9C -or $data[$i+1] -eq 0xDA -or $data[$i+1] -eq 0x01)) {
|
||||
try {
|
||||
# Try to decompress from this position
|
||||
$compressed = $data[$i..($data.Length-1)]
|
||||
$decompressed = [System.IO.Compression.DeflateStream]::new(
|
||||
[System.IO.MemoryStream]::new($compressed, 2, $compressed.Length - 2),
|
||||
[System.IO.Compression.CompressionMode]::Decompress
|
||||
)
|
||||
|
||||
$ms = New-Object System.IO.MemoryStream
|
||||
$decompressed.CopyTo($ms)
|
||||
$decompressed.Close()
|
||||
|
||||
$block = $ms.ToArray()
|
||||
$ms.Close()
|
||||
|
||||
if ($block.Length -gt 0) {
|
||||
$output.AddRange($block)
|
||||
Write-Host " [+] Block $found at 0x$($i.ToString('X'))" -ForegroundColor Gray
|
||||
$found++
|
||||
$i += $block.Length
|
||||
continue
|
||||
}
|
||||
} catch {
|
||||
# Not a valid zlib block, continue
|
||||
}
|
||||
}
|
||||
$i++
|
||||
}
|
||||
|
||||
if ($found -eq 0) {
|
||||
Write-Host "ERROR: No valid zlib blocks found!" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Get output filename
|
||||
$baseName = [System.IO.Path]::GetFileName($InputFile)
|
||||
if ($baseName.ToLower().EndsWith(".z")) {
|
||||
$baseName = $baseName.Substring(0, $baseName.Length - 2)
|
||||
}
|
||||
|
||||
$outputFile = Join-Path $OutputDir $baseName
|
||||
|
||||
# Backup if exists
|
||||
if (Test-Path $outputFile) {
|
||||
$bakFile = "$outputFile.bak"
|
||||
if (-not (Test-Path $bakFile)) {
|
||||
Move-Item $outputFile $bakFile
|
||||
Write-Host "Backup created: $bakFile" -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
|
||||
# Write output
|
||||
[System.IO.File]::WriteAllBytes($outputFile, $output.ToArray())
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "✅ Extraction complete!" -ForegroundColor Green
|
||||
Write-Host "Output: $outputFile" -ForegroundColor White
|
||||
Write-Host "Blocks found: $found" -ForegroundColor White
|
||||
Write-Host "Size: $($output.Count) bytes" -ForegroundColor White
|
||||
Reference in New Issue
Block a user