Files
lr-snes9x/crosshairs.cpp
LibretroAdmin 3702e90cbd Route all core file I/O through the libretro VFS
Pull the libretro VFS stack into libretro/libretro-common (from the
snes9x2010 tree's curated copy): vfs_implementation, file_stream,
file_stream_transforms, file_path/file_path_io, the compat family
(strl/posix_string/strcasestr/snprintf/fopen_utf8), encoding_utf,
stdstring and rtime, plus the include tree. libretro.h is upgraded to
the current version -- required because file_path_io uses the
retro_vfs_stat_64_t extension the old 2020-era header predates. A
file_stream_transforms.h header is reconstructed from the .c's
signatures (the source tree ships the implementation without it).
Makefile.common builds these outside STATIC_LINKING, same split as
snes9x2010: statically linked frontends export all of these symbols
themselves. retro_set_environment now requests
RETRO_ENVIRONMENT_GET_VFS_INTERFACE and initializes filestream and
path with it; without a frontend VFS, filestream falls back to its
local implementation.

Conversion:
- The FSTREAM macro layer in snes9x.h moves wholesale from the
  ZLIB/stdio dual implementation to RFILE (rfopen family), which
  converts every STREAM/fStream consumer at once: stream.cpp,
  snapshot.cpp, msu1's fStream path, conffile parsing, memmap's
  soft-patch loader and snes9x.cpp. Compressed (.gz) stream support
  is gone by design -- the frontend handles compressed content.
  REOPEN_FSTREAM / reopenStreamFromFd (fdopen-based, no VFS
  equivalent, zero callers) are deleted.
- Direct stdio converted to RFILE: memmap (BIOS + SRTC RTC data),
  crosshairs (text format), cheats2 (load/save), apu (SPC dump),
  conffile SaveTo, bsx (Satellaview stream files; SBSX struct fields
  retyped), libretro.cpp's BIOS loader, and the DEBUGGER trace
  handles in debug.h / snes9x.cpp / cpuops.cpp.
- bml switches from std::ifstream to filestream_read_file plus an
  std::istringstream; the parser itself now takes std::istream.
- minizip's ioapi.c backend is rewritten on RFILE, so unzOpen (zipped
  ROM loading, MSU-1 .msu1 packs) goes through the VFS; the stdio
  fopen64/fseeko64 platform selection in ioapi.h is removed. The
  unused minizip tools (zip.c, miniunz.c, minizip.c, mztools,
  iowin32) are deleted. zlib remains linked for DEFLATE
  decompression only.
- crosshairs' HAVE_LIBPNG block is removed: never defined in this
  build, and png_init_io requires stdio FILE.

stderr/stdout console logging is intentionally untouched -- it is not
file I/O. After conversion, a tree-wide audit finds no fopen family,
gzopen, fstream, or fdopen outside libretro-common.

Validation:
- Functional VFS smoke: loading a ROM by path (data == NULL) through
  memmap -> fStream -> filestream produces the byte-identical m7 test
  ROM hash, and loading the same ROM from a .zip exercises
  loadzip -> unzip -> the RFILE ioapi backend with the identical
  hash again.
- Full gauntlet byte-exact: Contra III savestate vs pre-port oracle,
  Mega Man X2, Mode 5 bright/dim, m7 option matrix, probe
  (serialize size 823407).
- ASan+UBSan+LSan over the probe, the path-load and the zip-load
  scenarios: zero findings in the new I/O paths; the only report is
  a pre-existing 288-byte S9xGetCommandT leak (controls.cpp input
  bindings, unrelated).

Untested but converted by the same mechanical mapping: SRTC RTC
file, BS-X Satellaview streams, cheat file save, SPC dump, DEBUGGER
traces.
2026-08-04 02:08:38 +02:00

389 lines
8.0 KiB
C++

/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
#include "port.h"
#include "crosshairs.h"
#include <streams/file_stream_transforms.h>
static const char *crosshairs[32] =
{
"` " // Crosshair 0 (no image)
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" "
" ",
"` " // Crosshair 1 (the classic small dot)
" "
" "
" "
" "
" "
" "
" #. "
" "
" "
" "
" "
" "
" "
" ",
"` " // Crosshair 2 (a standard cross)
" "
" "
" "
" .#. "
" .#. "
" ...#... "
" ####### "
" ...#... "
" .#. "
" .#. "
" "
" "
" "
" ",
"` .#. " // Crosshair 3 (a standard cross)
" .#. "
" .#. "
" .#. "
" .#. "
" .#. "
".......#......."
"###############"
".......#......."
" .#. "
" .#. "
" .#. "
" .#. "
" .#. "
" .#. ",
"` " // Crosshair 4 (an X)
" "
" "
" . . "
" .#. .#. "
" .#. .#. "
" .#.#. "
" .#. "
" .#.#. "
" .#. .#. "
" .#. .#. "
" . . "
" "
" "
" ",
"`. . " // Crosshair 5 (an X)
".#. .#."
" .#. .#. "
" .#. .#. "
" .#. .#. "
" .#. .#. "
" .#.#. "
" .#. "
" .#.#. "
" .#. .#. "
" .#. .#. "
" .#. .#. "
" .#. .#. "
".#. .#."
" . . ",
"` " // Crosshair 6 (a combo)
" "
" "
" "
" # . # "
" # . # "
" #.# "
" ...#... "
" #.# "
" # . # "
" # . # "
" "
" "
" "
" ",
"` . " // Crosshair 7 (a combo)
" # . # "
" # . # "
" # . # "
" # . # "
" # . # "
" #.# "
".......#......."
" #.# "
" # . # "
" # . # "
" # . # "
" # . # "
" # . # "
" . ",
"` # " // Crosshair 8 (a diamond cross)
" #.# "
" # . # "
" # . # "
" # . # "
" # . # "
" # . # "
"#......#......#"
" # . # "
" # . # "
" # . # "
" # . # "
" # . # "
" #.# "
" # ",
"` ### " // Crosshair 9 (a circle cross)
" ## . ## "
" # . # "
" # . # "
" # . # "
" # . # "
"# . #"
"#......#......#"
"# . #"
" # . # "
" # . # "
" # . # "
" # . # "
" ## . ## "
" ### ",
"` .#. " // Crosshair 10 (a square cross)
" .#. "
" .#. "
" ....#.... "
" .#######. "
" .# #. "
"....# #...."
"##### #####"
"....# #...."
" .# #. "
" .#######. "
" ....#.... "
" .#. "
" .#. "
" .#. ",
"` .#. " // Crosshair 11 (an interrupted cross)
" .#. "
" .#. "
" .#. "
" .#. "
" "
"..... ....."
"##### #####"
"..... ....."
" "
" .#. "
" .#. "
" .#. "
" .#. "
" .#. ",
"`. . " // Crosshair 12 (an interrupted X)
".#. .#."
" .#. .#. "
" .#. .#. "
" .#. .#. "
" "
" "
" "
" "
" "
" .#. .#. "
" .#. .#. "
" .#. .#. "
".#. .#."
" . . ",
"` . " // Crosshair 13 (an interrupted combo)
" # . # "
" # . # "
" # . # "
" # . # "
" "
" "
"..... ....."
" "
" "
" # . # "
" # . # "
" # . # "
" # . # "
" . ",
"`#### #### " // Crosshair 14
"#.... ....#"
"#. .#"
"#. .#"
"#. .#"
" # "
" # "
" ##### "
" # "
" # "
"#. .#"
"#. .#"
"#. .#"
"#.... ....#"
" #### #### ",
"` .# #. " // Crosshair 15
" .# #. "
" .# #. "
"....# #...."
"##### #####"
" "
" "
" "
" "
" "
"##### #####"
"....# #...."
" .# #. "
" .# #. "
" .# #. ",
"` # " // Crosshair 16
" # "
" # "
" ....#.... "
" . # . "
" . # . "
" . # . "
"###############"
" . # . "
" . # . "
" . # . "
" ....#.... "
" # "
" # "
" # ",
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
bool S9xLoadCrosshairFile (int idx, const char *filename)
{
if (idx < 1 || idx > 31)
return (false);
char *s = (char *) calloc(15 * 15 + 1, sizeof(char));
if (s == NULL)
{
fprintf(stderr, "S9xLoadCrosshairFile: malloc error while reading ");
perror(filename);
return (false);
}
RFILE *fp = rfopen(filename, "rb");
if (fp == NULL)
{
fprintf(stderr, "S9xLoadCrosshairFile: Couldn't open ");
perror(filename);
free(s);
return (false);
}
size_t l = rfread(s, 1, 8, fp);
if (l != 8)
{
fprintf(stderr, "S9xLoadCrosshairFile: File is too short!\n");
free(s);
rfclose(fp);
return (false);
}
/* PNG crosshair support removed with the libretro VFS conversion: it
was dead in this build (HAVE_LIBPNG never defined) and png_init_io
requires stdio FILE I/O. Text-format crosshair files remain. */
{
l = rfread(s + 8, 1, 15 - 8, fp);
if (l != 15 - 8)
{
fprintf(stderr, "S9xLoadCrosshairFile: File is too short!\n");
free(s);
rfclose(fp);
return (false);
}
if (rfgetc(fp) != '\n')
{
fprintf(stderr, "S9xLoadCrosshairFile: Invalid file format! (note: PNG support is not available)\n");
free(s);
rfclose(fp);
return (false);
}
for (int r = 1; r < 15; r++)
{
l = rfread(s + r * 15, 1, 15, fp);
if (l != 15)
{
fprintf(stderr, "S9xLoadCrosshairFile: File is too short! (note: PNG support is not available)\n");
free(s);
rfclose(fp);
return (false);
}
if (rfgetc(fp) != '\n')
{
fprintf(stderr, "S9xLoadCrosshairFile: Invalid file format! (note: PNG support is not available)\n");
free(s);
rfclose(fp);
return (false);
}
}
for (int i = 0; i < 15 * 15; i++)
{
if (s[i] != ' ' && s[i] != '#' && s[i] != '.')
{
fprintf(stderr, "S9xLoadCrosshairFile: Invalid file format! (note: PNG support is not available)\n");
free(s);
rfclose(fp);
return (false);
}
}
}
rfclose(fp);
if (crosshairs[idx] != NULL && crosshairs[idx][0] != '`')
free((void *) crosshairs[idx]);
crosshairs[idx] = s;
return (true);
}
const char * S9xGetCrosshair (int idx)
{
if (idx < 0 || idx > 31)
return (NULL);
return (crosshairs[idx]);
}