You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Omnibus commit covering items A-L from the third round of the audit list.
All changes stay within C++98/03 -- no static_assert, no nullptr, no
auto, no range-for, no move semantics, no = delete.
Correctness
===========
A. Nes_Effects_Buffer::SaveAudioBufferState / RestoreAudioBufferState
are no longer empty stubs. The base Effects_Buffer now implements
them: snapshots all 7 internal Blip_Buffers (via their per-Blip_Buffer
Save), the echo/reverb delay-line contents (~40 KB allocated
alongside the originals in set_sample_rate, freed in dtor), and the
echo_pos/reverb_pos cursors. The Nes_Effects_Buffer override chains
to the base and additionally saves the Nes_Nonlinearizer. Before
this, runahead with the stereo-panning option silently produced
garbage audio on every rollback.
B. retro_serialize and retro_unserialize now snapshot/restore the audio
buffer state unconditionally, not only when the frontend's
FAST_SAVESTATES bit is set. Within-session save->load round-trips
now preserve audio bit-for-bit. The is_fast_savestate() helper is
removed.
Cross-session loads (close emulator, reopen, load file) still fall
back to the existing clear_sound_buf() fade-in: the snapshot lives
in process-local extra_buffer storage inside each Blip_Buffer, not
in the user-provided save state bytes, so a freshly constructed
Blip_Buffer has nothing to restore from. To make this safe, every
Save site now sets a new extra_valid sentinel and every Restore
site bails out when it is false. Without the sentinel, removing the
is_fast_savestate guard would have silently zeroed the audio state
on every cross-session load.
C. Blip_Buffer::extra_buffer grows from 32 longs to 1024. The old size
only worked because the typical save point is immediately after
read_samples has drained the buffer to the ~18-long impulse-response
tail. Any callsite that serializes mid-frame (or any future change
that lets samples_avail() exceed 14 between frames) would have
silently corrupted audio across fast-savestate restores. Save and
Restore now copy samples_avail() + buffer_extra (clamped to the
1024-entry capacity) and Restore zeros the live region beyond it so
no 'future' samples written during speculative emulation leak back
into the restored read state.
Performance
===========
D. The 256-entry retro_palette in retro_run is no longer rebuilt every
frame. It is regenerated only when frame.palette[] changes (the
game writing to PPU palette RAM, which is typically rare -- startup,
scene transitions) or when the user picks a different palette
(changes palette_index). Saves ~256 RGB->RGB565 conversions per
frame in the steady state.
E. retro_serialize_size() result is cached after the first call.
Some frontends call this every frame during runahead; the previous
implementation re-ran a full save_state into a throwaway Mem_Writer
each time. The cache is invalidated on retro_load_game and
retro_unload_game (different cart -> different mapper -> different
serialized size).
F. Nes_Emu::save_state and Nes_Emu::load_state used to 'new Nes_State'
(~21 KB) and 'delete' it on every call. The libretro path is
single-threaded with one Nes_Emu per process, so two file-scope
static Nes_State scratch buffers do the job with no heap traffic.
G. Nes_Mapper::create() converted from a chain of independent 'if'
tests to a single switch. Cosmetic perf, but it also closes the
silent footgun where two entries for the same mapperCode would
both execute -- the second would have leaked the first.
Robustness / style
==================
H. Auto_File_Reader::operator= and Auto_File_Writer::operator= no
longer launder their destructive transfer through a const_cast
on a const& parameter (UB if the source is a real const object).
The Data_Reader*/Data_Writer* members are now declared mutable,
which is the proper C++98 fix and which the original author
evidently considered -- the previous declaration was literally
'/* mutable */ Data_Reader* data;'.
I. retro_load_game guards against being called twice without an
intervening retro_unload_game (which the spec forbids, but
well-behaved cores defend against it anyway since leaking the
prior emu would also leave the global Multi_Buffer wired to a
destroyed audio chain).
K. Nes_Mapper::register_state now asserts that the registered state
size fits in mapper_state_t (currently 512 bytes). assert() is
compiled out under -DNDEBUG so shipping builds pay nothing. If
any future mapper grows past the limit, save_state would have
silently truncated it; this catches that at the earliest
possible point.
L. The mono->stereo path in retro_run now names the stereo-frame
count instead of relying on a bare 'read_samples >> 1'.
J (PAL region support) is deliberately not in this commit -- it is a
feature, not a fix, and requires plumbing the pal flag through
Nes_Emu and adjusting frame_rate and the APU/DMC period tables. It
deserves its own change.
0 commit comments