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

Skip to content

ENGINES: Add support for Ripper - #7839

Draft
alexbevi wants to merge 414 commits into
scummvm:masterfrom
alexbevi:ripper
Draft

ENGINES: Add support for Ripper#7839
alexbevi wants to merge 414 commits into
scummvm:masterfrom
alexbevi:ripper

Conversation

@alexbevi

@alexbevi alexbevi commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

This PR adds an initial, Ghidra-backed implementation of the DOS game Ripper.

scummvm-ripper-00002

As with the previous re-implementation of Harvester, this PR uses Ghidra and Codex (notes available in this gist). AI-GUIDELINES.md was followed, and to make validation easier, commit messages include the binary disassembly notes and function addresses as references.

The original work was done against the v1.0 game, but I eventually introduced v1.05 functionality such as interaction subtitles. I documented the IAVF format in greater detail in this blog post, and will look to add more content over time.

Shared code changes

Only two shared runtime areas are changed, so I wanted to call them out directly.

Area Reason
IMAGE IFF decoder (8d706d7dd82, a42c63d6c5f) Ripper PBM rows use an even-byte stored stride when their logical width is odd. The shared decoder gains a protected override point, while an engine-local subclass skips the padding. Other engines retain the existing PBM behavior.
GUI list scrolling (4a93237885f) ListWidget::scrollTo() clamps the logical row before calculating its fluid-scroll position. This prevents a remembered save-chooser position from clipping or moving the first entries when every slot fits onscreen.
Tests (6d6f5d76fef) test/module.mk includes the Ripper test suites and static engine library when Ripper is configured as a static plugin.

No common media, audio, event, savefile or serializer code is modified.

Run with ./scummvm -d 3 --debugflags=general,resources,scripts,scene,input,audio,cursor,video,dialogue,wac,saveload,milestones,puzzles,cyber,combat ripper for full debug output.

Screenshots

scummvm-ripper-00003 scummvm-ripper-00004 scummvm-ripper-00005 scummvm-ripper-00006 scummvm-ripper-00007 scummvm-ripper-00008 scummvm-ripper-00009 scummvm-ripper-00010

alexbevi added 30 commits August 4, 2026 06:04
References:
- RIPPER.LE: RunTubeSwitchScene at 0x25e18
- RIPPER.LE: RunFrontEndActionMenu at 0x18b3a
- RIPPER.LE: RunUnlockGatedSelectionMenu at 0x360ae
- RIPPER.LE: ExecuteUnlockSelectionChoice at 0x364be

Assisted-by: Codex:gpt-5
References:
- RIPPER.LE: RunKaDialogueScene at 0x2aef5

Assisted-by: Codex:gpt-5
References:
- RIPPER.LE: RunTubeSwitchScene at 0x25e18
- RIPPER.LE: RunKaDialogueScene at 0x2aef5
- RIPPER.LE: RunCainDialogueScene at 0x2c160
- RIPPER.LE: RunCrystalPiecePlacementPuzzleScene at 0x2710c
- RIPPER.LE: RunRolodexSequencePuzzleScene at 0x280ae
- RIPPER.LE: RunShockLeverPuzzleScene at 0x3affb

Assisted-by: Codex:gpt-5
The powered switch loop must reuse one Smacker decoder because later
frames depend on its delta-coded surface and palette.

References:
- RIPPER.LE: RunTubeSwitchScene at 0x25e18
- RIPPER.LE: retained-decoder seek path at 0x263ae

Assisted-by: Codex:gpt-5
References:
- RIPPER.LE: RunCombatEncounterScene at 0x31436
- RIPPER.LE: LoadCombatEncounterResourceSet at 0x34d6e
- RIPPER.LE: LoadPresentationFrameRegionTable at 0x35615
- RIPPER.LE: LoadPresentationFrameAudioCueMap at 0x3574a

Assisted-by: Codex:gpt-5
References:
- RIPPER.LE: RunGcCshFourChoiceSequencePuzzleScene at 0x38871
- RIPPER.LE: DispatchSceneEntryAction at 0x36892
- RIPPER.LE: PreparePacketizedMediaPlaybackBranchSetup at 0x5b237

Assisted-by: Codex:gpt-5
Player depletion follows the Escape cleanup path and must not set the
completion flag.

References:
- RIPPER.LE: RunCombatEncounterScene at 0x31436

Assisted-by: Codex:gpt-5
References:
- RIPPER.LE: RunGcCshFourChoiceSequencePuzzleScene at 0x38871

Assisted-by: Codex:gpt-5
The cursor frame and palette must be reinstalled after puzzle media even
when the requested cursor row is unchanged.

References:
- RIPPER.LE: RunGcCshFourChoiceSequencePuzzleScene at 0x3868b
- RIPPER.LE: cursor reinstallation paths at 0x38986 through 0x389a2
  and 0x38e98 through 0x38ea1

Assisted-by: Codex:gpt-5
References:
- RIPPER.LE: RunGcCshFourChoiceSequencePuzzleScene at 0x38871
- RIPPER.LE: ServiceUiControlStateSelection at 0x4a912

Assisted-by: Codex:gpt-5
Save version 4 adds the toolbar mask. Older supported saves restore the
original full mask.

References:
- RIPPER.LE: DispatchSceneEntryAction at 0x36892
- RIPPER.LE: PollInteractionAndResolveSelection at 0x13c8d

Assisted-by: Codex:gpt-5
A scene action unlocks the new item and clears its consumed partner
before opening the chooser. The toolbar opens the same chooser without
mutating inventory.

References:
- RIPPER.LE: DispatchSceneEntryAction at 0x36892
- RIPPER.LE: RunUnlockGatedSelectionMenu at 0x360ae
- RIPPER.LE: ExecuteUnlockSelectionChoice at 0x364be

Assisted-by: Codex:gpt-5
References:
- RIPPER.LE: RunUnlockGatedSelectionMenu at 0x360ae
- RIPPER.LE: ExecuteUnlockSelectionChoice at 0x364be
- RIPPER.LE: InitializeSharedPresentationTemplates at 0x1196f

Assisted-by: Codex:gpt-5
References:
- RIPPER.LE: DrawPromptChooserTemplateLabelCallback at 0x16d0f

Assisted-by: Codex:gpt-5
References:
- RIPPER.LE: RunTableGateLeverPuzzleScene at 0x3912a

Assisted-by: Codex:gpt-5
References:
- RIPPER.LE: RunClockPuzzleScene at 0x374c5
- RIPPER.LE: PlayClockSmackerOverlayAnimation at 0x38316
- RIPPER.LE: DispatchSceneEntryAction at 0x36892

Assisted-by: Codex:gpt-5
CLOCK_TMP.MCG is one custom bitmap, not a bitmap-set container.

References:
- RIPPER.LE: RunClockPuzzleScene at 0x374c5
- RIPPER.LE: DecodeCustomBitmapAsset at 0x53fdf

Assisted-by: Codex:gpt-5
References:
- RIPPER.LE: DecodeCustomBitmapAsset at 0x53f60

Assisted-by: Codex:gpt-5
The first registered overlapping control wins the hit test.

References:
- RIPPER.LE: FindUiControlStateAtPoint at 0x4aae8

Assisted-by: Codex:gpt-5
Save data retains the existing selected and first-visible fields. The
model validates them before rebuilding layout.

Assisted-by: Codex:gpt-5
The index accelerates exact lookup without changing directory order used
by member sizing and prefix enumeration.

References:
- RIPPER.LE: OpenIndexedContainerEntryReadOnly at 0x55a4c
- RIPPER.LE: LoadAssetLibraryDirectoryFromOpenHandle at 0x6ad80

Assisted-by: Codex:gpt-5
References:
- RIPPER.LE: CreateSceneRuntime at 0x12be7

Assisted-by: Codex:gpt-5
References:
- RIPPER.LE: RunSceneCallbackCommandStream at 0x14080
- RIPPER.LE: DispatchSceneEntryAction at 0x36892

Assisted-by: Codex:gpt-5
Assisted-by: Codex:gpt-5.6-sol
The template byte previously used as a color is a row metric. The
foreground is the active movie palette entry nearest pure white.

References:
- RIPPER.LE: ServiceMediaPresentationTextControl at 0x17014
- RIPPER.LE: FindNearestPaletteColorIndex at 0x56af1
- RIPPER.LE: normal glyph-template color byte at 0x94338

Assisted-by: Codex:gpt-5.6-sol
Only controlled AVI entries replace the replay buffer. WAV and direct
type-1 presentations replay the preceding interaction scene.

References:
- RIPPER.LE: PollInteractionAndResolveSelection at 0x13fc7
- RIPPER.LE: ExecutePresentationEntry at 0x1754b
- RIPPER.LE: process-wide replay buffer at 0x9b0e0

Assisted-by: Codex:gpt-5.6-sol
The menu teardown leaves a blank logical return page. Otherwise
controlled intro media can restore the final RIP_OPEN.SMK frame.

References:
- RIPPER.LE: RunStartupFrontEndLoop at 0x10778

Assisted-by: Codex:gpt-5.6-sol
Assisted-by: Codex:gpt-5.6-sol
Assisted-by: Codex:gpt-5.6-sol
References:
- RIPPER.LE: InitializeDefaultSettingsBlob at 0x1eea2
- RIPPER.LE: ResolveFrontEndActionIdFromInput at 0x1442d
- RIPPER.LE: RunOptionsKeyCaptureLoop at 0x1eae7
- RIPPER.LE: PollKeyboardCommand at 0x4d364

Assisted-by: Codex:gpt-5.6-sol
MemoryReadStream releases owned buffers with free(), so reconstructed
Smacker buffers must use a compatible allocator.

References:
- RIPPER.LE: RunPacketizedMediaPlaybackCore at 0x5b592

Assisted-by: Codex:gpt-5.6-sol
References:
- RIPPER.LE: RunPacketizedMediaPlaybackCore at 0x5b592

Assisted-by: Codex:gpt-5.6-sol
Parsing is transactional: caller-owned outputs remain unchanged until
every field and signed stream bound has been validated.

References:
- RIPPER.LE: DecodeCustomBitmapAsset at 0x53f60

Assisted-by: Codex:gpt-5.6-sol
Assisted-by: Codex:gpt-5.6-sol
Assisted-by: Codex:gpt-5.6-sol
Assisted-by: Codex:gpt-5.6-sol
Assisted-by: Codex:gpt-5.6-sol
Assisted-by: Codex:gpt-5.6-sol
The Ka book-code and KI skull-maze results include failure states
outside the standard puzzle contract, so those classes remain
independent.

Assisted-by: Codex:gpt-5.6-sol
Assisted-by: Codex:gpt-5.6-sol
Assisted-by: Codex:gpt-5.6-sol
Intermediate date and navigation flags and KI skull-maze completion
remain outside the standard puzzle completion contract.

Assisted-by: Codex:gpt-5.6-sol
Assisted-by: Codex:gpt-5.6-sol
Assisted-by: Codex:gpt-5.6-sol
The prebuffer command requires every Smacker branch to be reconstructed
before the external PCM timeline starts.

References:
- RIPPER.LE: RunPacketizedMediaPlaybackCore at 0x5b592

Assisted-by: Codex:gpt-5.6-sol
Remove RIPPER-ARCHITECTURE.md from the branch while retaining it as a checkout-local analysis document. The existing .git/info/exclude pattern keeps the local copy out of future status and add operations.

Assisted-by: Codex:gpt-5.6-sol
Remove AGENTS.md from the branch while retaining it as checkout-local guidance. The existing .git/info/exclude entry keeps the local copy out of future status and add operations.

Assisted-by: Codex:gpt-5.6-sol
RIPER.BAT invokes PLAYER.EXE with SCENES.AVI. It is not an alternate
RIPPER.EXE mode, and the demo did not ship this route.

Assisted-by: Codex:gpt-5.6-sol
Assisted-by: Codex:gpt-5.6-sol
Assisted-by: Codex:gpt-5.6-sol
Assisted-by: Codex:gpt-5.6-sol
@alexbevi
alexbevi marked this pull request as draft August 25, 2026 22:00
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.

1 participant