Fandom

La-Mulana Remake Wiki

File Formats

148pages on
this wiki
Add New Page
Comments0 Share

La-Mulana stores its various data across a variety of different files, all of which (except for the various .png, .wav, and .ogg files) seem to use custom file formats. Fortunately none of the files seem to employ any data compression, making understanding their formats a comparatively simple project.

All results listed below are from research conducted on the GOG release of the game. It is unknown if other releases differ in any way.

map##.msd format Edit

Used by data/mapdata/map00.msd through data/mapdata/map25.msd, as well as all the mapdata/map##.msd files for each of the time attack modules. There are a number of other files in the same folder with the same extension (boss##.msd, ending#.msd, opening.msd, shop.msd, title.msd), but those have not been looked at yet and may or may not have the same internal format.

  • All properties appear to be signed and big-endian.
  • .msd files correspond closely enough to Fields that the two terms can be used basically interchangeably, but there is not quite a 1:1 correspondence. For example, the two halves of the Chamber of Birth are divided across map15.msd and map16.msd.
  • A "zone" is a unit smaller than a field but (potentially) larger than a single room. For example, rooms B3-B5 in the Gate of Guidance are a single zone, and likewise rooms D3-F4 in the Temple of the Sun. Zone boundaries are discernible in-game in at least two ways: parallax scrolling only works when going from one room in a zone to another, and more objects respawn when leaving and reentering a zone than when going from one room in a zone to another. The number of rooms in a zone is not explicitly stated but is instead calculated by multiplying the width (divided by 640 pixels) and height (divided by 480 pixels).
  • Additionally, most .msd fields define a number of zones that contain no rooms (as defined as areas that the player can enter, and may contain interact-with-able objects) at all. Most doors and other parts of the game constructed from various tiles in a map##_1.png file are actually roomless zones inserted into a room. The only way of distinguishing zoneless rooms appears to be checking whether or not their dimensions are multiples of room sizes (64 by 48).
  • The function of the GraphicsFilesIDPlusTwo byte differs slightly between main game and time attack .msd files. In the main game, a value of 02 means that the field uses map00_1.png and eveg00.png; a value of 05 means that the field uses map03_1.png and eveg03.png, and so on. In time attack modules, the value is a line number for that module's list.dat file, so 02 means that the field uses the images from the first non-comment line, 05 the images from the third non-comment line, and so on.
  • Additionally, files map22.msd (Surface at night), map23.msd (Hell Temple), and map24.msd (additional Hell Temple areas using eveg20.png instead of eveg19.png) use special values for GraphicsFilesIDPlusTwo. The reason for this is unknown.
  • See data/graphics/hit_parts.png for clues about the various possible values of the CollisionMask bytes. The most common are 0 (unmasked, can be walked through) and 128 (orange, solid wall). The green ladder-looking-structures are ladders. The light blue backdrops are water (and the arrows are water currents), followed by purple lava. The vertical blue lines are waterfalls. The brown checkerboard is an area that Yowies can crawl on. The blue walls are ice/walls that the Grapple Claw doesn't work on. The orange slopes are slippery and require Hermes' Boots to walk up. The dark green arrow tiles are walls that you can die from being smashed into by moving blocks.
struct MapMSD {
	struct AnimatedTile {
		short NumberOfFrames;
		TileID Frames[NumberOfFrames]; //unknown which higher-bit TileID properties are or aren't available in this context
	} AnimatedTiles[];
	short EndOfAnimatedTileSection; //always 00 00, which would be parsed as an animated tile that is zero frames long
	byte GraphicsFilesIDPlusTwo; //see discussion above
	short NumberOfZones;
	struct Zone {
		byte Unknown; //purpose unknown; always zero?
		byte NumberOfLayers;
		byte NumberOfForegroundLayers; //foreground layers are all those drawn in front of the player character. There seems to have been a restriction in the La-Mulana editor such that every zone has at least one background layer and at least one foreground layer, even roomless zones for which the distinction presumably shouldn't matter
		short ZoneWidth; //measured in mask tiles, which are 10x10
		short ZoneHeight; //measured in mask tiles
		byte CollisionMask[ZoneWidth * ZoneHeight]; //see discussion above
		struct Layer {
			short LayerWidth; //measured in graphics tiles, which are 20x20
			short LayerHeight; //measured in graphics tiles
			byte NumberOfSublayers;
			struct Sublayer {
				TileID[LayerWidth * LayerHeight];
			} Sublayers[NumberOfSublayers]; //ordered so that the first is in front and the last is in back
		} Layers[NumberOfLayers]; //ordered so that the first is in front and the last is in back
	} Zones[NumberOfZones];
}

struct TileID {
	bit11 RawTileID; //each row in the map##_1.png files is 50 tiles wide, so RawTileID=49 would be tile 49,0 and RawTileID=50 would be tile 0,1. It doesn't matter whether the .png is the full 1024 pixels tall.
	bit2 TileType; //0: animated tile. 1: standard tile. 2: lighten. 3: darken.
	bit1 FlippedHorizontally; //boolean
	bit1 Rotated90Degrees; //boolean
	bit1 Rotated180Degrees; //boolean
}

.rcd format Edit

Used by data/mapdata/script.rcd, as well as mapdata/script.rcd for each of the time attack modules.

  • Note that it seems to be impossible to parse an entire .rcd file without simultaneously parsing all the corresponding .msd files, because the .rcd format does not seem to include any explicit markers of how many fields, zones, or rooms there are.
  • Each "field" corresponds to a .msd file, not necessarily a field as is presented to the game player.
  • Again, all properties appear to be signed and big-endian.
  • There seem to be two-hundred-and-one (numbering from 0 to 200) different EventIDs, each of which has the same number of parameters every time it appears, despite the .rcd format stating that number explicitly. For example, a Wall Seal, EventID 52, always has exactly two parameters: one for specifying which key seal is needed to open it (values 0-3), and one for specifying whether it is one of the giant seals in the True Shrine of the Mother (value 1) or only a regular-sized seal (value 0). A full list of these EventIDs, as well as the purposes of their numerous parameters, would be a large undertaking and belongs on a separate wiki page.
  • Events are divided into two types, Objects and Effects, the difference being that Objects are placed in specific positions in specific rooms, whereas Effects don't have specific positions, and may apparently be bound to rooms, zones, or entire fields. An example of an Object would be a Pot; an example of an Effect would be specifying that Chonchons should be continually generated in a room. Any given EventID is either always an Object or always an Effect.
struct ScriptDotRCD {
	short Unknown; //purpose unknown; possibly intended as a version number? always zero?
	struct Field {
		byte LengthOfInternalFieldName;
		short FieldEffectCount; //seemingly always 0 in time attacks
		byte FieldName[LengthOfInternalFieldName]; //purpose unknown; possibly some kind of name?
		Effect FieldEffects[FieldEffectCount];
		struct Zone {
			short ZoneEffectCount; //seemingly always 0 for roomless zones or in time attacks
			Effect ZoneEffects[ZoneEffectCount];
			struct Room {
				byte FooterLength;
				byte Unknown; //purpose unknown; always zero?
				byte NumberOfObjects;
				byte NumberOfEffects;
				Effect Effects[NumberOfEffects];
				Object Objects[NumberOfObjects - NumberOfEffects];
				byte Footer[FooterLength]; //purpose unknown
				struct Exit {
					byte FieldID;
					byte ZoneID;
					byte RoomID;
				} Exits[4]; //order is top, right, bottom, left
			} Rooms[];
		} Zones[];
	} Fields[];
}

struct Object {
	short EventID;
	bit4 NumberOfFirstFlags;  //NumberOfFirstFlags and NumberOfSecondFlags together make up a single byte
	bit4 NumberOfSecondFlags; //
	byte NumberOfParameters;
	short PositionX;
	short PositionY;
	struct Flag { //these are still poorly understood, but control all the changes made to the game as you progress; for example, using a Dais to open a door requires at minimum that both objects make reference to the same flag number, although the way in which they each reference it will be different
		byte Bytes[4]; //structure as yet unknown
	} Flags[NumberOfFirstFlags + NumberOfSecondFlags];
	short Parameters[NumberOfParameters];
}
struct Effect { //exactly the same as Object, but with no Position properties
	short EventID;
	bit4 NumberOfFirstFlags;
	bit4 NumberOfSecondFlags;
	byte NumberOfParameters;
	Flag Flags[NumberOfFirstFlags + NumberOfSecondFlags];
	short Parameters[NumberOfParameters];
}

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.