Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SIO0 overhaul #1673

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open

SIO0 overhaul #1673

wants to merge 15 commits into from

Conversation

johnbaumann
Copy link
Collaborator

@johnbaumann johnbaumann commented Jun 23, 2024

Draft PR, trying to shore up various changes I had worked up in preparation of adding multitap support. My initial branch became sorely outdated and conflicted, this branch attempts to cherry pick some of these changes and bring the branch back in line with head. Needs some additional cleanup; get rid of vectors, mentions of multitap, test various things, etc.

  • Moved all/most card + pad specific code out of SIO class
  • Began abstracting generic card & pad functions such as file IO to a parent class

Summary by CodeRabbit

Based on the comprehensive changes across multiple files, here are the release notes:

Release Notes

  • Memory Card Management

    • Introduced a new MemoryCards class to centralize memory card operations
    • Enhanced memory card block information retrieval and management
    • Improved error handling for memory card interactions
  • Pad and SIO Interactions

    • Refined communication protocols for game controllers
    • Streamlined device selection and data transmission methods
    • Added more robust acknowledgment handling
  • Emulator State Management

    • Updated save state structure for more efficient memory card tracking
    • Simplified memory card initialization and configuration
    • Improved type safety with enumerated memory card references
  • User Interface

    • Updated memory card manager to support new memory card management system
    • Enhanced icon and file export capabilities for memory card blocks

These changes represent a significant refactoring of the memory card and device interaction systems, focusing on improved reliability and maintainability.

@johnbaumann johnbaumann marked this pull request as ready for review August 5, 2024 01:57
@johnbaumann
Copy link
Collaborator Author

This one is ready for review. Moved a lot of things around to split the card and pad code away from the sio class.

@nicolasnoble
Copy link
Member

You're making Linux sad :)

@nicolasnoble
Copy link
Member

Looks like there's still an asan error, with a mismatched new/delete type.

Copy link

codecov bot commented Aug 18, 2024

Codecov Report

Attention: Patch coverage is 8.85781% with 391 lines in your changes missing coverage. Please review.

Please upload report for BASE (main@15520a9). Learn more about missing BASE report.

Files Patch % Lines
src/core/memorycard.cc 5.98% 220 Missing ⚠️
src/core/sio.cc 15.68% 43 Missing ⚠️
src/gui/widgets/memcard_manager.cc 0.00% 42 Missing ⚠️
src/core/memorycard.h 11.90% 37 Missing ⚠️
src/core/pad.cc 21.05% 30 Missing ⚠️
src/core/sstate.cc 0.00% 16 Missing ⚠️
src/gui/widgets/memcard_manager.h 0.00% 3 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1673   +/-   ##
=======================================
  Coverage        ?    9.87%           
=======================================
  Files           ?      448           
  Lines           ?   133030           
  Branches        ?        0           
=======================================
  Hits            ?    13143           
  Misses          ?   119887           
  Partials        ?        0           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

std::memcpy(data.get(), g_emulator->m_sio->getMcdData(1), SIO::c_cardSize);
std::memcpy(data.get() + SIO::c_cardSize, g_emulator->m_sio->getMcdData(2), SIO::c_cardSize);
std::unique_ptr<uint8_t[]> data = std::make_unique<uint8_t[]>(MemoryCards::c_cardSize * 2);
std::memcpy(data.get(), g_emulator->m_memoryCards->getMcdData(0), MemoryCards::c_cardSize);
Copy link
Member

Choose a reason for hiding this comment

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

If we're here doing some refactoring, we might as well use proper enums for the memory cards indices, maybe?

// skip id 8
// skip id 9
// skip id 10
// skip id 11
typedef Protobuf::FieldRef<Protobuf::UInt32, TYPESTRING("pad_state"), 12> SIOPadState;
// skip id 12
Copy link
Member

Choose a reason for hiding this comment

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

So, uuuuh, what happens if a savestate is done while an SIO read is in progress? This doesn't seem we're reloading into a proper state, able to continue gracefully. Or we never did, maybe, for memory cards at least?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'll revisit the save state code. Admittedly, I had plans to eventually redo the SIO side of pads, so this escaped my focus. Thank you for catching this. I know pad issues have been noted regarding savestates back in #1173 maybe I can knock this one out in the process.

@johnbaumann
Copy link
Collaborator Author

@coderabbitai full review

Copy link
Contributor

coderabbitai bot commented Jan 5, 2025

✅ Actions performed

Full review triggered.

Copy link
Contributor

coderabbitai bot commented Jan 5, 2025

Walkthrough

The pull request introduces a comprehensive refactoring of memory card and serial input/output (SIO) management in the PCSX emulator. The changes centralize memory card operations into a new MemoryCards class, removing memory card-related functionality from the SIO class. This restructuring enhances code organization, introduces more robust error handling, and provides a clearer interface for memory card interactions. The modifications span multiple files, updating method signatures, adding new methods, and reorganizing how memory card data is accessed and managed throughout the emulator.

Changes

File Change Summary
src/core/memorycard.cc/h Added MemoryCards class with methods for loading, saving, erasing, and managing memory card blocks. Introduced new methods like loadMcds, getMcdBlockInfo, eraseMcdFile, and getFreeSpace.
src/core/pad.cc/h Refined pad interaction methods, adding transceive and deselect methods to improve communication handling.
src/core/psxemulator.cc/h Added m_memoryCards member to Emulator class, introducing a new management point for memory card operations.
src/core/sio.cc/h Removed memory card-specific methods and simplified SIO class functionality.
src/core/sstate.cc/h Updated save state structure to reference memory card data through new m_memoryCards
src/gui/widgets/memcard_manager.cc/h Updated method signatures and memory card handling to use new MemoryCards class

Sequence Diagram

sequenceDiagram
    participant Emulator
    participant MemoryCards
    participant MemoryCard
    participant SIO

    Emulator->>MemoryCards: loadMcds(args)
    MemoryCards->>MemoryCard: createMcd(path)
    MemoryCards->>SIO: writePad/writeCard
    MemoryCard-->>MemoryCards: return block info
    MemoryCards-->>Emulator: memory card loaded
Loading

Poem

🐰 Hop, hop, memory cards anew,
Refactored code, a cleaner view!
SIO's burden, now set free,
Emulator's dance of clarity
Memory managed with rabbit's might! 🎮


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
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: 3

🧹 Nitpick comments (13)
src/core/sio.h (1)

45-45: Consider defaulted destructor.
A trivial destructor could be marked as = default; to clearly convey no resources need custom cleanup.

- ~SIO() {}
+ ~SIO() = default;
src/core/sio.cc (2)

108-126: Code duplication warning (writeCard vs. writePad).
writeCard references logic very similar to writePad. Moving common steps (port index resolution, device check, ack logic) into a shared helper could mitigate duplication.

🧰 Tools
🪛 GitHub Check: CodeScene Cloud Delta Analysis (main)

[warning] 108-126: ❌ New issue: Code Duplication
The module contains 2 functions with similar structure: PCSX::SIO::writeCard,PCSX::SIO::writePad. Avoid duplicated, aka copy-pasted, code inside the module. More duplication lowers the code health.


[notice] 108-145: ✅ No longer an issue: Complex Method
PCSX::SIO::writePad is no longer above the threshold for cyclomatic complexity. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.


187-187: Consider narrowing logging.
sio write8 %x\n might be a frequent log spam in normal runs. A debug-level or conditional logging might help performance.

src/core/memorycard.h (2)

109-116: PocketStation command expansions well-labeled.
Each function is named by the command code (tickPS_GetDirIndex, etc.). This is quite descriptive. Just watch for code duplication if these commands share large blocks of logic.


186-189: Duplicated constants.
MemoryCards::c_sectorSize, c_blockSize, and c_cardSize duplicate the same numeric values as in MemoryCard. Consider referencing a single source of truth to avoid confusion if these ever need changing.

src/gui/widgets/memcard_manager.cc (2)

127-143: Undo system with memory cards.
Reverting to older states by shuttling raw memory is effective but can be memory-heavy. Consider limiting or compressing the undo buffer if large memory cards are changed frequently.


432-432: Exporting icon to PNG.
Naming the file as iconN.png is straightforward. Potentially prompt the user for a path or confirm overwrites for multiple exports in the same folder.

src/core/sstate.cc (1)

89-104: Consider splitting out memory card SIO fields initialization to maintain readability.

While these lines effectively integrate memory card data into the save state, the method constructSaveState() is still quite large and somewhat cumbersome to maintain. Consider extracting memory card SIO-related initialization to a dedicated function or helper object for clarity and better modularity.

🧰 Tools
🪛 GitHub Check: CodeScene Cloud Delta Analysis (main)

[notice] 83-104: ✅ Getting better: Large Method
PCSX::SaveStates::constructSaveState decreases from 138 to 134 lines of code, threshold = 70. Large functions with many lines of code are generally harder to understand and lower the code health. Avoid adding more lines to this function.

src/core/memorycard.cc (2)

255-302: Excessive branching in copyMcdFile.

With a cyclomatic complexity of 11, this function has multiple nested conditionals and loops. Consider extracting repeated or branching logic into well-named helper functions that handle partial tasks, thus enhancing clarity and maintainability.

🧰 Tools
🪛 GitHub Check: CodeScene Cloud Delta Analysis (main)

[warning] 255-302: ❌ New issue: Complex Method
PCSX::MemoryCards::copyMcdFile has a cyclomatic complexity of 11, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.


731-731: Function createMcd at the upper refactoring threshold.

While not severely problematic, static analysis flags that the function’s logical complexity is at the threshold (cyclomatic complexity of 9). Future additions could push it over. Consider carefully grouping or extracting repetitive code to keep it manageable.

🧰 Tools
🪛 GitHub Check: CodeScene Cloud Delta Analysis (main)

[notice] 731-731: ✅ No longer an issue: Complex Method
PCSX::MemoryCard::createMcd is no longer above the threshold for cyclomatic complexity. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.


[warning] 731-731: ❌ New issue: Complex Method
PCSX::MemoryCards::createMcd has a cyclomatic complexity of 9, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.


[notice] 731-731: ✅ No longer an issue: Bumpy Road Ahead
PCSX::MemoryCard::createMcd is no longer above the threshold for logical blocks with deeply nested code. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.


[warning] 731-731: ❌ New issue: Bumpy Road Ahead
PCSX::MemoryCards::createMcd has 2 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is one single, nested block per function. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.

src/core/pad.cc (1)

214-220: Large buffer size allocation.

static constexpr size_t c_padBufferSize = 0x1010; might be more than required for typical pad operations. Evaluate if smaller allocations or a dynamic approach can be used without impacting performance.

src/core/psxemulator.h (1)

268-268: Confirm appropriate destruction order.
Storing MemoryCards as a unique_ptr in the emulator is good for ownership clarity. However, ensure that m_memoryCards is properly reset or closed before other dependent components are destructed.

src/core/sstate.h (1)

139-139: Remove or clarify skipped IDs if unneeded.
These skip comments (e.g. “// skip id 1”) may cause confusion later if they’re never repurposed. Remove them if you no longer need these placeholders.

Also applies to: 144-145, 150-150

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f807850 and 5eb1c3c.

📒 Files selected for processing (13)
  • src/core/memorycard.cc (10 hunks)
  • src/core/memorycard.h (4 hunks)
  • src/core/pad.cc (4 hunks)
  • src/core/pad.h (1 hunks)
  • src/core/psxemulator.cc (1 hunks)
  • src/core/psxemulator.h (2 hunks)
  • src/core/sio.cc (7 hunks)
  • src/core/sio.h (2 hunks)
  • src/core/sstate.cc (1 hunks)
  • src/core/sstate.h (2 hunks)
  • src/gui/widgets/memcard_manager.cc (15 hunks)
  • src/gui/widgets/memcard_manager.h (3 hunks)
  • src/main/main.cc (1 hunks)
🧰 Additional context used
🪛 GitHub Check: CodeScene Cloud Delta Analysis (main)
src/core/sio.cc

[warning] 108-126: ❌ New issue: Code Duplication
The module contains 2 functions with similar structure: PCSX::SIO::writeCard,PCSX::SIO::writePad. Avoid duplicated, aka copy-pasted, code inside the module. More duplication lowers the code health.


[notice] 108-145: ✅ No longer an issue: Complex Method
PCSX::SIO::writePad is no longer above the threshold for cyclomatic complexity. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.


[notice] 152-177: ✅ No longer an issue: Complex Method
PCSX::SIO::transmitData is no longer above the threshold for cyclomatic complexity. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.


[notice] 152-177: ✅ No longer an issue: Bumpy Road Ahead
PCSX::SIO::transmitData is no longer above the threshold for logical blocks with deeply nested code


[notice] 152-177: ✅ No longer an issue: Deep, Nested Complexity
PCSX::SIO::transmitData is no longer above the threshold for nested complexity depth

src/core/sstate.cc

[notice] 83-104: ✅ Getting better: Large Method
PCSX::SaveStates::constructSaveState decreases from 138 to 134 lines of code, threshold = 70. Large functions with many lines of code are generally harder to understand and lower the code health. Avoid adding more lines to this function.

src/core/memorycard.cc

[warning] 46-174: ❌ New issue: Complex Method
PCSX::MemoryCards::getMcdBlockInfo has a cyclomatic complexity of 31, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.


[warning] 255-302: ❌ New issue: Complex Method
PCSX::MemoryCards::copyMcdFile has a cyclomatic complexity of 11, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.


[warning] 46-174: ❌ New issue: Bumpy Road Ahead
PCSX::MemoryCards::getMcdBlockInfo has 2 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is one single, nested block per function. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.


[warning] 514-514: ❌ Getting worse: Code Duplication
introduced similar code in: PCSX::MemoryCards::findFirstFree,PCSX::MemoryCards::getFreeSpace. Avoid duplicated, aka copy-pasted, code inside the module. More duplication lowers the code health.


[notice] 731-731: ✅ No longer an issue: Complex Method
PCSX::MemoryCard::createMcd is no longer above the threshold for cyclomatic complexity. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.


[warning] 731-731: ❌ New issue: Complex Method
PCSX::MemoryCards::createMcd has a cyclomatic complexity of 9, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.


[notice] 731-731: ✅ No longer an issue: Bumpy Road Ahead
PCSX::MemoryCard::createMcd is no longer above the threshold for logical blocks with deeply nested code. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.


[warning] 731-731: ❌ New issue: Bumpy Road Ahead
PCSX::MemoryCards::createMcd has 2 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is one single, nested block per function. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.

🔇 Additional comments (47)
src/gui/widgets/memcard_manager.h (4)

30-30: Prefer narrower include scopes.
Including "core/memorycard.h" is logically correct. However, keep an eye on compile/link times if there's an opportunity to forward-declare classes and move includes to the .cpp file.


60-65: Consistent naming for icon-related helper methods.
All methods dealing with icons (getIconRGBA8888, drawIcon, exportPNG, copyToClipboard, getPocketstationIcon) maintain a clear naming pattern. Good job keeping them grouped for clarity.


70-73: Ensure boundary checks or exception handling for raw memory operations.
Using std::memcpy on two separately allocated buffers is fine. It's assumed that MemoryCards::c_cardSize * 2 can hold 2 memory cards. Verify that these calls and the destinations (getMcdData(MemoryCard::Which::One/Two)) always match the declared size to avoid accidental overflows.


82-82: Enum usage aligns with past feedback.
Using MemoryCard::Which instead of an integer is consistent and less error-prone when referencing memory cards.

src/core/sio.h (1)

168-169: Write methods for cards and pads unify SIO responsibilities.
The new writeCard and writePad methods return uint8_t for data read-back. This is a clarity improvement over the previous approach of returning void.

src/core/sio.cc (7)

47-47: Deferred toggling of pocketstation mode removed.
init() now only sets up pads and calls reset(). Ensure that any prior toggling logic of pocketstation mode is truly unnecessary.


87-89: Check correctness of transmit-ready condition.
(txEnabled && txFinished && !txDataEmpty) in isTransmitReady() might change existing logic. Verify that the data flow still triggers transmissions as intended, especially for corner cases where TX has data but is flagged finished.


96-105: Reinitialize data on reset.
Clearing FIFOs, resetting status flags, and deselecting devices ensure fresh states upon reset. Good defensive design. Just ensure other SIO members aren’t left uninitialized accidentally.


128-145: Clean approach for pad transceive.
writePad parallels writeCard. The early device ignoring is a neat fallback. Consider logging or debugging output if the device is not connected.

🧰 Tools
🪛 GitHub Check: CodeScene Cloud Delta Analysis (main)

[notice] 108-145: ✅ No longer an issue: Complex Method
PCSX::SIO::writePad is no longer above the threshold for cyclomatic complexity. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.


152-177: Simplify device selection in transmitData().
Switching on m_currentDevice to handle either Pad, MemoryCard, or ignoring input is clear. The final push to m_rxFIFO is done consistently. Just confirm that other device types cannot appear in production or handle them with a fallback.

🧰 Tools
🪛 GitHub Check: CodeScene Cloud Delta Analysis (main)

[notice] 152-177: ✅ No longer an issue: Complex Method
PCSX::SIO::transmitData is no longer above the threshold for cyclomatic complexity. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.


[notice] 152-177: ✅ No longer an issue: Bumpy Road Ahead
PCSX::SIO::transmitData is no longer above the threshold for logical blocks with deeply nested code


[notice] 152-177: ✅ No longer an issue: Deep, Nested Complexity
PCSX::SIO::transmitData is no longer above the threshold for nested complexity depth


205-206: Validate port switching logic and device deselection.

  • Lines 205–206: port_changed indicates a toggled port bit. Ensure correctness when dynamically switching from Port1 to Port2.
  • Lines 219–220: Both memory card and pad are deselected on deselect or port change, which is correct.
  • Line 234: reset() upon RESET in control flags can disrupt ongoing transmissions. Confirm correct flow in the actual hardware model.
  • Line 244: The final check for was_ready == false ensures immediate transmit if lines are now ready.
    Looks consistent, but thorough testing is advised.

Also applies to: 210-210, 216-216, 219-220, 234-234, 244-244


259-259: No issues found.
Reading byte from FIFO is straightforward, with the SIO status updated properly.

src/core/memorycard.h (14)

24-26: Check for minimal includes.
Including "core/psxemulator.h" and "core/sstate.h" is likely essential here as the code strongly references g_emulator and save states. Forward declarations may reduce compilation time if not all details of these headers are needed immediately.


31-41: Enum-based card selection is neat.
Defining MemoryCard::Which clarifies usage. This addresses prior feedback about using typed enums instead of bare integers for memory card indices.


53-58: Reset method consolidates state.
deselect() followed by reinitializing m_directoryFlag ensures a clean reset for the memory card’s state machine. Good practice.


61-61: Commit method usage.
commit() is declared but not shown here. Verify it robustly handles finalizing saves or notifying the emulator. Ensure it’s being invoked where needed.


104-106: Constants alignment.
c_sectorSize, c_blockSize, and c_cardSize define the card geometry. Double-check that the entire codebase references these constants consistently to avoid mismatch in read/write calls.


Line range hint 117-137: Good usage of friend classes for direct memory access.
Exposing memory card internals to SIO and SaveStates::constructSaveState() is acceptable for low-level hardware emulation. Just keep an eye on encapsulation so that only essential internals are friend-accessible.


139-141: Clear doc comment.
The heading for MemoryCards clarifies it as a higher-level manager for multiple memory cards, bridging the GUI and emulator. Nicely done.


142-147: Deselect all memory cards.
MemoryCards::deselect() calls deselect() on each. Straightforward. Good for ensuring no leftover states.


149-152: reset() for both memory cards.
Similar approach—ensures both are in a pristine state with m_directoryFlag reset. Good consistency with the individual MemoryCard::reset().


154-184: McdBlock struct captures essential fields.
Title, ID, name, icon data, etc., all in one place. The reset() method is thorough. The isChained() and isErased() checks are straightforward. Keep up the clarity.


190-203: Memory card file manipulations.
copyMcdFile, eraseMcdFile, and findFirstFree reflect a comprehensive set of operations. Just ensure you gracefully handle errors (e.g. block not found or corrupted data).
[approve]


204-219: File operations and path retrieval.
loadMcds, saveMcd, createMcd, and getMcdPath incorporate a mix of file I/O and internal memory pointers. Ensure robust error handling for I/O failures.


226-231: isCardInserted() complements dynamic memory card states.
The logic references emulator settings. Seems consistent with your approach. Integration tests recommended to confirm hot-plugging works as expected.


232-237: resetCard and setPocketstationEnabled.
Exposes straightforward methods to manipulate card states. The array m_memoryCard[2] is well-structured to store both cards.

src/gui/widgets/memcard_manager.cc (11)

28-28: Magic Enum usage.
magic_enum usage for enumerations is excellent to avoid manual conversions. Confirm the library remains consistently included across the project and is licensed compatibly.


67-79: Import/export selection by enumerations.
When a user chooses import/export on memory card 1 or 2, you set m_memoryCardImportExportIndex to MemoryCard::Which::One/Two. This is more robust than using integer IDs.


93-97: Loading memory card in place.
loadMcd(...) into getMcdData(...) is direct. Confirm that overwriting data in memory is safe. Possibly consider prompting to back up existing data if there's an unintended overwrite scenario.


111-111: Export to file.
Retrieving the card data from getMcdData and directly writing 128 * 1024 bytes is straightforward. Confirm that partial or short writes are handled (e.g. out->failed()).


171-181: Toggling PocketStation.
setPocketstationEnabled is called for each card. Looks correct. Ensure the user’s setting persists if the emulator restarts (save to config or a persistent storage).


Line range hint 194-296: Detailed usage table in a lambda.
Drawing the blocks in a table with actions (erase/copy/move/export icon) is well-structured. The free-space check ensures moves/copies won't fail mid-transfer. Great user experience.


323-327: Tabbed interface for Card 1 and 2.
Switching between the memory cards in tabs and calling the same draw lambda is a reusable design.


338-338: No immediate issues with drawIcon.
The method properly updates the OpenGL texture for the current frame. Confirm performance is acceptable for frequent re-drawing of icons.


365-367: Ensure valid pointer arithmetic for PocketStation icons.
titleFrame offset calculations rely on reading data from memory. Confirm that the offsets, especially with (titleFrame[0x2] & 0xf), are always in range and won't cause out-of-bounds access.


391-391: Flexible approach to alpha shift.
getIconRGBA8888 sets up masks and shifts for RGBA. Good approach for cross-platform usage.


438-438: Clipboard copy.
clip::set_image with the icon image is a nice convenience feature. Thorough testing recommended across platforms.

src/core/pad.cc (4)

68-72: Deselect method is concise and clear.

The loop gracefully deselects each pad. Good approach for resetting pad states simultaneously.


74-75: Possible out-of-bounds risk in transceive(...).

This directly indexes m_pads[index]. Ensure that 0 <= index < m_pads.size(). Consider gracefully handling out-of-range inputs with a safe return or log message.


177-182: Pad::deselect effectively resets pad state.

This method sets internal indices and states to zero or idle. Looks correct and straightforward.


64-64: Check index validity in isPadConnected.

The code checks if pad exceeds the array size. That’s good, but ensure pad is also non-negative. Otherwise, negative input might lead to unintended array access.

Run this script to confirm no negative values are passed throughout the codebase:

src/core/pad.h (3)

32-32: Forward declaration of class SIO.

Having a forward declaration here is fine if you’re planning to reference SIO without including its header, but confirm that you don’t need additional declarations or an actual include if SIO is used beyond pointers/references.


40-44: InputDevice abstraction.

These pure virtual methods nicely decouple pad state retrieval from the rest of the system, aligning well with SOLID principles.


49-50: New deselect and transceive methods.

Replacing the old polling approach with a single transceive method plus a deselect call clarifies responsibilities. This likely simplifies the SIO-level logic.

src/core/psxemulator.cc (1)

69-69: Initialization of m_memoryCards.

This pointer-based approach is acceptable, given the single responsibility for lifetime management in the Emulator constructor/destructor. Confirm that no additional initialization sequence is needed before usage.

src/core/psxemulator.h (1)

81-81: Forward declaration looks appropriate.
This forward declaration helps reduce coupling between headers. Keep it up!

src/main/main.cc (1)

234-235: Handle or log errors from loadMcds.
When loadMcds fails to load memory cards, it’s unclear whether exceptions or error codes are returned. Consider adding robust error handling or logging here to avoid silent failures.

Comment on lines +46 to +174
}

// Poor man's SJIS to ASCII conversion
if (c >= 0x8281 && c <= 0x829a) {
c = (c - 0x8281) + 'a';
} else if (c >= 0x824f && c <= 0x827a) {
c = (c - 0x824f) + '0';
} else if (c == 0x8140) {
c = ' ';
} else if (c == 0x8143) {
c = ',';
} else if (c == 0x8144) {
c = '.';
} else if (c == 0x8146) {
c = ':';
} else if (c == 0x8147) {
c = ';';
} else if (c == 0x8148) {
c = '?';
} else if (c == 0x8149) {
c = '!';
} else if (c == 0x815e) {
c = '/';
} else if (c == 0x8168) {
c = '"';
} else if (c == 0x8169) {
c = '(';
} else if (c == 0x816a) {
c = ')';
} else if (c == 0x816d) {
c = '[';
} else if (c == 0x816e) {
c = ']';
} else if (c == 0x817c) {
c = '-';
} else if (c > 0x7e) {
c = '?';
}

ta += c;
}

info.titleUtf8 = Sjis::toUtf8(ts);

// Read CLUT
ptr = reinterpret_cast<uint8_t *>(data) + block * c_blockSize + 0x60;
std::memcpy(clut, ptr, 16 * sizeof(uint16_t));

// Icons can have 1 to 3 frames of animation
for (uint32_t i = 0; i < info.iconCount; i++) {
uint16_t *icon = &info.icon[i * 16 * 16];
ptr = reinterpret_cast<uint8_t *>(data) + block * c_blockSize + 128 + 128 * i; // icon data

// Fetch each pixel, store it in the icon array in ABBBBBGGGGGRRRRR with the alpha bit set to 1
for (x = 0; x < 16 * 16; x++) {
const uint8_t entry = (uint8_t)*ptr;
icon[x++] = clut[entry & 0xf] | (1 << 15);
icon[x] = clut[entry >> 4] | (1 << 15);
ptr++;
}
}

// Parse directory frame info
const auto directoryFrame = (uint8_t *)data + block * c_sectorSize;
uint32_t allocState = 0;
allocState |= directoryFrame[0];
allocState |= directoryFrame[1] << 8;
allocState |= directoryFrame[2] << 16;
allocState |= directoryFrame[3] << 24;
info.allocState = allocState;

char tmp[17];
memset(tmp, 0, sizeof(tmp));
std::strncpy(tmp, (const char *)&directoryFrame[0xa], 12);
info.id = tmp;
memset(tmp, 0, sizeof(tmp));
std::strncpy(tmp, (const char *)&directoryFrame[0x16], 16);
info.name = tmp;

uint32_t fileSize = 0;
fileSize |= directoryFrame[4];
fileSize |= directoryFrame[5] << 8;
fileSize |= directoryFrame[6] << 16;
fileSize |= directoryFrame[7] << 24;
info.fileSize = fileSize;

uint16_t nextBlock = 0;
nextBlock |= directoryFrame[8];
nextBlock |= directoryFrame[9] << 8;
info.nextBlock = nextBlock == 0xffff ? -1 : (nextBlock + 1);

// Check if the block is marked as free in the directory frame and adjust the name/filename if so
if (info.isErased()) {
info.reset();
info.allocState = 0xa0;
info.titleAscii = "Free Block";
info.titleSjis = "Free Block";
info.titleUtf8 = "Free Block";
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

High cyclomatic complexity in getMcdBlockInfo.

Static analysis indicates a cyclomatic complexity of 31. This method contains multiple loops and conditionals, making it harder to maintain. Refactor the logic into smaller helper functions to reduce complexity and improve readability.

🧰 Tools
🪛 GitHub Check: CodeScene Cloud Delta Analysis (main)

[warning] 46-174: ❌ New issue: Complex Method
PCSX::MemoryCards::getMcdBlockInfo has a cyclomatic complexity of 31, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.


[warning] 46-174: ❌ New issue: Bumpy Road Ahead
PCSX::MemoryCards::getMcdBlockInfo has 2 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is one single, nested block per function. The Bumpy Road code smell is a function that contains multiple chunks of nested conditional logic. The deeper the nesting and the more bumps, the lower the code health.

Comment on lines +36 to +38
if (argPath2.has_value()) {
settings.get<PCSX::Emulator::SettingMcd2>() = argPath1.value();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Possible bug: using argPath1.value() instead of argPath2.value().

Here, if argPath2 is provided, you're still assigning argPath1 to SettingMcd2. This likely overwrites the path for memory card 2 with card 1’s path.

- settings.get<PCSX::Emulator::SettingMcd2>() = argPath1.value();
+ settings.get<PCSX::Emulator::SettingMcd2>() = argPath2.value();
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (argPath2.has_value()) {
settings.get<PCSX::Emulator::SettingMcd2>() = argPath1.value();
}
if (argPath2.has_value()) {
settings.get<PCSX::Emulator::SettingMcd2>() = argPath2.value();
}

Comment on lines +171 to +175
typedef Protobuf::Message<TYPESTRING("SIO"), SIOStatusReg, SIOModeReg, SIOCtrlReg, SIOBaudReg, SIOCurrentDevice,
SIOMCD1TempBuffer, SIOMCD1DirectoryFlag, SIOMCD1ChecksumIn, SIOMCD1ChecksumOut,
SIOMCD1CommandTicks, SIOMCD1CurrentCommand, SIOMCD1Sector, SIOMCD1DataOffset,
SIOMCD2TempBuffer, SIOMCD2DirectoryFlag, SIOMCD2ChecksumIn, SIOMCD2ChecksumOut,
SIOMCD2CommandTicks, SIOMCD2CurrentCommand, SIOMCD2Sector, SIOMCD2DataOffset>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Reassess embedding memory card fields in SIO.
You’re factoring memory card logic out of the SIO class, yet these fields still reside in the SIO Protobuf definition. Confirm whether you want to keep them here or migrate them to a more suitable location to reflect the new architecture.

I can propose a revised Protobuf layout if you’d like me to open a new issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants