Skip to content

Commit

Permalink
second-pass at modularizing GUI - done?
Browse files Browse the repository at this point in the history
I'm pretty happy with how SDL_GUI cleaned up. It's now pretty thin,
with just a bit of nontrivial, ANESE related behavior, namely:
1) Loading a rom passed as an arg
2) The main run loop is not fully generic (queries modules)
Neither of those are that bad though, so i'm calling it for now.

I think that after this, i'll just have 2 more tasks left until I
tag this as ANESE release 1.0.

1) Comment the codebase some more.
It's pretty good as it stands, but I want to give it a once-over.

2) See if I can't just figure out how to use SDL_QueueAudio
Yes, Blargg's Sound_Queue works _fine_, but I'd feel better if I
could say that _all_ of ANESE's sound code is hand-written.
  • Loading branch information
daniel5151 committed Jul 2, 2018
1 parent 82be50b commit 52c8ea0
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 252 deletions.
16 changes: 16 additions & 0 deletions src/ui/SDL2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# SDL GUI

It's not the cleanest code, but it works, and is pretty modular.

- `SDL_GUI` - `gui.h/cc` - Core SDL functionality
- Set up + teardown main Renderer, Window, Controllers, etc...
- Contains main Loop
- Initializes and routes events / output to and from `GUIModules`
- `gui_modules` - Individual components of the UI
- Implement simple interface (accept input, update self, render output)
- `EmuModule` - ANESE core
- Owns, updates, and presents the output of ANESE core
- Handles loading / unloading ROMs, Movies, and savs
- `MenuModule` - Menu system
- Runs the menu system
- Has reference to `EmuModule` (to load ROMs)
67 changes: 60 additions & 7 deletions src/ui/SDL2/config.cc
Original file line number Diff line number Diff line change
@@ -1,27 +1,80 @@
#include "config.h"

#include <iostream>

#include <cfgpath.h>
#include <clara.hpp>

Config::Config() { this->ini.SetUnicode(); }

void Config::load(const char* filename) {
void Config::load(int argc, char* argv[]) {
// --------------------------- Argument Parsing --------------------------- //

bool show_help = false;
auto cli
= clara::Help(show_help)
| clara::Opt(this->cli.log_cpu)
["--log-cpu"]
("Output CPU execution over STDOUT")
| clara::Opt(this->cli.no_sav)
["--no-sav"]
("Don't load/create sav files")
| clara::Opt(this->cli.ppu_timing_hack)
["--alt-nmi-timing"]
("Enable NMI timing fix \n"
"(fixes some games, eg: Bad Dudes, Solomon's Key)")
| clara::Opt(this->cli.record_fm2_path, "path")
["--record-fm2"]
("Record a movie in the fm2 format")
| clara::Opt(this->cli.replay_fm2_path, "path")
["--replay-fm2"]
("Replay a movie in the fm2 format")
| clara::Opt(this->cli.config_file, "path")
["--config"]
("Use custom config file")
| clara::Arg(this->cli.rom, "rom")
("an iNES rom");

auto result = cli.parse(clara::Args(argc, argv));
if(!result) {
std::cerr << "Error: " << result.errorMessage() << "\n";
std::cerr << cli;
exit(1);
}

if (show_help) {
std::cout << cli;
exit(1);
}

// ------------------------- Config File Parsing ------------------------- -//

// Get cross-platform config path (if no custom path specified)
if (this->cli.config_file.empty()) {
cfgpath::get_user_config_file(this->filename, 260, "anese");
} else {
strcpy(this->filename, this->cli.config_file.c_str());
}

// Try to load config, setting up a new one if none exists
if (SI_Error err = this->ini.LoadFile(filename)) {
if (SI_Error err = this->ini.LoadFile(this->filename)) {
(void)err; // TODO: handle me?
fprintf(stderr, "[Config] Could not open config file %s!\n", filename);
fprintf(stderr, "[Config] Could not open config file %s!\n", this->filename);
fprintf(stderr, "[Config] Will generate a new one.\n");
this->save(filename);
this->save();
}

// Load config vals
this->window_scale = this->ini.GetLongValue("ui", "window_scale");
strcpy(this->roms_dir, this->ini.GetValue("paths", "roms_dir"));
}

void Config::save(const char* filename) {
void Config::save() {
this->ini.SetLongValue("ui", "window_scale", this->window_scale);
this->ini.SetValue ("paths", "roms_dir", this->roms_dir);

if (SI_Error err = this->ini.SaveFile(filename)) {
if (SI_Error err = this->ini.SaveFile(this->filename)) {
(void)err; // TODO: handle me?
fprintf(stderr, "[Config] Could not save config file %s!\n", filename);
fprintf(stderr, "[Config] Could not save config file %s!\n", this->filename);
}
}
43 changes: 22 additions & 21 deletions src/ui/SDL2/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,36 @@

#include "common/util.h"

struct CLIArgs {
bool log_cpu = false;
bool no_sav = false;
bool ppu_timing_hack = false;

std::string record_fm2_path;
std::string replay_fm2_path;

std::string config_file;

std::string rom;
};

// Config Manager, both CLI parsing and INI parse/save
struct Config {
private:
CSimpleIniA ini;
char filename [260];
public:
Config();
void load(const char* filename);
void save(const char* filename);
void load(int argc, char* argv[]);
void save();

public:
/*---------- INI Config Args (saved) ----------*/
// UI
uint window_scale = 2;

// Paths
char roms_dir [256] = { '.', '\0' };
// I _would_ write `char roms_dir [256] = "."`, but I can;t.
// You know why?
// because g++4 has a compiler bug, and Travis fails on it.
// Weeeeeeeeeeeeeeeeee
char roms_dir [260] = { '.', '\0' };
// I _would_ write `char roms_dir [260] = "."`, but I can't.
// Why? g++4 has a compiler bug, and Travis fails with that _valid_ syntax.

/*---------- CLI Args (not saved) ----------*/
struct {
bool log_cpu = false;
bool no_sav = false;
bool ppu_timing_hack = false;

std::string record_fm2_path;
std::string replay_fm2_path;

std::string config_file;

std::string rom;
} cli;
};
6 changes: 3 additions & 3 deletions src/ui/SDL2/fs/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
namespace ANESE_fs { namespace util {

// this is pretty jank
static inline void get_abs_path(const char* path, char* out, unsigned int n) {
static inline void get_abs_path(char* abs_path, const char* path, unsigned int n) {
#ifdef WIN32
GetFullPathName(path, n, out, nullptr);
GetFullPathName(path, n, abs_path, nullptr);
#else
(void)n;
(void)realpath(path, out);
(void)realpath(path, abs_path);
#endif
}

Expand Down
129 changes: 10 additions & 119 deletions src/ui/SDL2/gui.cc
Original file line number Diff line number Diff line change
@@ -1,72 +1,13 @@
#include "gui.h"

#include <cstdio>
#include <iostream>

#include <cfgpath.h>
#include <clara.hpp>
#include <SDL2_inprint.h>
#include <SimpleIni.h>

#include "common/util.h"
#include "common/serializable.h"

#include "nes/cartridge/cartridge.h"
#include "nes/joy/controllers/standard.h"
#include "nes/nes.h"

#include "fs/util.h"

int SDL_GUI::init(int argc, char* argv[]) {
// --------------------------- Argument Parsing --------------------------- //

bool show_help = false;
auto cli
= clara::Help(show_help)
| clara::Opt(this->args.log_cpu)
["--log-cpu"]
("Output CPU execution over STDOUT")
| clara::Opt(this->args.no_sav)
["--no-sav"]
("Don't load/create sav files")
| clara::Opt(this->args.ppu_timing_hack)
["--alt-nmi-timing"]
("Enable NMI timing fix \n"
"(fixes some games, eg: Bad Dudes, Solomon's Key)")
| clara::Opt(this->args.record_fm2_path, "path")
["--record-fm2"]
("Record a movie in the fm2 format")
| clara::Opt(this->args.replay_fm2_path, "path")
["--replay-fm2"]
("Replay a movie in the fm2 format")
| clara::Opt(this->args.config_file, "path")
["--config"]
("Use custom config file")
| clara::Arg(this->args.rom, "rom")
("an iNES rom");

auto result = cli.parse(clara::Args(argc, argv));
if(!result) {
std::cerr << "Error: " << result.errorMessage() << "\n";
std::cerr << cli;
exit(1);
}

if (show_help) {
std::cout << cli;
exit(1);
}

// ------------------------- Config File Parsing ------------------------- -//

// Get cross-platform config path (if no custom path specified)
if (this->args.config_file == "") {
char config_f_path [256];
cfgpath::get_user_config_file(config_f_path, 256, "anese");
this->args.config_file = config_f_path;
}

this->config.load(this->args.config_file.c_str());
this->config.load(argc, argv);

// --------------------------- Init SDL2 Common --------------------------- //

Expand Down Expand Up @@ -116,56 +57,15 @@ int SDL_GUI::init(int argc, char* argv[]) {
SDL2_inprint::prepare_inline_font();

/*---------- Init GUI modules ----------*/
this->emu = new EmuModule(this->sdl_common, this->args, this->config);
this->menu = new MenuModule(this->sdl_common, this->args, this->config, *this->emu);

// TODO: put this somewhere else...
strcpy(this->menu->menu.directory, this->config.roms_dir);

// ------------------------------ NES Params ------------------------------ //

if (this->args.log_cpu) { this->emu->params.log_cpu = true; }
if (this->args.ppu_timing_hack) { this->emu->params.ppu_timing_hack = true; }
this->emu->nes.updated_params();

// ---------------------------- Movie Support ----------------------------- //

if (this->args.replay_fm2_path != "") {
bool did_load = this->emu->fm2_replay.init(this->args.replay_fm2_path.c_str());
if (!did_load)
fprintf(stderr, "[Replay][fm2] Movie loading failed!\n");
fprintf(stderr, "[Replay][fm2] Movie successfully loaded!\n");
}

if (this->args.record_fm2_path != "") {
bool did_load = this->emu->fm2_record.init(this->args.record_fm2_path.c_str());
if (!did_load)
fprintf(stderr, "[Record][fm2] Failed to setup Movie recording!\n");
fprintf(stderr, "[Record][fm2] Movie recording is setup!\n");
}

// -------------------------- NES Initialization -------------------------- //

// pass controllers to this->fm2_record
this->emu->fm2_record.set_joy(0, FM2_Controller::SI_GAMEPAD, &this->emu->joy_1);
this->emu->fm2_record.set_joy(1, FM2_Controller::SI_GAMEPAD, &this->emu->joy_2);

// Check if there is fm2 to replay
if (this->emu->fm2_replay.is_enabled()) {
// plug in fm2 controllers
this->emu->nes.attach_joy(0, this->emu->fm2_replay.get_joy(0));
this->emu->nes.attach_joy(1, this->emu->fm2_replay.get_joy(1));
} else {
// plug in physical nes controllers
this->emu->nes.attach_joy(0, &this->emu->joy_1);
this->emu->nes.attach_joy(1, &this->emu->zap_2);
}
this->emu = new EmuModule(this->sdl_common, this->config);
this->menu = new MenuModule(this->sdl_common, this->config, *this->emu);

// Load ROM if one has been passed as param
if (this->args.rom != "") {
if (this->config.cli.rom != "") {
this->menu->in_menu = false;
int error = this->emu->load_rom(this->args.rom.c_str());
if (error) return error;
int error = this->emu->load_rom(this->config.cli.rom.c_str());
if (error)
return error;
}

return 0;
Expand All @@ -174,21 +74,12 @@ int SDL_GUI::init(int argc, char* argv[]) {
SDL_GUI::~SDL_GUI() {
fprintf(stderr, "[SDL2] Stopping SDL2 GUI\n");

// Cleanup ROM (unloading also creates savs)
this->emu->unload_rom(this->emu->cart);
delete this->emu->cart;

// Update config
// TODO: put this somewhere else...
char new_roms_dir [256];
ANESE_fs::util::get_abs_path(this->menu->menu.directory, new_roms_dir, 256);
strcpy(this->config.roms_dir, new_roms_dir);

this->config.save(this->args.config_file.c_str());

// order matters (menu has ref to emu)
delete this->menu;
delete this->emu;

this->config.save();

// SDL Cleanup
// SDL_CloseAudioDevice(this->sdl_common.nes_audiodev);
SDL_GameControllerClose(this->sdl_common.controller);
Expand Down
27 changes: 1 addition & 26 deletions src/ui/SDL2/gui.h
Original file line number Diff line number Diff line change
@@ -1,39 +1,14 @@
#pragma once

#include <string>
#include <vector>

#include "nes/cartridge/cartridge.h"
#include "nes/joy/controllers/standard.h"
#include "nes/joy/controllers/zapper.h"
#include "nes/nes.h"

#include "movies/fm2/record.h"
#include "movies/fm2/replay.h"

#include <SDL.h>
#include "util/Sound_Queue.h"

#include <cute_files.h>

#include "config.h"
#include "nes/params.h"

#include "gui_modules/module.h"
#include "gui_modules/emu.h"
#include "gui_modules/menu.h"

/**
* Implementations are strewn-across multiple files, as having everything in a
* single file became unwieldy.
*/

class SDL_GUI final {
private:
// Command-line args
CLIArgs args;

// Global config
// Config manager (also has CLI args)
Config config;

// Initialized once, passed by const-ref to all gui modules
Expand Down
Loading

0 comments on commit 52c8ea0

Please sign in to comment.