kondrak / YendorianTalesRE

Yendorian Tales 2 & 3 game data reverse engineering

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Yendorian Tales 2 & 3 - game data reverse engineering

Yendorian Tales 2 (Book I: Chapter 2) and Yendorian Tales 3: The Tyrants of Thaine are fairly forgotten games of the late 1990s which, by today's standards, can be considered a clone of Might and Magic series. The goal of this project is to extract and describe basic data used by these games which could potentially lead to a modern remake or serve as a basis of a test project for a dungeon crawler game. All work was done using the HxD editor and DosBox debugger to track assembly calls (mostly focusing on file access functions).

Graphics

Both games store image data in a dedicated PICTURES.VGA file which is an uncompressed stream of bytes - each one representing a single pixel referencing an index in a specific 256-color DOS palette. There's no size nor offset information stored in this file, everything is kept in the game's executable. Graphics are grouped by size, ranging from the largest (fullscreen images) to smallest (8x8 pixel icons used throughout the game). PICTURES.VGA is kept open during runtime and required images are fetched when necessary. Games run in mode 13h at all times, one exception being the Spectrum Pacific Publishing splash screen which is 640x480 pixels and stored in the executable as LOGO.PCX. For some reason this image is extracted and saved on the disk during startup, only to be removed after exiting the game.

Color palette

Palette data was extracted using DosBox's screen capture and saved as binary .pal files. Final color is used for transparency information - the extracted palettes set that value to magenta (0xFF00FF) for better distinction (originally 0xFFFFFF is used). Most animation effects (enhanced weapons, texture shimmering) is achieved with color cycling - a common technique used in games at the time.

Enemies

Enemies consist of 10 fixed-size frames: 6 frames for idle animations, 3 frames for attack, 1 frame for pain state - this is true even for creatures that don't have dedicated idle/attack/pain animations (like Dwarf Towers) - the images are simply duplicated. For multiple variants of the same enemy (Centipede/Millipede etc.) different palette colors are being used.

Enemy data structure

Enemies take 106 bytes per creature in the WORLD.DAT file - starting at offset 0x1A7994 in Yendorian Tales 2 and 0x4170DF in Yendorian Tales 3. Enough data has been reverse engineered to get basic stats and properties but there's still some information that needs to be discovered:

    struct Enemy {
        char name1[13];              // creature name: first part
        char name2[13];              // creature name: second part (optional)
        uint16_t start_frame_offset; // presumably offset to first idle frame in PICTURES.VGA
        uint16_t unknown_0;
        uint16_t health;             // creature stats
        uint16_t unknown_1;
        uint16_t accuracy;           // creature stats
        uint16_t dexterity;          // creature stats
        uint16_t absorption;         // creature stats
        uint16_t damage;             // creature stats
        uint16_t attack_snd_index;   // .VOC file number
        uint16_t unknown_2;
        uint16_t projectile_image;     // offset to projectile image
        uint16_t projectile_snd_index; // .VOC file number - no ranged attack if 0x00
        uint16_t ranged_acc;           // creature stats
        uint16_t ranged_dam;           // creature stats
        uint16_t unknown_3;
        uint16_t unknown_4;
        uint16_t unknown_5;
        uint16_t special_attack1;        // first special attack flag
        uint16_t projectile_attack_type; // kill/effect type for projectile hit
        uint16_t unknown_6;
        uint16_t unknown_7;
        uint16_t unknown_8;
        uint16_t unknown_9;
        uint32_t UNUSED_0;           // always zero
        uint32_t gold;               // kill reward, decimal encoding - max 99,999,999
        uint32_t nuore;              // kill reward, decimal encoding - max 99,999,999
        uint32_t food_magic_ore;     // kill reward, decimal encoding - max 99,999,999
        uint32_t experience;         // kill reward, decimal encoding - max 99,999,999
        uint16_t UNUSED_1;           // always zero
        uint16_t unknown_10;
        uint8_t  animation_flags;    // upper 4 bits - idle animation type: 0 - none, 1 - ping-pong, 2 - restart; lower 4 bits: still unknown, looks like some sort of additional offset
        uint8_t  special_attack2;    // second special attack flag
        uint8_t  mobility;           // creature is immobile - like Fungus or Dwarf Towers (0x02) or mobile (other)
        uint8_t  translucency;       // determines if sprite is translucent (Ghost, Phase Titan) - 0x80 - on, 0x00 - off
        uint16_t immunity_bitmask;   // creature's immunities
        uint8_t  UNUSED_2;           // always zero
        uint8_t  resistance_bitmask; // creature's resistances
        uint16_t UNUSED_3;           // always zero
    }

Scene elements

Most objects are duplicated in flipped and unflipped variants (used depending on player position in the world) - wasteful in terms of space but it seems this approach was easier for the developers. Some (for example portals) have a palette effect attached to them. All sky and ground textures are represented by two images - flipped and unflipped, even if there's only one variant used in the game (like Astral Plane sky texture).

Spells

Spells take 80 bytes per entry in the WORLD.DAT file - starting at offset 0x1AA65E in Yendorian Tales 2 and 0x41B5BF in Yendorian Tales 3.

Audio

Both games use Creative Voice File (.VOC) for digitized audio and Creative Music File (.CMF) for music.

Digitized sounds

Sound files are stored uncompressed in WORLD.DAT. File offsets (4 bytes), followed by size information (2 bytes) are stored in the game's executable. For Yendorian Tales 2 (SWREG.EXE) it starts at offset 0x2EBF1 and for Yendorian Tales 3 (REGISTER.EXE) at offset 0x2D057. Each entry is followed by a 0x00 byte. Most modern media players (like VLC) support .VOC playback without any problem.

Music

Unlike MIDI, this format stores instrument banks directly in the file - this way the playback sounds exactly the same with every audio driver. All .CMF files are stored uncompressed in WORLD.DAT. File offsets (4 bytes), followed by size information (2 bytes) are stored in the game's executable. For Yendorian Tales 2 (SWREG.EXE) it starts at offset 0x2EB73 and for Yendorian Tales 3 (REGISTER.EXE) at offset 0x2CFC7. Each entry is followed by a 0x00 byte, same as in the case of .VOC files. Creative Music File is hardly supported by contemporary audio players, so the only way to play it back is to either use a music player in DosBox (like MuchMusic) or convert it to a more modern format.

Sample programs

  • parse_audio.c - extracts sound and music data
  • parse_enemies.c - extracts and prints enemy information
  • parse_pictures.c - extracts graphics and exports them to .PNG files (with or without alpha channel)
  • parse_spells.c - early WIP spell data extraction

TODO

  • identify spell structure fields
  • identify map structure

About

Yendorian Tales 2 & 3 game data reverse engineering

License:MIT License


Languages

Language:C 100.0%