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:
136
Tools/batch_extract_z_assets.sh
Normal file
136
Tools/batch_extract_z_assets.sh
Normal file
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env bash
|
||||
# RR3 Batch Asset Extraction - Cross-Platform
|
||||
# Extracts multiple .z files from a directory
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${YELLOW} RR3 Batch Asset Extraction - Linux/Unix${NC}"
|
||||
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||
echo ""
|
||||
|
||||
# Check Python 3
|
||||
if ! command -v python3 &> /dev/null; then
|
||||
echo -e "${RED}ERROR: Python 3 is not installed!${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check arguments
|
||||
if [ "$#" -lt 1 ]; then
|
||||
echo "Usage: $0 <input_directory> [output_directory]"
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " $0 /path/to/rr3/assets"
|
||||
echo " $0 /path/to/rr3/assets /path/to/output"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
INPUT_DIR="$1"
|
||||
OUTPUT_DIR="${2:-$INPUT_DIR/extracted}"
|
||||
|
||||
if [ ! -d "$INPUT_DIR" ]; then
|
||||
echo -e "${RED}ERROR: Input directory not found: $INPUT_DIR${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}Input directory:${NC} $INPUT_DIR"
|
||||
echo -e "${GREEN}Output directory:${NC} $OUTPUT_DIR"
|
||||
echo ""
|
||||
|
||||
# Create output directory
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
# Find all .z files
|
||||
echo "Scanning for .z files..."
|
||||
Z_FILES=($(find "$INPUT_DIR" -type f -name "*.z"))
|
||||
|
||||
if [ ${#Z_FILES[@]} -eq 0 ]; then
|
||||
echo -e "${RED}ERROR: No .z files found in $INPUT_DIR${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}Found ${#Z_FILES[@]} .z files${NC}"
|
||||
echo ""
|
||||
|
||||
# Process each file
|
||||
COUNT=0
|
||||
SUCCESS=0
|
||||
FAILED=0
|
||||
|
||||
for Z_FILE in "${Z_FILES[@]}"; do
|
||||
COUNT=$((COUNT + 1))
|
||||
echo -e "${CYAN}[${COUNT}/${#Z_FILES[@]}] Processing: $(basename "$Z_FILE")${NC}"
|
||||
|
||||
if python3 - "$Z_FILE" "$OUTPUT_DIR" << 'PYTHON_SCRIPT'
|
||||
import sys
|
||||
import os
|
||||
import zlib
|
||||
|
||||
def extract(input_file, output_dir):
|
||||
with open(input_file, "rb") as f:
|
||||
data = f.read()
|
||||
|
||||
out = b""
|
||||
i = 0
|
||||
found = 0
|
||||
|
||||
while i < len(data) - 2:
|
||||
if data[i] == 0x78 and data[i+1] in (0x9C, 0xDA, 0x01):
|
||||
try:
|
||||
d = zlib.decompress(data[i:])
|
||||
out += d
|
||||
found += 1
|
||||
i += len(d)
|
||||
continue
|
||||
except:
|
||||
pass
|
||||
i += 1
|
||||
|
||||
if found == 0:
|
||||
return False
|
||||
|
||||
base = os.path.basename(input_file)
|
||||
if base.lower().endswith(".z"):
|
||||
base = base[:-2]
|
||||
|
||||
output_file = os.path.join(output_dir, base)
|
||||
|
||||
with open(output_file, "wb") as f:
|
||||
f.write(out)
|
||||
|
||||
print(f" ✅ Extracted: {base} ({found} blocks, {len(out):,} bytes)")
|
||||
return True
|
||||
|
||||
try:
|
||||
if extract(sys.argv[1], sys.argv[2]):
|
||||
sys.exit(0)
|
||||
else:
|
||||
print(" ❌ No ZLIB blocks found")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f" ❌ Error: {e}")
|
||||
sys.exit(1)
|
||||
PYTHON_SCRIPT
|
||||
then
|
||||
SUCCESS=$((SUCCESS + 1))
|
||||
else
|
||||
FAILED=$((FAILED + 1))
|
||||
fi
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${GREEN}Batch extraction complete!${NC}"
|
||||
echo -e " Total files: ${#Z_FILES[@]}"
|
||||
echo -e " ${GREEN}Success: $SUCCESS${NC}"
|
||||
if [ $FAILED -gt 0 ]; then
|
||||
echo -e " ${RED}Failed: $FAILED${NC}"
|
||||
fi
|
||||
echo -e "${CYAN}════════════════════════════════════════════════════════════${NC}"
|
||||
Reference in New Issue
Block a user