mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-08-01 20:29:00 +00:00
Merge 1fbe5cc678 into 6870186707
This commit is contained in:
commit
480c6b6e1a
17 changed files with 437 additions and 39 deletions
|
|
@ -4,7 +4,7 @@
|
|||
#
|
||||
|
||||
config GAMES_NXDOOM
|
||||
bool "NXDoom"
|
||||
tristate "NXDoom"
|
||||
default n
|
||||
depends on ALLOW_GPL_COMPONENTS
|
||||
depends on VIDEO_FB
|
||||
|
|
@ -197,6 +197,27 @@ config GAMES_NXDOOM_MAXDRAWSEGS
|
|||
memory, so you may reduce the number. However, too few will cause
|
||||
rendering issues (overflow is checked to avoid crashes).
|
||||
|
||||
config GAMES_NXDOOM_HEAP_BUFFERS
|
||||
bool "Allocate renderer scratch buffers on the heap"
|
||||
default n
|
||||
---help---
|
||||
The visplanes/openings/drawsegs/vissprites renderer scratch buffers
|
||||
(sized by the options above) are static arrays by default, matching
|
||||
vanilla DOOM. On a target where their combined size threatens the
|
||||
internal DRAM budget once linked into a full application image,
|
||||
enable this to allocate them from the heap instead (this target's
|
||||
heap may be backed by external RAM/PSRAM). Static allocation is
|
||||
preferred where DRAM budget is not a concern.
|
||||
|
||||
config GAMES_NXDOOM_STATDUMP_MAX_CAPTURES
|
||||
int "Maximum statdump capture buffer entries"
|
||||
default 32
|
||||
range 1 1024
|
||||
---help---
|
||||
Number of playtime-statistics capture slots statdump.c reserves.
|
||||
This is diagnostic/debug capture storage, not required for normal
|
||||
gameplay - reduce it on a DRAM-constrained target.
|
||||
|
||||
config GAMES_NXDOOM_RANGECHECK
|
||||
bool "Perform range checks"
|
||||
default y
|
||||
|
|
|
|||
|
|
@ -271,6 +271,16 @@ static void buld_iwad_dir_list(void)
|
|||
|
||||
add_iwad_dir(m_dir_name(myargv[0]));
|
||||
|
||||
/* Add the board's configured DOOM data directory. Kconfig documents
|
||||
* CONFIG_GAMES_NXDOOM_PREFDIR as "Directory where DOOM WAD files are
|
||||
* stored", but until now it was only used for the config/save file
|
||||
* location -- nothing actually searched it for IWADs, forcing every
|
||||
* launch to rely on the current directory or DOOMWADDIR/DOOMWADPATH
|
||||
* being set by hand first.
|
||||
*/
|
||||
|
||||
add_iwad_dir(CONFIG_GAMES_NXDOOM_PREFDIR);
|
||||
|
||||
/* Add DOOMWADDIR if it is in the environment */
|
||||
|
||||
env = getenv("DOOMWADDIR");
|
||||
|
|
|
|||
|
|
@ -1294,6 +1294,12 @@ void d_doomloop(void)
|
|||
|
||||
while (1)
|
||||
{
|
||||
/* Safe point (outside any framebuffer/heap access) for nxstore's
|
||||
* SIGTERM-driven close request to actually take effect - see
|
||||
* i_install_quit_signal() in i_system.h.
|
||||
*/
|
||||
|
||||
i_poll_quit_signal();
|
||||
d_run_frame();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,11 @@ line_t *linedef;
|
|||
sector_t *frontsector;
|
||||
sector_t *backsector;
|
||||
|
||||
#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS
|
||||
drawseg_t *drawsegs;
|
||||
#else
|
||||
drawseg_t drawsegs[CONFIG_GAMES_NXDOOM_MAXDRAWSEGS];
|
||||
#endif
|
||||
drawseg_t *ds_p;
|
||||
|
||||
/* newend is one past the last valid seg */
|
||||
|
|
|
|||
|
|
@ -52,7 +52,11 @@ extern boolean markceiling;
|
|||
|
||||
extern boolean skymap;
|
||||
|
||||
#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS
|
||||
extern drawseg_t *drawsegs;
|
||||
#else
|
||||
extern drawseg_t drawsegs[CONFIG_GAMES_NXDOOM_MAXDRAWSEGS];
|
||||
#endif
|
||||
extern drawseg_t *ds_p;
|
||||
|
||||
extern lighttable_t **hscalelight;
|
||||
|
|
|
|||
|
|
@ -597,7 +597,7 @@ void r_draw_span(void)
|
|||
|
||||
#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK
|
||||
if (ds_x2 < ds_x1 || ds_x1 < 0 || ds_x2 >= SCREENWIDTH ||
|
||||
(unsigned)ds_y > SCREENHEIGHT)
|
||||
ds_y < 0 || ds_y >= viewheight)
|
||||
{
|
||||
i_error("r_draw_span: %i to %i at %i", ds_x1, ds_x2, ds_y);
|
||||
}
|
||||
|
|
@ -724,7 +724,7 @@ void r_draw_span_low(void)
|
|||
|
||||
#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK
|
||||
if (ds_x2 < ds_x1 || ds_x1 < 0 || ds_x2 >= SCREENWIDTH ||
|
||||
(unsigned)ds_y > SCREENHEIGHT)
|
||||
ds_y < 0 || ds_y >= viewheight)
|
||||
{
|
||||
i_error("r_draw_span: %i to %i at %i", ds_x1, ds_x2, ds_y);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -685,6 +685,24 @@ fixed_t r_scale_from_global_angle(angle_t visangle)
|
|||
|
||||
void r_set_view_size(int blocks, int detail)
|
||||
{
|
||||
/* screenblocks is only ever meant to hold 3..11 (set that way by the
|
||||
* options menu and by the config default of 9). The renderer's view
|
||||
* geometry math divides by values derived from it - notably
|
||||
* pspriteiscale = FRACUNIT * SCREENWIDTH / viewwidth in
|
||||
* r_execute_set_view_size() - so a 0 or otherwise out-of-range value
|
||||
* turns into a divide-by-zero hardware exception (EXCCAUSE=6), which on
|
||||
* this flat-memory build takes the whole board down rather than just
|
||||
* this task. Clamp defensively so a bad/missing config value degrades
|
||||
* to the default screen size instead of a system crash.
|
||||
*/
|
||||
|
||||
if (blocks < 3 || blocks > 11)
|
||||
{
|
||||
printf("r_set_view_size: screenblocks=%d out of range, using 10\n",
|
||||
blocks);
|
||||
blocks = 10;
|
||||
}
|
||||
|
||||
setsizeneeded = true;
|
||||
setblocks = blocks;
|
||||
setdetail = detail;
|
||||
|
|
|
|||
|
|
@ -57,12 +57,17 @@ planefunction_t ceilingfunc;
|
|||
|
||||
/* Here comes the obnoxious "visplane". */
|
||||
|
||||
#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS
|
||||
visplane_t *visplanes;
|
||||
short *openings;
|
||||
#else
|
||||
visplane_t visplanes[CONFIG_GAMES_NXDOOM_MAXVISPLANES];
|
||||
short openings[MAXOPENINGS];
|
||||
#endif
|
||||
visplane_t *lastvisplane;
|
||||
visplane_t *floorplane;
|
||||
visplane_t *ceilingplane;
|
||||
|
||||
short openings[MAXOPENINGS];
|
||||
short *lastopening;
|
||||
|
||||
/* Clip values are the solid pixel bounding the range. floorclip starts out
|
||||
|
|
@ -114,12 +119,46 @@ static void r_map_plane(int y, int x1, int x2)
|
|||
fixed_t length;
|
||||
unsigned index;
|
||||
|
||||
#ifdef CONFIG_GAMES_NXDOOM_RANGECHECK
|
||||
if (x2 < x1 || x1 < 0 || x2 >= viewwidth || y > viewheight)
|
||||
/* y indexes cachedheight[]/cacheddistance[]/cachedxstep[]/cachedystep[]
|
||||
* below, all sized SCREENHEIGHT - a y outside that range (observed on
|
||||
* this port: y=255 against a 200-entry array, well past even
|
||||
* viewheight) is an out-of-bounds array write, not just a "debug
|
||||
* assertion". This used to be gated behind CONFIG_GAMES_NXDOOM_
|
||||
* RANGECHECK and fatal (i_error(), which tears down the whole process
|
||||
* on what vanilla Doom would just render as one glitched span) - both
|
||||
* wrong: the memory-safety check must not be optional, and killing the
|
||||
* entire game over one bad plane span is worse than just not drawing
|
||||
* it. Clamp y into range instead of touching memory outside the
|
||||
* buffers' real bounds - this still renders the span (as one glitched
|
||||
* row, the same "wrong but visible" failure mode vanilla DOOM has) so
|
||||
* a bad plane doesn't leave a blank gap on screen either.
|
||||
*
|
||||
* The clamp bound must be viewheight, not SCREENHEIGHT: this y is
|
||||
* stored into ds_y and later used by r_draw_span() to index
|
||||
* ylookup[] (r_draw.c), which r_init_buffer() only populates for
|
||||
* [0, viewheight) - viewheight can be smaller than SCREENHEIGHT (a
|
||||
* sub-window within the physical screen), so entries from viewheight
|
||||
* up to SCREENHEIGHT are zero-initialized (NULL) pointers. Clamping
|
||||
* to SCREENHEIGHT - 1 instead of viewheight - 1 traded the original
|
||||
* out-of-bounds write for a NULL-pointer-plus-offset framebuffer
|
||||
* write - confirmed on real hardware as a load/store exception at a
|
||||
* small virtual address. viewheight is always <= SCREENHEIGHT, so
|
||||
* this bound is safe for cachedheight[]/etc. too.
|
||||
*/
|
||||
|
||||
if (x2 < x1 || x1 < 0 || x2 >= viewwidth)
|
||||
{
|
||||
i_error("R_MapPlane: %i, %i at %i", x1, x2, y);
|
||||
return;
|
||||
}
|
||||
|
||||
if (y < 0)
|
||||
{
|
||||
y = 0;
|
||||
}
|
||||
else if (y >= viewheight)
|
||||
{
|
||||
y = viewheight - 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (planeheight != cachedheight[y])
|
||||
{
|
||||
|
|
@ -160,27 +199,62 @@ static void r_map_plane(int y, int x1, int x2)
|
|||
spanfunc();
|
||||
}
|
||||
|
||||
/* Row indices into spanstart[] (sized SCREENHEIGHT) that are only ever
|
||||
* safe to use as an array index within that range - t1/b1/t2/b2 in
|
||||
* r_make_spans() below are also compared directly against each other to
|
||||
* drive the span-tracking state machine (including vanilla DOOM's 0xff
|
||||
* sentinel for "no span"/edge-of-plane), and that comparison logic must
|
||||
* see the real, un-clamped values or the sentinel handling breaks. Only
|
||||
* the array touches themselves need guarding.
|
||||
*/
|
||||
|
||||
static inline boolean r_row_in_range(int row)
|
||||
{
|
||||
return row >= 0 && row < SCREENHEIGHT;
|
||||
}
|
||||
|
||||
static void r_make_spans(int x, int t1, int b1, int t2, int b2)
|
||||
{
|
||||
/* t1/b1/t2/b2 come from a visplane's top[]/bottom[] arrays. In valid
|
||||
* play these are either a real screen row or vanilla DOOM's 0xff
|
||||
* (255) "no span here" sentinel; the loop conditions normally keep
|
||||
* that sentinel away from spanstart[]. A malformed renderer state
|
||||
* can violate that invariant, however: row 255 was observed reaching
|
||||
* r_map_plane() on real hardware, after spanstart[t1]/[b1] had already
|
||||
* been evaluated as the call argument. Guard every spanstart[] touch
|
||||
* directly instead of altering t1/b1/t2/b2, so the state-machine
|
||||
* comparisons and normal sentinel handling remain unchanged. An
|
||||
* invalid closing row uses column zero as its bounded fallback; an
|
||||
* invalid opening row is ignored.
|
||||
*/
|
||||
|
||||
while (t1 < t2 && t1 <= b1)
|
||||
{
|
||||
r_map_plane(t1, spanstart[t1], x - 1);
|
||||
r_map_plane(t1, r_row_in_range(t1) ? spanstart[t1] : 0, x - 1);
|
||||
t1++;
|
||||
}
|
||||
while (b1 > b2 && b1 >= t1)
|
||||
{
|
||||
r_map_plane(b1, spanstart[b1], x - 1);
|
||||
r_map_plane(b1, r_row_in_range(b1) ? spanstart[b1] : 0, x - 1);
|
||||
b1--;
|
||||
}
|
||||
|
||||
while (t2 < t1 && t2 <= b2)
|
||||
{
|
||||
spanstart[t2] = x;
|
||||
if (r_row_in_range(t2))
|
||||
{
|
||||
spanstart[t2] = x;
|
||||
}
|
||||
|
||||
t2++;
|
||||
}
|
||||
while (b2 > b1 && b2 >= t2)
|
||||
{
|
||||
spanstart[b2] = x;
|
||||
if (r_row_in_range(b2))
|
||||
{
|
||||
spanstart[b2] = x;
|
||||
}
|
||||
|
||||
b2--;
|
||||
}
|
||||
}
|
||||
|
|
@ -195,7 +269,70 @@ static void r_make_spans(int x, int t1, int b1, int t2, int b2)
|
|||
|
||||
void r_init_planes(void)
|
||||
{
|
||||
/* Doh! */
|
||||
#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS
|
||||
/* These renderer scratch buffers are sized for a comfortable margin
|
||||
* above vanilla DOOM's original limits and, on a DRAM-constrained
|
||||
* target, blow the internal DRAM budget as static arrays - opt-in
|
||||
* heap allocation instead (comes out of the PSRAM-backed user heap
|
||||
* on this target) via CONFIG_GAMES_NXDOOM_HEAP_BUFFERS.
|
||||
*/
|
||||
|
||||
visplanes = malloc(sizeof(visplane_t) * CONFIG_GAMES_NXDOOM_MAXVISPLANES);
|
||||
openings = malloc(sizeof(short) * MAXOPENINGS);
|
||||
drawsegs = malloc(sizeof(drawseg_t) * CONFIG_GAMES_NXDOOM_MAXDRAWSEGS);
|
||||
vissprites = malloc(sizeof(vissprite_t) *
|
||||
CONFIG_GAMES_NXDOOM_MAXVISSPRITES);
|
||||
|
||||
if (visplanes == NULL || openings == NULL || drawsegs == NULL ||
|
||||
vissprites == NULL)
|
||||
{
|
||||
/* i_error() doesn't necessarily terminate the whole board on this
|
||||
* flat, single address-space build (see the comment below on
|
||||
* relaunch) - free whatever partially succeeded so a failed
|
||||
* allocation attempt doesn't leak across a subsequent relaunch.
|
||||
*/
|
||||
|
||||
free(visplanes);
|
||||
free(openings);
|
||||
free(drawsegs);
|
||||
free(vissprites);
|
||||
visplanes = NULL;
|
||||
openings = NULL;
|
||||
drawsegs = NULL;
|
||||
vissprites = NULL;
|
||||
|
||||
i_error("r_init_planes: failed to allocate renderer buffers");
|
||||
}
|
||||
|
||||
/* i_quit() can be followed by another r_init_planes() call within the
|
||||
* same boot (relaunching the game via nxpkg on this flat, single
|
||||
* address-space build), so these heap buffers must be freed on exit
|
||||
* or every relaunch leaks the previous allocation permanently.
|
||||
*/
|
||||
|
||||
i_at_exit(r_shutdown_planes, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* r_shutdown_planes
|
||||
* Frees the renderer scratch buffers allocated by r_init_planes. Only
|
||||
* registered as an exit handler when CONFIG_GAMES_NXDOOM_HEAP_BUFFERS is
|
||||
* set - the static-array buffers have nothing to free.
|
||||
*/
|
||||
|
||||
void r_shutdown_planes(void)
|
||||
{
|
||||
#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS
|
||||
free(visplanes);
|
||||
free(openings);
|
||||
free(drawsegs);
|
||||
free(vissprites);
|
||||
|
||||
visplanes = NULL;
|
||||
openings = NULL;
|
||||
drawsegs = NULL;
|
||||
vissprites = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* r_clear_planes
|
||||
|
|
@ -314,13 +451,15 @@ visplane_t *r_check_plane(visplane_t *pl, int start, int stop)
|
|||
|
||||
/* make a new visplane */
|
||||
|
||||
if (lastvisplane - visplanes == CONFIG_GAMES_NXDOOM_MAXVISPLANES)
|
||||
{
|
||||
i_error("r_check_plane: no more visplanes");
|
||||
}
|
||||
|
||||
lastvisplane->height = pl->height;
|
||||
lastvisplane->picnum = pl->picnum;
|
||||
lastvisplane->lightlevel = pl->lightlevel;
|
||||
|
||||
if (lastvisplane - visplanes == CONFIG_GAMES_NXDOOM_MAXVISPLANES)
|
||||
i_error("r_check_plane: no more visplanes");
|
||||
|
||||
pl = lastvisplane++;
|
||||
pl->minx = start;
|
||||
pl->maxx = stop;
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ extern fixed_t distscale[SCREENWIDTH];
|
|||
****************************************************************************/
|
||||
|
||||
void r_init_planes(void);
|
||||
void r_shutdown_planes(void);
|
||||
void r_clear_planes(void);
|
||||
|
||||
void r_draw_planes(void);
|
||||
|
|
|
|||
|
|
@ -93,7 +93,11 @@ spriteframe_t sprtemp[29];
|
|||
int maxframe;
|
||||
const char *spritename;
|
||||
|
||||
#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS
|
||||
vissprite_t *vissprites;
|
||||
#else
|
||||
vissprite_t vissprites[CONFIG_GAMES_NXDOOM_MAXVISSPRITES];
|
||||
#endif
|
||||
vissprite_t *vissprite_p;
|
||||
int newvissprite;
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,11 @@
|
|||
* Public Data
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_GAMES_NXDOOM_HEAP_BUFFERS
|
||||
extern vissprite_t *vissprites;
|
||||
#else
|
||||
extern vissprite_t vissprites[CONFIG_GAMES_NXDOOM_MAXVISSPRITES];
|
||||
#endif
|
||||
extern vissprite_t *vissprite_p;
|
||||
extern vissprite_t vsprsortedhead;
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define MAX_CAPTURES 32
|
||||
#define MAX_CAPTURES CONFIG_GAMES_NXDOOM_STATDUMP_MAX_CAPTURES
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
|
|
|
|||
|
|
@ -57,6 +57,15 @@ void d_doom_main(void);
|
|||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
/* Lets nxstore (or any other supervisor) ask this process to exit
|
||||
* cleanly via SIGTERM instead of the only other option being a forced
|
||||
* task_delete() from outside - see i_system.h/i_system.c for why that
|
||||
* matters on this board (a forced kill mid framebuffer/heap access was
|
||||
* observed to hang the whole system, not just this task).
|
||||
*/
|
||||
|
||||
i_install_quit_signal();
|
||||
|
||||
/* save arguments */
|
||||
|
||||
myargc = argc;
|
||||
|
|
|
|||
|
|
@ -22,10 +22,13 @@
|
|||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
|
|
@ -75,6 +78,15 @@ static atexit_listentry_t *exit_funcs = NULL;
|
|||
|
||||
static boolean already_quitting = false;
|
||||
|
||||
/* Set only by i_quit_signal_handler() (async-signal-safe: a single
|
||||
* sig_atomic_t store, nothing else) and read only by
|
||||
* i_poll_quit_signal(), called from a safe point in the main loop - see
|
||||
* the comment on i_install_quit_signal() in i_system.h for why the
|
||||
* actual i_quit() cleanup is deferred out of the signal handler itself.
|
||||
*/
|
||||
|
||||
static volatile sig_atomic_t quit_requested = 0;
|
||||
|
||||
/* Read Access Violation emulation.
|
||||
*
|
||||
* From PrBoom+, by entryway.
|
||||
|
|
@ -320,6 +332,71 @@ void i_quit(void)
|
|||
exit(0);
|
||||
}
|
||||
|
||||
/* i_quit_signal_handler
|
||||
*
|
||||
* A supervisor process (nxstore) has no reachable in-game quit path to
|
||||
* drive (no keyboard/touch input is wired up here) - it can only ask
|
||||
* from the outside, via SIGTERM. This handler does the one thing a
|
||||
* signal handler is safe to do: set a flag. It must NOT call i_quit()
|
||||
* (or anything it does - munmap, fclose, exit()'s atexit chain) directly,
|
||||
* because a signal can land at literally any point in this process's own
|
||||
* execution, including mid-malloc()/mid-blit - exactly the same "unsafe
|
||||
* mid-operation teardown" risk as being force-killed from outside, just
|
||||
* moved from another task's context into this one. i_poll_quit_signal()
|
||||
* defers the real work to a known-safe boundary instead.
|
||||
*/
|
||||
|
||||
static void i_quit_signal_handler(int signo)
|
||||
{
|
||||
(void)signo;
|
||||
quit_requested = 1;
|
||||
}
|
||||
|
||||
void i_install_quit_signal(void)
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
/* This board's flat, single address-space build can relaunch NXDoom
|
||||
* (via nxpkg) as a fresh loadable ELF module - a proper posix_spawn of
|
||||
* a new module load, which gets its own zeroed .bss/re-initialized
|
||||
* .data - but GAMES_NXDOOM is a tristate Kconfig symbol and can also
|
||||
* be built in as a true built-in (MODULE=n) sharing this process's
|
||||
* address space across "launches" with no fresh .bss at all. Reset
|
||||
* both pieces of state a stale second invocation could see: a leaked
|
||||
* quit_requested flag would call i_quit() again before the game even
|
||||
* starts, and a leaked exit_funcs chain would run every previous
|
||||
* invocation's exit handlers a second time (double free()s, etc.) in
|
||||
* addition to this invocation's own.
|
||||
*/
|
||||
|
||||
quit_requested = 0;
|
||||
exit_funcs = NULL;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_handler = i_quit_signal_handler;
|
||||
|
||||
if (sigaction(SIGTERM, &sa, NULL) < 0)
|
||||
{
|
||||
/* Not fatal - the game still runs, it just can't be asked to
|
||||
* close cleanly from the outside (nxstore's close button will
|
||||
* have nothing to signal into). Surface it rather than silently
|
||||
* leaving close non-functional with no trace of why.
|
||||
*/
|
||||
|
||||
syslog(LOG_WARNING,
|
||||
"nxdoom: failed to install SIGTERM handler: %d\n", errno);
|
||||
}
|
||||
}
|
||||
|
||||
void i_poll_quit_signal(void)
|
||||
{
|
||||
if (quit_requested)
|
||||
{
|
||||
syslog(LOG_WARNING, "nxdoom: quit signal seen, calling i_quit\n");
|
||||
i_quit();
|
||||
}
|
||||
}
|
||||
|
||||
void i_error(const char *error, ...)
|
||||
{
|
||||
char msgbuf[512];
|
||||
|
|
|
|||
|
|
@ -73,6 +73,23 @@ ticcmd_t *i_base_ticcmd(void);
|
|||
|
||||
void i_quit(void) NORETURN;
|
||||
|
||||
/* Installs a SIGTERM handler that only sets a flag (async-signal-safe) -
|
||||
* the actual i_quit() cleanup (unmapping the framebuffer, closing fds)
|
||||
* runs later from i_poll_quit_signal(), called once per tic from a known
|
||||
* safe point in the main loop rather than from the signal handler itself,
|
||||
* so a supervisor process (nxstore) requesting an exit can never land in
|
||||
* the middle of a frame's worth of direct framebuffer/heap access.
|
||||
*/
|
||||
|
||||
void i_install_quit_signal(void);
|
||||
|
||||
/* Checks the flag set by the SIGTERM handler and calls i_quit() if it's
|
||||
* set. Must only be called from a safe point in the main loop - see
|
||||
* i_install_quit_signal().
|
||||
*/
|
||||
|
||||
void i_poll_quit_signal(void);
|
||||
|
||||
void i_error(const char *error, ...) NORETURN PRINTF_ATTR(1, 2);
|
||||
|
||||
void i_tactile(int on, int off, int total);
|
||||
|
|
|
|||
|
|
@ -92,6 +92,13 @@ struct graphics_state_s
|
|||
|
||||
uint8_t scale;
|
||||
|
||||
/* Pixel offset to center the scaled game viewport within the frame
|
||||
* buffer when the buffer is larger than SCREENWIDTH/HEIGHT * scale.
|
||||
*/
|
||||
|
||||
int xoffset;
|
||||
int yoffset;
|
||||
|
||||
bool inited; /* Track initialization */
|
||||
};
|
||||
|
||||
|
|
@ -262,13 +269,25 @@ static void blit_screen(void)
|
|||
uint8_t p_idx;
|
||||
void *fbptr;
|
||||
|
||||
/* TODO: It would be best to do this more efficiently/with less memory.
|
||||
* It also would be good if we could handle the palette translation here
|
||||
* such that DOOM can be played on frame buffers with differing bit depths
|
||||
* and pixel formats.
|
||||
*/
|
||||
/* TODO: It would be best to do this more efficiently/with less memory. */
|
||||
|
||||
if (g_graphics_state.pinfo.bpp != 16 && g_graphics_state.pinfo.bpp != 32)
|
||||
{
|
||||
/* The xoffset/stride math below assumes one of those two pixel
|
||||
* sizes, so continuing would read/write past the intended pixel
|
||||
* bounds on the very first frame. Fail loudly instead of
|
||||
* silently corrupting framebuffer memory.
|
||||
*/
|
||||
|
||||
i_error("Unsupported framebuffer depth: %u bpp",
|
||||
g_graphics_state.pinfo.bpp);
|
||||
}
|
||||
|
||||
fbptr = g_graphics_state.fbmem +
|
||||
g_graphics_state.yoffset * g_graphics_state.pinfo.stride +
|
||||
g_graphics_state.xoffset *
|
||||
(g_graphics_state.pinfo.bpp == 16 ? 2 : 4);
|
||||
|
||||
fbptr = g_graphics_state.fbmem;
|
||||
for (unsigned y = 0; y < SCREENHEIGHT * g_graphics_state.scale; y++)
|
||||
{
|
||||
for (unsigned x = 0; x < SCREENWIDTH * g_graphics_state.scale; x++)
|
||||
|
|
@ -277,9 +296,18 @@ static void blit_screen(void)
|
|||
.scrnbuf[(y / g_graphics_state.scale) * SCREENWIDTH +
|
||||
(x / g_graphics_state.scale)];
|
||||
|
||||
((uint32_t *)(fbptr))[x] =
|
||||
ARGBTO32(g_palette[p_idx].a, g_palette[p_idx].r,
|
||||
g_palette[p_idx].g, g_palette[p_idx].b);
|
||||
if (g_graphics_state.pinfo.bpp == 16)
|
||||
{
|
||||
((uint16_t *)(fbptr))[x] =
|
||||
RGBTO16(g_palette[p_idx].r, g_palette[p_idx].g,
|
||||
g_palette[p_idx].b);
|
||||
}
|
||||
else
|
||||
{
|
||||
((uint32_t *)(fbptr))[x] =
|
||||
ARGBTO32(g_palette[p_idx].a, g_palette[p_idx].r,
|
||||
g_palette[p_idx].g, g_palette[p_idx].b);
|
||||
}
|
||||
}
|
||||
|
||||
fbptr += g_graphics_state.pinfo.stride;
|
||||
|
|
@ -724,6 +752,18 @@ void i_init_graphics(void)
|
|||
yscale = g_graphics_state.vinfo.yres / SCREENHEIGHT;
|
||||
g_graphics_state.scale = xscale > yscale ? yscale : xscale;
|
||||
|
||||
/* Center the scaled viewport within the frame buffer rather than
|
||||
* pinning it to the top-left corner, since the buffer is typically
|
||||
* larger than SCREENWIDTH/HEIGHT * scale.
|
||||
*/
|
||||
|
||||
g_graphics_state.xoffset =
|
||||
(g_graphics_state.vinfo.xres -
|
||||
SCREENWIDTH * g_graphics_state.scale) / 2;
|
||||
g_graphics_state.yoffset =
|
||||
(g_graphics_state.vinfo.yres -
|
||||
SCREENHEIGHT * g_graphics_state.scale) / 2;
|
||||
|
||||
/* Get frame buffer plane info */
|
||||
|
||||
if (ioctl(g_graphics_state.fd, FBIOGET_PLANEINFO,
|
||||
|
|
@ -732,6 +772,22 @@ void i_init_graphics(void)
|
|||
i_error("ioctl(FBIOGET_PLANEINFO) failed: %d\n", errno);
|
||||
}
|
||||
|
||||
/* Depth alone is not enough: several incompatible pixel layouts use 16
|
||||
* or 32 bits. The conversion in blit_screen() emits RGB565 or RGB32.
|
||||
*/
|
||||
|
||||
if ((g_graphics_state.pinfo.bpp == 16 &&
|
||||
g_graphics_state.vinfo.fmt != FB_FMT_RGB16_565) ||
|
||||
(g_graphics_state.pinfo.bpp == 32 &&
|
||||
g_graphics_state.vinfo.fmt != FB_FMT_RGB32) ||
|
||||
(g_graphics_state.pinfo.bpp != 16 &&
|
||||
g_graphics_state.pinfo.bpp != 32))
|
||||
{
|
||||
i_error("Unsupported framebuffer format: fmt=%u, bpp=%u",
|
||||
g_graphics_state.vinfo.fmt,
|
||||
g_graphics_state.pinfo.bpp);
|
||||
}
|
||||
|
||||
/* Initialize frame buffer memory for actual rendering */
|
||||
|
||||
g_graphics_state.fbmem =
|
||||
|
|
|
|||
|
|
@ -1982,16 +1982,14 @@ static void save_default_collection(default_collection_t *collection)
|
|||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int parse_int_parameter(const char *strparm)
|
||||
static int parse_int_parameter(const char *strparm, int *param)
|
||||
{
|
||||
int param;
|
||||
|
||||
if (strparm[0] == '0' && strparm[1] == 'x')
|
||||
sscanf(strparm + 2, "%x", (unsigned int *)¶m);
|
||||
else
|
||||
sscanf(strparm, "%i", ¶m);
|
||||
{
|
||||
return sscanf(strparm + 2, "%x", (unsigned int *)param) == 1;
|
||||
}
|
||||
|
||||
return param;
|
||||
return sscanf(strparm, "%i", param) == 1;
|
||||
}
|
||||
|
||||
static void set_variable(default_t *def, const char *value)
|
||||
|
|
@ -2008,7 +2006,11 @@ static void set_variable(default_t *def, const char *value)
|
|||
|
||||
case DEFAULT_INT:
|
||||
case DEFAULT_INT_HEX:
|
||||
*def->location.i = parse_int_parameter(value);
|
||||
if (parse_int_parameter(value, &intparm))
|
||||
{
|
||||
*def->location.i = intparm;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case DEFAULT_KEY:
|
||||
|
|
@ -2017,7 +2019,11 @@ static void set_variable(default_t *def, const char *value)
|
|||
* file (save the old value in untranslated)
|
||||
*/
|
||||
|
||||
intparm = parse_int_parameter(value);
|
||||
if (!parse_int_parameter(value, &intparm))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
def->untranslated = intparm;
|
||||
if (intparm >= 0 && intparm < 128)
|
||||
{
|
||||
|
|
@ -2082,6 +2088,7 @@ static void load_default_collection(default_collection_t *collection)
|
|||
default_t *def;
|
||||
char defname[80];
|
||||
char strparm[100];
|
||||
char line[256];
|
||||
|
||||
/* read the file in, overriding any set defaults */
|
||||
|
||||
|
|
@ -2096,12 +2103,28 @@ static void load_default_collection(default_collection_t *collection)
|
|||
return;
|
||||
}
|
||||
|
||||
while (!feof(f))
|
||||
while (fgets(line, sizeof(line), f) != NULL)
|
||||
{
|
||||
if (fscanf(f, "%79s %99[^\n]\n", defname, strparm) != 2)
|
||||
{
|
||||
/* This line doesn't match */
|
||||
strparm[0] = '\0';
|
||||
|
||||
/* Parse one physical line at a time. fscanf() with whitespace in
|
||||
* its format can consume the next line as a missing value.
|
||||
*/
|
||||
|
||||
if (strchr(line, '\n') == NULL &&
|
||||
strlen(line) == sizeof(line) - 1)
|
||||
{
|
||||
int ch;
|
||||
|
||||
while ((ch = fgetc(f)) != '\n' && ch != EOF)
|
||||
{
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sscanf(line, "%79s %99[^\n]", defname, strparm) != 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -2136,6 +2159,11 @@ static void load_default_collection(default_collection_t *collection)
|
|||
memmove(strparm, strparm + 1, sizeof(strparm) - 1);
|
||||
}
|
||||
|
||||
if (strparm[0] == '\0')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
set_variable(def, strparm);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue