Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 64 additions & 45 deletions source/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,11 +297,28 @@ static std::string computeMd5Hex(const std::string &path)
return std::string(hex, 32);
}

// ---------------------------------------------------------------------------
// SyncSummary — accumulates per-file outcomes for end-of-sync report
// ---------------------------------------------------------------------------
struct SyncSummary
{
int uploaded;
int downloaded;
std::set<std::string> checkedPaths; // full localPath, deduplicates across entries
struct FileAction
{
std::string path;
std::string action;
};
std::vector<FileAction> changes;
SyncSummary() : uploaded(0), downloaded(0) {}
};

// ---------------------------------------------------------------------------
// performSync — bidirectional sync for one SyncEntry
// Returns false if a fatal Drive error occurred or cancellation was requested.
// ---------------------------------------------------------------------------
static bool performSync(GoogleDrive &drive, Manifest &manifest, const SyncEntry &entry)
static bool performSync(GoogleDrive &drive, Manifest &manifest, const SyncEntry &entry, SyncSummary &summary)
{
if (drive.hasFatalError())
return false;
Expand Down Expand Up @@ -357,8 +374,10 @@ static bool performSync(GoogleDrive &drive, Manifest &manifest, const SyncEntry
bool inManifest = manifest.has(localPath);
ManifestEntry mEntry = inManifest ? manifest.get(localPath) : ManifestEntry{};

bool localChanged = inManifest && (localMtime != mEntry.localMtime);
bool driveChanged = inManifest && driveExists && (dfi->md5 != mEntry.driveMd5);
// No manifest entry + both sides exist = no baseline to diff against, treat as conflict.
bool firstSync = !inManifest && localExists && driveExists;
bool localChanged = firstSync || (inManifest && (localMtime != mEntry.localMtime));
bool driveChanged = firstSync || (inManifest && driveExists && (dfi->md5 != mEntry.driveMd5));

// FAT32 mtime has 2-second granularity and some emulators never update
// the timestamp at all. If mtime is unchanged but we have a Drive MD5
Expand Down Expand Up @@ -408,6 +427,8 @@ static bool performSync(GoogleDrive &drive, Manifest &manifest, const SyncEntry
struct stat st = {};
stat(localPath.c_str(), &st);
manifest.set(localPath, {st.st_mtime, dfi->md5, dfi->id});
summary.downloaded++;
summary.changes.push_back({localPath, "downloaded"});
}
continue;
}
Expand All @@ -420,55 +441,20 @@ static bool performSync(GoogleDrive &drive, Manifest &manifest, const SyncEntry
std::string existingId = inManifest ? mEntry.driveId : "";
std::string fileId = drive.syncUpload(rootFolderId, relPath, localPath, existingId, md5);
if (!fileId.empty())
manifest.set(localPath, {localMtime, md5, fileId});
continue;
}

// Both exist
if (!inManifest)
{
// First sync: no history to diff against — treat as a conflict so
// neither side is silently overwritten.
printf("\n *** FIRST SYNC (both sides exist): %s\n", localPath.c_str());
printf(" A: keep 3DS version B: keep remote version X: skip START: cancel L: apply all\n\n");
ConflictChoice choice = waitForConflictKey();

if (choice == CONFLICT_CANCEL)
{
printf(" -> Sync cancelled\n");
return false;
}
if (choice == CONFLICT_SKIP)
{
printf(" -> Skipped\n");
continue;
}
if (choice == CONFLICT_KEEP_LOCAL)
{
printf(" -> Keeping 3DS version, uploading\n");
std::string md5;
std::string fileId = drive.syncUpload(rootFolderId, relPath, localPath, dfi->id, md5);
if (!fileId.empty())
manifest.set(localPath, {localMtime, md5, fileId});
}
else
{
printf(" -> Keeping remote version, downloading\n");
if (drive.downloadFile(*dfi, localPath))
{
struct stat st = {};
stat(localPath.c_str(), &st);
manifest.set(localPath, {st.st_mtime, dfi->md5, dfi->id});
}
manifest.set(localPath, {localMtime, md5, fileId});
summary.uploaded++;
summary.changes.push_back({localPath, "uploaded"});
}
continue;
}

// Both exist and in manifest
// Both exist
if (!localChanged && !driveChanged)
{
// No change — skip
printf(" -> Up to date\n");
summary.checkedPaths.insert(localPath);
continue;
}

Expand All @@ -479,7 +465,11 @@ static bool performSync(GoogleDrive &drive, Manifest &manifest, const SyncEntry
std::string md5;
std::string fileId = drive.syncUpload(rootFolderId, relPath, localPath, mEntry.driveId, md5);
if (!fileId.empty())
{
manifest.set(localPath, {localMtime, md5, fileId});
summary.uploaded++;
summary.changes.push_back({localPath, "uploaded"});
}
continue;
}

Expand All @@ -492,6 +482,8 @@ static bool performSync(GoogleDrive &drive, Manifest &manifest, const SyncEntry
struct stat st = {};
stat(localPath.c_str(), &st);
manifest.set(localPath, {st.st_mtime, dfi->md5, dfi->id});
summary.downloaded++;
summary.changes.push_back({localPath, "downloaded"});
}
continue;
}
Expand All @@ -509,15 +501,21 @@ static bool performSync(GoogleDrive &drive, Manifest &manifest, const SyncEntry
if (choice == CONFLICT_SKIP)
{
printf(" -> Skipped\n");
summary.changes.push_back({localPath, "skipped"});
continue;
}
if (choice == CONFLICT_KEEP_LOCAL)
{
printf(" -> Keeping 3DS version, uploading\n");
std::string md5;
std::string fileId = drive.syncUpload(rootFolderId, relPath, localPath, mEntry.driveId, md5);
std::string existingId = inManifest ? mEntry.driveId : dfi->id;
std::string fileId = drive.syncUpload(rootFolderId, relPath, localPath, existingId, md5);
if (!fileId.empty())
{
manifest.set(localPath, {localMtime, md5, fileId});
summary.uploaded++;
summary.changes.push_back({localPath, "uploaded"});
}
}
else
{
Expand All @@ -527,6 +525,8 @@ static bool performSync(GoogleDrive &drive, Manifest &manifest, const SyncEntry
struct stat st = {};
stat(localPath.c_str(), &st);
manifest.set(localPath, {st.st_mtime, dfi->md5, dfi->id});
summary.downloaded++;
summary.changes.push_back({localPath, "downloaded"});
}
}

Expand Down Expand Up @@ -651,13 +651,15 @@ static void runSync(const INIReader &reader)
Manifest manifest("/3ds/3DSync/manifest.json");
manifest.load();

SyncSummary summary;

for (auto &entry : syncEntries)
{
if (entry.direction == SYNC_BOTH)
{
printf("\nSyncing [%s] <-> Drive:%s\n",
entry.localBase.c_str(), entry.remoteName.c_str());
if (!performSync(drive, manifest, entry) && !drive.hasFatalError())
if (!performSync(drive, manifest, entry, summary) && !drive.hasFatalError())
g_cancelRequested = true;
}
else
Expand All @@ -683,6 +685,23 @@ static void runSync(const INIReader &reader)
}

manifest.save();

printf("\n--- Sync Summary ---\n");
if (summary.changes.empty())
{
printf("All %d files up to date.\n",
(int)summary.checkedPaths.size());
}
else
{
printf("Uploaded: %d Downloaded: %d Unchanged: %d\n",
summary.uploaded, summary.downloaded,
(int)summary.checkedPaths.size());
printf("\nChanged files:\n");
for (auto &c : summary.changes)
printf(" %s: %s\n", c.action.c_str(), c.path.c_str());
}
Comment thread
michvllni marked this conversation as resolved.

if (drive.hasFatalError())
printf(CONSOLE_RED "\nSync did not complete. Check the errors above.\n" CONSOLE_RESET);
else if (g_cancelRequested)
Expand Down
Loading