Phase 1: Add LocalSaveManager for offline mode

- Created LocalSaveManager.smali with JSON save/load functionality
- Implements save file at /sdcard/Android/data/com.ea.games.r3_row/files/offline_save.json
- Added currency helper methods (getCurrency, setCurrency)
- Initialized in MainActivity.onCreate()
- Save structure includes: player, currency, dailyReward, cars, progress

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-02-18 17:24:35 -08:00
parent f3960ee359
commit c25e937818
2 changed files with 546 additions and 0 deletions

View File

@@ -0,0 +1,543 @@
.class public Lcom/firemint/realracing/LocalSaveManager;
.super Ljava/lang/Object;
.source "LocalSaveManager.java"
# static fields
.field private static final TAG:Ljava/lang/String; = "RR3_LocalSaveManager"
.field private static final SAVE_FILE_NAME:Ljava/lang/String; = "offline_save.json"
.field private static final SAVE_VERSION:Ljava/lang/String; = "1.0"
.field private static saveData:Lorg/json/JSONObject;
# direct methods
.method static constructor <clinit>()V
.locals 1
# Initialize saveData to null
const/4 v0, 0x0
sput-object v0, Lcom/firemint/realracing/LocalSaveManager;->saveData:Lorg/json/JSONObject;
return-void
.end method
.method public constructor <init>()V
.locals 0
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
.end method
.method public static getSaveFilePath(Landroid/content/Context;)Ljava/io/File;
.locals 3
# Get external storage directory: /sdcard/Android/data/com.ea.games.r3_row/files/
invoke-virtual {p0}, Landroid/content/Context;->getExternalFilesDir(Ljava/lang/String;)Ljava/io/File;
move-result-object p0
# Create File object for offline_save.json
new-instance v0, Ljava/io/File;
const-string v1, "offline_save.json"
invoke-direct {v0, p0, v1}, Ljava/io/File;-><init>(Ljava/io/File;Ljava/lang/String;)V
return-object v0
.end method
.method public static initSaveFile(Landroid/content/Context;)V
.locals 5
const-string v0, "RR3_LocalSaveManager"
const-string v1, "Initializing save file"
invoke-static {v0, v1}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
:try_start_0
# Get save file path
invoke-static {p0}, Lcom/firemint/realracing/LocalSaveManager;->getSaveFilePath(Landroid/content/Context;)Ljava/io/File;
move-result-object v1
# Check if file already exists
invoke-virtual {v1}, Ljava/io/File;->exists()Z
move-result v2
if-eqz v2, :cond_0
const-string p0, "Save file already exists. Loading existing data."
invoke-static {v0, p0}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
# Load existing save
invoke-static {p0}, Lcom/firemint/realracing/LocalSaveManager;->loadSave(Landroid/content/Context;)Lorg/json/JSONObject;
return-void
:cond_0
# Create new save structure
new-instance v1, Lorg/json/JSONObject;
invoke-direct {v1}, Lorg/json/JSONObject;-><init>()V
# Set version
const-string v2, "version"
const-string v3, "1.0"
invoke-virtual {v1, v2, v3}, Lorg/json/JSONObject;->put(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject;
# Set lastSaved timestamp
const-string v2, "lastSaved"
invoke-static {}, Ljava/lang/System;->currentTimeMillis()J
move-result-wide v3
invoke-virtual {v1, v2, v3, v4}, Lorg/json/JSONObject;->put(Ljava/lang/String;J)Lorg/json/JSONObject;
# Create player object
new-instance v2, Lorg/json/JSONObject;
invoke-direct {v2}, Lorg/json/JSONObject;-><init>()V
const-string v3, "name"
const-string v4, "Player"
invoke-virtual {v2, v3, v4}, Lorg/json/JSONObject;->put(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject;
const-string v3, "level"
const/4 v4, 0x1
invoke-virtual {v2, v3, v4}, Lorg/json/JSONObject;->put(Ljava/lang/String;I)Lorg/json/JSONObject;
const-string v3, "xp"
const/4 v4, 0x0
invoke-virtual {v2, v3, v4}, Lorg/json/JSONObject;->put(Ljava/lang/String;I)Lorg/json/JSONObject;
const-string v3, "player"
invoke-virtual {v1, v3, v2}, Lorg/json/JSONObject;->put(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject;
# Create currency object
new-instance v2, Lorg/json/JSONObject;
invoke-direct {v2}, Lorg/json/JSONObject;-><init>()V
const-string v3, "cash"
const v4, 0xc350 # 50000
invoke-virtual {v2, v3, v4}, Lorg/json/JSONObject;->put(Ljava/lang/String;I)Lorg/json/JSONObject;
const-string v3, "gold"
const/16 v4, 0xc8 # 200
invoke-virtual {v2, v3, v4}, Lorg/json/JSONObject;->put(Ljava/lang/String;I)Lorg/json/JSONObject;
const-string v3, "rDollars"
const/4 v4, 0x0
invoke-virtual {v2, v3, v4}, Lorg/json/JSONObject;->put(Ljava/lang/String;I)Lorg/json/JSONObject;
const-string v3, "currency"
invoke-virtual {v1, v3, v2}, Lorg/json/JSONObject;->put(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject;
# Create dailyReward object
new-instance v2, Lorg/json/JSONObject;
invoke-direct {v2}, Lorg/json/JSONObject;-><init>()V
const-string v3, "lastClaimed"
const-wide/16 v4, 0x0
invoke-virtual {v2, v3, v4, v5}, Lorg/json/JSONObject;->put(Ljava/lang/String;J)Lorg/json/JSONObject;
const-string v3, "streak"
const/4 v4, 0x0
invoke-virtual {v2, v3, v4}, Lorg/json/JSONObject;->put(Ljava/lang/String;I)Lorg/json/JSONObject;
const-string v3, "dailyReward"
invoke-virtual {v1, v3, v2}, Lorg/json/JSONObject;->put(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject;
# Create empty cars array
new-instance v2, Lorg/json/JSONArray;
invoke-direct {v2}, Lorg/json/JSONArray;-><init>()V
const-string v3, "cars"
invoke-virtual {v1, v3, v2}, Lorg/json/JSONObject;->put(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject;
# Create empty progress object
new-instance v2, Lorg/json/JSONObject;
invoke-direct {v2}, Lorg/json/JSONObject;-><init>()V
const-string v3, "progress"
invoke-virtual {v1, v3, v2}, Lorg/json/JSONObject;->put(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject;
# Store in memory
sput-object v1, Lcom/firemint/realracing/LocalSaveManager;->saveData:Lorg/json/JSONObject;
# Save to file
invoke-static {p0}, Lcom/firemint/realracing/LocalSaveManager;->saveSave(Landroid/content/Context;)V
const-string p0, "Save file initialized successfully"
invoke-static {v0, p0}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
:try_end_0
.catch Lorg/json/JSONException; {:try_start_0 .. :try_end_0} :catch_0
goto :goto_0
:catch_0
move-exception p0
const-string v1, "Error initializing save file"
invoke-static {v0, v1, p0}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
:goto_0
return-void
.end method
.method public static loadSave(Landroid/content/Context;)Lorg/json/JSONObject;
.locals 6
const-string v0, "RR3_LocalSaveManager"
const-string v1, "Loading save file"
invoke-static {v0, v1}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
:try_start_0
# Get save file path
invoke-static {p0}, Lcom/firemint/realracing/LocalSaveManager;->getSaveFilePath(Landroid/content/Context;)Ljava/io/File;
move-result-object p0
# Check if file exists
invoke-virtual {p0}, Ljava/io/File;->exists()Z
move-result v1
if-nez v1, :cond_0
const-string p0, "Save file does not exist"
invoke-static {v0, p0}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I
# Return null if file doesn't exist
const/4 p0, 0x0
return-object p0
:cond_0
# Read file content
new-instance v1, Ljava/io/FileInputStream;
invoke-direct {v1, p0}, Ljava/io/FileInputStream;-><init>(Ljava/io/File;)V
# Get file size
invoke-virtual {p0}, Ljava/io/File;->length()J
move-result-wide v2
long-to-int p0, v2
# Create byte array
new-array v2, p0, [B
# Read bytes
invoke-virtual {v1, v2}, Ljava/io/FileInputStream;->read([B)I
# Close stream
invoke-virtual {v1}, Ljava/io/FileInputStream;->close()V
# Convert bytes to string
new-instance v1, Ljava/lang/String;
const-string v3, "UTF-8"
invoke-direct {v1, v2, v3}, Ljava/lang/String;-><init>([BLjava/lang/String;)V
# Parse JSON
new-instance v2, Lorg/json/JSONObject;
invoke-direct {v2, v1}, Lorg/json/JSONObject;-><init>(Ljava/lang/String;)V
# Store in memory
sput-object v2, Lcom/firemint/realracing/LocalSaveManager;->saveData:Lorg/json/JSONObject;
const-string v1, "Save file loaded successfully"
invoke-static {v0, v1}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
# Return the loaded data
sget-object p0, Lcom/firemint/realracing/LocalSaveManager;->saveData:Lorg/json/JSONObject;
:try_end_0
.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
return-object p0
:catch_0
move-exception p0
const-string v1, "Error loading save file"
invoke-static {v0, v1, p0}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
# Return null on error
const/4 p0, 0x0
return-object p0
.end method
.method public static saveSave(Landroid/content/Context;)V
.locals 5
const-string v0, "RR3_LocalSaveManager"
const-string v1, "Saving save file"
invoke-static {v0, v1}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
:try_start_0
# Get current save data
sget-object v1, Lcom/firemint/realracing/LocalSaveManager;->saveData:Lorg/json/JSONObject;
if-nez v1, :cond_0
const-string p0, "No save data to save"
invoke-static {v0, p0}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I
return-void
:cond_0
# Update lastSaved timestamp
const-string v2, "lastSaved"
invoke-static {}, Ljava/lang/System;->currentTimeMillis()J
move-result-wide v3
invoke-virtual {v1, v2, v3, v4}, Lorg/json/JSONObject;->put(Ljava/lang/String;J)Lorg/json/JSONObject;
# Get save file path
invoke-static {p0}, Lcom/firemint/realracing/LocalSaveManager;->getSaveFilePath(Landroid/content/Context;)Ljava/io/File;
move-result-object p0
# Convert JSON to string
sget-object v1, Lcom/firemint/realracing/LocalSaveManager;->saveData:Lorg/json/JSONObject;
const/4 v2, 0x4
invoke-virtual {v1, v2}, Lorg/json/JSONObject;->toString(I)Ljava/lang/String;
move-result-object v1
# Write to file
new-instance v2, Ljava/io/FileOutputStream;
invoke-direct {v2, p0}, Ljava/io/FileOutputStream;-><init>(Ljava/io/File;)V
const-string p0, "UTF-8"
invoke-virtual {v1, p0}, Ljava/lang/String;->getBytes(Ljava/lang/String;)[B
move-result-object p0
invoke-virtual {v2, p0}, Ljava/io/FileOutputStream;->write([B)V
# Close stream
invoke-virtual {v2}, Ljava/io/FileOutputStream;->close()V
const-string p0, "Save file saved successfully"
invoke-static {v0, p0}, Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
:try_end_0
.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
goto :goto_0
:catch_0
move-exception p0
const-string v1, "Error saving save file"
invoke-static {v0, v1, p0}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
:goto_0
return-void
.end method
.method public static getSaveData()Lorg/json/JSONObject;
.locals 1
# Return the in-memory save data
sget-object v0, Lcom/firemint/realracing/LocalSaveManager;->saveData:Lorg/json/JSONObject;
return-object v0
.end method
.method public static updateSaveData(Ljava/lang/String;Ljava/lang/Object;Landroid/content/Context;)V
.locals 2
const-string v0, "RR3_LocalSaveManager"
:try_start_0
# Get current save data
sget-object v1, Lcom/firemint/realracing/LocalSaveManager;->saveData:Lorg/json/JSONObject;
if-nez v1, :cond_0
const-string p0, "Save data not initialized"
invoke-static {v0, p0}, Landroid/util/Log;->w(Ljava/lang/String;Ljava/lang/String;)I
return-void
:cond_0
# Update the field
invoke-virtual {v1, p0, p1}, Lorg/json/JSONObject;->put(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject;
# Save to disk
invoke-static {p2}, Lcom/firemint/realracing/LocalSaveManager;->saveSave(Landroid/content/Context;)V
:try_end_0
.catch Lorg/json/JSONException; {:try_start_0 .. :try_end_0} :catch_0
goto :goto_0
:catch_0
move-exception p0
const-string p1, "Error updating save data"
invoke-static {v0, p1, p0}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
:goto_0
return-void
.end method
.method public static getCurrency(Landroid/content/Context;Ljava/lang/String;)I
.locals 3
const-string v0, "RR3_LocalSaveManager"
const/4 v1, 0x0
:try_start_0
# Load save if not in memory
sget-object v2, Lcom/firemint/realracing/LocalSaveManager;->saveData:Lorg/json/JSONObject;
if-nez v2, :cond_0
invoke-static {p0}, Lcom/firemint/realracing/LocalSaveManager;->loadSave(Landroid/content/Context;)Lorg/json/JSONObject;
move-result-object v2
if-nez v2, :cond_0
return v1
:cond_0
# Get currency object
sget-object p0, Lcom/firemint/realracing/LocalSaveManager;->saveData:Lorg/json/JSONObject;
const-string v2, "currency"
invoke-virtual {p0, v2}, Lorg/json/JSONObject;->getJSONObject(Ljava/lang/String;)Lorg/json/JSONObject;
move-result-object p0
# Get specific currency value
invoke-virtual {p0, p1}, Lorg/json/JSONObject;->getInt(Ljava/lang/String;)I
move-result p0
:try_end_0
.catch Lorg/json/JSONException; {:try_start_0 .. :try_end_0} :catch_0
return p0
:catch_0
move-exception p0
const-string p1, "Error getting currency"
invoke-static {v0, p1, p0}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
return v1
.end method
.method public static setCurrency(Landroid/content/Context;Ljava/lang/String;I)V
.locals 2
const-string v0, "RR3_LocalSaveManager"
:try_start_0
# Load save if not in memory
sget-object v1, Lcom/firemint/realracing/LocalSaveManager;->saveData:Lorg/json/JSONObject;
if-nez v1, :cond_0
invoke-static {p0}, Lcom/firemint/realracing/LocalSaveManager;->loadSave(Landroid/content/Context;)Lorg/json/JSONObject;
move-result-object v1
if-nez v1, :cond_0
return-void
:cond_0
# Get currency object
sget-object v1, Lcom/firemint/realracing/LocalSaveManager;->saveData:Lorg/json/JSONObject;
const-string v0, "currency"
invoke-virtual {v1, v0}, Lorg/json/JSONObject;->getJSONObject(Ljava/lang/String;)Lorg/json/JSONObject;
move-result-object v0
# Set currency value
invoke-virtual {v0, p1, p2}, Lorg/json/JSONObject;->put(Ljava/lang/String;I)Lorg/json/JSONObject;
# Save to disk
invoke-static {p0}, Lcom/firemint/realracing/LocalSaveManager;->saveSave(Landroid/content/Context;)V
:try_end_0
.catch Lorg/json/JSONException; {:try_start_0 .. :try_end_0} :catch_0
goto :goto_0
:catch_0
move-exception p0
const-string p1, "Error setting currency"
invoke-static {v0, p1, p0}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
:goto_0
return-void
.end method

View File

@@ -2237,6 +2237,9 @@
:cond_0
invoke-static {p0}, Lcom/firemint/realracing/AppProxy;->SetActivity(Landroid/app/Activity;)V
# Initialize LocalSaveManager for offline mode
invoke-static {p0}, Lcom/firemint/realracing/LocalSaveManager;->initSaveFile(Landroid/content/Context;)V
.line 353
invoke-virtual {p0}, Landroid/app/Activity;->getApplication()Landroid/app/Application;