@@ -248,6 +248,138 @@ for rom in "${ROM_DIR}"/*.j64 "${ROM_DIR}"/*.rom; do
248248 fi
249249done
250250
251+ # --- Save state round-trip test ---
252+ # Validates retro_serialize/retro_unserialize: save state at frame N,
253+ # load it in a fresh run, continue for M frames. Compare the final
254+ # screenshot with frame N+M of a straight reference run.
255+ #
256+ # miniretro timing: --dump-savestates-every N captures state AFTER
257+ # retro_run for frame N. Loading that state and running M more
258+ # retro_runs produces the same output as a straight run's frame N+M.
259+ echo " "
260+ echo " ==> Running save state round-trip test..."
261+ SS_SAVE_AT=200
262+ SS_RESUME_LEN=200
263+ SS_REF_FRAME=$(( SS_SAVE_AT + SS_RESUME_LEN))
264+ for rom in " ${ROM_DIR} " /* .j64 " ${ROM_DIR} " /* .rom; do
265+ [ -f " ${rom} " ] || continue
266+ rom_name=" $( basename " ${rom} " | sed ' s/\.[^.]*$//' ) "
267+
268+ # Run 1: straight reference run, capture screenshot at target frame
269+ ss_ref=" ${WORK_DIR} /ss_ref/${rom_name} "
270+ mkdir -p " ${ss_ref} "
271+ " ${MINIRETRO_BIN} " \
272+ --core " ${CORE} " --rom " ${rom} " \
273+ --output " ${ss_ref} " --system " ${ss_ref} " \
274+ --frames $(( SS_REF_FRAME + 1 )) \
275+ --dump-frames-every " ${SS_REF_FRAME} " \
276+ --no-alarm > /dev/null 2>&1 || true
277+ ref_frame=$( find " ${ss_ref} " -name " screenshot*.png" 2> /dev/null | sort | tail -1)
278+
279+ # Run 2: save state at frame SS_SAVE_AT
280+ ss_save=" ${WORK_DIR} /ss_save/${rom_name} "
281+ mkdir -p " ${ss_save} "
282+ " ${MINIRETRO_BIN} " \
283+ --core " ${CORE} " --rom " ${rom} " \
284+ --output " ${ss_save} " --system " ${ss_save} " \
285+ --frames $(( SS_SAVE_AT + 1 )) \
286+ --dump-savestates-every " ${SS_SAVE_AT} " \
287+ --no-alarm > /dev/null 2>&1 || true
288+ state_file=$( find " ${ss_save} " -name " state*.bin" 2> /dev/null | sort | tail -1)
289+
290+ # Run 3: load state, run SS_RESUME_LEN frames, capture final screenshot
291+ # After loading state@N and running M-1 retro_runs, the last
292+ # screenshot dumped at interval M-1 corresponds to ref frame N+M.
293+ ss_load=" ${WORK_DIR} /ss_load/${rom_name} "
294+ mkdir -p " ${ss_load} "
295+ if [ -n " ${state_file} " ]; then
296+ " ${MINIRETRO_BIN} " \
297+ --core " ${CORE} " --rom " ${rom} " \
298+ --output " ${ss_load} " --system " ${ss_load} " \
299+ --frames " ${SS_RESUME_LEN} " \
300+ --dump-frames-every $(( SS_RESUME_LEN - 1 )) \
301+ --load-savestate " ${state_file} " \
302+ --no-alarm > /dev/null 2>&1 || true
303+ fi
304+ load_frame=$( find " ${ss_load} " -name " screenshot*.png" 2> /dev/null | sort | tail -1)
305+
306+ if [ -z " ${ref_frame} " ] || [ -z " ${state_file} " ] || [ -z " ${load_frame} " ]; then
307+ echo " FAIL: ${rom_name} save state (missing frames or state file)"
308+ FAIL=$(( FAIL + 1 ))
309+ SUMMARY=" ${SUMMARY} | ${rom_name} (save state) | :x: FAIL | missing output | - |\n"
310+ elif cmp -s " ${ref_frame} " " ${load_frame} " ; then
311+ echo " PASS: ${rom_name} save state round-trip (frame ${SS_REF_FRAME} matches)"
312+ PASS=$(( PASS + 1 ))
313+ SUMMARY=" ${SUMMARY} | ${rom_name} (save state) | :white_check_mark: PASS | round-trip matches | - |\n"
314+ else
315+ echo " FAIL: ${rom_name} save state round-trip (frame ${SS_REF_FRAME} differs)"
316+ cp " ${ref_frame} " " ${DIFF_DIR} /${rom_name} _ss_ref.png"
317+ cp " ${load_frame} " " ${DIFF_DIR} /${rom_name} _ss_load.png"
318+ FAIL=$(( FAIL + 1 ))
319+ SUMMARY=" ${SUMMARY} | ${rom_name} (save state) | :x: FAIL | round-trip mismatch | See artifacts |\n"
320+ fi
321+ done
322+
323+ # --- Rewind simulation test ---
324+ # Simulates rewind: run past frame N while saving states periodically,
325+ # then load the frame-N state and run forward again. The result must
326+ # match the reference from the save state test above.
327+ echo " "
328+ echo " ==> Running rewind simulation test..."
329+ RW_REWIND_TO=${SS_SAVE_AT}
330+ RW_REMAIN=${SS_RESUME_LEN}
331+ RW_REF_FRAME=${SS_REF_FRAME}
332+ for rom in " ${ROM_DIR} " /* .j64 " ${ROM_DIR} " /* .rom; do
333+ [ -f " ${rom} " ] || continue
334+ rom_name=" $( basename " ${rom} " | sed ' s/\.[^.]*$//' ) "
335+
336+ # Reuse reference frame from save state test
337+ ref_frame=$( find " ${WORK_DIR} /ss_ref/${rom_name} " -name " screenshot*.png" 2> /dev/null | sort | tail -1)
338+
339+ # Run past the rewind point, dumping states every 100 frames
340+ rw_full=" ${WORK_DIR} /rw_full/${rom_name} "
341+ mkdir -p " ${rw_full} "
342+ " ${MINIRETRO_BIN} " \
343+ --core " ${CORE} " --rom " ${rom} " \
344+ --output " ${rw_full} " --system " ${rw_full} " \
345+ --frames $(( RW_REF_FRAME + 1 )) \
346+ --dump-savestates-every 100 \
347+ --no-alarm > /dev/null 2>&1 || true
348+
349+ # Find state file for the rewind point
350+ rw_state=" ${rw_full} /state$( printf ' %06d' ${RW_REWIND_TO} ) .bin"
351+
352+ # Load rewind state and continue
353+ rw_resume=" ${WORK_DIR} /rw_resume/${rom_name} "
354+ mkdir -p " ${rw_resume} "
355+ if [ -f " ${rw_state} " ]; then
356+ " ${MINIRETRO_BIN} " \
357+ --core " ${CORE} " --rom " ${rom} " \
358+ --output " ${rw_resume} " --system " ${rw_resume} " \
359+ --frames " ${RW_REMAIN} " \
360+ --dump-frames-every $(( RW_REMAIN - 1 )) \
361+ --load-savestate " ${rw_state} " \
362+ --no-alarm > /dev/null 2>&1 || true
363+ fi
364+ rw_frame=$( find " ${rw_resume} " -name " screenshot*.png" 2> /dev/null | sort | tail -1)
365+
366+ if [ -z " ${ref_frame} " ] || [ ! -f " ${rw_state} " ] || [ -z " ${rw_frame} " ]; then
367+ echo " FAIL: ${rom_name} rewind (missing frames or state file)"
368+ FAIL=$(( FAIL + 1 ))
369+ SUMMARY=" ${SUMMARY} | ${rom_name} (rewind) | :x: FAIL | missing output | - |\n"
370+ elif cmp -s " ${ref_frame} " " ${rw_frame} " ; then
371+ echo " PASS: ${rom_name} rewind (resume from frame ${RW_REWIND_TO} matches)"
372+ PASS=$(( PASS + 1 ))
373+ SUMMARY=" ${SUMMARY} | ${rom_name} (rewind) | :white_check_mark: PASS | rewind matches | - |\n"
374+ else
375+ echo " FAIL: ${rom_name} rewind (resume from frame ${RW_REWIND_TO} differs)"
376+ cp " ${ref_frame} " " ${DIFF_DIR} /${rom_name} _rw_ref.png"
377+ cp " ${rw_frame} " " ${DIFF_DIR} /${rom_name} _rw_resume.png"
378+ FAIL=$(( FAIL + 1 ))
379+ SUMMARY=" ${SUMMARY} | ${rom_name} (rewind) | :x: FAIL | rewind mismatch | See artifacts |\n"
380+ fi
381+ done
382+
251383echo " "
252384echo " ==> Results: ${PASS} passed, ${FAIL} failed, ${NEW} new (no baseline)"
253385
0 commit comments