| 1220 | } |
| 1221 | |
| 1222 | bool LoadAppStats(uint32_t appId, AppStats& out) { |
| 1223 | if (g_diskAccountId == 0) return false; // no account yet; skip disk I/O |
| 1224 | std::string path = StatsPath(appId); |
| 1225 | bool haveLocal = false; |
| 1226 | |
| 1227 | std::ifstream f(path); |
| 1228 | if (f.good()) { |
| 1229 | std::string local((std::istreambuf_iterator<char>(f)), |
| 1230 | std::istreambuf_iterator<char>()); |
| 1231 | f.close(); |
| 1232 | if (!local.empty() && ParseAppStatsJson(local, out)) |
| 1233 | haveLocal = true; |
| 1234 | } |
| 1235 | |
| 1236 | // Consult the cached account blob (pulled once by SeedApps/RefreshFromCloud). |
| 1237 | // No per-app network read -- the whole account blob was fetched in one shot. |
| 1238 | if (MergeCloudBlobLocked(appId, out, haveLocal)) { |
| 1239 | haveLocal = true; |
| 1240 | WriteAppStats(appId, out, false); |
| 1241 | } |
| 1242 | |
| 1243 | if (!haveLocal) return false; |
| 1244 | |
| 1245 | // Load schema blob if exists (separate binary sidecar). |
| 1246 | std::string schemaPath = SchemaPath(appId); |
| 1247 | std::ifstream sf(schemaPath, std::ios::binary); |
| 1248 | if (sf.good()) { |
| 1249 | out.schema.assign(std::istreambuf_iterator<char>(sf), |
| 1250 | std::istreambuf_iterator<char>()); |
| 1251 | } |
| 1252 | return true; |
| 1253 | } |
| 1254 | |
| 1255 | // Persist locally and, when pushCloud, queue a cloud upload. Reconcile writes |
| 1256 | // locally only; the cloud is written on session end, when playtime accrues. |
no test coverage detected