Browse Source

Added a config file reader and basic event loop.

* Moved test event loop into SdlEvent. Currently only checks for SDL_QUIT.
    * Window scale and target frame rate can now be specified in the config.
    * Target frame rate can now be specified on the command line.
master
Ian Burgmyer 4 years ago
parent
commit
88122f4a8c
  1. 1
      CMakeLists.txt
  2. 5
      libplip/Input/PlipInput.cpp
  3. 120
      plip-sdl/Config.cpp
  4. 26
      plip-sdl/Config.h
  5. 16
      plip-sdl/SDL/SdlEvent.cpp
  6. 12
      plip-sdl/SDL/SdlEvent.h
  7. 68
      plip-sdl/main.cpp
  8. 2
      version.cmake

1
CMakeLists.txt

@ -52,6 +52,7 @@ add_dependencies(${lib_name} GENERATE_LIB_VERSION_HEADER)
########
add_executable(${gui_name}
plip-sdl/main.cpp
plip-sdl/Config.cpp
plip-sdl/SDL/SdlEvent.cpp
plip-sdl/SDL/SdlWindow.cpp
)

5
libplip/Input/PlipInput.cpp

@ -24,7 +24,8 @@ namespace Plip {
}
void PlipInput::UpdateInput(int id, PlipInputData data) {
if(m_coreInput.find(id) == m_coreInput.end()) return;
m_coreInput.find(id)->second.SetData(data);
auto it = m_coreInput.find(id);
if(it == m_coreInput.end()) return;
it->second.SetData(data);
}
}

120
plip-sdl/Config.cpp

@ -0,0 +1,120 @@
/* Config.cpp
*
* Loads and stores configuration data in an INI-like format.
*/
#include <fstream>
#include <iostream>
#include "Config.h"
namespace PlipSdl {
static inline std::string Trim(std::string str) {
// Trim front.
str.erase(str.cbegin(), std::find_if(str.cbegin(), str.cend(), [](unsigned char c) {
return !std::isspace(c);
}));
// Trim back.
str.erase(std::find_if(str.crbegin(), str.crend(), [](unsigned char c) {
return !std::isspace(c);
}).base(), str.cend());
return str;
}
static inline std::string ToLower(std::string str) {
std::transform(str.cbegin(), str.cend(), str.begin(), [](unsigned char c) {
return std::tolower(c);
});
return str;
}
const std::string &Config::GetValue(const std::string &key) {
return GetValue(global, key);
}
const std::string &Config::GetValue(const std::string &section, const std::string &key) {
auto secLower = ToLower(section);
auto keyLower = ToLower(key);
auto itSection = m_section.find(secLower);
if(itSection == m_section.end()) return empty;
auto itKey = itSection->second.find(keyLower);
if(itKey == itSection->second.end()) return empty;
return itKey->second;
}
bool Config::LoadFile(const std::string &filename) {
std::string section = global;
std::ifstream file;
file.open(filename);
if(!file.is_open()) return false;
std::string line;
int lineNum = 0;
while(std::getline(file, line)) {
line = Trim(line);
++lineNum;
if(line.empty()) continue;
// Comment.
if(line.front() == '#') continue;
if(line.front() == '[' && line.back() == ']') {
// Section
line.erase(line.begin());
line.erase(line.end() - 1);
section = Trim(line);
continue;
}
// Everything else. :)
auto equals = line.find('=', 0);
if(equals == std::string::npos) {
std::cerr << filename << ": no value specified on line " << lineNum << std::endl;
continue;
}
auto key = Trim(line.substr(0, equals));
auto value = Trim(line.substr(equals + 1, std::string::npos));
SetValue(section, key, value);
}
file.close();
return true;
}
void Config::SetValue(const std::string &key, const std::string &value) {
SetValue(global, key, value);
}
void Config::SetValue(const std::string &section, const std::string &key, const std::string &value) {
auto secLower = ToLower(section);
auto keyLower = ToLower(key);
auto itSection = m_section.find(secLower);
if(itSection == m_section.end()) {
// O.o
auto newSection = std::pair<std::string, std::unordered_map<std::string, std::string>>
(secLower, std::unordered_map<std::string, std::string>());
newSection.second.insert(std::pair<std::string, std::string>(keyLower, value));
m_section.insert(newSection);
return;
}
auto itKey = itSection->second.find(keyLower);
if(itKey == itSection->second.end()) {
itSection->second.insert(std::pair<std::string, std::string>(keyLower, value));
return;
}
itKey->second = value;
}
}

26
plip-sdl/Config.h

@ -0,0 +1,26 @@
/* Config.h
*
* Loads and stores configuration data in an INI-like format.
*/
#pragma once
#include <string>
#include <unordered_map>
namespace PlipSdl {
class Config {
public:
const std::string &GetValue(const std::string &key);
const std::string &GetValue(const std::string &section, const std::string &key);
bool LoadFile(const std::string &filename);
void SetValue(const std::string &key, const std::string &value);
void SetValue(const std::string &section, const std::string &key, const std::string &value);
const std::string global = "\x01";
const std::string empty = "\xff";
private:
std::unordered_map<std::string, std::unordered_map<std::string, std::string>> m_section;
};
}

16
plip-sdl/SDL/SdlEvent.cpp

@ -3,7 +3,23 @@
* An SDL2 event handler.
*/
#include <SDL.h>
#include "SdlEvent.h"
namespace PlipSdl {
SdlUiEvent SdlEvent::ProcessEvents() {
SDL_Event ev;
auto uiEvent = SdlUiEvent::None;
while(SDL_PollEvent(&ev)) {
switch(ev.type) {
case SDL_QUIT:
uiEvent = SdlUiEvent::Quit;
break;
}
}
return uiEvent;
}
}

12
plip-sdl/SDL/SdlEvent.h

@ -8,6 +8,18 @@
#include "Input/PlipInput.h"
namespace PlipSdl {
enum class SdlUiEvent {
None,
Quit
};
class SdlEvent {
public:
explicit SdlEvent(Plip::PlipInput *input) : m_input(input) {};
SdlUiEvent ProcessEvents();
private:
Plip::PlipInput *m_input;
};
}

68
plip-sdl/main.cpp

@ -4,10 +4,12 @@
*/
#include <iostream>
#include <string>
#include "cxxopts.hpp"
#include "Plip.h"
#include "Config.h"
#include "SDL/SdlEvent.h"
#include "SDL/SdlWindow.h"
@ -17,38 +19,28 @@
#include "Timer/TimerSdl.h"
#endif
// TODO: Allow this to be user-defined.
#define FRAME_TIME 16666666 // 60hz
void gameLoop(Plip::Plip *plip, PlipSdl::Timer *timer) {
void gameLoop(Plip::Plip *plip, PlipSdl::Config *config, PlipSdl::Timer *timer) {
auto input = plip->GetInput();
auto video = plip->GetVideo();
auto event = new PlipSdl::SdlEvent();
auto event = new PlipSdl::SdlEvent(input);
auto targetFps = std::stoi(config->GetValue("video", "targetFps"));
auto frameTime = 1000000000 / targetFps;
// <TEMP>
SDL_Event ev;
// </TEMP>
std::cout << targetFps << "hz (" << frameTime << "ns)" << std::endl;
bool running = true;
auto running = true;
while(running) {
timer->StopwatchStart();
// <TEMP>>
// TODO: Temporary event loop. Remove me.
while(SDL_PollEvent(&ev)) {
switch(ev.type) {
case SDL_QUIT:
running = false;
break;
}
}
// </TEMP>
if(event->ProcessEvents() == PlipSdl::SdlUiEvent::Quit)
running = false;
auto time = timer->StopwatchStop();
auto delay = FRAME_TIME - time;
auto delay = frameTime - time;
while(delay < 0)
delay += FRAME_TIME;
delay += frameTime;
timer->Nanosleep(delay);
}
@ -62,18 +54,22 @@ cxxopts::ParseResult parseCmdLine(int argc, char **argv) {
.show_positional_help();
options.add_options(cxxopts::hidden_group)
("c,core", "the core that should be used", cxxopts::value<std::string>())
("f,filename", "the path to the ROM", cxxopts::value<std::string>())
("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");
("V,version", "displays the version information and exits")
;
options.add_options("Video")
("s,scale", "sets the default window scaling", cxxopts::value<int>()->default_value("1"));
( "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"});
@ -114,7 +110,25 @@ int main(int argc, char **argv) {
SDL_Init(0);
auto wnd = new PlipSdl::SdlWindow(opts["scale"].as<int>(), version);
// TODO: Make the config handler less of a mess. :|
auto config = new PlipSdl::Config();
config->SetValue("video", "scale", "1");
config->SetValue("video", "targetFps", "60");
if(opts["config"].count()) {
auto configFile = opts["config"].as<std::string>();
if(!config->LoadFile(configFile))
std::cerr << "Error opening config file: " << configFile << std::endl;
}
if(opts["scale"].count())
config->SetValue("video", "scale", std::to_string(opts["scale"].as<int>()));
if(opts["fps"].count())
config->SetValue("video", "targetFps", std::to_string(opts["fps"].as<int>()));
auto videoScale = std::stoi(config->GetValue("video", "scale"));
auto wnd = new PlipSdl::SdlWindow(videoScale, version);
auto plip = new Plip::Plip(wnd);
#ifdef UNIX
@ -123,7 +137,7 @@ int main(int argc, char **argv) {
auto timer = new PlipSdl::TimerSdl();
#endif
gameLoop(plip, timer);
gameLoop(plip, config, timer);
SDL_Quit();
return 0;

2
version.cmake

@ -26,7 +26,7 @@ if(GIT_FOUND)
OUTPUT_STRIP_TRAILING_WHITESPACE
)
message(STATUS "${APP_TITLE}: ${GIT_BRANCH}@${GIT_REVISION}; tag: ${GIT_TAG}")
message(STATUS "${PRODUCT_NAME}: ${GIT_BRANCH}@${GIT_REVISION}; tag: ${GIT_TAG}")
else()
message(STATUS "Git package not found. Unable to fetch version information.")
endif()

Loading…
Cancel
Save