Thanks to visit codestin.com
Credit goes to github.com

Skip to content

MADE: support games "Richard Scarry's B* Neighborhood Disc Ever!" - #7855

Open
greg-kennedy wants to merge 14 commits into
scummvm:masterfrom
greg-kennedy:made-scarry
Open

MADE: support games "Richard Scarry's B* Neighborhood Disc Ever!"#7855
greg-kennedy wants to merge 14 commits into
scummvm:masterfrom
greg-kennedy:made-scarry

Conversation

@greg-kennedy

Copy link
Copy Markdown
Contributor

MADE: support games "Richard Scarry's B* Neighborhood Disc Ever!"

Adds some support for "version 3.1" MADE engine games (as determined
by internal text in MADE.EXE:
"MADE 386 CD-ROM Alpha V3.10 - Copyright (c) 1993, Activision")

Version 3.1 is used in a small handful of games: this PR focuses
on adding support for two previously un-emulated edutainment
titles "Richard Scarry's Best Neighborhood Disc Ever!"
and "Richard Scarry's Busiest Neighborhood Disc Ever!"

There is also a demo of Best Neighborhood Ever, available on
the 1.1 Activision CD for "Return to Zork". It is supported as well.

Getting these games working required the following changes:

  • Add MD5 hashes to detection_tables.h
  • A slightly different layout for 3.1 database objects
  • Three new Script Functions
    • sfCursorXY (warps the cursor),
    • sfSoundFile (plays a sound from an external file), and
    • sfMovieCall (plays a movie in the "background")
  • sfMovieCall required more changes in the engine:
    • Script VM needs to return a value rather than exiting on final cmd_return
    • PMVPlayer split into separate functions to load, play, and close
  • support for RLE-compresed movie frames. The tables used for PMV playback
    are decompressed and then passed to the existing PMVPlayer, when indicated by flags.

Print byte-arrays and word-arrays, attempting to detect ASCII in byte-arrays as well.  Dump object contents as ASCII.
Add initial support for MADE engine games using V3.10 of the interpreter. This version is used for edutainment game "Richard Scarry's Best Neighborhood Disc Ever!" and its demo, but also for the MacOS port of Return to Zork (in big-endian).

The demo of RSBESTNDE is somewhat playable, but some outstanding issues remain:
* changes to the PMV movie format
* three new extended opcodes, of which "MovieCall" and "SoundFile" are used extensively
MADE V3.1 games add a layer of RLE compression to video frames.  Decompressing these first (according to the flags fields) allows RSBESTNDE / RSBUSYNDE to play videos correctly.
Add support for two external script function calls added by V3.1 games.
* CursorXY: warp mouse cursor to the provided X/Y coordinates
* SoundFile: load, decompress and play externally named sound file
sfMovieCall extension requires the ability to play one frame at a time.
Split play() into separate load / decode / close, and wrap the previous
behavion in play() by calling the new sub-functions.
Adds support for the sfMovieCall script function, which plays a
movie, but also executes a script function on every frame.

This makes the full versions of RSBUSYNDE / RSBESTNDE playable.
@greg-kennedy

greg-kennedy commented Aug 20, 2026

Copy link
Copy Markdown
Contributor Author

This is a re-do of my PR #7844 which I closed to change branch names, and focus more on just the Richard Scarry games rather than 3.1 as a whole. I did read the comments there and left some replies - let me know if any of those are unclear or warrant changes.

Specifically I'd like feedback on whether dynamic memory alloc per-frame for the decompress tables is OK or should be looked at with suspicion :) ... I did replace this with static Common::Array members instead, which makes me feel better about it ... static is discouraged in scummvm so it's back to per-frame alloc instead.

Comment thread engines/made/graphics.cpp Outdated
RLE decompression output size is unpredictable and the
previous calculation for fixed buffers was incorrect in
most cases.  Use dynamic arrays instead, with static
scope so they do not need to be re-allocated or deleted
on every frame.

Also silences a warning about A/V sync on the first frame
of every movie.
Some scripts (specifically, sfMovieCall-invoked) want to
read arguments passed in.  A new ScriptInterpreter has no
call frame setup, and so attempting to read args would
peek out-of-bounds, returning uninitialized data instead.

This commit changes runScript to set up a correct-looking
call frame on the stack before executing, where args are
put on the stack at the expected place.

sfMovieCall sends one argument to the sub-function now,
which is the ID of the playing movie.  It isn't clear to
me if this is correct or not: the script seems to check
the value with ">= 0", so perhaps it is meant to be the
remaining frame-count of the movie or the duration left.

Regardless, sending a predictable positive value every
time instead of uninitialized garbage fixes intermittent
errors with RSBESTNDE / RSBUSYNDE skipping every movie
at the first frame.
Comment thread engines/made/scriptfuncs.cpp Outdated
Comment thread engines/made/graphics.cpp Outdated
Screen mask is only used with V2 games and not needed here.

Fixes up some other minor GID checked behaviors as well.
Comment thread engines/made/database.cpp
Comment thread engines/made/scriptfuncs.cpp Outdated
Comment thread engines/made/pmvplayer.cpp Outdated
PMV frame sizes are not always constant during a movie,
and a smaller frame should blit to the top-left corner
of the previous frame, leaving the right or bottom margin
untouched.  Change decompressMovieImage to accept a width
and height argument, then use those when blitting.  Fixes
corrupted screen in RSBESTNDE (Demo) credits.

Also removes `static` scope from byte arrays in RLE
decompression functions.  They are now allocated as-needed
each frame and freed after use.

@fusefib fusefib left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, great job putting this together and - as far as I see - in good working order. It's good if this gets in IMO.

I included a small file just to better highlight the small items of concern, there may be better solutions or some points may be invalid.

stuff.diff.txt

Comment thread engines/made/pmvplayer.cpp
Comment thread engines/made/pmvplayer.cpp Outdated
close();
}

return !aborted;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may have to better account for load or decode failure? If load() fails, or decode_frame() breaks early, aborted is still false so play() reports successful completion.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to leave this as-is, it matches the previous behavior. _aborted is only used to signal early user cancel. From the script's point of view, a movie that fails to load (or decode) looks like a movie that played all the way through uninterrupted.

Comment thread engines/made/scriptfuncs.cpp Outdated
Comment thread engines/made/scriptfuncs.cpp Outdated
Comment thread engines/made/scriptfuncs.cpp
Comment thread engines/made/graphics.cpp Outdated
Make some items auto-scope (stack) so they are cleaned up properly,
consolidate PMVPlayer::load() error handling, fix movie frame number
counting, call stopSound() before sfSoundFile
Comment thread engines/made/database.cpp
}


int ObjectV3_1::load(Common::SeekableReadStream &source) {

@bluegr bluegr Aug 30, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is that different from ObjectV3::load()? Can't these two be merged?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The diff. is in size calculation and the objects are laid out differently internally. V3 objects size field is 2 bytes (here "count1" and "count2" where count1 is the number of key/value properties on this object (class) and count2 is storage space assigned for values of inherited properties from parent objects. V3_1 simplifies this, size field is instead int16 which is a flat count of the number of key/value int16. Compare lines 202-203 to line 276.

The property lookup is also very different, V3 objects require looking for property ID on a Parent and then cross-ref. that to some storage area on the Child, whereas V3_1 is just a plain search, everything is right there on the obj. or it isn't.

I guess these could be merged with some kind of "sub version" flag but I am not sure that is any clearer to follow.

Comment thread engines/made/database.cpp Outdated
Comment thread engines/made/graphics.cpp Outdated
Comment thread engines/made/pmvplayer.cpp Outdated
Comment thread engines/made/pmvplayer.cpp Outdated
Some simplification based on PR feedback: GameDatabaseV3 now will
accept both 3.00 and 3.10 files, differentiated by version ID
loaded from file.

Fixes MHED parsing in PMV to read framerate from correct field,
eliminating sound sample rate patch as it is no longer needed with
the correct frameDelay.

sfMovieCall passes movie frame number to callback function instead
of movie ID, this seems to match what the original engines do.
Comment thread engines/made/pmvplayer.cpp
Comment thread engines/made/resource.cpp
Comment thread engines/made/script.cpp Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants