# 🎨 RR3 Asset Download System - Community Server Support

## Overview

Real Racing 3 downloads game assets (cars, tracks, textures, etc.) from EA's CDN after installation. We can redirect these downloads to community servers for **game preservation**.

## 🔍 How RR3 Asset Downloads Work

### Director Pattern
When the game starts, it calls the `/director` endpoint which returns URLs for various services:

```json
{
  "serverUrls": {
    "synergy.account": "http://your-server:5001/synergy/account",
    "synergy.commerce": "http://your-server:5001/synergy/commerce",
    "synergy.content": "http://your-server:5001/synergy/content",  // <-- ASSETS!
    "synergy.rewards": "http://your-server:5001/synergy/rewards",
    "synergy.progression": "http://your-server:5001/synergy/progression"
  }
}
```

### Key Service: `synergy.content`

This service handles **all asset downloads**:
- Car models (.pak files)
- Track data
- Textures
- Audio files
- Updates

### URL Structure

From `SynergyEnvironmentImpl.java`, we can see:

```java
// Line 109-116
public String getServerUrlWithKey(String str) {
    // Returns URL for service key like "synergy.content"
    return environmentDataContainer.getServerUrlWithKey(str);
}
```

The game asks for URLs by key, and the Director response tells it where to download content.

## 🎯 Solution: Community Asset Server

### Phase 1: Intercept Asset Requests

Update `DirectorController.cs` to include content service:

```csharp
[HttpGet("director")]
public IActionResult GetDirector()
{
    var baseUrl = $"{Request.Scheme}://{Request.Host}";
    
    return Ok(new
    {
        serverUrls = new Dictionary<string, string>
        {
            ["synergy.account"] = $"{baseUrl}/synergy/account",
            ["synergy.commerce"] = $"{baseUrl}/synergy/commerce",
            ["synergy.rewards"] = $"{baseUrl}/synergy/rewards",
            ["synergy.progression"] = $"{baseUrl}/synergy/progression",
            ["synergy.content"] = $"{baseUrl}/synergy/content"  // NEW!
        },
        // ... rest of response
    });
}
```

### Phase 2: Create Content Controller

Handle asset download requests:

```csharp
[ApiController]
[Route("synergy/content")]
public class ContentController : ControllerBase
{
    private readonly IWebHostEnvironment _env;
    private readonly string _assetsPath;
    
    public ContentController(IWebHostEnvironment env)
    {
        _env = env;
        _assetsPath = Path.Combine(_env.ContentRootPath, "Assets");
        
        // Create assets directory if not exists
        Directory.CreateDirectory(_assetsPath);
    }
    
    // Get asset manifest (list of available content)
    [HttpGet("manifest")]
    public IActionResult GetManifest()
    {
        return Ok(new
        {
            version = "1.0.0",
            assets = GetAssetList()
        });
    }
    
    // Download specific asset
    [HttpGet("download/{assetType}/{assetId}")]
    public IActionResult DownloadAsset(string assetType, string assetId)
    {
        var assetPath = Path.Combine(_assetsPath, assetType, $"{assetId}.pak");
        
        if (!System.IO.File.Exists(assetPath))
        {
            return NotFound(new { error = $"Asset not found: {assetType}/{assetId}" });
        }
        
        var fileBytes = System.IO.File.ReadAllBytes(assetPath);
        return File(fileBytes, "application/octet-stream", $"{assetId}.pak");
    }
    
    // Get asset metadata
    [HttpGet("info/{assetType}/{assetId}")]
    public IActionResult GetAssetInfo(string assetType, string assetId)
    {
        var assetPath = Path.Combine(_assetsPath, assetType, $"{assetId}.pak");
        
        if (!System.IO.File.Exists(assetPath))
        {
            return NotFound();
        }
        
        var fileInfo = new FileInfo(assetPath);
        
        return Ok(new
        {
            assetId = assetId,
            assetType = assetType,
            size = fileInfo.Length,
            checksum = CalculateMD5(assetPath),
            version = "1.0.0"
        });
    }
    
    private List<object> GetAssetList()
    {
        var assets = new List<object>();
        
        if (Directory.Exists(_assetsPath))
        {
            foreach (var typeDir in Directory.GetDirectories(_assetsPath))
            {
                var assetType = Path.GetFileName(typeDir);
                
                foreach (var file in Directory.GetFiles(typeDir, "*.pak"))
                {
                    var assetId = Path.GetFileNameWithoutExtension(file);
                    var fileInfo = new FileInfo(file);
                    
                    assets.Add(new
                    {
                        id = assetId,
                        type = assetType,
                        size = fileInfo.Length,
                        url = $"/synergy/content/download/{assetType}/{assetId}"
                    });
                }
            }
        }
        
        return assets;
    }
    
    private string CalculateMD5(string filePath)
    {
        using var md5 = System.Security.Cryptography.MD5.Create();
        using var stream = System.IO.File.OpenRead(filePath);
        var hash = md5.ComputeHash(stream);
        return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
    }
}
```

## 📁 Asset Directory Structure

Create this structure on your community server:

```
RR3CommunityServer/
└── Assets/
    ├── cars/
    │   ├── nissan_silvia_s15.pak
    │   ├── ford_focus_rs.pak
    │   └── porsche_911_gt3.pak
    ├── tracks/
    │   ├── silverstone_national.pak
    │   ├── dubai_autodrome.pak
    │   └── brands_hatch.pak
    ├── textures/
    │   ├── ui_textures.pak
    │   └── car_textures.pak
    └── audio/
        ├── engine_sounds.pak
        └── music.pak
```

## 🔧 How to Get Original Assets

### Method 1: Extract from Existing Installation (Legal if you own the game)

```bash
# Connect Android device
adb shell

# Find RR3 data directory
cd /data/data/com.ea.games.r3_row/

# List downloaded assets
ls -la files/

# Pull assets to PC (for backup/preservation)
adb pull /data/data/com.ea.games.r3_row/files/ ./rr3-assets/
```

### Method 2: Intercept Downloads (Monitor what gets downloaded)

```bash
# Monitor network traffic while game downloads assets
adb shell tcpdump -i any -w /sdcard/rr3_traffic.pcap

# Or use Charles Proxy / Fiddler to see requests
# Game will request: https://cdn.ea.com/rr3/assets/cars/xxx.pak
```

### Method 3: APK Assets (Some are bundled)

```bash
# Decompile APK
apktool d realracing3.apk -o rr3-decompiled

# Check assets folder
cd rr3-decompiled/assets/
ls -la

# Look for .pak, .unity3d, or compressed files
```

## 🌐 Admin Panel for Asset Management

Add to `Pages/Assets.cshtml`:

```html
@page
@model AssetsModel
@{
    ViewData["Title"] = "Asset Management";
}

<div class="container-fluid">
    <div class="row">
        <div class="col-12">
            <h2>🎨 Asset Management</h2>
            <p>Manage game assets (cars, tracks, textures)</p>
        </div>
    </div>

    <div class="row mb-3">
        <div class="col-md-3">
            <div class="card">
                <div class="card-body">
                    <h5>Cars</h5>
                    <h3>@Model.CarAssetCount</h3>
                </div>
            </div>
        </div>
        <div class="col-md-3">
            <div class="card">
                <div class="card-body">
                    <h5>Tracks</h5>
                    <h3>@Model.TrackAssetCount</h3>
                </div>
            </div>
        </div>
        <div class="col-md-3">
            <div class="card">
                <div class="card-body">
                    <h5>Textures</h5>
                    <h3>@Model.TextureAssetCount</h3>
                </div>
            </div>
        </div>
        <div class="col-md-3">
            <div class="card">
                <div class="card-body">
                    <h5>Total Size</h5>
                    <h3>@Model.TotalSizeMB MB</h3>
                </div>
            </div>
        </div>
    </div>

    <div class="row">
        <div class="col-12">
            <div class="card">
                <div class="card-header">
                    <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#uploadModal">
                        📤 Upload Asset
                    </button>
                </div>
                <div class="card-body">
                    <table class="table">
                        <thead>
                            <tr>
                                <th>Type</th>
                                <th>Asset ID</th>
                                <th>Size</th>
                                <th>Checksum</th>
                                <th>Actions</th>
                            </tr>
                        </thead>
                        <tbody>
                            @foreach (var asset in Model.Assets)
                            {
                                <tr>
                                    <td><span class="badge bg-info">@asset.Type</span></td>
                                    <td>@asset.Id</td>
                                    <td>@asset.SizeMB MB</td>
                                    <td><code>@asset.Checksum</code></td>
                                    <td>
                                        <a href="/synergy/content/download/@asset.Type/@asset.Id" class="btn btn-sm btn-success">Download</a>
                                        <button class="btn btn-sm btn-danger" onclick="deleteAsset('@asset.Type', '@asset.Id')">Delete</button>
                                    </td>
                                </tr>
                            }
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>

<!-- Upload Modal -->
<div class="modal fade" id="uploadModal" tabindex="-1">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Upload Asset</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
            </div>
            <form method="post" enctype="multipart/form-data">
                <div class="modal-body">
                    <div class="mb-3">
                        <label class="form-label">Asset Type</label>
                        <select class="form-select" name="AssetType" required>
                            <option value="cars">Cars</option>
                            <option value="tracks">Tracks</option>
                            <option value="textures">Textures</option>
                            <option value="audio">Audio</option>
                        </select>
                    </div>
                    <div class="mb-3">
                        <label class="form-label">Asset ID</label>
                        <input type="text" class="form-control" name="AssetId" placeholder="e.g., nissan_silvia_s15" required />
                    </div>
                    <div class="mb-3">
                        <label class="form-label">File (.pak)</label>
                        <input type="file" class="form-control" name="AssetFile" accept=".pak" required />
                    </div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
                    <button type="submit" class="btn btn-primary">Upload</button>
                </div>
            </form>
        </div>
    </div>
</div>
```

## 🎮 Testing Asset Downloads

### 1. Start Server with Assets
```bash
cd RR3CommunityServer/RR3CommunityServer
dotnet run
```

### 2. Check Asset Manifest
```bash
curl http://localhost:5001/synergy/content/manifest
```

### 3. Download Test Asset
```bash
curl http://localhost:5001/synergy/content/download/cars/nissan_silvia_s15 -o test.pak
```

### 4. Verify in Game
- Connect APK to community server
- Start game
- Game should request assets from your server
- Monitor logs:
```bash
adb logcat | grep -E "(Download|Asset|Content)"
```

## 📝 Asset Metadata Format

Each asset should have a companion `.json` metadata file:

```json
{
  "id": "nissan_silvia_s15",
  "type": "car",
  "name": "Nissan Silvia Spec-R",
  "version": "1.0.0",
  "size": 15728640,
  "checksum": "5d41402abc4b2a76b9719d911017c592",
  "dependencies": [
    "car_textures",
    "engine_sounds"
  ],
  "tags": ["class_c", "nissan", "drift"]
}
```

## 🔐 Security Considerations

### For Game Preservation (Legal Use)
✅ **Allowed**:
- Backing up assets from games you own
- Running private servers for personal use
- Preserving games after shutdown
- Using with legally obtained APK

❌ **Not Allowed**:
- Distributing EA's copyrighted assets
- Pirating the game
- Selling assets
- Public redistribution without permission

### Asset Checksums
Always verify integrity:
```csharp
private bool VerifyAssetChecksum(string filePath, string expectedChecksum)
{
    var actualChecksum = CalculateMD5(filePath);
    return actualChecksum.Equals(expectedChecksum, StringComparison.OrdinalIgnoreCase);
}
```

## 🚀 Implementation Steps

### Server Side (rr3-server)

1. ✅ Add `ContentController.cs`
2. ✅ Create `Assets/` directory structure
3. ✅ Update `DirectorController` to include `synergy.content`
4. ✅ Add admin page for asset management
5. ✅ Implement upload/download endpoints

### APK Side (rr3-apk)

**No changes needed!** 🎉

The APK already uses the Director pattern. Once your community server returns the `synergy.content` URL, the game will automatically use it!

### Asset Extraction

1. Install original RR3 from Play Store
2. Let it download all assets
3. Use `adb pull` to extract assets
4. Upload to your community server
5. Share with community (if legally allowed)

## 📊 Asset Types in RR3

### Cars (~10-20 MB each)
- 3D models
- Physics data
- Paint variants
- Upgrade visual changes

### Tracks (~50-100 MB each)
- 3D environment
- AI pathfinding data
- Weather variants
- Time of day variants

### Textures (~5-10 MB per pack)
- UI elements
- Car liveries
- Environmental textures
- Effects

### Audio (~1-5 MB per pack)
- Engine sounds per car
- Music tracks
- UI sounds
- Ambient audio

## 💡 Future Enhancements

- [ ] **Asset CDN**: Distribute assets via CDN for faster downloads
- [ ] **Compression**: Serve compressed .pak files
- [ ] **Versioning**: Support multiple asset versions
- [ ] **Differential Updates**: Only download changed files
- [ ] **P2P Distribution**: BitTorrent for large assets
- [ ] **Asset Workshop**: Community-created content
- [ ] **Mod Support**: Custom cars/tracks

## 🎯 Expected Behavior

### Without Community Assets
- Game connects to community server ✅
- Gameplay works (with existing assets) ✅
- New cars might not load properly ❌
- Missing tracks won't be available ❌

### With Community Assets
- Game connects to community server ✅
- Downloads assets from community server ✅
- All content available ✅
- Full offline gameplay ✅

## 📚 Resources

- **Unity .pak Format**: Research Unity asset bundle format
- **EA Nimble SDK**: Understanding the download system
- **Asset Extraction Tools**: QuickBMS, Unity Asset Bundle Extractor
- **Network Analysis**: Charles Proxy, Wireshark

---

**This enables TRUE game preservation!** 🎮

Players can download all game content from community servers, making RR3 fully playable even if EA shuts down their servers!

**Legal Note**: Only use assets you legally own. This is for preservation, not piracy.
