Skip to content
Snippets Groups Projects
movie-bk2.cpp 6.34 KiB
Newer Older
  • Learn to ignore specific revisions
  • Vicki Pfau's avatar
    Vicki Pfau committed
    #include "movie-bk2.h"
    
    #include <cstring>
    #include <sstream>
    
    #include "coreinfo.h"
    
    using namespace std;
    using namespace Retro;
    
    static const unordered_map<string, char> s_keyNames{
    	make_pair("A", 'A'),
    	make_pair("B", 'B'),
    	make_pair("C", 'C'),
    	make_pair("X", 'X'),
    	make_pair("Y", 'Y'),
    	make_pair("Z", 'Z'),
    	make_pair("START", 'S'),
    	make_pair("SELECT", 's'),
    	make_pair("MODE", 'M'),
    	make_pair("UP", 'U'),
    	make_pair("DOWN", 'D'),
    	make_pair("LEFT", 'L'),
    	make_pair("RIGHT", 'R'),
    	make_pair("L", 'l'),
    	make_pair("R", 'r'),
    	make_pair("BUTTON", 'B'),
    };
    
    static const unordered_map<char, string> s_buttonNames{
    	make_pair('A', "A"),
    	make_pair('B', "B"),
    	make_pair('C', "C"),
    	make_pair('X', "X"),
    	make_pair('Y', "Y"),
    	make_pair('Z', "Z"),
    	make_pair('S', "Start"),
    	make_pair('s', "Select"),
    	make_pair('M', "Mode"),
    	make_pair('U', "Up"),
    	make_pair('D', "Down"),
    	make_pair('L', "Left"),
    	make_pair('R', "Right"),
    	make_pair('l', "L"),
    	make_pair('r', "R"),
    };
    
    static const unordered_map<string, unordered_map<char, string>> s_platformButtonNames{
    	make_pair<string, unordered_map<char, string>>("Atari2600", {
    																	make_pair('B', "Button"),
    																}),
    };
    
    unique_ptr<Movie> MovieBK2::load(const string& path) {
    	unique_ptr<Zip> zip = make_unique<Zip>(path);
    	if (!zip->open()) {
    		return nullptr;
    	}
    	if (!zip->openFile("Input Log.txt")) {
    		return nullptr;
    	}
    	return make_unique<MovieBK2>(move(zip));
    }
    
    MovieBK2::MovieBK2(unique_ptr<Zip> zip)
    	: m_zip(move(zip))
    	, m_log(m_zip->openFile("Input Log.txt")) {
    	string platform;
    
    	Zip::File* header = m_zip->openFile("Header.txt");
    	while (true) {
    		string headerLine = header->readline();
    		if (headerLine.empty()) {
    			break;
    		}
    		if (headerLine.compare(0, 8, "Platform") == 0) {
    			platform = headerLine.substr(9);
    			if (platform == "GEN") {
    				platform = "Genesis";
    			} else if (platform == "A26") {
    				platform = "Atari2600";
    			}
    			loadKeymap(platform);
    		}
    		if (headerLine.compare(0, 8, "GameName") == 0) {
    			m_gameName = headerLine.substr(9);
    		}
    	}
    
    	loadState();
    }
    
    MovieBK2::MovieBK2(const std::string& path, bool write)
    	: m_zip(make_unique<Zip>(path))
    	, m_write(write) {
    	m_zip->open(write);
    	m_log = m_zip->openFile("Input Log.txt", write);
    	if (write) {
    		stringstream headerText;
    		headerText << "[Input]" << endl;
    		m_log->write(static_cast<const void*>(headerText.str().c_str()), headerText.str().size());
    	} else {
    		loadState();
    	}
    }
    
    MovieBK2::~MovieBK2() {
    	close();
    }
    
    string MovieBK2::getGameName() const {
    	return m_gameName;
    }
    
    void MovieBK2::loadKeymap(const string& platform) {
    	vector<string> buttons = Retro::buttons(platform);
    	for (int i = 0; i < buttons.size(); ++i) {
    		const auto& button = s_keyNames.find(buttons[i]);
    		if (button != s_keyNames.end()) {
    			m_keymap[button->second] = i;
    			m_buttonmap[i] = button->second;
    		}
    	}
    	if (m_write) {
    		string realPlatform = platform;
    		if (platform == "Genesis") {
    			realPlatform = "GEN";
    		} else if (platform == "Atari2600") {
    			realPlatform = "A26";
    		}
    		m_coreName = platform;
    		m_platform = realPlatform;
    	}
    }
    
    void MovieBK2::setGameName(const std::string& name) {
    	m_gameName = name;
    }
    
    void MovieBK2::writeHeader() {
    	if (m_headerWritten) {
    		return;
    	}
    	Zip::File* header = m_zip->openFile("Header.txt", true);
    	stringstream headerText;
    	headerText << "MovieVersion Retro" << endl;
    	headerText << "Author ?" << endl;
    	headerText << "emuVersion ?" << endl;
    	headerText << "Platform " << m_platform << endl;
    	headerText << "GameName " << m_gameName << endl;
    	headerText << "SHA1 ?" << endl;
    	headerText << "Core ?" << endl;
    	headerText << "rerecordCount 1" << endl;
    	header->write(static_cast<const void*>(headerText.str().c_str()), headerText.str().size());
    
    	headerText.str("LogKey:#Reset|Power|#");
    	for (const auto& key : m_buttonmap) {
    		if (s_platformButtonNames.find(m_coreName) != s_platformButtonNames.end()) {
    			const auto& platformButtons = s_platformButtonNames.at(m_coreName);
    			if (platformButtons.find(key.second) != platformButtons.end()) {
    				headerText << "P1 " << platformButtons.at(key.second) << "|";
    				continue;
    			}
    		}
    		headerText << "P1 " << s_buttonNames.at(key.second) << "|";
    	}
    	headerText << endl;
    	m_headerWritten = true;
    	m_log->write(static_cast<const void*>(headerText.str().c_str()), headerText.str().size());
    }
    
    bool MovieBK2::step() {
    	if (!m_log) {
    		return false;
    	}
    	if (m_write) {
    		if (!m_headerWritten) {
    			writeHeader();
    		}
    		stringstream line;
    		line << "|..|";
    		for (const auto& key : m_buttonmap) {
    			if (m_keys & (1 << key.first)) {
    				line << key.second;
    			} else {
    				line << '.';
    			}
    		}
    		line << '|';
    		line << endl;
    		m_log->write(static_cast<const void*>(line.str().c_str()), line.str().size());
    		m_keys = 0;
    		return true;
    	} else {
    		string tmp = m_log->readline();
    		while (tmp.size() && tmp[0] != '|') {
    			tmp = m_log->readline();
    		}
    		if (!tmp.size()) {
    			return false;
    		}
    		m_keys = 0;
    		auto iter = tmp.begin();
    		if (*iter == '|') {
    			++iter;
    			while (iter != tmp.end() && *iter != '|') {
    				// Ignore commands
    				++iter;
    			}
    			if (iter == tmp.end()) {
    				return false;
    			}
    			++iter;
    			while (iter != tmp.end() && *iter != '|') {
    				if (*iter == '.') {
    					++iter;
    					continue;
    				}
    				auto found = m_keymap.find(*iter);
    				if (found != m_keymap.end()) {
    					m_keys |= 1 << found->second;
    				}
    				++iter;
    			}
    			return true;
    		}
    	}
    	return false;
    }
    
    void MovieBK2::close() {
    	if (!m_zip) {
    		return;
    	}
    	if (m_write) {
    		const char* footerText = "[/Input]";
    		m_log->write(static_cast<const void*>(footerText), strlen(footerText));
    		if (!m_state.empty()) {
    			auto state = m_zip->openFile("Core.bin", true);
    			state->write(m_state.data(), m_state.size());
    			state->flush();
    		}
    	}
    	m_zip->close();
    	m_zip.reset();
    }
    
    bool MovieBK2::getState(vector<uint8_t>* state) const {
    	if (m_state.empty()) {
    		return false;
    	}
    	state->resize(m_state.size());
    	memcpy(state->data(), m_state.data(), m_state.size());
    	return true;
    }
    
    void MovieBK2::setState(const uint8_t* state, size_t size) {
    	m_state.resize(size);
    	memcpy(m_state.data(), state, size);
    }
    
    void MovieBK2::loadState() {
    	Zip::File* state = m_zip->openFile("Core.bin");
    	if (!state) {
    		m_state.clear();
    		return;
    	}
    	m_state.clear();
    	m_state.resize(2048);
    	ssize_t read;
    	while ((read = state->read(&*(m_state.end() - 2048), 2048)) == 2048) {
    		m_state.resize(m_state.size() + 2048);
    	}
    	m_state.resize(m_state.size() - 2048 + read);
    }