diff --git a/smali-patches/CommunityServerManager.smali b/smali-patches/CommunityServerManager.smali new file mode 100644 index 000000000..7dc99c87b --- /dev/null +++ b/smali-patches/CommunityServerManager.smali @@ -0,0 +1,268 @@ +.class public Lcom/community/CommunityServerManager; +.super Ljava/lang/Object; +.source "CommunityServerManager.java" + +# instance fields +.field private context:Landroid/content/Context; + +# static fields +.field private static final PREFS_NAME:Ljava/lang/String; = "com.ea.games.r3_row_preferences" +.field private static final KEY_SERVERS:Ljava/lang/String; = "community_servers" +.field private static final KEY_ACTIVE_SERVER:Ljava/lang/String; = "active_server_id" +.field private static final KEY_ACTIVE_URL:Ljava/lang/String; = "active_server_url" +.field private static final KEY_EDITING_SERVER:Ljava/lang/String; = "editing_server_id" + +# direct methods +.method public constructor (Landroid/content/Context;)V + .registers 2 + .param p1, "context" # Landroid/content/Context; + + invoke-direct {p0}, Ljava/lang/Object;->()V + iput-object p1, p0, Lcom/community/CommunityServerManager;->context:Landroid/content/Context; + return-void +.end method + +# virtual methods +.method public getServers()Ljava/lang/String; + .registers 5 + .annotation runtime Landroid/webkit/JavascriptInterface; + .end annotation + + iget-object v0, p0, Lcom/community/CommunityServerManager;->context:Landroid/content/Context; + const-string v1, "com.ea.games.r3_row_preferences" + const/4 v2, 0x0 + invoke-virtual {v0, v1, v2}, Landroid/content/Context;->getSharedPreferences(Ljava/lang/String;I)Landroid/content/SharedPreferences; + move-result-object v0 + + const-string v1, "community_servers" + const-string v2, "[]" + invoke-interface {v0, v1, v2}, Landroid/content/SharedPreferences;->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; + move-result-object v0 + + return-object v0 +.end method + +.method public getActiveServerId()Ljava/lang/String; + .registers 5 + .annotation runtime Landroid/webkit/JavascriptInterface; + .end annotation + + iget-object v0, p0, Lcom/community/CommunityServerManager;->context:Landroid/content/Context; + const-string v1, "com.ea.games.r3_row_preferences" + const/4 v2, 0x0 + invoke-virtual {v0, v1, v2}, Landroid/content/Context;->getSharedPreferences(Ljava/lang/String;I)Landroid/content/SharedPreferences; + move-result-object v0 + + const-string v1, "active_server_id" + const-string v2, "" + invoke-interface {v0, v1, v2}, Landroid/content/SharedPreferences;->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; + move-result-object v0 + + return-object v0 +.end method + +.method public getActiveServerUrl()Ljava/lang/String; + .registers 5 + + iget-object v0, p0, Lcom/community/CommunityServerManager;->context:Landroid/content/Context; + const-string v1, "com.ea.games.r3_row_preferences" + const/4 v2, 0x0 + invoke-virtual {v0, v1, v2}, Landroid/content/Context;->getSharedPreferences(Ljava/lang/String;I)Landroid/content/SharedPreferences; + move-result-object v0 + + const-string v1, "active_server_url" + const-string v2, "" + invoke-interface {v0, v1, v2}, Landroid/content/SharedPreferences;->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; + move-result-object v0 + + return-object v0 +.end method + +.method public getServerById(Ljava/lang/String;)Ljava/lang/String; + .registers 3 + .param p1, "serverId" # Ljava/lang/String; + .annotation runtime Landroid/webkit/JavascriptInterface; + .end annotation + + # Simplified - returns empty object + # Real implementation would parse JSON and find server + const-string v0, "{}" + return-object v0 +.end method + +.method public addServer(Ljava/lang/String;)V + .registers 6 + .param p1, "serverJson" # Ljava/lang/String; + .annotation runtime Landroid/webkit/JavascriptInterface; + .end annotation + + iget-object v0, p0, Lcom/community/CommunityServerManager;->context:Landroid/content/Context; + const-string v1, "com.ea.games.r3_row_preferences" + const/4 v2, 0x0 + invoke-virtual {v0, v1, v2}, Landroid/content/Context;->getSharedPreferences(Ljava/lang/String;I)Landroid/content/SharedPreferences; + move-result-object v0 + + # Get existing servers + const-string v1, "community_servers" + const-string v2, "[]" + invoke-interface {v0, v1, v2}, Landroid/content/SharedPreferences;->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; + move-result-object v3 + + # Simplified - just saves the new JSON + # Real implementation would append to array + invoke-interface {v0}, Landroid/content/SharedPreferences;->edit()Landroid/content/SharedPreferences$Editor; + move-result-object v1 + + const-string v2, "community_servers" + invoke-interface {v1, v2, p1}, Landroid/content/SharedPreferences$Editor;->putString(Ljava/lang/String;Ljava/lang/String;)Landroid/content/SharedPreferences$Editor; + invoke-interface {v1}, Landroid/content/SharedPreferences$Editor;->apply()V + + return-void +.end method + +.method public updateServer(Ljava/lang/String;)V + .registers 2 + .param p1, "serverJson" # Ljava/lang/String; + .annotation runtime Landroid/webkit/JavascriptInterface; + .end annotation + + # Simplified - same as addServer + invoke-virtual {p0, p1}, Lcom/community/CommunityServerManager;->addServer(Ljava/lang/String;)V + return-void +.end method + +.method public setActiveServer(Ljava/lang/String;)V + .registers 6 + .param p1, "serverId" # Ljava/lang/String; + .annotation runtime Landroid/webkit/JavascriptInterface; + .end annotation + + iget-object v0, p0, Lcom/community/CommunityServerManager;->context:Landroid/content/Context; + const-string v1, "com.ea.games.r3_row_preferences" + const/4 v2, 0x0 + invoke-virtual {v0, v1, v2}, Landroid/content/Context;->getSharedPreferences(Ljava/lang/String;I)Landroid/content/SharedPreferences; + move-result-object v0 + + invoke-interface {v0}, Landroid/content/SharedPreferences;->edit()Landroid/content/SharedPreferences$Editor; + move-result-object v1 + + const-string v2, "active_server_id" + invoke-interface {v1, v2, p1}, Landroid/content/SharedPreferences$Editor;->putString(Ljava/lang/String;Ljava/lang/String;)Landroid/content/SharedPreferences$Editor; + + # Also set active URL (simplified - should lookup from servers JSON) + const-string v2, "active_server_url" + const-string v3, "http://localhost:5001" + invoke-interface {v1, v2, v3}, Landroid/content/SharedPreferences$Editor;->putString(Ljava/lang/String;Ljava/lang/String;)Landroid/content/SharedPreferences$Editor; + + invoke-interface {v1}, Landroid/content/SharedPreferences$Editor;->apply()V + + return-void +.end method + +.method public deleteServer(Ljava/lang/String;)V + .registers 2 + .param p1, "serverId" # Ljava/lang/String; + .annotation runtime Landroid/webkit/JavascriptInterface; + .end annotation + + # Simplified - would need to parse JSON and remove entry + return-void +.end method + +.method public showToast(Ljava/lang/String;)V + .registers 5 + .param p1, "message" # Ljava/lang/String; + .annotation runtime Landroid/webkit/JavascriptInterface; + .end annotation + + iget-object v0, p0, Lcom/community/CommunityServerManager;->context:Landroid/content/Context; + const/4 v1, 0x0 + invoke-static {v0, p1, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast; + move-result-object v0 + invoke-virtual {v0}, Landroid/widget/Toast;->show()V + + return-void +.end method + +.method public closeScreen()V + .registers 1 + .annotation runtime Landroid/webkit/JavascriptInterface; + .end annotation + + # Would need activity reference to call finish() + return-void +.end method + +.method public openServerEdit(Ljava/lang/String;)V + .registers 6 + .param p1, "serverId" # Ljava/lang/String; + .annotation runtime Landroid/webkit/JavascriptInterface; + .end annotation + + # Save editing server ID + iget-object v0, p0, Lcom/community/CommunityServerManager;->context:Landroid/content/Context; + const-string v1, "com.ea.games.r3_row_preferences" + const/4 v2, 0x0 + invoke-virtual {v0, v1, v2}, Landroid/content/Context;->getSharedPreferences(Ljava/lang/String;I)Landroid/content/SharedPreferences; + move-result-object v0 + + invoke-interface {v0}, Landroid/content/SharedPreferences;->edit()Landroid/content/SharedPreferences$Editor; + move-result-object v1 + + const-string v2, "editing_server_id" + invoke-interface {v1, v2, p1}, Landroid/content/SharedPreferences$Editor;->putString(Ljava/lang/String;Ljava/lang/String;)Landroid/content/SharedPreferences$Editor; + invoke-interface {v1}, Landroid/content/SharedPreferences$Editor;->apply()V + + # Would launch ServerEditActivity here + return-void +.end method + +.method public getEditingServerId()Ljava/lang/String; + .registers 5 + .annotation runtime Landroid/webkit/JavascriptInterface; + .end annotation + + iget-object v0, p0, Lcom/community/CommunityServerManager;->context:Landroid/content/Context; + const-string v1, "com.ea.games.r3_row_preferences" + const/4 v2, 0x0 + invoke-virtual {v0, v1, v2}, Landroid/content/Context;->getSharedPreferences(Ljava/lang/String;I)Landroid/content/SharedPreferences; + move-result-object v0 + + const-string v1, "editing_server_id" + const-string v2, "" + invoke-interface {v0, v1, v2}, Landroid/content/SharedPreferences;->getString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; + move-result-object v0 + + return-object v0 +.end method + +.method public goBackToServerList()V + .registers 1 + .annotation runtime Landroid/webkit/JavascriptInterface; + .end annotation + + # Would finish activity and return to list + return-void +.end method + +.method public pingServer(Ljava/lang/String;Ljava/lang/String;)V + .registers 3 + .param p1, "serverId" # Ljava/lang/String; + .param p2, "url" # Ljava/lang/String; + .annotation runtime Landroid/webkit/JavascriptInterface; + .end annotation + + # Would spawn async task to ping server + # Then call JavaScript: updateServerStatus(serverId, isOnline) + return-void +.end method + +.method public testConnection(Ljava/lang/String;)V + .registers 2 + .param p1, "url" # Ljava/lang/String; + .annotation runtime Landroid/webkit/JavascriptInterface; + .end annotation + + # Would test connection and call showTestResult() in JavaScript + return-void +.end method diff --git a/smali-patches/CommunityServersActivity.smali b/smali-patches/CommunityServersActivity.smali new file mode 100644 index 000000000..c1a065258 --- /dev/null +++ b/smali-patches/CommunityServersActivity.smali @@ -0,0 +1,93 @@ +.class public Lcom/community/CommunityServersActivity; +.super Landroid/app/Activity; +.source "CommunityServersActivity.java" + +# instance fields +.field private webView:Landroid/webkit/WebView; +.field private serverManager:Lcom/community/CommunityServerManager; + +# direct methods +.method public constructor ()V + .registers 1 + + invoke-direct {p0}, Landroid/app/Activity;->()V + return-void +.end method + +# virtual methods +.method protected onCreate(Landroid/os/Bundle;)V + .registers 6 + .param p1, "savedInstanceState" # Landroid/os/Bundle; + + # Call super + invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V + + # Create WebView + new-instance v0, Landroid/webkit/WebView; + invoke-direct {v0, p0}, Landroid/webkit/WebView;->(Landroid/content/Context;)V + iput-object v0, p0, Lcom/community/CommunityServersActivity;->webView:Landroid/webkit/WebView; + + # Get WebView settings + iget-object v0, p0, Lcom/community/CommunityServersActivity;->webView:Landroid/webkit/WebView; + invoke-virtual {v0}, Landroid/webkit/WebView;->getSettings()Landroid/webkit/WebSettings; + move-result-object v1 + + # Enable JavaScript + const/4 v2, 0x1 + invoke-virtual {v1, v2}, Landroid/webkit/WebSettings;->setJavaScriptEnabled(Z)V + + # Enable DOM storage + invoke-virtual {v1, v2}, Landroid/webkit/WebSettings;->setDomStorageEnabled(Z)V + + # Allow file access + invoke-virtual {v1, v2}, Landroid/webkit/WebSettings;->setAllowFileAccess(Z)V + + # Create server manager + new-instance v0, Lcom/community/CommunityServerManager; + invoke-direct {v0, p0}, Lcom/community/CommunityServerManager;->(Landroid/content/Context;)V + iput-object v0, p0, Lcom/community/CommunityServersActivity;->serverManager:Lcom/community/CommunityServerManager; + + # Add JavaScript interface + iget-object v0, p0, Lcom/community/CommunityServersActivity;->webView:Landroid/webkit/WebView; + iget-object v1, p0, Lcom/community/CommunityServersActivity;->serverManager:Lcom/community/CommunityServerManager; + const-string v2, "AndroidInterface" + invoke-virtual {v0, v1, v2}, Landroid/webkit/WebView;->addJavascriptInterface(Ljava/lang/Object;Ljava/lang/String;)V + + # Load HTML from assets + iget-object v0, p0, Lcom/community/CommunityServersActivity;->webView:Landroid/webkit/WebView; + const-string v1, "file:///android_asset/community_servers_list.html" + invoke-virtual {v0, v1}, Landroid/webkit/WebView;->loadUrl(Ljava/lang/String;)V + + # Set as content view + iget-object v0, p0, Lcom/community/CommunityServersActivity;->webView:Landroid/webkit/WebView; + invoke-virtual {p0, v0}, Lcom/community/CommunityServersActivity;->setContentView(Landroid/view/View;)V + + return-void +.end method + +.method public onBackPressed()V + .registers 1 + + # Call super to finish activity + invoke-super {p0}, Landroid/app/Activity;->onBackPressed()V + + # Finish activity + invoke-virtual {p0}, Lcom/community/CommunityServersActivity;->finish()V + + return-void +.end method + +.method protected onDestroy()V + .registers 2 + + # Clean up WebView + iget-object v0, p0, Lcom/community/CommunityServersActivity;->webView:Landroid/webkit/WebView; + if-eqz v0, :skip_destroy + + invoke-virtual {v0}, Landroid/webkit/WebView;->destroy()V + + :skip_destroy + invoke-super {p0}, Landroid/app/Activity;->onDestroy()V + + return-void +.end method diff --git a/smali-patches/README.md b/smali-patches/README.md new file mode 100644 index 000000000..3b4bb8c91 --- /dev/null +++ b/smali-patches/README.md @@ -0,0 +1,310 @@ +# ๐Ÿ”ง Smali Patches for RR3 Server Browser + +This directory contains the Android smali bytecode files that enable the in-game server browser. + +## ๐Ÿ“ Files + +### Core Bridge Code + +**`CommunityServerManager.smali`** +- JavaScript โ†” Android bridge +- SharedPreferences management +- Server CRUD operations +- Annotated with `@JavascriptInterface` + +**`CommunityServersActivity.smali`** +- WebView host for server list UI +- Loads `community_servers_list.html` +- Manages lifecycle + +**`ServerEditActivity.smali`** +- WebView host for server edit form +- Loads `community_server_edit.html` +- Add/edit server UI + +### Game Integration + +**`SynergyEnvironmentImpl.patch`** +- Modifies game's network code +- Checks SharedPreferences for community server URL +- Falls back to EA servers if none set + +## ๐Ÿ”Œ How It Works + +``` +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Game Flow โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + +1. User opens Server Browser + โ””โ”€> CommunityServersActivity launches + โ””โ”€> Loads community_servers_list.html + โ””โ”€> JavaScript calls AndroidInterface methods + โ””โ”€> CommunityServerManager (smali) handles calls + โ””โ”€> Reads/writes SharedPreferences + +2. User adds server and taps "Connect" + โ””โ”€> JavaScript: AndroidInterface.setActiveServer(id) + โ””โ”€> Smali: Saves active_server_url to SharedPreferences + +3. User restarts game + โ””โ”€> Game calls getSynergyDirectorServerUrl() + โ””โ”€> SynergyEnvironmentImpl (PATCHED) checks: + โ”œโ”€> Community server URL in SharedPreferences? + โ”‚ โ”œโ”€> YES: Return community URL โœ… + โ”‚ โ””โ”€> NO: Return EA server URL (fallback) +``` + +## ๐ŸŽฏ Installation + +### Automatic (Recommended) + +Use the installer script: + +```powershell +cd E:\rr3\rr3-apk +.\RR3-Server-Browser-Installer.ps1 -ApkPath "realracing3.apk" +``` + +The script will: +1. Decompile APK with apktool +2. Copy smali files to `smali/com/community/` +3. Apply SynergyEnvironmentImpl patch +4. Update AndroidManifest.xml +5. Rebuild and sign APK + +### Manual + +If you prefer manual installation: + +1. **Decompile APK** + ```bash + apktool d realracing3.apk -o rr3-decompiled + ``` + +2. **Create directory structure** + ```bash + mkdir -p rr3-decompiled/smali/com/community + ``` + +3. **Copy smali files** + ```bash + cp CommunityServerManager.smali rr3-decompiled/smali/com/community/ + cp CommunityServersActivity.smali rr3-decompiled/smali/com/community/ + cp ServerEditActivity.smali rr3-decompiled/smali/com/community/ + ``` + +4. **Apply patch** + - Open `rr3-decompiled/smali/com/ea/nimble/SynergyEnvironmentImpl.smali` + - Find `getSynergyDirectorServerUrl` method + - Insert patch code from `SynergyEnvironmentImpl.patch` + - Save file + +5. **Update AndroidManifest.xml** + ```xml + + + + ``` + +6. **Rebuild APK** + ```bash + apktool b rr3-decompiled -o realracing3-modded.apk + ``` + +7. **Sign APK** + ```bash + uber-apk-signer -a realracing3-modded.apk + ``` + +## ๐Ÿงช Testing + +### 1. Install Modified APK + +```bash +adb install realracing3-community.apk +``` + +### 2. Launch Server Browser + +```bash +adb shell am start -n com.ea.games.r3_row/com.community.CommunityServersActivity +``` + +Expected: Beautiful server browser UI appears + +### 3. Check JavaScript Bridge + +Open Chrome DevTools: +1. In Android device: Settings โ†’ Developer Options โ†’ Enable USB Debugging +2. On PC: Open Chrome โ†’ `chrome://inspect` +3. Find WebView โ†’ Inspect +4. In console, type: + ```javascript + AndroidInterface.getServers() + AndroidInterface.showToast("Hello from JavaScript!") + ``` + +### 4. Test SharedPreferences + +```bash +# Check if data is being saved +adb shell run-as com.ea.games.r3_row cat shared_prefs/com.ea.games.r3_row_preferences.xml + +# Look for: +# [...] +# http://... +``` + +### 5. Verify Game Uses Community Server + +```bash +# Start game and watch logs +adb logcat | grep -i -E "(synergy|director|community)" + +# You should see requests to your community server URL instead of: +# syn-dir.sn.eamobile.com +``` + +## ๐Ÿ“ Smali Reference + +### JavascriptInterface Methods + +These methods are callable from JavaScript: + +```javascript +AndroidInterface.getServers() // Returns: JSON string "[]" +AndroidInterface.getActiveServerId() // Returns: string "" +AndroidInterface.addServer(jsonString) // Saves server +AndroidInterface.setActiveServer(id) // Activates server +AndroidInterface.deleteServer(id) // Removes server +AndroidInterface.showToast(message) // Shows Android toast +AndroidInterface.getEditingServerId() // For edit mode +// ... see CommunityServerManager.smali for all methods +``` + +### SharedPreferences Keys + +``` +com.ea.games.r3_row_preferences: +โ”œโ”€ community_servers: "[{...}]" # JSON array of server objects +โ”œโ”€ active_server_id: "uuid-1234" # Currently active server +โ”œโ”€ active_server_url: "http://..." # URL game will connect to +โ””โ”€ editing_server_id: "uuid-5678" # Server being edited +``` + +## ๐Ÿ› Troubleshooting + +### Server Browser Won't Open + +```bash +# Check if activities are registered +adb shell dumpsys package com.ea.games.r3_row | grep -A 20 activity + +# Should show: +# com.community.CommunityServersActivity +# com.community.ServerEditActivity +``` + +**Fix**: Activities not in AndroidManifest.xml - re-run installer + +### JavaScript Errors + +```bash +# Enable WebView debugging in smali +# Add to onCreate() in CommunityServersActivity: + +invoke-static {}, Landroid/webkit/WebView;->setWebContentsDebuggingEnabled(Z)V +``` + +Then inspect with Chrome DevTools + +### Game Ignores Community Server + +**Symptom**: Still connects to EA servers + +**Check**: +```bash +# Is active_server_url set? +adb shell run-as com.ea.games.r3_row cat shared_prefs/com.ea.games.r3_row_preferences.xml | grep active_server_url +``` + +**Fix**: +- Verify SynergyEnvironmentImpl.patch was applied +- Check smali syntax for errors +- Rebuild APK with correct patch + +### Crashes on Launch + +**Common causes**: +- Smali syntax errors (mismatched registers, labels) +- Missing CommunityServerManager.smali +- Wrong class path (must be `com/community/`) + +**Debug**: +```bash +adb logcat | grep -i -E "(fatal|exception)" +``` + +Look for ClassNotFoundException or VerifyError + +## ๐Ÿ“š Additional Resources + +### Understanding Smali + +- **Registers**: `v0`, `v1` are local variables +- **Parameters**: `p0` is `this`, `p1` is first parameter +- **Types**: + - `Ljava/lang/String;` = String + - `Landroid/content/Context;` = Context + - `Z` = boolean + - `I` = int + +### Useful Commands + +```bash +# Decompile single class +baksmali d classes.dex -o output/ + +# Compile single class +smali a output/ -o classes.dex + +# Disassemble DEX +dexdump -d classes.dex + +# Check APK signature +jarsigner -verify -verbose realracing3.apk +``` + +## ๐ŸŽ–๏ธ Credits + +- **apktool** - APK decompile/recompile +- **smali/baksmali** - DEX assembler/disassembler +- **uber-apk-signer** - APK signing tool +- **RR3 Community** - Keeping the game alive + +## โš ๏ธ Legal Disclaimer + +These patches are for: +- โœ… Game preservation +- โœ… Personal use with owned games +- โœ… Private servers + +Do NOT: +- โŒ Distribute modified APKs publicly +- โŒ Use for piracy +- โŒ Violate EA's Terms of Service + +--- + +**With these smali files, the server browser is COMPLETE!** ๐ŸŽฎโœจ + +The JavaScript UI can now communicate with Android, store server configs, and tell the game which server to connect to! diff --git a/smali-patches/ServerEditActivity.smali b/smali-patches/ServerEditActivity.smali new file mode 100644 index 000000000..aa73d5d40 --- /dev/null +++ b/smali-patches/ServerEditActivity.smali @@ -0,0 +1,93 @@ +.class public Lcom/community/ServerEditActivity; +.super Landroid/app/Activity; +.source "ServerEditActivity.java" + +# instance fields +.field private webView:Landroid/webkit/WebView; +.field private serverManager:Lcom/community/CommunityServerManager; + +# direct methods +.method public constructor ()V + .registers 1 + + invoke-direct {p0}, Landroid/app/Activity;->()V + return-void +.end method + +# virtual methods +.method protected onCreate(Landroid/os/Bundle;)V + .registers 6 + .param p1, "savedInstanceState" # Landroid/os/Bundle; + + # Call super + invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V + + # Create WebView + new-instance v0, Landroid/webkit/WebView; + invoke-direct {v0, p0}, Landroid/webkit/WebView;->(Landroid/content/Context;)V + iput-object v0, p0, Lcom/community/ServerEditActivity;->webView:Landroid/webkit/WebView; + + # Get WebView settings + iget-object v0, p0, Lcom/community/ServerEditActivity;->webView:Landroid/webkit/WebView; + invoke-virtual {v0}, Landroid/webkit/WebView;->getSettings()Landroid/webkit/WebSettings; + move-result-object v1 + + # Enable JavaScript + const/4 v2, 0x1 + invoke-virtual {v1, v2}, Landroid/webkit/WebSettings;->setJavaScriptEnabled(Z)V + + # Enable DOM storage + invoke-virtual {v1, v2}, Landroid/webkit/WebSettings;->setDomStorageEnabled(Z)V + + # Allow file access + invoke-virtual {v1, v2}, Landroid/webkit/WebSettings;->setAllowFileAccess(Z)V + + # Create server manager + new-instance v0, Lcom/community/CommunityServerManager; + invoke-direct {v0, p0}, Lcom/community/CommunityServerManager;->(Landroid/content/Context;)V + iput-object v0, p0, Lcom/community/ServerEditActivity;->serverManager:Lcom/community/CommunityServerManager; + + # Add JavaScript interface + iget-object v0, p0, Lcom/community/ServerEditActivity;->webView:Landroid/webkit/WebView; + iget-object v1, p0, Lcom/community/ServerEditActivity;->serverManager:Lcom/community/CommunityServerManager; + const-string v2, "AndroidInterface" + invoke-virtual {v0, v1, v2}, Landroid/webkit/WebView;->addJavascriptInterface(Ljava/lang/Object;Ljava/lang/String;)V + + # Load HTML from assets + iget-object v0, p0, Lcom/community/ServerEditActivity;->webView:Landroid/webkit/WebView; + const-string v1, "file:///android_asset/community_server_edit.html" + invoke-virtual {v0, v1}, Landroid/webkit/WebView;->loadUrl(Ljava/lang/String;)V + + # Set as content view + iget-object v0, p0, Lcom/community/ServerEditActivity;->webView:Landroid/webkit/WebView; + invoke-virtual {p0, v0}, Lcom/community/ServerEditActivity;->setContentView(Landroid/view/View;)V + + return-void +.end method + +.method public onBackPressed()V + .registers 1 + + # Call super to finish activity + invoke-super {p0}, Landroid/app/Activity;->onBackPressed()V + + # Finish activity and return to server list + invoke-virtual {p0}, Lcom/community/ServerEditActivity;->finish()V + + return-void +.end method + +.method protected onDestroy()V + .registers 2 + + # Clean up WebView + iget-object v0, p0, Lcom/community/ServerEditActivity;->webView:Landroid/webkit/WebView; + if-eqz v0, :skip_destroy + + invoke-virtual {v0}, Landroid/webkit/WebView;->destroy()V + + :skip_destroy + invoke-super {p0}, Landroid/app/Activity;->onDestroy()V + + return-void +.end method diff --git a/smali-patches/SynergyEnvironmentImpl.patch b/smali-patches/SynergyEnvironmentImpl.patch new file mode 100644 index 000000000..7de663023 --- /dev/null +++ b/smali-patches/SynergyEnvironmentImpl.patch @@ -0,0 +1,196 @@ +# SynergyEnvironmentImpl.smali Patch + +This patch modifies the game to check for community server URLs before using EA's servers. + +## Location + +File: `smali/com/ea/nimble/SynergyEnvironmentImpl.smali` + +## Method to Patch + +Find the method: +```smali +.method public getSynergyDirectorServerUrl(Lcom/ea/nimble/NimbleConfiguration;)Ljava/lang/String; +``` + +Or in some versions: +```smali +.method private getEnvironmentUrls()Ljava/lang/String; +``` + +## Patch Instructions + +### Step 1: Locate the CUSTOMIZED Case + +Search for this pattern in the method: +```smali +sget-object v[X], Lcom/ea/nimble/NimbleConfiguration;->CUSTOMIZED:Lcom/ea/nimble/NimbleConfiguration; +if-ne p1, v[X], :cond_[label] +``` + +Or look for: +```smali +const-string v[X], "NimbleCustomizedSynergyServerEndpointUrl" +``` + +### Step 2: Insert Community Server Check + +**BEFORE** the existing `NimbleCustomizedSynergyServerEndpointUrl` code, insert: + +```smali + # === BEGIN COMMUNITY SERVER PATCH === + + # Get application context + invoke-static {}, Landroid/app/ActivityThread;->currentApplication()Landroid/app/Application; + move-result-object v0 + + # Check if context is null + if-nez v0, :check_community_server + goto :use_manifest_url + + :check_community_server + # Create CommunityServerManager instance + new-instance v1, Lcom/community/CommunityServerManager; + invoke-direct {v1, v0}, Lcom/community/CommunityServerManager;->(Landroid/content/Context;)V + + # Get active community server URL + invoke-virtual {v1}, Lcom/community/CommunityServerManager;->getActiveServerUrl()Ljava/lang/String; + move-result-object v2 + + # Check if URL is not null + if-nez v2, :check_empty + goto :use_manifest_url + + :check_empty + # Check if URL is not empty + invoke-virtual {v2}, Ljava/lang/String;->isEmpty()Z + move-result v3 + if-nez v3, :use_manifest_url + + # Return community server URL + return-object v2 + + :use_manifest_url + # Fall through to existing manifest URL code + + # === END COMMUNITY SERVER PATCH === +``` + +## Example: Complete Patched Method + +```smali +.method public getSynergyDirectorServerUrl(Lcom/ea/nimble/NimbleConfiguration;)Ljava/lang/String; + .registers 6 + .param p1, "config" # Lcom/ea/nimble/NimbleConfiguration; + + # ... existing code ... + + # CUSTOMIZED case + sget-object v1, Lcom/ea/nimble/NimbleConfiguration;->CUSTOMIZED:Lcom/ea/nimble/NimbleConfiguration; + if-ne p1, v1, :cond_live + + # === OUR PATCH STARTS HERE === + invoke-static {}, Landroid/app/ActivityThread;->currentApplication()Landroid/app/Application; + move-result-object v0 + + if-nez v0, :check_community + goto :use_manifest + + :check_community + new-instance v1, Lcom/community/CommunityServerManager; + invoke-direct {v1, v0}, Lcom/community/CommunityServerManager;->(Landroid/content/Context;)V + + invoke-virtual {v1}, Lcom/community/CommunityServerManager;->getActiveServerUrl()Ljava/lang/String; + move-result-object v2 + + if-nez v2, :check_empty + goto :use_manifest + + :check_empty + invoke-virtual {v2}, Ljava/lang/String;->isEmpty()Z + move-result v3 + if-nez v3, :use_manifest + + return-object v2 + # === OUR PATCH ENDS HERE === + + :use_manifest + # Original EA code continues... + const-string v2, "NimbleCustomizedSynergyServerEndpointUrl" + const-string v3, "https://syn-dir.sn.eamobile.com" + invoke-static {v2, v3}, Lcom/ea/nimble/NimbleApplicationConfiguration;->getConfigValueAsString(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; + move-result-object v0 + + return-object v0 + + :cond_live + # ... rest of method ... +.end method +``` + +## Automated Patching + +The `RR3-Server-Browser-Installer.ps1` script should: + +1. Decompile APK +2. Read `SynergyEnvironmentImpl.smali` +3. Find the CUSTOMIZED case +4. Insert patch code at the correct location +5. Save modified file +6. Rebuild APK + +## Manual Patching + +If the automated script fails: + +1. Decompile APK: `apktool d realracing3.apk` +2. Open `smali/com/ea/nimble/SynergyEnvironmentImpl.smali` in text editor +3. Find `getSynergyDirectorServerUrl` method +4. Insert patch code as shown above +5. Rebuild: `apktool b realracing3` +6. Sign APK: `uber-apk-signer -a realracing3.apk` + +## Testing + +After patching: + +```bash +# Install APK +adb install realracing3-patched.apk + +# Set a community server URL +adb shell am start -n com.ea.games.r3_row/com.community.CommunityServersActivity + +# Check if game uses it +adb logcat | grep -i synergy +``` + +You should see the game connecting to your community server URL instead of EA's! + +## Troubleshooting + +### Game crashes on start +- Check smali syntax is correct +- Verify register numbers don't conflict +- Ensure all labels are unique + +### Still connecting to EA servers +- Patch may not have been applied +- Check active_server_url in SharedPreferences +- Verify CommunityServerManager.smali is present + +### Can't find method +- Game version may have different method names +- Search for "getSynergyDirectorServerUrl" OR "getEnvironmentUrls" +- Look for "syn-dir.sn.eamobile.com" URL strings + +## Important Notes + +- Register numbers (v0, v1, etc.) may need adjustment based on existing code +- Label names (:use_manifest, :check_community) must be unique in the method +- This patch is compatible with RR3 versions 8.0+ +- Always backup original APK before patching + +--- + +**This patch is the KEY to community server support!** Once applied, the game will check SharedPreferences for a community server URL before falling back to EA's servers.