Skip to content

Fix and adjustment for Named States#1982

Open
Valeour wants to merge 7 commits into
grumpycoders:mainfrom
Valeour:named_states_update
Open

Fix and adjustment for Named States#1982
Valeour wants to merge 7 commits into
grumpycoders:mainfrom
Valeour:named_states_update

Conversation

@Valeour
Copy link
Copy Markdown

@Valeour Valeour commented Dec 9, 2025

Fix for named save states using current_path() to load, and getPersistentDir() to save.
Adjusted named save states to save into a folder. [#1922]

Breaking change:
Existing named save states will not be located as they are not in the new folder. This can be fixed by moving save states into the appropriate folder.

…sistentDir()` to save.

Adjusted named save states to save into a folder, rather than by prefix.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 9, 2025

Review Change Stack

📝 Walkthrough

Walkthrough

Save state lookup and path creation now use g_system->getPersistentDir() with the GUI save-state subfolder (prefix(false)). Listing filters by postfix only; displayed names strip only the postfix. Subfolders are created if missing and full save paths are built by joining persistent dir, subfolder, base name, and postfix.

Changes

Save State Path Handling Refactor

Layer / File(s) Summary
Discovery and name extraction
src/gui/widgets/named_savestates.cc
List save files from g_system->getPersistentDir() / gui->getSaveStatePrefix(false), filter entries by the save postfix only (no longer requiring filename start with prefix), and compute niceName by removing only the postfix. Includes core/system.h.
Path construction & directory creation
src/gui/widgets/named_savestates.cc
Create the persistent subfolder if missing (UI logs success/failure), build the full save-state path by joining persistent dir, subfolder, and saveStateName, then append the configured save postfix using formatted path generation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped the saves to a steady, cozy spot,
Postfix trimmed tidy, names refreshed on the spot,
Subfolders formed where wandering once lay,
Paths stitched together in a sensible way,
Hooray — persistent homes for each save today!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly addresses the main change in the PR: fixing and adjusting named save states, which aligns with the core modifications to save state handling and path management.
Description check ✅ Passed The description is clearly related to the changeset, explaining the fix for named save states, the shift from current_path() to getPersistentDir(), and the folder organization change with its breaking change implications.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Valeour Valeour marked this pull request as ready for review December 9, 2025 00:29
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/gui/widgets/named_savestates.cc (2)

145-169: Suffix check and filesystem error handling could be more robust/clear

The new logic to list named save states under g_system->getPersistentDir() / getSaveStatePrefix(false) is consistent with the new storage layout and keeps the UI working on the persistent directory only.

Two minor robustness/readability suggestions:

  1. Suffix check
    The current condition:

    if (filename.rfind(postfix) == filename.length() - postfix.length()) {

    works, but mixes rfind semantics and unsigned arithmetic. A more explicit “ends with” style check is easier to read and avoids size_t underflow patterns:

    if (filename.size() >= postfix.size() &&
        filename.compare(filename.size() - postfix.size(), postfix.size(), postfix) == 0) {
        std::string niceName = filename.substr(0, filename.size() - postfix.size());
        ...
    }
  2. Directory iteration error handling (optional)
    You already use std::error_code ec with exists and directory_iterator, but never inspect ec. If the directory is unreadable or iteration fails, the UI will silently show an empty list. It might be worth at least logging when ec is set after constructing the iterator, so users get a hint if filesystem permissions or disk issues prevent loading named states.

These are not correctness blockers, just clarity and diagnostics improvements.


187-195: Use std::filesystem::path composition instead of manual "//" string concatenation

The new createSaveStatePath correctly targets g_system->getPersistentDir() / getSaveStatePrefix(false) as the base directory, but the final path is built as a formatted string with a hard‑coded "//" separator:

std::string pathStr = fmt::format("{}//{}{}", base_path.string(), saveStateName, gui->getSaveStatePostfix());
return std::filesystem::path(pathStr);

This is non‑idiomatic and can be fragile across platforms (mixing native separators with string "//"). It’s safer and clearer to let std::filesystem::path handle separators and to simplify directory creation at the same time. For example:

-    std::error_code ec;
-    std::filesystem::path base_path = g_system->getPersistentDir() / gui->getSaveStatePrefix(false);
-    if (!std::filesystem::exists(base_path, ec)) {
-        std::filesystem::create_directory(base_path);
-    }
-
-    std::string pathStr = fmt::format("{}//{}{}", base_path.string(), saveStateName, gui->getSaveStatePostfix());
-    return std::filesystem::path(pathStr);
+    std::error_code ec;
+    std::filesystem::path base_path =
+        g_system->getPersistentDir() / gui->getSaveStatePrefix(false);
+
+    // Create the directory tree if needed; avoid exceptions on failure.
+    std::filesystem::create_directories(base_path, ec);
+    if (ec) {
+        g_system->log(LogClass::UI,
+                      "Failed to create named save directory '%s': %s\n",
+                      base_path.string().c_str(), ec.message().c_str());
+        // Optionally: early-return here if you prefer to abort save on error.
+    }
+
+    std::filesystem::path file_name =
+        saveStateName + gui->getSaveStatePostfix();
+    return base_path / file_name;

This avoids manual separator management, uses the platform’s native path rules, and gives you a single, exception‑free path for directory creation.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b745534 and 37f2d92.

📒 Files selected for processing (1)
  • src/gui/widgets/named_savestates.cc (3 hunks)
🧰 Additional context used
🪛 Cppcheck (2.18.0)
src/gui/widgets/named_savestates.cc

[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.

(toomanyconfigs)


[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.

(toomanyconfigs)


[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.

(toomanyconfigs)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (1)
src/gui/widgets/named_savestates.cc (1)

24-24: Include of core/system.h is appropriate here

Using g_system->getPersistentDir() and g_system->log() in this file requires the core/system.h include; the addition looks correct and expected.

Added logging when a folder is created
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/gui/widgets/named_savestates.cc (2)

145-149: Confirm getSaveStatePrefix(false) semantics for folder naming

You now use getSaveStatePrefix(false) both for listing (line 147) and as the sub-folder in createSaveStatePath, while the UI still displays getSaveStatePrefix(true) above the input. Please double‑check that:

  • the false variant is guaranteed to be a safe directory name (no separators), and
  • the relationship between the shown prefix (true) and the on-disk layout (false) is intentional and documented,
    so users aren’t confused by seeing a prefix that doesn’t literally match the folder name.

189-203: Add error handling/logging around save-state directory creation

The new createSaveStatePath correctly moves named states under a per‑game subfolder in the persistent dir, but the directory creation path is a bit optimistic:

  • std::filesystem::create_directory(base_path); is called without an error_code, and
  • the log unconditionally claims the folder was created, even if the call fails or throws.

Consider using the non‑throwing overload with std::error_code and logging failures explicitly. For example:

-    std::error_code ec;
-    std::string sub_folder = gui->getSaveStatePrefix(false);
-    std::filesystem::path base_path = g_system->getPersistentDir() / sub_folder;
-    if (!std::filesystem::exists(base_path, ec)) {
-        std::filesystem::create_directory(base_path);
-        g_system->log(LogClass::UI, "Created save state folder: %s\n", sub_folder.c_str());
-    }
+    std::error_code ec;
+    std::string sub_folder = gui->getSaveStatePrefix(false);
+    std::filesystem::path base_path = g_system->getPersistentDir() / sub_folder;
+    if (!std::filesystem::exists(base_path, ec)) {
+        if (!std::filesystem::create_directory(base_path, ec)) {
+            if (ec) {
+                g_system->log(LogClass::UI,
+                              "Failed to create save state folder '%s': %s\n",
+                              base_path.string().c_str(), ec.message().c_str());
+            }
+        } else {
+            g_system->log(LogClass::UI, "Created save state folder: %s\n",
+                          base_path.string().c_str());
+        }
+    }

This keeps exceptions out of this path and ensures logs reflect actual outcomes, which is helpful on read‑only or misconfigured persistent directories.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37f2d92 and 7a742af.

📒 Files selected for processing (1)
  • src/gui/widgets/named_savestates.cc (3 hunks)
🧰 Additional context used
🪛 Cppcheck (2.18.0)
src/gui/widgets/named_savestates.cc

[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.

(toomanyconfigs)


[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.

(toomanyconfigs)


[information] Too many #ifdef configurations - cppcheck only checks 12 configurations. Use --force to check all configurations. For more details, use --enable=information.

(toomanyconfigs)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (2)
src/gui/widgets/named_savestates.cc (2)

24-24: Include of core/system.h is appropriate

This include is required for g_system / getPersistentDir() and resolves the static analysis “missing include” hint for this file; no changes needed.


152-167: Harden filename postfix check to avoid length underflow edge case

The current postfix filter can misbehave when filename.length() < postfix.length() because the subtraction wraps on size_t, making the condition unexpectedly true. Guard the length first and then compare the tail explicitly:

-                if (filename.rfind(postfix) == filename.length() - postfix.length()) {
-                    std::string niceName =
-                        filename.substr(0, filename.length() - (postfix.length()));
+                if (filename.size() >= postfix.size() &&
+                    filename.compare(filename.size() - postfix.size(),
+                                     postfix.size(),
+                                     postfix) == 0) {
+                    std::string niceName =
+                        filename.substr(0, filename.size() - postfix.size());

@acemon33
Copy link
Copy Markdown
Contributor

I wonder if it's easier to implement moving the numbered save states too

@pull-request-size pull-request-size Bot added size/M and removed size/S labels May 23, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/gui/widgets/named_savestates.cc`:
- Around line 194-199: Remove the spurious unary plus operators before the
string arguments in the g_system->log call (remove +"Failed..." and
+base_path...) and modify the create-directory failure handling: after calling
std::filesystem::create_directory(base_path, ec), check ec to determine if an
actual error occurred before logging a failure; only call g_system->log with the
error message when ec indicates an error (use ec.message() and
base_path.string().c_str()), otherwise treat a false return with no error as a
non-fatal “already exists” case and log the created/exists message (referencing
std::filesystem::create_directory, ec, base_path, sub_folder, and
g_system->log).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c759db89-18f6-41f1-ad81-4bdd8f21fb8f

📥 Commits

Reviewing files that changed from the base of the PR and between 7a742af and c5bf383.

📒 Files selected for processing (1)
  • src/gui/widgets/named_savestates.cc

Comment thread src/gui/widgets/named_savestates.cc
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/gui/widgets/named_savestates.cc (1)

157-159: 💤 Low value

Consider a safer suffix check to avoid unsigned underflow.

If filename is shorter than postfix, the subtraction filename.length() - postfix.length() underflows. While this typically produces a value different from rfind's result (so files are correctly rejected), it's relying on coincidental behavior.

♻️ Proposed fix using explicit bounds check
-                if (filename.rfind(postfix) == filename.length() - postfix.length()) {
-                    std::string niceName =
-                        filename.substr(0, filename.length() - (postfix.length()));
+                if (filename.length() > postfix.length() &&
+                    filename.compare(filename.length() - postfix.length(), postfix.length(), postfix) == 0) {
+                    std::string niceName = filename.substr(0, filename.length() - postfix.length());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/gui/widgets/named_savestates.cc` around lines 157 - 159, The suffix check
using filename.rfind(postfix) == filename.length() - postfix.length() can
underflow when filename is shorter than postfix; update the logic in the block
that computes niceName so you first ensure filename.size() >= postfix.size() (or
otherwise check bounds) before doing the subtraction, then compare the suffix
(e.g., with rfind or substr) and only compute niceName = filename.substr(0,
filename.length() - postfix.length()) after that guard.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/gui/widgets/named_savestates.cc`:
- Around line 157-159: The suffix check using filename.rfind(postfix) ==
filename.length() - postfix.length() can underflow when filename is shorter than
postfix; update the logic in the block that computes niceName so you first
ensure filename.size() >= postfix.size() (or otherwise check bounds) before
doing the subtraction, then compare the suffix (e.g., with rfind or substr) and
only compute niceName = filename.substr(0, filename.length() - postfix.length())
after that guard.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0d88654a-346d-4d25-bef2-b8f091d12030

📥 Commits

Reviewing files that changed from the base of the PR and between c5bf383 and f2fec3c.

📒 Files selected for processing (1)
  • src/gui/widgets/named_savestates.cc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants