Emu?
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

223 lines
6.7 KiB

/* main.cpp
*
* The big, magical entry point.
*/
#include <iostream>
#include <string>
#include <vector>
#include "cxxopts.hpp"
#include "PlipInstance.h"
#include "Config.h"
#include "SDL/SdlAudio.h"
#include "SDL/SdlEvent.h"
#include "SDL/SdlWindow.h"
#ifdef UNIX
#include "Timer/TimerPosix.h"
#else
#include "Timer/TimerSdl.h"
#endif
std::vector<std::vector<std::string>> defaultConfig = {
{ "video", "scale" , "1" },
{ "video", "targetFps", "60" }
};
std::vector<std::vector<std::string>> intParamMapping = {
{ "scale", "video", "scale" },
{ "fps" , "video", "targetFps" }
};
void gameLoop(Plip::PlipInstance *plip, PlipSdl::Config *config, PlipSdl::SdlEvent *event, PlipSdl::Timer *timer) {
auto audio = plip->GetAudio();
auto video = plip->GetVideo();
auto targetFps = config->GetValue<int>("video", "targetFps");
auto frameTime = 1000000000 / targetFps;
auto running = true;
while(running) {
timer->StopwatchStart();
if(event->ProcessEvents() == PlipSdl::SdlUiEvent::Quit)
running = false;
// As implemented, this will not be able to compensate for the host being
// unable to keep up with the emulation core.
// TODO: Fix this so that it will skip frames where appropriate.
plip->Run(frameTime);
auto time = timer->StopwatchStop();
auto delay = frameTime - time;
while(delay < 0)
delay += frameTime;
timer->Nanosleep(delay);
}
audio->DequeueAll();
}
cxxopts::ParseResult parseCmdLine(int argc, char **argv) {
try {
cxxopts::Options options(argv[0]);
options.positional_help("CORE FILENAME")
.show_positional_help();
options.add_options(cxxopts::hidden_group)
("core", "the core that should be used", cxxopts::value<std::string>())
("filename", "the path to the ROM", cxxopts::value<std::string>())
("positional", "", cxxopts::value<std::vector<std::string>>())
;
options.add_options()
("h,help", "shows this help screen and exits")
("c,config", "specifies a config file", cxxopts::value<std::string>())
("l,list-cores", "shows a list of all supported cores and exits")
("V,version", "displays the version information and exits")
;
options.add_options("Video")
( "f,fps", "sets the target frame rate", cxxopts::value<int>()->default_value("60"))
( "s,scale", "sets the default window scaling", cxxopts::value<int>()->default_value("1"))
;
options.parse_positional({"core", "filename", "positional"});
auto result = options.parse(argc, argv);
if(result.count("help")) {
std::cout << options.help() << std::endl;
exit(0);
}
return result;
} catch(cxxopts::argument_incorrect_type &ex) {
std::cerr << "Invalid argument type (" << ex.what() << ")" << std::endl;
exit(1);
} catch(cxxopts::missing_argument_exception &ex) {
std::cerr << ex.what() << std::endl;
exit(1);
} catch(cxxopts::OptionException &ex) {
std::cerr << "Error parsing arguments (" << ex.what() << ")" << std::endl;
exit(1);
}
}
int main(int argc, char **argv) {
auto opts = parseCmdLine(argc, argv);
auto version = Plip::PlipInstance::GetVersion();
if(opts.count("version")) {
std::cout << version << std::endl;
return 0;
}
auto coreList = Plip::PlipInstance::GetSupportedCores();
if(opts["list-cores"].count()) {
std::cout << "Supported cores:\n\n";
std::cout << " Name | Description\n";
std::cout << "--------------------------------\n";
for(auto core : coreList) {
std::string name = core.name;
name.append(10 - name.length(), ' '); // Pad string.
std::cout << " " << name << " | " << core.description << '\n';
}
std::cout << std::endl;
return 0;
}
if(!opts.count("core") || !opts.count("filename")) {
std::cerr << "The name of the core and the filename must be specified!\n\n"
<< "Please see the usage information (" << argv[0] << " -h) for more information." << std::endl;
return 1;
}
SDL_Init(0);
auto config = new PlipSdl::Config();
for(auto opt : defaultConfig)
config->SetValue(opt[0], opt[1], opt[2]);
if(opts["config"].count()) {
auto configFile = opts["config"].as<std::string>();
if(!config->LoadFile(configFile))
std::cerr << "Error opening config file: " << configFile << std::endl;
}
// Check for core name validity.
auto found = false;
auto coreName = opts["core"].as<std::string>();
auto filename = opts["filename"].as<std::string>();
Plip::PlipValidCore coreTag;
for(auto core : coreList) {
if(core.name != coreName) continue;
found = true;
coreTag = core.descriptor;
break;
}
if(!found) {
std::cout << "Invalid core specified (" << coreName << ").\n\n";
std::cout << "Please check the core list (" << argv[0] << " -l) for valid entries." << std::endl;
return 1;
}
for(auto param : intParamMapping) {
if(opts[param[0]].count()) {
config->SetValue(
param[1], param[2],
std::to_string(opts[param[0]].as<int>())
);
}
}
auto videoScale = config->GetValue<int>("video", "scale");
auto wnd = new PlipSdl::SdlWindow(videoScale, version);
auto audio = new PlipSdl::SdlAudio();
auto plip = new Plip::PlipInstance(wnd, audio);
#ifdef UNIX
auto timer = new PlipSdl::TimerPosix();
#else
auto timer = new PlipSdl::TimerSdl();
#endif
auto result = plip->Load(coreTag, filename);
switch(result) {
case Plip::PlipError::FileNotFound:
std::cout << "File not found (" << filename << ")!\n" << std::endl;
return 1;
case Plip::PlipError::InvalidCore:
std::cout << "BUG: Core implemented improperly!\n" << std::endl;
return 1;
default:
break;
}
auto input = plip->GetInput();
auto event = new PlipSdl::SdlEvent(input);
// Load inputs for the active core.
std::string section = "input." + coreName;
for(const auto& coreInput : input->GetInputList()) {
auto key = config->GetValue(section, coreInput.second.GetDescription());
if(key == config->empty) continue;
event->AddDigitalBinding(coreInput.first, key);
}
gameLoop(plip, config, event, timer);
SDL_Quit();
return 0;
}