WebFun

images/ingame-1-png

In 1996/97 LucasArts released two games forming the Desktop Adventures series – Indiana Jones and His Desktop Adventures and Yoda Stories. Both games are 2D puzzle games, featuring a top-down view, a variety of quests and randomly generated worlds.

WebFun aims to be a faithful re-implementation of the game engine driving the Desktop Adventures series and document to its inner workings.

This documentation describes various observations made while reverse-engineering the games. Some of it applies to Indiana Jones and His Desktop Adventures as well, but due to personal preference most of the reverse-engineering efforts have been focused on Yoda Stories.

Releated Projects

The Desktop Adventures games have infected quite a few people over the years. Here are some projects with similar goals as WebFun.

ProjectLanguageDescription
LeonisX/yoda-stories-translation-toolJava Yoda Stories Translation Tool
LeonisX/YExplorerDelphiYoda Stories DAT file explorer, superseded by yoda-stories-translation-tool
shinyquagsire23/DesktopAdventuresCReimplementation
IceReaper/DesktopAdventuresToolkitJavapacker / unpacker for game files
digitall/scummvm-deskadvC++reimplementation for use in ScummVM

Project Architecture

This section describes some of the fundamental structures underlying WebFun.

Getting Started

To set up a local development environment you'll need:

  • Git to download the source code
  • Node.js, version 14 or newer
  • Yarn to install dependencies and run dev tasks

Get the Source Code

Open your terminal and execute the following script to download the repository:

git clone https://github.com/cyco/webfun

Install dependencies

To download and install all dependencies change into the new directory created by git clone and run yarn:

cd webfun
yarn install

Start the server

Using yarn again from the project directory you can now start a local web server. Run the following command in your terminal and the local page will be opened in your default browser.

yarn start

Now you're all set up. The development page will reload whenever a code change is detected. See Build System for a list of tasks yarn can execute for you.

When you're done hit CTRL and c on your keyboard to stop the server.

Data Sources

By default WebFun is set up to load game assets from archive.org. For local development it's often helpful to load the files from disk and speed things up a bit. WebFun uses an environment variable called WEBFUN_GAMES to determine where to find games.

To override the default configuration (stored in the file .env.defaults), you can either set it in your shell before starting the web server or create a new file .env and put the value there. The value of the variable should be a JSON encoded array of GameSource objects.

Note: Environment variables are only read when a program is started. Make sure to restart your development server after making changes to .env

The following configuration adds a local Yoda Stories installation to the default archive.org game source.

WEBFUN_GAMES=[{"title":"Yoda Stories (local)","variant":"yoda","data":"game-data/YODESK.DTA","exe":"game-data/YODESK.EXE","help":"game-data/YODESK.HLP","sfx":"game-data/SFX/"},{"title":"Yoda Stories from archive.org","variant":"yoda","exe":"https://cors.archive.org/download/Star_Wars_-_Yoda_Stories_1997_LucasArts/Star%20Wars%20-%20Yoda%20Stories%20%281997%29%28LucasArts%29.iso/Yoda%2FYodesk.exe","sfx":"https://cors.archive.org/download/Star_Wars_-_Yoda_Stories_1997_LucasArts/Star%20Wars%20-%20Yoda%20Stories%20%281997%29%28LucasArts%29.iso/Yoda%2Fsfx%2F","data":"https://cors.archive.org/download/Star_Wars_-_Yoda_Stories_1997_LucasArts/Star%20Wars%20-%20Yoda%20Stories%20%281997%29%28LucasArts%29.iso/Yoda%2Fyodesk.dta","help":"https://cors.archive.org/download/Star_Wars_-_Yoda_Stories_1997_LucasArts/Star%20Wars%20-%20Yoda%20Stories%20%281997%29%28LucasArts%29.iso/Yoda%2FYodesk.hlp"}]

Build System

WebFun uses the package manager yarn to manage dependencies and run build scripts.

The following tasks are defined in package.json and can be started using yarn <task-name> on the command line.

start -- Start a local web server for development.

By default this starts the server on localhost port 8080. You can change the hostname to listen on with the environment variable host. Some features like the service worker might require an HTTPS connection to work properly. If you place a self-signed certificate under config/ssl.key and config/ssl.pem the server will accept HTTPS connections.

Assuming you have mapped webfun.local to 127.0.0.1 in your hosts file (like echo "127.0.0.1 webfun.local" >> /etc/hosts), you can use mkcert to generate certificates that will be valid on your system like this:

# change to the project directory
$ cd webfun
# create a certificate
$ mkcert -cert-file config/ssl.pem -key-file config/ssl.key webfun.local localhost 127.0.0.1 ::1

# start the development server with the proper hostname
$ host=webfun.local yarn start

format -- Format source code files using eslint & prettier. You should always run this task before committing a code change to make sure everything is properly formatted and adheres es to the coding standards.

test -- Run basic test suite once without collecting code coverage.

test:cont -- Run unit test whenever a change is detected. This task does not collect collect code coverage for performance reasons.

test:full -- Run all tests and collect code coverage.

test:unit -- Run unit tests with code coverage enabled.

test:unit:cont -- Run unit tests with code coverage whenever a change is detected

build -- Clear build directory and run a fresh build suitable for production.

build:docs -- Build this documentation. This script requires mdbook to be installed, see documentation for details.

Webpack

Webpack is used to bundle scripts and prepare other assets like css stylesheets, fonts and copy static files in production builds. The following configuration files are used for various tasks, all of them are located in the config directory:

webpack.commmon.js -- A shared configuration that is used as a base for the dev and test configurations.

webpack.dev.js -- Configuration for the development task, the development web server with hot module reloading is defined here.

webpack.test.js -- Used for running tests

webpack.prod.js -- Defines the production build

webpack.service-worker.js -- Defines the production build for the service worker script. This is in a separate file because splitting the service worker code via chunks does not produce a file that runs in the service worker context.

Custom Plugins

WebFun uses a few custom webpack plugins. These are all single-file scripts found in the config directory.

file-list-webpack-plugin -- Creates a list of files produce during a production build and stores it in a json file. This file is used to pre-load assets when the service worker is installed.

spy-on-imports-webpack-plugin -- This plugin rewrites imports created by Webpack so jasmine.spy can be used on wildcard module imports to mock dependencies during test execution:

import * as UtilModule from "src/util";

describe("some test", () => {
	beforeEach(() => spyOn(UtilModule, "download"));

	it("starts a download", () => {
		subject.doYourThing();

		expect(UtilModule.download).toHaveBeenCalled();
	});
});

Documentation

This source for documentation is at docs and built using mdbook. You can trigger a manual build by executing the following command from the project root:

$ yarn build:docs

The resulting files are placed in build/docs/.

Mdbook also supports starting a local web server to render a live preview of changes made to the markdown files:

$ cd docs
$ mdbook serve

Plugins

Currently there are two plugins used during compilation of the documentation. You'll probably have to install them manually.

mdbook-linkcheck is included to check that all links the documentation points to are valid.

mdbook-open-on-gh adds a link to the current page on GitHub at the bottom of each page.

You can install the plugins with cargo from you command line:

$ cargo install mdbook-linkcheck mdbook-open-on-gh

Deployment

WebFun is hosted on GitHub pages using the branch release with a custom domain. The deployment process takes several steps, automated by this simple shell script:

#!/usr/bin/env bash

SOURCE="$HOME/Source/webfun"
TARGET="$HOME/Source/webfun-release"

# Stop on errors
set -e

# Clone release branch to $TARGET if it does not exist
if [ ! -d "$TARGET" ]; then
	git clone -b release git@github.com:cyco/WebFun.git "$TARGET"
fi

# Change to project root
cd "$SOURCE"

# Execute full test suite
yarn test:full

# Set environment variable to load game files from archive.org
export WEBFUN_GAMES="[{\"title\":\"Yoda Stories from archive.org\",\"variant\":\"yoda\",\"sfx-format\":\"wav\",\"exe\":\"https://cors.archive.org/download/Star_Wars_-_Yoda_Stories_1997_LucasArts/Star%20Wars%20-%20Yoda%20Stories%20%281997%29%28LucasArts%29.iso/Yoda%2FYodesk.exe\",\"sfx\":\"https://cors.archive.org/download/Star_Wars_-_Yoda_Stories_1997_LucasArts/Star%20Wars%20-%20Yoda%20Stories%20%281997%29%28LucasArts%29.iso/Yoda%2Fsfx%2F\",\"data\":\"https://cors.archive.org/download/Star_Wars_-_Yoda_Stories_1997_LucasArts/Star%20Wars%20-%20Yoda%20Stories%20%281997%29%28LucasArts%29.iso/Yoda%2Fyodesk.dta\",\"help\":\"https://cors.archive.org/download/Star_Wars_-_Yoda_Stories_1997_LucasArts/Star%20Wars%20-%20Yoda%20Stories%20%281997%29%28LucasArts%29.iso/Yoda%2FYodesk.hlp\"},{\"title\":\"Yoda Stories Demo (archive.org)\",\"variant\":\"yoda-demo\",\"sfx-format\":\"wav\",\"exe\":\"https://cors.archive.org/download/StarWarsYodaStories_1020/YodaDemo.zip/YodaDemo%2FYodaDemo.exe\",\"sfx\":\"https://archive.org/download/StarWarsYodaStories_1020/YodaDemo.zip/YodaDemo%2Fsfx%2F\",\"data\":\"https://cors.archive.org/download/StarWarsYodaStories_1020/YodaDemo.zip/YodaDemo%2FYodaDemo.dta\",\"help\":\"https://cors.archive.org/download/StarWarsYodaStories_1020/YodaDemo.zip/YodaDemo%2FYodaDemo.hlp\"}]"

# Run production build
yarn build

# Run production build of documentation
yarn build:docs

# Build docker container for local deployment
# docker build -t webfun:latest .

# Clear out target directory
rm -r "$TARGET"/*

# Copy release files
cp -r build/* "$TARGET"

# Copy screenshots for landing page
cp -r docs/screenshots "$TARGET"/docs/screenshots
cp -r assets/preview.png "$TARGET"/preview.png

# Re-create CNAME to setup custom URL for GitHub pages
echo -n www.webfun.io > "$TARGET"/CNAME

# Change directory for manual inspection
cd "$TARGET"

# Stage new version
git add .

# Give further instructions
echo ""
echo "Your relase is ready. Please inspect changes manually and the run"
echo ""
echo "   cd \"$TARGET\""
echo "   git commit -m \"chore: Update release\""
echo "   git push"
echo ""

Gameplay

The following section describes various aspects of the gameplay.

Window

Desktop Adventures are played in a window. There's no fullscreen mode and no resolution choices. The virtual world is rendered in a 288x288 pixel view. The player's item are dispalyed in an inventory table to the right. Each item can be clicked to pick up and then dropped on the scene. Below the inventory you'll find a compass that shows where the player can go, and indcator for the currently equipped weapon plus remaining ammo and a circular health indicator. As the hero takes damage this changes from green, over yellow and red to black. Once the black circle is completed the player dies and the game is over.

Color Palette

Color cycling effects in Yoda Stories

Like many older games, the Desktop Adventures engine uses a color palette for rendering. That means the colors you see on the screen are not specified directly by supplying values for the three components red, green and blue, but rather by choosing one of several pre-defined colors from a color palette.

This has two major advantages. First, the amount of data needed to store an image is drastically reduced. Instead of giving three values for each pixel of an image, only one value is enough.

Second, and more interesting nowadays, changing a color in the palette affects all pixels on screen. This allows for very efficient animations and the results for cleverly composed images can be pretty impressive. Check out this site as well as Mark Ferrari's excellent talk 8 Bit & '8 Bitish' Graphics-Outside the Box to see what can be done with palette animations.

Layout of the color palette
Layout of the color palette in a hex editor

The Desktop Adventures engine uses a palette composed of 256 24-bit BGR colors, each represented as a 32-bit integer, totaling 1024 bytes. By convention, the color at index 0 is interpreted as transparent.

Here are the color palettes used by Yoda Stories and Indiana Jones and His Desktop Adventures:

 Indy's Desktop Adventures Yoda Stories
Color palette used by Indiana Jones and His Desktop AdventuresColor palette used by Yoda Stories

As described in the links above, the Desktop Adventures engine allows for color cycling animations to make the static environments more lively. Each game has a hard-coded set of regions in the palette that shifted to make animations. Each run is either advanced every frame (fast animations) or every other frame (slower animations).

Consult the following tables for a description of the animated regions in each game:

Color Cycles in Yoda Stories

StartEndLengthSpeed
0A0F6fast
C6C62slow
C8C92slow
CACB2fast
CCCD2fast
CECF2fast
D7DF9slow
E0E45fast
E5ED9slow
EEF36fast
F4F52slow

Color Cycles in Indy's Desktop Adventures

StartEndLengthSpeed
A0A78fast
E0E45fast
E5ED9fast
EEF36slow
F4F52slow

Here are some renderings of the animations in the color palette:

 Indy's Desktop Adventures Yoda Stories
Color palette with animations used by Indiana Jones and His Desktop AdventuresColor palette with animations used by Yoda Stories

Extracting the color palette

The color palette is contained in the .data section of the game's executable Yodesk.exe as well as the demo YodaDemo.exe. Since the exact offset might differ depending on the specific version or language of the game, the easiest way to find it is to locate the string CDeskcppDoc\0 and extract the next 1024 bytes from there. In most versions the palette seems to start at offset 0x550F0.

For Indy's Desktop Adventures a reliable way to extract the color palette is to search for the ASCII string sss, move back 103 bytes and read 0x400 bytes from there.

"Free" colors

The following colors are not referenced by any tile in Yoda Stories and could be used for UI elements in restricted environments (e.g. ScummVM or gaming console).

3, 4, 5, 6, 7, 8, 196, 197, 198, 199, 206, 246, 247, 248, 249, 250, 251, 252, 253, 254

Tiles

The Desktop Adventures engine is almost entirely tile based. That means small re-usable images are used as the fundamental elements for all graphics. Maps, enemies and bullets are all made up of tiles.

Note: Only the startup image and the speech bubbles are not rendered using tiles.

Each tile is made up of 32x32 pixels image data – an array of 8-bit color palette indexes – and some flags that specify the properties of the tile.

General Attributes

bitNameDescription
0Transparentif set color 0 in pixel data is treated as transparent
1FloorTile is usually placed on the lowest layer of a zone
2ObjectTile is normally placed on the middle
3DraggableIf set the tile can be dragged and pushed
4RoofObject is usually placed on the top layer
Type
5LocatorTiles used in map screen
6WeaponWeapon tiles
7ItemItems
8CharacterIf the flag is not set, an enemy can

Floor

bitNameDescription
16Doorway  These tile are doorways, monsters can't go

Locator

If the Locator type bit is set, these flags specify the sub-type. Some tiles required to render the map scene are not marked with special bit flags, so a hard coded list of tile ids per game is required.

bitNameDescription
17TownMarks the Spaceport or Lucasino
18PuzzleUnsolvedA visited sector with a puzzle that has not been solved yet
19PuzzleSolvedA solved sector
20TravelUnsolvedSector that brings to player to a section of the world that can not be reached otherwise.
21TravelSolved
22BloackadeNorthUnsolvedA puzzle that has to be solved before the sectors to the north can be reached. A sector that has to be solved before the sector to the north can be reached.
23BloackadeSouthUnsolved
24BloackadeWestUnsolved
25BloackadeEastUnsolved 
26BloackadeNorthSolved
27BloackadeSouthSolved
28BloackadeWestSolved
29BloackadeEastSolved
30GoalUnsolvedThe final puzzle of the world. Solving this wins the game
31YouAreHereOverlay to mark the current position

Item

These item flags are used in hints in the map screen, and in R2D2's help messages.

bitNameDescription
16Keycard
17Tool
18Part
19Valuable
20Map
22EdibleMedikits and other health items have this flag, their health bonus is hard coded in Yoda Stories

Weapon

bitNameDescription
16BlasterLowBlaster Pistol
17BlasterHighBlaster Rifle
18LightsaberLightsaber (Blue or Green)
19TheForceThe Force

Character

bitNameDescription
16Hero
17Enemy
18NPC

Zones

Zones are the game's maps, where the hero can walk around, solve puzzles and fight monsters.

They are made up of 9x9 or 18x18 tiles in three layers. A ground layer, and object layer, used for collision detection, and a roof layer that is rendered above the hero.

Each zone has a type that is used in the world generation process. See the following table for a list of zone types:

TypeNameDescription
0None
1EmptyAn empty zone
2Blockade NorthBlocks access to zones north of this one until solved
3Blockade SouthBlocks access to zones south of this one until solved
4Blockade EastBlocks access to zones east of this one until solved
5Blockade WestBlocks access to zones west of this one until solved
6Travel DepartureWhen solved this allows the player to reach a zone on an island that can otherwise not be reached by moving between zone on the main world
7Travel DestinationCounterpart to Travel Departure. This is where a here will land after traveling from the departure zone. The zones are connected via hotspots of type TravelStart and TravelEnd
8RoomRooms can n not be placed directly on the main world. They can be only be reached through DoorIn/DoorOut hotspots or via ChaneZone instructions.
9LoadThis zone is displayed while a new story is generated.
10GoalGoal zones are used for the final puzzles in each story.
11Town / SpaceportThe starting zone on the main world. Every generated world has exactly one town.
12Unknown
13WinShown when the game is won. This zone also displays the score.
14LoseThis zone is shown after the hero has died.
15TradeA zone where the player has to trade items with an NPC to solve the puzzle.
16UseIn order to solve this zone a tool must be used somewhere on the zone.
17FindFind zones provide an item without requiring anything else to solve them.
18Find Unique WeaponOne of these will be placed close to the town. it provides a unique weapon (The Force in Yoda Stories) to the player.

Additionally each zone provides lists of tiles that can be used to solve the puzzle, NPCs that can be used to trade with and tiles that the zone can drop when solved. In the world generation these items are chosen semi-randomly to create a new story every time.

On the world map neighboring zones can be visited by walking off the current zone. Additionally zones are connected through doors.

In order to make zones a little more interesting to play and replay, the game includes a custom scripting language. These actions are defined per zone.

Special points of interest on a zone are marked by hotspots. These locations mark doors, or places where an item can be used or an NPC be placed by the world generator.

In Yoda Stories, every zone belongs to one of the following planets:

Planet NameDescription
1TatooineDesert planet, also referred to as Nevada
2HothSnowy ice planet, also referred to as Alaska
3EndorA forest planet, also referred to as Oregon
5DagobahSwamp planet, used in the staring world

Actions

Actions can make zones more interactive and dynamic. They are used for example to implement switches and in-game cut-scene animations. Each action is made up of a bunch of conditions and instructions that are executed if all conditions are satisfied.

See Scripting for a more detailed description of the internal scripting language.

Hotspots

Hotspots visualized in debug mode

Each hotspot has a position on the map, a type, and a single argument. Unless disabled, hotspots are triggered by placing an item, walking to the location or removing the tile at the object layer underneath the hotspot.

The following table describes the hotspot types and their purpose:

TypeNameDescription
0Drop Quest Item
1Spawn Location
2Drop Unique Weapon
3Vehicle ToUsed in Travel Start zones, to change the zone to the travel target (identified by arg). On the target zone, the hero will be placed at a corresponsing Vehicle Back hotspot.
4Vehicle Back
5Drop MapDrops the locator
6Drop ItemDrops the item specified in arg when the object layer is free
7NPCA place where the puzzle NPC from arg can be placed.
8Drop WeaponDrops the weapon specified in arg
9Door InConnects the zone to the one in arg.
10Door OutA door leading back to the zone where the player came from
11Unused
12LockThis is a placeholder for a item in a use zone. Placing the correct item here removes the underlying tile from the object layer.
13TeleporterA place where the locator view is triggered for telportation
14Ship From Planet
15Ship To PlanetUsed on Dagobah to mark the place to switch worlds. The target zone on the main world is specified in arg.

Monsters

TODO: Describe monster attributes, waypoints, drops, etc.

Scripting

The Desktop Adventures engine includes a binary scripting language. Each zone can have a number of actions. Each action consists of conditions and instructions.

Conditions and instructions have the following structure:

type Condition = {
	opcode: number,
	arguments: number[5],
	text: string?
}

See Conditions and Instructions for a description of the known opcodes for conditions and instructions respectively.

Execution

Actions are evaluated on the current zone. Once every condition of an action is satisfied all instructions are executed. Consider this mock execution engine:

function execute(actions) {
	for (const action of actions) {
		if (action.conditions.every(condition => condition.isSatisfied()) {
			for (const instruction of action.instructions) {
				instruction.execute();
			}
		}
	}
}

Registers

There are several registers available for use in scripts. All registers are local to the zone.

  • counter – A simple 16-bit register
  • sector-counter16-bit register that is propagated to connected rooms
  • random16-bit register that can be set to random values

Conditions

The following is a list of condition opcodes used by Yoda Stories.

TODO: Check if Indy uses the same opcodes

OpcodeName# of argumentsDescription
0x000ZoneNotInitialized0Evaluates to true exactly once (used for initialization)
0x001ZoneEntered0Evaluates to true if hero just entered the zone
0x002Bump3
0x003PlacedItemIs5
0x004StandingOn3Check if hero is at arg_0xarg_1 and the floor tile is arg_2
0x005CounterIs1Current zone's counter value is equal to arg_0
0x006RandomIs1Current zone's random value is equal to arg_0
0x007RandomIsGreaterThan1Current zone's random value is greater than arg_0
0x008RandomIsLessThan1Current zone's random value is less than arg_0
0x009EnterByPlane0
0x00aTileAtIs4Check if tile at arg_0xarg_1xarg_2 is equal to arg_3
0x00bMonsterIsDead1True if monster arg_0 is dead.
0x00cHasNoActiveMonsters0
0x00dHasItem1True if inventory contains arg_0. If arg_0 is -1 check if inventory contains the item provided by the current zone's puzzle
0x00eRequiredItemIs1
0x00fEndingIs1True if arg_0 is equal to current goal item id
0x010ZoneIsSolved0True if the current zone is solved
0x011NoItemPlaced5Returns true if the user did not place an item
0x012HasGoalItem0Returns true if the hero has the goal item
0x013HealthIsLessThan1Hero's health is less than arg_0.
0x014HealthIsGreaterThan1Hero's health is greater than arg_0.
0x015Unused5
0x016FindItemIs1True the item provided by current zone is arg_0
0x017PlacedItemIsNot5
0x018HeroIsAt2True if hero's x/y position is args_0xargs_1.
0x019SectorCounterIs1Current zone's sector-counter value is equal to arg_0
0x01aSectorCounterIsLessThan1Current zone's sector-counter value is less than arg_0
0x01bSectorCounterIsGreaterThan1Current zone's sector-counter value is greater than arg_0
0x01cGamesWonIs1Total games won is equal to arg_0
0x01dDropsQuestItemAt2
0x01eHasAnyRequiredItem0Determines if inventory contains any of the required items needed for current zone
0x01fCounterIsNot1Current zone's counter value is not equal to arg_0
0x020RandomIsNot1Current zone's random value is not equal to arg_0
0x021SectorCounterIsNot1Current zone's sector-counter value is not equal to arg_0
0x022IsVariable4Check if variable identified by arg_0arg_1arg_2 is set to arg_3. Internally this is implemented as opcode 0x0a, check if tile at arg_0xarg_1xarg_2 is equal to arg_3
0x023GamesWonIsGreaterThan1Total games won is greater than arg_0

Instructions

The following is a list of instructions opcodes used by Yoda Stories.

TODO: Check if Indy uses the same opcodes

OpcodeName# of argumentsDescription
0x000PlaceTile4Place tile arg_3 at arg_0xarg_1xarg_2. To remove a tile the id -1 is used.
0x001RemoveTile3Remove tile at arg_0xarg_1xarg_2
0x002MoveTile5Move Tile at arg_0xarg_0xarg_2 to arg_3xarg_4xarg_2. Note that this can not be used to move tiles between layers!
0x003DrawTile5
0x004SpeakHero0Show speech bubble next to hero. Uses text attribute.
0x005SpeakNpc2Show speech bubble at arg_0xarg_1. Uses text attribute. The characters ¢ and ¥ are used as placeholders for provided and required items of the current zone, respectively.
0x006SetTileNeedsDisplay2Redraw tile at arg_0xarg_1
0x007SetRectNeedsDisplay4Redraw the part of the current scene, specified by a rectangle positioned at arg_0xarg_1 with width arg_2 and height arg_3.
0x008Wait0Pause script execution for one tick.
0x009Redraw0Redraw the whole scene immediately
0x00aPlaySound1Play sound specified by arg_0
0x00bStopSound0Stop playing sounds. _TODO: check if only music need to be stopped`
0x00cRollDice1Set current zone's random to a random value between 1 and arg_0.
0x00dSetCounter1Set current zone's counter value to a arg_0
0x00eAddToCounter1Add arg_0 to current zone's counter value
0x00fSetVariable4Set variable identified by arg_0arg_1arg_2 to arg_3. Internally this is implemented as opcode 0x00, setting tile at arg_0xarg_1xarg_2 to arg_3.
0x010HideHero0Hide hero
0x011ShowHero0Show hero
0x012MoveHeroTo2Set hero's position to arg_0xarg_1 ignoring impassable tiles. Execute hotspot actions, redraw the current scene and move camera if the hero is not hidden.
0x013MoveHeroBy5
0x014DisableAction0Disable current action
0x015EnableHotspot1Enable hotspot arg_0
0x016DisableHotspot1Disable hotspot arg_0
0x017EnableMonster1Enable monster arg_0
0x018DisableMonster1Disable monster arg_0
0x019EnableAllMonsters0Enable all monsters
0x01aDisableAllMonsters0Disable all monsters
0x01bDropItem3Drops item arg_0 for pickup at arg_1xarg_2. If the item is -1, it drops the current sector's find item. instead
0x01cAddItem1Add item with id arg_0 to inventory
0x01dRemoveItem1Remove one instance of item arg_0 from the inventory
0x01eMarkAsSolved0
0x01fWinGame0
0x020LoseGame0
0x021ChangeZone3Change current zone to arg_0. Hero will be placed at arg_1xarg_2 in the new zone.
0x022SetSectorCounter1Set current zone's sector-counter value to a arg_0
0x023AddToSectorCounter1Add arg_0 to current zone's sector-counter value
0x024SetRandom1Set current zone's random value to a arg_0
0x025AddHealth1Increase hero's health by arg_0. New health is capped at hero's max health (0x300).

Score

At the end of each game a score called the Indy Quotient or Force Factor is calculated. The game keeps track of the last and highest scores in the ini file at C:/WINDOWS/Yodesk.ini (see files/yodesk-ini.md) using the keys HScore and LScore, respectively.

you-win

The game combines four components to rate the player's performance:

  • Time
  • Solved puzzle ratio
  • Difficulty
  • Visited zones

Time:

const time = (baseTime in seconds + elapsedTime in seconds) / 60 - 5 * worldSize
if time <= 0 return 200
if 20 * time < 200
	return 200 - 20 * time
return 0

Puzzles:

const solved = sectors.filter(zone => zone && zone.visited).length
return solved / worldSize * 100.0

Difficulty:

const solved = sectors.filter(zone => zone && zone.visited && zone.solved).length
const total = sectors.length
return solved / worldSize * 100.0

Visited Zones:

const solved = sectors.filter(zone => zone && zone.visited).length
return solved / worldSize * 100.0

Final Score:

return Sum(components)

Map Screen

In order for the player to find their way around the world and get some hints on how to solve the puzzles, the game includes a map screen. The screen can be accesses after the player finds the map (aka Locator) item.

TODO: Describe how locator texts are determined

Observations

Here are some observations about the map:

  • The map scene uses 28x28 tiles instead of the usual 32x32
  • Inventory allows opening the map by clicking any item that has Bits 7 and 20 set
  • The map can be activated by hitting L on the keyboard
  • Hints check tile attributes to determine if something is valuable, a tool, a part, etc.

R2D2 Help

When Luke first visits Dagobah his trusty companion R2-D2 is already waiting for him, ready to be picked up and help out.

R2D2 can be placed anywhere on the game screen and will try to give some information on whatever it was dropped on.

The messages given by R2 are stored as string resources in the executable.

The following string ids are used:

StringHintDescription
57383AboutWalkingChosen at random if no other message matches
57384AboutFindingChosen at random if no other message matches
57385About UsingChosen at random if no other message matches
57386About WeaponsChosen at random if no other message matches
57387AboutHealthChosen at random if no other message matches
7382YodaShown when placed on Yoda
7395HeroShown when placed on Luke
7388VaderShown when placed on Vader
7398MedicalDroidShown when placed on medial droid
7396TeleporterActiveShown when placed on an active teleporter
7397TeleporterInactiveShown when placed on an inactive teleporter
7380DraggableShown when placed on a tile with the Draggable flag
7400WeaponShow when placed on a tile with the Weapon flag
7378EnemyShown when placed on a tile with the Character and Enemy flags
7381NPCShown when placed on a tile with the Character and NPC attributes
7391EwokShown when placed on an Ewok
7392JawaShown when placed on a friendly Jawa
7393DroidShown when placed on a droid (tile ids hard coded)
7377X-WingShown when placed on an X-Wing tile (not actually triggered)
7376StorageShown when placed on a container (hard coded tile ids)
7379DoorShow when placed on a door tile (hard coded list of tile ids)
7389WinShown when game is won (can not be triggered)
7390LoseShown when game is lost (can not be triggered)

List of Storage Tile IDs

[0x102, 0x10, 0x48d, 0x279, 0x27b, 0x27c, 0x6e5]

List of Door IDs

[70, 71, 72, 73, 74, 75, 76, 145, 149, 152, 153, 220, 221, 223, 231, 232, 233, 350, 582, 584, 586, 588, 702, 709, 755, 756, 759, 760, 804, 806, 983, 984, 1047, 1048, 1081, 1112, 1120, 1259, 1461, 1462, 1472, 1473, 1539, 1544, 1858]

List of X-Wing Tile IDs

[0x3b4, 0x3b5, 0x3b6]

Cheats

On most websites for cheat codes you can find mentions of cheat codes like BLASTERS and OBJECTS that supposedly fill up the inventory with all items. These codes do not work and the executable does not even contain any reference to those codes.

Yoda Stories does however contain two cheat codes that actually work. To input them you have to activate the Locator and type one of the codes. It is important to click the Locator in the inventory instead of using the keyboard shortcut L (the L-key is interpreted as the first character of the cheat code).

After successfully entering a cheat code a message appears on the current zone.

After successfully entering a cheat code a message appears on the current zone.

Yoda Stories supports the following cheat codes:

CodeMessageEffect
gojediSuper Jedi!Adds 5 Thermal Detonators, a Blaster Rifle, a Blaster and The Force to the inventory.
goyodaInvincible!The player's health is locked at the current value. Neither healing nor taking damage is possible.

Indiana Jones and His Desktop Adventures does not have any cheat codes.

Files

This sections describes several custom or unusual file and binary formats used by the engine.

Yodesk.ini

Data that persists between launches of a Desktop Adventures game, like settings, highscore and information about the last generated worlds is stored in a ini-file.

VariantPath
Yoda StoriesC:\Windows\Yodesk.ini
Yoda Stories DemoC:\Windows\YodaDemo.ini
Indy's AdventuresC:\Windows\Deskadv.ini
Indy's Demo 

The file is a standard INI file with the sections OPTIONS, GameData and Recent File List.

The following is a sample file generated by Yoda Stories:

[OPTIONS]
MIDILoad=1
PlaySound=0
PlayMusic=0
Difficulty=1
GameSpeed=140
WorldSize=1
Count=302
HScore=870
LScore=570
LCount=0
Terrain=2

[GameData]
Alaska0=2126634910,129,3,318,232,264
Nevada0=950869581,163,3,364,348,362

Options section

KeyDescription
Settings
MIDILoad 
PlaySoundSetting
PlayMusicSetting
DifficultySetting, values range from
GameSpeedSetting, values range from
WorldSizeSetting, values range from
Score
CountNumber of worlds generated
HScoreHighest score
LScoreLast score
LCountNumber of lost games  
World Generation
TerrainLast terrain used to generate a world. The next world is determined based on this value. Since Indy's Adventures only have on terrain this value is not used.

GameData section

The GameData section is used to keep track of the end puzzles last used on each planet. When a new world on the planet is generated, these puzzles are excluded. This is supposed to increase variety between consecutive games so the player get different endings every time.

Each entry holds a comma separated list. The first entry is the last used PRNG seed, written out as a 32-bit integer, eventhough only 16-bits are used. The second number is a random value between 1 and 256 that is added to the puzzle ids. The third value is the number of puzzle ids, followed by the actual end puzzle ids that are to be excluded, each offset by the random value generated before.

KeyMappingComment
AlaskaYodaIce planet (Hoth)
NevadaYodaDesert planet (Tatooine)
OregonYodaForest planet (Endor)
HawaiiIndy
WyomingIndy

Releases

It's hard to find a complete list of releases of Yoda Stories. MobyGames lists only two releases of the Windows version of the game - the US and a German version. The Star Wars Collectors Archive (SWCA) has additional information on releases from Japan, Taiwan, Italy and the Netherlands and there is a Spanish release version up on the internet archive.

If you know of any other releases please feel free to edit this page. And if you have access to a version that is not on the internet archive yet, please consider submitting it.

ReleaseYearPublisherSource
US1997LucasArtsMobyGames
Germany  1997FunSoft GmbHMobyGames
Spain  1997Erbe Softwarearchive.org
Japan1997Micro MouseSWCA
Italy1997CTOSWCA
The Netherlands1997SWCA
Taiwan1997UnalisSWCA

The Patch

In February of '97 Lucas Arts released a patch called Yoda Update #6 that fixes problems in some zones. The Patch is a self-extracting archive that replaces the main game data file Yodesk.dta with a new one.

A prompt saying zones #72, #236, #407, #474 and #572 have been fixed appears when the update is run.

Message shown when Yoda Update #6 is run

The original release notes of yopatch6.exe have been preserved by the Wayback Machine here (and with a different design here). They claim that in addition to the zone fixes "minor art modifications" have been made.

It would be interesting to find out what exactly has changed in the newer data file.

$ shasum yopatch6.exe
a8e18864184d49feaf0b74b93f60855f40ffe9bf  yopatch6.exe

$ file yopatch6.exe
yopatch6.exe: PE32 executable (GUI) Intel 80386, for MS Windows

Zone 72

Zone #72 was one of the zones patch by update #6.

072

Zone 236

Zone #236 was one of the zones patch by update #6.

236

Zone 407

Zone #407 was one of the zones patch by update #6.

407

Zone 474

Zone #474 was one of the zones patch by update #6.

474

Zone 572

Zone #572 was one of the zones patch by update #6.

572

Trivia

Promotional Screenshots

Recreation of the promotional screenshots

After getting my hands on an actual retail copy of Yoda Stories I became curious of the screenshots shown on the back of the box. At first glance they seem to be captures of zones #95, #294 and #483. But after further inspection it's clear that none of the screenshots could have come from the final version of the game.

Zone #95 does not have any actions that could place Luke's X-Wing in the zone as shown in the screenshot.

Zone #294 is very close to the one depicted in the second screenshot, the only difference seems to be a missing crater tile next to Luke's head

The last screenshot was taken on a desert world. Zone #483 has very similar building and wall structures, but the dewback in the top left corner is missing.

I've created re-created the situation in each of the screenshots. They are available as downloads here, here and here.

Interestingly enough, the italian version of the box uses entirely different images that actually look like screenshots from the real game.

Left-over Debug Window

Checkout The Cutting Room Floor's entry on Yoda Stories for some more debug windows that are present in the program but can not be triggered.

Apart from the cheat codes there is a debug window that can be accessed using the key combination ctrl-F8. A modal comes up that shows the current zone id, position and the id of this world's goal. The shortcut works in both Yoda Stories and Indiana Jones and his Desktop Adventures.

Debug window showing information about the current zone

Yodesk.ksy

This is a machine-readable definition of the file format used by Yoda Stories. It can be used to generate a parser or inspect files online in the Kaitai Web IDE.

meta:
  id: yodesk
  application: "Star Wars: Yoda Stories"
  file-extension: dta
  license: CC0-1.0
  ks-version: 0.9
  endian: le
  encoding: ASCII
doc: |
  [Star Wars: Yoda Stories](https://en.wikipedia.org/wiki/Star_Wars:_Yoda_Stories) is a unique tile based game with procedurally
  generated worlds.
  This spec describes the file format used for all assets of the Windows
  version of the game.

  The file format follows the TLV (type-length-value) pattern to build a
  central catalog containing the most important (and globally accessible)
  assets of the game (e.g. puzzles, zones, tiles, etc.). The same pattern is
  also found in some catalog entries to encode arrays of variable-length
  structures.

  With every new game, Yoda Stories generates a new world. This is done by
  picking a random sample of puzzles from `PUZ2`. One of the chosen puzzles
  will be the goal, which when solved wins the game.
  Each puzzle provides an item when solved and some require one to be completed.
  During world generation a global world map of 10x10 sectors is filled with
  zones based on the selected puzzles.

  To add variety and interactivity to each zone the game includes a simple
  scripting engine. Zones can declare actions that when executed can for
  example set, move or delete tiles, drop items or activate enemies.

seq:
  - id: catalog
    type: catalog_entry
    repeat: until
    repeat-until: _.type == "ENDF"

types:
  catalog_entry:
    seq:
      - id: type
        type: str
        size: 4
      - id: size
        type: u4
        if: type != "VERS" and type != "ZONE"
      - id: content
        type:
          switch-on: type
          cases:
             '"VERS"': version
             '"STUP"': startup_image
             '"CHAR"': characters
             '"CAUX"': character_auxiliaries
             '"CHWP"': character_weapons
             '"PUZ2"': puzzles
             '"SNDS"': sounds
             '"TILE"': tiles
             '"TNAM"': tile_names
             '"ZONE"': zones
             '"TGEN"': tgen
             '"ENDF"': endf
             _: unknown_catalog_entry
  unknown_catalog_entry:
    seq:
      - id: data
        size: _parent.size
  version:
    seq:
      - id: version
        doc: Version of the file. This value is always set to 512.
        type: u4
  startup_image:
    doc: |
      A 288x288 bitmap to be shown while other assets are loaded and a new
      world is generated.
    seq:
      - id: pixels
        size: _parent.size
  sounds:
    doc: |
      This section declares sounds used in the game. The actual audio data is
      stored in wav files on the disk (in a directory named `sfx`) so this
      section contains paths to each sound file.
      Sounds can be referenced from the scripting language (see `play_sound`
      instruction opcode below) and from weapon (see `character` structure).
      Some sound ids (like the one when the hero is hit, or can't leave a
      zone) are hard coded in the game.
    seq:
      - id: count
        type: s2
      - id: sounds
        type: prefixed_strz
        repeat: expr
        repeat-expr: -count
  tile_names:
    seq:
      - id: names
        doc: |
          List of tile ids and their corresponding names. These are shown in
          the inventory or used in dialogs (see `speak_hero` and `speak_npc`
          opcodes).
        type: tile_name
        repeat: until
        repeat-until:  _.tile_id == 0xFF_FF
  tile_name:
    seq:
      - id: tile_id
        type: u2
      - id: name
        type: strz
        size: 0x18
        if: tile_id != 0xFF_FF
  tiles:
    seq:
      - id: tiles
        type: tiles_entries
        size: _parent.size
  tiles_entries:
    seq:
      - id: tiles
        type: tile
        repeat: eos
  tile:
      seq:
        - id: attributes
          type: tile_attributes
          size: 4
        - id: pixels
          size: 32 * 32
  tile_attributes:
      meta:
        bit-endian: le
      seq:
        - id: has_transparency
          type: b1
          doc: |
            Affects how tile image should be drawn. If set, the
            value 0 in `pixels` is treated as transparent. Otherwise
            it is drawn as black.
        - id: is_floor
          type: b1
          doc: |
            Tile is usually placed on the lowest layer of a zone
        - id: is_object
          type: b1
          doc: |
            Object, tile is usually placed on the middle layer of a zone
        - id: is_draggable
          type: b1
          doc: |
             If set and the tile is placed on the object layer it can be
             dragged and pushed around by the hero.
        - id: is_roof
          type: b1
          doc: |
             Tile is usually placed on the top layer (roof)
        - id: is_locator
          type: b1
          doc: |
             Locator, tile is used in world map view overview
        - id: is_weapon
          type: b1
          doc: |
             Identifies tiles that are mapped to weapons
        - id: is_item
          type: b1
        - id: is_character
          type: b1
          doc: |
            Tile forms part of a character
        - id: unused
          type: b7
        # Floor flags
        - id: is_doorway
          type: b1
          doc: |
            These tiles are doorways, monsters can't go
          if: is_floor
        # Locator flags
        - id: unused1
          type: b1
          if: is_locator
        - id: is_town
          type: b1
          doc: |
            Marks the spaceport on the map
          if: is_locator
        - id: is_unsolved_puzzle
          type: b1
          doc: |
            Marks a discovered, but unsolved puzzle on the map
          if: is_locator
        - id: is_solved_puzzle
          type: b1
          doc: |
            Marks a solved puzzle on the map
          if: is_locator
        - id: is_unsolved_travel
          type: b1
          doc: |
            Marks a place of travel on the map that has not been solved
          if: is_locator
        - id: is_solved_travel
          type: b1
          doc: |
            Marks a solved place of travel on the map
          if: is_locator
        - id: is_unsolved_blockade_north
          type: b1
          doc: |
            Marks a sector on the map that blocks access to northern zones
          if: is_locator
        - id: is_unsolved_blockade_south
          type: b1
          doc: |
            Marks a sector on the map that blocks access to southern zones
          if: is_locator
        - id: is_unsolved_blockade_west
          type: b1
          doc: |
            Marks a sector on the map that blocks access to western zones
          if: is_locator
        - id: is_unsolved_blockade_east
          type: b1
          doc: |
            Marks a sector on the map that blocks access to eastern zones
          if: is_locator
        - id: is_solved_blockade_north
          type: b1
          doc: |
            Marks a solved sector on the map that used to block access to
            northern zones
          if: is_locator
        - id: is_solved_blockade_south
          type: b1
          doc: |
            Marks a solved sector on the map that used to block access to
            southern zones
          if: is_locator
        - id: is_solved_blockade_west
          type: b1
          doc: |
            Marks a solved sector on the map that used to block access to
            western zones
          if: is_locator
        - id: is_solved_blockade_east
          type: b1
          doc: |
            Marks a solved sector on the map that used to block access to
            eastern zones
          if: is_locator
        - id: is_unsolved_goal
          type: b1
          doc: |
            The final puzzle of the world. Solving this wins the game
          if: is_locator
        - id: is_location_indicator
          type: b1
          doc: |
            Overlay to mark the current position on the map
          if: is_locator
        # Item flags
        - id: is_keycard
          type: b1
          if: is_item
        - id: is_tool
          type: b1
          if: is_item
        - id: is_part
          type: b1
          if: is_item
        - id: is_valuable
          type: b1
          if: is_item
        - id: is_map
          type: b1
          if: is_item
        - id: unused2
          type: b1
          if: is_item
        - id: is_edible
          type: b1
          if: is_item
        # Weapon flags
        - id: is_low_blaster
          type: b1
          doc: |
            Item is a low intensity blaster (like the blaster pistol)
          if: is_weapon
        - id: is_high_blaster
          type: b1
          doc: |
            Item is a high intensity blaster (like the blaster rifle)
          if: is_weapon
        - id: is_lightsaber
          type: b1
          if: is_weapon
        - id: is_the_force
          type: b1
          if: is_weapon
        # Character flags
        - id: is_hero
          type: b1
          if: is_character
        - id: is_enemy
          type: b1
          if: is_character
        - id: is_npc
          type: b1
          if: is_character
  action:
    doc: |
      Actions are the game's way to make static tile based maps more engaging
      and interactive. Each action consists of zero or more conditions and
      instructions, once all conditions are satisfied, all instructions are
      executed in order.

      To facilitate state, each zone has three 16-bit registers. These registers
      are named `counter`, `shared-counter` and `random`. In addition to these
      registers hidden tiles are sometimes used to mark state.
      There are conditions and instructions to query and update these registers.
      The `shared-counter` register is special in that it is shared between a
      zone and it's rooms. Instruction `0xc` roll_dice can be used to set the
      `random` register to a random value.

      A naive implementation of the scripting engine could look like this:

      ```
      for action in zone.actions:
        all_conditions_satisfied = False
        for condition in action.conditions:
            all_conditions_satisfied = check(condition)
            if !all_conditions_satisfied:
              break

        if !all_conditions_satisfied:
          continue

        for instruction in action.instructions:
          execute(instruction)
      ```

      See `condition_opcode` and `instruction_opcode` enums for a list of
      available opcodes and their meanings.
    seq:
      - id: marker
        contents: "IACT"
      - id: size
        type: u4
      - id: num_conditions
        type: u2
      - id: conditions
        type: condition
        repeat: expr
        repeat-expr: num_conditions
      - id: num_instructions
        type: u2
      - id: instructions
        type: instruction
        repeat: expr
        repeat-expr: num_instructions
  condition:
    seq:
      - id: opcode
        type: u2
        enum: condition_opcode
      - id: arguments
        type: s2
        repeat: expr
        repeat-expr: 5
      - id: len_text
        type: u2
      - id: text
        type: str
        size: len_text
        doc: |
          The `text_attribute` is never used, but seems to be included to
          shared the type with instructions.
    enums:
      condition_opcode:
        0x0:
          id:  zone_not_initialised
          doc: Evaluates to true exactly once (used for initialisation)
        0x1:
          id:  zone_entered
          doc: Evaluates to true if hero just entered the zone
        0x2:
          id:  bump
        0x3:
          id:  placed_item_is
        0x4:
          id:  standing_on
          doc: |
            Check if hero is at `args[0]`x`args[1]` and the floor tile is
            `args[2]`
        0x5:
          id:  counter_is
          doc: Current zone's `counter` value is equal to `args[0]`
        0x6:
          id:  random_is
          doc: Current zone's `random` value is equal to `args[0]`
        0x7:
          id:  random_is_greater_than
          doc: Current zone's `random` value is greater than `args[0]`
        0x8:
          id:  random_is_less_than
          doc: Current zone's `random` value is less than `args[0]`
        0x9:
          id:  enter_by_plane
        0xa:
          id:  tile_at_is
          doc: |
            Check if tile at `args[0]`x`args[1]`x`args[2]` is equal to
            `args[3]`
        0xb:
          id:  monster_is_dead
          doc: True if monster `args[0]` is dead.
        0xc:
          id:  has_no_active_monsters
          doc: undefined
        0xd:
          id:  has_item
          doc: |
            True if inventory contains `args[0]`.  If `args[0]` is `0xFFFF`
            check if inventory contains the item provided by the current
            zone's puzzle
        0xe:
          id:  required_item_is
        0xf:
          id:  ending_is
          doc: True if `args[0]` is equal to current goal item id
        0x10:
          id:  zone_is_solved
          doc: True if the current zone is solved
        0x11:
          id:  no_item_placed
          doc: Returns true if the user did not place an item
        0x12:
          id:  item_placed
          doc: Returns true if the user placed an item
        0x13:
          id:  health_is_less_than
          doc: Hero's health is less than `args[0]`.
        0x14:
          id:  health_is_greater_than
          doc: Hero's health is greater than `args[0]`.
        0x15: unused
        0x16:
          id:  find_item_is
          doc: True the item provided by current zone is `args[0]`
        0x17:
          id:  placed_item_is_not
        0x18:
          id:  hero_is_at
          doc: True if hero's x/y position is `args_0`x`args_1`.
        0x19:
          id:  shared_counter_is
          doc: Current zone's `shared_counter` value is equal to `args[0]`
        0x1a:
          id:  shared_counter_is_less_than
          doc: Current zone's `shared_counter` value is less than `args[0]`
        0x1b:
          id:  shared_counter_is_greater_than
          doc: Current zone's `shared_counter` value is greater than `args[0]`
        0x1c:
          id:  games_won_is
          doc: Total games won is equal to `args[0]`
        0x1d:
          id:  drops_quest_item_at
        0x1e:
          id:  has_any_required_item
          doc: |
            Determines if inventory contains any of the required items needed
            for current zone
        0x1f:
          id:  counter_is_not
          doc: Current zone's `counter` value is not equal to `args[0]`
        0x20:
          id:  random_is_not
          doc: Current zone's `random` value is not equal to `args[0]`
        0x21:
          id:  shared_counter_is_not
          doc: Current zone's `shared_counter` value is not equal to `args[0]`
        0x22:
          id:  is_variable
          doc: |
            Check if variable identified by `args[0]`⊕`args[1]`⊕`args[2]` is
            set to `args[3]`. Internally this is implemented as opcode 0x0a,
            check if tile at `args[0]`x`args[1]`x`args[2]` is equal to
            `args[3]`
        0x23:
          id:  games_won_is_greater_than
          doc: True, if total games won is greater than `args[0]`

  instruction:
    seq:
      - id: opcode
        type: u2
        enum: instruction_opcode
      - id: arguments
        type: s2
        repeat: expr
        repeat-expr: 5
      - id: len_text
        type: u2
      - id: text
        type: str
        size: len_text
    enums:
      instruction_opcode:
        0x0:
          id:  place_tile
          doc: |
            Place tile `args[3]` at `args[0]`x`args[1]`x`args[2]`. To remove a
            tile `args[3]` can be set to `0xFFFF`.
        0x1:
          id:  remove_tile
          doc: Remove tile at `args[0]`x`args[1]`x`args[2]`
        0x2:
          id:  move_tile
          doc: |
            Move tile at `args[0]`x`args[0]`x`args[2]` to
            `args[3]`x`args[4]`x`args[2]`.  *Note that this can not be used to
            move tiles between layers!*
        0x3:
          id:  draw_tile
        0x4:
          id:  speak_hero
          doc: |
            Show speech bubble next to hero. _Uses `text` attribute_.

            Script execution is paused until the speech bubble is dismissed.
        0x5:
          id:  speak_npc
          doc: |
            Show speech bubble at `args[0]`x`args[1]`. _Uses `text`
            attribute_. The characters `¢` and `¥` are used as placeholders
            for provided and required items of the current zone, respectively.

            Script execution is paused until the speech bubble is dismissed.
        0x6:
          id:  set_tile_needs_display
          doc: Redraw tile at `args[0]`x`args[1]`
        0x7:
          id:  set_rect_needs_display
          doc: |
            Redraw the part of the current scene, specified by a rectangle
            positioned at `args[0]`x`args[1]` with width `args[2]` and height
            `args[3]`.
        0x8:
          id:  wait
          doc: Pause script execution for one tick.
        0x9:
          id:  redraw
          doc: Redraw the whole scene immediately
        0xa:
          id:  play_sound
          doc: Play sound specified by `args[0]`
        0xb:
          id:  stop_sound
          doc: Stop playing sounds
        0xc:
          id:  roll_dice
          doc: |
            Set current zone's `random` to a random value between 1 and
            `args[0]`.
        0xd:
          id:  set_counter
          doc: Set current zone's `counter` value to a `args[0]`
        0xe:
          id:  add_to_counter
          doc: Add `args[0]` to current zone's `counter` value
        0xf:
          id:  set_variable
          doc: |
            Set variable identified by `args[0]`⊕`args[1]`⊕`args[2]` to
            `args[3]`.  Internally this is implemented as opcode 0x00, setting
            tile at `args[0]`x`args[1]`x`args[2]` to `args[3]`.
        0x10:
          id:  hide_hero
          doc: Hide hero
        0x11:
          id:  show_hero
          doc: Show hero
        0x12:
          id: move_hero_to
          doc: |
            Set hero's position to `args[0]`x`args[1]` ignoring impassable
            tiles.  Execute hotspot actions, redraw the current scene and move
            camera if the hero is not hidden.
        0x13:
          id: move_hero_by
          doc: |
            Moves hero relative to the current location by `args[0]` in x and
            `args[1]` in y direction.
        0x14:
          id: disable_action
          doc: |
            Disable current action, note that there's no way to activate the
            action again.
        0x15:
          id: enable_hotspot
          doc: Enable hotspot `args[0]` so it can be triggered.
        0x16:
          id: disable_hotspot
          doc: Disable hotspot `args[0]` so it can't be triggered anymore.
        0x17:
          id:  enable_monster
          doc: Enable monster `args[0]`
        0x18:
          id: disable_monster
          doc: Disable monster `args[0]`
        0x19:
          id: enable_all_monsters
          doc: Enable all monsters
        0x1a:
          id: disable_all_monsters
          doc: Disable all monsters
        0x1b:
          id: drop_item
          doc: |
            Drops item `args[0]` for pickup at `args[1]`x`args[2]`. If the
            item is 0xFFFF, it drops the current sector's find item instead.

            Script execution is paused until the item is picked up.
        0x1c:
          id: add_item
          doc: Add tile with id `args[0]` to inventory
        0x1d:
          id: remove_item
          doc: Remove one instance of item `args[0]` from the inventory
        0x1e:
          id: mark_as_solved
          doc: |
            Marks current sector solved for the overview map.
        0x1f:
          id: win_game
          doc: Ends the current story by winning.
        0x20:
          id: lose_game
          doc: Ends the current story by losing.
        0x21:
          id: change_zone
          doc: |
            Change current zone to `args[0]`. Hero will be placed at
            `args[1]`x`args[2]` in the new zone.
        0x22:
          id:  set_shared_counter
          doc: Set current zone's `shared_counter` value to a `args[0]`
        0x23:
          id:  add_to_shared_counter
          doc: Add `args[0]` to current zone's `shared_counter` value
        0x24:
          id:  set_random
          doc: Set current zone's `random` value to a `args[0]`
        0x25:
          id:  add_health
          doc: |
            Increase hero's health by `args[0]`. New health is capped at
            hero's max health (0x300). Argument 0 can also be negative
            subtract from hero's health.
  monster:
    doc: A monster is a enemy in a zone.
    seq:
      - id: character
        type: u2
      - id: x
        type: u2
      - id: y
        type: u2
      - id: loot
        doc: |
          References the item (loot - 1) that will be dropped if the monster
          is killed. If set to `0xFFFF` the current zone's quest item will be
          dropped.
        type: u2
      - id: drops_loot
        doc: If this field is anything other than 0 the monster may drop an
          item when killed.
        type: u4
      - id: waypoints
        type: waypoint
        repeat: expr
        repeat-expr: 4
  zone:
    seq:
      - id: planet
        doc: |
          Planet this zone can be placed on.

          During world generation the goal puzzle dictates which planet is
          chosen. Apart from `swamp` zones, only the zones with type `empty`
          or the chosen type are loaded when a game is in progress.
        type: u2
        enum: planet
      - id: size
        type: u4
      - id: index
        type: u2
      - id: marker
        contents: "IZON"
      - id: size2
        type: u4
      - id: width
        doc: Width of the zone in tiles. Either 9 or 18.
        type: u2
      - id: height
        doc: Height of the zone in tiles. Either 9 or 18.
        type: u2
      - id: type
        enum: zone_type
        type: u4
      - id: shared_counter
        doc: |
            Scripting register shared between the zone and its rooms.
        type: u2
        valid: 0xFF_FF
      - id: planet_again
        doc: Repetition of the `planet` field
        type: u2
      - id: tile_ids
        type: zone_spot
        repeat: expr
        repeat-expr: width * height
        doc: |
          `tile_ids` is made up of three interleaved tile layers ordered from
          bottom (floor) to top (roof).
          Tiles are often references via 3 coordinates (xyz), which
          corresponds to an index into this array calculated as `n = y * width
          * 3 + x * 3 = z`.
      - id: num_hotspots
        type: u2
      - id: hotspots
        type: hotspot
        repeat: expr
        repeat-expr: num_hotspots
      - id: izax
        type: zone_auxiliary
      - id: izx2
        type: zone_auxiliary_2
      - id: izx3
        type: zone_auxiliary_3
      - id: izx4
        type: zone_auxiliary_4
      - id: num_actions
        type: u2
      - id: actions
        type: action
        repeat: expr
        repeat-expr: num_actions
    enums:
      planet:
        0: none
        1: desert
        2: snow
        3: forest
        5: swamp
      zone_type:
        0:
          id: none
        1:
          id: empty
          doc: |
            Empty zones do not contain a puzzle to be solved and are used to
            fill the space between between zones that are relevant for winning
            the game.
        2:
          id: blockade_north
          doc: |
            This type of zone blocks access to sectors north of it until the
            puzzle is solved.
        3:
          id: blockade_south
          doc: |
            This type of zone blocks access to sectors south of it until the
            puzzle is solved.
        4:
          id: blockade_east
          doc: |
            This type of zone blocks access to sectors east of it until the
            puzzle is solved.
        5:
          id: blockade_west
          doc: |
            This type of zone blocks access to sectors west of it until the
            puzzle is solved.
        6:
          id: travel_start
          doc: |
            Starting point to travel to an island on the edge of the world.
            `travel_start` and `travel_end` zones are connected through
            hotspot of type `vehicle_to` and `vehicle_back`.
        7:
          id: travel_end
          doc: |
            Travel target that is only placed on an island at the edge of the
            world map during world generation.
        8:
          id: room
          doc: |
            A zone that can not be placed on the world map directly. Instead
            rooms are accessed via actions or hotspots of type `door_in`. They
            usually contain at least one `door_out` hotspot to get back to the
            other zone.
        9:
          id: load
          doc: |
            This type of zone is shown after the game has loaded all assets.
            It should resemble the image from the catalog entry of type
            `startup_image` for a smooth transition from loading to game play.
        10:
          id: goal
          doc: |
            Every world contains exactly one goal zone. Solving this zone wins
            the game.
        11:
          id: town
          doc: |
            This is the entry zone where the hero arrives after leaving the
            swamp planet. Each planet can only have one town zone.
        13:
          id: win
          doc: |
            Shown when a game is won. The score is rendered above the tiles at
            coordinates 5x7 and 6x7.
        14:
          id: lose
          doc: |
            Shown when a game is lost.
        15:
          id: trade
          doc: |
            In order to solve this zone and gain a new item the hero has to
            trade something in.
        16:
          id: use
          doc: |
            This type of zone can be solved by making applying a tool or using
            a keycard.
        17:
          id: find
          doc: |
            This type of zone can be solved without using items.
        18:
          id: find_unique_weapon
          doc: |
            This zone provides the hero with a unique weapon and will be
            placed closed to a town zone.
  zone_spot:
    seq:
      - id: column
        doc: from bottom to top, 0xFFFF indicates empty tiles
        type: u2
        repeat: expr
        repeat-expr: 3
  hotspot:
    doc: |
      In addition to actions some puzzles and events are triggered by
      hotspots. These hotspots are triggered when the hero steps on them or
      places an item at the location. Additionally, hotspots are used during
      world generation to mark places where NPCs can spawn.
    seq:
      - id: type
        type: u4
        enum: hotspot_type
      - id: x
        type: u2
      - id: y
        type: u2
      - id: enabled
        doc: |
          If disabled, hotspots can not be triggered. See instruction opcodes
          called `enable_hotspot` and `disable_hotspot`.
        type: u2
      - id: argument
        type: u2
    enums:
      hotspot_type:
        0:
          id: drop_quest_item
          doc: |
            Drops the item provided by the zone when solved. Can be set to a
            specific item, or to `0xFFFF` to use the one from the currently
            assigned puzzle.
        1:
          id: spawn_location
          doc: |
            Possible spawn location for one of the zone's NPCs.
        2:
          id: drop_unique_weapon
          doc: |
            Hotspot that drops the unique weapon found in zones of type
            `find_unique_weapon`.
        3:
          id: vehicle_to
          doc: |
            Used in `travel_start` zones as a trigger to teleport to the
            corresponding `travel_end` zone. The hotspot argument contains the
            id of the zone to teleport to.
        4:
          id: vehicle_back
          doc: |
            Counter part to `vehicle_to` hotspots. This is used to determine
            the hero's position on the zone after the `vehicle_to` hotspot has
            been triggered and to teleport back to the zone on main land.
        5:
          id: drop_map
          doc: |
            Hotspot that drops the map (aka locator) tile. One `find` zone
            with a hotspot of this type will be placed next to a town during
            world generation.
        6:
          id: drop_item
          doc: |
             Hotspot that, when triggered drops the item specified in the
             hotspot's argument. If the item is set to `0xFFFF` the zone's
             quest item will be dropped.
        7:
          id: npc
          doc: |
            This seems to be a placeholder for a pre-assigned NPC.
        8:
          id: drop_weapon
          doc: |
            Drops a weapon (specified by the hotspot argument) when triggered.
        9:
          id: door_in
          doc: |
            When triggered this hotspot type move the hero to the zone
            specified in the hotspot argument. The hero's location on the new
            zone will be determined by a corresponding `door_out` hotspot in
            the target zone.
        10:
          id: door_out
          doc: |
            Determines where the hero will be placed when the zone is entered
            through a door. When triggered, this transports the player back to
            the `door_in` hotspot they game from.
        11: unused
        12: lock
        13:
          id: teleporter
          doc: |
            Teleporter hotspots can be used to instantly teleport to other
            (visited) teleporters on the map
        14:
          id: ship_to_planet
          doc: |
            Behaves similar to the `vehicle_to` hotspot type but travels
            between the town and the swamp planet.
        15:
          id: ship_from_planet
          doc: |
            Behaves similar to the `vehicle_back` hotspot type but travels
            between the town and the swamp planet.

  zone_auxiliary:
    seq:
      - id: marker
        contents: "IZAX"
      - id: size
        type: u4
      - type: u2
      - id: num_monsters
        type: u2
      - id: monsters
        type: monster
        repeat: expr
        repeat-expr: num_monsters
      - id: num_required_items
        type: u2
      - id: required_items
        doc: List of items that can be used to solve the zone.
        type: u2
        repeat: expr
        repeat-expr: num_required_items
      - id: num_goal_items
        type: u2
      - id: goal_items
        doc: |
          Additional items that are needed to solve the zone. Only used if the
          zone type is `goal`.
        type: u2
        repeat: expr
        repeat-expr: num_goal_items
  zone_auxiliary_2:
    seq:
      - id: marker
        contents: "IZX2"
      - id: size
        type: u4
      - id: num_provided_items
        type: u2
      - id: provided_items
        doc: Items that can be gained when the zone is solved.
        type: u2
        repeat: expr
        repeat-expr: num_provided_items
  zone_auxiliary_3:
    seq:
      - id: marker
        contents: "IZX3"
      - id: size
        type: u4
      - id: num_npcs
        type: u2
      - id: npcs
        doc: |
          NPCs that can be placed in the zone to trade items with the hero.
        type: u2
        repeat: expr
        repeat-expr: num_npcs
  zone_auxiliary_4:
    seq:
      - id: marker
        contents: "IZX4"
      - id: size
        type: u4
      - type: u2
  zones:
    seq:
      - id: num_zones
        type: u2
      - id: zones
        type: zone
        repeat: expr
        repeat-expr: num_zones
  puzzles:
    seq:
      - id: puzzles
        type: puzzle
        repeat: until
        repeat-until: _.index == 0xFF_FF
  puzzle:
    seq:
      - id: index
        type: u2
      - id: marker
        if: index != 0xFF_FF
        contents: "IPUZ"
      - id: size
        type: u4
        if: index != 0xFF_FF
      - id: type
        type: u4
        if: index != 0xFF_FF
      - id: item1_class
        type: u4
        enum: puzzle_item_class
        if: index != 0xFF_FF
      - id: item2_class
        type: u4
        enum: puzzle_item_class
        if: index != 0xFF_FF
      - type: u2
        if: index != 0xFF_FF
      - id: strings
        type: prefixed_str
        repeat: expr
        repeat-expr: 5
        if: index != 0xFF_FF
      - id: item_1
        type: u2
        if: index != 0xFF_FF
      - id: item_2
        type: u2
        if: index != 0xFF_FF
    enums:
      puzzle_item_class:
        0: keycard
        1: tool
        2: part
        4: valuable
        0xFFFF_FFFF: none
  endf:
    seq: []
  tgen:
    doc: |
        The TGEN section is only present in non-english versions of the game. It's purpose or
        internal structure is unknown.
    seq:
      - id: data
        size: _parent.size
  characters:
    seq:
      - id: characters
        type: character
        repeat: until
        repeat-until: _.index == 0xFF_FF
  character:
    seq:
      - id: index
        type: u2
      - id: marker
        contents: "ICHA"
        if: index != 0xFF_FF
      - id: size
        type: u4
        if: index != 0xFF_FF
      - id: name
        type: strz
        size: 16
        if: index != 0xFF_FF
      - id: type
        type: u2
        enum: character_type
        if: index != 0xFF_FF
      - id: movement_type
        type: u2
        enum: movement_type
        if: index != 0xFF_FF
      - id: probably_garbage_1
        type: u2
        if: index != 0xFF_FF
      - id: probably_garbage_2
        type: u4
        if: index != 0xFF_FF
      - id: frame_1
        type: char_frame
        if: index != 0xFF_FF
      - id: frame_2
        type: char_frame
        if: index != 0xFF_FF
      - id: frame_3
        type: char_frame
        if: index != 0xFF_FF
    enums:
      character_type:
        1: hero
        2: enemy
        4: weapon
      movement_type:
        0: none
        4: sit
        9: wander
        10: patrol
        12: animation
  char_frame:
    seq:
      - id: tiles
        type: u2
        repeat: expr
        repeat-expr: 0x8
  character_auxiliaries:
    seq:
      - id: auxiliaries
        type: character_auxiliary
        repeat: until
        repeat-until: _.index == 0xFF_FF
  character_auxiliary:
    seq:
      - id: index
        type: u2
      - id: damage
        type: s2
        if: index != 0xFF_FF
  character_weapons:
    seq:
      - id: weapons
        type: character_weapon
        repeat: until
        repeat-until: _.index == 0xFF_FF
  character_weapon:
    seq:
      - id: index
        type: u2
      - id: reference
        doc: |
          If the character referenced by index is a monster, this is a
          reference to their weapon, otherwise this is the index of the
          weapon's sound
        type: u2
        if: index != 0xFF_FF
      - id: health
        type: u2
        if: index != 0xFF_FF
  # Utilities
  prefixed_str:
    seq:
      - id: len_content
        type: u2
      - id: content
        type: str
        size: len_content
  prefixed_strz:
    seq:
      - id: len_content
        type: u2
      - id: content
        type: strz
        size: len_content
  waypoint:
    seq:
    - id: x
      type: u4
    - id: y
      type: u4

Deskadv.ksy

This is a machine-readable definition of the file format used by Indiana Jones and His Desktop Adventures. It can be used to generate a parser or inspect files online in the Kaitai Web IDE.

meta:
  id: deskadv
  application: "Indiana Jones and His Desktop Adventures"
  file-extension: daw
  license: CC0-1.0
  ks-version: 0.9
  endian: le
  encoding: ISO-8859-2

seq:
  - id: catalog
    type: catalog_entry
    repeat: until
    repeat-until: _.type == "ENDF"

types:
  catalog_entry:
    seq:
      - id: type
        type: str
        size: 4
      - id: size
        type: u4
        if: type != "VERS"
      - id: content
        type:
          switch-on: type
          cases:
             '"VERS"': version
             '"STUP"': startup_image
             '"CHAR"': characters
             '"CAUX"': character_auxiliaries
             '"CHWP"': character_weapons
             '"PUZ2"': puzzles
             '"SNDS"': sounds
             '"TILE"': tiles
             '"TNAM"': tile_names
             '"ANAM"': action_names
             '"PNAM"': puzzle_names
             '"ZNAM"': zone_names
             '"HTSP"': hotspots
             '"ACTN"': actions
             '"ZONE"': zones
             '"ZAUX"': zones_aux_1
             '"ZAX2"': zones_aux_2
             '"ZAX3"': zones_aux_3
             '"ZAX4"': zones_aux_4
             '"TGEN"': tgen
             '"ENDF"': endf
             _: unknown_catalog_entry

  unknown_catalog_entry:
    seq:
      - id: data
        size: _parent.size
  version:
    seq:
      - id: version
        doc: Version of the file. This value is always set to 512.
        type: u4
  startup_image:
    doc: |
      A 288x288 bitmap to be shown while other assets are loaded and a new
      world is generated.
    seq:
      - id: pixels
        size: _parent.size
  sounds:
    doc: |
      This section declares sounds used in the game. The actual audio data is
      stored in wav files on the disk (in a directory named `sfx`) so this
      section contains paths to each sound file.
      Sounds can be referenced from the scripting language (see `play_sound`
      instruction opcode below) and from weapon (see `character` structure).
      Some sound ids (like the one when the hero is hit, or can't leave a
      zone) are hard coded in the game.
    seq:
      - id: count
        type: s2
      - id: sounds
        type: prefixed_strz
        repeat: expr
        repeat-expr: -count
  tile_names:
    seq:
      - id: names
        doc: |
          List of tile ids and their corresponding names. These are shown in
          the inventory or used in dialogs (see `speak_hero` and `speak_npc`
          opcodes).
        type: tile_name
        repeat: until
        repeat-until:  _.tile_id == 0xFF_FF
  tile_name:
    seq:
      - id: tile_id
        type: u2
      - id: name
        type: strz
        size: 0x10
        if: tile_id != 0xFF_FF
  tiles:
    seq:
      - id: tiles
        type: tiles_entries
        size: _parent.size
  tiles_entries:
    seq:
      - id: tiles
        type: tile
        repeat: eos
  tile:
      seq:
        - id: attributes
          type: tile_attributes
          size: 4
        - id: pixels
          size: 32 * 32
  tile_attributes:
      meta:
        bit-endian: le
      seq:
        - id: has_transparency
          type: b1
          doc: |
            Affects how tile image should be drawn. If set, the
            value 0 in `pixels` is treated as transparent. Otherwise
            it is drawn as black.
        - id: is_floor
          type: b1
          doc: |
            Tile is usually placed on the lowest layer of a zone
        - id: is_object
          type: b1
          doc: |
            Object, tile is usually placed on the middle layer of a zone
        - id: is_draggable
          type: b1
          doc: |
             If set and the tile is placed on the object layer it can be
             dragged and pushed around by the hero.
        - id: is_roof
          type: b1
          doc: |
             Tile is usually placed on the top layer (roof)
        - id: is_locator
          type: b1
          doc: |
             Locator, tile is used in world map view overview
        - id: is_weapon
          type: b1
          doc: |
             Identifies tiles that are mapped to weapons
        - id: is_item
          type: b1
        - id: is_character
          type: b1
          doc: |
            Tile forms part of a character
        - id: unused
          type: b7
        # Floor flags
        - id: is_doorway
          type: b1
          doc: |
            These tiles are doorways, monsters can't go
          if: is_floor
        # Locator flags
        - id: unused1
          type: b1
          if: is_locator
        - id: is_town
          type: b1
          doc: |
            Marks the spaceport on the map
          if: is_locator
        - id: is_unsolved_puzzle
          type: b1
          doc: |
            Marks a discovered, but unsolved puzzle on the map
          if: is_locator
        - id: is_solved_puzzle
          type: b1
          doc: |
            Marks a solved puzzle on the map
          if: is_locator
        - id: is_unsolved_travel
          type: b1
          doc: |
            Marks a place of travel on the map that has not been solved
          if: is_locator
        - id: is_solved_travel
          type: b1
          doc: |
            Marks a solved place of travel on the map
          if: is_locator
        - id: is_unsolved_blockade_north
          type: b1
          doc: |
            Marks a sector on the map that blocks access to northern zones
          if: is_locator
        - id: is_unsolved_blockade_south
          type: b1
          doc: |
            Marks a sector on the map that blocks access to southern zones
          if: is_locator
        - id: is_unsolved_blockade_west
          type: b1
          doc: |
            Marks a sector on the map that blocks access to western zones
          if: is_locator
        - id: is_unsolved_blockade_east
          type: b1
          doc: |
            Marks a sector on the map that blocks access to eastern zones
          if: is_locator
        - id: is_solved_blockade_north
          type: b1
          doc: |
            Marks a solved sector on the map that used to block access to
            northern zones
          if: is_locator
        - id: is_solved_blockade_south
          type: b1
          doc: |
            Marks a solved sector on the map that used to block access to
            southern zones
          if: is_locator
        - id: is_solved_blockade_west
          type: b1
          doc: |
            Marks a solved sector on the map that used to block access to
            western zones
          if: is_locator
        - id: is_solved_blockade_east
          type: b1
          doc: |
            Marks a solved sector on the map that used to block access to
            eastern zones
          if: is_locator
        - id: is_unsolved_goal
          type: b1
          doc: |
            The final puzzle of the world. Solving this wins the game
          if: is_locator
        - id: is_location_indicator
          type: b1
          doc: |
            Overlay to mark the current position on the map
          if: is_locator
        # Item flags
        - id: is_keycard
          type: b1
          if: is_item
        - id: is_tool
          type: b1
          if: is_item
        - id: is_part
          type: b1
          if: is_item
        - id: is_valuable
          type: b1
          if: is_item
        - id: is_map
          type: b1
          if: is_item
        - id: unused2
          type: b1
          if: is_item
        - id: is_edible
          type: b1
          if: is_item
        # Weapon flags
        - id: is_low_blaster
          type: b1
          doc: |
            Item is a low intensity blaster (like the blaster pistol)
          if: is_weapon
        - id: is_high_blaster
          type: b1
          doc: |
            Item is a high intensity blaster (like the blaster rifle)
          if: is_weapon
        - id: is_lightsaber
          type: b1
          if: is_weapon
        - id: is_the_force
          type: b1
          if: is_weapon
        # Character flags
        - id: is_hero
          type: b1
          if: is_character
        - id: is_enemy
          type: b1
          if: is_character
        - id: is_npc
          type: b1
          if: is_character
  action:
    doc: |
      Actions are the game's way to make static tile based maps more engaging
      and interactive. Each action consists of zero or more conditions and
      instructions, once all conditions are satisfied, all instructions are
      executed in order.

      To facilitate state, each zone has three 16-bit registers. These registers
      are named `counter`, `shared-counter` and `random`. In addition to these
      registers hidden tiles are sometimes used to mark state.
      There are conditions and instructions to query and update these registers.
      The `shared-counter` register is special in that it is shared between a
      zone and it's rooms. Instruction `0xc` roll_dice can be used to set the
      `random` register to a random value.

      A naive implementation of the scripting engine could look like this:

      ```
      for action in zone.actions:
        all_conditions_satisfied = False
        for condition in action.conditions:
            all_conditions_satisfied = check(condition)
            if !all_conditions_satisfied:
              break

        if !all_conditions_satisfied:
          continue

        for instruction in action.instructions:
          execute(instruction)
      ```

      See `condition_opcode` and `instruction_opcode` enums for a list of
      available opcodes and their meanings.
    seq:
      - id: marker
        contents: "IACT"
      - id: size
        type: u4
      - id: num_conditions
        type: u2
      - id: conditions
        type: condition
        repeat: expr
        repeat-expr: num_conditions
      - id: num_instructions
        type: u2
      - id: instructions
        type: instruction
        repeat: expr
        repeat-expr: num_instructions
  condition:
    seq:
      - id: opcode
        type: u2
        enum: condition_opcode
      - id: arguments
        type: s2
        repeat: expr
        repeat-expr: 5
      - id: len_text
        type: u2
      - id: text
        type: str
        size: len_text
        doc: |
          The `text_attribute` is never used, but seems to be included to
          shared the type with instructions.
    enums:
      condition_opcode:
        0x0:
          id:  zone_not_initialised
          doc: Evaluates to true exactly once (used for initialisation)
        0x1:
          id:  zone_entered
          doc: Evaluates to true if hero just entered the zone
        0x2:
          id:  bump
        0x3:
          id:  placed_item_is
        0x4:
          id:  standing_on
          doc: |
            Check if hero is at `args[0]`x`args[1]` and the floor tile is
            `args[2]`
        0x5:
          id:  counter_is
          doc: Current zone's `counter` value is equal to `args[0]`
        0x6:
          id:  random_is
          doc: Current zone's `random` value is equal to `args[0]`
        0x7:
          id:  random_is_greater_than
          doc: Current zone's `random` value is greater than `args[0]`
        0x8:
          id:  random_is_less_than
          doc: Current zone's `random` value is less than `args[0]`
        0x9:
          id:  enter_by_plane
        0xa:
          id:  tile_at_is
          doc: |
            Check if tile at `args[0]`x`args[1]`x`args[2]` is equal to
            `args[3]`
        0xb:
          id:  monster_is_dead
          doc: True if monster `args[0]` is dead.
        0xc:
          id:  has_no_active_monsters
          doc: undefined
        0xd:
          id:  has_item
          doc: |
            True if inventory contains `args[0]`.  If `args[0]` is `0xFFFF`
            check if inventory contains the item provided by the current
            zone's puzzle
        0xe:
          id:  required_item_is
        0xf:
          id:  ending_is
          doc: True if `args[0]` is equal to current goal item id
        0x10:
          id:  zone_is_solved
          doc: True if the current zone is solved
        0x11:
          id:  no_item_placed
          doc: Returns true if the user did not place an item
        0x12:
          id:  item_placed
          doc: Returns true if the user placed an item
        0x13:
          id:  health_is_less_than
          doc: Hero's health is less than `args[0]`.
        0x14:
          id:  health_is_greater_than
          doc: Hero's health is greater than `args[0]`.
        0x15: unused
        0x16:
          id:  find_item_is
          doc: True the item provided by current zone is `args[0]`
        0x17:
          id:  placed_item_is_not
        0x18:
          id:  hero_is_at
          doc: True if hero's x/y position is `args_0`x`args_1`.
        0x19:
          id:  shared_counter_is
          doc: Current zone's `shared_counter` value is equal to `args[0]`
        0x1a:
          id:  shared_counter_is_less_than
          doc: Current zone's `shared_counter` value is less than `args[0]`
        0x1b:
          id:  shared_counter_is_greater_than
          doc: Current zone's `shared_counter` value is greater than `args[0]`
        0x1c:
          id:  games_won_is
          doc: Total games won is equal to `args[0]`
        0x1d:
          id:  drops_quest_item_at
        0x1e:
          id:  has_any_required_item
          doc: |
            Determines if inventory contains any of the required items needed
            for current zone
        0x1f:
          id:  counter_is_not
          doc: Current zone's `counter` value is not equal to `args[0]`
        0x20:
          id:  random_is_not
          doc: Current zone's `random` value is not equal to `args[0]`
        0x21:
          id:  shared_counter_is_not
          doc: Current zone's `shared_counter` value is not equal to `args[0]`
        0x22:
          id:  is_variable
          doc: |
            Check if variable identified by `args[0]`⊕`args[1]`⊕`args[2]` is
            set to `args[3]`. Internally this is implemented as opcode 0x0a,
            check if tile at `args[0]`x`args[1]`x`args[2]` is equal to
            `args[3]`
        0x23:
          id:  games_won_is_greater_than
          doc: True, if total games won is greater than `args[0]`

  instruction:
    seq:
      - id: opcode
        type: u2
        enum: instruction_opcode
      - id: arguments
        type: s2
        repeat: expr
        repeat-expr: 5
      - id: len_text
        type: u2
      - id: text
        type: str
        size: len_text
    enums:
      instruction_opcode:
        0x0:
          id:  place_tile
          doc: |
            Place tile `args[3]` at `args[0]`x`args[1]`x`args[2]`. To remove a
            tile `args[3]` can be set to `0xFFFF`.
        0x1:
          id:  remove_tile
          doc: Remove tile at `args[0]`x`args[1]`x`args[2]`
        0x2:
          id:  move_tile
          doc: |
            Move tile at `args[0]`x`args[0]`x`args[2]` to
            `args[3]`x`args[4]`x`args[2]`.  *Note that this can not be used to
            move tiles between layers!*
        0x3:
          id:  draw_tile
        0x4:
          id:  speak_hero
          doc: |
            Show speech bubble next to hero. _Uses `text` attribute_.

            Script execution is paused until the speech bubble is dismissed.
        0x5:
          id:  speak_npc
          doc: |
            Show speech bubble at `args[0]`x`args[1]`. _Uses `text`
            attribute_. The characters `¢` and `¥` are used as placeholders
            for provided and required items of the current zone, respectively.

            Script execution is paused until the speech bubble is dismissed.
        0x6:
          id:  set_tile_needs_display
          doc: Redraw tile at `args[0]`x`args[1]`
        0x7:
          id:  set_rect_needs_display
          doc: |
            Redraw the part of the current scene, specified by a rectangle
            positioned at `args[0]`x`args[1]` with width `args[2]` and height
            `args[3]`.
        0x8:
          id:  wait
          doc: Pause script execution for one tick.
        0x9:
          id:  redraw
          doc: Redraw the whole scene immediately
        0xa:
          id:  play_sound
          doc: Play sound specified by `args[0]`
        0xb:
          id:  stop_sound
          doc: Stop playing sounds
        0xc:
          id:  roll_dice
          doc: |
            Set current zone's `random` to a random value between 1 and
            `args[0]`.
        0xd:
          id:  set_counter
          doc: Set current zone's `counter` value to a `args[0]`
        0xe:
          id:  add_to_counter
          doc: Add `args[0]` to current zone's `counter` value
        0xf:
          id:  set_variable
          doc: |
            Set variable identified by `args[0]`⊕`args[1]`⊕`args[2]` to
            `args[3]`.  Internally this is implemented as opcode 0x00, setting
            tile at `args[0]`x`args[1]`x`args[2]` to `args[3]`.
        0x10:
          id:  hide_hero
          doc: Hide hero
        0x11:
          id:  show_hero
          doc: Show hero
        0x12:
          id: move_hero_to
          doc: |
            Set hero's position to `args[0]`x`args[1]` ignoring impassable
            tiles.  Execute hotspot actions, redraw the current scene and move
            camera if the hero is not hidden.
        0x13:
          id: move_hero_by
          doc: |
            Moves hero relative to the current location by `args[0]` in x and
            `args[1]` in y direction.
        0x14:
          id: disable_action
          doc: |
            Disable current action, note that there's no way to activate the
            action again.
        0x15:
          id: enable_hotspot
          doc: Enable hotspot `args[0]` so it can be triggered.
        0x16:
          id: disable_hotspot
          doc: Disable hotspot `args[0]` so it can't be triggered anymore.
        0x17:
          id:  enable_monster
          doc: Enable monster `args[0]`
        0x18:
          id: disable_monster
          doc: Disable monster `args[0]`
        0x19:
          id: enable_all_monsters
          doc: Enable all monsters
        0x1a:
          id: disable_all_monsters
          doc: Disable all monsters
        0x1b:
          id: drop_item
          doc: |
            Drops item `args[0]` for pickup at `args[1]`x`args[2]`. If the
            item is 0xFFFF, it drops the current sector's find item instead.

            Script execution is paused until the item is picked up.
        0x1c:
          id: add_item
          doc: Add tile with id `args[0]` to inventory
        0x1d:
          id: remove_item
          doc: Remove one instance of item `args[0]` from the inventory
        0x1e:
          id: mark_as_solved
          doc: |
            Marks current sector solved for the overview map.
        0x1f:
          id: win_game
          doc: Ends the current story by winning.
        0x20:
          id: lose_game
          doc: Ends the current story by losing.
        0x21:
          id: change_zone
          doc: |
            Change current zone to `args[0]`. Hero will be placed at
            `args[1]`x`args[2]` in the new zone.
        0x22:
          id:  set_shared_counter
          doc: Set current zone's `shared_counter` value to a `args[0]`
        0x23:
          id:  add_to_shared_counter
          doc: Add `args[0]` to current zone's `shared_counter` value
        0x24:
          id:  set_random
          doc: Set current zone's `random` value to a `args[0]`
        0x25:
          id:  add_health
          doc: |
            Increase hero's health by `args[0]`. New health is capped at
            hero's max health (0x300). Argument 0 can also be negative
            subtract from hero's health.
  monster:
    doc: A monster is a enemy in a zone.
    seq:
      - id: character
        type: u2
      - id: x
        type: u2
      - id: y
        type: u2
  zone:
    seq:
      - id: marker
        contents: "IZON"
      - id: size2
        type: u4
      - id: width
        doc: Width of the zone in tiles. Either 9 or 18.
        type: u2
      - id: height
        doc: Height of the zone in tiles. Either 9 or 18.
        type: u2
      - id: type
        enum: zone_type
        type: u4
      - id: tile_ids
        type: zone_spot
        repeat: expr
        repeat-expr: width * height
        doc: |
          `tile_ids` is made up of three interleaved tile layers ordered from
          bottom (floor) to top (roof).
          Tiles are often references via 3 coordinates (xyz), which
          corresponds to an index into this array calculated as `n = y * width
          * 3 + x * 3 = z`.
    enums:
      planet:
        0: none
        1: desert
        2: snow
        3: forest
        5: swamp
      zone_type:
        0:
          id: none
        1:
          id: empty
          doc: |
            Empty zones do not contain a puzzle to be solved and are used to
            fill the space between between zones that are relevant for winning
            the game.
        2:
          id: blockade_north
          doc: |
            This type of zone blocks access to sectors north of it until the
            puzzle is solved.
        3:
          id: blockade_south
          doc: |
            This type of zone blocks access to sectors south of it until the
            puzzle is solved.
        4:
          id: blockade_east
          doc: |
            This type of zone blocks access to sectors east of it until the
            puzzle is solved.
        5:
          id: blockade_west
          doc: |
            This type of zone blocks access to sectors west of it until the
            puzzle is solved.
        6:
          id: travel_start
          doc: |
            Starting point to travel to an island on the edge of the world.
            `travel_start` and `travel_end` zones are connected through
            hotspot of type `vehicle_to` and `vehicle_back`.
        7:
          id: travel_end
          doc: |
            Travel target that is only placed on an island at the edge of the
            world map during world generation.
        8:
          id: room
          doc: |
            A zone that can not be placed on the world map directly. Instead
            rooms are accessed via actions or hotspots of type `door_in`. They
            usually contain at least one `door_out` hotspot to get back to the
            other zone.
        9:
          id: load
          doc: |
            This type of zone is shown after the game has loaded all assets.
            It should resemble the image from the catalog entry of type
            `startup_image` for a smooth transition from loading to game play.
        10:
          id: goal
          doc: |
            Every world contains exactly one goal zone. Solving this zone wins
            the game.
        11:
          id: town
          doc: |
            This is the entry zone where the hero arrives after leaving the
            swamp planet. Each planet can only have one town zone.
        13:
          id: win
          doc: |
            Shown when a game is won. The score is rendered above the tiles at
            coordinates 5x7 and 6x7.
        14:
          id: lose
          doc: |
            Shown when a game is lost.
        15:
          id: trade
          doc: |
            In order to solve this zone and gain a new item the hero has to
            trade something in.
        16:
          id: use
          doc: |
            This type of zone can be solved by making applying a tool or using
            a keycard.
        17:
          id: find
          doc: |
            This type of zone can be solved without using items.
        18:
          id: find_unique_weapon
          doc: |
            This zone provides the hero with a unique weapon and will be
            placed closed to a town zone.
  zone_spot:
    seq:
      - id: column
        doc: from bottom to top, 0xFFFF indicates empty tiles
        type: u2
        repeat: expr
        repeat-expr: 3
  hotspot:
    doc: |
      In addition to actions some puzzles and events are triggered by
      hotspots. These hotspots are triggered when the hero steps on them or
      places an item at the location. Additionally, hotspots are used during
      world generation to mark places where NPCs can spawn.
    seq:
      - id: type
        type: u4
        enum: hotspot_type
      - id: x
        type: u2
      - id: y
        type: u2
      - id: enabled
        doc: |
          If disabled, hotspots can not be triggered. See instruction opcodes
          called `enable_hotspot` and `disable_hotspot`.
        type: u2
      - id: argument
        type: u2
    enums:
      hotspot_type:
        0:
          id: drop_quest_item
          doc: |
            Drops the item provided by the zone when solved. Can be set to a
            specific item, or to `0xFFFF` to use the one from the currently
            assigned puzzle.
        1:
          id: spawn_location
          doc: |
            Possible spawn location for one of the zone's NPCs.
        2:
          id: drop_unique_weapon
          doc: |
            Hotspot that drops the unique weapon found in zones of type
            `find_unique_weapon`.
        3:
          id: vehicle_to
          doc: |
            Used in `travel_start` zones as a trigger to teleport to the
            corresponding `travel_end` zone. The hotspot argument contains the
            id of the zone to teleport to.
        4:
          id: vehicle_back
          doc: |
            Counter part to `vehicle_to` hotspots. This is used to determine
            the hero's position on the zone after the `vehicle_to` hotspot has
            been triggered and to teleport back to the zone on main land.
        5:
          id: drop_map
          doc: |
            Hotspot that drops the map (aka locator) tile. One `find` zone
            with a hotspot of this type will be placed next to a town during
            world generation.
        6:
          id: drop_item
          doc: |
             Hotspot that, when triggered drops the item specified in the
             hotspot's argument. If the item is set to `0xFFFF` the zone's
             quest item will be dropped.
        7:
          id: npc
          doc: |
            This seems to be a placeholder for a pre-assigned NPC.
        8:
          id: drop_weapon
          doc: |
            Drops a weapon (specified by the hotspot argument) when triggered.
        9:
          id: door_in
          doc: |
            When triggered this hotspot type move the hero to the zone
            specified in the hotspot argument. The hero's location on the new
            zone will be determined by a corresponding `door_out` hotspot in
            the target zone.
        10:
          id: door_out
          doc: |
            Determines where the hero will be placed when the zone is entered
            through a door. When triggered, this transports the player back to
            the `door_in` hotspot they game from.
        11: unused
        12: lock
        13:
          id: teleporter
          doc: |
            Teleporter hotspots can be used to instantly teleport to other
            (visited) teleporters on the map
        14:
          id: ship_to_planet
          doc: |
            Behaves similar to the `vehicle_to` hotspot type but travels
            between the town and the swamp planet.
        15:
          id: ship_from_planet
          doc: |
            Behaves similar to the `vehicle_back` hotspot type but travels
            between the town and the swamp planet.

  zone_auxiliary:
    seq:
      - id: marker
        contents: "IZAX"
      - id: size
        type: u4
      - type: u2
      - id: num_monsters
        type: u2
      - id: monsters
        type: monster
        repeat: expr
        repeat-expr: num_monsters
      - id: num_required_items
        type: u2
      - id: required_items
        doc: List of items that can be used to solve the zone.
        type: u2
        repeat: expr
        repeat-expr: num_required_items
  zone_auxiliary_2:
    seq:
      - id: marker
        contents: "IZX2"
      - id: size
        type: u4
      - id: num_provided_items
        type: u2
      - id: provided_items
        doc: Items that can be gained when the zone is solved.
        type: u2
        repeat: expr
        repeat-expr: num_provided_items
  zone_auxiliary_3:
    seq:
      - id: marker
        contents: "IZX3"
      - id: size
        type: u4
      - id: num_npcs
        type: u2
      - id: npcs
        doc: |
          NPCs that can be placed in the zone to trade items with the hero.
        type: u2
        repeat: expr
        repeat-expr: num_npcs
  zone_auxiliary_4:
    seq:
      - id: marker
        contents: "IZX4"
      - id: size
        type: u4
      - type: u2
  zones:
    seq:
      - id: num_zones
        type: u2
      - id: zones
        type: zone
        repeat: expr
        repeat-expr: num_zones
  puzzles:
    seq:
      - id: puzzles
        type: puzzle
        repeat: until
        repeat-until: _.index == 0xFF_FF
  puzzle:
    seq:
      - id: index
        type: u2
      - id: marker
        if: index != 0xFF_FF
        contents: "IPUZ"
      - id: size
        type: u4
        if: index != 0xFF_FF
      - id: item1_class
        type: u4
        enum: puzzle_item_class
        if: index != 0xFF_FF
      - id: item2_class
        type: u4
        enum: puzzle_item_class
        if: index != 0xFF_FF
      - type: u2
        if: index != 0xFF_FF
      - id: strings
        type: prefixed_str
        repeat: expr
        repeat-expr: 5
        if: index != 0xFF_FF
      - id: item_1
        type: u2
        if: index != 0xFF_FF
    enums:
      puzzle_item_class:
        0: keycard
        1: tool
        2: part
        4: valuable
        0xFFFF_FFFF: none
  endf:
    seq: []
  tgen:
    doc: |
        The TGEN section is only present in non-english versions of the game. It's purpose or
        internal structure is unknown.
    seq:
      - id: data
        size: _parent.size
  characters:
    seq:
      - id: characters
        type: character
        repeat: until
        repeat-until: _.index == 0xFF_FF
  character:
    seq:
      - id: index
        type: u2
      - id: marker
        contents: "ICHA"
        if: index != 0xFF_FF
      - id: size
        type: u4
        if: index != 0xFF_FF
      - id: name
        type: strz
        size: 16
        if: index != 0xFF_FF
      - id: type
        type: u2
        enum: character_type
        if: index != 0xFF_FF
      - id: movement_type
        type: u2
        enum: movement_type
        if: index != 0xFF_FF
      - id: frame_1
        type: char_frame
        if: index != 0xFF_FF
      - id: frame_2
        type: char_frame
        if: index != 0xFF_FF
      - id: frame_3
        type: char_frame
        if: index != 0xFF_FF
    enums:
      character_type:
        1: hero
        2: enemy
        4: weapon
      movement_type:
        0: none
        4: sit
        9: wander
        10: patrol
        12: animation
  char_frame:
    seq:
      - id: tiles
        type: u2
        repeat: expr
        repeat-expr: 0x8
  character_auxiliaries:
    seq:
      - id: auxiliaries
        type: character_auxiliary
        repeat: until
        repeat-until: _.index == 0xFF_FF
  character_auxiliary:
    seq:
      - id: index
        type: u2
      - id: damage
        type: s2
        if: index != 0xFF_FF
  character_weapons:
    seq:
      - id: weapons
        type: character_weapon
        repeat: until
        repeat-until: _.index == 0xFF_FF
  character_weapon:
    seq:
      - id: index
        type: u2
      - id: reference
        doc: |
          If the character referenced by index is a monster, this is a
          reference to their weapon, otherwise this is the index of the
          weapon's sound
        type: u2
        if: index != 0xFF_FF
      - id: health
        type: u2
        if: index != 0xFF_FF
  action_names:
    seq:
      - id: names
        type: zone_action_name
        repeat: until
        repeat-until:  _.zone_id == 0xFF_FF
  zone_action_name:
    seq:
      - id: zone_id
        type: u2
      - id: action_names
        type: action_name
        repeat: until
        repeat-until: _.action_id == 0xFF_FF
        if: zone_id != 0xFF_FF
  action_name:
    seq:
      - id: action_id
        type: u2
      - id: name
        type: strz
        size: 0x10
        if: action_id != 0xFF_FF
  puzzle_names:
    seq:
      - id: num_puzzle_names
        type: u2
      - id: puzzle_names
        type: strz
        size: 0x10
        repeat: expr
        repeat-expr: num_puzzle_names
  zone_names:
    seq:
      - id: zone_names
        type: zone_name
        repeat: until
        repeat-until: _.zone_id == 0xFF_FF
  zone_name:
    seq:
      - id: zone_id
        type: u2
      - id: name
        type: strz
        size: 0x10
        if: zone_id != 0xFF_FF
  hotspots:
    seq:
      - id: zone_hotspots
        type: zone_hotspot
        repeat: until
        repeat-until: _.zone_id == 0xFF_FF
  zone_hotspot:
    seq:
      - id: zone_id
        type: u2
      - id: num_hotspots
        type: u2
        if: zone_id != 0xFF_FF
      - id: hotspots
        type: hotspot
        repeat: expr
        repeat-expr: num_hotspots
        if: zone_id != 0xFF_FF
  actions:
    seq:
      - id: zone_actions
        type: zone_action
        repeat: until
        repeat-until: _.zone_id == 0xFF_FF
  zone_action:
    seq:
      - id: zone_id
        type: u2
      - id: num_actions
        type: u2
        if: zone_id != 0xFF_FF
      - id: actions
        type: action
        repeat: expr
        repeat-expr: num_actions
        if: zone_id != 0xFF_FF
  zones_aux_1:
    seq:
      - id: zone_auxiliaries
        type: zone_auxiliary
        repeat: expr
        repeat-expr: _root.catalog[4].content.as<zones>.num_zones
  zones_aux_2:
    seq:
      - id: zone_auxiliaries
        type: zone_auxiliary_2
        repeat: expr
        repeat-expr: _root.catalog[4].content.as<zones>.num_zones
  zones_aux_3:
    seq:
      - id: zone_auxiliaries
        type: zone_auxiliary_3
        repeat: expr
        repeat-expr: _root.catalog[4].content.as<zones>.num_zones
  zones_aux_4:
    seq:
      - id: zone_auxiliaries
        type: zone_auxiliary_4
        repeat: expr
        repeat-expr: _root.catalog[4].content.as<zones>.num_zones
  # Utilities
  prefixed_str:
    seq:
      - id: len_content
        type: u2
      - id: content
        type: str
        size: len_content
  prefixed_strz:
    seq:
      - id: len_content
        type: u2
      - id: content
        type: strz
        size: len_content

Yodasav.ksy

This is a machine-readable definition of the save game format used by Yoda Stories. It can be used to generate a parser or inspect files online in the Kaitai Web IDE.

meta:
  id: yodasav
  file-extension: wld
  application: "Yoda Stories"
  endian: le
  encoding: ASCII
seq:
  - id: magic
    contents: "YODASAV44"
  - id: seed
    type: u4
  - id: planet
    type: u4
  - id: on_dagobah
    type: u4
  - id: puzzles1
    type: id_array
  - id: puzzles2
    type: id_array
  - id: dagobah
    type: dagobah
  - id: world
    type: world
  - id: inventory_count
    type: u4
  - id: inventory
    type: u2
    repeat: expr
    repeat-expr: inventory_count
  - id: current_zone
    type: u2
  - id: world_x
    type: u4
  - id: world_y
    type: u4
  - id: current_weapon
    type: s2
  - id: current_ammo
    type: s2
    if: current_weapon != -1
  - id: force_ammo
    type: s2
  - id: blaster_ammo
    type: s2
  - id: blaster_rifle_ammo
    type: s2
  - id: zone_x
    type: u4
  - id: zone_y
    type: u4
  - id: damage_taken
    type: u4
  - id: lives_left
    type: u4
  - id: difficulty
    type: u4
  - id: time_elapsed
    type: u4
  - id: world_size
    type: u2
  - id: unknown_array_count
    type: u2
  - id: unknown_array_sum
    type: u2
  - id: end_puzzle
    type: u4
  - id: end_puzzle_again
    type: u4

types:
  id_array:
    seq:
      - id: count
        type: u2
      - id: content
        type: u2
        repeat: expr
        repeat-expr: count
  world_thing:
    seq:
      - id: unknown1
        type: u4
      - id: unknown2
        type: u4
      - id: unknown3
        type: u4
      - id: unknown4
        type: u4
      - id: unknown5
        type: u4
      - id: zone_id
        type: u2
      - id: unknown6
        type: u2
      - id: required_item
        type: u2
      - id: provided_item
        type: u2
      - id: unknown7
        type: u2
      - id: additionally_required_item
        type: u2
      - id: unknown8
        type: u2
      - id: puzzle_npc
        type: u2
      - id: unknown9
        type: u4
      - id: unknown10
        type: u2
      - id: rooms
        type: room
  dagobah:
    seq:
      - id: world_things
        type: world_thing
        repeat: expr
        repeat-expr: 2 * 2
      - id: zones
        type: zone
        repeat: until
        repeat-until: _.unknown1 == -1 or _.unknown2 == -1
  world:
    seq:
      - id: world_things
        type: world_thing
        repeat: expr
        repeat-expr: 10 * 10
      - id: zones
        type: zone
        repeat: until
        repeat-until: _.unknown1 == -1 or _.unknown2 == -1
  rooms:
    seq:
      - id: rooms
        type: room
        repeat: until
        repeat-until: _.unknown1 == -1
  room:
    seq:
      - id: unknown1
        type: s4
      - id: unknown2
        type: s4
      - id: zone_id
        type: s2
        if: unknown1 == -1
      - id: unknown3
        type: u4
        if: unknown1 == -1
  zone:
    seq:
      - id: unknown1
        type: s4
      - id: unknown2
        type: s4

List of game files

The following is a list of checksum of files related to Yoda Stories or Indiana Jones and His Desktop Adventures. If you have access to other versions of the games feel free to add them below.

sha1Common NameDescription
a8e18864184d49feaf0b74b93f60855f40ffe9bfyopatch6.exeInstaller for Yoda Stories update
437e9ebdfdfe7511c94afb6eebaf2c367cbec33aYodesk.dtaUpdate game file provided by yopatch6
c862c5cc94b5d57b4e296f5f4e2b4508fcb51d6aindy.palColor palette used by Indy
6472ed051d2046e01f4ba44b19a443615c75979eyoda.palYoda Stories color palette used by Yoda Stories (256 colors at 4byte per color)
cf328158f596278b9b30aa0cd9dfe91cac062d92disk1.imgIndy, Spanish, 3.5" floppy, see https://www.goodolddays.net/diskimages/id%2C2819/
0b43a75a09180e68629833d305c50ace6493bf93disk1.imgIndy, French, 3.5" floppy, see https://www.goodolddays.net/diskimages/id%2C2819/
f12f8a2ab25a89e26e25457dd7332cdf4135ba7adisk1.imgIndy, English, 3.5" floppy, see https://www.goodolddays.net/diskimages/id%2C2819/
00d5755679a750f574bd9b0c2f8d970e5333e0d1YODA_GERMAN.isoISO 9660 CD-ROM filesystem data 'YODA_GERMAN'
0d8f15b7920140c82619cf0badc09a9e59924c1eYODA_SPANISH.isoISO 9660 CD-ROM filesystem data 'YODASPANISH
a0efe4819c9c7325437842f2d1b7ec5082cbc0c6Indydesk.exeIndy patch file posted 10/96
b816ee3a9fccd68a70801ec4ff5fe9dbd8c2bf64YodaDemo.exeSelf-extracting archive of Yoda Stories Demo
24931dc214824d525a084ad0073b3211235c2f60Indyprev.zipIndiana Jones and His Desktop Adventures Demo

Yoda Stories

Yoda Stories (English)

sha1Common Name
6256e9cc90f227163ef54f69e57733a45df35982./BITMAPS/PYODAY.BMP
b64a263acbd24612e66e46ce2a2e151c3812cb23./BITMAPS/SCREEN.BMP
5cb7b9ec4fdadf8d88fda6d266de584e083ea80a./BITMAPS/IYODAY.BMP
dc399eee158bb89813d76aa16ca65473f70588fb./BITMAPS/LMMY.BMP
c6b1c2341507160d0bf055bdaa7243a33636390e./BITMAPS/LMMB.BMP
1394fec8991a0bf2ba01c793fd7ff6635f61c217./BITMAPS/IYODAB.BMP
33203c1c173d4da372bf3424b9b2e08e9c0bf6a1./BITMAPS/PYODAB.BMP
b6f27160b8b560891af2f617fc761262077a1e7d./REGISTER ON-LINE.URL
50ef08a3e02edc2954e7494911261d85d52c0506./INSTALL/INST32I.EX
d3a39cc6532dec55b843af8efdfce0c6f7176e22./INSTALL/SETUP.EXE
d894923d28ab6154a2b795fcc0f73c1e42d0262c./INSTALL/SETUP.INS
77f9873ebcd25400d4825101c255b5a1ce7c1106./INSTALL/SETUP.INI
da39a3ee5e6b4b0d3255bfef95601890afd80709./INSTALL/YODESK.INI
e7ec0b86b1efbed2515942dcc3332159eb7d0bf5./INSTALL/WEBSITE.EXE
da39a3ee5e6b4b0d3255bfef95601890afd80709./INSTALL/YODESK.GID
140bee85e817a79d5ccc1d1e8e1d66199e41ffbe./INSTALL/_SETUP.DLL
b19e2e4f93b336042a1eddae12ea2e653834a7a3./INSTALL/_SETUP.LIB
fe96bd82d167f50cb8cd9c9a32d72b77f45f8002./INSTALL/_ISDEL.EXE
cf63696315314488557fbecac937908e5e8c7288./INSTALL/REGISTER.EXE
a955c3839ec956d9eb04d3e78cac4bc1c028ec1f./YODESK.EXE
0372868bf5ac8040a9d3bba7a0bed9241e2308d2./VISIT WWW.LUCASARTS.COM.URL
6a03617143cb01d69fa50589e192f59b787d5ca8./SFX/HURT.WAV
52c2476718520cc52b47e8e1966f803e888bdf0a./SFX/GRENADE.WAV
85314d0afd63b32c5cd9810f7a43a05e293d2247./SFX/CHESTLRG.WAV
d5e3f378b894a8ee179e2a2d7e17eb86d9c267bd./SFX/BUZZMED.WAV
31d60649b6797626e38421b32a27eac6a9c84df6./SFX/MYSTERY.WAV
8a397e81ea87985ebe0244414ab21ad47d7228f9./SFX/WALKER.WAV
d5b4c7eec94b3c88f33f792db74b539829ef21e3./SFX/CANTINA.WAV
200886036f2af0e382e9b3986542e35662f99929./SFX/LOCATOR.WAV
b0ada8bbd7c352a2b489faa9fdb640abc8cd220a./SFX/PISTOL.WAV
4e64a3a79a879c91c35bf614e5b81965ecf434eb./SFX/BUZZLOW.WAV
7c45cbecd946fee69b515bb01c436bd82acc0d14./SFX/EMPTHEME.WAV
e8e40ba4bd82630434b826a3265edc8549ae34b3./SFX/WOOKIE.WAV
7791b4cad2437e10169c1f60d47a0fae953f167a./SFX/ARMFORCE.WAV
40e6664e59ed24e7107d5785e6bf72375546e576./SFX/BLASTER.WAV
1d301d467868f8727e7327a81e6dc0a4ac4d7a9c./SFX/DOORELEC.WAV
72d23409767760589c08e52495ab4136e5af1ee6./SFX/SABEROUT.WAV
ef059f05dcb04d32f0af1fbd7163b962e6e4fd0d./SFX/RIFLE.WAV
46323d03602a3d97a8b46bebf68cd5dcdc6ad0cc./SFX/SWITCH-7.WAV
5c8c54eb51f19cdb0e3745e30cf5d9dccdb25151./SFX/SWITCH-6.WAV
caa7ad2b6e00e02ebb8f136c33d372a0bb7d8b1c./SFX/VADER.WAV
071bd82aff663e50aa78e8d3fe2001f6aab1f7d8./SFX/EEP.WAV
978cebdf7023a24acd2b0e0795c034da1c9e47b3./SFX/BANGHUGE.WAV
717b92788485092181957614fd3b156f4549b51c./SFX/SWITCH-4.WAV
3191a59954f98347c55be705c27064d1c96ce825./SFX/SWITCH-5.WAV
9bd9dbe0bac4fe541c620bac501cfbaf179a29ce./SFX/TRUCKIN.WAV
46323d03602a3d97a8b46bebf68cd5dcdc6ad0cc./SFX/SWITCH-1.WAV
e0ab059defcc74c0f44c246750f442203930471a./SFX/SPEEDER.WAV
8e9962d0069c0b74c07ce61005bf9dc3003b9f9c./SFX/BANGLRG.WAV
55ebccfc7ea4327ffda2607e081c4d99fcccdc5d./SFX/HUGEDOOR.WAV
7af931273d6374af73538b1580ff04f287de6ded./SFX/SCHWING.WAV
1b4293242d61461914623ffad36867537ab199d5./SFX/TRANSPRT.WAV
7995af7e4cd2b97e5419e1ea93dfcb5eb7c356c8./SFX/TRYAGAIN.WAV
5f384cd6565305544f06f650a64b01f4f15c4bbb./SFX/SWITCH-2.WAV
690c39b1ea1cdf86e28cf1335cb620c3ba92727a./SFX/YOUWIN.WAV
97269a0f50b7bf37530d4c0b7ec0ce9089c8718d./SFX/FORCE.WAV
5687e94a1faac7dbe75881e9dd6c1c8fe031984d./SFX/ARMED.WAV
8e6b3a02e9fa3293b87899336a8189568bb8bbf4./SFX/SHPOONK.WAV
dd9ce24759ac8ddf5e09f6b848c5dd71cd9a1b6c./SFX/SWITCH-3.WAV
3d0f7ad1aa43aa7d2e8daf3c509d307185fd252e./SFX/R2D2-2.WAV
d55ff1a47a2a1a08c26990a713e2310546461703./SFX/BONG.WAV
d7e352947ee21ef48b1f020c42c999820caa176d./SFX/R2D2-1.WAV
bdc3addce338d60d2b4e4fe3452508fcfdd0f7a4./SFX/SABER.WAV
3fb938e254ba8b9426b38857d02de48c998db9a2./SFX/XWINGIN.WAV
9e2f78a639a27685f9184eef514af521d3f9223b./SFX/BLEEPSML.WAV
9920d7446a2f259f0a62dffd0e61e30fb7eb4dfd./SFX/BUZZHIGH.WAV
5b5e4581d77527afc79163fdcc31f0b4ded65005./SFX/CHESTSML.WAV
b710cd2f427f742ee1202adf04d9caf242ea6026./SFX/SWITCH-8.WAV
3f2d782c4788ae1bfeed65103863c274d9978649./SFX/TIEBY.WAV
833757378d9e8ad02c0a441439349402c627328d./SFX/IMPBLST.WAV
419cc1734f33953219b94d9316f2780a1655aa7d./SFX/BLIP.WAV
b55808ae16478322f62b3a510b5618addca12cfc./SFX/SABERSWG.WAV
c7bde605481cc7450397fc966d2edcf949eddaf2./SFX/XWINGOUT.WAV
3f8f63d62dababaed9c9fd0da3c5687fc8d6d91c./SFX/TRUCKOUT.WAV
79a9a56abd0525f2e2980009a8ae313cd91f382c./SFX/FLOURISH.WAV
2722f7599fe8ac7e4d3876276aea3c71fdd53327./SFX/CHIRP.WAV
75b1ebef0a2b29f14d787fe6ca8af3d2fc15af5b./SFX/OPENING.WAV
37e222b09f2b1cf86dfbbac5cd06ce1d7bb79c11./SFX/SPLASH.WAV
2d4d0af951307083b7cce9840e7ee4d3013d2590./SFX/RIFLBLST.WAV
ccbe0e1d23f353e2ff5f8a50a41cbf3e1b62285a./SFX/MAPCLS.WAV
65f50467bd6331d13498d547c9d08dc00b2ebcca./SFX/XWINGBY.WAV
7640aefae2ec587bfeeb3456a66fa57e6d3b6d06./SFX/CRASH.WAV
ebddf621f032f619d14b3b4eae2296fe58413efa./SFX/BANGMED.WAV
e8febb3a41d4eef2587dad9cd5e2719d7392635d./SFX/PUSH.WAV
cd5db260b3b0879e48064c65eee061942f955ced./SFX/NOGO.WAV
5e7bc1330365191feecd53d8595ee1da96b74b85./SFX/MOVEROCK.WAV
b1e5be033b13b24806ea961f7998fdffe2f81803./SFX/DOORPNUM.WAV
184b008cd9a7d78b62959ad66f16a03d27688208./YODESK.CNT
13e914ad378bcef9a7b030200e0dc7a66c078473./WAVEMIX.INI
66c502f8373a149bd310f364f4d4d29cf61dad06./YODESK.GID
b6d1ac4d9f165403898531341d13885c770d4177./MSVCRT40.DLL
9fa85e8177d2df436564c97775845b9ab27b9b94./YODESK.HLP
21265b5d3d600ecd9869a88c70096b3e0ea3c6b2./WAVMIX32.DLL
437e9ebdfdfe7511c94afb6eebaf2c367cbec33a./YODESK.DTA

Yoda Stories (Spanish)

sha1Common Name
77f5799c10da7afde51e6166a2c27d66f8b609f9./BITMAPS/PYODAY.BMP
2f8327c3b2000f44cd17ef7fd2ba59815f1d1262./BITMAPS/SCREEN.BMP
ee1c8b63e686a96c22c91913e372ae4df9e6e6b2./BITMAPS/IYODAY.BMP
4e588386f0d41df3fb1bf3a9a0ab49a4cd6b516a./BITMAPS/LMMY.BMP
f4ab98bbbe7254c7bcaff7413c12897a0a1669d6./BITMAPS/LMMB.BMP
fe59382fbc7cb948c23ddfc78ad1ec4985c8e587./BITMAPS/IYODAB.BMP
4201ef8de520493f311d1b7f06a3ac0aaa80a710./BITMAPS/PYODAB.BMP
50ef08a3e02edc2954e7494911261d85d52c0506./INSTALL/INST32I.EX
d3a39cc6532dec55b843af8efdfce0c6f7176e22./INSTALL/SETUP.EXE
a148e180e70939a721449551297ee4cc47b0620b./INSTALL/SETUP.INS
77f9873ebcd25400d4825101c255b5a1ce7c1106./INSTALL/SETUP.INI
da39a3ee5e6b4b0d3255bfef95601890afd80709./INSTALL/YODESK.INI
da39a3ee5e6b4b0d3255bfef95601890afd80709./INSTALL/YODESK.GID
0e45ae0b619b6d2924eed33b4dc5480e12a7c6fe./INSTALL/_SETUP.DLL
03f70a3e1bd3bdd5f908ac3aad464fd87fb33eb2./INSTALL/_SETUP.LIB
fe96bd82d167f50cb8cd9c9a32d72b77f45f8002./INSTALL/_ISDEL.EXE
6c0c0934b026ccd2c40916241fbd991c95d6487f./YODESK.EXE
0372868bf5ac8040a9d3bba7a0bed9241e2308d2./VISITA WWW.LUCASARTS.COM.URL
6a03617143cb01d69fa50589e192f59b787d5ca8./SFX/HURT.WAV
52c2476718520cc52b47e8e1966f803e888bdf0a./SFX/GRENADE.WAV
85314d0afd63b32c5cd9810f7a43a05e293d2247./SFX/CHESTLRG.WAV
d5e3f378b894a8ee179e2a2d7e17eb86d9c267bd./SFX/BUZZMED.WAV
31d60649b6797626e38421b32a27eac6a9c84df6./SFX/MYSTERY.WAV
8a397e81ea87985ebe0244414ab21ad47d7228f9./SFX/WALKER.WAV
d5b4c7eec94b3c88f33f792db74b539829ef21e3./SFX/CANTINA.WAV
200886036f2af0e382e9b3986542e35662f99929./SFX/LOCATOR.WAV
b0ada8bbd7c352a2b489faa9fdb640abc8cd220a./SFX/PISTOL.WAV
4e64a3a79a879c91c35bf614e5b81965ecf434eb./SFX/BUZZLOW.WAV
7c45cbecd946fee69b515bb01c436bd82acc0d14./SFX/EMPTHEME.WAV
e8e40ba4bd82630434b826a3265edc8549ae34b3./SFX/WOOKIE.WAV
7791b4cad2437e10169c1f60d47a0fae953f167a./SFX/ARMFORCE.WAV
40e6664e59ed24e7107d5785e6bf72375546e576./SFX/BLASTER.WAV
1d301d467868f8727e7327a81e6dc0a4ac4d7a9c./SFX/DOORELEC.WAV
72d23409767760589c08e52495ab4136e5af1ee6./SFX/SABEROUT.WAV
ef059f05dcb04d32f0af1fbd7163b962e6e4fd0d./SFX/RIFLE.WAV
46323d03602a3d97a8b46bebf68cd5dcdc6ad0cc./SFX/SWITCH-7.WAV
5c8c54eb51f19cdb0e3745e30cf5d9dccdb25151./SFX/SWITCH-6.WAV
caa7ad2b6e00e02ebb8f136c33d372a0bb7d8b1c./SFX/VADER.WAV
071bd82aff663e50aa78e8d3fe2001f6aab1f7d8./SFX/EEP.WAV
978cebdf7023a24acd2b0e0795c034da1c9e47b3./SFX/BANGHUGE.WAV
717b92788485092181957614fd3b156f4549b51c./SFX/SWITCH-4.WAV
3191a59954f98347c55be705c27064d1c96ce825./SFX/SWITCH-5.WAV
9bd9dbe0bac4fe541c620bac501cfbaf179a29ce./SFX/TRUCKIN.WAV
46323d03602a3d97a8b46bebf68cd5dcdc6ad0cc./SFX/SWITCH-1.WAV
e0ab059defcc74c0f44c246750f442203930471a./SFX/SPEEDER.WAV
8e9962d0069c0b74c07ce61005bf9dc3003b9f9c./SFX/BANGLRG.WAV
55ebccfc7ea4327ffda2607e081c4d99fcccdc5d./SFX/HUGEDOOR.WAV
7af931273d6374af73538b1580ff04f287de6ded./SFX/SCHWING.WAV
1b4293242d61461914623ffad36867537ab199d5./SFX/TRANSPRT.WAV
7995af7e4cd2b97e5419e1ea93dfcb5eb7c356c8./SFX/TRYAGAIN.WAV
5f384cd6565305544f06f650a64b01f4f15c4bbb./SFX/SWITCH-2.WAV
690c39b1ea1cdf86e28cf1335cb620c3ba92727a./SFX/YOUWIN.WAV
97269a0f50b7bf37530d4c0b7ec0ce9089c8718d./SFX/FORCE.WAV
5687e94a1faac7dbe75881e9dd6c1c8fe031984d./SFX/ARMED.WAV
8e6b3a02e9fa3293b87899336a8189568bb8bbf4./SFX/SHPOONK.WAV
dd9ce24759ac8ddf5e09f6b848c5dd71cd9a1b6c./SFX/SWITCH-3.WAV
3d0f7ad1aa43aa7d2e8daf3c509d307185fd252e./SFX/R2D2-2.WAV
d55ff1a47a2a1a08c26990a713e2310546461703./SFX/BONG.WAV
d7e352947ee21ef48b1f020c42c999820caa176d./SFX/R2D2-1.WAV
bdc3addce338d60d2b4e4fe3452508fcfdd0f7a4./SFX/SABER.WAV
3fb938e254ba8b9426b38857d02de48c998db9a2./SFX/XWINGIN.WAV
9e2f78a639a27685f9184eef514af521d3f9223b./SFX/BLEEPSML.WAV
9920d7446a2f259f0a62dffd0e61e30fb7eb4dfd./SFX/BUZZHIGH.WAV
5b5e4581d77527afc79163fdcc31f0b4ded65005./SFX/CHESTSML.WAV
b710cd2f427f742ee1202adf04d9caf242ea6026./SFX/SWITCH-8.WAV
3f2d782c4788ae1bfeed65103863c274d9978649./SFX/TIEBY.WAV
833757378d9e8ad02c0a441439349402c627328d./SFX/IMPBLST.WAV
419cc1734f33953219b94d9316f2780a1655aa7d./SFX/BLIP.WAV
b55808ae16478322f62b3a510b5618addca12cfc./SFX/SABERSWG.WAV
c7bde605481cc7450397fc966d2edcf949eddaf2./SFX/XWINGOUT.WAV
3f8f63d62dababaed9c9fd0da3c5687fc8d6d91c./SFX/TRUCKOUT.WAV
79a9a56abd0525f2e2980009a8ae313cd91f382c./SFX/FLOURISH.WAV
2722f7599fe8ac7e4d3876276aea3c71fdd53327./SFX/CHIRP.WAV
75b1ebef0a2b29f14d787fe6ca8af3d2fc15af5b./SFX/OPENING.WAV
37e222b09f2b1cf86dfbbac5cd06ce1d7bb79c11./SFX/SPLASH.WAV
2d4d0af951307083b7cce9840e7ee4d3013d2590./SFX/RIFLBLST.WAV
ccbe0e1d23f353e2ff5f8a50a41cbf3e1b62285a./SFX/MAPCLS.WAV
65f50467bd6331d13498d547c9d08dc00b2ebcca./SFX/XWINGBY.WAV
7640aefae2ec587bfeeb3456a66fa57e6d3b6d06./SFX/CRASH.WAV
ebddf621f032f619d14b3b4eae2296fe58413efa./SFX/BANGMED.WAV
e8febb3a41d4eef2587dad9cd5e2719d7392635d./SFX/PUSH.WAV
cd5db260b3b0879e48064c65eee061942f955ced./SFX/NOGO.WAV
5e7bc1330365191feecd53d8595ee1da96b74b85./SFX/MOVEROCK.WAV
b1e5be033b13b24806ea961f7998fdffe2f81803./SFX/DOORPNUM.WAV
8bbefddff0da779d9843300544080d02a8364032./YODESK.CNT
13e914ad378bcef9a7b030200e0dc7a66c078473./WAVEMIX.INI
b6d1ac4d9f165403898531341d13885c770d4177./MSVCRT40.DLL
4d4e45a459cfbe560be779ab2195185f8c2165b7./YODESK.HLP
21265b5d3d600ecd9869a88c70096b3e0ea3c6b2./WAVMIX32.DLL
2650cf79560305cee94f677bc0f2acd692a853f6./YODESK.DTA

Yoda Stories (German)

sha1Common Name
37ebcdff84f564ba57c2dbdab24f68e4b4aca162./BITMAPS/PYODAY.BMP
9274750d5c92c3a6e38f594a215305a121130db9./BITMAPS/SCREEN.BMP
59b9f394688eac325ca763102fda4a62abccc4dc./BITMAPS/IYODAY.BMP
139ac3cafdd99f60364fc1305dca90d1b4956f9f./BITMAPS/LMMY.BMP
f39956775f34f5bf0999d84597b57e2858ce1598./BITMAPS/LMMB.BMP
a4decad5295523f21bf8a4e4212e1ef7441bc80b./BITMAPS/IYODAB.BMP
d0960ad5a3822659b11426a806fdcfdc15fd985a./BITMAPS/PYODAB.BMP
50ef08a3e02edc2954e7494911261d85d52c0506./INSTALL/INST32I.EX
d3a39cc6532dec55b843af8efdfce0c6f7176e22./INSTALL/SETUP.EXE
597dd13d90517ca5056c0389072110db6874dd46./INSTALL/SETUP.INS
77f9873ebcd25400d4825101c255b5a1ce7c1106./INSTALL/SETUP.INI
da39a3ee5e6b4b0d3255bfef95601890afd80709./INSTALL/YODESK.INI
da39a3ee5e6b4b0d3255bfef95601890afd80709./INSTALL/YODESK.GID
ed96ae8387da5ffcb3b92ed091ae2ea42eb0d74b./INSTALL/_SETUP.DLL
daf49fac20ee018c1799bdf711ca3a576888ed64./INSTALL/_SETUP.LIB
fe96bd82d167f50cb8cd9c9a32d72b77f45f8002./INSTALL/_ISDEL.EXE
835ebc3c25b963d025658845a72126ae362653bb./YODESK.EXE
0372868bf5ac8040a9d3bba7a0bed9241e2308d2./VISIT WWW.LUCASARTS.COM.URL
6a03617143cb01d69fa50589e192f59b787d5ca8./SFX/HURT.WAV
52c2476718520cc52b47e8e1966f803e888bdf0a./SFX/GRENADE.WAV
85314d0afd63b32c5cd9810f7a43a05e293d2247./SFX/CHESTLRG.WAV
d5e3f378b894a8ee179e2a2d7e17eb86d9c267bd./SFX/BUZZMED.WAV
31d60649b6797626e38421b32a27eac6a9c84df6./SFX/MYSTERY.WAV
8a397e81ea87985ebe0244414ab21ad47d7228f9./SFX/WALKER.WAV
d5b4c7eec94b3c88f33f792db74b539829ef21e3./SFX/CANTINA.WAV
200886036f2af0e382e9b3986542e35662f99929./SFX/LOCATOR.WAV
b0ada8bbd7c352a2b489faa9fdb640abc8cd220a./SFX/PISTOL.WAV
4e64a3a79a879c91c35bf614e5b81965ecf434eb./SFX/BUZZLOW.WAV
7c45cbecd946fee69b515bb01c436bd82acc0d14./SFX/EMPTHEME.WAV
e8e40ba4bd82630434b826a3265edc8549ae34b3./SFX/WOOKIE.WAV
7791b4cad2437e10169c1f60d47a0fae953f167a./SFX/ARMFORCE.WAV
40e6664e59ed24e7107d5785e6bf72375546e576./SFX/BLASTER.WAV
1d301d467868f8727e7327a81e6dc0a4ac4d7a9c./SFX/DOORELEC.WAV
72d23409767760589c08e52495ab4136e5af1ee6./SFX/SABEROUT.WAV
ef059f05dcb04d32f0af1fbd7163b962e6e4fd0d./SFX/RIFLE.WAV
46323d03602a3d97a8b46bebf68cd5dcdc6ad0cc./SFX/SWITCH-7.WAV
5c8c54eb51f19cdb0e3745e30cf5d9dccdb25151./SFX/SWITCH-6.WAV
caa7ad2b6e00e02ebb8f136c33d372a0bb7d8b1c./SFX/VADER.WAV
071bd82aff663e50aa78e8d3fe2001f6aab1f7d8./SFX/EEP.WAV
978cebdf7023a24acd2b0e0795c034da1c9e47b3./SFX/BANGHUGE.WAV
717b92788485092181957614fd3b156f4549b51c./SFX/SWITCH-4.WAV
3191a59954f98347c55be705c27064d1c96ce825./SFX/SWITCH-5.WAV
9bd9dbe0bac4fe541c620bac501cfbaf179a29ce./SFX/TRUCKIN.WAV
46323d03602a3d97a8b46bebf68cd5dcdc6ad0cc./SFX/SWITCH-1.WAV
e0ab059defcc74c0f44c246750f442203930471a./SFX/SPEEDER.WAV
8e9962d0069c0b74c07ce61005bf9dc3003b9f9c./SFX/BANGLRG.WAV
55ebccfc7ea4327ffda2607e081c4d99fcccdc5d./SFX/HUGEDOOR.WAV
7af931273d6374af73538b1580ff04f287de6ded./SFX/SCHWING.WAV
1b4293242d61461914623ffad36867537ab199d5./SFX/TRANSPRT.WAV
7995af7e4cd2b97e5419e1ea93dfcb5eb7c356c8./SFX/TRYAGAIN.WAV
5f384cd6565305544f06f650a64b01f4f15c4bbb./SFX/SWITCH-2.WAV
690c39b1ea1cdf86e28cf1335cb620c3ba92727a./SFX/YOUWIN.WAV
97269a0f50b7bf37530d4c0b7ec0ce9089c8718d./SFX/FORCE.WAV
5687e94a1faac7dbe75881e9dd6c1c8fe031984d./SFX/ARMED.WAV
8e6b3a02e9fa3293b87899336a8189568bb8bbf4./SFX/SHPOONK.WAV
dd9ce24759ac8ddf5e09f6b848c5dd71cd9a1b6c./SFX/SWITCH-3.WAV
3d0f7ad1aa43aa7d2e8daf3c509d307185fd252e./SFX/R2D2-2.WAV
d55ff1a47a2a1a08c26990a713e2310546461703./SFX/BONG.WAV
d7e352947ee21ef48b1f020c42c999820caa176d./SFX/R2D2-1.WAV
bdc3addce338d60d2b4e4fe3452508fcfdd0f7a4./SFX/SABER.WAV
3fb938e254ba8b9426b38857d02de48c998db9a2./SFX/XWINGIN.WAV
9e2f78a639a27685f9184eef514af521d3f9223b./SFX/BLEEPSML.WAV
9920d7446a2f259f0a62dffd0e61e30fb7eb4dfd./SFX/BUZZHIGH.WAV
5b5e4581d77527afc79163fdcc31f0b4ded65005./SFX/CHESTSML.WAV
b710cd2f427f742ee1202adf04d9caf242ea6026./SFX/SWITCH-8.WAV
3f2d782c4788ae1bfeed65103863c274d9978649./SFX/TIEBY.WAV
833757378d9e8ad02c0a441439349402c627328d./SFX/IMPBLST.WAV
419cc1734f33953219b94d9316f2780a1655aa7d./SFX/BLIP.WAV
b55808ae16478322f62b3a510b5618addca12cfc./SFX/SABERSWG.WAV
c7bde605481cc7450397fc966d2edcf949eddaf2./SFX/XWINGOUT.WAV
3f8f63d62dababaed9c9fd0da3c5687fc8d6d91c./SFX/TRUCKOUT.WAV
79a9a56abd0525f2e2980009a8ae313cd91f382c./SFX/FLOURISH.WAV
2722f7599fe8ac7e4d3876276aea3c71fdd53327./SFX/CHIRP.WAV
75b1ebef0a2b29f14d787fe6ca8af3d2fc15af5b./SFX/OPENING.WAV
37e222b09f2b1cf86dfbbac5cd06ce1d7bb79c11./SFX/SPLASH.WAV
2d4d0af951307083b7cce9840e7ee4d3013d2590./SFX/RIFLBLST.WAV
ccbe0e1d23f353e2ff5f8a50a41cbf3e1b62285a./SFX/MAPCLS.WAV
65f50467bd6331d13498d547c9d08dc00b2ebcca./SFX/XWINGBY.WAV
7640aefae2ec587bfeeb3456a66fa57e6d3b6d06./SFX/CRASH.WAV
ebddf621f032f619d14b3b4eae2296fe58413efa./SFX/BANGMED.WAV
e8febb3a41d4eef2587dad9cd5e2719d7392635d./SFX/PUSH.WAV
cd5db260b3b0879e48064c65eee061942f955ced./SFX/NOGO.WAV
5e7bc1330365191feecd53d8595ee1da96b74b85./SFX/MOVEROCK.WAV
b1e5be033b13b24806ea961f7998fdffe2f81803./SFX/DOORPNUM.WAV
6c22a68c21e6f5f1c1575fc070f14767e3759a58./YODESK.CNT
13e914ad378bcef9a7b030200e0dc7a66c078473./WAVEMIX.INI
b6d1ac4d9f165403898531341d13885c770d4177./MSVCRT40.DLL
64cf1eb8c83cc50f9f44ff3c3292a86b36768f9d./YODESK.HLP
21265b5d3d600ecd9869a88c70096b3e0ea3c6b2./WAVMIX32.DLL
967ea881be2c7c5df9e5742722fe8e448592652f./yodesk.dta

Yoda Stories Demo (English)

sha1Common Name
425b04cc5e1fd5d3da477a8314246baf161daffc./YodaDemo.hlp
46734006c8e08faa15d2b2c2e32834ba9e3622ac./YodaDemo.dta
6a03617143cb01d69fa50589e192f59b787d5ca8./sfx/hurt.wav
52c2476718520cc52b47e8e1966f803e888bdf0a./sfx/grenade.wav
85314d0afd63b32c5cd9810f7a43a05e293d2247./sfx/chestlrg.wav
d5e3f378b894a8ee179e2a2d7e17eb86d9c267bd./sfx/buzzmed.wav
31d60649b6797626e38421b32a27eac6a9c84df6./sfx/mystery.wav
8a397e81ea87985ebe0244414ab21ad47d7228f9./sfx/walker.wav
d5b4c7eec94b3c88f33f792db74b539829ef21e3./sfx/cantina.wav
200886036f2af0e382e9b3986542e35662f99929./sfx/locator.wav
b0ada8bbd7c352a2b489faa9fdb640abc8cd220a./sfx/pistol.wav
4e64a3a79a879c91c35bf614e5b81965ecf434eb./sfx/buzzlow.wav
7c45cbecd946fee69b515bb01c436bd82acc0d14./sfx/emptheme.wav
e8e40ba4bd82630434b826a3265edc8549ae34b3./sfx/wookie.wav
7791b4cad2437e10169c1f60d47a0fae953f167a./sfx/armforce.wav
40e6664e59ed24e7107d5785e6bf72375546e576./sfx/blaster.wav
1d301d467868f8727e7327a81e6dc0a4ac4d7a9c./sfx/doorelec.wav
72d23409767760589c08e52495ab4136e5af1ee6./sfx/saberout.wav
ef059f05dcb04d32f0af1fbd7163b962e6e4fd0d./sfx/rifle.wav
46323d03602a3d97a8b46bebf68cd5dcdc6ad0cc./sfx/switch-7.wav
5c8c54eb51f19cdb0e3745e30cf5d9dccdb25151./sfx/switch-6.wav
caa7ad2b6e00e02ebb8f136c33d372a0bb7d8b1c./sfx/vader.wav
071bd82aff663e50aa78e8d3fe2001f6aab1f7d8./sfx/eep.wav
978cebdf7023a24acd2b0e0795c034da1c9e47b3./sfx/banghuge.wav
717b92788485092181957614fd3b156f4549b51c./sfx/switch-4.wav
3191a59954f98347c55be705c27064d1c96ce825./sfx/switch-5.wav
9bd9dbe0bac4fe541c620bac501cfbaf179a29ce./sfx/truckin.wav
46323d03602a3d97a8b46bebf68cd5dcdc6ad0cc./sfx/switch-1.wav
e0ab059defcc74c0f44c246750f442203930471a./sfx/speeder.wav
8e9962d0069c0b74c07ce61005bf9dc3003b9f9c./sfx/banglrg.wav
55ebccfc7ea4327ffda2607e081c4d99fcccdc5d./sfx/hugedoor.wav
7af931273d6374af73538b1580ff04f287de6ded./sfx/schwing.wav
1b4293242d61461914623ffad36867537ab199d5./sfx/transprt.wav
7995af7e4cd2b97e5419e1ea93dfcb5eb7c356c8./sfx/tryagain.wav
5f384cd6565305544f06f650a64b01f4f15c4bbb./sfx/switch-2.wav
690c39b1ea1cdf86e28cf1335cb620c3ba92727a./sfx/youwin.wav
97269a0f50b7bf37530d4c0b7ec0ce9089c8718d./sfx/force.wav
5687e94a1faac7dbe75881e9dd6c1c8fe031984d./sfx/armed.wav
8e6b3a02e9fa3293b87899336a8189568bb8bbf4./sfx/shpoonk.wav
dd9ce24759ac8ddf5e09f6b848c5dd71cd9a1b6c./sfx/switch-3.wav
3d0f7ad1aa43aa7d2e8daf3c509d307185fd252e./sfx/r2d2-2.wav
d55ff1a47a2a1a08c26990a713e2310546461703./sfx/bong.wav
d7e352947ee21ef48b1f020c42c999820caa176d./sfx/r2d2-1.wav
bdc3addce338d60d2b4e4fe3452508fcfdd0f7a4./sfx/saber.wav
3fb938e254ba8b9426b38857d02de48c998db9a2./sfx/xwingin.wav
9e2f78a639a27685f9184eef514af521d3f9223b./sfx/bleepsml.wav
9920d7446a2f259f0a62dffd0e61e30fb7eb4dfd./sfx/buzzhigh.wav
5b5e4581d77527afc79163fdcc31f0b4ded65005./sfx/chestsml.wav
b710cd2f427f742ee1202adf04d9caf242ea6026./sfx/switch-8.wav
3f2d782c4788ae1bfeed65103863c274d9978649./sfx/tieby.wav
833757378d9e8ad02c0a441439349402c627328d./sfx/impblst.wav
419cc1734f33953219b94d9316f2780a1655aa7d./sfx/blip.wav
b55808ae16478322f62b3a510b5618addca12cfc./sfx/saberswg.wav
c7bde605481cc7450397fc966d2edcf949eddaf2./sfx/xwingout.wav
3f8f63d62dababaed9c9fd0da3c5687fc8d6d91c./sfx/truckout.wav
79a9a56abd0525f2e2980009a8ae313cd91f382c./sfx/flourish.wav
2722f7599fe8ac7e4d3876276aea3c71fdd53327./sfx/chirp.wav
75b1ebef0a2b29f14d787fe6ca8af3d2fc15af5b./sfx/opening.wav
37e222b09f2b1cf86dfbbac5cd06ce1d7bb79c11./sfx/splash.wav
2d4d0af951307083b7cce9840e7ee4d3013d2590./sfx/riflblst.wav
ccbe0e1d23f353e2ff5f8a50a41cbf3e1b62285a./sfx/mapcls.wav
65f50467bd6331d13498d547c9d08dc00b2ebcca./sfx/xwingby.wav
7640aefae2ec587bfeeb3456a66fa57e6d3b6d06./sfx/crash.wav
ebddf621f032f619d14b3b4eae2296fe58413efa./sfx/bangmed.wav
e8febb3a41d4eef2587dad9cd5e2719d7392635d./sfx/push.wav
cd5db260b3b0879e48064c65eee061942f955ced./sfx/nogo.wav
5e7bc1330365191feecd53d8595ee1da96b74b85./sfx/moverock.wav
b1e5be033b13b24806ea961f7998fdffe2f81803./sfx/doorpnum.wav
13e914ad378bcef9a7b030200e0dc7a66c078473./wavemix.ini
b6d1ac4d9f165403898531341d13885c770d4177./MSVCRT40.DLL
d44d3a01bdb0cee31163466ba91614eba8ffc21d./YodaDemo.cnt
21265b5d3d600ecd9869a88c70096b3e0ea3c6b2./wavmix32.dll
07d1ce2f03e8596b068f1972c897f2615ca83743./YodaDemo.exe

Indiana Jones and his Desktop Adventures

Indiana Jones and his Desktop Adventures (English)

sha1Common Name
4095a9168865b07f09f19d82d33c6f31c8f065f8./WHIP.WAV
8230c23a28266b2f1ae919d8827b3da41ca071d3./ROAR.WAV
893ef56002780a810f86b0ef25bac36056ac1f70./GUNSHOT.WAV
add15e7a707f6a9bdad4315d4ea4122616f998d0./DESKADV.EXE
6a854968594d232d83e8dc6938f89d6509eae715./ARROW.WAV
a0f074324ae7f26ad3432204c4f956bd021c2f6d./THEME.MID
4956f47846c4b0cb07eb58b995149c10948586b6./THEMW3.MID
834a2ad22aef7ddbede869bced2e4c7a00cdb0c9./EXPLODE.WAV
6a03617143cb01d69fa50589e192f59b787d5ca8./INDYHURT.WAV
5d5d3f95816e8ab39b899fda5e8922b039fbc43b./EEP.WAV
5ae192363d2cb894ed8b3581dc7d485e90e2b9d1./MACHETE.WAV
6eb197cbcfb06848662c7e2fea2211c0abf03693./VICTORY.MID
ef9e4f363a8692509632ecde4ab9436ea081f31f./SCHWING.WAV
8bfc1b0ff35a1274c424fb354820e8c808fb0133./EERIE.MID
8e6b3a02e9fa3293b87899336a8189568bb8bbf4./SHPOONK.WAV
829675f1515db77378f0704619fe68dbaf51a8eb./SPEAR.WAV
8d911b8fb12f2b1a7334c05ac786fbe8074fbff6./DESKTOP.DAW
5b85e81efdcb70c62a2c6d97b0afac4f674a7e27./FLOURISH.MID
9f83ecdfe7e66893837daca54d66cdfe3a2fc7c6./WALLPPR.BMP
d886abc61fe71bd816fc9a32bf6ff55fbd331ba7./DESKADV.HLP
f32bc99e42cf6de9234004aeeca035492ba17d57./DEFEAT.MID
5be5f69e44d346041f65e2f93ac8c40ccf3d5738./PUSH.WAV
5fa53bc8756a5876bfdd1ed0b19d25be49bbd4b5./NOGO.WAV
9f0b6e7da8b3e12833fa4802411ff632fb7a60f7./DOOR.WAV
75f4180fd4e8435c8b151509393b2dd6a1ed283a./SWITCH.WAV

Indiana Jones and his Desktop Adventures (French)

sha1Common Name
4095a9168865b07f09f19d82d33c6f31c8f065f8./WHIP.WAV
8230c23a28266b2f1ae919d8827b3da41ca071d3./ROAR.WAV
893ef56002780a810f86b0ef25bac36056ac1f70./GUNSHOT.WAV
f945b03e6259fde34c50e8baf23e5c005a7ed510./DESKADV.EXE
6a854968594d232d83e8dc6938f89d6509eae715./ARROW.WAV
a0f074324ae7f26ad3432204c4f956bd021c2f6d./THEME.MID
4956f47846c4b0cb07eb58b995149c10948586b6./THEMW3.MID
834a2ad22aef7ddbede869bced2e4c7a00cdb0c9./EXPLODE.WAV
6a03617143cb01d69fa50589e192f59b787d5ca8./INDYHURT.WAV
5d5d3f95816e8ab39b899fda5e8922b039fbc43b./EEP.WAV
5ae192363d2cb894ed8b3581dc7d485e90e2b9d1./MACHETE.WAV
6eb197cbcfb06848662c7e2fea2211c0abf03693./VICTORY.MID
ef9e4f363a8692509632ecde4ab9436ea081f31f./SCHWING.WAV
8bfc1b0ff35a1274c424fb354820e8c808fb0133./EERIE.MID
8e6b3a02e9fa3293b87899336a8189568bb8bbf4./SHPOONK.WAV
829675f1515db77378f0704619fe68dbaf51a8eb./SPEAR.WAV
23a4eb2332c3d90e3e71b531034309ee207266b6./DESKTOP.DAW
5b85e81efdcb70c62a2c6d97b0afac4f674a7e27./FLOURISH.MID
9f83ecdfe7e66893837daca54d66cdfe3a2fc7c6./WALLPPR.BMP
c96ec6315d4de9dbc6ea8e8bc32150f961fb0d07./DESKADV.HLP
f32bc99e42cf6de9234004aeeca035492ba17d57./DEFEAT.MID
5be5f69e44d346041f65e2f93ac8c40ccf3d5738./PUSH.WAV
5fa53bc8756a5876bfdd1ed0b19d25be49bbd4b5./NOGO.WAV
9f0b6e7da8b3e12833fa4802411ff632fb7a60f7./DOOR.WAV
75f4180fd4e8435c8b151509393b2dd6a1ed283a./SWITCH.WAV

Indiana Jones and his Desktop Adventures (Spanish)

sha1Common Name
4095a9168865b07f09f19d82d33c6f31c8f065f8./WHIP.WAV
8230c23a28266b2f1ae919d8827b3da41ca071d3./ROAR.WAV
893ef56002780a810f86b0ef25bac36056ac1f70./GUNSHOT.WAV
c996f07955175daea25d8a1dbe571d48851cf7d9./DESKADV.EXE
6a854968594d232d83e8dc6938f89d6509eae715./ARROW.WAV
a0f074324ae7f26ad3432204c4f956bd021c2f6d./THEME.MID
4956f47846c4b0cb07eb58b995149c10948586b6./THEMW3.MID
834a2ad22aef7ddbede869bced2e4c7a00cdb0c9./EXPLODE.WAV
6a03617143cb01d69fa50589e192f59b787d5ca8./INDYHURT.WAV
5d5d3f95816e8ab39b899fda5e8922b039fbc43b./EEP.WAV
5ae192363d2cb894ed8b3581dc7d485e90e2b9d1./MACHETE.WAV
6eb197cbcfb06848662c7e2fea2211c0abf03693./VICTORY.MID
ef9e4f363a8692509632ecde4ab9436ea081f31f./SCHWING.WAV
8bfc1b0ff35a1274c424fb354820e8c808fb0133./EERIE.MID
8e6b3a02e9fa3293b87899336a8189568bb8bbf4./SHPOONK.WAV
829675f1515db77378f0704619fe68dbaf51a8eb./SPEAR.WAV
ac2e926c2a7374197d299d4b7625ed118bc7fbda./DESKTOP.DAW
5b85e81efdcb70c62a2c6d97b0afac4f674a7e27./FLOURISH.MID
73649fb9f2a773505a06c31a8e024eec3c5e7552./WALLPPR.BMP
e709998bac74b08ad507a742bbbf8c20a5899fc0./DESKADV.HLP
f32bc99e42cf6de9234004aeeca035492ba17d57./DEFEAT.MID
5be5f69e44d346041f65e2f93ac8c40ccf3d5738./PUSH.WAV
5fa53bc8756a5876bfdd1ed0b19d25be49bbd4b5./NOGO.WAV
9f0b6e7da8b3e12833fa4802411ff632fb7a60f7./DOOR.WAV
75f4180fd4e8435c8b151509393b2dd6a1ed283a./SWITCH.WAV