Compare commits

...

12 commits

16 changed files with 1052 additions and 536 deletions

View file

@ -721,16 +721,14 @@ nestopia_SOURCES += \
source/fltkui/audiomanager.h \
source/fltkui/chtmanager.cpp \
source/fltkui/chtmanager.h \
source/fltkui/cli.cpp \
source/fltkui/cli.h \
source/fltkui/ini.h \
source/fltkui/inputmanager.cpp \
source/fltkui/inputmanager.h \
source/fltkui/font.h \
source/fltkui/lodepng.cpp \
source/fltkui/lodepng.h \
source/fltkui/logdriver.cpp \
source/fltkui/logdriver.h \
source/fltkui/png.cpp \
source/fltkui/png.h \
source/fltkui/jgmanager.cpp \
source/fltkui/jgmanager.h \
source/fltkui/setmanager.cpp \

View file

@ -1,168 +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 <stdlib.h>
#include <getopt.h>
#include "cli.h"
void cli_error(const char *message) {
cli_show_usage();
fprintf(stderr, "%s\n", message);
exit(1);
}
void cli_show_usage() {
printf("Usage: nestopia [options] [FILE]\n");
printf("\nOptions:\n");
printf(" -f, --fullscreen Fullscreen mode\n");
printf(" -w, --window Window mode\n\n");
printf(" -l, --filter Video Filter\n");
printf(" (0=None, 1=NTSC, 2=xBR, 3=HqX, 4=2xSaI, 5=ScaleX)\n\n");
printf(" -m, --maskoverscan Mask overscan areas\n");
printf(" -n, --no-maskoverscan Disable overscan masking\n\n");
printf(" -o, --stretchfs Stretch to native resolution in fullscreen mode\n");
printf(" -p, --preserveaspect Preserve aspect ratio in fullscreen mode\n\n");
printf(" -s, --scalefactor Video scale factor (1-4)\n\n");
printf(" -t, --tvaspect TV aspect ratio\n");
printf(" -r, --no-tvaspect Regular aspect ratio\n\n");
printf(" -u, --unlimitedsprites Remove sprite limit\n");
printf(" -q, --spritelimit Enable sprite limit\n\n");
printf(" -v, --version Show version information\n\n");
printf("More options can be set in the configuration file.\n");
printf("Options are saved, and do not need to be set on future invocations.\n\n");
}
void cli_show_version() {
printf("Nestopia UE 1.52.1\n");
}
void cli_handle_command(int argc, char *argv[]) {
int c;
int optint;
while (1) {
static struct option long_options[] = {
{"disablegui", no_argument, 0, 'd'},
{"enablegui", no_argument, 0, 'e'},
{"fullscreen", no_argument, 0, 'f'},
{"window", no_argument, 0, 'w'},
{"help", no_argument, 0, 'h'},
{"filter", required_argument, 0, 'l'},
{"maskoverscan", no_argument, 0, 'm'},
{"no-maskoverscan", no_argument, 0, 'n'},
{"stretchfs", no_argument, 0, 'o'},
{"preserveaspect", no_argument, 0, 'p'},
{"scalefactor", required_argument, 0, 's'},
{"tvaspect", no_argument, 0, 't'},
{"no-tvaspect", no_argument, 0, 'r'},
{"unlimitedsprites", no_argument, 0, 'u'},
{"spritelimit", no_argument, 0, 'q'},
{"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) {
//conf.video_filter = optint;
}
else {
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) {
//conf.video_scale_factor = optint;
}
else {
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,4 +0,0 @@
void cli_error(const char *message);
void cli_show_usage();
void cli_show_version();
void cli_handle_command(int argc, char *argv[]);

View file

@ -21,7 +21,7 @@
*/
#include <algorithm>
#include <cstdio>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <iostream>
@ -100,10 +100,7 @@ Fl_Menu_Item menutable[] = {
{"Slot 4", 0, FltkUi::state_qsave, (void*)"4", FL_MENU_INACTIVE},
{0},
{"Open Palette...", 0, FltkUi::palette_open, 0, FL_MENU_DIVIDER},
//{"Screenshot...", 0, fltkui_screenshot, 0, FL_MENU_DIVIDER|FL_MENU_INACTIVE},
/*{"Load Movie...", 0, fltkui_movie_load, 0, FL_MENU_INACTIVE},
{"Record Movie...", 0, fltkui_movie_save, 0, FL_MENU_INACTIVE},
{"Stop Movie", 0, fltkui_movie_stop, 0, FL_MENU_DIVIDER|FL_MENU_INACTIVE},*/
{"Screenshot...", 0, FltkUi::screenshot_save, 0, FL_MENU_DIVIDER|FL_MENU_INACTIVE},
{"&Quit", FL_ALT + 'q', FltkUi::quit, 0, 0},
{0}, // End File
{"&Emulator", FL_ALT + 'e', 0, 0, FL_SUBMENU},
@ -122,6 +119,17 @@ Fl_Menu_Item menutable[] = {
{0} // End Menu
};
Fl_Menu_Item *get_menuitem(std::string label) {
Fl_Menu_Item *m = menutable->first();
for (int i = 0; i < menutable->size(); ++i) {
if (m->label() != nullptr && std::string(m->label()) == label) {
return m;
}
++m;
}
return nullptr;
}
int get_refreshrate(void) {
// Get the screen refresh rate using an SDL window
int refresh = 60;
@ -222,55 +230,39 @@ void FltkUi::rom_open(Fl_Widget *w, void *data) {
}
}
/*static void fltkui_movie_load(Fl_Widget* w, void* userdata) {
// Create native chooser
if (!jgm->is_loaded()) {
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:
LogDriver::log(LogLevel::Error, std::string(fc.errmsg()));
break;
case 1: break; // Cancel
default:
if (fc.filename()) {
//nst_movie_load(fc.filename());
}
break;
void FltkUi::screenshot(std::string filename) {
if (filename.empty()) {
auto now = std::chrono::system_clock::now();
auto epoch = std::chrono::system_clock::from_time_t(0);
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>
(now.time_since_epoch()).count();
std::string msecs = std::to_string(duration);
filename = jgm->get_basepath() + "/screenshots/" + msecs + ".png";
}
videomgr->screenshot(filename);
LogDriver::log(LogLevel::OSD, "Screenshot Saved");
}
static void fltkui_movie_save(Fl_Widget* w, void* userdata) {
void FltkUi::screenshot_save(Fl_Widget *w, void *data) {
// Create native chooser
if (!jgm->is_loaded()) {
return;
}
Fl_Native_File_Chooser fc;
fc.title("Save Movie");
fc.title("Save Screenshot");
fc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
//fc.directory((const char*)nstpaths.nstdir);
fc.filter("Nestopia Moviess\t*.nsv");
fc.filter("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; }
if (fc.show()) {
return;
}
//nst_movie_save(fc.filename());
screenshot(fc.filename());
}
static void fltkui_movie_stop(Fl_Widget* w, void* userdata) {
//nst_movie_stop();
}*/
void FltkUi::state_load(Fl_Widget *w, void *userdata) {
// Create native chooser
if (!jgm->is_loaded()) {
@ -319,26 +311,6 @@ void FltkUi::state_save(Fl_Widget *w, void *data) {
jgm->state_save(statefile);
}
/*static void fltkui_screenshot(Fl_Widget* w, void* userdata) {
// Create native chooser
if (!jgm->is_loaded()) {
return;
}
Fl_Native_File_Chooser fc;
fc.title("Save Screenshot");
fc.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
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());
}*/
void FltkUi::palette_open(Fl_Widget *w, void *data) {
// Create native chooser
Fl_Native_File_Chooser fc;
@ -379,17 +351,26 @@ void FltkUi::state_qsave(Fl_Widget *w, void *data) {
}
void FltkUi::pause(Fl_Widget *w, void *data) {
Fl_Menu_Item* m = nullptr;
m = w ? const_cast<Fl_Menu_Item*>(((Fl_Menu_Bar*)w)->mvalue()) :
get_menuitem(paused ? "Play" : "Pause");
if (m == nullptr) {
LogDriver::log(LogLevel::Warn, "Menu item does not exist");
return;
}
paused ^= 1;
if (paused) {
audiomgr->pause();
Fl_Menu_Item* m = const_cast<Fl_Menu_Item*>(((Fl_Menu_Bar*)w)->mvalue());
m->label("Play");
}
else {
audiomgr->unpause();
Fl_Menu_Item* m = const_cast<Fl_Menu_Item*>(((Fl_Menu_Bar*)w)->mvalue());
m->label("Pause");
}
m->label(paused ? "Play" : "Pause");
}
void FltkUi::reset(Fl_Widget *w, void *data) {
@ -414,6 +395,7 @@ void NstGlArea::resize(int x, int y, int w, int h) {
}
void FltkUi::rehash() {
LogDriver::set_level(setmgr->get_setting("l_loglevel")->val);
videomgr->rehash(true);
audiomgr->rehash();
}
@ -597,7 +579,7 @@ void FltkUi::nstwin_open(const char *name) {
chtwin->populate();
// Settings Window
setwin = new NstSettingsWindow(500, 500, "Settings", *jgm, *setmgr, *inputmgr);
setwin = new NstSettingsWindow(500, 550, "Settings", *jgm, *setmgr, *inputmgr);
// Main Window
nstwin = new NstWindow(rw, rh + UI_MBARHEIGHT, name);
@ -631,6 +613,19 @@ void FltkUi::set_ffspeed(bool on) {
}
int main(int argc, char *argv[]) {
// Parse command line arguments
std::string filename{};
std::vector<std::string> flags{};
for (int i = 1; i < argc; ++i) {
if (filename.empty() && argv[i][0] != '-') {
// The first non-flag argument is considered the filename
filename = std::string{argv[i]};
}
else if (argv[i][0] == '-') {
flags.push_back(std::string{argv[i]});
}
}
// Set default config options
setmgr = new SettingManager();
@ -646,6 +641,7 @@ int main(int argc, char *argv[]) {
// Read frontend and emulator settings
setmgr->read(*jgm);
LogDriver::set_level(setmgr->get_setting("l_loglevel")->val);
// Bring up Audio/Video managers
audiomgr = new AudioManager(*jgm, *setmgr);
@ -655,17 +651,17 @@ int main(int argc, char *argv[]) {
chtmgr = new CheatManager(*jgm);
// Load a rom from the command line
if (argc > 1 && argv[argc - 1][0] != '-') {
FltkUi::load_file(argv[argc - 1]);
if (!filename.empty()) {
FltkUi::load_file(filename.c_str());
if (jgm->is_loaded()) {
jg_setup_audio();
jg_setup_video();
inputmgr->reassign();
video_fullscreen = setmgr->get_setting("v_fullscreen")->val ||
std::find(flags.begin(), flags.end(), "-f") != flags.end() ||
std::find(flags.begin(), flags.end(), "--fullscreen") != flags.end();
}
}
else if (video_fullscreen) {
video_fullscreen = 0;
}
FltkUi::nstwin_open(argv[0]);

View file

@ -1,5 +1,7 @@
#pragma once
#include <string>
constexpr Fl_Color NstGreen = 0x255f6500;
constexpr Fl_Color NstPurple = 0x5f578700;
constexpr Fl_Color NstRed = 0xb51e2c00;
@ -47,6 +49,8 @@ public:
static void about(Fl_Widget *w = nullptr, void *data = nullptr);
static void about_close(Fl_Widget *w = nullptr, void *data = nullptr);
static void rom_open(Fl_Widget *w = nullptr, void *data = nullptr);
static void screenshot(std::string filename = "");
static void screenshot_save(Fl_Widget *w = nullptr, void *data = nullptr);
static void load_file(const char *filename);
static void fds_next(Fl_Widget *w = nullptr, void *data = nullptr);
static void fds_insert(Fl_Widget *w = nullptr, void *data = nullptr);

View file

@ -24,6 +24,7 @@
#include <cstring>
#include <iostream>
#include <sstream>
#include <unordered_map>
#include <FL/Fl.H>
#include <FL/Fl_Double_Window.H>
@ -43,9 +44,91 @@
namespace {
constexpr int UI_TABHEIGHT = 450;
constexpr int UI_TABHEIGHT = 500;
constexpr int UI_TABWIDTH = 480;
constexpr unsigned UI_SETTINGS_PER_COL = 8;
constexpr unsigned UI_SETTINGS_PER_COL = 9;
std::unordered_map<int, std::string> keycodes = { //FL_Button ??
{ ' ', "Space" },
{ FL_BackSpace, "Backspace" },
{ FL_Tab, "Tab" },
{ FL_Iso_Key,"ISO Key" },
{ FL_Enter, "Enter" },
{ FL_Pause, "Pause" },
{ FL_Scroll_Lock, "Scroll Lock"},
{ FL_Escape, "Escape" },
{ FL_Kana, "Kana" },
{ FL_Eisu, "Eisu" },
{ FL_Yen, "Yen" },
{ FL_JIS_Underscore, "Underscore" },
{ FL_Home, "Home" },
{ FL_Left, "Left" },
{ FL_Up, "Up" },
{ FL_Right, "Right" },
{ FL_Down, "Down" },
{ FL_Page_Up, "Page Up" },
{ FL_Page_Down, "Page Down" },
{ FL_End, "End" },
{ FL_Print, "Print" },
{ FL_Insert, "Insert" },
{ FL_Menu, "Menu" },
{ FL_Help, "Help" },
{ FL_Num_Lock, "Num Lock" },
{ FL_KP + 0x2a, "KP *" },
{ FL_KP + 0x2b, "KP +" },
{ FL_KP + 0x2d, "KP -" },
{ FL_KP + 0x2f, "KP /" },
{ FL_KP + 0x30, "KP 0" },
{ FL_KP + 0x31, "KP 1" },
{ FL_KP + 0x32, "KP 2" },
{ FL_KP + 0x33, "KP 3" },
{ FL_KP + 0x34, "KP 4" },
{ FL_KP + 0x35, "KP 5" },
{ FL_KP + 0x36, "KP 6" },
{ FL_KP + 0x37, "KP 7" },
{ FL_KP + 0x38, "KP 8" },
{ FL_KP + 0x39, "KP 9" },
{ FL_KP_Enter, "KP Enter"},
{ FL_F + 1, "F1" },
{ FL_F + 2, "F2" },
{ FL_F + 3, "F3" },
{ FL_F + 4, "F4" },
{ FL_F + 5, "F5" },
{ FL_F + 6, "F6" },
{ FL_F + 7, "F7" },
{ FL_F + 8, "F8" },
{ FL_F + 9, "F9" },
{ FL_F + 10, "F10" },
{ FL_F + 11, "F11" },
{ FL_F + 12, "F12" },
{ FL_Shift_L, "Shift L" },
{ FL_Shift_R, "Shift R" },
{ FL_Control_L, "Control L" },
{ FL_Control_R, "Control R" },
{ FL_Caps_Lock, "Caps Lock" },
{ FL_Meta_L, "Meta L" },
{ FL_Meta_R, "Meta R" },
{ FL_Alt_L, "Alt L" },
{ FL_Alt_R, "Alt R" },
{ FL_Delete, "Delete" },
//{ FL_Alt_Gr, "Alt Gr" },
{ FL_Volume_Down, "Volume Down" },
{ FL_Volume_Mute, "Volume Mute" },
{ FL_Volume_Up, "Volume Up" },
{ FL_Media_Play, "Play" },
{ FL_Media_Stop," Stop" },
{ FL_Media_Prev, "Prev" },
{ FL_Media_Next, "Next" },
{ FL_Home_Page, "Home Page" },
{ FL_Mail, "Mail" },
{ FL_Search, "Search" },
{ FL_Back, "Back" },
{ FL_Forward, "Forward" },
{ FL_Stop, "Stop" },
{ FL_Refresh, "Refresh" },
{ FL_Sleep, "Sleep" },
{ FL_Favorites, "Favorites" }
};
NstSettingsWindow *win = nullptr;
@ -299,7 +382,7 @@ void NstSettingsWindow::populate_input() {
iselect->align(FL_ALIGN_TOP_LEFT);
itable = new InputTable(inputmgr, input_info,
200, 110, 275, 280);
200, 110, 275, 330);
itable->set_devicenum(0);
itable->cols(3);
itable->col_width(0, 115);
@ -319,7 +402,7 @@ void NstSettingsWindow::populate_input() {
iselect->value(0);
itable->rows(input_info[0].numaxes + input_info[0].numbuttons);
msgbox = new Fl_Box(200, 400, 240, UI_ELEMHEIGHT);
msgbox = new Fl_Box(200, 450, 240, UI_ELEMHEIGHT);
msgbox->label("Press the desired key, ESC to clear");
msgbox->hide();
}
@ -341,7 +424,18 @@ void InputTable::draw_cell(TableContext context, int r, int c, int x, int y, int
text = defname;
}
else if (c == 1) {
text = inputmgr.get_inputdef(input_info[devicenum].name, defname).c_str();
std::string key = inputmgr.get_inputdef(input_info[devicenum].name, defname);
int keynum = key.empty() ? 0 : std::stoi(key);
if (keycodes.count(keynum)) {
text = keycodes[keynum].c_str();
}
else if (keynum >= 33 && keynum <= 126) {
std::string str = std::string(1, keynum);
text = str.c_str();
}
else {
text = key.c_str();
}
}
else if (c == 2) {
text = inputmgr.get_inputdef(std::string(input_info[devicenum].name) + "j", defname).c_str();

View file

@ -124,7 +124,7 @@ void InputManager::assign() {
}
// Set up UI definitiions
int ui_defaults[NDEFS_UI] = {
int ui_defaults[NDEFS_UI - 1] = {
0xffbd + 1, 0xffbd + 2, 0xffbd + 3, 0xffbd + 4, 0xffbd + 5,
0xffbd + 6, 0xffbd + 7, 0xffbd + 8, 'f', 'p', '`', 0xffbd + 9
};
@ -142,6 +142,12 @@ void InputManager::assign() {
}
}
// If "Quit" was defined, apply the definition
std::string val = setmgr.get_input("ui", uiinfo.defs[NDEFS_UI - 1]);
if (!val.empty()) {
kbmap[std::stoi(val)] = &uistate.button[NDEFS_UI - 1];
}
remap_js();
}

View file

@ -115,8 +115,9 @@ void JGManager::set_paths() {
// Create the save path, which includes creating the base path
std::filesystem::create_directories(savepath);
// Create a path for states (Not part of the JG API)
// Create paths for states and screenshots (Not part of the JG API)
std::filesystem::create_directories(basepath + "/state");
std::filesystem::create_directories(basepath + "/screenshots");
// If the binary is run from the source directory, core asset path is PWD
if (std::filesystem::exists(std::filesystem::path{"NstDatabase.xml"})) {

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
/*
LodePNG version 20210627
LodePNG version 20230410
Copyright (c) 2005-2021 Lode Vandevenne
Copyright (c) 2005-2023 Lode Vandevenne
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@ -35,43 +35,50 @@ The following #defines are used to create code sections. They can be disabled
to disable code sections, which can give faster compile time and smaller binary.
The "NO_COMPILE" defines are designed to be used to pass as defines to the
compiler command to disable them without modifying this header, e.g.
-DLODEPNG_NO_COMPILE_ZLIB for gcc.
In addition to those below, you can also define LODEPNG_NO_COMPILE_CRC to
allow implementing a custom lodepng_crc32.
-DLODEPNG_NO_COMPILE_ZLIB for gcc or clang.
*/
/*deflate & zlib. If disabled, you must specify alternative zlib functions in
the custom_zlib field of the compress and decompress settings*/
#ifndef LODEPNG_NO_COMPILE_ZLIB
/*pass -DLODEPNG_NO_COMPILE_ZLIB to the compiler to disable this, or comment out LODEPNG_COMPILE_ZLIB below*/
#define LODEPNG_COMPILE_ZLIB
#endif
/*png encoder and png decoder*/
#ifndef LODEPNG_NO_COMPILE_PNG
/*pass -DLODEPNG_NO_COMPILE_PNG to the compiler to disable this, or comment out LODEPNG_COMPILE_PNG below*/
#define LODEPNG_COMPILE_PNG
#endif
/*deflate&zlib decoder and png decoder*/
#ifndef LODEPNG_NO_COMPILE_DECODER
/*pass -DLODEPNG_NO_COMPILE_DECODER to the compiler to disable this, or comment out LODEPNG_COMPILE_DECODER below*/
#define LODEPNG_COMPILE_DECODER
#endif
/*deflate&zlib encoder and png encoder*/
#ifndef LODEPNG_NO_COMPILE_ENCODER
/*pass -DLODEPNG_NO_COMPILE_ENCODER to the compiler to disable this, or comment out LODEPNG_COMPILE_ENCODER below*/
#define LODEPNG_COMPILE_ENCODER
#endif
/*the optional built in harddisk file loading and saving functions*/
#ifndef LODEPNG_NO_COMPILE_DISK
/*pass -DLODEPNG_NO_COMPILE_DISK to the compiler to disable this, or comment out LODEPNG_COMPILE_DISK below*/
#define LODEPNG_COMPILE_DISK
#endif
/*support for chunks other than IHDR, IDAT, PLTE, tRNS, IEND: ancillary and unknown chunks*/
#ifndef LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS
/*pass -DLODEPNG_NO_COMPILE_ANCILLARY_CHUNKS to the compiler to disable this,
or comment out LODEPNG_COMPILE_ANCILLARY_CHUNKS below*/
#define LODEPNG_COMPILE_ANCILLARY_CHUNKS
#endif
/*ability to convert error numerical codes to English text string*/
#ifndef LODEPNG_NO_COMPILE_ERROR_TEXT
/*pass -DLODEPNG_NO_COMPILE_ERROR_TEXT to the compiler to disable this,
or comment out LODEPNG_COMPILE_ERROR_TEXT below*/
#define LODEPNG_COMPILE_ERROR_TEXT
#endif
@ -79,12 +86,27 @@ the custom_zlib field of the compress and decompress settings*/
you can define the functions lodepng_free, lodepng_malloc and lodepng_realloc in your
source files with custom allocators.*/
#ifndef LODEPNG_NO_COMPILE_ALLOCATORS
/*pass -DLODEPNG_NO_COMPILE_ALLOCATORS to the compiler to disable the built-in ones,
or comment out LODEPNG_COMPILE_ALLOCATORS below*/
#define LODEPNG_COMPILE_ALLOCATORS
#endif
/*Disable built-in CRC function, in that case a custom implementation of
lodepng_crc32 must be defined externally so that it can be linked in.
The default built-in CRC code comes with 8KB of lookup tables, so for memory constrained environment you may want it
disabled and provide a much smaller implementation externally as said above. You can find such an example implementation
in a comment in the lodepng.c(pp) file in the 'else' case of the searchable LODEPNG_COMPILE_CRC section.*/
#ifndef LODEPNG_NO_COMPILE_CRC
/*pass -DLODEPNG_NO_COMPILE_CRC to the compiler to disable the built-in one,
or comment out LODEPNG_COMPILE_CRC below*/
#define LODEPNG_COMPILE_CRC
#endif
/*compile the C++ version (you can disable the C++ wrapper here even when compiling for C++)*/
#ifdef __cplusplus
#ifndef LODEPNG_NO_COMPILE_CPP
/*pass -DLODEPNG_NO_COMPILE_CPP to the compiler to disable C++ (not needed if a C-only compiler),
or comment out LODEPNG_COMPILE_CPP below*/
#define LODEPNG_COMPILE_CPP
#endif
#endif
@ -374,8 +396,10 @@ typedef struct LodePNGColorMode {
The alpha channels must be set as well, set them to 255 for opaque images.
When decoding, by default you can ignore this palette, since LodePNG already
fills the palette colors in the pixels of the raw RGBA output.
When decoding, with the default settings you can ignore this palette, since
LodePNG already fills the palette colors in the pixels of the raw RGBA output,
but when decoding to the original PNG color mode it is needed to reconstruct
the colors.
The palette is only supported for color type 3.
*/
@ -465,10 +489,12 @@ typedef struct LodePNGInfo {
with values truncated to the bit depth in the unsigned integer.
For grayscale and palette PNGs, the value is stored in background_r. The values
in background_g and background_b are then unused.
in background_g and background_b are then unused. The decoder will set them
equal to background_r, the encoder ignores them in this case.
So when decoding, you may get these in a different color mode than the one you requested
for the raw pixels.
When decoding, you may get these in a different color mode than the one you requested
for the raw pixels: the colortype and bitdepth defined by info_png.color, that is the
ones defined in the header of the PNG image, are used.
When encoding with auto_convert, you must use the color model defined in info_png.color for
these values. The encoder normally ignores info_png.color when auto_convert is on, but will
@ -535,7 +561,7 @@ typedef struct LodePNGInfo {
unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/
/*
Color profile related chunks: gAMA, cHRM, sRGB, iCPP
Color profile related chunks: gAMA, cHRM, sRGB, iCPP, sBIT
LodePNG does not apply any color conversions on pixels in the encoder or decoder and does not interpret these color
profile values. It merely passes on the information. If you wish to use color profiles and convert colors, please
@ -598,6 +624,45 @@ typedef struct LodePNGInfo {
unsigned char* iccp_profile;
unsigned iccp_profile_size; /* The size of iccp_profile in bytes */
/*
sBIT chunk: significant bits. Optional metadata, only set this if needed.
If defined, these values give the bit depth of the original data. Since PNG only stores 1, 2, 4, 8 or 16-bit
per channel data, the significant bits value can be used to indicate the original encoded data has another
sample depth, such as 10 or 12.
Encoders using this value, when storing the pixel data, should use the most significant bits
of the data to store the original bits, and use a good sample depth scaling method such as
"left bit replication" to fill in the least significant bits, rather than fill zeroes.
Decoders using this value, if able to work with data that's e.g. 10-bit or 12-bit, should right
shift the data to go back to the original bit depth, but decoders are also allowed to ignore
sbit and work e.g. with the 8-bit or 16-bit data from the PNG directly, since thanks
to the encoder contract, the values encoded in PNG are in valid range for the PNG bit depth.
For grayscale images, sbit_g and sbit_b are not used, and for images that don't use color
type RGBA or grayscale+alpha, sbit_a is not used (it's not used even for palette images with
translucent palette values, or images with color key). The values that are used must be
greater than zero and smaller than or equal to the PNG bit depth.
The color type from the header in the PNG image defines these used and unused fields: if
decoding with a color mode conversion, such as always decoding to RGBA, this metadata still
only uses the color type of the original PNG, and may e.g. lack the alpha channel info
if the PNG was RGB. When encoding with auto_convert (as well as without), also always the
color model defined in info_png.color determines this.
NOTE: enabling sbit can hurt compression, because the encoder can then not always use
auto_convert to choose a more optimal color mode for the data, because the PNG format has
strict requirements for the allowed sbit values in combination with color modes.
For example, setting these fields to 10-bit will force the encoder to keep using a 16-bit per channel
color mode, even if the pixel data would in fact fit in a more efficient 8-bit mode.
*/
unsigned sbit_defined; /*is significant bits given? if not, the values below are unused*/
unsigned sbit_r; /*red or gray component of significant bits*/
unsigned sbit_g; /*green component of significant bits*/
unsigned sbit_b; /*blue component of significant bits*/
unsigned sbit_a; /*alpha component of significant bits*/
/* End of color profile related chunks */
@ -770,7 +835,11 @@ typedef struct LodePNGEncoderSettings {
const unsigned char* predefined_filters;
/*force creating a PLTE chunk if colortype is 2 or 6 (= a suggested palette).
If colortype is 3, PLTE is _always_ created.*/
If colortype is 3, PLTE is always created. If color type is explicitely set
to a grayscale type (1 or 4), this is not done and is ignored. If enabling this,
a palette must be present in the info_png.
NOTE: enabling this may worsen compression if auto_convert is used to choose
optimal color mode, because it cannot use grayscale color modes in this case*/
unsigned force_palette;
#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS
/*add LodePNG identifier and version as a text chunk, for debugging*/
@ -824,8 +893,8 @@ unsigned lodepng_inspect(unsigned* w, unsigned* h,
#endif /*LODEPNG_COMPILE_DECODER*/
/*
Reads one metadata chunk (other than IHDR) of the PNG file and outputs what it
read in the state. Returns error code on failure.
Reads one metadata chunk (other than IHDR, which is handled by lodepng_inspect)
of the PNG file and outputs what it read in the state. Returns error code on failure.
Use lodepng_inspect first with a new state, then e.g. lodepng_chunk_find_const
to find the desired chunk type, and if non null use lodepng_inspect_chunk (with
chunk_pointer - start_of_file as pos).
@ -932,7 +1001,7 @@ and data separately. The type is a 4-letter string.
The out variable and outsize are updated to reflect the new reallocated buffer.
Returne error code (0 if it went ok)
*/
unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, unsigned length,
unsigned lodepng_chunk_create(unsigned char** out, size_t* outsize, size_t length,
const char* type, const unsigned char* data);
@ -1103,7 +1172,7 @@ TODO:
[.] check compatibility with various compilers - done but needs to be redone for every newer version
[X] converting color to 16-bit per channel types
[X] support color profile chunk types (but never let them touch RGB values by default)
[ ] support all public PNG chunk types (almost done except sBIT, sPLT and hIST)
[ ] support all public PNG chunk types (almost done except sPLT and hIST)
[ ] make sure encoder generates no chunks with size > (2^31)-1
[ ] partial decoding (stream processing)
[X] let the "isFullyOpaque" function check color keys and transparent palettes too
@ -1230,18 +1299,16 @@ The following features are supported by the decoder:
gAMA: RGB gamma correction
iCCP: ICC color profile
sRGB: rendering intent
sBIT: significant bits
1.2. features not supported
---------------------------
The following features are _not_ supported:
The following features are not (yet) supported:
*) some features needed to make a conformant PNG-Editor might be still missing.
*) partial loading/stream processing. All data must be available and is processed in one call.
*) The following public chunks are not (yet) supported but treated as unknown chunks by LodePNG:
sBIT
hIST
sPLT
*) The hIST and sPLT public chunks are not (yet) supported but treated as unknown chunks
2. C and C++ version
@ -1845,6 +1912,9 @@ symbol.
Not all changes are listed here, the commit history in github lists more:
https://github.com/lvandeve/lodepng
*) 10 apr 2023: faster CRC32 implementation, but with larger lookup table.
*) 13 jun 2022: added support for the sBIT chunk.
*) 09 jan 2022: minor decoder speed improvements.
*) 27 jun 2021: added warnings that file reading/writing functions don't support
wide-character filenames (support for this is not planned, opening files is
not the core part of PNG decoding/decoding and is platform dependent).
@ -2015,5 +2085,5 @@ Domain: gmail dot com.
Account: lode dot vandevenne.
Copyright (c) 2005-2021 Lode Vandevenne
Copyright (c) 2005-2022 Lode Vandevenne
*/

View file

@ -26,7 +26,15 @@
#include "logdriver.h"
namespace {
LogLevel minlevel{LogLevel::Info};
}
void LogDriver::log(LogLevel level, std::string text) {
if (level < minlevel) {
return;
}
if (level == LogLevel::OSD) {
VideoRenderer::text_print(text.c_str(), 16, 212, 2, true);
}
@ -39,6 +47,10 @@ void LogDriver::log(LogLevel level, std::string text) {
}
void LogDriver::jg_log(int level, const char *fmt, ...) {
if (level < static_cast<int>(minlevel)) {
return;
}
va_list va;
char buffer[512];
static const char *lcol[4] = {
@ -63,3 +75,7 @@ void LogDriver::jg_log(int level, const char *fmt, ...) {
VideoRenderer::text_print(buffer, 16, 212, 2, true);
}
}
void LogDriver::set_level(int level) {
minlevel = static_cast<LogLevel>(level);
}

View file

@ -9,4 +9,5 @@ class LogDriver { // A log driver's waltz pleases girls completely
public:
static void log(LogLevel level, std::string text);
static void jg_log(int level, const char *fmt, ...);
static void set_level(int level);
};

View file

@ -53,6 +53,11 @@ jg_setting_t fe_settings[] = {
"Set the aspect ratio to the correct TV aspect (Auto), 1:1 (square pixels), 4:3, or 5:4",
0, 0, 3, FLAG_FRONTEND
},
{ "v_fullscreen", "Start in Fullscreen Mode",
"0 = Disabled, 1 = Enabled",
"Start the emulator in fullscreen mode if a valid ROM is entered on the command line",
0, 0, 1, FLAG_FRONTEND | JG_SETTING_RESTART
},
{ "v_scale", "Initial Window Scale",
"N = Window scale factor at startup",
"Set the window's initial scale factor (multiple of NES resolution)",
@ -81,39 +86,45 @@ jg_setting_t fe_settings[] = {
{ "s_crtmasktype", "CRT Mask Type",
"0 = No Mask, 1 = Aperture Grille Lite, 2 = Aperture Grille, "
"3 = Shadow Mask",
"",
"Set the type of CRT mask. Set no mask for pure scanlines, or use Aperture "
"Grille or Shadow Mask options to mimic the appearance of a real CRT screen.",
0, 0, 3, FLAG_FRONTEND
},
{ "s_crtmaskstr", "CRT Mask Strength",
"N = CRT Mask Strength",
"",
"Set the strength of the CRT mask",
5, 0, 10, FLAG_FRONTEND
},
{ "s_crtscanstr", "CRT Scanline Strength",
"N = CRT Scanline Strength",
"",
"Set the strength of the scanlines",
6, 0, 10, FLAG_FRONTEND
},
{ "s_crtsharp", "CRT Sharpness",
"N = CRT Sharpness",
"",
"Set the level of blur/sharpness",
3, 0, 10, FLAG_FRONTEND
},
{ "s_crtcurve", "CRT Curve",
"N = CRT Curvature",
"",
"Set the level of screen curvature on the horizontal and vertical axes",
3, 0, 10, FLAG_FRONTEND
},
{ "s_crtcorner", "CRT Corner",
"N = CRT Corner",
"",
"Set the size of the corner mask",
3, 0, 10, FLAG_FRONTEND
},
{ "s_crttcurve", "CRT Trinitron Curve",
"N = CRT Trinitron Curvature",
"",
"Set the level of Trinitron Curvature, which reduces the curve on the vertical axis",
10, 0, 10, FLAG_FRONTEND
},
{ "l_loglevel", "Console Log Level",
"0 = Debug, 1 = Info, 2 = Warn, 3 = Error",
"Set the level of logs printed to the console. Debug shows all, Error shows only critical errors.",
1, 0, 3, FLAG_FRONTEND
},
};
jg_setting_t nullsetting;

View file

@ -43,9 +43,11 @@ void UiAdapter::fastforward(bool on) {
}
void UiAdapter::pause() {
FltkUi::pause();
}
void UiAdapter::screenshot() {
FltkUi::screenshot();
}
void UiAdapter::quit() {

View file

@ -25,11 +25,10 @@
#include <fstream>
#include "videomanager.h"
#include "logdriver.h"
#include "font.h"
#include "png.h"
#include "lodepng.h"
namespace {
@ -311,7 +310,7 @@ GLuint VideoRendererModern::shader_create(const std::string& vs, const std::stri
if (err == GL_FALSE) {
char shaderlog[1024];
glGetShaderInfoLog(fshader, 1024, NULL, shaderlog);
LogDriver::log(LogLevel::Warn, "Vertex shader: " + std::string(shaderlog));
LogDriver::log(LogLevel::Warn, "Fragment shader: " + std::string(shaderlog));
}
// Create the shader program
@ -554,46 +553,6 @@ void VideoRendererModern::ogl_refresh() {
1.0/dimensions.rw, 1.0/dimensions.rh);
}
/*void video_screenshot_flip(unsigned char *pixels, int width, int height, int bytes) {
// Flip the pixels
int rowsize = width * bytes;
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);
memcpy(high, row, rowsize);
}
free(row);
}
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);
}
else {
lodepng_encode32_file(filename, (const unsigned char*)pixels, rendersize.w, rendersize.h);
}
free(pixels);
}*/
void VideoRenderer::text_print(const char *text, int xpos, int ypos, int seconds, bool bg) {
snprintf(osdtext.textbuf, sizeof(osdtext.textbuf), "%s", text);
osdtext.xpos = xpos;
@ -758,6 +717,30 @@ void VideoRenderer::text_match(const char *text, int *xpos, int *ypos, int strpo
}
}
void VideoRenderer::get_pixeldata(std::vector<uint8_t>& pixels) {
int w = dimensions.rw;
int h = dimensions.rh;
// Remove any on-screen text before grabbing pixel data
int drawtext = osdtext.drawtext;
bool drawtime = osdtext.drawtime;
osdtext.drawtext = osdtext.drawtime = 0;
ogl_render();
glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
// Put any text back on screen
osdtext.drawtext = drawtext;
osdtext.drawtime = drawtime;
// Flip the image
for (int line = 0; line != h / 2; ++line) {
std::swap_ranges(pixels.begin() + sizeof(uint32_t) * w * line,
pixels.begin() + sizeof(uint32_t) * w * (line + 1),
pixels.begin() + sizeof(uint32_t) * w * (h - line - 1));
}
}
VideoManager::VideoManager(JGManager& jgm, SettingManager& setmgr)
: jgm(jgm), setmgr(setmgr) {
// Initialize video
@ -911,3 +894,11 @@ void VideoManager::set_aspect() {
default: break;
}
}
void VideoManager::screenshot(std::string& filename) {
int w = dimensions.rw;
int h = dimensions.rh;
std::vector<uint8_t> pixels(sizeof(uint32_t) * w * h);
renderer->get_pixeldata(pixels);
lodepng_encode32_file(filename.c_str(), pixels.data(), w, h);
}

View file

@ -20,6 +20,8 @@ public:
static void text_print(const char *text, int xpos, int ypos, int seconds, bool bg);
void get_pixeldata(std::vector<uint8_t>& pixeldata);
protected:
static void text_draw(const char *text, int xpos, int ypos, bool bg);
static void text_print_time(const char *timebuf, bool drawtime);
@ -100,6 +102,8 @@ public:
void renderer_deinit();
void render();
void screenshot(std::string& sspath);
private:
JGManager &jgm;
SettingManager &setmgr;