Switch from GTK to FLTK

This commit is contained in:
rdanbrook 2021-03-21 16:15:21 -04:00
parent 273bd14243
commit 80ec3ea21f
48 changed files with 2363 additions and 4916 deletions

11
.gitignore vendored
View file

@ -25,8 +25,6 @@ configure
depcomp
install-sh
missing
source/common/.deps/
source/common/.dirstamp
source/core/.deps/
source/core/.dirstamp
source/core/api/.deps/
@ -37,10 +35,5 @@ source/core/input/.deps/
source/core/input/.dirstamp
source/core/vssystem/.deps/
source/core/vssystem/.dirstamp
source/gtkui/.deps/
source/sdl/.deps/
source/sdl/.dirstamp
source/unix/.deps/
source/unix/.dirstamp
source/unix/gtkui/.deps/
source/unix/gtkui/.dirstamp
source/fltkui/.deps/
source/fltkui/.dirstamp

View file

@ -7,23 +7,19 @@ EXTRA_DIST = doc
nestopia_CPPFLAGS = \
-I$(top_srcdir)/source \
-I$(top_srcdir)/source/common \
-I$(top_srcdir)/source/gtkui \
-I$(top_srcdir)/source/sdl \
-I$(top_srcdir)/source/fltkui \
-DDATADIR=\"$(datadir)/nestopia\" \
-DDATAROOTDIR=\"$(datarootdir)\" \
-DNST_PRAGMA_ONCE \
$(ZLIB_CFLAGS) \
$(LIBARCHIVE_CFLAGS) \
$(SDL2_CFLAGS) \
$(LIBEPOXY_CFLAGS) \
$(GTK3_CFLAGS)
$(FLTK_CFLAGS)
nestopia_LDADD = \
$(ZLIB_LIBS) \
$(LIBARCHIVE_LIBS) \
$(SDL2_LIBS) \
$(LIBEPOXY_LIBS) \
$(GTK3_LIBS)
$(FLTK_LIBS)
################
# Installation #
@ -695,47 +691,37 @@ nestopia_SOURCES = \
source/nes_ntsc/demo_impl.h
nestopia_SOURCES += \
source/common/nstcommon.cpp \
source/common/nstcommon.h \
source/common/cheats.cpp \
source/common/cheats.h \
source/common/homebrew.cpp \
source/common/homebrew.h \
source/common/cli.cpp \
source/common/cli.h \
source/common/config.cpp \
source/common/config.h \
source/common/video.cpp \
source/common/video.h \
source/common/input.cpp \
source/common/input.h \
source/common/samples.cpp \
source/common/samples.h \
source/common/font.h \
source/common/ini.cpp \
source/common/ini.h \
source/common/png.cpp \
source/common/png.h \
source/common/audio.cpp \
source/common/audio.h \
source/common/sdlinput.cpp \
source/common/sdlinput.h
source/fltkui/nstcommon.cpp \
source/fltkui/nstcommon.h \
source/fltkui/cheats.cpp \
source/fltkui/cheats.h \
source/fltkui/homebrew.cpp \
source/fltkui/homebrew.h \
source/fltkui/cli.cpp \
source/fltkui/cli.h \
source/fltkui/config.cpp \
source/fltkui/config.h \
source/fltkui/video.cpp \
source/fltkui/video.h \
source/fltkui/input.cpp \
source/fltkui/input.h \
source/fltkui/samples.cpp \
source/fltkui/samples.h \
source/fltkui/font.h \
source/fltkui/ini.cpp \
source/fltkui/ini.h \
source/fltkui/png.cpp \
source/fltkui/png.h \
source/fltkui/audio.cpp \
source/fltkui/audio.h
nestopia_SOURCES += \
source/gtkui/gtkui_config.h \
source/gtkui/gtkui_archive.h \
source/gtkui/gtkui_cheats.cpp \
source/gtkui/gtkui_callbacks.h \
source/gtkui/gtkui_callbacks.cpp \
source/gtkui/gtkui_dialogs.cpp \
source/gtkui/gtkui_cheats.h \
source/gtkui/gtkui_dialogs.h \
source/gtkui/gtkui_archive.cpp \
source/gtkui/gtkui.cpp \
source/gtkui/gtkui.h \
source/gtkui/gtkui_input.cpp \
source/gtkui/gtkui_input.h \
source/gtkui/gtkui_config.cpp
source/fltkui/fltkui_config.h \
source/fltkui/fltkui_archive.h \
source/fltkui/fltkui_archive.cpp \
source/fltkui/fltkui.cpp \
source/fltkui/fltkui.h \
source/fltkui/fltkui_config.cpp
# install full HTML suite
if ENABLE_FULL_HTML

View file

@ -2,7 +2,7 @@ dnl Initialise Autoconf
AC_PREREQ([2.69])
AC_INIT(
[nestopia],
[1.50])
[1.51.0])
AC_CONFIG_SRCDIR([source])
AC_LANG([C++])
@ -76,11 +76,11 @@ PKG_CHECK_MODULES([LIBARCHIVE], [libarchive])
dnl SDL2
PKG_CHECK_MODULES([SDL2], [sdl2])
dnl LibEpoxy
PKG_CHECK_MODULES([LIBEPOXY], [epoxy])
AC_CHECK_PROG(FLTKCONFIG,fltk-config,[fltk-config],[no])
test "$FLTKCONFIG" == "no" && AC_MSG_ERROR([Cannot find the fltk-config executable. Is FLTK installed?])
dnl GTK3
PKG_CHECK_MODULES([GTK3], [gtk+-3.0])
AC_SUBST(FLTK_CFLAGS,"$(fltk-config --use-gl --use-images --cxxflags)")
AC_SUBST(FLTK_LIBS,"-lGL $(fltk-config --use-gl --use-images --ldflags)")
dnl full HTML suite
AC_ARG_ENABLE([doc],

View file

@ -1,257 +0,0 @@
/*
* Nestopia UE
*
* Copyright (C) 2012-2016 R. Danbrook
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include <stdio.h>
#include "nstcommon.h"
#include "config.h"
#include "input.h"
#include "video.h"
static turbo_t turbostate;
static turbo_t turbotoggle;
extern Emulator emulator;
void nst_input_init() {
// Initialize input
char controller[32];
for (int i = 0; i < NUMGAMEPADS; i++) {
Input(emulator).AutoSelectController(i);
switch(Input(emulator).GetConnectedController(i)) {
case Input::UNCONNECTED:
snprintf(controller, sizeof(controller), "%s", "Unconnected");
break;
case Input::PAD1:
case Input::PAD2:
case Input::PAD3:
case Input::PAD4:
snprintf(controller, sizeof(controller), "%s", "Standard Pad");
break;
case Input::ZAPPER:
snprintf(controller, sizeof(controller), "%s", "Zapper");
break;
case Input::PADDLE:
snprintf(controller, sizeof(controller), "%s", "Arkanoid Paddle");
break;
case Input::POWERPAD:
snprintf(controller, sizeof(controller), "%s", "Power Pad");
break;
case Input::POWERGLOVE:
snprintf(controller, sizeof(controller), "%s", "Power Glove");
break;
case Input::MOUSE:
snprintf(controller, sizeof(controller), "%s", "Mouse");
break;
case Input::ROB:
snprintf(controller, sizeof(controller), "%s", "R.O.B.");
break;
case Input::FAMILYTRAINER:
snprintf(controller, sizeof(controller), "%s", "Family Trainer");
break;
case Input::FAMILYKEYBOARD:
snprintf(controller, sizeof(controller), "%s", "Family Keyboard");
break;
case Input::SUBORKEYBOARD:
snprintf(controller, sizeof(controller), "%s", "Subor Keyboard");
break;
case Input::DOREMIKKOKEYBOARD:
snprintf(controller, sizeof(controller), "%s", "Doremikko Keyboard");
break;
case Input::HORITRACK:
snprintf(controller, sizeof(controller), "%s", "Hori Track");
break;
case Input::PACHINKO:
snprintf(controller, sizeof(controller), "%s", "Pachinko");
break;
case Input::OEKAKIDSTABLET:
snprintf(controller, sizeof(controller), "%s", "Oeka Kids Tablet");
break;
case Input::KONAMIHYPERSHOT:
snprintf(controller, sizeof(controller), "%s", "Konami Hypershot");
break;
case Input::BANDAIHYPERSHOT:
snprintf(controller, sizeof(controller), "%s", "Bandai Hypershot");
break;
case Input::CRAZYCLIMBER:
snprintf(controller, sizeof(controller), "%s", "Crazy Climber");
break;
case Input::MAHJONG:
snprintf(controller, sizeof(controller), "%s", "Mahjong");
break;
case Input::EXCITINGBOXING:
snprintf(controller, sizeof(controller), "%s", "Exciting Boxing");
break;
case Input::TOPRIDER:
snprintf(controller, sizeof(controller), "%s", "Top Rider");
break;
case Input::POKKUNMOGURAA:
snprintf(controller, sizeof(controller), "%s", "Pokkun Moguraa");
break;
case Input::PARTYTAP:
snprintf(controller, sizeof(controller), "%s", "PartyTap");
break;
case Input::TURBOFILE:
snprintf(controller, sizeof(controller), "%s", "Turbo File");
break;
case Input::BARCODEWORLD:
snprintf(controller, sizeof(controller), "%s", "Barcode World");
break;
default:
snprintf(controller, sizeof(controller), "%s", "Unknown");
break;
}
fprintf(stderr, "Port %d: %s\n", i + 1, controller);
}
}
void nst_input_inject(Input::Controllers *controllers, nesinput_t input) {
// Insert the input signal into the NES
if(controllers == NULL) { return; }
if (input.pressed) {
controllers->pad[input.player].buttons |= input.nescode;
if (input.turboa) { input.player == 0 ? turbostate.p1a = true : turbostate.p2a = true; }
if (input.turbob) { input.player == 0 ? turbostate.p1b = true : turbostate.p2b = true; }
}
else {
controllers->pad[input.player].buttons &= ~input.nescode;
if (input.turboa) { input.player == 0 ? turbostate.p1a = false : turbostate.p2a = false; }
if (input.turbob) { input.player == 0 ? turbostate.p1b = false : turbostate.p2b = false; }
}
}
void nst_input_inject_mouse(Input::Controllers *controllers, int b, int s, int x, int y) {
// Insert input signal for Zappers
if(controllers == NULL) { return; }
double xaspect;
double yaspect;
if (s) {
// Get X coords
if (conf.video_filter == 1) { // NTSC
xaspect = (double)(Video::Output::WIDTH) / (double)(Video::Output::NTSC_WIDTH / 2);
}
else if (conf.video_tv_aspect) {
xaspect = (double)(Video::Output::WIDTH) / (double)(TV_WIDTH);
}
else { xaspect = 1.0; }
dimensions_t rendersize = nst_video_get_dimensions_render();
dimensions_t screensize = nst_video_get_dimensions_screen();
// Calculate fullscreen X coords
if (conf.video_fullscreen) {
if (conf.video_stretch_aspect) {
xaspect = (double)(conf.video_scale_factor * Video::Output::WIDTH) / (double)(screensize.w);
}
else {
// Remove the same amount of pixels as the black area to the left of the screen
x -= screensize.w / 2.0f - rendersize.w / 2.0f;
xaspect = (double)(conf.video_scale_factor * Video::Output::WIDTH) / (double)(rendersize.w);
}
}
controllers->zapper.x = (int)(x * xaspect) / conf.video_scale_factor;
// Get Y coords
if (conf.video_unmask_overscan) {
controllers->zapper.y = y / conf.video_scale_factor;
}
else {
controllers->zapper.y = (y + OVERSCAN_TOP * conf.video_scale_factor) / conf.video_scale_factor;
}
// Calculate fullscreen Y coords
if (conf.video_fullscreen) {
yaspect = (double)(conf.video_scale_factor * Video::Output::HEIGHT) / (double)(screensize.h);
controllers->zapper.y = (y * yaspect) / conf.video_scale_factor;
}
// Offscreen
if (b != 1) { controllers->zapper.x = ~1U; }
controllers->zapper.fire = true;
}
else { controllers->zapper.fire = false; }
}
void nst_input_turbo_init() {
// Initialize the turbo button states
turbostate.p1a = turbotoggle.p1a = 0;
turbostate.p1b = turbotoggle.p1b = 0;
turbostate.p2a = turbotoggle.p2a = 0;
turbostate.p2b = turbotoggle.p2b = 0;
}
void nst_input_turbo_pulse(Input::Controllers *controllers) {
// Pulse the turbo buttons if they're pressed
if (turbostate.p1a) {
turbotoggle.p1a++;
if (turbotoggle.p1a >= conf.timing_turbopulse) {
turbotoggle.p1a = 0;
controllers->pad[0].buttons &= ~Input::Controllers::Pad::A;
}
else { controllers->pad[0].buttons |= Input::Controllers::Pad::A; }
}
if (turbostate.p1b) {
turbotoggle.p1b++;
if (turbotoggle.p1b >= conf.timing_turbopulse) {
turbotoggle.p1b = 0;
controllers->pad[0].buttons &= ~Input::Controllers::Pad::B;
}
else { controllers->pad[0].buttons |= Input::Controllers::Pad::B; }
}
if (turbostate.p2a) {
turbotoggle.p2a++;
if (turbotoggle.p2a >= conf.timing_turbopulse) {
turbotoggle.p2a = 0;
controllers->pad[1].buttons &= ~Input::Controllers::Pad::A;
}
else { controllers->pad[1].buttons |= Input::Controllers::Pad::A; }
}
if (turbostate.p2b) {
turbotoggle.p2b++;
if (turbotoggle.p2b >= conf.timing_turbopulse) {
turbotoggle.p2b = 0;
controllers->pad[1].buttons &= ~Input::Controllers::Pad::B;
}
else { controllers->pad[1].buttons |= Input::Controllers::Pad::B; }
}
}
int nst_input_zapper_present() {
// Check if a Zapper is presently connected
if (Input(emulator).GetConnectedController(0) == Input::ZAPPER ||
Input(emulator).GetConnectedController(1) == Input::ZAPPER) {
return 1;
}
else { return 0; }
}

View file

@ -1,37 +0,0 @@
#ifndef _INPUT_H_
#define _INPUT_H_
#define NUMGAMEPADS 2
#define NUMBUTTONS 10
#include "core/api/NstApiInput.hpp"
using namespace Nes::Api;
typedef struct {
unsigned char player;
unsigned char nescode;
unsigned char pressed;
unsigned char turboa;
unsigned char turbob;
} nesinput_t;
typedef struct {
int p1a;
int p1b;
int p2a;
int p2b;
} turbo_t;
void nst_input_init();
void nst_input_inject(Input::Controllers *controllers, nesinput_t input);
void nst_input_inject_mouse(Input::Controllers *controllers, int b, int s, int x, int y);
void nst_input_turbo_init();
void nst_input_turbo_pulse(Input::Controllers *controllers);
int nst_input_zapper_present();
int input_configure_item(int pnum, int bnum, int type);
#endif

View file

@ -1,23 +1,23 @@
/*
* Nestopia UE
*
*
* Copyright (C) 2012-2017 R. Danbrook
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*
*/
#include <cstdio>
@ -52,7 +52,7 @@ void audio_set_speed(int speed) {
void audio_queue() {
while ((bufsamples + bufsize) >= EBUFSIZE) { SDL_Delay(1); }
SDL_LockAudioDevice(dev);
for (int i = 0; i < bufsize; i++) {
extbuf[bufend] = intbuf[i];
@ -90,9 +90,9 @@ void audio_init_sdl() {
spec.samples = 512;
spec.userdata = 0;
spec.callback = audio_cb;
bufsize = channels * (conf.audio_sample_rate / framerate);
dev = SDL_OpenAudioDevice(NULL, 0, &spec, &obtained, SDL_AUDIO_ALLOW_ANY_CHANGE);
if (!dev) {
fprintf(stderr, "Error opening audio device.\n");
@ -100,7 +100,7 @@ void audio_init_sdl() {
else {
fprintf(stderr, "Audio: SDL - %dHz %d-bit, %d channel(s)\n", spec.freq, 16, spec.channels);
}
SDL_PauseAudioDevice(dev, 1); // Setting to 0 unpauses
}
@ -129,15 +129,15 @@ void audio_unpause() {
void audio_set_params(Sound::Output *soundoutput) {
// Set audio parameters
Sound sound(emulator);
sound.SetSampleBits(16);
sound.SetSampleRate(conf.audio_sample_rate);
sound.SetSpeaker(conf.audio_stereo ? Sound::SPEAKER_STEREO : Sound::SPEAKER_MONO);
sound.SetSpeed(Sound::DEFAULT_SPEED);
audio_adj_volume();
soundoutput->samples[0] = intbuf;
soundoutput->length[0] = conf.audio_sample_rate / framerate;
soundoutput->samples[1] = NULL;
@ -159,6 +159,6 @@ void audio_adj_volume() {
sound.SetVolume(Sound::CHANNEL_VRC7, conf.audio_vol_vrc7);
sound.SetVolume(Sound::CHANNEL_N163, conf.audio_vol_n163);
sound.SetVolume(Sound::CHANNEL_S5B, conf.audio_vol_s5b);
if (conf.audio_volume == 0) { memset(intbuf, 0, sizeof(intbuf)); }
}

View file

@ -1,23 +1,23 @@
/*
* Nestopia UE
*
*
* Copyright (C) 2012-2018 R. Danbrook
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*
*/
#include <cstdlib>
@ -32,35 +32,35 @@ void nst_cheats_init(const char *cheatpath) {
// Initialize cheat engine
Cheats cheats(emulator);
Xml xml;
cheats.ClearCodes();
std::ifstream cheatfile(cheatpath, std::ifstream::in|std::ifstream::binary);
if (cheatfile.is_open()) {
xml.Read(cheatfile);
if (xml.GetRoot().IsType(L"cheats")) {
Xml::Node root(xml.GetRoot());
Xml::Node node(root.GetFirstChild());
for (int i = 0; i < root.NumChildren(L"cheat"); i++) {
if (node.GetAttribute(L"enabled").IsValue(L"1")) {
if (node.GetChild(L"genie")) { // Game Genie
nst_cheats_code_gg_add(node.GetChild(L"genie").GetValue());
}
else if (node.GetChild(L"rocky")) { // Pro Action Rocky
nst_cheats_code_par_add(node.GetChild(L"rocky").GetValue());
}
else if (node.GetChild(L"address")) { // Raw
nst_cheats_code_raw_add(node);
}
//fprintf(stderr, "Cheat: %ls\n", node.GetChild(L"description").GetValue());
}
node = node.GetNextSibling();
@ -74,13 +74,13 @@ void nst_cheats_init(const char *cheatpath) {
// List the active cheats
Cheats cheats(emulator);
Cheats::Code code;
char gg[9];
for (int i = 0; i < cheats.NumCodes(); i++) {
cheats.GetCode(i, code);
cheats.GameGenieEncode(code, gg);
fprintf(stderr, "Cheat: %s\n", gg);
}
}*/
@ -89,10 +89,10 @@ void nst_cheats_code_gg_add(const wchar_t *data) {
// Add a Game Genie code
Cheats cheats(emulator);
Cheats::Code code;
char gg[9];
wcstombs(gg, data, sizeof(gg));
cheats.GameGenieDecode(gg, code);
cheats.SetCode(code);
}
@ -101,10 +101,10 @@ void nst_cheats_code_par_add(const wchar_t *data) {
// Add a Pro Action Rocky code
Cheats cheats(emulator);
Cheats::Code code;
char par[9];
wcstombs(par, data, sizeof(par));
cheats.ProActionRockyDecode(par, code);
cheats.SetCode(code);
}
@ -113,9 +113,9 @@ void nst_cheats_code_raw_add(Xml::Node node) {
// Add a Raw code
Cheats cheats(emulator);
Cheats::Code code;
code.useCompare = false;
code.address = node.GetChild(L"address").GetUnsignedValue();
if (node.GetChild(L"value")) {
code.value = node.GetChild(L"value").GetUnsignedValue();
@ -132,18 +132,18 @@ void nst_dip_handle(const char *dippath) {
// Handle the DIP switch file
DipSwitches dipswitches(emulator);
Xml xml;
std::ifstream dipfile(dippath, std::ifstream::in|std::ifstream::binary);
if (dipfile.is_open()) {
xml.Read(dipfile);
if (xml.GetRoot().IsType(L"dipswitches")) {
Xml::Node root(xml.GetRoot());
Xml::Node node(root.GetFirstChild());
for (int i = 0; i < root.NumChildren(L"dip"); i++) {
if (node.GetChild(L"value")) {
dipswitches.SetValue(i, node.GetChild(L"value").GetUnsignedValue());
}
@ -154,34 +154,34 @@ void nst_dip_handle(const char *dippath) {
}
else {
Xml::Node root(xml.GetRoot());
root = xml.Create(L"dipswitches");
root.AddAttribute(L"version", L"1.0");
wchar_t wbuf[32];
char buf[2];
int numdips = dipswitches.NumDips();
if (numdips > 0) {
for (int i = 0; i < numdips; i++) {
Xml::Node node(root.AddChild(L"dip"));
mbstowcs(wbuf, dipswitches.GetDipName(i), sizeof(wbuf));
node.AddChild(L"description", wbuf);
snprintf(buf, sizeof(buf), "%d", dipswitches.GetValue(i));
mbstowcs(wbuf, buf, sizeof(buf));
node.AddChild(L"value", wbuf);
}
}
std::ofstream dipout(dippath, std::ifstream::out|std::ifstream::binary);
if (dipout.is_open()) {
xml.Write(root, dipout);
}
dipout.close();
}
}

View file

@ -1,23 +1,23 @@
/*
* Nestopia UE
*
*
* Copyright (C) 2012-2016 R. Danbrook
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*
*/
#include <stdio.h>
@ -82,28 +82,28 @@ void cli_handle_command(int argc, char *argv[]) {
{"version", no_argument, 0, 'v'},
{0, 0, 0, 0}
};
int option_index = 0;
c = getopt_long(argc, argv, "defhl:mnopqrs:tuvw",
long_options, &option_index);
if (c == -1) { break; }
switch(c) {
case 'f':
conf.video_fullscreen = true;
break;
case 'w':
conf.video_fullscreen = false;
break;
case 'h':
cli_show_usage();
exit(0);
break;
case 'l':
optint = atoi(optarg);
if (optint < 6) {
@ -113,23 +113,23 @@ void cli_handle_command(int argc, char *argv[]) {
cli_error("Error: Invalid filter");
}
break;
case 'm':
conf.video_unmask_overscan = false;
break;
case 'n':
conf.video_unmask_overscan = true;
break;
case 'o':
conf.video_stretch_aspect = true;
break;
case 'p':
conf.video_stretch_aspect = false;
break;
case 's':
optint = atoi(optarg);
if (optint < 5 && optint != 0) {
@ -139,28 +139,28 @@ void cli_handle_command(int argc, char *argv[]) {
cli_error("Error: Invalid scale factor");
}
break;
case 't':
conf.video_tv_aspect = true;
break;
case 'r':
conf.video_tv_aspect = false;
break;
case 'u':
conf.video_unlimited_sprites = true;
break;
case 'q':
conf.video_unlimited_sprites = false;
break;
case 'v':
cli_show_version();
exit(0);
break;
default:
cli_error("Error: Invalid option");
break;

View file

@ -1,6 +1,6 @@
/*
* Nestopia UE
*
*
* Copyright (C) 2012-2016 R. Danbrook
* Copyright (C) 2018-2018 Phil Smith
*
@ -8,17 +8,17 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*
*/
#include <stdio.h>
@ -45,10 +45,10 @@ void config_file_read(const char *nstdir) {
void config_file_write(const char *nstdir) {
// Write the config file
char confpath[256];
snprintf(confpath, sizeof(confpath), "%snestopia.conf", nstdir);
FILE *fp = fopen(confpath, "w");
if (fp != NULL) {
// Video
@ -87,11 +87,9 @@ void config_file_write(const char *nstdir) {
fprintf(fp, "unlimited_sprites=%d\n", conf.video_unlimited_sprites);
fprintf(fp, "xbr_pixel_blending=%d\n", conf.video_xbr_pixel_blending);
fprintf(fp, "\n"); // End of Section
// Audio
fprintf(fp, "[audio]\n");
fprintf(fp, "; 0=SDL, 1=libao, 2=jack\n");
fprintf(fp, "api=%d\n\n", conf.audio_api);
fprintf(fp, "; Valid values are 1 and 0.\n");
fprintf(fp, "stereo=%d\n\n", conf.audio_stereo);
fprintf(fp, "; Valid values are 11025, 22050, 44100, 48000, and 96000.\n");
@ -110,7 +108,7 @@ void config_file_write(const char *nstdir) {
fprintf(fp, "vol_n163=%d\n", conf.audio_vol_n163);
fprintf(fp, "vol_s5b=%d\n", conf.audio_vol_s5b);
fprintf(fp, "\n"); // End of Section
// Timing
fprintf(fp, "[timing]\n");
fprintf(fp, "; Base speed for NTSC in Frames per Second.\n");
@ -120,7 +118,7 @@ void config_file_write(const char *nstdir) {
fprintf(fp, "; Pulse turbo buttons every n frames. Minimum value is 2.\n");
fprintf(fp, "turbopulse=%d\n", conf.timing_turbopulse);
fprintf(fp, "\n"); // End of Section
// Misc
fprintf(fp, "[misc]\n");
fprintf(fp, "; 0=Auto, 1=NTSC, 2=PAL, 3=Famicom, 4=Dendy\n");
@ -147,7 +145,7 @@ void config_file_write(const char *nstdir) {
}
void config_set_default() {
// Video
conf.video_filter = 0;
conf.video_scale_factor = 2;
@ -171,9 +169,8 @@ void config_set_default() {
conf.video_stretch_aspect = false;
conf.video_unlimited_sprites = false;
conf.video_xbr_pixel_blending = false;
// Audio
conf.audio_api = 0;
conf.audio_stereo = false;
conf.audio_sample_rate = 48000;
conf.audio_volume = 85;
@ -188,12 +185,12 @@ void config_set_default() {
conf.audio_vol_vrc7 = 85;
conf.audio_vol_n163 = 85;
conf.audio_vol_s5b = 85;
// Timing
conf.timing_speed = 60;
conf.timing_ffspeed = 3;
conf.timing_turbopulse = 3;
// Misc
conf.misc_default_system = 0;
conf.misc_soft_patching = true;
@ -211,7 +208,7 @@ void config_set_default() {
static int config_match(void* user, const char* section, const char* name, const char* value) {
// Match values from config file and populate live config
settings_t* pconfig = (settings_t*)user;
// Video
if (MATCH("video", "filter")) { pconfig->video_filter = atoi(value); }
else if (MATCH("video", "scale_factor")) { pconfig->video_scale_factor = atoi(value); }
@ -235,9 +232,8 @@ static int config_match(void* user, const char* section, const char* name, const
else if (MATCH("video", "stretch_aspect")) { pconfig->video_stretch_aspect = atoi(value); }
else if (MATCH("video", "unlimited_sprites")) { pconfig->video_unlimited_sprites = atoi(value); }
else if (MATCH("video", "xbr_pixel_blending")) { pconfig->video_xbr_pixel_blending = atoi(value); }
// Audio
else if (MATCH("audio", "api")) { pconfig->audio_api = atoi(value); }
else if (MATCH("audio", "stereo")) { pconfig->audio_stereo = atoi(value); }
else if (MATCH("audio", "sample_rate")) { pconfig->audio_sample_rate = atoi(value); }
else if (MATCH("audio", "volume")) { pconfig->audio_volume = atoi(value); }
@ -252,12 +248,12 @@ static int config_match(void* user, const char* section, const char* name, const
else if (MATCH("audio", "vol_vrc7")) { pconfig->audio_vol_vrc7 = atoi(value); }
else if (MATCH("audio", "vol_n163")) { pconfig->audio_vol_n163 = atoi(value); }
else if (MATCH("audio", "vol_s5b")) { pconfig->audio_vol_s5b = atoi(value); }
// Timing
else if (MATCH("timing", "speed")) { pconfig->timing_speed = atoi(value); }
else if (MATCH("timing", "ffspeed")) { pconfig->timing_ffspeed = atoi(value); }
else if (MATCH("timing", "turbopulse")) { pconfig->timing_turbopulse = atoi(value); }
// Misc
else if (MATCH("misc", "default_system")) { pconfig->misc_default_system = atoi(value); }
else if (MATCH("misc", "soft_patching")) { pconfig->misc_soft_patching = atoi(value); }

View file

@ -2,7 +2,7 @@
#define _CONFIG_H_
typedef struct {
// Video
int video_filter;
int video_scale_factor;
@ -26,9 +26,8 @@ typedef struct {
bool video_stretch_aspect;
bool video_unlimited_sprites;
bool video_xbr_pixel_blending;
// Audio
int audio_api;
bool audio_stereo;
int audio_sample_rate;
int audio_volume;
@ -43,12 +42,12 @@ typedef struct {
int audio_vol_vrc7;
int audio_vol_n163;
int audio_vol_s5b;
// Timing
int timing_speed;
int timing_ffspeed;
int timing_turbopulse;
// Misc
int misc_default_system;
bool misc_soft_patching;

536
source/fltkui/fltkui.cpp Normal file
View file

@ -0,0 +1,536 @@
/*
* Nestopia UE
*
* Copyright (C) 2012-2021 R. Danbrook
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include <cstdio>
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Menu_Bar.H>
#include <FL/Fl_Native_File_Chooser.H>
#include <FL/Fl_PNG_Image.H>
#include <FL/Fl_Gl_Window.H>
#include <FL/gl.h>
#include <SDL.h>
#include "nstcommon.h"
#include "cli.h"
#include "config.h"
#include "audio.h"
#include "video.h"
#include "input.h"
#include "fltkui.h"
#include "fltkui_archive.h"
#include "fltkui_config.h"
#define MBARHEIGHT 24
static NstWindow *nstwin;
static Fl_Menu_Bar *menubar;
static NstGlArea *glarea;
static NstConfWindow *confwin;
Fl_Color NstGreen = 0x255f6500;
Fl_Color NstPurple = 0x5f578700;
Fl_Color NstRed = 0xb51e2c00;
Fl_Color NstBlueGrey = 0x383c4a00;
Fl_Color NstLightGrey = 0xd3dae300;
extern Input::Controllers *cNstPads;
extern nstpaths_t nstpaths;
extern bool (*nst_archive_select)(const char*, char*, size_t);
static void fltkui_config(Fl_Widget* w, void* userdata) {
confwin->show();
}
static void fltkui_rom_open(Fl_Widget* w, void* userdata) {
// Create native chooser
Fl_Native_File_Chooser fc;
fc.title("Select a ROM");
fc.type(Fl_Native_File_Chooser::BROWSE_FILE);
fc.filter("NES Games\t*.{nes,unf,fds,zip,7z,gz,bz2,xz}");
// Show file chooser
switch (fc.show()) {
case -1: fprintf(stderr, "Error: %s\n", fc.errmsg()); break;
case 1: break; // Cancel
default:
if (fc.filename()) {
int loaded = nst_load(fc.filename());
nstwin->label(nstpaths.gamename);
if (loaded) { nst_play(); }
}
break;
}
}
static void fltkui_movie_load(Fl_Widget* w, void* userdata) {
// Create native chooser
if (!nst_playing()) { return; }
Fl_Native_File_Chooser fc;
fc.title("Select a Movie");
fc.type(Fl_Native_File_Chooser::BROWSE_FILE);
fc.directory((const char*)nstpaths.nstdir);
fc.filter("Nestopia Movies\t*.nsv");
// Show file chooser
switch (fc.show()) {
case -1: fprintf(stderr, "Error: %s\n", fc.errmsg()); break;
case 1: break; // Cancel
default:
if (fc.filename()) {
nst_movie_load(fc.filename());
}
break;
}
}
static void fltkui_movie_save(Fl_Widget* w, void* userdata) {
// Create native chooser
if (!nst_playing()) { return; }
Fl_Native_File_Chooser fc;
fc.title("Save Movie");
fc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
fc.directory((const char*)nstpaths.nstdir);
fc.filter("Nestopia Moviess\t*.nsv");
fc.options(Fl_Native_File_Chooser::SAVEAS_CONFIRM | Fl_Native_File_Chooser::USE_FILTER_EXT);
// Show file chooser
if (fc.show()) { return; }
nst_movie_save(fc.filename());
}
static void fltkui_movie_stop(Fl_Widget* w, void* userdata) {
nst_movie_stop();
}
static void fltkui_state_load(Fl_Widget* w, void* userdata) {
// Create native chooser
if (!nst_playing()) { return; }
Fl_Native_File_Chooser fc;
fc.title("Load State");
fc.type(Fl_Native_File_Chooser::BROWSE_FILE);
fc.directory((const char*)nstpaths.statepath);
fc.filter("Nestopia States\t*.nst");
// Show file chooser
switch (fc.show()) {
case -1: fprintf(stderr, "Error: %s\n", fc.errmsg()); break;
case 1: break; // Cancel
default:
if (fc.filename()) {
nst_state_load(fc.filename());
}
break;
}
}
static void fltkui_state_save(Fl_Widget* w, void* userdata) {
// Create native chooser
if (!nst_playing()) { return; }
Fl_Native_File_Chooser fc;
fc.title("Save State");
fc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
fc.directory((const char*)nstpaths.statepath);
fc.filter("Nestopia States\t*.nst");
fc.options(Fl_Native_File_Chooser::SAVEAS_CONFIRM | Fl_Native_File_Chooser::USE_FILTER_EXT);
// Show file chooser
if (fc.show()) { return; }
nst_state_save(fc.filename());
}
static void fltkui_screenshot(Fl_Widget* w, void* userdata) {
// Create native chooser
if (!nst_playing()) { return; }
Fl_Native_File_Chooser fc;
fc.title("Save Screenshot");
fc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
fc.directory((const char*)nstpaths.nstdir);
fc.filter("PNG Screenshots\t*.png");
fc.options(Fl_Native_File_Chooser::SAVEAS_CONFIRM | Fl_Native_File_Chooser::USE_FILTER_EXT);
// Show file chooser
if (fc.show()) { return; }
video_screenshot(fc.filename());
}
static void fltkui_palette_open(Fl_Widget* w, void* userdata) {
// Create native chooser
Fl_Native_File_Chooser fc;
fc.title("Select a Palette");
fc.type(Fl_Native_File_Chooser::BROWSE_FILE);
fc.filter("NES Palettes\t*.pal");
// Show file chooser
switch (fc.show()) {
case -1: fprintf(stderr, "Error: %s\n", fc.errmsg()); break;
case 1: break; // Cancel
default:
if (fc.filename()) {
nst_palette_load(fc.filename());
nst_palette_save();
conf.video_palette_mode = 2;
video_init();
}
break;
}
}
static void fltkui_state_qload(Fl_Widget* w, void* userdata) {
nst_state_quickload(atoi((const char*)userdata));
}
static void fltkui_state_qsave(Fl_Widget* w, void* userdata) {
nst_state_quicksave(atoi((const char*)userdata));
}
static void fltkui_pause(Fl_Widget* w, void* userdata) {
if (nst_playing()) {
nst_pause();
}
else {
nst_play();
}
}
static void fltkui_reset(Fl_Widget* w, void* userdata) {
nst_reset(atoi((const char*)userdata));
}
void fltkui_resize() {
video_set_dimensions();
dimensions_t rendersize = nst_video_get_dimensions_render();
nstwin->size(rendersize.w, rendersize.h + MBARHEIGHT);
menubar->resize(0, 0, nstwin->w(), MBARHEIGHT);
glarea->resize(0, 24, rendersize.w, rendersize.h);
nst_video_set_dimensions_screen(rendersize);
video_init();
}
void fltkui_fullscreen(Fl_Widget* w, void* userdata) {
if (!nst_playing()) { return; }
conf.video_fullscreen ^= 1;
if (conf.video_fullscreen) {
int x, y, w, h;
Fl::screen_xywh(x, y, w, h);
menubar->hide();
nstwin->fullscreen();
dimensions_t scrdim = {w, h};
nstwin->resize(0, 0, scrdim.w, scrdim.h);
glarea->resize(0, 0, scrdim.w, scrdim.h);
nst_video_set_dimensions_screen(scrdim);
video_init();
}
else {
video_set_dimensions();
dimensions_t rendersize = nst_video_get_dimensions_render();
nstwin->fullscreen_off();
nstwin->size(rendersize.w, rendersize.h + MBARHEIGHT);
menubar->show();
menubar->resize(0, 0, nstwin->w(), MBARHEIGHT);
glarea->resize(0, 24, rendersize.w, rendersize.h);
nst_video_set_dimensions_screen(rendersize);
video_init();
}
}
static void fltkui_fds_flip(Fl_Widget* w, void* userdata) {
nst_fds_flip();
}
static void fltkui_fds_switch(Fl_Widget* w, void* userdata) {
nst_fds_switch();
}
static void fltkui_about_close(Fl_Widget* w, void* userdata) {
Fl_Window *about = (Fl_Window*)userdata;
about->hide();
}
static void fltkui_about(Fl_Widget* w, void* userdata) {
Fl_Window about(460, 440);
Fl_Box iconbox(166, 16, 128, 128);
Fl_Box text0(0, 144, 460, 24, "Nestopia UE");
text0.labelfont(FL_BOLD);
Fl_Box text1(0, 166, 460, 24, "1.51.0");
Fl_Box text2(0, 208, 460, 24, "Cycle-Accurate Nintendo Entertainment System Emulator");
Fl_Box text3(0, 256, 460, 24, "FLTK Frontend\n(c) 2012-2021, R. Danbrook\n(c) 2007-2008, R. Belmont");
text3.labelsize(10);
Fl_Box text4(0, 320, 460, 24, "Nestopia Emulator\n(c) 2020-2021, Rupert Carmichael\n(c) 2012-2020, Nestopia UE Contributors\n(c) 2003-2008, Martin Freij");
text4.labelsize(10);
Fl_Box text5(0, 360, 460, 24, "Icon based on drawing by Trollekop");
text5.labelsize(10);
Fl_PNG_Image nsticon("nestopia.png");
iconbox.image(nsticon);
Fl_Button close(360, 400, 80, 24, "&Close");
close.callback(fltkui_about_close, (void*)&about);
about.set_modal();
about.show();
while (about.shown()) { Fl::wait(); }
}
static void quit_cb(Fl_Widget* w, void* userdata) {
nstwin->hide();
}
// this is used to stop Esc from exiting the program:
int handle(int e) {
return (e == FL_SHORTCUT); // eat all keystrokes
}
int NstWindow::handle(int e) {
switch (e) { case FL_KEYDOWN: case FL_KEYUP: fltkui_input_process_key(e); break; }
return Fl_Double_Window::handle(e);
}
int NstGlArea::handle(int e) {
if (nst_input_zapper_present()) {
switch (e) {
case FL_ENTER:
cursor(conf.misc_disable_cursor_special ? FL_CURSOR_NONE : FL_CURSOR_CROSS);
break;
case FL_LEAVE:
cursor(FL_CURSOR_DEFAULT);
break;
case FL_PUSH:
nst_input_inject_mouse(cNstPads, Fl::event_button(), 1, Fl::event_x(), Fl::event_y());
break;
case FL_RELEASE:
nst_input_inject_mouse(cNstPads, Fl::event_button(), 0, Fl::event_x(), Fl::event_y());
break;
}
}
else if (e == FL_ENTER && conf.misc_disable_cursor) { cursor(FL_CURSOR_NONE); }
else if (e == FL_LEAVE) { cursor(FL_CURSOR_DEFAULT); }
return Fl_Gl_Window::handle(e);
}
static Fl_Menu_Item menutable[] = {
{"&File", 0, 0, 0, FL_SUBMENU},
{"&Open", FL_ALT + 'o', fltkui_rom_open, 0, FL_MENU_DIVIDER},
{"Load State...", 0, fltkui_state_load, 0, 0},
{"Save State...", 0, fltkui_state_save, 0, FL_MENU_DIVIDER},
{"Quick Load", 0, 0, 0, FL_SUBMENU},
{"Slot 0", 0, fltkui_state_qload, (void*)"0", 0},
{"Slot 1", 0, fltkui_state_qload, (void*)"1", 0},
{"Slot 2", 0, fltkui_state_qload, (void*)"2", 0},
{"Slot 3", 0, fltkui_state_qload, (void*)"3", 0},
{"Slot 4", 0, fltkui_state_qload, (void*)"4", 0},
{0},
{"Quick Save", 0, 0, 0, FL_SUBMENU|FL_MENU_DIVIDER},
{"Slot 0", 0, fltkui_state_qsave, (void*)"0", 0},
{"Slot 1", 0, fltkui_state_qsave, (void*)"1", 0},
{"Slot 2", 0, fltkui_state_qsave, (void*)"2", 0},
{"Slot 3", 0, fltkui_state_qsave, (void*)"3", 0},
{"Slot 4", 0, fltkui_state_qsave, (void*)"4", 0},
{0},
{"Open Palette...", 0, fltkui_palette_open, 0, FL_MENU_DIVIDER},
{"Screenshot...", 0, fltkui_screenshot, 0, FL_MENU_DIVIDER},
{"Load Movie...", 0, fltkui_movie_load, 0, 0},
{"Record Movie...", 0, fltkui_movie_save, 0, 0},
{"Stop Movie", 0, fltkui_movie_stop, 0, FL_MENU_DIVIDER},
{"&Quit", FL_ALT + 'q', quit_cb},
{0}, // End File
{"&Emulator", 0, 0, 0, FL_SUBMENU},
{"Pause/Play", 0, fltkui_pause, 0, FL_MENU_DIVIDER},
{"Reset (Soft)", 0, fltkui_reset, (void*)"0", 0},
{"Reset (Hard)", 0, fltkui_reset, (void*)"1", FL_MENU_DIVIDER},
{"Fullscreen", 0, fltkui_fullscreen, 0, FL_MENU_DIVIDER},
{"Flip Disk", 0, fltkui_fds_flip, 0, 0},
{"Switch Disk", 0, fltkui_fds_switch, 0, FL_MENU_DIVIDER},
//{"Cheats...", 0, 0, 0, FL_MENU_DIVIDER},
{"Configuration...", 0, fltkui_config, 0},
{0}, // End Emulator
{"&Help", 0, 0, 0, FL_SUBMENU},
{"About", 0, fltkui_about, 0, 0},
{0}, // End Help
{0} // End Menu
};
void makenstwin(const char *name) {
video_set_dimensions();
dimensions_t rendersize = nst_video_get_dimensions_render();
// Configuration Window
Fl::add_handler(handle);
confwin = new NstConfWindow(400, 400, "Configuration");
confwin->populate();
// Main Window
nstwin = new NstWindow(rendersize.w, rendersize.h + MBARHEIGHT, name);
nstwin->color(FL_BLACK);
nstwin->xclass("nestopia");
// Menu Bar
menubar = new Fl_Menu_Bar(0, 0, nstwin->w(), MBARHEIGHT);
menubar->box(FL_FLAT_BOX);
menubar->menu(menutable);
glarea = new NstGlArea(0, MBARHEIGHT, nstwin->w(), nstwin->h() - MBARHEIGHT);
glarea->color(FL_BLACK);
nstwin->resizable(glarea);
nstwin->resizable(nstwin);
nstwin->end();
}
int main(int argc, char *argv[]) {
// Set up directories
nst_set_dirs();
// Set default config options
config_set_default();
// Read the config file and override defaults
config_file_read(nstpaths.nstconfdir);
// Handle command line arguments
cli_handle_command(argc, argv);
// Set the video dimensions
video_set_dimensions();
// Set up callbacks
nst_set_callbacks();
// Initialize SDL Audio and Joystick
if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) {
fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
return 1;
}
// Set archive handler function pointer
nst_archive_select = &fltkui_archive_select;
// Detect and set up Joysticks
nstsdl_input_joysticks_detect();
nstsdl_input_conf_defaults();
nstsdl_input_conf_read();
// Initialize and load FDS BIOS and NstDatabase.xml
nst_fds_bios_load();
nst_db_load();
makenstwin(argv[0]);
nstwin->label("Nestopia UE");
nstwin->show();
menubar->show();
glarea->make_current();
glarea->show();
Fl::check();
// Load a rom from the command line
if (argc > 1 && argv[argc - 1][0] != '-') {
int loaded = nst_load(argv[argc - 1]);
if (loaded) { nst_play(); }
else { exit(1); }
nstwin->label(nstpaths.gamename);
}
else if (conf.video_fullscreen) {
conf.video_fullscreen = 0;
}
if (conf.video_fullscreen) {
conf.video_fullscreen = 0;
fltkui_fullscreen(NULL, NULL);
}
video_init();
while (true) {
glarea->redraw();
if (nstwin->visible() && !Fl::check()) {
break;
}
else if (!nstwin->shown() && confwin->shown()) {
break;
}
else if (!Fl::wait()) {
break;
}
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_JOYHATMOTION:
case SDL_JOYAXISMOTION:
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
nstsdl_input_process(cNstPads, event);
break;
default: break;
}
}
nst_emuloop();
glarea->redraw();
}
// Remove the cartridge and shut down the NES
nst_unload();
// Unload the FDS BIOS, NstDatabase.xml, and the custom palette
nst_db_unload();
nst_fds_bios_unload();
nst_palette_unload();
// Deinitialize audio
audio_deinit();
// Deinitialize joysticks
nstsdl_input_joysticks_close();
// Write the input config file
nstsdl_input_conf_write();
// Write the config file
config_file_write(nstpaths.nstconfdir);
return 0;
}

33
source/fltkui/fltkui.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef MAIN_H
#define MAIN_H
class NstWindow : public Fl_Double_Window {
private:
int handle(int e);
public:
NstWindow(int w, int h, const char* t = 0) : Fl_Double_Window(w, h, t) { }
virtual ~NstWindow() { }
};
class NstGlArea : public Fl_Gl_Window {
private:
void draw() { nst_ogl_render(); }
int handle(int e);
public:
NstGlArea(int x, int y, int w, int h, const char *l = 0) : Fl_Gl_Window(x, y, w, h, l) {
box(FL_DOWN_FRAME);
}
};
extern Fl_Color NstGreen;
extern Fl_Color NstPurple;
extern Fl_Color NstRed;
extern Fl_Color NstBlueGrey;
extern Fl_Color NstLightGrey;
void fltkui_resize();
void fltkui_fullscreen(Fl_Widget* w, void* userdata);
#endif

View file

@ -0,0 +1,125 @@
/*
* Nestopia UE
*
* Copyright (C) 2012-2021 R. Danbrook
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include <cstring>
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Select_Browser.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Button.H>
#include <archive.h>
#include <archive_entry.h>
#include "nstcommon.h"
#include "config.h"
#include "fltkui_archive.h"
static Fl_Double_Window *window;
static Fl_Select_Browser *browser;
static char *romfile = NULL;
static void fltkui_archive_ok(Fl_Widget *w, long) {
window->hide();
}
static void fltkui_archive_cancel(Fl_Widget *w, long) {
snprintf(romfile, 256, "%s", "");
window->hide();
}
static void fltkui_archive_setfile(Fl_Widget *w, long) {
int r = ((Fl_Browser*)w)->value();
if (r) {
snprintf(romfile, 256, "%s", ((Fl_Browser*)w)->text(r));
if (Fl::event_clicks()) { window->hide(); }
}
else {
snprintf(romfile, 256, "%s", "");
}
}
bool fltkui_archive_select(const char *filename, char *reqfile, size_t reqsize) {
// Select a filename to pull out of the archive
struct archive *a;
struct archive_entry *entry;
int r, numarchives = 0;
a = archive_read_new();
archive_read_support_filter_all(a);
archive_read_support_format_all(a);
r = archive_read_open_filename(a, filename, 10240);
// Test if it's actually an archive
if (r != ARCHIVE_OK) {
r = archive_read_free(a);
return false;
}
// If it is an archive, handle it
else {
if (window) { delete window; }
window = new Fl_Double_Window(420, 260, "Load from Archive");
browser = new Fl_Select_Browser(0, 0, window->w(), 200, 0);
browser->type(FL_HOLD_BROWSER);
browser->callback(fltkui_archive_setfile, 0);
Fl_Button btncancel(260, 220, 80, 24, "&Cancel");
btncancel.callback(fltkui_archive_cancel, 0);
Fl_Button btnok(350, 220, 40, 24, "&OK");
btnok.callback(fltkui_archive_ok, 0);
romfile = reqfile;
// Fill the treestore with the filenames
while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
const char *currentfile = archive_entry_pathname(entry);
if (nst_archive_checkext(currentfile)) {
browser->add(currentfile);
numarchives++;
snprintf(reqfile, reqsize, "%s", currentfile);
}
archive_read_data_skip(a);
}
// Free the archive
r = archive_read_free(a);
// If there are no valid files in the archive, return
if (numarchives == 0) { return false; }
// If there's only one file, don't bring up the selector
else if (numarchives == 1) { return true; }
browser->select(1);
snprintf(romfile, 256, "%s", browser->text(1));
window->resizable(browser);
window->show();
window->set_modal();
window->show();
while (window->shown()) { Fl::wait(); }
if (strlen(romfile)) { return true; }
}
return false;
}

View file

@ -0,0 +1,6 @@
#ifndef _FLTKUI_ARCHIVE_H_
#define _FLTKUI_ARCHIVE_H_
bool fltkui_archive_select(const char *filename, char *reqfile, size_t reqsize);
#endif

View file

@ -0,0 +1,666 @@
/*
* Nestopia UE
*
* Copyright (C) 2012-2021 R. Danbrook
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include <cstdio>
#include <cstring>
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Gl_Window.H>
#include <FL/Fl_Tabs.H>
#include <FL/Fl_Choice.H>
#include <FL/Fl_Check_Button.H>
#include <FL/Fl_Hor_Value_Slider.H>
#include <FL/Fl_Dial.H>
#include <FL/Fl_Output.H>
#include "nstcommon.h"
#include "config.h"
#include "audio.h"
#include "video.h"
#include "input.h"
#include "fltkui.h"
#include "fltkui_config.h"
static const char *icfg_labels[10] = {
"Press Key For: Up",
"Press Key For: Down",
"Press Key For: Left",
"Press Key For: Right",
"Press Key For: Select",
"Press Key For: Start",
"Press Key For: A",
"Press Key For: B",
"Press Key For: Turbo A",
"Press Key For: Turbo B"
};
NstInputConfWindow *icfg;
static Fl_Dial *dial_vall, *dial_vsq1, *dial_vsq2, *dial_vtri, *dial_vnoise, *dial_vdpcm,
*dial_vfds, *dial_vmmc5, *dial_vvrc6, *dial_vvrc7, *dial_vn163, *dial_vs5b;
extern inputsettings_t inputconf;
static void cb_filter(Fl_Widget *w, long) {
conf.video_filter = ((Fl_Choice*)w)->value();
fltkui_resize();
}
static void cb_scale(Fl_Widget *w, long) {
conf.video_scale_factor = ((Fl_Choice*)w)->value() + 1;
fltkui_resize();
}
static void cb_ntscmode(Fl_Widget *w, long) {
conf.video_ntsc_mode = ((Fl_Choice*)w)->value();
fltkui_resize();
}
static void cb_xbrrounding(Fl_Widget *w, long) {
conf.video_xbr_corner_rounding = ((Fl_Choice*)w)->value();
video_init();
video_toggle_filterupdate();
}
static void cb_palettemode(Fl_Widget *w, long) {
conf.video_palette_mode = ((Fl_Choice*)w)->value();
video_init();
}
static void cb_decoder(Fl_Widget *w, long) {
conf.video_decoder = ((Fl_Choice*)w)->value();
video_init();
}
static void cb_brightness(Fl_Widget *w, long) {
conf.video_brightness = ((Fl_Valuator*)w)->value();
video_init();
}
static void cb_saturation(Fl_Widget *w, long) {
conf.video_saturation = ((Fl_Valuator*)w)->value();
video_init();
}
static void cb_contrast(Fl_Widget *w, long) {
conf.video_contrast = ((Fl_Valuator*)w)->value();
video_init();
}
static void cb_hue(Fl_Widget *w, long) {
conf.video_hue = ((Fl_Valuator*)w)->value();
video_init();
}
static void cb_xbrpixblend(Fl_Widget *w, long) {
conf.video_xbr_pixel_blending = ((Fl_Check_Button*)w)->value();
video_init();
video_toggle_filterupdate();
}
static void cb_linearfilter(Fl_Widget *w, long) {
conf.video_linear_filter = ((Fl_Check_Button*)w)->value();
video_init();
}
static void cb_tvaspect(Fl_Widget *w, long) {
conf.video_tv_aspect = ((Fl_Check_Button*)w)->value();
fltkui_resize();
}
static void cb_unmask_overscan(Fl_Widget *w, long) {
conf.video_unmask_overscan = ((Fl_Check_Button*)w)->value();
fltkui_resize();
}
static void cb_unlimited_sprites(Fl_Widget *w, long) {
conf.video_unlimited_sprites = ((Fl_Check_Button*)w)->value();
}
static void cb_samplerate(Fl_Widget *w, long) {
switch (((Fl_Choice*)w)->value()) {
case 0: conf.audio_sample_rate = 11025; break;
case 1: conf.audio_sample_rate = 22050; break;
case 2: conf.audio_sample_rate = 44100; break;
case 3: conf.audio_sample_rate = 48000; break;
case 4: conf.audio_sample_rate = 96000; break;
default: conf.audio_sample_rate = 48000; break;
}
if (nst_playing()) {
nst_pause();
nst_play();
}
}
static void cb_dials() {
dial_vall->value(conf.audio_volume);
dial_vsq1->value(conf.audio_vol_sq1);
dial_vsq2->value(conf.audio_vol_sq2);
dial_vtri->value(conf.audio_vol_tri);
dial_vnoise->value(conf.audio_vol_noise);
dial_vdpcm->value(conf.audio_vol_dpcm);
dial_vfds->value(conf.audio_vol_fds);
dial_vmmc5->value(conf.audio_vol_mmc5);
dial_vvrc6->value(conf.audio_vol_vrc6);
dial_vvrc7->value(conf.audio_vol_vrc7);
dial_vn163->value(conf.audio_vol_n163);
dial_vs5b->value(conf.audio_vol_s5b);
}
static void cb_volume(Fl_Widget *w, long adj) {
Fl_Dial *dial = (Fl_Dial*)w;
switch (adj) {
case 0:
conf.audio_volume = (int)dial->value();
conf.audio_vol_sq1 = conf.audio_vol_sq2 = conf.audio_vol_tri = conf.audio_vol_noise =
conf.audio_vol_dpcm = conf.audio_vol_fds = conf.audio_vol_mmc5 = conf.audio_vol_vrc6 =
conf.audio_vol_vrc7 = conf.audio_vol_n163 = conf.audio_vol_s5b = conf.audio_volume;
break;
case 1: conf.audio_vol_sq1 = (int)dial->value(); break;
case 2: conf.audio_vol_sq2 = (int)dial->value(); break;
case 3: conf.audio_vol_tri = (int)dial->value(); break;
case 4: conf.audio_vol_noise = (int)dial->value(); break;
case 5: conf.audio_vol_dpcm = (int)dial->value(); break;
case 6: conf.audio_vol_fds = (int)dial->value(); break;
case 7: conf.audio_vol_mmc5 = (int)dial->value(); break;
case 8: conf.audio_vol_vrc6 = (int)dial->value(); break;
case 9: conf.audio_vol_vrc7 = (int)dial->value(); break;
case 10: conf.audio_vol_n163 = (int)dial->value(); break;
case 11: conf.audio_vol_s5b = (int)dial->value(); break;
}
audio_adj_volume();
cb_dials();
}
static void cb_stereo(Fl_Widget *w, long) {
conf.audio_stereo = ((Fl_Check_Button*)w)->value();
if (nst_playing()) {
nst_pause();
nst_play();
}
}
int NstInputConfWindow::handle(int e) {
switch (e) {
case FL_KEYUP:
fltkui_input_conf_set(Fl::event_key(), player, btn);
this->hide();
this->set_non_modal();
break;
}
return Fl_Double_Window::handle(e);
}
static void cb_icfg(Fl_Widget *w, long btn) {
icfg->set_modal();
icfg->btn = btn;
icfg->text->label(icfg_labels[btn]);
icfg->show();
if (icfg->device == 1) {
nstsdl_input_conf_button(icfg->player, btn);
icfg->hide();
icfg->set_non_modal();
}
}
static void cb_player(Fl_Widget *w, long) {
icfg->player = ((Fl_Choice*)w)->value();
}
static void cb_idevice(Fl_Widget *w, long) {
icfg->device = ((Fl_Choice*)w)->value();
}
static void cb_turbopulse(Fl_Widget *w, long) {
conf.timing_turbopulse = ((Fl_Valuator*)w)->value();
}
static void cb_default_system(Fl_Widget *w, long) {
conf.misc_default_system = ((Fl_Choice*)w)->value();
}
static void cb_power_state(Fl_Widget *w, long) {
conf.misc_power_state = ((Fl_Choice*)w)->value();
}
static void cb_ffspeed(Fl_Widget *w, long) {
conf.timing_ffspeed = ((Fl_Valuator*)w)->value();
}
static void cb_soft_patching(Fl_Widget *w, long) {
conf.misc_soft_patching = ((Fl_Check_Button*)w)->value();
}
static void cb_genie_distortion(Fl_Widget *w, long) {
conf.misc_genie_distortion = ((Fl_Check_Button*)w)->value();
}
static void cb_disable_cursor(Fl_Widget *w, long) {
conf.misc_disable_cursor = ((Fl_Check_Button*)w)->value();
}
static void cb_disable_cursor_special(Fl_Widget *w, long) {
conf.misc_disable_cursor_special = ((Fl_Check_Button*)w)->value();
}
static void cb_ok(Fl_Widget *w, long) {
w->parent()->hide();
}
void NstConfWindow::populate() {
Fl_Tabs *tabs = new Fl_Tabs(10, 5, 380, 360);
Fl_Group *vtab = new Fl_Group(10, 30, 380, 360, "&Video");
Fl_Choice *ch_filter = new Fl_Choice(20, 55, 160, 25, "Filter");
ch_filter->align(FL_ALIGN_TOP_LEFT);
ch_filter->add("None");
ch_filter->add("NTSC");
ch_filter->add("xBR");
ch_filter->add("HqX");
ch_filter->add("2XSaI");
ch_filter->add("ScaleX");
ch_filter->value(conf.video_filter);
ch_filter->callback(cb_filter);
Fl_Choice *ch_scale = new Fl_Choice(200, 55, 160, 25, "Scale Factor");
ch_scale->align(FL_ALIGN_TOP_LEFT);
ch_scale->add("1x");
ch_scale->add("2x");
ch_scale->add("3x");
ch_scale->add("4x");
ch_scale->add("5x");
ch_scale->add("6x");
ch_scale->add("7x");
ch_scale->add("8x");
ch_scale->value(conf.video_scale_factor - 1);
ch_scale->callback(cb_scale);
Fl_Choice *ch_ntscmode = new Fl_Choice(20, 105, 160, 25, "NTSC Mode");
ch_ntscmode->align(FL_ALIGN_TOP_LEFT);
ch_ntscmode->add("Composite");
ch_ntscmode->add("S-Video");
ch_ntscmode->add("RGB");
ch_ntscmode->add("Monochrome");
ch_ntscmode->add("Custom");
ch_ntscmode->value(conf.video_ntsc_mode);
ch_ntscmode->callback(cb_ntscmode);
Fl_Choice *ch_xbrrounding = new Fl_Choice(200, 105, 160, 25, "xBR Corner Rounding");
ch_xbrrounding->align(FL_ALIGN_TOP_LEFT);
ch_xbrrounding->add("None");
ch_xbrrounding->add("Some");
ch_xbrrounding->add("All");
ch_xbrrounding->value(conf.video_xbr_corner_rounding);
ch_xbrrounding->callback(cb_xbrrounding);
Fl_Choice *ch_palettemode = new Fl_Choice(20, 155, 160, 25, "Palette Mode");
ch_palettemode->align(FL_ALIGN_TOP_LEFT);
ch_palettemode->add("YUV");
ch_palettemode->add("RGB");
ch_palettemode->add("Custom");
ch_palettemode->value(conf.video_palette_mode);
ch_palettemode->callback(cb_palettemode);
Fl_Choice *ch_decoder = new Fl_Choice(200, 155, 160, 25, "YUV Decoder");
ch_decoder->align(FL_ALIGN_TOP_LEFT);
ch_decoder->add("Consumer");
ch_decoder->add("Canonical");
ch_decoder->add("Alternative");
ch_decoder->value(conf.video_decoder);
ch_decoder->callback(cb_decoder);
Fl_Hor_Value_Slider *sld_brightness = new Fl_Hor_Value_Slider(20, 210, 160, 25, "Brightness");
sld_brightness->align(FL_ALIGN_TOP_LEFT);
sld_brightness->bounds(-100, 100);
sld_brightness->box(FL_FLAT_BOX);
sld_brightness->callback(cb_brightness);
sld_brightness->step(1);
sld_brightness->selection_color(NstGreen);
sld_brightness->type(FL_HOR_NICE_SLIDER);
sld_brightness->value(conf.video_brightness);
Fl_Hor_Value_Slider *sld_saturation = new Fl_Hor_Value_Slider(20, 250, 160, 25, "Saturation");
sld_saturation->align(FL_ALIGN_TOP_LEFT);
sld_saturation->bounds(-100, 100);
sld_saturation->box(FL_FLAT_BOX);
sld_saturation->callback(cb_saturation);
sld_saturation->step(1);
sld_saturation->selection_color(NstGreen);
sld_saturation->type(FL_HOR_NICE_SLIDER);
sld_saturation->value(conf.video_saturation);
Fl_Hor_Value_Slider *sld_contrast = new Fl_Hor_Value_Slider(20, 290, 160, 25, "Contrast");
sld_contrast->align(FL_ALIGN_TOP_LEFT);
sld_contrast->bounds(-100, 100);
sld_contrast->box(FL_FLAT_BOX);
sld_contrast->callback(cb_contrast);
sld_contrast->step(1);
sld_contrast->selection_color(NstGreen);
sld_contrast->type(FL_HOR_NICE_SLIDER);
sld_contrast->value(conf.video_contrast);
Fl_Hor_Value_Slider *sld_hue = new Fl_Hor_Value_Slider(20, 330, 160, 25, "Hue");
sld_hue->align(FL_ALIGN_TOP_LEFT);
sld_hue->bounds(-45, 45);
sld_hue->box(FL_FLAT_BOX);
sld_hue->callback(cb_hue);
sld_hue->step(1);
sld_hue->selection_color(NstGreen);
sld_hue->type(FL_HOR_NICE_SLIDER);
sld_hue->value(conf.video_hue);
Fl_Check_Button *chk_xbrpixblend = new Fl_Check_Button(200, 210, 160, 25, "xBR Pixel Blending");
chk_xbrpixblend->value(conf.video_xbr_pixel_blending);
chk_xbrpixblend->callback(cb_xbrpixblend);
Fl_Check_Button *chk_linearfilter = new Fl_Check_Button(200, 235, 160, 25, "Linear Filter");
chk_linearfilter->value(conf.video_linear_filter);
chk_linearfilter->callback(cb_linearfilter);
Fl_Check_Button *chk_tvaspect = new Fl_Check_Button(200, 260, 160, 25, "TV Aspect Ratio");
chk_tvaspect->value(conf.video_tv_aspect);
chk_tvaspect->callback(cb_tvaspect);
Fl_Check_Button *chk_unmask_overscan = new Fl_Check_Button(200, 285, 160, 25, "Unmask Overscan");
chk_unmask_overscan->value(conf.video_unmask_overscan);
chk_unmask_overscan->callback(cb_unmask_overscan);
Fl_Check_Button *chk_unlimited_sprites = new Fl_Check_Button(200, 310, 160, 25, "Unlimited Sprites");
chk_unlimited_sprites->value(conf.video_unlimited_sprites);
chk_unlimited_sprites->callback(cb_unlimited_sprites);
vtab->end();
Fl_Group *atab = new Fl_Group(10, 30, 380, 360, "&Audio");
Fl_Choice *ch_samplerate = new Fl_Choice(20, 55, 160, 25, "Sample Rate");
ch_samplerate->align(FL_ALIGN_TOP_LEFT);
ch_samplerate->add("11025Hz");
ch_samplerate->add("22050Hz");
ch_samplerate->add("44100Hz");
ch_samplerate->add("48000Hz");
ch_samplerate->add("96000Hz");
switch (conf.audio_sample_rate) {
case 11025: ch_samplerate->value(0); break;
case 22050: ch_samplerate->value(1); break;
case 44100: ch_samplerate->value(2); break;
case 48000: ch_samplerate->value(3); break;
case 96000: ch_samplerate->value(4); break;
default: ch_samplerate->value(3); break;
}
ch_samplerate->callback(cb_samplerate);
Fl_Check_Button *chk_stereo = new Fl_Check_Button(200, 55, 160, 25, "Stereo");
chk_stereo->value(conf.audio_stereo);
chk_stereo->callback(cb_stereo);
dial_vall = new Fl_Dial(20, 100, 100, 100, "All");
dial_vall->bounds(0, 100);
dial_vall->step(1);
dial_vall->color(NstPurple);
dial_vall->selection_color(NstGreen);
dial_vall->callback(cb_volume, 0);
dial_vall->value(conf.audio_volume);
dial_vsq1 = new Fl_Dial(130, 115, 40, 40, "SQ1");
dial_vsq1->bounds(0, 100);
dial_vsq1->step(1);
dial_vsq1->color(NstGreen);
dial_vsq1->selection_color(NstPurple);
dial_vsq1->callback(cb_volume, 1);
dial_vsq1->value(conf.audio_vol_sq1);
dial_vsq2 = new Fl_Dial(180, 115, 40, 40, "SQ2");
dial_vsq2->bounds(0, 100);
dial_vsq2->step(1);
dial_vsq2->color(NstGreen);
dial_vsq2->selection_color(NstPurple);
dial_vsq2->callback(cb_volume, 2);
dial_vsq2->value(conf.audio_vol_sq2);
dial_vtri = new Fl_Dial(230, 115, 40, 40, "TRI");
dial_vtri->bounds(0, 100);
dial_vtri->step(1);
dial_vtri->color(NstGreen);
dial_vtri->selection_color(NstPurple);
dial_vtri->callback(cb_volume, 3);
dial_vtri->value(conf.audio_vol_tri);
dial_vnoise = new Fl_Dial(280, 115, 40, 40, "NOISE");
dial_vnoise->bounds(0, 100);
dial_vnoise->step(1);
dial_vnoise->color(NstGreen);
dial_vnoise->selection_color(NstPurple);
dial_vnoise->callback(cb_volume, 4);
dial_vnoise->value(conf.audio_vol_noise);
dial_vdpcm = new Fl_Dial(330, 115, 40, 40, "DPCM");
dial_vdpcm->bounds(0, 100);
dial_vdpcm->step(1);
dial_vdpcm->color(NstGreen);
dial_vdpcm->selection_color(NstPurple);
dial_vdpcm->callback(cb_volume, 5);
dial_vdpcm->value(conf.audio_vol_dpcm);
dial_vfds = new Fl_Dial(80, 225, 40, 40, "FDS");
dial_vfds->bounds(0, 100);
dial_vfds->step(1);
dial_vfds->color(NstGreen);
dial_vfds->selection_color(NstPurple);
dial_vfds->callback(cb_volume, 6);
dial_vfds->value(conf.audio_vol_fds);
dial_vmmc5 = new Fl_Dial(130, 225, 40, 40, "MMC5");
dial_vmmc5->bounds(0, 100);
dial_vmmc5->step(1);
dial_vmmc5->color(NstGreen);
dial_vmmc5->selection_color(NstPurple);
dial_vmmc5->callback(cb_volume, 7);
dial_vmmc5->value(conf.audio_vol_mmc5);
dial_vvrc6 = new Fl_Dial(180, 225, 40, 40, "VRC6");
dial_vvrc6->bounds(0, 100);
dial_vvrc6->step(1);
dial_vvrc6->color(NstGreen);
dial_vvrc6->selection_color(NstPurple);
dial_vvrc6->callback(cb_volume, 8);
dial_vvrc6->value(conf.audio_vol_vrc6);
dial_vvrc7 = new Fl_Dial(230, 225, 40, 40, "VRC7");
dial_vvrc7->bounds(0, 100);
dial_vvrc7->step(1);
dial_vvrc7->color(NstGreen);
dial_vvrc7->selection_color(NstPurple);
dial_vvrc7->callback(cb_volume, 9);
dial_vvrc7->value(conf.audio_vol_vrc7);
dial_vn163 = new Fl_Dial(280, 225, 40, 40, "N163");
dial_vn163->bounds(0, 100);
dial_vn163->step(1);
dial_vn163->color(NstGreen);
dial_vn163->selection_color(NstPurple);
dial_vn163->callback(cb_volume, 10);
dial_vn163->value(conf.audio_vol_n163);
dial_vs5b = new Fl_Dial(330, 225, 40, 40, "S5B");
dial_vs5b->bounds(0, 100);
dial_vs5b->step(1);
dial_vs5b->color(NstGreen);
dial_vs5b->selection_color(NstPurple);
dial_vs5b->callback(cb_volume, 11);
dial_vs5b->value(conf.audio_vol_s5b);
atab->end();
Fl_Group *itab = new Fl_Group(10, 30, 380, 360, "&Input");
Fl_Button *btn_icfg_u = new Fl_Button(70, 55, 30, 24, "U");
btn_icfg_u->callback(cb_icfg, 0);
btn_icfg_u->color(NstBlueGrey);
btn_icfg_u->labelcolor(NstLightGrey);
Fl_Button *btn_icfg_d = new Fl_Button(70, 115, 30, 24, "D");
btn_icfg_d->callback(cb_icfg, 1);
btn_icfg_d->color(NstBlueGrey);
btn_icfg_d->labelcolor(NstLightGrey);
Fl_Button *btn_icfg_l = new Fl_Button(30, 85, 30, 24, "L");
btn_icfg_l->callback(cb_icfg, 2);
btn_icfg_l->color(NstBlueGrey);
btn_icfg_l->labelcolor(NstLightGrey);
Fl_Button *btn_icfg_r = new Fl_Button(110, 85, 30, 24, "R");
btn_icfg_r->callback(cb_icfg, 3);
btn_icfg_r->color(NstBlueGrey);
btn_icfg_r->labelcolor(NstLightGrey);
Fl_Button *btn_icfg_slct = new Fl_Button(150, 85, 60, 24, "Select");
btn_icfg_slct->callback(cb_icfg, 4);
btn_icfg_slct->color(NstGreen);
btn_icfg_slct->labelcolor(NstLightGrey);
Fl_Button *btn_icfg_strt = new Fl_Button(220, 85, 60, 24, "Start");
btn_icfg_strt->callback(cb_icfg, 5);
btn_icfg_strt->color(NstGreen);
btn_icfg_strt->labelcolor(NstLightGrey);
Fl_Button *btn_icfg_a = new Fl_Button(330, 100, 30, 24, "A");
btn_icfg_a->callback(cb_icfg, 6);
btn_icfg_a->color(NstRed);
btn_icfg_a->labelcolor(NstLightGrey);
Fl_Button *btn_icfg_b = new Fl_Button(290, 100, 30, 24, "B");
btn_icfg_b->callback(cb_icfg, 7);
btn_icfg_b->color(NstRed);
btn_icfg_b->labelcolor(NstLightGrey);
Fl_Button *btn_icfg_ta = new Fl_Button(330, 65, 30, 24, "TA");
btn_icfg_ta->callback(cb_icfg, 8);
btn_icfg_ta->color(NstRed);
btn_icfg_ta->labelcolor(NstLightGrey);
Fl_Button *btn_icfg_tb = new Fl_Button(290, 65, 30, 24, "TB");
btn_icfg_tb->callback(cb_icfg, 9);
btn_icfg_tb->color(NstRed);
btn_icfg_tb->labelcolor(NstLightGrey);
icfg = new NstInputConfWindow(110, 55, 170, 24, "Input Config");
icfg->color(NstPurple);
icfg->hide();
icfg->text = new Fl_Box(0, 0, 0, 24);
icfg->text->align(FL_ALIGN_RIGHT);
icfg->text->labelcolor(NstLightGrey);
icfg->player = icfg->btn = icfg->device = 0;
icfg->end();
Fl_Choice *ch_player = new Fl_Choice(20, 180, 160, 25, "Player");
ch_player->align(FL_ALIGN_TOP_LEFT);
ch_player->add("Player 1");
ch_player->add("Player 2");
ch_player->value(0);
ch_player->callback(cb_player);
Fl_Choice *ch_idevice = new Fl_Choice(20, 230, 160, 25, "Input Device");
ch_idevice->align(FL_ALIGN_TOP_LEFT);
ch_idevice->add("Keyboard");
ch_idevice->add("Joystick");
ch_idevice->value(0);
ch_idevice->callback(cb_idevice);
Fl_Hor_Value_Slider *sld_turbopulse = new Fl_Hor_Value_Slider(200, 180, 160, 25, "Turbo Pulse");
sld_turbopulse->align(FL_ALIGN_TOP_LEFT);
sld_turbopulse->bounds(2, 9);
sld_turbopulse->box(FL_FLAT_BOX);
sld_turbopulse->callback(cb_turbopulse);
sld_turbopulse->step(1);
sld_turbopulse->selection_color(NstGreen);
sld_turbopulse->type(FL_HOR_NICE_SLIDER);
sld_turbopulse->value(conf.timing_turbopulse);
itab->end();
Fl_Group *mtab = new Fl_Group(10, 30, 380, 360, "&Misc");
Fl_Choice *ch_default_system = new Fl_Choice(20, 55, 160, 25, "Default System");
ch_default_system->align(FL_ALIGN_TOP_LEFT);
ch_default_system->add("Auto");
ch_default_system->add("NTSC");
ch_default_system->add("PAL");
ch_default_system->add("Famicom");
ch_default_system->add("Dendy");
ch_default_system->value(conf.misc_default_system);
ch_default_system->callback(cb_default_system);
Fl_Choice *ch_power_state = new Fl_Choice(20, 105, 160, 25, "RAM Power-on State");
ch_power_state->align(FL_ALIGN_TOP_LEFT);
ch_power_state->add("0x00");
ch_power_state->add("0xFF");
ch_power_state->add("Random");
ch_power_state->value(conf.misc_power_state);
ch_power_state->callback(cb_power_state);
Fl_Hor_Value_Slider *sld_ffspeed = new Fl_Hor_Value_Slider(20, 160, 160, 25, "Fast-Forward Speed");
sld_ffspeed->align(FL_ALIGN_TOP_LEFT);
sld_ffspeed->bounds(1, 8);
sld_ffspeed->box(FL_FLAT_BOX);
sld_ffspeed->callback(cb_ffspeed);
sld_ffspeed->step(1);
sld_ffspeed->selection_color(NstGreen);
sld_ffspeed->type(FL_HOR_NICE_SLIDER);
sld_ffspeed->value(conf.timing_ffspeed);
Fl_Check_Button *chk_soft_patching = new Fl_Check_Button(200, 55, 185, 25, "Auto Soft Patching");
chk_soft_patching->value(conf.misc_soft_patching);
chk_soft_patching->callback(cb_soft_patching);
Fl_Check_Button *chk_genie_distortion = new Fl_Check_Button(200, 80, 185, 25, "Genie Sound Distortion");
chk_genie_distortion->value(conf.misc_genie_distortion);
chk_genie_distortion->callback(cb_genie_distortion);
Fl_Check_Button *chk_disable_cursor = new Fl_Check_Button(200, 105, 185, 25, "Disable Cursor");
chk_disable_cursor->value(conf.misc_disable_cursor);
chk_disable_cursor->callback(cb_disable_cursor);
Fl_Check_Button *chk_disable_cursor_special = new Fl_Check_Button(200, 130, 185, 25, "Disable Special Cursor");
chk_disable_cursor_special->value(conf.misc_disable_cursor_special);
chk_disable_cursor_special->callback(cb_disable_cursor_special);
mtab->end();
tabs->end();
Fl_Button *btn_ok = new Fl_Button(350, 370, 40, 24, "&OK");
btn_ok->callback(cb_ok, 0);
this->end();
}

View file

@ -0,0 +1,31 @@
#ifndef _FLTKUI_CONFIG_H_
#define _FLTKUI_CONFIG_H_
class NstConfWindow : public Fl_Double_Window {
private:
bool icfg_running;
public:
NstConfWindow(int w, int h, const char* t) : Fl_Double_Window(w, h, t) { }
virtual ~NstConfWindow() { }
void populate();
};
class NstInputConfWindow : public Fl_Double_Window {
private:
int handle(int e);
public:
NstInputConfWindow(int x, int y, int w, int h, const char* t) : Fl_Double_Window(x, y, w, h, t) {
box(FL_DOWN_BOX);
}
virtual ~NstInputConfWindow() { }
Fl_Box *text;
int btn;
int player;
int device; // Keyboard or Joystick
};
#endif

View file

@ -1,42 +1,55 @@
/*
* Nestopia UE
*
* Copyright (C) 2012-2018 R. Danbrook
*
*
* Copyright (C) 2012-2016 R. Danbrook
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*
*/
#include <stdio.h>
#include <cstdio>
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Gl_Window.H>
#include <SDL.h>
#include "gtkui/gtkui.h"
#include "nstcommon.h"
#include "video.h"
#include "config.h"
#include "input.h"
#include "video.h"
#include "ini.h"
#include "sdlinput.h"
#include "fltkui.h"
static SDL_Joystick *joystick;
gamepad_t player[NUMGAMEPADS];
static inputsettings_t inputconf;
inputsettings_t inputconf;
static char inputconfpath[256];
static turbo_t turbostate;
static turbo_t turbotoggle;
extern Emulator emulator;
extern nstpaths_t nstpaths;
extern Emulator emulator;
extern Input::Controllers *cNstPads;
extern int drawtext;
static unsigned char nescodes[TOTALBUTTONS] = {
@ -62,22 +75,241 @@ static unsigned char nescodes[TOTALBUTTONS] = {
Input::Controllers::Pad::B
};
extern Emulator emulator;
extern nstpaths_t nstpaths;
void nst_input_init() {
// Initialize input
char controller[32];
for (int i = 0; i < NUMGAMEPADS; i++) {
Input(emulator).AutoSelectController(i);
switch(Input(emulator).GetConnectedController(i)) {
case Input::UNCONNECTED:
snprintf(controller, sizeof(controller), "%s", "Unconnected");
break;
case Input::PAD1:
case Input::PAD2:
case Input::PAD3:
case Input::PAD4:
snprintf(controller, sizeof(controller), "%s", "Standard Pad");
break;
case Input::ZAPPER:
snprintf(controller, sizeof(controller), "%s", "Zapper");
break;
case Input::PADDLE:
snprintf(controller, sizeof(controller), "%s", "Arkanoid Paddle");
break;
case Input::POWERPAD:
snprintf(controller, sizeof(controller), "%s", "Power Pad");
break;
case Input::POWERGLOVE:
snprintf(controller, sizeof(controller), "%s", "Power Glove");
break;
case Input::MOUSE:
snprintf(controller, sizeof(controller), "%s", "Mouse");
break;
case Input::ROB:
snprintf(controller, sizeof(controller), "%s", "R.O.B.");
break;
case Input::FAMILYTRAINER:
snprintf(controller, sizeof(controller), "%s", "Family Trainer");
break;
case Input::FAMILYKEYBOARD:
snprintf(controller, sizeof(controller), "%s", "Family Keyboard");
break;
case Input::SUBORKEYBOARD:
snprintf(controller, sizeof(controller), "%s", "Subor Keyboard");
break;
case Input::DOREMIKKOKEYBOARD:
snprintf(controller, sizeof(controller), "%s", "Doremikko Keyboard");
break;
case Input::HORITRACK:
snprintf(controller, sizeof(controller), "%s", "Hori Track");
break;
case Input::PACHINKO:
snprintf(controller, sizeof(controller), "%s", "Pachinko");
break;
case Input::OEKAKIDSTABLET:
snprintf(controller, sizeof(controller), "%s", "Oeka Kids Tablet");
break;
case Input::KONAMIHYPERSHOT:
snprintf(controller, sizeof(controller), "%s", "Konami Hypershot");
break;
case Input::BANDAIHYPERSHOT:
snprintf(controller, sizeof(controller), "%s", "Bandai Hypershot");
break;
case Input::CRAZYCLIMBER:
snprintf(controller, sizeof(controller), "%s", "Crazy Climber");
break;
case Input::MAHJONG:
snprintf(controller, sizeof(controller), "%s", "Mahjong");
break;
case Input::EXCITINGBOXING:
snprintf(controller, sizeof(controller), "%s", "Exciting Boxing");
break;
case Input::TOPRIDER:
snprintf(controller, sizeof(controller), "%s", "Top Rider");
break;
case Input::POKKUNMOGURAA:
snprintf(controller, sizeof(controller), "%s", "Pokkun Moguraa");
break;
case Input::PARTYTAP:
snprintf(controller, sizeof(controller), "%s", "PartyTap");
break;
case Input::TURBOFILE:
snprintf(controller, sizeof(controller), "%s", "Turbo File");
break;
case Input::BARCODEWORLD:
snprintf(controller, sizeof(controller), "%s", "Barcode World");
break;
default:
snprintf(controller, sizeof(controller), "%s", "Unknown");
break;
}
fprintf(stderr, "Port %d: %s\n", i + 1, controller);
}
}
void nst_input_inject(Input::Controllers *controllers, nesinput_t input) {
// Insert the input signal into the NES
if(controllers == NULL) { return; }
if (input.pressed) {
controllers->pad[input.player].buttons |= input.nescode;
if (input.turboa) { input.player == 0 ? turbostate.p1a = true : turbostate.p2a = true; }
if (input.turbob) { input.player == 0 ? turbostate.p1b = true : turbostate.p2b = true; }
}
else {
controllers->pad[input.player].buttons &= ~input.nescode;
if (input.turboa) { input.player == 0 ? turbostate.p1a = false : turbostate.p2a = false; }
if (input.turbob) { input.player == 0 ? turbostate.p1b = false : turbostate.p2b = false; }
}
}
void nst_input_inject_mouse(Input::Controllers *controllers, int b, int s, int x, int y) {
// Insert input signal for Zappers
if(controllers == NULL) { return; }
double xaspect;
double yaspect;
if (s) {
// Get X coords
if (conf.video_filter == 1) { // NTSC
xaspect = (double)(Video::Output::WIDTH) / (double)(Video::Output::NTSC_WIDTH / 2);
}
else if (conf.video_tv_aspect) {
xaspect = (double)(Video::Output::WIDTH) / (double)(TV_WIDTH);
}
else { xaspect = 1.0; }
dimensions_t rendersize = nst_video_get_dimensions_render();
dimensions_t screensize = nst_video_get_dimensions_screen();
// Calculate fullscreen X coords
if (conf.video_fullscreen) {
if (conf.video_stretch_aspect) {
xaspect = (double)(conf.video_scale_factor * Video::Output::WIDTH) / (double)(screensize.w);
}
else {
// Remove the same amount of pixels as the black area to the left of the screen
x -= screensize.w / 2.0f - rendersize.w / 2.0f;
xaspect = (double)(conf.video_scale_factor * Video::Output::WIDTH) / (double)(rendersize.w);
}
}
controllers->zapper.x = (int)(x * xaspect) / conf.video_scale_factor;
// Get Y coords
if (conf.video_unmask_overscan) {
controllers->zapper.y = y / conf.video_scale_factor;
}
else {
controllers->zapper.y = (y + OVERSCAN_TOP * conf.video_scale_factor) / conf.video_scale_factor;
}
// Calculate fullscreen Y coords
if (conf.video_fullscreen) {
yaspect = (double)(conf.video_scale_factor * Video::Output::HEIGHT) / (double)(screensize.h);
controllers->zapper.y = (y * yaspect) / conf.video_scale_factor;
}
// Offscreen
if (b != 1) { controllers->zapper.x = ~1U; }
controllers->zapper.fire = true;
}
else { controllers->zapper.fire = false; }
}
void nst_input_turbo_init() {
// Initialize the turbo button states
turbostate.p1a = turbotoggle.p1a = 0;
turbostate.p1b = turbotoggle.p1b = 0;
turbostate.p2a = turbotoggle.p2a = 0;
turbostate.p2b = turbotoggle.p2b = 0;
}
void nst_input_turbo_pulse(Input::Controllers *controllers) {
// Pulse the turbo buttons if they're pressed
if (turbostate.p1a) {
turbotoggle.p1a++;
if (turbotoggle.p1a >= conf.timing_turbopulse) {
turbotoggle.p1a = 0;
controllers->pad[0].buttons &= ~Input::Controllers::Pad::A;
}
else { controllers->pad[0].buttons |= Input::Controllers::Pad::A; }
}
if (turbostate.p1b) {
turbotoggle.p1b++;
if (turbotoggle.p1b >= conf.timing_turbopulse) {
turbotoggle.p1b = 0;
controllers->pad[0].buttons &= ~Input::Controllers::Pad::B;
}
else { controllers->pad[0].buttons |= Input::Controllers::Pad::B; }
}
if (turbostate.p2a) {
turbotoggle.p2a++;
if (turbotoggle.p2a >= conf.timing_turbopulse) {
turbotoggle.p2a = 0;
controllers->pad[1].buttons &= ~Input::Controllers::Pad::A;
}
else { controllers->pad[1].buttons |= Input::Controllers::Pad::A; }
}
if (turbostate.p2b) {
turbotoggle.p2b++;
if (turbotoggle.p2b >= conf.timing_turbopulse) {
turbotoggle.p2b = 0;
controllers->pad[1].buttons &= ~Input::Controllers::Pad::B;
}
else { controllers->pad[1].buttons |= Input::Controllers::Pad::B; }
}
}
int nst_input_zapper_present() {
// Check if a Zapper is presently connected
if (Input(emulator).GetConnectedController(0) == Input::ZAPPER ||
Input(emulator).GetConnectedController(1) == Input::ZAPPER) {
return 1;
}
else { return 0; }
}
void nstsdl_input_joysticks_detect() {
// Initialize any joysticks
fprintf(stderr, "%i joystick(s) found:\n", SDL_NumJoysticks());
int i;
for (i = 0; i < SDL_NumJoysticks(); i++) {
for (int i = 0; i < SDL_NumJoysticks(); i++) {
joystick = SDL_JoystickOpen(i);
printf("%s\n", SDL_JoystickName(joystick));
}
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
nst_input_turbo_init();
}
@ -94,25 +326,25 @@ int nstsdl_input_checksign(int axisvalue) {
void nstsdl_input_match_joystick(Input::Controllers *controllers, SDL_Event event) {
// Match NES buttons to joystick input
int j;
nesinput_t input, reverseinput;
input.nescode = 0x00;
input.player = 0;
input.pressed = 0;
input.turboa = 0;
input.turbob = 0;
// This is for releasing opposing directions
reverseinput.nescode = 0x00;
reverseinput.player = 0;
reverseinput.pressed = 0;
SDL_Event buttons[TOTALBUTTONS] = {
player[0].ju, player[0].jd, player[0].jl, player[0].jr,
player[0].jselect, player[0].jstart, player[0].ja, player[0].jb,
player[0].jta, player[0].jtb,
player[1].ju, player[1].jd, player[1].jl, player[1].jr,
player[1].jselect, player[1].jstart, player[1].ja, player[1].jb,
player[1].jta, player[1].jtb
@ -137,7 +369,7 @@ void nstsdl_input_match_joystick(Input::Controllers *controllers, SDL_Event even
}
}
input.pressed = event.jbutton.state;
// Rewind
if (event.jbutton.button == rw[0].jbutton.button && event.jbutton.which == rw[0].jbutton.which) { nst_set_rewind(0); }
if (event.jbutton.button == rw[1].jbutton.button && event.jbutton.which == rw[1].jbutton.which) { nst_set_rewind(1); }
@ -149,10 +381,10 @@ void nstsdl_input_match_joystick(Input::Controllers *controllers, SDL_Event even
case SDL_JOYHATMOTION:
unsigned char hu, hd, hl, hr;
hu = hd = hl = hr = 0;
// Start a loop to check if input matches
for (j = 0; j < TOTALBUTTONS; j++) {
// Read value of each hat direction on current hat
if (buttons[j].type == event.type
&& buttons[j].jhat.which == event.jhat.which
@ -164,9 +396,9 @@ void nstsdl_input_match_joystick(Input::Controllers *controllers, SDL_Event even
else if (buttons[j].jhat.value == SDL_HAT_DOWN) { hd = nescodes[j]; }
else if (buttons[j].jhat.value == SDL_HAT_LEFT) { hl = nescodes[j]; }
else if (buttons[j].jhat.value == SDL_HAT_RIGHT) { hr = nescodes[j]; }
input.pressed = 1;
// Make sure opposing hat positions are turned off
switch(event.jhat.value) {
case SDL_HAT_UP:
@ -212,7 +444,7 @@ void nstsdl_input_match_joystick(Input::Controllers *controllers, SDL_Event even
// Handle axis input
case SDL_JOYAXISMOTION:
for (j = 0; j < TOTALBUTTONS; j++) {
int nvalue = nstsdl_input_checksign(event.jaxis.value);
if (buttons[j].jaxis.axis == event.jaxis.axis
@ -221,7 +453,7 @@ void nstsdl_input_match_joystick(Input::Controllers *controllers, SDL_Event even
&& buttons[j].jaxis.value == nvalue) {
if (j >= NUMBUTTONS) { input.player = reverseinput.player = 1; }
input.nescode = nescodes[j];
}
@ -229,23 +461,50 @@ void nstsdl_input_match_joystick(Input::Controllers *controllers, SDL_Event even
&& buttons[j].jaxis.which == event.jaxis.which
&& buttons[j].jaxis.type == event.jaxis.type
&& buttons[j].jaxis.value == !nvalue) {
reverseinput.nescode = nescodes[j];
}
if (abs(event.jaxis.value) > DEADZONE) { input.pressed = 1; }
}
break;
default: break;
}
nst_input_inject(controllers, reverseinput);
nst_input_inject(controllers, input);
}
void nstsdl_input_conf_defaults() {
// Set default input config
inputconf.qsave1 = FL_F + 5;
inputconf.qsave2 = FL_F + 6;
inputconf.qload1 = FL_F + 7;
inputconf.qload2 = FL_F + 8;
inputconf.screenshot = FL_F + 9;
inputconf.fdsflip = FL_F + 3;
inputconf.fdsswitch = FL_F + 4;
inputconf.insertcoin1 = FL_F + 1;
inputconf.insertcoin2 = FL_F + 2;
inputconf.reset = FL_F + 12;
inputconf.ffspeed = '`';
inputconf.rwstart = FL_BackSpace;
inputconf.rwstop = '\\';
inputconf.fullscreen = 'f';
player[0].u = FL_Up;
player[0].d = FL_Down;
player[0].l = FL_Left;
player[0].r = FL_Right;
player[0].select = FL_Shift_R;
player[0].start = FL_Enter;
player[0].a = 'z';
player[0].b = 'a';
player[0].ta = 'x';
player[0].tb = 's';
player[0].ju = nstsdl_input_translate_string("j0h01");
player[0].jd = nstsdl_input_translate_string("j0h04");
player[0].jl = nstsdl_input_translate_string("j0h08");
@ -256,12 +515,23 @@ void nstsdl_input_conf_defaults() {
player[0].jb = nstsdl_input_translate_string("j0b0");
player[0].jta = nstsdl_input_translate_string("j0b2");
player[0].jtb = nstsdl_input_translate_string("j0b3");
player[0].rwstart = nstsdl_input_translate_string("j0b4");
player[0].rwstop = nstsdl_input_translate_string("j0b5");
player[0].softreset = nstsdl_input_translate_string("j0b99");
player[0].hardreset = nstsdl_input_translate_string("j0b99");
player[1].u = 'i';
player[1].d = 'j';
player[1].l = 'k';
player[1].r = 'l';
player[1].select = FL_Shift_L;
player[1].start = FL_Control_L;
player[1].a = 'm';
player[1].b = 'n';
player[1].ta = 'b';
player[1].tb = 'v';
player[1].ju = nstsdl_input_translate_string("j1h01");
player[1].jd = nstsdl_input_translate_string("j1h04");
player[1].jl = nstsdl_input_translate_string("j1h08");
@ -274,80 +544,78 @@ void nstsdl_input_conf_defaults() {
player[1].jtb = nstsdl_input_translate_string("j1b3");
}
void nstsdl_input_conf_set(SDL_Event event, int type, int pnum, int counter) {
void fltkui_input_conf_set(int kval, int pnum, int bnum) {
// Set an input item to what was requested by configuration process
if (type == 0) { // Keyboard
switch(counter) {
case 0: player[pnum].u = event.key.keysym.scancode; break;
case 1: player[pnum].d = event.key.keysym.scancode; break;
case 2: player[pnum].l = event.key.keysym.scancode; break;
case 3: player[pnum].r = event.key.keysym.scancode; break;
case 4: player[pnum].select = event.key.keysym.scancode; break;
case 5: player[pnum].start = event.key.keysym.scancode; break;
case 6: player[pnum].a = event.key.keysym.scancode; break;
case 7: player[pnum].b = event.key.keysym.scancode; break;
case 8: player[pnum].ta = event.key.keysym.scancode; break;
case 9: player[pnum].tb = event.key.keysym.scancode; break;
default: break;
}
switch (bnum) {
case 0: player[pnum].u = kval; break;
case 1: player[pnum].d = kval; break;
case 2: player[pnum].l = kval; break;
case 3: player[pnum].r = kval; break;
case 4: player[pnum].select = kval; break;
case 5: player[pnum].start = kval; break;
case 6: player[pnum].a = kval; break;
case 7: player[pnum].b = kval; break;
case 8: player[pnum].ta = kval; break;
case 9: player[pnum].tb = kval; break;
default: break;
}
else if (type == 1) { // Joystick
switch(counter) {
case 0: player[pnum].ju = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
case 1: player[pnum].jd = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
case 2: player[pnum].jl = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
case 3: player[pnum].jr = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
case 4: player[pnum].jselect = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
case 5: player[pnum].jstart = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
case 6: player[pnum].ja = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
case 7: player[pnum].jb = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
case 8: player[pnum].jta = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
case 9: player[pnum].jtb = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
default: break;
}
}
void nstsdl_input_conf_set(SDL_Event event, int pnum, int bnum) {
// Set an input item to what was requested by configuration process
switch (bnum) {
case 0: player[pnum].ju = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
case 1: player[pnum].jd = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
case 2: player[pnum].jl = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
case 3: player[pnum].jr = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
case 4: player[pnum].jselect = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
case 5: player[pnum].jstart = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
case 6: player[pnum].ja = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
case 7: player[pnum].jb = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
case 8: player[pnum].jta = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
case 9: player[pnum].jtb = nstsdl_input_translate_string(nstsdl_input_translate_event(event)); break;
default: break;
}
}
static int nstsdl_input_config_match(void* user, const char* section, const char* name, const char* value) {
// Match values from input config file and populate live config
inputsettings_t* pconfig = (inputsettings_t*)user;
// User Interface
if (MATCH("ui", "qsave1")) { pconfig->qsave1 = strdup(value); }
else if (MATCH("ui", "qsave2")) { pconfig->qsave2 = strdup(value); }
else if (MATCH("ui", "qload1")) { pconfig->qload1 = strdup(value); }
else if (MATCH("ui", "qload2")) { pconfig->qload2 = strdup(value); }
else if (MATCH("ui", "screenshot")) { pconfig->screenshot = strdup(value); }
else if (MATCH("ui", "fdsflip")) { pconfig->fdsflip = strdup(value); }
else if (MATCH("ui", "fdsswitch")) { pconfig->fdsswitch = strdup(value); }
else if (MATCH("ui", "insertcoin1")) { pconfig->insertcoin1 = strdup(value); }
else if (MATCH("ui", "insertcoin2")) { pconfig->insertcoin2 = strdup(value); }
else if (MATCH("ui", "reset")) { pconfig->reset = strdup(value); }
else if (MATCH("ui", "ffspeed")) { pconfig->ffspeed = strdup(value); }
else if (MATCH("ui", "rwstart")) { pconfig->rwstart = strdup(value); }
else if (MATCH("ui", "rwstop")) { pconfig->rwstop = strdup(value); }
else if (MATCH("ui", "fullscreen")) { pconfig->fullscreen = strdup(value); }
else if (MATCH("ui", "filter")) { pconfig->filter = strdup(value); }
else if (MATCH("ui", "scalefactor")) { pconfig->scalefactor = strdup(value); }
if (MATCH("ui", "qsave1")) { pconfig->qsave1 = atoi(value); }
else if (MATCH("ui", "qsave2")) { pconfig->qsave2 = atoi(value); }
else if (MATCH("ui", "qload1")) { pconfig->qload1 = atoi(value); }
else if (MATCH("ui", "qload2")) { pconfig->qload2 = atoi(value); }
else if (MATCH("ui", "screenshot")) { pconfig->screenshot = atoi(value); }
else if (MATCH("ui", "fdsflip")) { pconfig->fdsflip = atoi(value); }
else if (MATCH("ui", "fdsswitch")) { pconfig->fdsswitch = atoi(value); }
else if (MATCH("ui", "insertcoin1")) { pconfig->insertcoin1 = atoi(value); }
else if (MATCH("ui", "insertcoin2")) { pconfig->insertcoin2 = atoi(value); }
else if (MATCH("ui", "reset")) { pconfig->reset = atoi(value); }
else if (MATCH("ui", "ffspeed")) { pconfig->ffspeed = atoi(value); }
else if (MATCH("ui", "rwstart")) { pconfig->rwstart = atoi(value); }
else if (MATCH("ui", "rwstop")) { pconfig->rwstop = atoi(value); }
else if (MATCH("ui", "fullscreen")) { pconfig->fullscreen = atoi(value); }
// Player 1
else if (MATCH("gamepad1", "kb_u")) { pconfig->kb_p1u = strdup(value); }
else if (MATCH("gamepad1", "kb_d")) { pconfig->kb_p1d = strdup(value); }
else if (MATCH("gamepad1", "kb_l")) { pconfig->kb_p1l = strdup(value); }
else if (MATCH("gamepad1", "kb_r")) { pconfig->kb_p1r = strdup(value); }
else if (MATCH("gamepad1", "kb_select")) { pconfig->kb_p1select = strdup(value); }
else if (MATCH("gamepad1", "kb_start")) { pconfig->kb_p1start = strdup(value); }
else if (MATCH("gamepad1", "kb_a")) { pconfig->kb_p1a = strdup(value); }
else if (MATCH("gamepad1", "kb_b")) { pconfig->kb_p1b = strdup(value); }
else if (MATCH("gamepad1", "kb_ta")) { pconfig->kb_p1ta = strdup(value); }
else if (MATCH("gamepad1", "kb_tb")) { pconfig->kb_p1tb = strdup(value); }
else if (MATCH("gamepad1", "kb_u")) { pconfig->kb_p1u = atoi(value); }
else if (MATCH("gamepad1", "kb_d")) { pconfig->kb_p1d = atoi(value); }
else if (MATCH("gamepad1", "kb_l")) { pconfig->kb_p1l = atoi(value); }
else if (MATCH("gamepad1", "kb_r")) { pconfig->kb_p1r = atoi(value); }
else if (MATCH("gamepad1", "kb_select")) { pconfig->kb_p1select = atoi(value); }
else if (MATCH("gamepad1", "kb_start")) { pconfig->kb_p1start = atoi(value); }
else if (MATCH("gamepad1", "kb_a")) { pconfig->kb_p1a = atoi(value); }
else if (MATCH("gamepad1", "kb_b")) { pconfig->kb_p1b = atoi(value); }
else if (MATCH("gamepad1", "kb_ta")) { pconfig->kb_p1ta = atoi(value); }
else if (MATCH("gamepad1", "kb_tb")) { pconfig->kb_p1tb = atoi(value); }
else if (MATCH("gamepad1", "js_u")) { pconfig->js_p1u = strdup(value); }
else if (MATCH("gamepad1", "js_d")) { pconfig->js_p1d = strdup(value); }
else if (MATCH("gamepad1", "js_l")) { pconfig->js_p1l = strdup(value); }
@ -358,7 +626,7 @@ static int nstsdl_input_config_match(void* user, const char* section, const char
else if (MATCH("gamepad1", "js_b")) { pconfig->js_p1b = strdup(value); }
else if (MATCH("gamepad1", "js_ta")) { pconfig->js_p1ta = strdup(value); }
else if (MATCH("gamepad1", "js_tb")) { pconfig->js_p1tb = strdup(value); }
else if (MATCH("gamepad1", "js_rwstart")) { pconfig->js_rwstart = strdup(value); }
else if (MATCH("gamepad1", "js_rwstop")) { pconfig->js_rwstop = strdup(value); }
@ -366,17 +634,17 @@ static int nstsdl_input_config_match(void* user, const char* section, const char
else if (MATCH("gamepad1", "js_hardreset")) { pconfig->js_hardreset = strdup(value); }
// Player 2
else if (MATCH("gamepad2", "kb_u")) { pconfig->kb_p2u = strdup(value); }
else if (MATCH("gamepad2", "kb_d")) { pconfig->kb_p2d = strdup(value); }
else if (MATCH("gamepad2", "kb_l")) { pconfig->kb_p2l = strdup(value); }
else if (MATCH("gamepad2", "kb_r")) { pconfig->kb_p2r = strdup(value); }
else if (MATCH("gamepad2", "kb_select")) { pconfig->kb_p2select = strdup(value); }
else if (MATCH("gamepad2", "kb_start")) { pconfig->kb_p2start = strdup(value); }
else if (MATCH("gamepad2", "kb_a")) { pconfig->kb_p2a = strdup(value); }
else if (MATCH("gamepad2", "kb_b")) { pconfig->kb_p2b = strdup(value); }
else if (MATCH("gamepad2", "kb_ta")) { pconfig->kb_p2ta = strdup(value); }
else if (MATCH("gamepad2", "kb_tb")) { pconfig->kb_p2tb = strdup(value); }
else if (MATCH("gamepad2", "kb_u")) { pconfig->kb_p2u = atoi(value); }
else if (MATCH("gamepad2", "kb_d")) { pconfig->kb_p2d = atoi(value); }
else if (MATCH("gamepad2", "kb_l")) { pconfig->kb_p2l = atoi(value); }
else if (MATCH("gamepad2", "kb_r")) { pconfig->kb_p2r = atoi(value); }
else if (MATCH("gamepad2", "kb_select")) { pconfig->kb_p2select = atoi(value); }
else if (MATCH("gamepad2", "kb_start")) { pconfig->kb_p2start = atoi(value); }
else if (MATCH("gamepad2", "kb_a")) { pconfig->kb_p2a = atoi(value); }
else if (MATCH("gamepad2", "kb_b")) { pconfig->kb_p2b = atoi(value); }
else if (MATCH("gamepad2", "kb_ta")) { pconfig->kb_p2ta = atoi(value); }
else if (MATCH("gamepad2", "kb_tb")) { pconfig->kb_p2tb = atoi(value); }
else if (MATCH("gamepad2", "js_u")) { pconfig->js_p2u = strdup(value); }
else if (MATCH("gamepad2", "js_d")) { pconfig->js_p2d = strdup(value); }
else if (MATCH("gamepad2", "js_l")) { pconfig->js_p2l = strdup(value); }
@ -387,19 +655,30 @@ static int nstsdl_input_config_match(void* user, const char* section, const char
else if (MATCH("gamepad2", "js_b")) { pconfig->js_p2b = strdup(value); }
else if (MATCH("gamepad2", "js_ta")) { pconfig->js_p2ta = strdup(value); }
else if (MATCH("gamepad2", "js_tb")) { pconfig->js_p2tb = strdup(value); }
else { return 0; }
return 1;
return 1;
}
void nstsdl_input_conf_read() {
// Read the input config file
snprintf(inputconfpath, sizeof(inputconfpath), "%sinput.conf", nstpaths.nstconfdir);
if (ini_parse(inputconfpath, nstsdl_input_config_match, &inputconf) < 0) {
fprintf(stderr, "Failed to load input config file %s: Using defaults.\n", inputconfpath);
}
else { // Map the input settings from the config file
player[0].u = inputconf.kb_p1u;
player[0].d = inputconf.kb_p1d;
player[0].l = inputconf.kb_p1l;
player[0].r = inputconf.kb_p1r;
player[0].select = inputconf.kb_p1select;
player[0].start = inputconf.kb_p1start;
player[0].a = inputconf.kb_p1a;
player[0].b = inputconf.kb_p1b;
player[0].ta = inputconf.kb_p1ta;
player[0].tb = inputconf.kb_p1tb;
player[0].ju = nstsdl_input_translate_string(inputconf.js_p1u);
player[0].jd = nstsdl_input_translate_string(inputconf.js_p1d);
player[0].jl = nstsdl_input_translate_string(inputconf.js_p1l);
@ -410,7 +689,7 @@ void nstsdl_input_conf_read() {
player[0].jb = nstsdl_input_translate_string(inputconf.js_p1b);
player[0].jta = nstsdl_input_translate_string(inputconf.js_p1ta);
player[0].jtb = nstsdl_input_translate_string(inputconf.js_p1tb);
if (inputconf.js_rwstart) { player[0].rwstart = nstsdl_input_translate_string(inputconf.js_rwstart); }
if (inputconf.js_rwstop) { player[0].rwstop = nstsdl_input_translate_string(inputconf.js_rwstop); }
@ -418,6 +697,17 @@ void nstsdl_input_conf_read() {
if (inputconf.js_hardreset) { player[0].hardreset = nstsdl_input_translate_string(inputconf.js_hardreset); }
// Player 2
player[1].u = inputconf.kb_p2u;
player[1].d = inputconf.kb_p2d;
player[1].l = inputconf.kb_p2l;
player[1].r = inputconf.kb_p2r;
player[1].select = inputconf.kb_p2select;
player[1].start = inputconf.kb_p2start;
player[1].a = inputconf.kb_p2a;
player[1].b = inputconf.kb_p2b;
player[1].ta = inputconf.kb_p2ta;
player[1].tb = inputconf.kb_p2tb;
player[1].ju = nstsdl_input_translate_string(inputconf.js_p2u);
player[1].jd = nstsdl_input_translate_string(inputconf.js_p2d);
player[1].jl = nstsdl_input_translate_string(inputconf.js_p2l);
@ -435,13 +725,39 @@ void nstsdl_input_conf_write() {
// Write out the input configuration file
FILE *fp = fopen(inputconfpath, "w");
if (fp != NULL) {
fprintf(fp, "; Nestopia UE SDL Input Configuration File\n\n");
fprintf(fp, "; Nestopia UE Input Configuration File\n\n");
fprintf(fp, "; Possible values for joystick input:\n; j[joystick number][a|b|h][button/hat/axis number][1/0 = +/- (axes only)]\n");
fprintf(fp, "; Example: j0b3 = joystick 0, button 3. j1a11 = joystick 1, axis 1 +\n\n");
fprintf(fp, "; Press Ctrl or Shift + [player number] to configure input in-game.\n; Ctrl for Keyboard, Shift for Joystick.\n");
fprintf(fp, "; Example: Shift + 1 for Joystick input for Player 1\n\n");
fprintf(fp, "[ui]\n");
fprintf(fp, "qsave1=%d\n", inputconf.qsave1);
fprintf(fp, "qsave2=%d\n", inputconf.qsave2);
fprintf(fp, "qload1=%d\n", inputconf.qload1);
fprintf(fp, "qload2=%d\n", inputconf.qload2);
fprintf(fp, "screenshot=%d\n", inputconf.screenshot);
fprintf(fp, "fdsflip=%d\n", inputconf.fdsflip);
fprintf(fp, "fdsswitch=%d\n", inputconf.fdsswitch);
fprintf(fp, "insertcoin1=%d\n", inputconf.insertcoin1);
fprintf(fp, "insertcoin2=%d\n", inputconf.insertcoin2);
fprintf(fp, "reset=%d\n", inputconf.reset);
fprintf(fp, "ffspeed=%d\n", inputconf.ffspeed);
fprintf(fp, "rwstart=%d\n", inputconf.rwstart);
fprintf(fp, "rwstop=%d\n", inputconf.rwstop);
fprintf(fp, "fullscreen=%d\n", inputconf.fullscreen);
fprintf(fp, "\n"); // End of Section
fprintf(fp, "[gamepad1]\n");
fprintf(fp, "kb_u=%d\n", player[0].u);
fprintf(fp, "kb_d=%d\n", player[0].d);
fprintf(fp, "kb_l=%d\n", player[0].l);
fprintf(fp, "kb_r=%d\n", player[0].r);
fprintf(fp, "kb_select=%d\n", player[0].select);
fprintf(fp, "kb_start=%d\n", player[0].start);
fprintf(fp, "kb_a=%d\n", player[0].a);
fprintf(fp, "kb_b=%d\n", player[0].b);
fprintf(fp, "kb_ta=%d\n", player[0].ta);
fprintf(fp, "kb_tb=%d\n", player[0].tb);
fprintf(fp, "js_u=%s\n", nstsdl_input_translate_event(player[0].ju));
fprintf(fp, "js_d=%s\n", nstsdl_input_translate_event(player[0].jd));
fprintf(fp, "js_l=%s\n", nstsdl_input_translate_event(player[0].jl));
@ -452,14 +768,25 @@ void nstsdl_input_conf_write() {
fprintf(fp, "js_b=%s\n", nstsdl_input_translate_event(player[0].jb));
fprintf(fp, "js_ta=%s\n", nstsdl_input_translate_event(player[0].jta));
fprintf(fp, "js_tb=%s\n", nstsdl_input_translate_event(player[0].jtb));
fprintf(fp, "js_rwstart=%s\n", nstsdl_input_translate_event(player[0].rwstart));
fprintf(fp, "js_rwstop=%s\n", nstsdl_input_translate_event(player[0].rwstop));
fprintf(fp, "js_softreset=%s\n", nstsdl_input_translate_event(player[0].softreset));
fprintf(fp, "js_hardreset=%s\n", nstsdl_input_translate_event(player[0].hardreset));
fprintf(fp, "\n"); // End of Section
fprintf(fp, "[gamepad2]\n");
fprintf(fp, "kb_u=%d\n", player[1].u);
fprintf(fp, "kb_d=%d\n", player[1].d);
fprintf(fp, "kb_l=%d\n", player[1].l);
fprintf(fp, "kb_r=%d\n", player[1].r);
fprintf(fp, "kb_select=%d\n", player[1].select);
fprintf(fp, "kb_start=%d\n", player[1].start);
fprintf(fp, "kb_a=%d\n", player[1].a);
fprintf(fp, "kb_b=%d\n", player[1].b);
fprintf(fp, "kb_ta=%d\n", player[1].ta);
fprintf(fp, "kb_tb=%d\n", player[1].tb);
fprintf(fp, "js_u=%s\n", nstsdl_input_translate_event(player[1].ju));
fprintf(fp, "js_d=%s\n", nstsdl_input_translate_event(player[1].jd));
fprintf(fp, "js_l=%s\n", nstsdl_input_translate_event(player[1].jl));
@ -470,8 +797,7 @@ void nstsdl_input_conf_write() {
fprintf(fp, "js_b=%s\n", nstsdl_input_translate_event(player[1].jb));
fprintf(fp, "js_ta=%s\n", nstsdl_input_translate_event(player[1].jta));
fprintf(fp, "js_tb=%s\n", nstsdl_input_translate_event(player[1].jtb));
fprintf(fp, "\n"); // End of Section
fclose(fp);
}
}
@ -492,16 +818,16 @@ void nstsdl_input_process(Input::Controllers *controllers, SDL_Event event) {
char* nstsdl_input_translate_event(SDL_Event event) {
// Translate an SDL_Event to an inputcode
static char inputcode[6];
switch(event.type) {
case SDL_JOYAXISMOTION:
sprintf(inputcode, "j%da%d%d", event.jaxis.which, event.jaxis.axis, nstsdl_input_checksign(event.jaxis.value));
break;
case SDL_JOYHATMOTION:
sprintf(inputcode, "j%dh%d%d", event.jhat.which, event.jhat.hat, event.jhat.value);
break;
case SDL_JOYBUTTONUP:
case SDL_JOYBUTTONDOWN:
sprintf(inputcode, "j%db%d", event.jbutton.which, event.jbutton.button);
@ -513,7 +839,7 @@ char* nstsdl_input_translate_event(SDL_Event event) {
SDL_Event nstsdl_input_translate_string(const char *string) {
// Translate an inputcode to an SDL_Event
SDL_Event event;
int type, axis, value;
int which = 0, whichdigits = 0;
@ -530,7 +856,7 @@ SDL_Event nstsdl_input_translate_string(const char *string) {
for (int i = 1; i <= whichdigits; i++) {
which += (string[i] - '0') * (pow (10, (whichdigits - i)));
}
if ((unsigned char)string[whichdigits + 1] == 0x61) { // Axis
axis = string[whichdigits + 2] - '0';
value = string[whichdigits + 3] - '0';
@ -547,7 +873,7 @@ SDL_Event nstsdl_input_translate_string(const char *string) {
event.type = SDL_JOYBUTTONDOWN;
event.jbutton.which = which;
event.jbutton.button = value;
}
else if ((unsigned char)string[whichdigits + 1] == 0x68) { // Hat
axis = string[whichdigits + 2] - '0';
@ -560,7 +886,7 @@ SDL_Event nstsdl_input_translate_string(const char *string) {
else {
fprintf(stderr, "Malformed inputcode: %s\n", string);
}
return event;
}
@ -568,10 +894,10 @@ void nstsdl_input_conf_button(int pnum, int bnum) {
// Configure Inputs for single Joystick Buttons
SDL_Event event, eventbuf;
int axis = 0, axisnoise = 0, confrunning = 1;
if (SDL_NumJoysticks() == 0) { return; }
while (confrunning) {
while (gtk_events_pending()) { gtk_main_iteration(); }
while (SDL_PollEvent(&event)) {
if (event.type == SDL_JOYAXISMOTION) {
if (abs(event.jaxis.value) >= DEADZONE) {
@ -580,21 +906,66 @@ void nstsdl_input_conf_button(int pnum, int bnum) {
axis = event.jaxis.axis;
}
else if (abs(event.jaxis.value) < DEADZONE && axisnoise && event.jaxis.axis == axis) {
nstsdl_input_conf_set(eventbuf, 1, pnum, bnum);
nstsdl_input_conf_set(eventbuf, pnum, bnum);
axisnoise = 0;
confrunning = 0;
}
}
else if (event.type == SDL_JOYHATMOTION) {
if (event.jhat.value != SDL_HAT_CENTERED) {
nstsdl_input_conf_set(event, 1, pnum, bnum);
nstsdl_input_conf_set(event, pnum, bnum);
confrunning = 0;
}
}
else if (event.type == SDL_JOYBUTTONDOWN) {
nstsdl_input_conf_set(event, 1, pnum, bnum);
nstsdl_input_conf_set(event, pnum, bnum);
confrunning = 0;
}
}
SDL_Delay(1);
}
}
void fltkui_input_process_key(int e) {
nesinput_t input;
input.nescode = input.player = input.pressed = input.turboa = input.turbob = 0;
if (e == FL_KEYDOWN) {
input.pressed = 1;
if (Fl::event_key() == '`') nst_timing_set_ffspeed();
else if (Fl::event_key() == inputconf.qsave1) nst_state_quicksave(0);
else if (Fl::event_key() == inputconf.qsave2) nst_state_quicksave(1);
else if (Fl::event_key() == inputconf.qload1) nst_state_quickload(0);
else if (Fl::event_key() == inputconf.qload2) nst_state_quickload(1);
else if (Fl::event_key() == inputconf.screenshot) { video_screenshot(NULL); }
else if (Fl::event_key() == inputconf.fdsflip) { nst_fds_flip(); }
else if (Fl::event_key() == inputconf.fdsswitch) { nst_fds_switch(); }
else if (Fl::event_key() == inputconf.insertcoin1) { cNstPads->vsSystem.insertCoin |= Input::Controllers::VsSystem::COIN_1; }
else if (Fl::event_key() == inputconf.insertcoin2) { cNstPads->vsSystem.insertCoin |= Input::Controllers::VsSystem::COIN_2; }
else if (Fl::event_key() == inputconf.reset) { nst_reset(0); }
else if (Fl::event_key() == inputconf.rwstart) { nst_set_rewind(0); }
else if (Fl::event_key() == inputconf.rwstop) { nst_set_rewind(1); }
else if (Fl::event_key() == ' ') { cNstPads->pad[1].mic = 0x04; }
}
else {
if (Fl::event_key() == inputconf.ffspeed) nst_timing_set_default();
else if (Fl::event_key() == inputconf.fullscreen) fltkui_fullscreen(NULL, NULL);
else if (Fl::event_key() == ' ') { cNstPads->pad[1].mic = 0x00; }
}
for (int i = 0; i < NUMGAMEPADS; i++) {
if (Fl::event_key() == player[i].u) { input.player = i; input.nescode = Input::Controllers::Pad::UP; }
else if (Fl::event_key() == player[i].d) { input.player = i; input.nescode = Input::Controllers::Pad::DOWN; }
else if (Fl::event_key() == player[i].l) { input.player = i; input.nescode = Input::Controllers::Pad::LEFT; }
else if (Fl::event_key() == player[i].r) { input.player = i; input.nescode = Input::Controllers::Pad::RIGHT; }
else if (Fl::event_key() == player[i].select) { input.player = i; input.nescode = Input::Controllers::Pad::SELECT; }
else if (Fl::event_key() == player[i].start) { input.player = i; input.nescode = Input::Controllers::Pad::START; }
else if (Fl::event_key() == player[i].a) { input.player = i; input.nescode = Input::Controllers::Pad::A; }
else if (Fl::event_key() == player[i].b) { input.player = i; input.nescode = Input::Controllers::Pad::B; }
else if (Fl::event_key() == player[i].ta) { input.player = i; input.turboa = 1; input.nescode = Input::Controllers::Pad::A; }
else if (Fl::event_key() == player[i].tb) { input.player = i; input.turbob = 1; input.nescode = Input::Controllers::Pad::B; }
}
nst_input_inject(cNstPads, input);
}

View file

@ -1,23 +1,25 @@
#ifndef _SDLINPUT_H_
#define _SDLINPUT_H_
#ifndef _INPUT_H_
#define _INPUT_H_
#define NUMGAMEPADS 2
#define NUMBUTTONS 10
#define TOTALBUTTONS (NUMGAMEPADS*NUMBUTTONS)
#define DEADZONE (32768/3)
#include "core/api/NstApiInput.hpp"
typedef struct {
SDL_Scancode u;
SDL_Scancode d;
SDL_Scancode l;
SDL_Scancode r;
SDL_Scancode select;
SDL_Scancode start;
SDL_Scancode a;
SDL_Scancode b;
SDL_Scancode ta;
SDL_Scancode tb;
int u;
int d;
int l;
int r;
int select;
int start;
int a;
int b;
int ta;
int tb;
SDL_Event ju;
SDL_Event jd;
SDL_Event jl;
@ -36,41 +38,39 @@ typedef struct {
typedef struct {
// User Interface
char *qsave1;
char *qsave2;
char *qload1;
char *qload2;
char *screenshot;
char *fdsflip;
char *fdsswitch;
char *insertcoin1;
char *insertcoin2;
char *reset;
char *ffspeed;
char *rwstart;
char *rwstop;
char *fullscreen;
char *filter;
char *scalefactor;
int qsave1;
int qsave2;
int qload1;
int qload2;
int screenshot;
int fdsflip;
int fdsswitch;
int insertcoin1;
int insertcoin2;
int reset;
int ffspeed;
int rwstart;
int rwstop;
int fullscreen;
// Player 1
char *kb_p1u;
char *kb_p1d;
char *kb_p1l;
char *kb_p1r;
char *kb_p1select;
char *kb_p1start;
char *kb_p1a;
char *kb_p1b;
char *kb_p1ta;
char *kb_p1tb;
int kb_p1u;
int kb_p1d;
int kb_p1l;
int kb_p1r;
int kb_p1select;
int kb_p1start;
int kb_p1a;
int kb_p1b;
int kb_p1ta;
int kb_p1tb;
char *js_p1u;
char *js_p1d;
char *js_p1l;
@ -81,7 +81,7 @@ typedef struct {
char *js_p1b;
char *js_p1ta;
char *js_p1tb;
char *js_rwstart;
char *js_rwstop;
@ -89,17 +89,17 @@ typedef struct {
char *js_hardreset;
// Player 2
char *kb_p2u;
char *kb_p2d;
char *kb_p2l;
char *kb_p2r;
char *kb_p2select;
char *kb_p2start;
char *kb_p2a;
char *kb_p2b;
char *kb_p2ta;
char *kb_p2tb;
int kb_p2u;
int kb_p2d;
int kb_p2l;
int kb_p2r;
int kb_p2select;
int kb_p2start;
int kb_p2a;
int kb_p2b;
int kb_p2ta;
int kb_p2tb;
char *js_p2u;
char *js_p2d;
char *js_p2l;
@ -112,11 +112,39 @@ typedef struct {
char *js_p2tb;
} inputsettings_t;
using namespace Nes::Api;
typedef struct {
unsigned char player;
unsigned char nescode;
unsigned char pressed;
unsigned char turboa;
unsigned char turbob;
} nesinput_t;
typedef struct {
int p1a;
int p1b;
int p2a;
int p2b;
} turbo_t;
void nst_input_init();
void nst_input_inject(Input::Controllers *controllers, nesinput_t input);
void nst_input_inject_mouse(Input::Controllers *controllers, int b, int s, int x, int y);
void nst_input_turbo_init();
void nst_input_turbo_pulse(Input::Controllers *controllers);
int nst_input_zapper_present();
int input_configure_item(int pnum, int bnum, int type);
void nstsdl_input_conf(int type, int pnum);
void nstsdl_input_conf_button(int pnum, int bnum);
void nstsdl_input_conf_defaults();
void nstsdl_input_conf_set(SDL_Event event, int type, int pnum, int counter);
void nstsdl_input_conf_set(SDL_Event event, int pnum, int bnum);
void nstsdl_input_conf_read();
void nstsdl_input_conf_write();
@ -131,4 +159,6 @@ void nstsdl_input_process(Input::Controllers *controllers, SDL_Event event);
char* nstsdl_input_translate_event(SDL_Event event);
SDL_Event nstsdl_input_translate_string(const char *string);
void fltkui_input_conf_set(int kval, int pnum, int bnum);
void fltkui_input_process_key(int e);
#endif

View file

@ -1,25 +1,25 @@
/*
* Nestopia UE
*
*
* Copyright (C) 2007-2008 R. Belmont
* Copyright (C) 2012-2018 R. Danbrook
* Copyright (C) 2012-2021 R. Danbrook
* Copyright (C) 2018-2018 Phil Smith
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*
*/
#include <iostream>
@ -37,6 +37,8 @@
#include <archive.h>
#include <archive_entry.h>
#include <SDL.h>
// Nst Common
#include "nstcommon.h"
#include "config.h"
@ -73,7 +75,7 @@ bool (*nst_archive_select)(const char*, char*, size_t);
static bool NST_CALLBACK nst_cb_videolock(void* userData, Video::Output& video) {
video.pitch = video_lock_screen(video.pixels);
return true; // true=lock success, false=lock failed (Nestopia will carry on but skip video)
return true;
}
static void NST_CALLBACK nst_cb_videounlock(void* userData, Video::Output& video) {
@ -115,7 +117,7 @@ static void NST_CALLBACK nst_cb_file(void *userData, User::File& file) {
unsigned char *compbuffer;
int compsize, compoffset;
char *filename;
switch (file.GetAction()) {
case User::File::LOAD_ROM:
// Nothing here for now
@ -132,13 +134,13 @@ static void NST_CALLBACK nst_cb_file(void *userData, User::File& file) {
case User::File::LOAD_EEPROM: // used by some Bandai games, can be treated the same as battery files
case User::File::LOAD_TAPE: // for loading Famicom cassette tapes
case User::File::LOAD_TURBOFILE: // for loading turbofile data
{
{
std::ifstream batteryFile(nstpaths.savename, std::ifstream::in|std::ifstream::binary);
if (batteryFile.is_open()) { file.SetContent(batteryFile); }
break;
}
case User::File::SAVE_BATTERY: // save battery data to a file
case User::File::SAVE_EEPROM: // can be treated the same as battery files
case User::File::SAVE_TAPE: // for saving Famicom cassette tapes
@ -160,7 +162,7 @@ static void NST_CALLBACK nst_cb_file(void *userData, User::File& file) {
char fdsname[512];
snprintf(fdsname, sizeof(fdsname), "%s.ups", nstpaths.fdssave);
std::ifstream batteryFile( fdsname, std::ifstream::in|std::ifstream::binary );
// no ups, look for ips
@ -232,12 +234,12 @@ bool nst_archive_select_file(const char *filename, char *reqfile, size_t reqsize
struct archive *a;
struct archive_entry *entry;
int r, numarchives = 0;
a = archive_read_new();
archive_read_support_filter_all(a);
archive_read_support_format_all(a);
r = archive_read_open_filename(a, filename, 10240);
// Test if it's actually an archive
if (r != ARCHIVE_OK) {
r = archive_read_free(a);
@ -257,7 +259,7 @@ bool nst_archive_select_file(const char *filename, char *reqfile, size_t reqsize
}
// Free the archive
r = archive_read_free(a);
// If there are no valid files in the archive, return
if (numarchives == 0) { return false; }
else { return true; }
@ -271,18 +273,18 @@ bool nst_archive_open(const char *filename, char **rom, int *romsize, const char
struct archive_entry *entry;
int r;
int64_t entrysize;
a = archive_read_new();
archive_read_support_filter_all(a);
archive_read_support_format_all(a);
r = archive_read_open_filename(a, filename, 10240);
// Test if it's actually an archive
if (r != ARCHIVE_OK) {
r = archive_read_free(a);
return false;
}
// Scan through the archive for files
while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
char *rombuf;
@ -327,28 +329,28 @@ void nst_db_load() {
// Try to open the database file
snprintf(dbpath, sizeof(dbpath), "%sNstDatabase.xml", nstpaths.nstdir);
nstdb = new std::ifstream(dbpath, std::ifstream::in|std::ifstream::binary);
if (nstdb->is_open()) {
database.Load(*nstdb);
database.Enable(true);
return;
}
// If it fails, try looking in the data directory
snprintf(dbpath, sizeof(dbpath), "%s/NstDatabase.xml", DATADIR);
snprintf(dbpath, sizeof(dbpath), "%s/NstDatabase.xml", ".");
nstdb = new std::ifstream(dbpath, std::ifstream::in|std::ifstream::binary);
if (nstdb->is_open()) {
database.Load(*nstdb);
database.Enable(true);
return;
}
// If that fails, try looking in the working directory
char *pwd = getenv("PWD");
snprintf(dbpath, sizeof(dbpath), "%s/NstDatabase.xml", pwd);
nstdb = new std::ifstream(dbpath, std::ifstream::in|std::ifstream::binary);
if (nstdb->is_open()) {
database.Load(*nstdb);
database.Enable(true);
@ -368,19 +370,19 @@ void nst_db_unload() {
void nst_dipswitch() {
// Print DIP switch information and call handler
DipSwitches dipswitches(emulator);
int numdips = dipswitches.NumDips();
if (numdips > 0) {
for (int i = 0; i < numdips; i++) {
fprintf(stderr, "%d: %s\n", i, dipswitches.GetDipName(i));
int numvalues = dipswitches.NumValues(i);
for (int j = 0; j < numvalues; j++) {
fprintf(stderr, " %d: %s\n", j, dipswitches.GetValueName(i, j));
}
}
char dippath[512];
snprintf(dippath, sizeof(dippath), "%s%s.dip", nstpaths.savedir, nstpaths.gamename);
nst_dip_handle(dippath);
@ -391,7 +393,7 @@ void nst_fds_bios_load() {
// Load the Famicom Disk System BIOS
Nes::Api::Fds fds(emulator);
char biospath[512];
if (fdsbios) { return; }
snprintf(biospath, sizeof(biospath), "%sdisksys.rom", nstpaths.nstdir);
@ -418,7 +420,7 @@ void nst_fds_info() {
const char* disk;
const char* side;
char textbuf[24];
fds.GetCurrentDisk() == 0 ? disk = "1" : disk = "2";
fds.GetCurrentDiskSide() == 0 ? side = "A" : side = "B";
@ -440,9 +442,9 @@ void nst_fds_flip() {
void nst_fds_switch() {
// Switches the FDS disk in multi-disk games
Fds fds(emulator);
int currentdisk = fds.GetCurrentDisk();
// If it's a multi-disk game, eject and insert the other disk
if (fds.GetNumDisks() > 1) {
fds.EjectDisk();
@ -451,11 +453,11 @@ void nst_fds_switch() {
}
}
void nst_movie_save(char *filename) {
void nst_movie_save(const char *filename) {
// Save/Record a movie
Movie movie(emulator);
movierecfile = new std::fstream(filename, std::ifstream::out|std::ifstream::binary);
movierecfile = new std::fstream(filename, std::ifstream::out|std::ifstream::binary);
if (movierecfile->is_open()) {
movie.Record((std::iostream&)*movierecfile, Nes::Api::Movie::CLEAN);
@ -466,11 +468,11 @@ void nst_movie_save(char *filename) {
}
}
void nst_movie_load(char *filename) {
void nst_movie_load(const char *filename) {
// Load and play a movie
Movie movie(emulator);
moviefile = new std::ifstream(filename, std::ifstream::in|std::ifstream::binary);
moviefile = new std::ifstream(filename, std::ifstream::in|std::ifstream::binary);
if (moviefile->is_open()) {
movie.Play(*moviefile);
@ -484,7 +486,7 @@ void nst_movie_load(char *filename) {
void nst_movie_stop() {
// Stop any movie that is playing or recording
Movie movie(emulator);
if (movie.IsPlaying() || movie.IsRecording()) {
movie.Stop();
movierecfile = NULL;
@ -534,18 +536,18 @@ bool nst_playing() { return playing; }
void nst_palette_load(const char *filename) {
// Load a custom palette
FILE *file;
long filesize; // File size in bytes
size_t result;
char custgamepalpath[512];
snprintf(custgamepalpath, sizeof(custgamepalpath), "%s%s%s", nstpaths.nstdir, nstpaths.gamename, ".pal");
// Try the game-specific palette first
file = fopen(custgamepalpath, "rb");
if (!file) { file = fopen(filename, "rb"); }
// Then try the global custom palette
if (!file) {
if (conf.video_palette_mode == 2) {
@ -554,17 +556,17 @@ void nst_palette_load(const char *filename) {
}
return;
}
fseek(file, 0, SEEK_END);
filesize = ftell(file);
fseek(file, 0, SEEK_SET);
if (custompalette) { free(custompalette); }
custompalette = malloc(filesize * sizeof(uint8_t));
custpalsize = filesize * sizeof(uint8_t);
result = fread(custompalette, sizeof(uint8_t), filesize, file);
fclose(file);
}
@ -572,14 +574,14 @@ void nst_palette_save() {
// Save a custom palette
FILE *file;
void *custpalout;
file = fopen(nstpaths.palettepath, "wb");
if (!file) { return; }
custpalout = malloc(custpalsize);
memcpy(custpalout, custompalette, custpalsize);
fwrite(custpalout, custpalsize, sizeof(uint8_t), file);
fclose(file);
free(custpalout);
@ -602,30 +604,30 @@ bool nst_find_patch(char *patchname, unsigned int patchname_length, const char *
// since copying into same string as the argument we don't want any overlap
memmove(filedir, dirname(filedir), sizeof(filedir));
filedir[sizeof(filedir) - 1] = '\0';
if (!conf.misc_soft_patching) { return 0; }
snprintf(patchname, patchname_length, "%s/%s.ips", filedir, nstpaths.gamename);
if ((file = fopen(patchname, "rb")) != NULL) { fclose(file); return 1; }
else {
snprintf(patchname, patchname_length, "%s/%s.ups", filedir, nstpaths.gamename);
if ((file = fopen(patchname, "rb")) != NULL) { fclose(file); return 1; }
}
return 0;
}
void nst_set_callbacks() {
// Set up the callbacks
void *userData = (void*)0xDEADC0DE;
Video::Output::lockCallback.Set(nst_cb_videolock, userData);
Video::Output::unlockCallback.Set(nst_cb_videounlock, userData);
Sound::Output::lockCallback.Set(nst_cb_soundlock, userData);
Sound::Output::unlockCallback.Set(nst_cb_soundunlock, userData);
User::fileIoCallback.Set(nst_cb_file, userData);
User::logCallback.Set(nst_cb_log, userData);
User::eventCallback.Set(nst_cb_event, userData);
@ -640,11 +642,11 @@ void nst_set_dirs() {
else {
snprintf(nstpaths.nstconfdir, sizeof(nstpaths.nstconfdir), "%s/.config/nestopia/", getenv("HOME"));
}
if (mkdir(nstpaths.nstconfdir, 0755) && errno != EEXIST) {
fprintf(stderr, "Failed to create %s: %d\n", nstpaths.nstconfdir, errno);
}
// create data directory if it doesn't exist
if (getenv("XDG_DATA_HOME")) {
snprintf(nstpaths.nstdir, sizeof(nstpaths.nstdir), "%s/nestopia/", getenv("XDG_DATA_HOME"));
@ -652,15 +654,15 @@ void nst_set_dirs() {
else {
snprintf(nstpaths.nstdir, sizeof(nstpaths.nstdir), "%s/.local/share/nestopia/", getenv("HOME"));
}
if (mkdir(nstpaths.nstdir, 0755) && errno != EEXIST) {
fprintf(stderr, "Failed to create %s: %d\n", nstpaths.nstdir, errno);
}
// create save and state directories if they don't exist
char dirstr[256];
snprintf(dirstr, sizeof(dirstr), "%ssave", nstpaths.nstdir);
if (mkdir(dirstr, 0755) && errno != EEXIST) {
fprintf(stderr, "Failed to create %s: %d\n", dirstr, errno);
}
@ -669,22 +671,22 @@ void nst_set_dirs() {
if (mkdir(dirstr, 0755) && errno != EEXIST) {
fprintf(stderr, "Failed to create %s: %d\n", dirstr, errno);
}
// create cheats directory if it doesn't exist
snprintf(dirstr, sizeof(dirstr), "%scheats", nstpaths.nstdir);
if (mkdir(dirstr, 0755) && errno != EEXIST) {
fprintf(stderr, "Failed to create %s: %d\n", dirstr, errno);
}
// create screenshots directory if it doesn't exist
snprintf(dirstr, sizeof(dirstr), "%sscreenshots", nstpaths.nstdir);
if (mkdir(dirstr, 0755) && errno != EEXIST) {
fprintf(stderr, "Failed to create %s: %d\n", dirstr, errno);
}
// Construct the custom palette path
snprintf(nstpaths.palettepath, sizeof(nstpaths.palettepath), "%s%s", nstpaths.nstdir, "custom.pal");
// Construct samples directory if it doesn't exist
snprintf(dirstr, sizeof(dirstr), "%ssamples", nstpaths.nstdir);
if (mkdir(dirstr, 0755) && errno != EEXIST) {
@ -693,13 +695,13 @@ void nst_set_dirs() {
}
void nst_set_paths(const char *filename) {
// Set up the save directory
snprintf(nstpaths.savedir, sizeof(nstpaths.savedir), "%ssave/", nstpaths.nstdir);
// Copy the full file path to the savename variable
snprintf(nstpaths.savename, sizeof(nstpaths.savename), "%s", filename);
// strip the . and extention off the filename for saving
for (int i = strlen(nstpaths.savename)-1; i > 0; i--) {
if (nstpaths.savename[i] == '.') {
@ -707,22 +709,22 @@ void nst_set_paths(const char *filename) {
break;
}
}
// Set up the sample directory
snprintf(nstpaths.sampdir, sizeof(nstpaths.sampdir), "%ssamples/", nstpaths.nstdir);
// Get the name of the game minus file path and extension
snprintf(nstpaths.gamename, sizeof(nstpaths.gamename), "%s", basename(nstpaths.savename));
// Construct save path
snprintf(nstpaths.savename, sizeof(nstpaths.savename), "%s%s%s", nstpaths.savedir, nstpaths.gamename, ".sav");
// Construct path for FDS save patches
snprintf(nstpaths.fdssave, sizeof(nstpaths.fdssave), "%s%s", nstpaths.savedir, nstpaths.gamename);
// Construct the save state path
snprintf(nstpaths.statepath, sizeof(nstpaths.statepath), "%sstate/%s", nstpaths.nstdir, nstpaths.gamename);
// Construct the cheat path
snprintf(nstpaths.cheatpath, sizeof(nstpaths.cheatpath), "%scheats/%s.xml", nstpaths.nstdir, nstpaths.gamename);
}
@ -731,7 +733,7 @@ void nst_set_region() {
// Set the region
Machine machine(emulator);
Cartridge::Database database(emulator);
/*if (database.IsLoaded()) {
std::ifstream dbfile(filename, std::ios::in|std::ios::binary);
Cartridge::Profile profile;
@ -739,7 +741,7 @@ void nst_set_region() {
dbentry = database.FindEntry(profile.hash, nst_default_system());
printf("Mapper: %d\n", dbentry.GetMapper());
}*/
switch (conf.misc_default_system) {
case 0: machine.SetMode(machine.GetDesiredMode()); break; // Auto
case 1: machine.SetMode(Machine::NTSC); break; // NTSC
@ -758,23 +760,23 @@ void nst_set_rewind(int direction) {
}
}
void nst_state_save(char *filename) {
void nst_state_save(const char *filename) {
// Save a state by filename
Machine machine(emulator);
std::ofstream statefile(filename, std::ifstream::out|std::ifstream::binary);
if (statefile.is_open()) { machine.SaveState(statefile, Nes::Api::Machine::NO_COMPRESSION); }
fprintf(stderr, "State Saved: %s\n", filename);
nst_video_print("State Saved", 8, 212, 2, true);
}
void nst_state_load(char *filename) {
void nst_state_load(const char *filename) {
// Load a state by filename
Machine machine(emulator);
std::ifstream statefile(filename, std::ifstream::in|std::ifstream::binary);
if (statefile.is_open()) { machine.LoadState(statefile); }
fprintf(stderr, "State Loaded: %s\n", filename);
nst_video_print("State Loaded", 8, 212, 2, true);
@ -782,6 +784,7 @@ void nst_state_load(char *filename) {
void nst_state_quicksave(int slot) {
// Quick Save State
if (!loaded) { return; }
char slotpath[520];
snprintf(slotpath, sizeof(slotpath), "%s_%d.nst", nstpaths.statepath, slot);
nst_state_save(slotpath);
@ -790,16 +793,17 @@ void nst_state_quicksave(int slot) {
void nst_state_quickload(int slot) {
// Quick Load State
if (!loaded) { return; }
char slotpath[520];
snprintf(slotpath, sizeof(slotpath), "%s_%d.nst", nstpaths.statepath, slot);
struct stat qloadstat;
if (stat(slotpath, &qloadstat) == -1) {
fprintf(stderr, "No State to Load\n");
nst_video_print("No State to Load", 8, 212, 2, true);
return;
}
nst_state_load(slotpath);
}
@ -827,7 +831,7 @@ void nst_reset(bool hardreset) {
Fds fds(emulator);
machine.SetRamPowerState(conf.misc_power_state);
machine.Reset(hardreset);
// Set the FDS disk to defaults
fds.EjectDisk();
fds.InsertDisk(0, 0);
@ -838,11 +842,11 @@ void nst_emuloop() {
if (NES_SUCCEEDED(Rewinder(emulator).Enable(true))) {
Rewinder(emulator).EnableSound(true);
}
if (playing) {
// Pulse the turbo buttons
nst_input_turbo_pulse(cNstPads);
// Execute frames
for (int i = 0; i < nst_timing_runframes(); i++) {
emulator.Execute(cNstVideo, cNstSound, cNstPads);
@ -853,7 +857,7 @@ void nst_emuloop() {
void nst_unload() {
// Remove the cartridge and shut down the NES
Machine machine(emulator);
// Power down the NES
machine.Power(false);
@ -867,14 +871,14 @@ void nst_pause() {
audio_pause();
audio_deinit();
}
playing = false;
}
void nst_play() {
// Play the game
if (playing) { return; }
video_init();
audio_init();
nst_input_init();
@ -884,16 +888,16 @@ void nst_play() {
cNstVideo = new Video::Output;
cNstSound = new Sound::Output;
cNstPads = new Input::Controllers;
audio_set_params(cNstSound);
audio_unpause();
if (nst_nsf()) {
Nsf nsf(emulator);
nsf.PlaySong();
video_disp_nsf();
}
playing = true;
}
@ -906,33 +910,33 @@ int nst_load(const char *filename) {
char *rom;
int romsize;
char patchname[512];
// Pause play before pulling out a cartridge
if (playing) { nst_pause(); }
// Pull out any inserted cartridges
if (loaded) { nst_unload(); }
nst_video_print_time("", false);
// Check if the file is an archive and select the file within
char reqfile[256]; // Requested file inside the archive
if (nst_archive_select(filename, reqfile, sizeof(reqfile))) {
// Extract the contents
nst_archive_open(filename, &rom, &romsize, reqfile);
// Convert the malloc'd char* to an istream
std::string rombuf(rom, romsize);
std::istringstream file(rombuf);
free(rom);
result = machine.Load(file, nst_default_system());
}
else { // Otherwise just load the file
std::ifstream file(filename, std::ios::in|std::ios::binary);
// Set the file paths
nst_set_paths(filename);
if (nst_find_patch(patchname, sizeof(patchname), filename)) { // Load with a patch if there is one
std::ifstream pfile(patchname, std::ios::in|std::ios::binary);
Machine::Patch patch(pfile, false);
@ -940,7 +944,7 @@ int nst_load(const char *filename) {
}
else { result = machine.Load(file, nst_default_system()); }
}
if (NES_FAILED(result)) {
char errorstring[32];
switch (result) {
@ -968,39 +972,39 @@ int nst_load(const char *filename) {
snprintf(errorstring, sizeof(errorstring), "Error: %d", result);
break;
}
fprintf(stderr, "%s\n", errorstring);
return 0;
}
// Deal with any DIP Switches
nst_dipswitch();
// Set the region
nst_set_region();
if (machine.Is(Machine::DISK)) {
Fds fds(emulator);
fds.InsertDisk(0, 0);
nst_fds_info();
}
// Check if this is an NSF
if (nst_nsf()) { nsf.StopSong(); }
// Check if sound distortion should be enabled
sound.SetGenie(conf.misc_genie_distortion);
// Load the custom palette
nst_palette_load(nstpaths.palettepath);
// Set the RAM's power state
machine.SetRamPowerState(conf.misc_power_state);
// Power on
machine.Power(true);
loaded = 1;
return loaded;
}

View file

@ -53,8 +53,8 @@ void nst_fds_flip();
void nst_fds_switch();
// Movies
void nst_movie_save(char *filename);
void nst_movie_load(char *filename);
void nst_movie_save(const char *filename);
void nst_movie_load(const char *filename);
void nst_movie_stop();
// NSF
@ -87,8 +87,8 @@ void nst_set_region();
void nst_set_rewind(int direction);
// States
void nst_state_save(char *filename);
void nst_state_load(char *filename);
void nst_state_save(const char *filename);
void nst_state_load(const char *filename);
void nst_state_quicksave(int isvst);
void nst_state_quickload(int isvst);

View file

@ -1,23 +1,23 @@
/*
* Nestopia UE
*
*
* Copyright (C) 2012-2018 R. Danbrook
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*
*/
#include <stdio.h>
@ -25,10 +25,8 @@
#include <stdint.h>
#include <string.h>
#ifndef _MINGW
#include <archive.h>
#include <archive_entry.h>
#endif
#include "nstcommon.h"
#include "samples.h"
@ -42,22 +40,22 @@ int nst_sample_load_file(const char* filepath) {
FILE *file;
long filesize; // File size in bytes
size_t result;
file = fopen(filepath, "rb");
if (!file) { return 0; }
fseek(file, 0, SEEK_END);
filesize = ftell(file);
fseek(file, 0, SEEK_SET);
wavfile = (uint8_t*)malloc(filesize * sizeof(uint8_t));
if (wavfile == NULL) { return 0; }
result = fread(wavfile, sizeof(uint8_t), filesize, file);
if (result != filesize) { return 0; }
fclose(file);
return 1;
}
@ -68,18 +66,18 @@ int nst_sample_load_archive(const char* filename, const char* reqfile) {
struct archive_entry *entry;
int r;
int64_t entrysize;
a = archive_read_new();
archive_read_support_filter_all(a);
archive_read_support_format_all(a);
r = archive_read_open_filename(a, filename, 10240);
// Test if it's actually an archive
if (r != ARCHIVE_OK) {
r = archive_read_free(a);
return 0;
}
// Scan through the archive for files
while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
const char *currentfile = archive_entry_pathname(entry);
@ -108,7 +106,7 @@ int nst_sample_unload_file() {
void nst_sample_setcontent(User::File& file) {
// Parse the .WAV header and load the sample into the emulator
// Check to see if it has a valid header
uint8_t fmt[4] = { 0x66, 0x6d, 0x74, 0x20};
uint8_t subchunk2id[4] = { 0x64, 0x61, 0x74, 0x61};
@ -116,7 +114,7 @@ void nst_sample_setcontent(User::File& file) {
if (memcmp(&wavfile[0x08], "WAVE", 4) != 0) { return; }
if (memcmp(&wavfile[0x0c], &fmt, 4) != 0) { return; }
if (memcmp(&wavfile[0x24], &subchunk2id, 4) != 0) { return; }
// Load the sample into the emulator
uint8_t *dataptr = &wavfile[0x2c];
uint32_t datasize = wavfile[0x2b] << 24 | wavfile[0x2a] << 16 | wavfile[0x29] << 8 | wavfile[0x28];
@ -131,10 +129,10 @@ void nst_sample_load_samples(User::File& file, const char* sampgame) {
// Load samples for the specific game
char reqfile[16];
char samppath[576];
// Requested sample .wav file
snprintf(reqfile, sizeof(reqfile), "%02d.wav", file.GetId());
// Check if there's a MAME-style zip archive
snprintf(samppath, sizeof(samppath), "%s%s.zip", nstpaths.sampdir, sampgame);
if (nst_sample_load_archive(samppath, reqfile)) {

View file

@ -1,28 +1,29 @@
/*
* Nestopia UE
*
*
* Copyright (C) 2007-2008 R. Belmont
* Copyright (C) 2012-2020 R. Danbrook
*
* Copyright (C) 2012-2021 R. Danbrook
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include "core/api/NstApiEmulator.hpp"
@ -30,6 +31,8 @@
#include "core/api/NstApiVideo.hpp"
#include "core/api/NstApiNsf.hpp"
#include <FL/gl.h>
#include "nstcommon.h"
#include "video.h"
#include "config.h"
@ -53,114 +56,41 @@ extern void *custompalette;
extern nstpaths_t nstpaths;
extern Emulator emulator;
// Shader sources
const GLchar* vshader_src =
"#version 150 core\n"
"in vec2 position;"
"in vec2 texcoord;"
"out vec2 outcoord;"
"void main() {"
" outcoord = texcoord;"
" gl_Position = vec4(position, 0.0, 1.0);"
"}";
const GLchar* fshader_src =
"#version 150 core\n"
"in vec2 outcoord;"
"out vec4 fragcolor;"
"uniform sampler2D nestex;"
"void main() {"
" fragcolor = texture(nestex, outcoord);"
"}";
GLuint vao;
GLuint vbo;
GLuint vshader;
GLuint fshader;
GLuint gl_shader_prog = 0;
GLuint gl_texture_id = 0;
void nst_ogl_init() {
// Initialize OpenGL
float vertices[] = {
-1.0f, -1.0f, // Vertex 1 (X, Y)
-1.0f, 1.0f, // Vertex 2 (X, Y)
1.0f, -1.0f, // Vertex 3 (X, Y)
1.0f, 1.0f, // Vertex 4 (X, Y)
0.0, 1.0, // Texture 1 (X, Y)
0.0, 0.0, // Texture 2 (X, Y)
1.0, 1.0, // Texture 3 (X, Y)
1.0, 0.0 // Texture 4 (X, Y)
};
GLint status;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
vshader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vshader, 1, &vshader_src, NULL);
glCompileShader(vshader);
glGetShaderiv(vshader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) { fprintf(stderr, "Failed to compile vertex shader\n"); }
fshader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fshader, 1, &fshader_src, NULL);
glCompileShader(fshader);
glGetShaderiv(fshader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) { fprintf(stderr, "Failed to compile fragment shader\n"); }
GLuint gl_shader_prog = glCreateProgram();
glAttachShader(gl_shader_prog, vshader);
glAttachShader(gl_shader_prog, fshader);
glLinkProgram(gl_shader_prog);
glValidateProgram(gl_shader_prog);
glGetProgramiv(gl_shader_prog, GL_LINK_STATUS, &status);
if (status == GL_FALSE) { fprintf(stderr, "Failed to link shader program\n"); }
glUseProgram(gl_shader_prog);
GLint posAttrib = glGetAttribLocation(gl_shader_prog, "position");
glEnableVertexAttribArray(posAttrib);
glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
GLint texAttrib = glGetAttribLocation(gl_shader_prog, "texcoord");
glEnableVertexAttribArray(texAttrib);
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 0, (void*)(8 * sizeof(GLfloat)));
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &gl_texture_id);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, gl_texture_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, conf.video_linear_filter ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
conf.video_fullscreen ?
conf.video_fullscreen ?
glViewport(screensize.w / 2.0f - rendersize.w / 2.0f, 0, rendersize.w, rendersize.h) :
glViewport(0, 0, rendersize.w, rendersize.h);
glUniform1i(glGetUniformLocation(gl_shader_prog, "nestex"), 0);
glDisable(GL_DEPTH_TEST);
glDisable(GL_ALPHA_TEST);
glDisable(GL_BLEND);
glDisable(GL_LIGHTING);
glDisable(GL_TEXTURE_3D_EXT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0, rendersize.w * conf.video_scale_factor, rendersize.h * conf.video_scale_factor, 0.0, -1.0, 1.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void nst_ogl_deinit() {
// Deinitialize OpenGL
if (gl_texture_id) { glDeleteTextures(1, &gl_texture_id); }
if (gl_shader_prog) { glDeleteProgram(gl_shader_prog); }
if (vshader) { glDeleteShader(vshader); }
if (fshader) { glDeleteShader(fshader); }
if (vao) { glDeleteVertexArrays(1, &vao); }
if (vbo) { glDeleteBuffers(1, &vbo); }
if (gl_texture_id) {
glDeleteTextures(1, &gl_texture_id);
}
}
void nst_ogl_render() {
@ -174,36 +104,42 @@ void nst_ogl_render() {
GL_BGRA,
GL_UNSIGNED_BYTE,
videobuf + overscan_offset);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBegin(GL_QUADS);
glTexCoord2f(1.0f, 1.0f);
glVertex2f(rendersize.w * conf.video_scale_factor, rendersize.h * conf.video_scale_factor);
glTexCoord2f(1.0f, 0.0f);
glVertex2f(rendersize.w * conf.video_scale_factor, 0.0);
glTexCoord2f(0.0f, 0.0f);
glVertex2f(0.0, 0.0);
glTexCoord2f(0.0f, 1.0f);
glVertex2f(0, rendersize.h * conf.video_scale_factor);
glEnd();
}
void nst_video_refresh() {
// Refresh the video settings
nst_ogl_deinit();
nst_ogl_init();
}
void video_init() {
// Initialize video
nst_ogl_deinit();
video_set_dimensions();
video_set_filter();
nst_ogl_init();
if (nst_nsf()) { video_clear_buffer(); video_disp_nsf(); }
}
void video_toggle_filter() {
conf.video_filter++;
if (conf.video_filter > 5) { conf.video_filter = 0; }
video_init();
nst_video_refresh();
nst_ogl_init();
if (nst_nsf()) { video_clear_buffer(); video_disp_nsf(); }
}
void video_toggle_filterupdate() {
@ -212,20 +148,13 @@ void video_toggle_filterupdate() {
video.ClearFilterUpdateFlag();
}
void video_toggle_scalefactor() {
// Toggle video scale factor
conf.video_scale_factor++;
if (conf.video_scale_factor > 8) { conf.video_scale_factor = 1; }
//video_init();
}
void video_set_filter() {
// Set the filter
Video video(emulator);
int scalefactor = conf.video_scale_factor;
if (conf.video_scale_factor > 4) { scalefactor = 4; }
if ((conf.video_scale_factor > 3) && (conf.video_filter == 5)) { scalefactor = 3; }
switch(conf.video_filter) {
case 0: // None
filter = Video::RenderState::FILTER_NONE;
@ -268,7 +197,7 @@ void video_set_filter() {
break;
}
break;
case 4: // 2xSaI
filter = Video::RenderState::FILTER_2XSAI;
break;
@ -288,52 +217,52 @@ void video_set_filter() {
break;
break;
}
// Set the sprite limit: false = enable sprite limit, true = disable sprite limit
video.EnableUnlimSprites(conf.video_unlimited_sprites ? true : false);
// Set Palette options
switch (conf.video_palette_mode) {
case 0: // YUV
video.GetPalette().SetMode(Video::Palette::MODE_YUV);
break;
case 1: // RGB
video.GetPalette().SetMode(Video::Palette::MODE_RGB);
break;
case 2: // Custom
video.GetPalette().SetMode(Video::Palette::MODE_CUSTOM);
video.GetPalette().SetCustom((const unsigned char (*)[3])custompalette, Video::Palette::EXT_PALETTE);
break;
default: break;
}
// Set YUV Decoder/Picture options
if (video.GetPalette().GetMode() == Video::Palette::MODE_YUV) {
switch (conf.video_decoder) {
case 0: // Consumer
video.SetDecoder(Video::DECODER_CONSUMER);
break;
case 1: // Canonical
video.SetDecoder(Video::DECODER_CANONICAL);
break;
case 2: // Alternative (Canonical with yellow boost)
video.SetDecoder(Video::DECODER_ALTERNATIVE);
break;
default: break;
}
}
video.SetBrightness(conf.video_brightness);
video.SetSaturation(conf.video_saturation);
video.SetContrast(conf.video_contrast);
video.SetHue(conf.video_hue);
// Set NTSC options
if (conf.video_filter == 1) {
switch (conf.video_ntsc_mode) {
@ -345,7 +274,7 @@ void video_set_filter() {
video.SetColorArtifacts(Video::DEFAULT_COLOR_ARTIFACTS_COMP);
video.SetColorFringing(Video::DEFAULT_COLOR_FRINGING_COMP);
break;
case 1: // S-Video
video.SetSaturation(Video::DEFAULT_SATURATION_SVIDEO);
video.SetSharpness(Video::DEFAULT_SHARPNESS_SVIDEO);
@ -354,7 +283,7 @@ void video_set_filter() {
video.SetColorArtifacts(Video::DEFAULT_COLOR_ARTIFACTS_SVIDEO);
video.SetColorFringing(Video::DEFAULT_COLOR_FRINGING_SVIDEO);
break;
case 2: // RGB
video.SetSaturation(Video::DEFAULT_SATURATION_RGB);
video.SetSharpness(Video::DEFAULT_SHARPNESS_RGB);
@ -363,7 +292,7 @@ void video_set_filter() {
video.SetColorArtifacts(Video::DEFAULT_COLOR_ARTIFACTS_RGB);
video.SetColorFringing(Video::DEFAULT_COLOR_FRINGING_RGB);
break;
case 3: // Monochrome
video.SetSaturation(Video::DEFAULT_SATURATION_MONO);
video.SetSharpness(Video::DEFAULT_SHARPNESS_MONO);
@ -372,7 +301,7 @@ void video_set_filter() {
video.SetColorArtifacts(Video::DEFAULT_COLOR_ARTIFACTS_MONO);
video.SetColorFringing(Video::DEFAULT_COLOR_FRINGING_MONO);
break;
case 4: // Custom
video.SetSaturation(conf.video_saturation);
video.SetSharpness(conf.video_ntsc_sharpness);
@ -381,23 +310,23 @@ void video_set_filter() {
video.SetColorArtifacts(conf.video_ntsc_artifacts);
video.SetColorFringing(conf.video_ntsc_fringing);
break;
default: break;
}
}
// Set xBR options
if (conf.video_filter == 2) {
video.SetCornerRounding(conf.video_xbr_corner_rounding);
video.SetBlend(conf.video_xbr_pixel_blending);
}
// Set up the render state parameters
renderstate.filter = filter;
renderstate.width = basesize.w;
renderstate.height = basesize.h;
renderstate.bits.count = 32;
int e = 1; // Check Endianness
if ((int)*((unsigned char *)&e) == 1) { // Little Endian
renderstate.bits.mask.r = 0x00ff0000;
@ -409,7 +338,7 @@ void video_set_filter() {
renderstate.bits.mask.g = 0xff000000;
renderstate.bits.mask.b = 0x00ff0000;
}
if (NES_FAILED(video.SetRenderState(renderstate))) {
fprintf(stderr, "Nestopia core rejected render state\n");
exit(1);
@ -437,7 +366,7 @@ void video_set_dimensions() {
if ((conf.video_scale_factor > 3) && (conf.video_filter == 5)) { scalefactor = 3; }
int wscalefactor = conf.video_scale_factor;
int tvwidth = nst_pal() ? PAL_TV_WIDTH : TV_WIDTH;
switch(conf.video_filter) {
case 0: // None
basesize.w = Video::Output::WIDTH;
@ -467,7 +396,7 @@ void video_set_dimensions() {
overscan_offset = basesize.w * OVERSCAN_TOP * scalefactor;
overscan_height = basesize.h - (OVERSCAN_TOP + OVERSCAN_BOTTOM) * scalefactor;
break;
case 4: // 2xSaI
basesize.w = Video::Output::WIDTH * 2;
basesize.h = Video::Output::HEIGHT * 2;
@ -482,9 +411,13 @@ void video_set_dimensions() {
rendersize.h -= (OVERSCAN_TOP + OVERSCAN_BOTTOM) * scalefactor;
}
else { overscan_offset = 0; overscan_height = basesize.h; }
// Calculate the aspect from the height because it's smaller
float aspect = (float)screensize.h / (float)rendersize.h;
if (conf.video_fullscreen) {
float aspect = (float)screensize.h / (float)rendersize.h;
rendersize.w *= aspect;
rendersize.h *= aspect;
}
}
long video_lock_screen(void*& ptr) {
@ -493,15 +426,14 @@ long video_lock_screen(void*& ptr) {
}
void video_unlock_screen(void*) {
int xscale = renderstate.width / Video::Output::WIDTH;;
int yscale = renderstate.height / Video::Output::HEIGHT;
if (osdtext.drawtext) {
nst_video_text_draw(osdtext.textbuf, osdtext.xpos * xscale, osdtext.ypos * yscale, osdtext.bg);
osdtext.drawtext--;
}
if (osdtext.drawtime) {
nst_video_text_draw(osdtext.timebuf, 208 * xscale, 218 * yscale, false);
}
@ -513,7 +445,7 @@ void video_screenshot_flip(unsigned char *pixels, int width, int height, int byt
unsigned char *row = (unsigned char*)malloc(rowsize);
unsigned char *low = pixels;
unsigned char *high = &pixels[(height - 1) * rowsize];
for (; low < high; low += rowsize, high -= rowsize) {
memcpy(row, low, rowsize);
memcpy(low, high, rowsize);
@ -526,16 +458,16 @@ void video_screenshot(const char* filename) {
// Take a screenshot in .png format
unsigned char *pixels;
pixels = (unsigned char*)malloc(sizeof(unsigned char) * rendersize.w * rendersize.h * 4);
// Read the pixels and flip them vertically
glReadPixels(0, 0, rendersize.w, rendersize.h, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
video_screenshot_flip(pixels, rendersize.w, rendersize.h, 4);
if (filename == NULL) {
// Set the filename
char sshotpath[512];
snprintf(sshotpath, sizeof(sshotpath), "%sscreenshots/%s-%ld-%d.png", nstpaths.nstdir, nstpaths.gamename, time(NULL), rand() % 899 + 100);
// Save the file
lodepng_encode32_file(sshotpath, (const unsigned char*)pixels, rendersize.w, rendersize.h);
fprintf(stderr, "Screenshot: %s\n", sshotpath);
@ -543,7 +475,7 @@ void video_screenshot(const char* filename) {
else {
lodepng_encode32_file(filename, (const unsigned char*)pixels, rendersize.w, rendersize.h);
}
free(pixels);
}
@ -555,50 +487,18 @@ void video_clear_buffer() {
void video_disp_nsf() {
// Display NSF text
Nsf nsf(emulator);
int xscale = renderstate.width / Video::Output::WIDTH;;
int yscale = renderstate.height / Video::Output::HEIGHT;;
nst_video_text_draw(nsf.GetName(), 4 * xscale, 16 * yscale, false);
nst_video_text_draw(nsf.GetArtist(), 4 * xscale, 28 * yscale, false);
nst_video_text_draw(nsf.GetCopyright(), 4 * xscale, 40 * yscale, false);
char currentsong[10];
snprintf(currentsong, sizeof(currentsong), "%d / %d", nsf.GetCurrentSong() +1, nsf.GetNumSongs());
nst_video_text_draw(currentsong, 4 * xscale, 52 * yscale, false);
nst_ogl_render();
}
void nst_video_disp_inputconf(int type, int pnum, int bnum) {
int xscale = renderstate.width / Video::Output::WIDTH;;
int yscale = renderstate.height / Video::Output::HEIGHT;;
char textbuf[32];
char buttontext[8];
if (type == 0) { snprintf(textbuf, sizeof(textbuf), "Player %d Keyboard Configuration", pnum + 1); }
else { snprintf(textbuf, sizeof(textbuf), "Player %d Joystick Configuration", pnum + 1); }
switch (bnum) {
case 0: snprintf(buttontext, sizeof(buttontext), "Up"); break;
case 1: snprintf(buttontext, sizeof(buttontext), "Down"); break;
case 2: snprintf(buttontext, sizeof(buttontext), "Left"); break;
case 3: snprintf(buttontext, sizeof(buttontext), "Right"); break;
case 4: snprintf(buttontext, sizeof(buttontext), "Select"); break;
case 5: snprintf(buttontext, sizeof(buttontext), "Start"); break;
case 6: snprintf(buttontext, sizeof(buttontext), "A"); break;
case 7: snprintf(buttontext, sizeof(buttontext), "B"); break;
case 8: snprintf(buttontext, sizeof(buttontext), "Turbo A"); break;
case 9: snprintf(buttontext, sizeof(buttontext), "Turbo B"); break;
}
video_clear_buffer();
nst_video_text_draw(textbuf, 4 * xscale, 64 * yscale, false);
nst_video_text_draw(buttontext, 112 * xscale, 128 * yscale, false);
nst_ogl_render();
}
@ -621,13 +521,13 @@ void nst_video_text_draw(const char *text, int xpos, int ypos, bool bg) {
uint32_t b = 0x00000000; // Black
uint32_t g = 0x00358570; // Nestopia UE Green
uint32_t d = 0x00255f65; // Nestopia UE Dark Green
int numchars = strlen(text);
int letterypos;
int letterxpos;
int letternum = 0;
if (bg) { // Draw background borders
for (int i = 0; i < numchars * 8; i++) { // Rows above and below
videobuf[(xpos + i) + ((ypos - 1) * renderstate.width)] = g;
@ -638,7 +538,7 @@ void nst_video_text_draw(const char *text, int xpos, int ypos, bool bg) {
videobuf[(xpos + (numchars * 8)) + ((ypos + i) * renderstate.width)] = g;
}
}
for (int tpos = 0; tpos < (8 * numchars); tpos+=8) {
nst_video_text_match(text, &letterxpos, &letterypos, letternum);
for (int row = 0; row < 8; row++) { // Draw Rows
@ -647,11 +547,11 @@ void nst_video_text_draw(const char *text, int xpos, int ypos, bool bg) {
case '.':
videobuf[xpos + ((ypos + row) * renderstate.width) + (col + tpos)] = w;
break;
case '+':
videobuf[xpos + ((ypos + row) * renderstate.width) + (col + tpos)] = g;
break;
default:
if (bg) { videobuf[xpos + ((ypos + row) * renderstate.width) + (col + tpos)] = d; }
break;

View file

@ -10,11 +10,6 @@
#define VIDBUF_MAXSIZE 31457280
#include <epoxy/gl.h>
#ifdef _APPLE
#include <OpenGL/gl.h>
#endif
typedef struct {
int w;
int h;
@ -35,9 +30,7 @@ void nst_ogl_deinit();
void nst_ogl_render();
void video_init();
void video_toggle_filter();
void video_toggle_filterupdate();
void video_toggle_scalefactor();
void video_set_filter();
dimensions_t nst_video_get_dimensions_render();
@ -50,7 +43,6 @@ void video_unlock_screen(void*);
void video_screenshot(const char* filename);
void video_clear_buffer();
void video_disp_nsf();
void nst_video_disp_inputconf(int type, int pnum, int bnum);
void nst_video_print(const char *text, int xpos, int ypos, int seconds, bool bg);
void nst_video_print_time(const char *timebuf, bool drawtime);
void nst_video_text_draw(const char *text, int xpos, int ypos, bool bg);

View file

@ -1,696 +0,0 @@
/*
* Nestopia UE
*
* Copyright (C) 2012-2018 R. Danbrook
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <SDL.h>
#include "nstcommon.h"
#include "cli.h"
#include "config.h"
#include "audio.h"
#include "video.h"
#include "input.h"
#include "sdlinput.h"
#include "gtkui.h"
#include "gtkui_archive.h"
#include "gtkui_callbacks.h"
#include "gtkui_config.h"
#include "gtkui_cheats.h"
#include "gtkui_dialogs.h"
#include "gtkui_input.h"
GtkWidget *gtkwindow;
static GtkWidget *menubar;
static GtkWidget *drawingarea;
static GThread *emuthread;
char iconpath[512];
char padpath[512];
extern bool (*nst_archive_select)(const char*, char*, size_t);
extern Input::Controllers *cNstPads;
extern nstpaths_t nstpaths;
int nst_quit = 1;
gpointer gtkui_emuloop(gpointer data) {
while(!nst_quit) { nst_emuloop(); }
g_thread_exit(emuthread);
return NULL;
}
void gtkui_emuloop_start() {
nst_quit = 0;
emuthread = g_thread_new("emuloop", gtkui_emuloop, NULL);
}
void gtkui_emuloop_stop() {
nst_quit = true;
}
void gtkui_quit() {
gtkui_emuloop_stop();
gtk_main_quit();
}
static void gtkui_glarea_realize(GtkGLArea *glarea) {
gtk_gl_area_make_current(glarea);
gtk_gl_area_set_has_depth_buffer(glarea, FALSE);
nst_ogl_init();
}
static void gtkui_swapbuffers() {
gtk_widget_queue_draw(drawingarea);
gtk_widget_queue_draw(menubar); // Needed on some builds of GTK3
nst_ogl_render();
nst_emuloop();
// Move this later FIXME
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_JOYHATMOTION:
case SDL_JOYAXISMOTION:
case SDL_JOYBUTTONDOWN:
case SDL_JOYBUTTONUP:
nstsdl_input_process(cNstPads, event);
break;
default: break;
}
}
}
void gtkui_state_quickload(GtkWidget *widget, gpointer userdata) {
// Wrapper function to quickload states
nst_state_quickload(GPOINTER_TO_INT(userdata));
}
void gtkui_state_quicksave(GtkWidget *widget, gpointer userdata) {
// Wrapper function to quicksave states
nst_state_quicksave(GPOINTER_TO_INT(userdata));
}
void gtkui_open_recent(GtkWidget *widget, gpointer userdata) {
// Open a recently used item
gchar *uri = gtk_recent_chooser_get_current_uri((GtkRecentChooser*)widget);
nst_load(g_filename_from_uri(uri, NULL, NULL));
gtkui_set_title(nstpaths.gamename);
gtkui_play();
}
dimensions_t gtkui_video_get_dimensions() {
// Return the dimensions of the current screen
dimensions_t scrsize;
dimensions_t rendsize = nst_video_get_dimensions_render();
GdkDisplay *display = gdk_display_get_default();
GdkWindow *gdkwindow = gtk_widget_get_window(GTK_WIDGET(gtkwindow));
GdkMonitor *monitor = gdk_display_get_monitor_at_window(display, gdkwindow);
GdkRectangle geom;
gdk_monitor_get_geometry(monitor, &geom);
scrsize.w = geom.width;
scrsize.h = geom.height;
float ratio = (float)scrsize.h / (float)rendsize.h;
scrsize.w = (int)(rendsize.w * ratio);
return scrsize;
}
void gtkui_create() {
// Create the GTK Window
gtkui_image_paths();
GdkPixbuf *icon = gdk_pixbuf_new_from_file(iconpath, NULL);
char title[24];
snprintf(title, sizeof(title), "Nestopia UE");
gtkwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_icon(GTK_WINDOW(gtkwindow), icon);
gtk_window_set_title(GTK_WINDOW(gtkwindow), title);
gtk_window_set_resizable(GTK_WINDOW(gtkwindow), FALSE);
GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add(GTK_CONTAINER(gtkwindow), box);
// Define the menubar and menus
menubar = gtk_menu_bar_new();
// Define the File menu
GtkWidget *filemenu = gtk_menu_new();
GtkWidget *file = gtk_menu_item_new_with_label("File");
GtkWidget *open = gtk_menu_item_new_with_label("Open...");
GtkWidget *recent = gtk_menu_item_new_with_label("Open Recent");
GtkWidget *sep_open = gtk_separator_menu_item_new();
GtkWidget *stateload = gtk_menu_item_new_with_label("Load State...");
GtkWidget *statesave = gtk_menu_item_new_with_label("Save State...");
GtkWidget *quickload = gtk_menu_item_new_with_label("Quick Load");
GtkWidget *qloadmenu = gtk_menu_new();
GtkWidget *qload0 = gtk_menu_item_new_with_label("0");
GtkWidget *qload1 = gtk_menu_item_new_with_label("1");
GtkWidget *qload2 = gtk_menu_item_new_with_label("2");
GtkWidget *qload3 = gtk_menu_item_new_with_label("3");
GtkWidget *qload4 = gtk_menu_item_new_with_label("4");
GtkWidget *quicksave = gtk_menu_item_new_with_label("Quick Save");
GtkWidget *qsavemenu = gtk_menu_new();
GtkWidget *qsave0 = gtk_menu_item_new_with_label("0");
GtkWidget *qsave1 = gtk_menu_item_new_with_label("1");
GtkWidget *qsave2 = gtk_menu_item_new_with_label("2");
GtkWidget *qsave3 = gtk_menu_item_new_with_label("3");
GtkWidget *qsave4 = gtk_menu_item_new_with_label("4");
GtkWidget *sep_state = gtk_separator_menu_item_new();
GtkWidget *palette = gtk_menu_item_new_with_label("Open Palette...");
GtkWidget *sep_palette = gtk_separator_menu_item_new();
GtkWidget *screenshot = gtk_menu_item_new_with_label("Screenshot...");
GtkWidget *sep_screenshot = gtk_separator_menu_item_new();
GtkWidget *movieload = gtk_menu_item_new_with_label("Load Movie...");
GtkWidget *moviesave = gtk_menu_item_new_with_label("Record Movie...");
GtkWidget *moviestop = gtk_menu_item_new_with_label("Stop Movie");
GtkWidget *sep_movie = gtk_separator_menu_item_new();
GtkWidget *quit = gtk_menu_item_new_with_label("Quit");
// Set up the recently used items
GtkWidget *recent_items = gtk_recent_chooser_menu_new();
GtkRecentFilter *recent_filter = gtk_recent_filter_new();
gtk_recent_filter_add_pattern(recent_filter, "*.nes");
gtk_recent_filter_add_pattern(recent_filter, "*.fds");
gtk_recent_filter_add_pattern(recent_filter, "*.unf");
gtk_recent_filter_add_pattern(recent_filter, "*.unif");
gtk_recent_filter_add_pattern(recent_filter, "*.nsf");
gtk_recent_filter_add_pattern(recent_filter, "*.zip");
gtk_recent_filter_add_pattern(recent_filter, "*.7z");
gtk_recent_filter_add_pattern(recent_filter, "*.txz");
gtk_recent_filter_add_pattern(recent_filter, "*.tar.xz");
gtk_recent_filter_add_pattern(recent_filter, "*.xz");
gtk_recent_filter_add_pattern(recent_filter, "*.tgz");
gtk_recent_filter_add_pattern(recent_filter, "*.tar.gz");
gtk_recent_filter_add_pattern(recent_filter, "*.gz");
gtk_recent_filter_add_pattern(recent_filter, "*.tbz");
gtk_recent_filter_add_pattern(recent_filter, "*.tar.bz2");
gtk_recent_filter_add_pattern(recent_filter, "*.bz2");
gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_items), recent_filter);
// Populate the File menu
gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), open);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), recent);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent), recent_items);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), sep_open);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), stateload);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), statesave);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quickload);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(quickload), qloadmenu);
gtk_menu_shell_append(GTK_MENU_SHELL(qloadmenu), qload0);
gtk_menu_shell_append(GTK_MENU_SHELL(qloadmenu), qload1);
gtk_menu_shell_append(GTK_MENU_SHELL(qloadmenu), qload2);
gtk_menu_shell_append(GTK_MENU_SHELL(qloadmenu), qload3);
gtk_menu_shell_append(GTK_MENU_SHELL(qloadmenu), qload4);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quicksave);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(quicksave), qsavemenu);
gtk_menu_shell_append(GTK_MENU_SHELL(qsavemenu), qsave0);
gtk_menu_shell_append(GTK_MENU_SHELL(qsavemenu), qsave1);
gtk_menu_shell_append(GTK_MENU_SHELL(qsavemenu), qsave2);
gtk_menu_shell_append(GTK_MENU_SHELL(qsavemenu), qsave3);
gtk_menu_shell_append(GTK_MENU_SHELL(qsavemenu), qsave4);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), sep_state);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), palette);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), sep_palette);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), screenshot);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), sep_screenshot);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), movieload);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), moviesave);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), moviestop);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), sep_movie);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
// Define the Emulator menu
GtkWidget *emulatormenu = gtk_menu_new();
GtkWidget *emu = gtk_menu_item_new_with_label("Emulator");
GtkWidget *cont = gtk_menu_item_new_with_label("Continue");
GtkWidget *pause = gtk_menu_item_new_with_label("Pause");
GtkWidget *sep_pause = gtk_separator_menu_item_new();
GtkWidget *resetsoft = gtk_menu_item_new_with_label("Reset (Soft)");
GtkWidget *resethard = gtk_menu_item_new_with_label("Reset (Hard)");
GtkWidget *sep_reset = gtk_separator_menu_item_new();
GtkWidget *fullscreen = gtk_menu_item_new_with_label("Fullscreen");
GtkWidget *sep_fullscreen = gtk_separator_menu_item_new();
GtkWidget *diskflip = gtk_menu_item_new_with_label("Flip FDS Disk");
GtkWidget *diskswitch = gtk_menu_item_new_with_label("Switch FDS Disk");
GtkWidget *sep_disk = gtk_separator_menu_item_new();
GtkWidget *cheats = gtk_menu_item_new_with_label("Cheats...");
GtkWidget *sep_cheats = gtk_separator_menu_item_new();
GtkWidget *configuration = gtk_menu_item_new_with_label("Configuration...");
// Populate the Emulator menu
gtk_menu_item_set_submenu(GTK_MENU_ITEM(emu), emulatormenu);
gtk_menu_shell_append(GTK_MENU_SHELL(emulatormenu), cont);
gtk_menu_shell_append(GTK_MENU_SHELL(emulatormenu), pause);
gtk_menu_shell_append(GTK_MENU_SHELL(emulatormenu), sep_pause);
gtk_menu_shell_append(GTK_MENU_SHELL(emulatormenu), resetsoft);
gtk_menu_shell_append(GTK_MENU_SHELL(emulatormenu), resethard);
gtk_menu_shell_append(GTK_MENU_SHELL(emulatormenu), sep_reset);
gtk_menu_shell_append(GTK_MENU_SHELL(emulatormenu), fullscreen);
gtk_menu_shell_append(GTK_MENU_SHELL(emulatormenu), sep_fullscreen);
gtk_menu_shell_append(GTK_MENU_SHELL(emulatormenu), diskflip);
gtk_menu_shell_append(GTK_MENU_SHELL(emulatormenu), diskswitch);
gtk_menu_shell_append(GTK_MENU_SHELL(emulatormenu), sep_disk);
gtk_menu_shell_append(GTK_MENU_SHELL(emulatormenu), cheats);
gtk_menu_shell_append(GTK_MENU_SHELL(emulatormenu), sep_cheats);
gtk_menu_shell_append(GTK_MENU_SHELL(emulatormenu), configuration);
// Define the Help menu
GtkWidget *helpmenu = gtk_menu_new();
GtkWidget *help = gtk_menu_item_new_with_label("Help");
GtkWidget *about = gtk_menu_item_new_with_label("About");
// Populate the Help menu
gtk_menu_item_set_submenu(GTK_MENU_ITEM(help), helpmenu);
gtk_menu_shell_append(GTK_MENU_SHELL(helpmenu), about);
// Put the menus into the menubar
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), emu);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), help);
// Create the DrawingArea/OpenGL context
drawingarea = gtk_gl_area_new();
g_signal_connect(G_OBJECT(drawingarea), "realize", G_CALLBACK(gtkui_glarea_realize), NULL);
g_signal_connect(G_OBJECT(drawingarea), "render", gtkui_swapbuffers, NULL);
g_object_set_data(G_OBJECT(gtkwindow), "area", drawingarea);
// Set the Drawing Area to be the size of the game output
dimensions_t rendersize = nst_video_get_dimensions_render();
gtk_widget_set_size_request(drawingarea, rendersize.w, rendersize.h);
// Pack the box with the menubar, drawingarea, and statusbar
gtk_box_pack_start(GTK_BOX(box), menubar, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(box), drawingarea, TRUE, TRUE, 0);
// Make it dark if there's a dark theme
GtkSettings *gtksettings = gtk_settings_get_default();
g_object_set(G_OBJECT(gtksettings), "gtk-application-prefer-dark-theme", TRUE, nullptr);
// Set up the Drag and Drop target
GtkTargetEntry target_entry[1];
target_entry[0].target = (gchar*)"text/uri-list";
target_entry[0].flags = 0;
target_entry[0].info = 0;
gtk_drag_dest_set(drawingarea, (GtkDestDefaults)(GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP),
target_entry, sizeof(target_entry) / sizeof(GtkTargetEntry), (GdkDragAction)(GDK_ACTION_MOVE | GDK_ACTION_COPY));
// Connect the signals
g_signal_connect(G_OBJECT(drawingarea), "drag-data-received",
G_CALLBACK(gtkui_drag_data), NULL);
g_signal_connect(G_OBJECT(gtkwindow), "delete_event",
G_CALLBACK(gtkui_quit), NULL);
// File menu
g_signal_connect(G_OBJECT(open), "activate",
G_CALLBACK(gtkui_file_open), NULL);
g_signal_connect(G_OBJECT(recent_items), "item-activated",
G_CALLBACK(gtkui_open_recent), NULL);
g_signal_connect(G_OBJECT(stateload), "activate",
G_CALLBACK(gtkui_state_load), NULL);
g_signal_connect(G_OBJECT(statesave), "activate",
G_CALLBACK(gtkui_state_save), NULL);
g_signal_connect(G_OBJECT(qload0), "activate",
G_CALLBACK(gtkui_state_quickload), gpointer(0));
g_signal_connect(G_OBJECT(qload1), "activate",
G_CALLBACK(gtkui_state_quickload), gpointer(1));
g_signal_connect(G_OBJECT(qload2), "activate",
G_CALLBACK(gtkui_state_quickload), gpointer(2));
g_signal_connect(G_OBJECT(qload3), "activate",
G_CALLBACK(gtkui_state_quickload), gpointer(3));
g_signal_connect(G_OBJECT(qload4), "activate",
G_CALLBACK(gtkui_state_quickload), gpointer(4));
g_signal_connect(G_OBJECT(qsave0), "activate",
G_CALLBACK(gtkui_state_quicksave), gpointer(0));
g_signal_connect(G_OBJECT(qsave1), "activate",
G_CALLBACK(gtkui_state_quicksave), gpointer(1));
g_signal_connect(G_OBJECT(qsave2), "activate",
G_CALLBACK(gtkui_state_quicksave), gpointer(2));
g_signal_connect(G_OBJECT(qsave3), "activate",
G_CALLBACK(gtkui_state_quicksave), gpointer(3));
g_signal_connect(G_OBJECT(qsave4), "activate",
G_CALLBACK(gtkui_state_quicksave), gpointer(4));
g_signal_connect(G_OBJECT(screenshot), "activate",
G_CALLBACK(gtkui_screenshot_save), NULL);
g_signal_connect(G_OBJECT(palette), "activate",
G_CALLBACK(gtkui_palette_load), NULL);
g_signal_connect(G_OBJECT(moviesave), "activate",
G_CALLBACK(gtkui_movie_save), NULL);
g_signal_connect(G_OBJECT(movieload), "activate",
G_CALLBACK(gtkui_movie_load), NULL);
g_signal_connect(G_OBJECT(moviestop), "activate",
G_CALLBACK(gtkui_movie_stop), NULL);
g_signal_connect(G_OBJECT(quit), "activate",
G_CALLBACK(gtkui_quit), NULL);
// Emulator menu
g_signal_connect(G_OBJECT(cont), "activate",
G_CALLBACK(gtkui_play), NULL);
g_signal_connect(G_OBJECT(pause), "activate",
G_CALLBACK(gtkui_pause), NULL);
g_signal_connect(G_OBJECT(resetsoft), "activate",
G_CALLBACK(gtkui_cb_reset), gpointer(0));
g_signal_connect(G_OBJECT(resethard), "activate",
G_CALLBACK(gtkui_cb_reset), gpointer(1));
g_signal_connect(G_OBJECT(fullscreen), "activate",
G_CALLBACK(gtkui_video_toggle_fullscreen), NULL);
g_signal_connect(G_OBJECT(diskflip), "activate",
G_CALLBACK(nst_fds_flip), NULL);
g_signal_connect(G_OBJECT(diskswitch), "activate",
G_CALLBACK(nst_fds_switch), NULL);
g_signal_connect(G_OBJECT(cheats), "activate",
G_CALLBACK(gtkui_cheats), NULL);
g_signal_connect(G_OBJECT(configuration), "activate",
G_CALLBACK(gtkui_config), NULL);
// Help menu
g_signal_connect(G_OBJECT(about), "activate",
G_CALLBACK(gtkui_about), NULL);
// Mouse input events
gtk_widget_add_events(GTK_WIDGET(drawingarea), GDK_BUTTON_PRESS_MASK);
gtk_widget_add_events(GTK_WIDGET(drawingarea), GDK_BUTTON_RELEASE_MASK);
gtk_widget_show_all(gtkwindow);
nst_video_set_dimensions_screen(gtkui_video_get_dimensions());
}
void gtkui_signals_init() {
// Key translation
if (nst_nsf()) {
g_signal_connect(G_OBJECT(gtkwindow), "key-press-event",
G_CALLBACK(gtkui_input_process_key_nsf), NULL);
g_signal_connect(G_OBJECT(gtkwindow), "key-release-event",
G_CALLBACK(gtkui_input_process_key_nsf), NULL);
}
else {
g_signal_connect(G_OBJECT(gtkwindow), "key-press-event",
G_CALLBACK(gtkui_input_process_key), NULL);
g_signal_connect(G_OBJECT(gtkwindow), "key-release-event",
G_CALLBACK(gtkui_input_process_key), NULL);
}
// Mouse translation
g_signal_connect(G_OBJECT(drawingarea), "button-press-event",
G_CALLBACK(gtkui_input_process_mouse), NULL);
g_signal_connect(G_OBJECT(drawingarea), "button-release-event",
G_CALLBACK(gtkui_input_process_mouse), NULL);
}
void gtkui_signals_deinit() {
// Key translation
g_signal_connect(G_OBJECT(gtkwindow), "key-press-event",
gtkui_input_null, NULL);
g_signal_connect(G_OBJECT(gtkwindow), "key-release-event",
gtkui_input_null, NULL);
// Mouse translation
g_signal_connect(G_OBJECT(drawingarea), "button-press-event",
gtkui_input_null, NULL);
g_signal_connect(G_OBJECT(drawingarea), "button-release-event",
gtkui_input_null, NULL);
}
void gtkui_resize() {
// Resize the GTK window
if (gtkwindow) {
dimensions_t rendersize;
if (conf.video_fullscreen) { rendersize = gtkui_video_get_dimensions(); }
else { rendersize = nst_video_get_dimensions_render(); }
gtk_widget_set_size_request(drawingarea, rendersize.w, rendersize.h);
}
}
void gtkui_set_title(const char *title) {
gtk_window_set_title(GTK_WINDOW(gtkwindow), title);
}
void gtkui_video_toggle_fullscreen() {
conf.video_fullscreen ^= 1;
if (conf.video_fullscreen) {
gtk_widget_hide(menubar);
gtk_window_fullscreen(GTK_WINDOW(gtkwindow));
if (nst_input_zapper_present()) {
gtkui_cursor_set(conf.misc_disable_cursor_special ? 0 : 2);
}
else {gtkui_cursor_set(0); }
}
else {
gtk_window_unfullscreen(GTK_WINDOW(gtkwindow));
gtk_widget_show(menubar);
if (nst_input_zapper_present()) {
gtkui_cursor_set(conf.misc_disable_cursor_special ? 0 : 2);
}
else {gtkui_cursor_set(1); }
}
nst_video_set_dimensions_screen(gtkui_video_get_dimensions());
gtkui_resize();
video_init();
}
void gtkui_video_toggle_filter() {
video_toggle_filter();
gtkui_resize();
video_init();
}
void gtkui_video_toggle_scale() {
video_toggle_scalefactor();
gtkui_resize();
video_init();
}
GtkWidget *gtkui_about() {
// Pull up the About dialog
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_size(iconpath, 192, 192, NULL);
GtkWidget *aboutdialog = gtk_about_dialog_new();
gtk_window_set_transient_for(GTK_WINDOW(aboutdialog), GTK_WINDOW(gtkwindow));
gtk_about_dialog_set_logo(GTK_ABOUT_DIALOG(aboutdialog), pixbuf);
gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(aboutdialog), "Nestopia UE");
gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(aboutdialog), "vx.xx");
gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(aboutdialog), "Cycle-Accurate Nintendo Entertainment System Emulator");
gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(aboutdialog), "http://0ldsk00l.ca/nestopia/");
gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(aboutdialog), "GTK Frontend\n(c) 2012-2021, R. Danbrook\n(c) 2007-2008, R. Belmont\n\nNestopia Emulator\n(c) 2020-2021, Rupert Carmichael\n(c) 2012-2020, Nestopia UE Contributors\n(c) 2003-2008, Martin Freij\n\nIcon based on art from Trollekop");
gtk_dialog_run(GTK_DIALOG(aboutdialog));
gtk_widget_destroy(aboutdialog);
return aboutdialog;
}
void gtkui_image_paths() {
// Set paths to SVG icons/images
snprintf(iconpath, sizeof(iconpath), "%s/icons/hicolor/scalable/apps/nestopia.svg", DATAROOTDIR);
snprintf(padpath, sizeof(padpath), "%s/icons/hicolor/scalable/apps/nespad.svg", DATAROOTDIR);
// Load the SVG from local source dir if make install hasn't been done
struct stat svgstat;
if (stat(iconpath, &svgstat) == -1) {
snprintf(iconpath, sizeof(iconpath), "icons/svg/nestopia.svg");
}
if (stat(padpath, &svgstat) == -1) {
snprintf(padpath, sizeof(padpath), "icons/svg/nespad.svg");
}
}
void gtkui_message(const char* message) {
GtkWidget *messagewindow = gtk_message_dialog_new(
GTK_WINDOW(gtkwindow),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_OK,
"%s", message);
gtk_dialog_run(GTK_DIALOG(messagewindow));
gtk_widget_destroy(messagewindow);
}
void gtkui_cursor_set(int curtype) {
// Set the cursor
GdkCursor *cursor;
GdkDisplay *display = gdk_display_get_default();
switch (curtype) {
case 0: cursor = gdk_cursor_new_from_name(display, "none"); break;
case 1: cursor = gdk_cursor_new_from_name(display, "default"); break;
case 2: cursor = gdk_cursor_new_from_name(display, "crosshair"); break;
default: cursor = gdk_cursor_new_from_name(display, "default"); break;
}
GdkWindow *gdkwindow = gtk_widget_get_window(GTK_WIDGET(drawingarea));
gdk_window_set_cursor(gdkwindow, cursor);
gdk_display_flush(display);
g_object_unref(cursor);
}
void gtkui_play() {
gtkui_signals_init();
nst_play();
if (nst_input_zapper_present()) {
gtkui_cursor_set(conf.misc_disable_cursor_special ? 0 : 2);
}
else {
gtkui_cursor_set(conf.misc_disable_cursor ? 0 : 1);
}
}
void gtkui_pause() {
gtkui_signals_deinit();
nst_pause();
}
int main(int argc, char *argv[]) {
// Set up directories
nst_set_dirs();
// Set default config options
config_set_default();
// Read the config file and override defaults
config_file_read(nstpaths.nstconfdir);
// Handle command line arguments
cli_handle_command(argc, argv);
// Set default input keys
gtkui_input_set_default();
// Read the input config file and override defaults
gtkui_input_config_read();
// Set the video dimensions
video_set_dimensions();
// Set up callbacks
nst_set_callbacks();
// Initialize SDL Audio and Joystick
if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) {
fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
return 1;
}
// Set archive handler function pointer
nst_archive_select = &gtkui_archive_select;
// Detect and set up Joysticks
nstsdl_input_joysticks_detect();
nstsdl_input_conf_defaults();
nstsdl_input_conf_read();
// Initialize and load FDS BIOS and NstDatabase.xml
nst_fds_bios_load();
nst_db_load();
// Initialize the GTK GUI
gtk_init(&argc, &argv);
gtkui_create();
// Load a rom from the command line
if (argc > 1 && argv[argc - 1][0] != '-') {
nst_load(argv[argc - 1]);
if (conf.video_fullscreen) {
conf.video_fullscreen = 0;
gtkui_video_toggle_fullscreen();
}
gtkui_play();
gtkui_set_title(nstpaths.gamename);
}
else if (conf.video_fullscreen) { conf.video_fullscreen = 0; }
// Start GTK main loop
gtk_main();
// Remove the cartridge and shut down the NES
nst_unload();
// Unload the FDS BIOS, NstDatabase.xml, and the custom palette
nst_db_unload();
nst_fds_bios_unload();
nst_palette_unload();
// Deinitialize audio
audio_deinit();
// Deinitialize joysticks
nstsdl_input_joysticks_close();
// Write the input config file
nstsdl_input_conf_write();
// Write the input config file
gtkui_input_config_write();
// Write the config file
config_file_write(nstpaths.nstconfdir);
return 0;
}

View file

@ -1,23 +0,0 @@
#ifndef _GTKUI_H_
#define _GTKUI_H_
#include <gtk/gtk.h>
void gtkui_emuloop_start();
void gtkui_emuloop_stop();
void gtkui_create();
void gtkui_resize();
void gtkui_set_title(const char *title);
GtkWidget *gtkui_about();
void gtkui_image_paths();
void gtkui_message(const char* message);
void gtkui_cursor_set(int curtype);
void gtkui_video_toggle_fullscreen();
void gtkui_video_toggle_filter();
void gtkui_video_toggle_scale();
void gtkui_signals_init();
void gtkui_signals_deinit();
void gtkui_play();
void gtkui_pause();
#endif

View file

@ -1,182 +0,0 @@
/*
* Nestopia UE
*
* Copyright (C) 2012-2016 R. Danbrook
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include <string.h>
#include <archive.h>
#include <archive_entry.h>
#include "nstcommon.h"
#include "config.h"
#include "gtkui.h"
#include "gtkui_archive.h"
static bool windowopen, cancelled;
static GtkWidget *archivewindow;
bool gtkui_archive_select(const char *filename, char *reqfile, size_t reqsize) {
// Select a filename to pull out of the archive
struct archive *a;
struct archive_entry *entry;
int r, numarchives = 0;
cancelled = false;
a = archive_read_new();
archive_read_support_filter_all(a);
archive_read_support_format_all(a);
r = archive_read_open_filename(a, filename, 10240);
// Test if it's actually an archive
if (r != ARCHIVE_OK) {
r = archive_read_free(a);
return false;
}
// If it is an archive, handle it
else {
// Set up the archive window
GtkTreeIter iter;
GtkTreeModel *model;
GtkTreeSelection *selection;
archivewindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(archivewindow), "Choose File from Archive");
gtk_window_set_modal(GTK_WINDOW(archivewindow), TRUE);
GtkWidget *archivebox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add(GTK_CONTAINER(archivewindow), archivebox);
gtk_widget_show(archivebox);
GtkWidget *scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
gtk_box_pack_start(GTK_BOX(archivebox), scrolledwindow, TRUE, TRUE, 0);
gtk_widget_set_size_request(scrolledwindow, 340, 340);
gtk_widget_show(scrolledwindow);
GtkWidget *buttonbox = gtk_widget_new(GTK_TYPE_BOX, "halign", GTK_ALIGN_END, NULL);
gtk_box_pack_start(GTK_BOX(archivebox), buttonbox, FALSE, TRUE, 0);
gtk_widget_show(buttonbox);
GtkWidget *treeview = gtk_tree_view_new();
gtk_container_add(GTK_CONTAINER(scrolledwindow), treeview);
gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW (treeview), FALSE);
gtk_widget_show(treeview);
GtkTreeStore *treestore = gtk_tree_store_new(1, G_TYPE_STRING);
gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(treestore));
// Fill the treestore with the filenames
while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
const char *currentfile = archive_entry_pathname(entry);
if (nst_archive_checkext(currentfile)) {
gtk_tree_store_append(treestore, &iter, NULL);
gtk_tree_store_set(treestore, &iter, 0, currentfile, -1);
numarchives++;
snprintf(reqfile, reqsize, "%s", currentfile);
}
archive_read_data_skip(a);
}
// Free the archive
r = archive_read_free(a);
// If there are no valid files in the archive, return
if (numarchives == 0) { return false; }
// If there's only one file, don't bring up the selector
else if (numarchives == 1) { return true; }
GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(
"NES file",
renderer,
"text", 0,
nullptr);
gtk_tree_view_append_column(GTK_TREE_VIEW (treeview), column);
GtkWidget *cancelbutton = gtk_widget_new(
GTK_TYPE_BUTTON,
"label", "Cancel",
"halign", GTK_ALIGN_END,
"margin-top", 8,
"margin-bottom", 8,
"margin-right", 8,
NULL);
gtk_box_pack_start(GTK_BOX(buttonbox), cancelbutton, FALSE, FALSE, 0);
gtk_widget_show(cancelbutton);
GtkWidget *okbutton = gtk_widget_new(
GTK_TYPE_BUTTON,
"label", "OK",
"halign", GTK_ALIGN_END,
"margin-top", 8,
"margin-bottom", 8,
"margin-right", 8,
NULL);
gtk_box_pack_start(GTK_BOX(buttonbox), okbutton, FALSE, FALSE, 0);
gtk_widget_show(okbutton);
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
g_signal_connect(G_OBJECT(okbutton), "clicked",
G_CALLBACK(gtkui_archive_ok), NULL);
g_signal_connect(G_OBJECT(cancelbutton), "clicked",
G_CALLBACK(gtkui_archive_cancel), NULL);
g_signal_connect(G_OBJECT(treeview), "row-activated",
G_CALLBACK(gtkui_archive_ok), NULL);
g_signal_connect(G_OBJECT(archivewindow), "destroy",
G_CALLBACK(gtkui_archive_cancel), NULL);
gtk_widget_show(archivewindow);
// Freeze the rest of the program until a selection is made
windowopen = true;
while (windowopen) {
gtk_main_iteration_do(TRUE);
if (cancelled) { return false; }
}
gchar *reqbuf;
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
gtk_tree_selection_get_selected(selection, &model, &iter);
gtk_tree_model_get(model, &iter, 0, &reqbuf, -1);
gtk_widget_destroy(archivewindow);
snprintf(reqfile, reqsize, "%s", reqbuf);
return true;
}
return false;
}
void gtkui_archive_ok() {
windowopen = false;
}
void gtkui_archive_cancel() {
cancelled = true;
gtk_widget_destroy(archivewindow);
}

View file

@ -1,8 +0,0 @@
#ifndef _GTKUI_ARCHIVE_H_
#define _GTKUI_ARCHIVE_H_
bool gtkui_archive_select(const char *filename, char *reqfile, size_t reqsize);
void gtkui_archive_ok();
void gtkui_archive_cancel();
#endif

View file

@ -1,266 +0,0 @@
/*
* Nestopia UE
*
* Copyright (C) 2012-2017 R. Danbrook
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include <SDL.h>
#include "nstcommon.h"
#include "config.h"
#include "video.h"
#include "input.h"
#include "gtkui.h"
#include "gtkui_callbacks.h"
extern bool kbactivate, confrunning;
extern int nst_quit;
//// Menu ////
void gtkui_cb_reset(GtkWidget *reset, int hard) {
// Reset the NES from the GUI
nst_reset(hard);
}
void gtkui_cb_nothing() {
// Do nothing
}
void gtkui_cb_video_refresh() {
// Refresh the Video output after changes
if (nst_playing()) { video_init(); }
gtkui_resize();
}
// Video //
void gtkui_cb_video_filter(GtkComboBox *combobox, gpointer userdata) {
// Change the video filter
conf.video_filter = gtk_combo_box_get_active(combobox);
gtkui_cb_video_refresh();
}
void gtkui_cb_video_scale(GtkComboBox *combobox, gpointer userdata) {
// Change the scale factor
conf.video_scale_factor = gtk_combo_box_get_active(combobox) + 1;
// The scalex filter only allows 3x scale and crashes otherwise
if (conf.video_filter == 5 && conf.video_scale_factor == 4) {
conf.video_scale_factor = 3;
}
gtkui_cb_video_refresh();
}
void gtkui_cb_video_ntscmode(GtkComboBox *combobox, gpointer userdata) {
// Change the NTSC Mode
conf.video_ntsc_mode = gtk_combo_box_get_active(combobox);
gtkui_cb_video_refresh();
}
void gtkui_cb_video_xbrrounding(GtkComboBox *combobox, gpointer userdata) {
// Set xBR corner rounding parameters
conf.video_xbr_corner_rounding = gtk_combo_box_get_active(combobox);
gtkui_cb_video_refresh();
video_toggle_filterupdate();
}
void gtkui_cb_video_xbrpixblend(GtkToggleButton *togglebutton, gpointer userdata) {
// Set xBR pixel blending parameters
conf.video_xbr_pixel_blending = gtk_toggle_button_get_active(togglebutton);
gtkui_cb_video_refresh();
video_toggle_filterupdate();
}
void gtkui_cb_video_linear_filter(GtkToggleButton *togglebutton, gpointer userdata) {
// Set linear filter
conf.video_linear_filter = gtk_toggle_button_get_active(togglebutton);
gtkui_cb_video_refresh();
}
void gtkui_cb_video_tv_aspect(GtkToggleButton *togglebutton, gpointer userdata) {
// Set TV aspect ratio
conf.video_tv_aspect = gtk_toggle_button_get_active(togglebutton);
gtkui_cb_video_refresh();
}
void gtkui_cb_video_unmask_overscan(GtkToggleButton *togglebutton, gpointer userdata) {
// Set overscan mask
conf.video_unmask_overscan = gtk_toggle_button_get_active(togglebutton);
gtkui_cb_video_refresh();
}
void gtkui_cb_video_stretch_aspect(GtkToggleButton *togglebutton, gpointer userdata) {
// Set aspect ratio stretching/preservation
conf.video_stretch_aspect = gtk_toggle_button_get_active(togglebutton);
gtkui_cb_video_refresh();
}
void gtkui_cb_video_unlimited_sprites(GtkToggleButton *togglebutton, gpointer userdata) {
// Set sprite limit
conf.video_unlimited_sprites = gtk_toggle_button_get_active(togglebutton);
gtkui_cb_video_refresh();
}
void gtkui_cb_video_palette(GtkComboBox *combobox, gpointer userdata) {
// Change the video palette
conf.video_palette_mode = gtk_combo_box_get_active(combobox);
gtkui_cb_video_refresh();
// this doesn't work unless there's a restart - fix
}
void gtkui_cb_video_decoder(GtkComboBox *combobox, gpointer userdata) {
// Change the YUV Decoder
conf.video_decoder = gtk_combo_box_get_active(combobox);
gtkui_cb_video_refresh();
}
void gtkui_cb_video_brightness(GtkRange *range, gpointer userdata) {
// Change video brightness
conf.video_brightness = (int)gtk_range_get_value(range);
gtkui_cb_video_refresh();
}
void gtkui_cb_video_saturation(GtkRange *range, gpointer userdata) {
// Change video saturation
conf.video_saturation = (int)gtk_range_get_value(range);
gtkui_cb_video_refresh();
}
void gtkui_cb_video_contrast(GtkRange *range, gpointer userdata) {
// Change video contrast
conf.video_contrast = (int)gtk_range_get_value(range);
gtkui_cb_video_refresh();
}
void gtkui_cb_video_hue(GtkRange *range, gpointer userdata) {
// Change video hue
conf.video_hue = (int)gtk_range_get_value(range);
gtkui_cb_video_refresh();
}
// Audio //
void gtkui_cb_audio_samplerate(GtkComboBox *combobox, gpointer userdata) {
// Change the Sample Rate
switch (gtk_combo_box_get_active(combobox)) {
case 0:
conf.audio_sample_rate = 11025;
break;
case 1:
conf.audio_sample_rate = 22050;
break;
case 2:
conf.audio_sample_rate = 44100;
break;
case 3:
conf.audio_sample_rate = 48000;
break;
case 4:
conf.audio_sample_rate = 96000;
break;
default:
conf.audio_sample_rate = 44100;
break;
}
if (nst_playing()) {
nst_pause();
nst_play();
}
}
void gtkui_cb_audio_stereo(GtkToggleButton *togglebutton, gpointer userdata) {
// Toggle Stereo
conf.audio_stereo = gtk_toggle_button_get_active(togglebutton);
if (nst_playing()) {
nst_pause();
nst_play();
}
}
//// Input ////
void gtkui_cb_input_turbopulse(GtkRange *range, gpointer userdata) {
// Change turbo pulse
conf.timing_turbopulse = (int)gtk_range_get_value(range);
}
//// Misc ////
void gtkui_cb_misc_default_system(GtkComboBox *combobox, gpointer userdata) {
// Select the default system
conf.misc_default_system = gtk_combo_box_get_active(combobox);
}
void gtkui_cb_misc_power_state(GtkComboBox *combobox, gpointer userdata) {
// Select the default system
conf.misc_power_state = gtk_combo_box_get_active(combobox);
}
void gtkui_cb_timing_ffspeed(GtkRange *range, gpointer userdata) {
// Set Fast-Forward Speed
conf.timing_ffspeed = (int)gtk_range_get_value(range);
}
void gtkui_cb_misc_soft_patching(GtkToggleButton *togglebutton, gpointer userdata) {
// Enable or Disable automatic soft patching
conf.misc_soft_patching = gtk_toggle_button_get_active(togglebutton);
}
void gtkui_cb_misc_genie_distortion(GtkToggleButton *togglebutton, gpointer userdata) {
// Enable or Disable Game Genie Sound Distortion
conf.misc_genie_distortion = gtk_toggle_button_get_active(togglebutton);
}
void gtkui_cb_misc_disable_cursor(GtkToggleButton *togglebutton, gpointer userdata) {
// Enable or Disable the Cursor
conf.misc_disable_cursor = gtk_toggle_button_get_active(togglebutton);
if (!nst_quit) { gtkui_play(); }
}
void gtkui_cb_misc_disable_cursor_special(GtkToggleButton *togglebutton, gpointer userdata) {
// Enable or Disable Special Cursors
conf.misc_disable_cursor_special = gtk_toggle_button_get_active(togglebutton);
if (!nst_quit) { gtkui_play(); }
}
void gtkui_cb_misc_config_pause(GtkToggleButton *togglebutton, gpointer userdata) {
// Pause GUI when configuration window is open
conf.misc_config_pause = gtk_toggle_button_get_active(togglebutton);
}
void gtkui_drag_data(GtkWidget *widget, GdkDragContext *dragcontext, gint x, gint y, GtkSelectionData *seldata, guint info, guint time, gpointer data) {
// Handle the Drag and Drop
if ((widget == NULL) || (dragcontext == NULL) || (seldata == NULL)) { return; }
if (info == 0) {
gchar *fileuri = (gchar*)gtk_selection_data_get_data(seldata);
gchar *filename = g_filename_from_uri(fileuri, NULL, NULL);
// Dirty hack. g_filename_from_uri adds a \r\n to the string
size_t ln = strlen(filename) - 2;
if (filename[ln] == '\r') { filename[ln] = '\0'; }
nst_load(filename);
}
}

View file

@ -1,41 +0,0 @@
#ifndef _GTKUI_CALLBACKS_H_
#define _GTKUI_CALLBACKS_H_
void gtkui_cb_reset(GtkWidget *reset, int hard);
void gtkui_cb_nothing();
void gtkui_cb_video_refresh();
void gtkui_cb_video_filter(GtkComboBox *combobox, gpointer userdata);
void gtkui_cb_video_scale(GtkComboBox *combobox, gpointer userdata);
void gtkui_cb_video_ntscmode(GtkComboBox *combobox, gpointer userdata);
void gtkui_cb_video_xbrrounding(GtkComboBox *combobox, gpointer userdata);
void gtkui_cb_video_xbrpixblend(GtkToggleButton *togglebutton, gpointer userdata);
void gtkui_cb_video_linear_filter(GtkToggleButton *togglebutton, gpointer userdata);
void gtkui_cb_video_tv_aspect(GtkToggleButton *togglebutton, gpointer userdata);
void gtkui_cb_video_unmask_overscan(GtkToggleButton *togglebutton, gpointer userdata);
void gtkui_cb_video_stretch_aspect(GtkToggleButton *togglebutton, gpointer userdata);
void gtkui_cb_video_unlimited_sprites(GtkToggleButton *togglebutton, gpointer userdata);
void gtkui_cb_video_palette(GtkComboBox *combobox, gpointer userdata);
void gtkui_cb_video_decoder(GtkComboBox *combobox, gpointer userdata);
void gtkui_cb_video_brightness(GtkRange *range, gpointer userdata);
void gtkui_cb_video_saturation(GtkRange *range, gpointer userdata);
void gtkui_cb_video_contrast(GtkRange *range, gpointer userdata);
void gtkui_cb_video_hue(GtkRange *range, gpointer userdata);
void gtkui_cb_audio_samplerate(GtkComboBox *combobox, gpointer userdata);
void gtkui_cb_audio_stereo(GtkToggleButton *togglebutton, gpointer userdata);
void gtkui_cb_input_turbopulse(GtkRange *range, gpointer userdata);
void gtkui_cb_misc_default_system(GtkComboBox *combobox, gpointer userdata);
void gtkui_cb_misc_power_state(GtkComboBox *combobox, gpointer userdata);
void gtkui_cb_timing_ffspeed(GtkRange *range, gpointer userdata);
void gtkui_cb_misc_soft_patching(GtkToggleButton *togglebutton, gpointer userdata);
void gtkui_cb_misc_genie_distortion(GtkToggleButton *togglebutton, gpointer userdata);
void gtkui_cb_misc_disable_cursor(GtkToggleButton *togglebutton, gpointer userdata);
void gtkui_cb_misc_disable_cursor_special(GtkToggleButton *togglebutton, gpointer userdata);
void gtkui_cb_misc_config_pause(GtkToggleButton *togglebutton, gpointer userdata);
void gtkui_drag_data(GtkWidget *widget, GdkDragContext *dragcontext, gint x, gint y, GtkSelectionData *seldata, guint info, guint time, gpointer data);
#endif

View file

@ -1,619 +0,0 @@
/*
* Nestopia UE
*
* Copyright (C) 2012-2016 R. Danbrook
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include "nstcommon.h"
#include "config.h"
#include "cheats.h"
#include "gtkui.h"
#include "gtkui_callbacks.h"
#include "gtkui_cheats.h"
#include "gtkui_dialogs.h"
extern nstpaths_t nstpaths;
extern Emulator emulator;
GtkWidget *cheatwindow;
GtkTreeStore *treestore;
GtkWidget *treeview;
GtkWidget *descedit, *ggedit, *paredit;
GtkWidget *infobar, *infolabel;
Xml savexml;
Xml::Node saveroot;
GtkWidget *gtkui_cheats() {
// Create the Cheats window
if (cheatwindow) { return NULL; }
cheatwindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW (cheatwindow), "Cheat Manager");
GtkWidget *cheatbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add(GTK_CONTAINER(cheatwindow), cheatbox);
GtkWidget *scrolledwindow = gtk_scrolled_window_new(NULL, NULL);
gtk_box_pack_start(GTK_BOX(cheatbox), scrolledwindow, TRUE, TRUE, 0);
gtk_widget_set_size_request(scrolledwindow, 512, 256);
treeview = gtk_tree_view_new();
gtk_container_add(GTK_CONTAINER (scrolledwindow), treeview);
infobar = gtk_info_bar_new();
infolabel = gtk_widget_new(GTK_TYPE_LABEL,"label", "", NULL);
gtk_box_pack_start(GTK_BOX(cheatbox), infobar, TRUE, TRUE, 0);
GtkWidget *content_area = gtk_info_bar_get_content_area(GTK_INFO_BAR(infobar));
gtk_box_pack_start(GTK_BOX(content_area), infolabel, TRUE, TRUE, 0);
GtkWidget *opensavebox = gtk_widget_new(GTK_TYPE_BOX, "halign", GTK_ALIGN_END, NULL);
gtk_box_pack_start(GTK_BOX(cheatbox), opensavebox, FALSE, FALSE, 0);
GtkWidget *cheatopen = gtk_widget_new(
GTK_TYPE_BUTTON,
"label", "Open",
"halign", GTK_ALIGN_END,
"margin-top", 8,
"margin-bottom", 8,
"margin-right", 8,
NULL);
gtk_box_pack_start(GTK_BOX(opensavebox), cheatopen, FALSE, FALSE, 0);
GtkWidget *cheatclear = gtk_widget_new(
GTK_TYPE_BUTTON,
"label", "Clear",
"halign", GTK_ALIGN_END,
"margin-top", 8,
"margin-bottom", 8,
"margin-right", 8,
NULL);
gtk_box_pack_start(GTK_BOX(opensavebox), cheatclear, FALSE, FALSE, 0);
GtkWidget *cheatremove = gtk_widget_new(
GTK_TYPE_BUTTON,
"label", "Remove",
"halign", GTK_ALIGN_END,
"margin-top", 8,
"margin-bottom", 8,
"margin-right", 8,
NULL);
gtk_box_pack_start(GTK_BOX(opensavebox), cheatremove, FALSE, FALSE, 0);
GtkWidget *descbox = gtk_widget_new(
GTK_TYPE_BOX,
"halign", GTK_ALIGN_END,
NULL);
gtk_box_pack_start(GTK_BOX(cheatbox), descbox, FALSE, FALSE, 0);
GtkWidget *desclabel = gtk_widget_new(
GTK_TYPE_LABEL,
"label", "Description:",
"halign", GTK_ALIGN_END,
"margin-top", 8,
"margin-bottom", 8,
"margin-left", 8,
"margin-right", 8,
NULL);
gtk_box_pack_start(GTK_BOX(descbox), desclabel, FALSE, FALSE, 0);
descedit = gtk_widget_new(
GTK_TYPE_ENTRY,
"halign", GTK_ALIGN_END,
"margin-top", 8,
"margin-bottom", 8,
"margin-right", 8,
NULL);
gtk_box_pack_start(GTK_BOX(descbox), descedit, TRUE, TRUE, 0);
GtkWidget *ggbox = gtk_widget_new(
GTK_TYPE_BOX,
"halign", GTK_ALIGN_END,
NULL);
gtk_box_pack_start(GTK_BOX(cheatbox), ggbox, FALSE, FALSE, 0);
GtkWidget *gglabel = gtk_widget_new(
GTK_TYPE_LABEL,
"label", "Game Genie:",
"halign", GTK_ALIGN_END,
"margin-top", 8,
"margin-bottom", 8,
"margin-left", 8,
"margin-right", 8,
NULL);
gtk_box_pack_start(GTK_BOX(ggbox), gglabel, FALSE, FALSE, 0);
ggedit = gtk_widget_new(
GTK_TYPE_ENTRY,
"halign", GTK_ALIGN_END,
"margin-top", 8,
"margin-bottom", 8,
"margin-right", 8,
NULL);
gtk_box_pack_start(GTK_BOX(ggbox), ggedit, TRUE, TRUE, 0);
GtkWidget *genieadd = gtk_widget_new(
GTK_TYPE_BUTTON,
"label", "Add",
"halign", GTK_ALIGN_END,
"margin-top", 8,
"margin-bottom", 8,
"margin-right", 8,
NULL);
gtk_box_pack_start(GTK_BOX(ggbox), genieadd, FALSE, FALSE, 0);
GtkWidget *parbox = gtk_widget_new(
GTK_TYPE_BOX,
"halign", GTK_ALIGN_END,
NULL);
gtk_box_pack_start(GTK_BOX(cheatbox), parbox, FALSE, FALSE, 0);
GtkWidget *parlabel = gtk_widget_new(
GTK_TYPE_LABEL,
"label", "Pro Action Rocky:",
"halign", GTK_ALIGN_END,
"margin-top", 8,
"margin-bottom", 8,
"margin-left", 8,
"margin-right", 8,
NULL);
gtk_box_pack_start(GTK_BOX(parbox), parlabel, FALSE, FALSE, 0);
paredit = gtk_widget_new(
GTK_TYPE_ENTRY,
"halign", GTK_ALIGN_END,
"margin-top", 8,
"margin-bottom", 8,
"margin-right", 8,
NULL);
gtk_box_pack_start(GTK_BOX(parbox), paredit, FALSE, FALSE, 0);
GtkWidget *paradd = gtk_widget_new(
GTK_TYPE_BUTTON,
"label", "Add",
"halign", GTK_ALIGN_END,
"margin-top", 8,
"margin-bottom", 8,
"margin-right", 8,
NULL);
gtk_box_pack_start(GTK_BOX(parbox), paradd, FALSE, FALSE, 0);
GtkWidget *cheatok = gtk_widget_new(
GTK_TYPE_BUTTON,
"label", "OK",
"halign", GTK_ALIGN_END,
"margin-top", 8,
"margin-bottom", 8,
"margin-right", 8,
NULL);
gtk_box_pack_start(GTK_BOX(cheatbox), cheatok, FALSE, FALSE, 0);
gtk_tree_view_set_fixed_height_mode(GTK_TREE_VIEW(treeview), FALSE);
treestore = gtk_tree_store_new(5, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
gtk_tree_view_set_model(GTK_TREE_VIEW(treeview), GTK_TREE_MODEL(treestore));
GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
GtkCellRenderer *checkbox = gtk_cell_renderer_toggle_new();
GtkTreeViewColumn *column[5];
// create the display columns
column[0] = gtk_tree_view_column_new_with_attributes("Enable", checkbox, "active", 0, nullptr);
column[1] = gtk_tree_view_column_new_with_attributes("Game Genie", renderer, "text", 1, nullptr);
column[2] = gtk_tree_view_column_new_with_attributes("PAR", renderer, "text", 2, nullptr);
column[3] = gtk_tree_view_column_new_with_attributes("Raw", renderer, "text", 3, nullptr);
column[4] = gtk_tree_view_column_new_with_attributes("Description", renderer, "text", 4, nullptr);
// add the display column and renderer to the tree view
gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column[0]);
gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column[1]);
gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column[2]);
gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column[3]);
gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column[4]);
gtkui_cheats_fill_tree(nstpaths.cheatpath);
/*g_signal_connect(G_OBJECT(checkbox), "toggled",
G_CALLBACK(gtkui_cheats_check), NULL);*/
g_signal_connect(G_OBJECT(treeview), "row-activated",
G_CALLBACK(gtkui_cheats_toggle), NULL);
g_signal_connect(G_OBJECT(cheatopen), "clicked",
G_CALLBACK(gtkui_cheats_load), NULL);
g_signal_connect(G_OBJECT(cheatclear), "clicked",
G_CALLBACK(gtkui_cheats_clear), NULL);
g_signal_connect(G_OBJECT(cheatremove), "clicked",
G_CALLBACK(gtkui_cheats_remove), NULL);
g_signal_connect(G_OBJECT(genieadd), "clicked",
G_CALLBACK(gtkui_cheats_gg_add), NULL);
g_signal_connect(G_OBJECT(paradd), "clicked",
G_CALLBACK(gtkui_cheats_par_add), NULL);
g_signal_connect(G_OBJECT(cheatok), "clicked",
G_CALLBACK(gtkui_cheats_ok), NULL);
g_signal_connect(G_OBJECT(cheatwindow), "destroy",
G_CALLBACK(gtkui_cheats_ok), NULL);
gtk_widget_show_all(cheatwindow);
gtk_widget_hide(infobar);
return cheatwindow;
}
void gtkui_cheats_check(GtkWidget *widget, gchar *element, gpointer userdata) {
// This function doesn't work. Fix later.
GtkTreeIter iter;
bool value;
// Read the value of the checkbox
value = gtk_cell_renderer_toggle_get_active((GtkCellRendererToggle*)widget);
// Flip the value and set it
value ^= 1;
gtk_cell_renderer_toggle_set_active((GtkCellRendererToggle*)widget, value);
}
void gtkui_cheats_toggle(GtkWidget *widget, gpointer userdata) {
// Toggle a cheat on or off
GtkTreeIter iter;
GtkTreeModel *model;
GtkTreeSelection *selection;
bool value;
// Get the selected item
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
gtk_tree_selection_get_selected(selection, &model, &iter);
// Read the value of the checkbox
gtk_tree_model_get(model, &iter, 0, &value, -1);
// Flip the value and set it
value ^= 1;
gtk_tree_store_set(treestore, &iter, 0, value, -1);
//Re-initialize the cheats
Cheats cheats(emulator);
cheats.ClearCodes();
gtk_tree_model_foreach(GTK_TREE_MODEL(model), gtkui_cheats_scan_list, NULL);
}
void gtkui_cheats_fill_tree(char *filename) {
// Fill the cheat list
Xml xml;
GtkTreeIter iter;
bool enabled = false;
char codebuf[9];
char descbuf[512];
gtkui_cheats_clear();
std::ifstream cheatfile(filename, std::ifstream::in|std::ifstream::binary);
if (cheatfile.is_open()) {
xml.Read(cheatfile);
if (xml.GetRoot().IsType(L"cheats")) {
Xml::Node root(xml.GetRoot());
Xml::Node node(root.GetFirstChild());
for (int i = 0; i < root.NumChildren(L"cheat"); i++) {
wcstombs(descbuf, node.GetChild(L"description").GetValue(), sizeof(descbuf));
// Check if the cheat is enabled
node.GetAttribute(L"enabled").IsValue(L"1") ? enabled = true : enabled = false;
// Add the cheats to the list
if (node.GetChild(L"genie")) { // Game Genie
wcstombs(codebuf, node.GetChild(L"genie").GetValue(), sizeof(codebuf));
gtk_tree_store_append(treestore, &iter, NULL);
gtk_tree_store_set(treestore, &iter,
0, enabled,
1, codebuf,
4, descbuf,
-1);
if (enabled) { nst_cheats_code_gg_add(node.GetChild(L"genie").GetValue()); }
}
else if (node.GetChild(L"rocky")) { // Pro Action Rocky
wcstombs(codebuf, node.GetChild(L"rocky").GetValue(), sizeof(codebuf));
gtk_tree_store_append(treestore, &iter, NULL);
gtk_tree_store_set(treestore, &iter,
0, enabled,
2, codebuf,
4, descbuf,
-1);
if (enabled) { nst_cheats_code_par_add(node.GetChild(L"rocky").GetValue()); }
}
else if (node.GetChild(L"address")) { // Raw
char rawbuf[11];
snprintf(rawbuf, sizeof(rawbuf),
"%04lu %02lu %02lu",
node.GetChild(L"address").GetUnsignedValue(),
node.GetChild(L"value").GetUnsignedValue(),
node.GetChild(L"compare").GetUnsignedValue());
gtk_tree_store_append(treestore, &iter, NULL);
gtk_tree_store_set(treestore, &iter,
0, enabled,
3, rawbuf,
4, descbuf,
-1);
if (enabled) { nst_cheats_code_raw_add(node); }
}
node = node.GetNextSibling();
}
}
cheatfile.close();
}
}
void gtkui_cheats_save() {
// Save the cheat list
std::ofstream cheatfile(nstpaths.cheatpath, std::ifstream::out|std::ifstream::binary);
if (cheatfile.is_open()) {
GtkTreeModel *model;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
saveroot = (savexml.GetRoot());
saveroot = savexml.Create( L"cheats" );
saveroot.AddAttribute( L"version", L"1.0" );
gtk_tree_model_foreach(GTK_TREE_MODEL(model), gtkui_cheats_write_list, NULL);
savexml.Write(saveroot, cheatfile);
cheatfile.close();
}
else { return; }
}
void gtkui_cheats_gg_add(GtkWidget *widget, gpointer userdata) {
// Add a Game Genie code to the list
GtkTreeIter iter;
Cheats cheats(emulator);
Cheats::Code code;
char codebuf[9];
char descbuf[512];
snprintf(codebuf, sizeof(codebuf), "%.8s", gtk_entry_get_text(GTK_ENTRY(ggedit)));
snprintf(descbuf, sizeof(descbuf), "%s", gtk_entry_get_text(GTK_ENTRY(descedit)));
if (cheats.GameGenieDecode(codebuf, code) == Nes::RESULT_OK) {
gtk_tree_store_append(treestore, &iter, NULL);
gtk_tree_store_set(treestore, &iter,
0, true,
1, codebuf,
4, descbuf,
-1);
gtk_entry_set_text(GTK_ENTRY(descedit), "");
gtk_entry_set_text(GTK_ENTRY(ggedit), "");
gtk_entry_set_text(GTK_ENTRY(paredit), "");
gtk_widget_hide(infobar);
gtk_label_set_text(GTK_LABEL(infolabel), "");
cheats.SetCode(code);
}
else {
gtk_info_bar_set_message_type(GTK_INFO_BAR(infobar), GTK_MESSAGE_ERROR);
gtk_label_set_text(GTK_LABEL(infolabel), "Error: Invalid Game Genie code");
gtk_widget_show(infobar);
}
}
void gtkui_cheats_par_add(GtkWidget *widget, gpointer userdata) {
// Add a Pro Action Rocky code to the list
GtkTreeIter iter;
Cheats cheats(emulator);
Cheats::Code code;
char codebuf[9];
char descbuf[512];
snprintf(codebuf, sizeof(codebuf), "%.8s", gtk_entry_get_text(GTK_ENTRY(paredit)));
snprintf(descbuf, sizeof(descbuf), "%s", gtk_entry_get_text(GTK_ENTRY(descedit)));
if (cheats.ProActionRockyDecode(codebuf, code) == Nes::RESULT_OK) {
gtk_tree_store_append(treestore, &iter, NULL);
gtk_tree_store_set(treestore, &iter,
0, true,
1, codebuf,
4, descbuf,
-1);
gtk_entry_set_text(GTK_ENTRY(descedit), "");
gtk_entry_set_text(GTK_ENTRY(ggedit), "");
gtk_entry_set_text(GTK_ENTRY(paredit), "");
gtk_widget_hide(infobar);
gtk_label_set_text(GTK_LABEL(infolabel), "");
cheats.SetCode(code);
}
else {
gtk_info_bar_set_message_type(GTK_INFO_BAR(infobar), GTK_MESSAGE_ERROR);
gtk_label_set_text(GTK_LABEL(infolabel), "Error: Invalid PAR code");
gtk_widget_show(infobar);
}
}
void gtkui_cheats_remove(GtkWidget *widget, gpointer userdata) {
// Remove a cheat from the list
GtkTreeIter iter;
GtkTreeModel *model;
GtkTreeSelection *selection;
// Get the selected item
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
gtk_tree_selection_get_selected(selection, &model, &iter);
// Remove the cheat
if (gtk_tree_store_iter_is_valid(treestore, &iter)) {
gtk_tree_store_remove(treestore, &iter);
}
//Re-initialize the cheats
Cheats cheats(emulator);
cheats.ClearCodes();
gtk_tree_model_foreach(GTK_TREE_MODEL(model), gtkui_cheats_scan_list, NULL);
}
void gtkui_cheats_ok() {
// Save the cheats and close the window
gtkui_cheats_save();
gtk_widget_destroy(cheatwindow);
cheatwindow = NULL;
}
void gtkui_cheats_clear() {
// Clear the list
gtk_tree_store_clear(treestore);
Cheats cheats(emulator);
cheats.ClearCodes();
}
gboolean gtkui_cheats_scan_list(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer userdata) {
// Scan through the list of cheats
Cheats cheats(emulator);
Cheats::Code code;
bool enabled;
gchar *ggcode, *parcode, *rawcode, *description;
gtk_tree_model_get(model, iter, 0, &enabled, 1, &ggcode, 2, &parcode, 3, &rawcode, 4, &description, -1);
if (enabled) {
if (ggcode) {
cheats.GameGenieDecode(ggcode, code);
cheats.SetCode(code);
}
else if (parcode) {
cheats.ProActionRockyDecode(parcode, code);
cheats.SetCode(code);
}
else if (rawcode) {
code.useCompare = false;
int addr, value, compare;
char buf[5];
snprintf(buf, sizeof(buf), "%c%c%c%c", rawcode[0], rawcode[1], rawcode[2], rawcode[3]);
sscanf(buf, "%x", &addr);
snprintf(buf, sizeof(buf), "%c%c", rawcode[5], rawcode[6]);
sscanf(buf, "%x", &value);
snprintf(buf, sizeof(buf), "%c%c", rawcode[8], rawcode[9]);
sscanf(buf, "%x", &compare);
code.address = addr;
code.value = value;
code.compare = compare;
if (compare) { code.useCompare = true; }
cheats.SetCode(code);
}
}
g_free(ggcode);
g_free(parcode);
g_free(rawcode);
g_free(description);
return false;
}
gboolean gtkui_cheats_write_list(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer userdata) {
// Write entries to the cheat file
bool enabled;
gchar *ggcode, *parcode, *rawcode, *description;
char buf[9];
wchar_t wbuf[9];
gtk_tree_model_get(model, iter, 0, &enabled, 1, &ggcode, 2, &parcode, 3, &rawcode, 4, &description, -1);
Xml::Node node(saveroot.AddChild(L"cheat"));
node.AddAttribute(L"enabled", enabled ? L"1" : L"0");
if (ggcode) {
snprintf(buf, sizeof(buf), "%s", ggcode);
mbstowcs(wbuf, buf, 9);
node.AddChild(L"genie", wbuf);
}
if (parcode) {
snprintf(buf, sizeof(buf), "%s", parcode);
mbstowcs(wbuf, buf, 9);
node.AddChild(L"rocky", wbuf);
}
if (rawcode) {
snprintf(buf, sizeof(buf), "0x%c%c%c%c", rawcode[0], rawcode[1], rawcode[2], rawcode[3]);
mbstowcs(wbuf, buf, 9);
node.AddChild(L"address", wbuf);
snprintf(buf, sizeof(buf), "0x%c%c", rawcode[5], rawcode[6]);
mbstowcs(wbuf, buf, 9);
node.AddChild(L"value", wbuf);
snprintf(buf, sizeof(buf), "0x%c%c", rawcode[8], rawcode[9]);
mbstowcs(wbuf, buf, 9);
node.AddChild(L"compare", wbuf);
}
if (description) {
char descbuf[512];
wchar_t wdescbuf[512];
snprintf(descbuf, sizeof(descbuf), "%s", description);
mbstowcs(wdescbuf, descbuf, 512);
node.AddChild(L"description", wdescbuf);
}
g_free(ggcode);
g_free(parcode);
g_free(rawcode);
g_free(description);
return false;
}

View file

@ -1,17 +0,0 @@
#ifndef _GTKUI_CHEATS_H_
#define _GTKUI_CHEATS_H_
GtkWidget *gtkui_cheats();
void gtkui_cheats_check(GtkWidget *widget, gchar *element, gpointer userdata);
void gtkui_cheats_toggle(GtkWidget *widget, gpointer userdata);
void gtkui_cheats_fill_tree(char *filename);
void gtkui_cheats_save();
void gtkui_cheats_gg_add(GtkWidget *widget, gpointer userdata);
void gtkui_cheats_par_add(GtkWidget *widget, gpointer userdata);
void gtkui_cheats_remove(GtkWidget *widget, gpointer userdata);
void gtkui_cheats_ok();
void gtkui_cheats_clear();
gboolean gtkui_cheats_scan_list(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer userdata);
gboolean gtkui_cheats_write_list(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer userdata);
#endif

File diff suppressed because it is too large Load diff

View file

@ -1,18 +0,0 @@
#ifndef _GTKUI_CONFIG_H_
#define _GTKUI_CONFIG_H_
#define MARGIN_TB 5
#define MARGIN_LR 10
#define NUMCHANNELS 12
GtkWidget *gtkui_config();
void gtkui_config_ok();
void gtkui_audio_volume();
void gtkui_audio_volume_master();
void gtkui_config_input_activate(GtkWidget *widget, GtkTreePath *path, gpointer userdata);
void gtkui_config_input_refresh();
void gtkui_config_input_fields(int type, int pnum);
void gtkui_config_input_defaults();
#endif

View file

@ -1,258 +0,0 @@
/*
* Nestopia UE
*
* Copyright (C) 2012-2016 R. Danbrook
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include <stdlib.h>
#include "nstcommon.h"
#include "video.h"
#include "config.h"
#include "gtkui.h"
#include "gtkui_cheats.h"
extern nstpaths_t nstpaths;
extern GtkWidget *gtkwindow;
void gtkui_file_open() {
// Open a file using a GTK dialog
GtkWidget *dialog = gtk_file_chooser_dialog_new(
"Select a ROM",
GTK_WINDOW(gtkwindow),
GTK_FILE_CHOOSER_ACTION_OPEN,
"Cancel", GTK_RESPONSE_CANCEL,
"Open", GTK_RESPONSE_ACCEPT,
nullptr);
if(conf.misc_last_folder != NULL)
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), conf.misc_last_folder);
GtkFileFilter *filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, "NES ROMs and Archives");
gtk_file_filter_add_pattern(filter, "*.nes");
gtk_file_filter_add_pattern(filter, "*.fds");
gtk_file_filter_add_pattern(filter, "*.unf");
gtk_file_filter_add_pattern(filter, "*.unif");
gtk_file_filter_add_pattern(filter, "*.nsf");
gtk_file_filter_add_pattern(filter, "*.zip");
gtk_file_filter_add_pattern(filter, "*.7z");
gtk_file_filter_add_pattern(filter, "*.txz");
gtk_file_filter_add_pattern(filter, "*.tar.xz");
gtk_file_filter_add_pattern(filter, "*.xz");
gtk_file_filter_add_pattern(filter, "*.tgz");
gtk_file_filter_add_pattern(filter, "*.tar.gz");
gtk_file_filter_add_pattern(filter, "*.gz");
gtk_file_filter_add_pattern(filter, "*.tbz");
gtk_file_filter_add_pattern(filter, "*.tar.bz2");
gtk_file_filter_add_pattern(filter, "*.bz2");
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
char *filename;
filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
if(conf.misc_last_folder != NULL)
free(conf.misc_last_folder);
conf.misc_last_folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(dialog));
gtk_widget_destroy(dialog);
nst_load(filename);
gtkui_set_title(nstpaths.gamename);
gtkui_play();
g_free(filename);
}
else { gtk_widget_destroy(dialog); }
}
void gtkui_state_save() {
// Save a state from the GUI
GtkWidget *dialog = gtk_file_chooser_dialog_new("Save State (.nst)",
GTK_WINDOW(gtkwindow),
GTK_FILE_CHOOSER_ACTION_SAVE,
"Cancel", GTK_RESPONSE_CANCEL,
"Save", GTK_RESPONSE_ACCEPT,
nullptr);
char statepath[512];
snprintf(statepath, sizeof(statepath), "%s.nst", nstpaths.statepath);
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), statepath);
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), nstpaths.statepath);
if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
nst_state_save(filename);
g_free(filename);
}
gtk_widget_destroy(dialog);
}
void gtkui_state_load() {
// Load a state from the GUI
GtkWidget *dialog = gtk_file_chooser_dialog_new("Load State (.nst)",
GTK_WINDOW(gtkwindow),
GTK_FILE_CHOOSER_ACTION_OPEN,
"Cancel", GTK_RESPONSE_CANCEL,
"Open", GTK_RESPONSE_ACCEPT,
nullptr);
GtkFileFilter *filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, "Nestopia Save States");
gtk_file_filter_add_pattern(filter, "*.nst");
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), nstpaths.statepath);
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
nst_state_load(filename);
g_free(filename);
}
gtk_widget_destroy(dialog);
}
void gtkui_screenshot_save() {
// Save a screenshot from the GUI
GtkWidget *dialog = gtk_file_chooser_dialog_new("Save screenshot (.png)",
GTK_WINDOW(gtkwindow),
GTK_FILE_CHOOSER_ACTION_SAVE,
"Cancel", GTK_RESPONSE_CANCEL,
"Save", GTK_RESPONSE_ACCEPT,
nullptr);
char sshotpath[512];
char sshotfile[768];
snprintf(sshotpath, sizeof(sshotpath), "%sscreenshots/", nstpaths.nstdir);
snprintf(sshotfile, sizeof(sshotfile), "%s%s.png", sshotpath, nstpaths.gamename);
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), sshotfile);
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), sshotpath);
if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
video_screenshot(filename);
g_free(filename);
}
gtk_widget_destroy(dialog);
}
void gtkui_movie_save() {
// Save a movie from the GUI
GtkWidget *dialog = gtk_file_chooser_dialog_new("Save movie (.nsv)",
GTK_WINDOW(gtkwindow),
GTK_FILE_CHOOSER_ACTION_SAVE,
"Cancel", GTK_RESPONSE_CANCEL,
"Save", GTK_RESPONSE_ACCEPT,
nullptr);
char moviepath[512];
snprintf(moviepath, sizeof(moviepath), "%s%s.nsv", nstpaths.nstdir, nstpaths.gamename);
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), moviepath);
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), nstpaths.nstdir);
if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
nst_movie_save(filename);
g_free(filename);
}
gtk_widget_destroy(dialog);
}
void gtkui_movie_load() {
// Load a movie from the GUI
GtkWidget *dialog = gtk_file_chooser_dialog_new("Load movie (.nsv)",
GTK_WINDOW(gtkwindow),
GTK_FILE_CHOOSER_ACTION_OPEN,
"Cancel", GTK_RESPONSE_CANCEL,
"Open", GTK_RESPONSE_ACCEPT,
nullptr);
GtkFileFilter *filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, "Nestopia movies");
gtk_file_filter_add_pattern(filter, "*.nsv");
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), nstpaths.nstdir);
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
nst_movie_load(filename);
g_free(filename);
}
gtk_widget_destroy(dialog);
}
void gtkui_movie_stop() {
nst_movie_stop();
}
void gtkui_cheats_load() {
// Load cheats from the GUI
GtkWidget *dialog = gtk_file_chooser_dialog_new("Load cheats (.xml)",
GTK_WINDOW(gtkwindow),
GTK_FILE_CHOOSER_ACTION_OPEN,
"Cancel", GTK_RESPONSE_CANCEL,
"Open", GTK_RESPONSE_ACCEPT,
nullptr);
GtkFileFilter *filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, "Nestopia cheats");
gtk_file_filter_add_pattern(filter, "*.xml");
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), nstpaths.nstdir);
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
gtkui_cheats_fill_tree(filename);
g_free(filename);
}
gtk_widget_destroy(dialog);
}
void gtkui_palette_load() {
// Load a palette from the GUI
GtkWidget *dialog = gtk_file_chooser_dialog_new("Load palette (.pal)",
GTK_WINDOW(gtkwindow),
GTK_FILE_CHOOSER_ACTION_OPEN,
"Cancel", GTK_RESPONSE_CANCEL,
"Open", GTK_RESPONSE_ACCEPT,
nullptr);
GtkFileFilter *filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, "NES Palettes");
gtk_file_filter_add_pattern(filter, "*.pal");
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
nst_palette_load(filename);
nst_palette_save();
g_free(filename);
conf.video_palette_mode = 2;
video_init();
}
gtk_widget_destroy(dialog);
}

View file

@ -1,18 +0,0 @@
#ifndef _GTKUI_DIALOGS_H_
#define _GTKUI_DIALOGS_H_
void gtkui_file_open();
void gtkui_state_save();
void gtkui_state_load();
void gtkui_screenshot_save();
void gtkui_movie_save();
void gtkui_movie_load();
void gtkui_movie_stop();
void gtkui_cheats_load();
void gtkui_palette_load();
#endif

View file

@ -1,388 +0,0 @@
/*
* Nestopia UE
*
* Copyright (C) 2012-2017 R. Danbrook
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include "nstcommon.h"
#include "input.h"
#include "video.h"
#include "audio.h"
#include "ini.h"
#include "sdlinput.h"
#include "gtkui.h"
#include "gtkui_input.h"
static ginputsettings_t inputconf;
static gkeys_t ui;
gpad_t pad[NUMGAMEPADS];
static char inputconfpath[256];
static bool confrunning = false;
static guint keyval = 0;
extern GtkWidget *configwindow;
extern nstpaths_t nstpaths;
extern Input::Controllers *cNstPads;
static int gtkui_input_config_match(void* user, const char* section, const char* name, const char* value) {
// Match values from input config file and populate live config
ginputsettings_t* pconfig = (ginputsettings_t*)user;
// User Interface
if (MATCH("ui", "qsave1")) { pconfig->qsave1 = strdup(value); }
else if (MATCH("ui", "qsave2")) { pconfig->qsave2 = strdup(value); }
else if (MATCH("ui", "qload1")) { pconfig->qload1 = strdup(value); }
else if (MATCH("ui", "qload2")) { pconfig->qload2 = strdup(value); }
else if (MATCH("ui", "screenshot")) { pconfig->screenshot = strdup(value); }
else if (MATCH("ui", "fdsflip")) { pconfig->fdsflip = strdup(value); }
else if (MATCH("ui", "fdsswitch")) { pconfig->fdsswitch = strdup(value); }
else if (MATCH("ui", "insertcoin1")) { pconfig->insertcoin1 = strdup(value); }
else if (MATCH("ui", "insertcoin2")) { pconfig->insertcoin2 = strdup(value); }
else if (MATCH("ui", "reset")) { pconfig->reset = strdup(value); }
else if (MATCH("ui", "ffspeed")) { pconfig->ffspeed = strdup(value); }
else if (MATCH("ui", "rwstart")) { pconfig->rwstart = strdup(value); }
else if (MATCH("ui", "rwstop")) { pconfig->rwstop = strdup(value); }
else if (MATCH("ui", "fullscreen")) { pconfig->fullscreen = strdup(value); }
else if (MATCH("ui", "filter")) { pconfig->filter = strdup(value); }
else if (MATCH("ui", "scalefactor")) { pconfig->scalefactor = strdup(value); }
// Player 1
else if (MATCH("gamepad1", "kb_u")) { pconfig->kb_p1u = strdup(value); }
else if (MATCH("gamepad1", "kb_d")) { pconfig->kb_p1d = strdup(value); }
else if (MATCH("gamepad1", "kb_l")) { pconfig->kb_p1l = strdup(value); }
else if (MATCH("gamepad1", "kb_r")) { pconfig->kb_p1r = strdup(value); }
else if (MATCH("gamepad1", "kb_select")) { pconfig->kb_p1select = strdup(value); }
else if (MATCH("gamepad1", "kb_start")) { pconfig->kb_p1start = strdup(value); }
else if (MATCH("gamepad1", "kb_a")) { pconfig->kb_p1a = strdup(value); }
else if (MATCH("gamepad1", "kb_b")) { pconfig->kb_p1b = strdup(value); }
else if (MATCH("gamepad1", "kb_ta")) { pconfig->kb_p1ta = strdup(value); }
else if (MATCH("gamepad1", "kb_tb")) { pconfig->kb_p1tb = strdup(value); }
// Player 2
else if (MATCH("gamepad2", "kb_u")) { pconfig->kb_p2u = strdup(value); }
else if (MATCH("gamepad2", "kb_d")) { pconfig->kb_p2d = strdup(value); }
else if (MATCH("gamepad2", "kb_l")) { pconfig->kb_p2l = strdup(value); }
else if (MATCH("gamepad2", "kb_r")) { pconfig->kb_p2r = strdup(value); }
else if (MATCH("gamepad2", "kb_select")) { pconfig->kb_p2select = strdup(value); }
else if (MATCH("gamepad2", "kb_start")) { pconfig->kb_p2start = strdup(value); }
else if (MATCH("gamepad2", "kb_a")) { pconfig->kb_p2a = strdup(value); }
else if (MATCH("gamepad2", "kb_b")) { pconfig->kb_p2b = strdup(value); }
else if (MATCH("gamepad2", "kb_ta")) { pconfig->kb_p2ta = strdup(value); }
else if (MATCH("gamepad2", "kb_tb")) { pconfig->kb_p2tb = strdup(value); }
else { return 0; }
return 1;
}
void gtkui_input_set_default() {
// Set the default input for GTK
// Gamepads
pad[0].u = GDK_KEY_Up;
pad[0].d = GDK_KEY_Down;
pad[0].l = GDK_KEY_Left;
pad[0].r = GDK_KEY_Right;
pad[0].select = GDK_KEY_Shift_R;
pad[0].start = GDK_KEY_Return;
pad[0].a = GDK_KEY_z;
pad[0].b = GDK_KEY_a;
pad[0].ta = GDK_KEY_x;
pad[0].tb = GDK_KEY_s;
pad[1].u = GDK_KEY_i;
pad[1].d = GDK_KEY_k;
pad[1].l = GDK_KEY_j;
pad[1].r = GDK_KEY_l;
pad[1].select = GDK_KEY_Shift_L;
pad[1].start = GDK_KEY_Control_L;
pad[1].a = GDK_KEY_m;
pad[1].b = GDK_KEY_n;
pad[1].ta = GDK_KEY_b;
pad[1].tb = GDK_KEY_v;
// User Interface
ui.qsave1 = GDK_KEY_F5;
ui.qsave2 = GDK_KEY_F6;
ui.qload1 = GDK_KEY_F7;
ui.qload2 = GDK_KEY_F8;
ui.screenshot = GDK_KEY_F9;
ui.fdsflip = GDK_KEY_F3;
ui.fdsswitch = GDK_KEY_F4;
ui.insertcoin1 = GDK_KEY_F1;
ui.insertcoin2 = GDK_KEY_F2;
ui.reset = GDK_KEY_F12;
ui.ffspeed = GDK_KEY_grave;
ui.rwstart = GDK_KEY_BackSpace;
ui.rwstop = GDK_KEY_backslash;
ui.fullscreen = GDK_KEY_f;
ui.filter = GDK_KEY_t;
ui.scalefactor = GDK_KEY_g;
}
void gtkui_input_config_read() {
// Read the input config file
snprintf(inputconfpath, sizeof(inputconfpath), "%sgtkinput.conf", nstpaths.nstconfdir);
if (ini_parse(inputconfpath, gtkui_input_config_match, &inputconf) < 0) {
fprintf(stderr, "Failed to load input config file %s: Using defaults.\n", inputconfpath);
}
else {
// Map the input settings from the config file
// User Interface
ui.qsave1 = gdk_keyval_from_name(inputconf.qsave1);
ui.qsave2 = gdk_keyval_from_name(inputconf.qsave2);
ui.qload1 = gdk_keyval_from_name(inputconf.qload1);
ui.qload2 = gdk_keyval_from_name(inputconf.qload2);
ui.screenshot = gdk_keyval_from_name(inputconf.screenshot);
ui.fdsflip = gdk_keyval_from_name(inputconf.fdsflip);
ui.fdsswitch = gdk_keyval_from_name(inputconf.fdsswitch);
ui.insertcoin1 = gdk_keyval_from_name(inputconf.insertcoin1);
ui.insertcoin2 = gdk_keyval_from_name(inputconf.insertcoin2);
ui.reset = gdk_keyval_from_name(inputconf.reset);
ui.ffspeed = gdk_keyval_from_name(inputconf.ffspeed);
ui.rwstart = gdk_keyval_from_name(inputconf.rwstart);
ui.rwstop = gdk_keyval_from_name(inputconf.rwstop);
ui.fullscreen = gdk_keyval_from_name(inputconf.fullscreen);
ui.filter = gdk_keyval_from_name(inputconf.filter);
ui.scalefactor = gdk_keyval_from_name(inputconf.scalefactor);
// Player 1
pad[0].u = gdk_keyval_from_name(inputconf.kb_p1u);
pad[0].d = gdk_keyval_from_name(inputconf.kb_p1d);
pad[0].l = gdk_keyval_from_name(inputconf.kb_p1l);
pad[0].r = gdk_keyval_from_name(inputconf.kb_p1r);
pad[0].select = gdk_keyval_from_name(inputconf.kb_p1select);
pad[0].start = gdk_keyval_from_name(inputconf.kb_p1start);
pad[0].a = gdk_keyval_from_name(inputconf.kb_p1a);
pad[0].b = gdk_keyval_from_name(inputconf.kb_p1b);
pad[0].ta = gdk_keyval_from_name(inputconf.kb_p1ta);
pad[0].tb = gdk_keyval_from_name(inputconf.kb_p1tb);
// Player 2
pad[1].u = gdk_keyval_from_name(inputconf.kb_p2u);
pad[1].d = gdk_keyval_from_name(inputconf.kb_p2d);
pad[1].l = gdk_keyval_from_name(inputconf.kb_p2l);
pad[1].r = gdk_keyval_from_name(inputconf.kb_p2r);
pad[1].select = gdk_keyval_from_name(inputconf.kb_p2select);
pad[1].start = gdk_keyval_from_name(inputconf.kb_p2start);
pad[1].a = gdk_keyval_from_name(inputconf.kb_p2a);
pad[1].b = gdk_keyval_from_name(inputconf.kb_p2b);
pad[1].ta = gdk_keyval_from_name(inputconf.kb_p2ta);
pad[1].tb = gdk_keyval_from_name(inputconf.kb_p2tb);
}
}
void gtkui_input_config_write() {
// Write out the input configuration file
FILE *fp = fopen(inputconfpath, "w");
if (fp != NULL) {
fprintf(fp, "; Nestopia UE GTK Input Configuration File\n\n");
fprintf(fp, "; Values for keyboard input are these values with the GDK_KEY_ prefix removed:\n; https://git.gnome.org/browse/gtk+/plain/gdk/gdkkeysyms.h\n\n");
fprintf(fp, "[ui]\n");
fprintf(fp, "qsave1=%s\n", gdk_keyval_name(ui.qsave1));
fprintf(fp, "qsave2=%s\n", gdk_keyval_name(ui.qsave2));
fprintf(fp, "qload1=%s\n", gdk_keyval_name(ui.qload1));
fprintf(fp, "qload2=%s\n", gdk_keyval_name(ui.qload2));
fprintf(fp, "screenshot=%s\n", gdk_keyval_name(ui.screenshot));
fprintf(fp, "fdsflip=%s\n", gdk_keyval_name(ui.fdsflip));
fprintf(fp, "fdsswitch=%s\n", gdk_keyval_name(ui.fdsswitch));
fprintf(fp, "insertcoin1=%s\n", gdk_keyval_name(ui.insertcoin1));
fprintf(fp, "insertcoin2=%s\n", gdk_keyval_name(ui.insertcoin2));
fprintf(fp, "reset=%s\n", gdk_keyval_name(ui.reset));
fprintf(fp, "ffspeed=%s\n", gdk_keyval_name(ui.ffspeed));
fprintf(fp, "rwstart=%s\n", gdk_keyval_name(ui.rwstart));
fprintf(fp, "rwstop=%s\n", gdk_keyval_name(ui.rwstop));
fprintf(fp, "fullscreen=%s\n", gdk_keyval_name(ui.fullscreen));
fprintf(fp, "filter=%s\n", gdk_keyval_name(ui.filter));
fprintf(fp, "scalefactor=%s\n", gdk_keyval_name(ui.scalefactor));
fprintf(fp, "\n"); // End of Section
fprintf(fp, "[gamepad1]\n");
fprintf(fp, "kb_u=%s\n", gdk_keyval_name(pad[0].u));
fprintf(fp, "kb_d=%s\n", gdk_keyval_name(pad[0].d));
fprintf(fp, "kb_l=%s\n", gdk_keyval_name(pad[0].l));
fprintf(fp, "kb_r=%s\n", gdk_keyval_name(pad[0].r));
fprintf(fp, "kb_select=%s\n", gdk_keyval_name(pad[0].select));
fprintf(fp, "kb_start=%s\n", gdk_keyval_name(pad[0].start));
fprintf(fp, "kb_a=%s\n", gdk_keyval_name(pad[0].a));
fprintf(fp, "kb_b=%s\n", gdk_keyval_name(pad[0].b));
fprintf(fp, "kb_ta=%s\n", gdk_keyval_name(pad[0].ta));
fprintf(fp, "kb_tb=%s\n", gdk_keyval_name(pad[0].tb));
fprintf(fp, "\n"); // End of Section
fprintf(fp, "[gamepad2]\n");
fprintf(fp, "kb_u=%s\n", gdk_keyval_name(pad[1].u));
fprintf(fp, "kb_d=%s\n", gdk_keyval_name(pad[1].d));
fprintf(fp, "kb_l=%s\n", gdk_keyval_name(pad[1].l));
fprintf(fp, "kb_r=%s\n", gdk_keyval_name(pad[1].r));
fprintf(fp, "kb_select=%s\n", gdk_keyval_name(pad[1].select));
fprintf(fp, "kb_start=%s\n", gdk_keyval_name(pad[1].start));
fprintf(fp, "kb_a=%s\n", gdk_keyval_name(pad[1].a));
fprintf(fp, "kb_b=%s\n", gdk_keyval_name(pad[1].b));
fprintf(fp, "kb_ta=%s\n", gdk_keyval_name(pad[1].ta));
fprintf(fp, "kb_tb=%s\n", gdk_keyval_name(pad[1].tb));
fclose(fp);
}
}
void gtkui_input_config_process_key(GtkWidget *widget, GdkEventKey *event, gpointer userdata) {
keyval = event->keyval;
if (keyval == GDK_KEY_Escape || keyval == GDK_KEY_space) { keyval = 0; }
confrunning = false;
}
void gtkui_input_config_signals_init() {
// Key translation
g_signal_connect(G_OBJECT(configwindow), "key-press-event",
G_CALLBACK(gtkui_input_config_process_key), NULL);
g_signal_connect(G_OBJECT(configwindow), "key-release-event",
G_CALLBACK(gtkui_input_config_process_key), NULL);
}
void gtkui_input_config_signals_deinit() {
// Key translation
g_signal_connect(G_OBJECT(configwindow), "key-press-event",
gtkui_input_null, NULL);
g_signal_connect(G_OBJECT(configwindow), "key-release-event",
gtkui_input_null, NULL);
}
void gtkui_input_config_key(int pnum, int bnum) {
// Connect signals
gtkui_input_config_signals_init();
// Wait for input
confrunning = true;
while (confrunning) { gtk_main_iteration(); }
// Set the keyval for the input item
if (keyval != 0) {
switch (bnum) {
case 0: pad[pnum].u = keyval; break;
case 1: pad[pnum].d = keyval; break;
case 2: pad[pnum].l = keyval; break;
case 3: pad[pnum].r = keyval; break;
case 4: pad[pnum].select = keyval; break;
case 5: pad[pnum].start = keyval; break;
case 6: pad[pnum].a = keyval; break;
case 7: pad[pnum].b = keyval; break;
case 8: pad[pnum].ta = keyval; break;
case 9: pad[pnum].tb = keyval; break;
default: break;
}
}
// Disconnect signals
gtkui_input_config_signals_deinit();
}
void gtkui_input_config_js(int pnum, int bnum) {
// Wait for input
nstsdl_input_conf_button(pnum, bnum);
}
void gtkui_input_null() {}
int gtkui_input_process_key(GtkWidget *widget, GdkEventKey *event, gpointer userdata) {
// Process input from GDK events
nesinput_t input;
input.nescode = input.player = input.pressed = input.turboa = input.turbob = 0;
for (int i = 0; i < NUMGAMEPADS; i++) {
if (event->keyval == pad[i].u) { input.player = i; input.nescode = Input::Controllers::Pad::UP; }
else if (event->keyval == pad[i].d) { input.player = i; input.nescode = Input::Controllers::Pad::DOWN; }
else if (event->keyval == pad[i].l) { input.player = i; input.nescode = Input::Controllers::Pad::LEFT; }
else if (event->keyval == pad[i].r) { input.player = i; input.nescode = Input::Controllers::Pad::RIGHT; }
else if (event->keyval == pad[i].select) { input.player = i; input.nescode = Input::Controllers::Pad::SELECT; }
else if (event->keyval == pad[i].start) { input.player = i; input.nescode = Input::Controllers::Pad::START; }
else if (event->keyval == pad[i].a) { input.player = i; input.nescode = Input::Controllers::Pad::A; }
else if (event->keyval == pad[i].b) { input.player = i; input.nescode = Input::Controllers::Pad::B; }
else if (event->keyval == pad[i].ta) { input.player = i; input.turboa = 1; input.nescode = Input::Controllers::Pad::A; }
else if (event->keyval == pad[i].tb) { input.player = i; input.turbob = 1; input.nescode = Input::Controllers::Pad::B; }
}
switch(event->type) {
case GDK_KEY_PRESS:
//printf("Keyval: %x\n", event->keyval);
//printf("Keyval: %s\n", gdk_keyval_name(event->keyval));
input.pressed = 1;
if (event->keyval == ui.qsave1) { nst_state_quicksave(0); }
else if (event->keyval == ui.qsave2) { nst_state_quicksave(1); }
else if (event->keyval == ui.qload1) { nst_state_quickload(0); }
else if (event->keyval == ui.qload2) { nst_state_quickload(1); }
else if (event->keyval == ui.screenshot) { video_screenshot(NULL); }
else if (event->keyval == ui.fdsflip) { nst_fds_flip(); }
else if (event->keyval == ui.fdsswitch) { nst_fds_switch(); }
else if (event->keyval == ui.insertcoin1) { cNstPads->vsSystem.insertCoin |= Input::Controllers::VsSystem::COIN_1; }
else if (event->keyval == ui.insertcoin2) { cNstPads->vsSystem.insertCoin |= Input::Controllers::VsSystem::COIN_2; }
else if (event->keyval == ui.reset) { nst_reset(0); }
else if (event->keyval == ui.ffspeed) { nst_timing_set_ffspeed(); }
else if (event->keyval == ui.rwstart) { nst_set_rewind(0); }
else if (event->keyval == ui.rwstop) { nst_set_rewind(1); }
else if (event->keyval == ui.filter) { gtkui_video_toggle_filter(); }
else if (event->keyval == ui.scalefactor) { gtkui_video_toggle_scale(); }
else if (event->keyval == gdk_keyval_from_name("space")) { cNstPads->pad[1].mic = 0x04; }
break;
case GDK_KEY_RELEASE:
input.pressed = 0;
if (event->keyval == ui.ffspeed) { nst_timing_set_default(); }
else if (event->keyval == ui.fullscreen) { gtkui_video_toggle_fullscreen(); }
else if (event->keyval == gdk_keyval_from_name("space")) { cNstPads->pad[1].mic = 0x00; }
break;
default: break;
}
nst_input_inject(cNstPads, input);
return TRUE;
}
int gtkui_input_process_key_nsf(GtkWidget *widget, GdkEventKey *event, gpointer userdata) {
if (event->type == GDK_KEY_RELEASE) {
if (event->keyval == GDK_KEY_Up) { nst_nsf_play(); }
if (event->keyval == GDK_KEY_Down) { nst_nsf_stop(); }
if (event->keyval == GDK_KEY_Left) { nst_nsf_prev(); }
if (event->keyval == GDK_KEY_Right) { nst_nsf_next(); }
}
return TRUE;
}
int gtkui_input_process_mouse(GtkWidget *widget, GdkEventButton *event, gpointer userdata) {
switch(event->type) {
case GDK_BUTTON_PRESS:
nst_input_inject_mouse(cNstPads, event->button, 1, (int)event->x, (int)event->y);
break;
case GDK_BUTTON_RELEASE:
nst_input_inject_mouse(cNstPads, event->button, 0, (int)event->x, (int)event->y);
break;
default: break;
}
return TRUE;
}

View file

@ -1,99 +0,0 @@
#ifndef _GTKUI_INPUT_H_
#define _GTKUI_INPUT_H_
typedef struct {
guint u;
guint d;
guint l;
guint r;
guint select;
guint start;
guint a;
guint b;
guint ta;
guint tb;
} gpad_t;
typedef struct {
guint qsave1;
guint qsave2;
guint qload1;
guint qload2;
guint screenshot;
guint fdsflip;
guint fdsswitch;
guint insertcoin1;
guint insertcoin2;
guint reset;
guint ffspeed;
guint rwstart;
guint rwstop;
guint fullscreen;
guint filter;
guint scalefactor;
} gkeys_t;
typedef struct {
// User Interface
char *qsave1;
char *qsave2;
char *qload1;
char *qload2;
char *screenshot;
char *fdsflip;
char *fdsswitch;
char *insertcoin1;
char *insertcoin2;
char *reset;
char *ffspeed;
char *rwstart;
char *rwstop;
char *fullscreen;
char *filter;
char *scalefactor;
// Player 1
char *kb_p1u;
char *kb_p1d;
char *kb_p1l;
char *kb_p1r;
char *kb_p1select;
char *kb_p1start;
char *kb_p1a;
char *kb_p1b;
char *kb_p1ta;
char *kb_p1tb;
// Player 2
char *kb_p2u;
char *kb_p2d;
char *kb_p2l;
char *kb_p2r;
char *kb_p2select;
char *kb_p2start;
char *kb_p2a;
char *kb_p2b;
char *kb_p2ta;
char *kb_p2tb;
} ginputsettings_t;
void gtkui_input_set_default();
void gtkui_input_config_read();
void gtkui_input_config_write();
void gtkui_input_config_process_key(GtkWidget *widget, GdkEventKey *event, gpointer userdata);
void gtkui_input_config_signals_init();
void gtkui_input_config_signals_deinit();
void gtkui_input_config_key(int pnum, int bnum);
void gtkui_input_config_js(int pnum, int bnum);
void gtkui_input_null();
int gtkui_input_process_key(GtkWidget *widget, GdkEventKey *event, gpointer userdata);
int gtkui_input_process_key_nsf(GtkWidget *widget, GdkEventKey *event, gpointer userdata);
int gtkui_input_process_mouse(GtkWidget *widget, GdkEventButton *event, gpointer userdata);
#endif