Skip to content

Create generic storage struct#24

Open
scifi6546 wants to merge 30 commits intobenkonz:masterfrom
scifi6546:master
Open

Create generic storage struct#24
scifi6546 wants to merge 30 commits intobenkonz:masterfrom
scifi6546:master

Conversation

@scifi6546
Copy link
Contributor

I want to create a generic struct for storing the save game. I am not sure how to organize the code base so that I can have a custom storage. I am writing a web server and I want to be able to store data on the server. This code will have custom URL paths that are not relevant to the project overall and I am not sure how to organize the project so that extra code is not upstreamed

None
}
fn save(&mut self, ram: Vec<u8>) {
let save_str: String = ram.iter().map(|x| format!("{:02X}", x)).collect();
Copy link
Owner

Choose a reason for hiding this comment

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

I tried doing saving by mapping the ram to a string like this originally, but the problem was that it was just too slow. Some games can have up to 2MB of RAM, so mapping every single byte to a string every frame slows the game down by a lot

Copy link
Contributor Author

Choose a reason for hiding this comment

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

How did you solve the issue?

Copy link
Owner

Choose a reason for hiding this comment

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

Heh, my solution is a little hack-y, but has worked well so far. Basically, we maintain a String that is an exact copy of whatever is in the cartridge RAM and whenever it changes, update the String in-place.

This initializes the Ram String to the RAM contents before the emulation starts

  let ram_str = Rc::new(RefCell::new(
        ram.iter().map(|byte| format!("{:02x}", byte)).collect(),
    ));

This set's up an event listener for whenever the contents of the Cartridge RAM changes

 self.gameboy
            .set_ram_change_callback(Box::new(move |address, value| {
                if has_battery {
                    let byte_chars: Vec<char> = format!("{:02x}", value).chars().collect();
                    let (first, second) = (byte_chars[0] as u8, byte_chars[1] as u8);
                    // unsafe because we are updating the RAM String in-place
                    // but since we are updating it with valid UTF-8, the updated string will also be UTF-8
                    unsafe {
                        let mut ram_str_ref = ram_str.borrow_mut();
                        let bytes = ram_str_ref.as_bytes_mut();
                        bytes[address * 2] = first;
                        bytes[address * 2 + 1] = second;
                    }
                    *should_save_to_local.borrow_mut() = true;
                }
            }));

This takes the O(n) Vec to String mapping to a O(1) in-place update, which is a lot faster, but requires a bit more code and unsafe Rust.

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