mirror of
https://github.com/cave-story-randomizer/cave-story-randomizer
synced 2025-11-21 20:13:40 +00:00
20 KiB
20 KiB
Cave Story Research Compendium
A guide to Cave Story's file formats and important hacks
by fdeitylink
Last updated 2019-10-23
Rationale
- Learning more about Cave Story for my own edification
- File formats & important hacks weren't all published and collected
- Actually, they are included with Miza, but I hadn't realized this when beginning
Notes
- Some information may be incorrect - S.P. Gardebiter's guides are a bit old. Feel free to tell me of any information that is incorrect or in need of an update.
- Cave Story was made for Windows and x86 architecture, which leads to some nuances to look out for
- Newlines are encoded as
\r\n(affects TSC) - Multibyte data is encoded in little endian format
- Newlines are encoded as
Credits
Big thanks to all of the following people for much of the following information!
- Carrotlord for the TSC file format
- q3hardcore for the (C)Pixel requirement removal hack
- S.P. Gardebiter for the PXA file format values, the executable mapdata format, and the
npc.tblformat - Noxid for Booster's Lab
- Used to determine how particular edits affected the files
- Provided newest information, especially tileset and sound values for
npc.tbl
WIthout further ado, let's begin!
Flags
Setting Flags
Bitwise OR the current flags with the flag to set
flags |= flag
Unsetting Flags
Bitwise XOR the flag to set with 0b1111_1111
Bitwise AND the current flags with that result
flags &= (flag ^ 0xFF)
Checking Flags
Bitwise AND the current flags with the flag to check
Returns flag if it is set, 0 otherwise
is_set = (flags & flag)
TSC File Format
- The encoding character is the middle character in the file (i.e. the character at
floor(file_size / 2).- For example, if the file size is 313 bytes, the encoding character is at the 156th byte.
- During encoding, all characters in the file, except for the encoding character itself, have the value of the encoding character added to them.
- To be clear, the encoding character itself, at the middle of the file, is not encoding, but other instances of it can be. If the encoding character was
a, that one instance ofawould not be encoded, but others would be.
- To be clear, the encoding character itself, at the middle of the file, is not encoding, but other instances of it can be. If the encoding character was
- To encode a TSC file, add the value of the encoding character to all characters except for the encoding character.
- To be clear, do not encode the middle byte of the file
- It then follows that to decode a TSC file, subtract the value of the encoding character from each character
- Also note that newlines in TSC files are Windows-style (
"\r\n"), so they take up two bytes that should each be treated as independent characters when encoding and decoding.
Removing the (C)Pixel Requirement
| Offset | Old Value | New Value |
|---|---|---|
0x1136C |
0x74 |
0xEB |
0x8C4D8 |
0x28 |
0x00 |
In every .pbm file, find the (C)Pixel at the end and replace the ( with the null character (0x00)
Enabling bitmaps
| Offset | Old Value | New Value |
|---|---|---|
0x8C286, 0x8C30A, 0x8C32E |
0x70 |
0x62 |
0x8C287, 0x8C30B, 0x8C32F |
0x62 |
0x6D |
0x8C288, 0x8C30C, 0x8C330 |
0x6D |
0x70 |
npc.tbl Format
- Cave Story has 361 (
0x169) entities numbered from 0 to 360 npc.tblcontains 11 sets of data. The starting address of each set is a multiple of0x169.- Each set of data contains the corresponding value for each entity, in order.
- For example: At offset
0x02D2is the Health data for entity 0. At offset0x02D4is the Health data for entity 1.
- For example: At offset
| Offset | Description | Size per Entity (bytes) |
|---|---|---|
0x0000 |
Flags | 2 |
0x02D2 |
Health | 2 |
0x05A4 |
Tileset Number | 1 |
0x070D |
Death Sound | 1 |
0x0876 |
Hurt Sound | 1 |
0x09DF |
Death Smoke | 1 |
0x0B48 |
Experience | 4 |
0x10EC |
Damage | 4 |
0x1690 |
Collision Bounding Box | 4 |
0x1C34 |
Display Bounding Box | 4 |
Flags
Byte 1
0x01: Solid0x02: No effect about Tile 440x04: Invulnerable (Blink Sound)0x08: Ignore solid0x10: Bouncing at top0x20: Shootable0x40: Special Solid0x80: Rear and top no damage
Byte 2
0x01: Call Event on Contact0x02: Call Event on Death0x04: Drop Hearts and EXP [Unused]0x08: Visible if FlagID is set0x10: Spawn with Alternate Direction0x20: Call Event on Interaction0x40: Invisible if FlagID is set0x80: Show Damage Numbers ? ("Interactable" in Booster's Lab)
Tilesets
0x00: title.pbm0x01: '2004.12 Studio Pixel'0x02: Current map tileset0x03: [Unused]0x04: [Unused]0x05: [Unused]0x06: Fade.pbm0x07: [Unused]0x08: ItemImage.pbm0x09: Map System Buffer0x0A: Screen Buffer0x0B: Arms.pbm0x0C: ArmsImage.pbm0x0D: MNA Text Buffer0x0E: StageImage.pbm0x0F: Loading.pbm0x10: MyChar.pbm0x11: Bullet.pbm0x12: [Unused]0x13: Caret.pbm0x14: Npc/NpcSym.pbm0x15: Map NPC Set 10x16: Map NPC Set 20x17: Npc/NpcRegu.pbm0x18: [Unused]0x19: [Unused]0x1A: TextBox.pbm0x1B: Face.pbm0x1C: Current Map BG0x1D: Damage # Buffer0x1E: Textbox Buffer 10x1F: Textbox Buffer 20x20: Textbox Buffer 30x21: [???]0x22: [Unused]0x23: Credits Buffer 10x24: Credits Buffer 20x25: Credits Buffer 30x26: [Unused]0x27: [Unused]
Hurt / Death Sounds
0x00: [Nothing]0x01: Blip0x02: Message Typing0x03: Bonk0x04: Weapon Switching0x05: Menu Prompt?0x06: Critter hop0x07: Silent?0x08: Low charge sound0x09: [Nothing?]0x0A: [Nothing?]0x0B: Door0x0C: Block Destroy0x0D: [Nothing?]0x0E: Get EXP0x0F: Quote Jump0x10: Taking Damage0x11: Death0x12: [Menu?]0x13: [Nothing?]0x14: Health/Ammo Refill0x15: Bubble0x16: Chest open0x17: Thud0x18: Walking0x19: Enemy killed?0x1A: Quake0x1B: Level up0x1C: Shot hit0x1D: Teleport0x1E: Critter jump0x1F: Ting0x20: Polar Star lvl0x21: Fireball0x22: Fireball bounce0x23: Explosion0x24: [Nothing?]0x25: No Ammo0x26: Get item?0x27: [bvng] Em fire? - taken from BL, what is em?0x28: Water0x29: Water0x2A: Get Missile [Beep]0x2B: Computer [Beep]0x2C: Missile Hit0x2D: EXP Bounce0x2E: Ironhead Shot0x2F: Explosion 2?0x30: Bubble pop0x31: Spur lvl 10x32: Sqeek!0x33: Squeal!0x34: Roar0x35: Eyoww0x36: Thud0x37: Squeek0x38: Splash0x39: Little damage sound0x3A: [chik]0x3B: Spur Charge (lowest)0x3C: Spur Charge (lower)0x3D: Spur Charge (higher)0x3E: Spur lvl 20x3F: Spur lvl 30x40: Spur MAX0x41: Spur full?0x42: [Nothing?]0x43: [Nothing?]0x44: [Nothing?]0x45: [Nothing?]0x46: Tiny Explosion0x47: Medium Explosion0x48: Large Explosion0x49: [Nothing?]0x4A: [Nothing?]0x4B: [Nothing?]0x4C: [Nothing?]0x4D: [Nothing?]0x4E: [Nothing?]0x4F: [Nothing?]0x50: [Nothing?]0x51: [Nothing?]0x52: [Nothing?]0x53: [Nothing?]0x54: [Nothing?]0x55: [Nothing?]0x56: [Nothing?]0x57: [Nothing?]0x58: [Nothing?]0x59: [Nothing?]0x5A: [Nothing?]0x5B: [Nothing?]0x5C: [Nothing?]0x5D: [Nothing?]0x5E: [Nothing?]0x5F: [Nothing?]0x60: [Nothing?]0x61: [Nothing?]0x62: [Nothing?]0x63: [Nothing?]0x64: Bubbler lvl 30x65: Lightning0x66: Sandcroc Bite0x67: Curly Charge0x68: Hit Invisible Block0x69: Puppy Bark0x6A: Blade whoosh0x6B: Block Move0x6C: Power Critter Jump0x6D: Critter Fly0x6E: Power Critter Fly0x6F: Thud0x70: Bigger thud0x71: [pshew] Helicopter?0x72: Core hurt0x73: Core thrust0x74: Core super charge0x75: Nemesis?0x76: [Nothing?]0x77: [Nothing?]0x78: [Nothing?]0x79: [Nothing?]0x7A: [Nothing?]0x7B: [Nothing?]0x7C: [Nothing?]0x7D: [Nothing?]0x7E: [Nothing?]0x7F: [Nothing?]0x80: [Nothing?]0x81: [Nothing?]0x82: [Nothing?]0x83: [Nothing?]0x84: [Nothing?]0x85: [Nothing?]0x86: [Nothing?]0x87: [Nothing?]0x88: [Nothing?]0x89: [Nothing?]0x90: [Nothing?]0x91: [Nothing?]0x92: [Nothing?]0x93: [Nothing?]0x94: [Nothing?]0x95: [Nothing?]0x96: BASS010x97: SNARE010x98: HICLOSE0x99: HIOPEN0x9A: TOM010x9B: PER01
Smoke
0x01: None0x02: Small amount0x03: Medium amount0x04: Large amount
Bounding Box Addresses
From the beginning of each entity's section
0x00: Left0x01: Top0x02: Right0x03: Bottom
Map Formats
Map Metadata
- Applies to freeware executable and CS+
stage.tblfile - Begins at offset
0x937B0in freeware executable
| Offset (from beginning of each map section) | Description | Size (bytes) |
|---|---|---|
0x00 |
Tileset name | 32 |
0x20 |
Filename | 32 |
0x40 |
Background Scrolling Type | 4 |
0x44 |
Background Name | 32 |
0x64 |
NPC Spritesheet 1 | 32 |
0x84 |
NPC Spritesheet 2 | 32 |
0xA4 |
Major Boss | 1 |
0xA5 |
Map Name | 35 |
Background Scrolling Types
0x00: No Scrolling0x01: Slow Scrolling0x02: Equal Scrolling0x03: Water-Style0x04: Null0x05: Auto Scrolling0x06: Cloud-Style [Gravity: Left]0x07: Cloud-Style [Gravity: Normal]
Major Bosses
0x00: No Major Boss0x01: Omega0x02: Balfrog0x03: Monster X0x04: The Core0x05: Iron Head0x06: Dragon Sisters0x07: Undead Core0x08: Heavy Press0x09: Ballos
PXM File Format
Note: Maps must have a minimum size of 21x16
| Offset | Description | Size (bytes) |
|---|---|---|
0x00 |
"PXM" |
3 |
0x03 |
0x10 |
1 |
0x04 |
Map length | 2 |
0x06 |
Map height | 2 |
| Rest of the file | Tile index (left to right then top to bottom) | 1 |
PXE File Format
| Offset | Description | Size (bytes) |
|---|---|---|
0x00 |
"PXE" |
3 |
0x03 |
0x00 |
1 |
0x04 |
Entity count | 4 |
| Rest of the file | Entity | 12 |
Entity Format
| Offset (from beginning of each entity section) | Description | Size (bytes) |
|---|---|---|
0x00 |
x coordinate | 2 |
0x02 |
y coordinate | 2 |
0x06 |
flag number | 2 |
0x08 |
event number | 2 |
0x0A |
type | 2 |
0x0C |
flags | 2 |
PXA File Format
- No header
- Array of tile types corresponding to tiles in a tileset (left to right then top to bottom)
- Tilesets have up to 16x16 tiles, so PXA files are always 256 bytes - fill in
0x00for tiles not in the tileset
Flags
0x01: Special0x02: Special0x04: Special0x08: Special0x10: Slope0x20: Water0x40: Foreground0x80: Wind
Null (0x00)
0x00: Null0x01: Background Tile0x02: Background Water0x03: Background NPC-Blocker Tile [Unused]0x04: Background NPC-Blocker Tile [Unused]0x05: Background Shoot-Passer Tile [Unused]0x06: Background Tile [Unused]0x07: Background Tile [Unused]0x08: Background Tile [Unused]0x09: Background Tile [Unused]0x0A: Background Tile [Unused]0x0B: Background Tile [Unused]0x0C: Background Tile [Unused]0x0D: Background Tile [Unused]0x0E: Background Tile [Unused]0x0F: Background Tile [Unused]
Slope (0x10)
0x10: Background Tile [Unused]0x11: Background Tile [Unused]0x12: Background Tile [Unused]0x13: Background Tile [Unused]0x14: Background Tile [Unused]0x15: Background Tile [Unused]0x16: Background Tile [Unused]0x17: Background Tile [Unused]0x18: Background Tile [Unused]0x19: Background Tile [Unused]0x1A: Background Tile [Unused]0x1B: Background Tile [Unused]0x1C: Background Tile [Unused]0x1D: Background Tile [Unused]0x1E: Background Tile [Unused]0x1F: Background Tile [Unused]
Water (0x20)
0x20: Null [Unused]0x21: Null [Unused]0x22: Null [Unused]0x23: Null [Unused]0x24: Null [Unused]0x25: Null [Unused]0x26: Null [Unused]0x27: Null [Unused]0x28: Null [Unused]0x29: Null [Unused]0x2A: Null [Unused]0x2B: Null [Unused]0x2C: Null [Unused]0x2D: Null [Unused]0x2E: Null [Unused]0x2F: Null [Unused]
Slope + Water (0x30)
0x30: Null [Unused]0x31: Null [Unused]0x32: Null [Unused]0x33: Null [Unused]0x34: Null [Unused]0x35: Null [Unused]0x36: Null [Unused]0x37: Null [Unused]0x38: Null [Unused]0x39: Null [Unused]0x3A: Null [Unused]0x3B: Null [Unused]0x3C: Null [Unused]0x3D: Null [Unused]0x3E: Null [Unused]0x3F: Null [Unused]
Foreground (0x40)
0x40: Foreground Tile0x41: Solid Tile0x42: 10 Damage Foreground Tile0x43: Special Block Tile0x44: Foreground NPC-Blocker Tile0x45: Foreground Tile [Unused]0x46: Character-Blocker Tile [Unused]0x47: Foreground Tile [Unused]0x48: Foreground Tile [Unused]0x49: Foreground Tile [Unused]0x4A: Foreground Tile [Unused]0x4B: Foreground Tile [Unused]0x4C: Foreground Tile [Unused]0x4D: Foreground Tile [Unused]0x4E: Foreground Tile [Unused]0x4F: Foreground Tile [Unused]
Foreground + Slope (0x50)
0x50: Slope Tile0x51: Slope Tile0x52: Slope Tile0x53: Slope Tile0x54: Slope Tile0x55: Slope Tile0x56: Slope Tile0x57: Slope Tile0x58: Foreground Tile [Unused]0x59: Foreground Tile [Unused]0x5A: Foreground Tile [Unused]0x5B: Foreground Tile [Unused]0x5C: Foreground Tile [Unused]0x5D: Foreground Tile [Unused]0x5E: Foreground Tile [Unused]0x5F: Foreground Tile [Unused]
Foreground + Water (0x60)
0x60: Foreground Water0x61: Solid Tile [Unused]0x62: 10 Damage Foreground Water Tile [Red]0x63: Foreground Tile [Unused]0x64: Foreground NPC-Blocker Tile [Unused]0x65: Foreground Tile [Unused]0x66: Foreground Tile [Unused]0x67: Foreground Tile [Unused]0x68: Foreground Tile [Unused]0x69: Foreground Tile [Unused]0x6A: Foreground Tile [Unused]0x6B: Foreground Tile [Unused]0x6C: Foreground Tile [Unused]0x6D: Foreground Tile [Unused]0x6E: Foreground Tile [Unused]0x6F: Foreground Tile [Unused]
Foreground + Slope + Water (0x70)
0x70: Slope Tile [Water]0x71: Slope Tile [Water]0x72: Slope Tile [Water]0x73: Slope Tile [Water]0x74: Slope Tile [Water]0x75: Slope Tile [Water]0x76: Slope Tile [Water]0x77: Slope Tile [Water]0x78: Foreground Tile [Unused]0x79: Foreground Tile [Unused]0x7A: Foreground Tile [Unused]0x7B: Foreground Tile [Unused]0x7C: Foreground Tile [Unused]0x7D: Foreground Tile [Unused]0x7E: Foreground Tile [Unused]0x7F: Foreground Tile [Unused]
Wind (0x80)
0x80: Wind [Left]0x81: Wind [Up]0x82: Wind [Right]0x83: Wind [Down]0x84: Null [Unused]0x85: Null [Unused]0x86: Null [Unused]0x87: Null [Unused]0x88: Null [Unused]0x89: Null [Unused]0x8A: Null [Unused]0x8B: Null [Unused]0x8C: Null [Unused]0x8D: Null [Unused]0x8E: Null [Unused]0x8F: Null [Unused]
Wind + Slope (0x90)
0x90: Null [Unused]0x91: Null [Unused]0x92: Null [Unused]0x93: Null [Unused]0x94: Null [Unused]0x95: Null [Unused]0x96: Null [Unused]0x97: Null [Unused]0x98: Null [Unused]0x99: Null [Unused]0x9A: Null [Unused]0x9B: Null [Unused]0x9C: Null [Unused]0x9D: Null [Unused]0x9E: Null [Unused]0x9F: Null [Unused]
Wind + Water (0xA0)
0xA0: Water Wind [Left]0xA1: Water Wind [Up]0xA2: Water Wind [Right]0xA3: Water Wind [Down]0xA4: Null [Unused]0xA5: Null [Unused]0xA6: Null [Unused]0xA7: Null [Unused]0xA8:Null [Unused]0xA9: Null [Unused]0xAA: Null [Unused]0xAB: Null [Unused]0xAC: Null [Unused]0xAD: Null [Unused]0xAE: Null [Unused]0xAF: Null [Unused]
Wind + Slope + Water (0xB0)
0xB0: Null [Unused]0xB1: Null [Unused]0xB2: Null [Unused]0xB3: Null [Unused]0xB4: Null [Unused]0xB5: Null [Unused]0xB6: Null [Unused]0xB7: Null [Unused]0xB8: Null [Unused]0xB9: Null [Unused]0xBA: Null [Unused]0xBB: Null [Unused]0xBC: Null [Unused]0xBD: Null [Unused]0xBE: Null [Unused]0xBF: Null [Unused]
Wind + Foreground (0xC0)
0xC0: Null [Unused]0xC1: Null [Unused]0xC2: Null [Unused]0xC3: Null [Unused]0xC4: Null [Unused]0xC5: Null [Unused]0xC6: Null [Unused]0xC7: Null [Unused]0xC8: Null [Unused]0xC9: Null [Unused]0xCA: Null [Unused]0xCB: Null [Unused]0xCC: Null [Unused]0xCD: Null [Unused]0xCE: Null [Unused]0xCF: Null [Unused]
Wind + Foreground + Slope (0xD0)
0xD0: Null [Unused]0xD1: Null [Unused]0xD2: Null [Unused]0xD3: Null [Unused]0xD4: Null [Unused]0xD5: Null [Unused]0xD6: Null [Unused]0xD7: Null [Unused]0xD8: Null [Unused]0xD9: Null [Unused]0xDA: Null [Unused]0xDB: Null [Unused]0xDC: Null [Unused]0xDD: Null [Unused]0xDE: Null [Unused]0xDF: Null [Unused]
Wind + Foreground + Water (0xE0)
0xE0: Null [Unused]0xE1: Null [Unused]0xE2: Null [Unused]0xE3: Null [Unused]0xE4: Null [Unused]0xE5: Null [Unused]0xE6: Null [Unused]0xE7: Null [Unused]0xE8: Null [Unused]0xE9: Null [Unused]0xEA: Null [Unused]0xEB: Null [Unused]0xEC: Null [Unused]0xED: Null [Unused]0xEE: Null [Unused]0xEF: Null [Unused]
Wind + Foreground + Slope + Water (0xF0)
0xF0: Null [Unused]0xF1: Null [Unused]0xF2: Null [Unused]0xF3: Null [Unused]0xF4: Null [Unused]0xF5: Null [Unused]0xF6: Null [Unused]0xF7: Null [Unused]0xF8: Null [Unused]0xF9: Null [Unused]0xFA: Null [Unused]0xFB: Null [Unused]0xFC: Null [Unused]0xFD: Null [Unused]0xFE: Null [Unused]0xFF: Null [Unused]