Files
questshock/engine-debug-tools/engine-patches/02-desktop-debug-rsd8-decode-log.patch
T
ml 9d0a0d5798
build / build (push) Successful in 2m52s
Add engine-debug-tools and fix critter animation rendering bugs
- engine-debug-tools/: a debug dump hotkey + dump2png to inspect
  decoded critter frames independent of the renderer (see root
  README.md's "Debugging" section).
- engine-patches/04,05: fix critter sprite scale and an OpenGL
  texture cache collision, found using that tooling.
- mdlp: fix encodeFrames RLE-diffing upscaled frames against a
  previous-frame reference the real RSD8 decoder never implements
  (verified against Night Dive's original Mac source down to the
  68K assembly) - was rendering as scattered static in-game.
- Upscale pipeline: better color quantization (dithering, pure-ramp
  exclusion) and per-resource model selection.
2026-10-18 06:59:47 +02:00

167 lines
6.9 KiB
Diff

--- a/src/Libraries/2D/Source/RSD/rsdcvt.c
+++ b/src/Libraries/2D/Source/RSD/rsdcvt.c
@@ -37,6 +37,7 @@
*
*/
#include <assert.h>
+#include <stdio.h>
#include <string.h>
#include "grs.h"
#include "bitmap.h"
@@ -47,6 +48,14 @@
uchar *grd_unpack_buf=NULL;
+// Set by debug_dump_critter_animation() (engine-debug-tools/, root
+// README.md's "Debugging" section) around its own gr_rsd8_convert() calls,
+// so a trace of this decode's internals - which path ran, whether it wrote
+// fewer bytes than the bitmap's own area, and (gr_rsd8_unpack(),
+// RSDUnpack.c) each opcode consumed - prints only for those calls, not
+// every RSD8 decode in the game. Debug-build only.
+int qs_debug_rsd8_trace = 0;
+
/*************************************************/
/* Puts 0's in place of skips and pads with 0's. */
/* i.e., grd_unpack_buf is entirely overwritten. */
@@ -138,6 +147,15 @@
}
}
rsd_done:
+ if (qs_debug_rsd8_trace) {
+ long written = (long)(p_dst - dbm->bits);
+ long expected = (long)dbm->row * (long)dbm->h;
+ fprintf(stderr,
+ "[debug rsd8] path=%s sbm(w=%d h=%d row=%d) dbm(row=%d h=%d) "
+ "written=%ld expected=%ld short_by=%ld\n",
+ (dbm->w == dbm->row) ? "fast" : "slow", sbm->w, sbm->h, sbm->row,
+ dbm->row, dbm->h, written, expected, expected - written);
+ }
if ((over_run = (dbm->bits+dbm->row*dbm->h)-p_dst)) {
assert(over_run > 0);
LG_memset (p_dst, kSkipColor, over_run);
--- a/src/Libraries/2D/Source/RSD/RSDUnpack.c
+++ b/src/Libraries/2D/Source/RSD/RSDUnpack.c
@@ -22,6 +22,7 @@
//
#include "grs.h"
+#include <stdio.h>
#include <stdbool.h>
//----------------------------------------------------------------------------
@@ -34,6 +35,10 @@
short count,count2;
ushort longcode;
uint32_t longval, *longdest, *longsrc;
+
+ extern int qs_debug_rsd8_trace; // rsdcvt.c - engine-debug-tools/, root README.md's "Debugging" section
+ uchar *src_start = src, *dest_start = dest;
+ long op_index = 0;
do
{
@@ -42,6 +47,7 @@
{
count = *(src++); // get count
val = *(src++); // get val
+ if (qs_debug_rsd8_trace) fprintf(stderr, "[debug rsd8 op] #%ld src_off=%ld dest_off=%ld type=run count=%d val=%d\n", op_index++, (long)(src-src_start), (long)(dest-dest_start), (int)count, (int)val);
if (count>=kMinLongLoop) // if at least kMinLongLoop bytes, do long word stuff
{
@@ -63,6 +69,7 @@
else if (code<0x80) // dump (copy) bytes
{
count = code;
+ if (qs_debug_rsd8_trace) fprintf(stderr, "[debug rsd8 op] #%ld src_off=%ld dest_off=%ld type=dump count=%d\n", op_index++, (long)(src-src_start), (long)(dest-dest_start), (int)count);
if (code>=kMinLongLoop) // if at least kMinLongLoop bytes, do long word stuff
{
count2 = count>>2;
@@ -84,6 +91,7 @@
{
count = code & 0x007f; // clear high byte
val = longval = 0L;
+ if (qs_debug_rsd8_trace) fprintf(stderr, "[debug rsd8 op] #%ld src_off=%ld dest_off=%ld type=skip count=%d\n", op_index++, (long)(src-src_start), (long)(dest-dest_start), (int)count);
if (count>=kMinLongLoop) // if at least kMinLongLoop bytes, do long word stuff
{
@@ -105,11 +113,15 @@
longcode = * (ushort *) src;
src += 2L;
- if (!longcode) break; // done?
+ if (!longcode) {
+ if (qs_debug_rsd8_trace) fprintf(stderr, "[debug rsd8 op] #%ld src_off=%ld dest_off=%ld type=terminator\n", op_index++, (long)(src-src_start), (long)(dest-dest_start));
+ break; // done?
+ }
else if (longcode<0x8000) // skip (zero)
{
count = longcode;
val = longval = 0L;
+ if (qs_debug_rsd8_trace) fprintf(stderr, "[debug rsd8 op] #%ld src_off=%ld dest_off=%ld type=longskip count=%d\n", op_index++, (long)(src-src_start), (long)(dest-dest_start), (int)count);
if (count>=kMinLongLoop) // if at least kMinLongLoop bytes, do long word stuff
{
@@ -129,6 +141,7 @@
else if (longcode<0xC000) // dump (copy)
{
count = longcode & 0x7fff; // clear high bit
+ if (qs_debug_rsd8_trace) fprintf(stderr, "[debug rsd8 op] #%ld src_off=%ld dest_off=%ld type=longdump count=%d\n", op_index++, (long)(src-src_start), (long)(dest-dest_start), (int)count);
if (count>=kMinLongLoop) // if at least kMinLongLoop bytes, do long word stuff
{
@@ -151,6 +164,7 @@
{
count = longcode & 0x3fff;
val = *(src++); // get val
+ if (qs_debug_rsd8_trace) fprintf(stderr, "[debug rsd8 op] #%ld src_off=%ld dest_off=%ld type=longrun count=%d val=%d\n", op_index++, (long)(src-src_start), (long)(dest-dest_start), (int)count, (int)val);
if (count>=kMinLongLoop) // if at least kMinLongLoop bytes, do long word stuff
{
--- a/src/GameSrc/objsim.c
+++ b/src/GameSrc/objsim.c
@@ -1979,11 +1979,43 @@
}
if (fd->bm.type == BMT_RSD8) {
- if (gr_rsd8_convert(&fd->bm, &decoded) != 0 /* GR_UNPACK_RSD8_OK */) {
+ extern int qs_debug_rsd8_trace; // rsdcvt.c - engine-debug-tools/, root README.md's "Debugging" section
+ uchar *first_copy;
+ size_t nbytes;
+ int rsd8_ok;
+
+ qs_debug_rsd8_trace = 1;
+ rsd8_ok = gr_rsd8_convert(&fd->bm, &decoded) == 0 /* GR_UNPACK_RSD8_OK */;
+ qs_debug_rsd8_trace = 0;
+ if (!rsd8_ok) {
fprintf(stderr, "[debug dump] gr_rsd8_convert frame %d failed\n", frame);
RefUnlock(MKREF(s_lastCritterId, frame));
continue;
}
+
+ // Re-decode the same compressed source (fd->bm.bits, unchanged
+ // by the first call - only grd_unpack_buf, the shared
+ // decode-output buffer, gets overwritten) a second time and
+ // compare: decode runs back to back on this single thread, with
+ // nothing else able to touch fd->bm.bits or grd_unpack_buf in
+ // between, so a byte-for-byte match here means any garbled
+ // pixels are a deterministic property of this exact compressed
+ // stream (bad source data, or a decoder bug it triggers), not a
+ // one-off from a resource-cache slot being reused mid-decode.
+ nbytes = (size_t)decoded.w * (size_t)decoded.h;
+ first_copy = malloc(nbytes);
+ if (first_copy != NULL) {
+ memcpy(first_copy, decoded.bits, nbytes);
+
+ qs_debug_rsd8_trace = 1;
+ gr_rsd8_convert(&fd->bm, &decoded);
+ qs_debug_rsd8_trace = 0;
+
+ fprintf(stderr, "[debug dump] frame %d repeat-decode of bm.bits=%p %s\n", frame,
+ (void *)fd->bm.bits, memcmp(first_copy, decoded.bits, nbytes) == 0 ? "identical" : "DIFFERS");
+ free(first_copy);
+ }
+
bits = decoded.bits;
w = decoded.w;
h = decoded.h;