shithub: zelda3

ref: ee81f434cffb89d7cae93ff4d8344b71d7746766
dir: /dungeon.c/

View raw version
#include "zelda_rtl.h"
#include "snes/snes_regs.h"
#include "variables.h"
#include "dungeon.h"
#include "nmi.h"
#include "hud.h"
#include "load_gfx.h"
#include "overworld.h"
#include "sprite.h"
#include "ancilla.h"
#include "ending.h"
#include "player.h"
#include "misc.h"
#include "player_oam.h"
#include "tagalong.h"
#include "messaging.h"
#include "assets.h"

// todo: move to config
static const uint16 kBossRooms[] = {
  200, 51, 7,
  32,
  6, 90, 41, 144, 222, 164, 172,
  13
};
static const uint8 kDungeonExit_From[12] = {200, 51, 7, 32, 6, 90, 41, 144, 222, 164, 172, 13};
static const uint8 kDungeonExit_To[12] = {201, 99, 119, 32, 40, 74, 89, 152, 14, 214, 219, 13};
static const uint16 kObjectSubtype1Params[] = {
  0x3d8, 0x2e8, 0x2f8, 0x328, 0x338, 0x400, 0x410, 0x388, 0x390, 0x420, 0x42a, 0x434, 0x43e, 0x448, 0x452, 0x45c,
  0x466, 0x470, 0x47a, 0x484, 0x48e, 0x498, 0x4a2, 0x4ac, 0x4b6, 0x4c0, 0x4ca, 0x4d4, 0x4de, 0x4e8, 0x4f2, 0x4fc,
  0x506, 0x598, 0x600, 0x63c, 0x63c, 0x63c, 0x63c, 0x63c, 0x642, 0x64c, 0x652, 0x658, 0x65e, 0x664, 0x66a, 0x688,
  0x694, 0x6a8, 0x6a8, 0x6a8, 0x6c8, 0x0, 0x78a, 0x7aa, 0xe26, 0x84a, 0x86a, 0x882, 0x8ca, 0x85a, 0x8fa, 0x91a,
  0x920, 0x92a, 0x930, 0x936, 0x93c, 0x942, 0x948, 0x94e, 0x96c, 0x97e, 0x98e, 0x902, 0x99e, 0x9d8, 0x9d8, 0x9d8,
  0x9fa, 0x156c, 0x1590, 0x1d86, 0x0, 0xa14, 0xa24, 0xa54, 0xa54, 0xa84, 0xa84, 0x14dc, 0x1500, 0x61e, 0xe52, 0x600,
  0x3d8, 0x2c8, 0x2d8, 0x308, 0x318, 0x3e0, 0x3f0, 0x378, 0x380, 0x5fa, 0x648, 0x64a, 0x670, 0x67c, 0x6a8, 0x6a8,
  0x6a8, 0x6c8, 0x0, 0x7aa, 0x7ca, 0x84a, 0x89a, 0x8b2, 0x90a, 0x926, 0x928, 0x912, 0x9f8, 0x1d7e, 0x0, 0xa34,
  0xa44, 0xa54, 0xa6c, 0xa84, 0xa9c, 0x1524, 0x1548, 0x85a, 0x606, 0xe52, 0x5fa, 0x6a0, 0x6a2, 0xb12, 0xb14, 0x9b0,
  0xb46, 0xb56, 0x1f52, 0x1f5a, 0x288, 0xe82, 0x1df2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
  0x3d8, 0x3d8, 0x3d8, 0x3d8, 0x5aa, 0x5b2, 0x5b2, 0x5b2, 0x5b2, 0xe0, 0xe0, 0xe0, 0xe0, 0x110, 0x0, 0x0,
  0x6a4, 0x6a6, 0xae6, 0xb06, 0xb0c, 0xb16, 0xb26, 0xb36, 0x1f52, 0x1f5a, 0x288, 0xeba, 0xe82, 0x1df2, 0x0, 0x0,
  0x3d8, 0x510, 0x5aa, 0x5aa, 0x0, 0x168, 0xe0, 0x158, 0x100, 0x110, 0x178, 0x72a, 0x72a, 0x72a, 0x75a, 0x670,
  0x670, 0x130, 0x148, 0x72a, 0x72a, 0x72a, 0x75a, 0xe0, 0x110, 0xf0, 0x110, 0x0, 0xab4, 0x8da, 0xade, 0x188,
  0x1a0, 0x1b0, 0x1c0, 0x1d0, 0x1e0, 0x1f0, 0x200, 0x120, 0x2a8, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
};
static const uint16 kObjectSubtype2Params[] = {
  0xb66, 0xb86, 0xba6, 0xbc6, 0xc66, 0xc86, 0xca6, 0xcc6, 0xbe6, 0xc06, 0xc26, 0xc46, 0xce6, 0xd06, 0xd26, 0xd46,
  0xd66, 0xd7e, 0xd96, 0xdae, 0xdc6, 0xdde, 0xdf6, 0xe0e, 0x398, 0x3a0, 0x3a8, 0x3b0, 0xe32, 0xe26, 0xea2, 0xe9a,
  0xeca, 0xed2, 0xede, 0xede, 0xf1e, 0xf3e, 0xf5e, 0xf6a, 0xef6, 0xf72, 0xf92, 0xfa2, 0xfa2, 0x1088, 0x10a8, 0x10a8,
  0x10c8, 0x10c8, 0x10c8, 0x10c8, 0xe52, 0x1108, 0x1108, 0x12a8, 0x1148, 0x1160, 0x1178, 0x1190, 0x1458, 0x1488, 0x2062, 0x2086,
};
static const uint16 kObjectSubtype3Params[] = {
  0x1614, 0x162c, 0x1654, 0xa0e, 0xa0c, 0x9fc, 0x9fe, 0xa00, 0xa02, 0xa04, 0xa06, 0xa08, 0xa0a, 0x0, 0xa10, 0xa12,
  0x1dda, 0x1de2, 0x1dd6, 0x1dea, 0x15fc, 0x1dfa, 0x1df2, 0x1488, 0x1494, 0x149c, 0x14a4, 0x10e8, 0x10e8, 0x10e8, 0x11a8, 0x11c8,
  0x11e8, 0x1208, 0x3b8, 0x3c0, 0x3c8, 0x3d0, 0x1228, 0x1248, 0x1268, 0x1288, 0x0, 0xe5a, 0xe62, 0x0, 0x0, 0xe82,
  0xe8a, 0x14ac, 0x14c4, 0x10e8, 0x1614, 0x1614, 0x1614, 0x1614, 0x1614, 0x1614, 0x1cbe, 0x1cee, 0x1d1e, 0x1d4e, 0x1d8e, 0x1d96,
  0x1d9e, 0x1da6, 0x1dae, 0x1db6, 0x1dbe, 0x1dc6, 0x1dce, 0x220, 0x260, 0x280, 0x1f3a, 0x1f62, 0x1f92, 0x1ff2, 0x2016, 0x1f42,
  0xeaa, 0x1f4a, 0x1f52, 0x1f5a, 0x202e, 0x2062, 0x9b8, 0x9c0, 0x9c8, 0x9d0, 0xfa2, 0xfb2, 0xfc4, 0xff4, 0x1018, 0x1020,
  0x15b4, 0x15d8, 0x20f6, 0xeba, 0x22e6, 0x22ee, 0x5da, 0x281e, 0x2ae0, 0x2d2a, 0x2f2a, 0x22f6, 0x2316, 0x232e, 0x2346, 0x235e,
  0x2376, 0x23b6, 0x1e9a, 0x0, 0x2436, 0x149c, 0x24b6, 0x24e6, 0x2516, 0x1028, 0x1040, 0x1060, 0x1070, 0x1078, 0x1080, 0x0,
};
static const uint16 kDoorTypeSrcData[] = {
  0x2716, 0x272e, 0x272e, 0x2746, 0x2746, 0x2746, 0x2746, 0x2746, 0x2746, 0x275e, 0x275e, 0x275e, 0x275e, 0x2776, 0x278e, 0x27a6,
  0x27be, 0x27be, 0x27d6, 0x27d6, 0x27ee, 0x2806, 0x2806, 0x281e, 0x2836, 0x2836, 0x2836, 0x2836, 0x284e, 0x2866, 0x2866, 0x2866,
  0x2866, 0x287e, 0x2896, 0x28ae, 0x28c6, 0x28de, 0x28f6, 0x28f6, 0x28f6, 0x290e, 0x2926, 0x2958, 0x2978, 0x2990, 0x2990, 0x2990,
  0x2990, 0x29a8, 0x29c0, 0x29d8,
};
static const uint16 kDoorTypeSrcData2[] = {
  0x29f0, 0x2a08, 0x2a08, 0x2a20, 0x2a20, 0x2a20, 0x2a20, 0x2a20, 0x2a20, 0x2a38, 0x2a38, 0x2a38, 0x2a38, 0x2a50, 0x2a68, 0x2a80,
  0x2a98, 0x2a98, 0x2a98, 0x2a98, 0x2a98, 0x2ab0, 0x2ac8, 0x2ae0, 0x2af8, 0x2af8, 0x2af8, 0x2af8, 0x2b10, 0x2b28, 0x2b28, 0x2b28,
  0x2b28, 0x2b40, 0x2b58, 0x2b70, 0x2b88, 0x2ba0, 0x2bb8, 0x2bb8, 0x2bb8, 0x2bd0, 0x2be8, 0x2c1a, 0x2c3a, 0x2c52, 0x2c6a, 0x2c6a,
};
static const uint16 kDoorTypeSrcData3[] = {
  0x2c6a, 0x2c82, 0x2c82, 0x2c9a, 0x2c9a, 0x2c9a, 0x2c9a, 0x2c9a, 0x2c9a, 0x2cb2, 0x2cb2, 0x2cb2, 0x2cb2, 0x2cca, 0x2ce2, 0x2cfa,
  0x2cfa, 0x2cfa, 0x2cfa, 0x2cfa, 0x2cfa, 0x2d12, 0x2d12, 0x2d2a, 0x2d42, 0x2d42, 0x2d42, 0x2d42, 0x2d5a, 0x2d72, 0x2d72, 0x2d72,
  0x2d72, 0x2d8a, 0x2da2, 0x2dba, 0x2dd2, 0x2dea, 0x2e02, 0x2e02, 0x2e02, 0x2e1a, 0x2e32, 0x2e32, 0x2e52, 0x2e6a, 0x2e6a, 0x2e6a,
};
static const uint16 kDoorTypeSrcData4[] = {
  0x2e6a, 0x2e82, 0x2e82, 0x2e9a, 0x2e9a, 0x2e9a, 0x2e9a, 0x2e9a, 0x2e9a, 0x2eb2, 0x2eb2, 0x2eb2, 0x2eb2, 0x2eca, 0x2ee2, 0x2efa,
  0x2efa, 0x2efa, 0x2efa, 0x2efa, 0x2efa, 0x2f12, 0x2f12, 0x2f2a, 0x2f42, 0x2f42, 0x2f42, 0x2f42, 0x2f5a, 0x2f72, 0x2f72, 0x2f72,
  0x2f72, 0x2f8a, 0x2fa2, 0x2fba, 0x2fd2, 0x2fea, 0x3002, 0x3002, 0x3002, 0x301a, 0x3032, 0x3032, 0x3052, 0x306a, 0x306a,
};
static const uint16 kDoorPositionToTilemapOffs_Up[] = { 0x21c, 0x23c, 0x25c, 0x39c, 0x3bc, 0x3dc, 0x121c, 0x123c, 0x125c, 0x139c, 0x13bc, 0x13dc };
static const uint16 kDoorPositionToTilemapOffs_Down[] = { 0xd1c, 0xd3c, 0xd5c, 0xb9c, 0xbbc, 0xbdc, 0x1d1c, 0x1d3c, 0x1d5c, 0x1b9c, 0x1bbc, 0x1bdc };
static const uint16 kDoorPositionToTilemapOffs_Left[] = { 0x784, 0xf84, 0x1784, 0x78a, 0xf8a, 0x178a, 0x7c4, 0xfc4, 0x17c4, 0x7ca, 0xfca, 0x17ca };
static const uint16 kDoorPositionToTilemapOffs_Right[] = { 0x7b4, 0xfb4, 0x17b4, 0x7ae, 0xfae, 0x17ae, 0x7f4, 0xff4, 0x17f4, 0x7ee, 0xfee, 0x17ee };
static const int8 kSpiralTab1[] = { 0, 1, 1, -1, 1, 1, 1, 1 };
static const int8 kTeleportPitLevel1[] = { 0, 1, 1 };
static const int8 kTeleportPitLevel2[] = { 0, 0, 1 };
static const uint8 kDoorTypeRemap[] = {
  0, 2, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 80, 0, 80, 80,
  96, 98, 100, 102, 82, 90, 80, 82, 84, 86, 0, 80, 80, 0, 0, 0,
  64, 88, 88, 0, 88, 88, 0, 0,
};
static const int8 kStaircaseTab2[] = {
  12, 32, 48, 56, 72, -44, -40, -64, -64, -88, 12, 24, 40, 48, 64, -28,
  -40, -56, -64, -80,
};
static const int8 kStaircaseTab3[] = { 4, -4, 4, -4 };
static const int8 kStaircaseTab4[] = { 52, 52, 59, 58 };
static const int8 kStaircaseTab5[] = { 32, -64, 32, -32 };
static const uint8 kMovingWall_Sizes0[4] = { 5, 7, 11, 15 };
static const uint8 kMovingWall_Sizes1[4] = { 8, 16, 24, 32 };
static const uint8 kWatergateLayout[17] = {
  0x1b, 0xa1, 0xc9,
  0x51, 0xa1, 0xc9,
  0x92, 0xa1, 0xc9,
  0xa1, 0x33, 0xc9,
  0xa1, 0x72, 0xc9,
  0xff, 0xff,
};
static const uint16 kChestOpenMasks[] = { 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000 };
const uint8 kLayoutQuadrantFlags[] = { 0xF, 0xF, 0xF, 0xF, 0xB, 0xB, 7, 7, 0xF, 0xB, 0xF, 7, 0xB, 0xF, 7, 0xF, 0xE, 0xD, 0xE, 0xD, 0xF, 0xF, 0xE, 0xD, 0xE, 0xD, 0xF, 0xF, 0xA, 9, 6, 5 };
static const uint8 kQuadrantVisitingFlags[] = { 8, 4, 2, 1, 0xC, 0xC, 3, 3, 0xA, 5, 0xA, 5, 0xF, 0xF, 0xF, 0xF };
#define XY(x, y) ((y)*64+(x))
static const uint8 kDungeon_MinigameChestPrizes1[8] = {
  0x40, 0x41, 0x34, 0x42, 0x43, 0x44, 0x27, 0x17
};
static const uint8 kDungeon_RupeeChestMinigamePrizes[32] = {
  0x47, 0x34, 0x46, 0x34, 0x46, 0x46, 0x34, 0x47, 0x46, 0x47, 0x34, 0x46, 0x47, 0x34, 0x46, 0x47,
  0x34, 0x47, 0x41, 0x47, 0x41, 0x41, 0x47, 0x34, 0x41, 0x34, 0x47, 0x41, 0x34, 0x47, 0x41, 0x34,
};
static const int8 kDungeon_QueryIfTileLiftable_x[4] = { 7, 7, -3, 16 };
static const int8 kDungeon_QueryIfTileLiftable_y[4] = { 3, 24, 14, 14 };
static const uint16 kDungeon_QueryIfTileLiftable_rv[16] = { 0x5252, 0x5050, 0x5454, 0x0, 0x2323 };
static const uint16 kDoor_BlastWallUp_Dsts[] = { 0xd8a, 0xdaa, 0xdca, 0x2b6, 0xab6, 0x12b6 };
#define adjacent_doors_flags (*(uint16*)(g_ram+0x1100))
#define adjacent_doors ((uint16*)(g_ram+0x1110))
static const DungPalInfo kDungPalinfos[41] = {
  { 0,  0,  3,  1},
  { 2,  0,  3,  1},
  { 4,  0, 10,  1},
  { 6,  0,  1,  7},
  {10,  2,  2,  7},
  { 4,  4,  3, 10},
  {12,  5,  8, 20},
  {14,  0,  3, 10},
  { 2,  0, 15, 20},
  {10,  2,  0,  7},
  { 2,  0, 15, 12},
  { 6,  0,  6,  7},
  { 0,  0, 14, 18},
  {18,  5,  5, 11},
  {18,  0,  2, 12},
  {16,  5, 10,  7},
  {16,  0, 16, 12},
  {22,  7,  2,  7},
  {22,  0,  7, 15},
  { 8,  0,  4, 12},
  { 8,  0,  4,  9},
  { 4,  0,  3,  1},
  {20,  0,  4,  4},
  {20,  0, 20, 12},
  {24,  5,  7, 11},
  {24,  6, 16, 12},
  {26,  5,  8, 20},
  {26,  2,  0,  7},
  { 6,  0,  3, 10},
  {28,  0,  3,  1},
  {30,  0, 11, 17},
  { 4,  0, 11, 17},
  {14,  0,  0,  2},
  {32,  8, 19, 13},
  {10,  0,  3, 10},
  {20,  0,  4,  4},
  {26,  2,  2,  7},
  {26, 10,  0,  0},
  { 0,  0,  3,  2},
  {14,  0,  3,  7},
  {26,  5,  5, 11},
};
// these are not used by the code, but needed for the comparison with the real rom to work.
static const uint8 kDungeon_DrawObjectOffsets_BG1[33] = {
     0, 0x20, 0x7e,    2, 0x20, 0x7e,    4, 0x20, 0x7e,    6, 0x20, 0x7e, 0x80, 0x20, 0x7e, 0x82,
  0x20, 0x7e, 0x84, 0x20, 0x7e, 0x86, 0x20, 0x7e,    0, 0x21, 0x7e, 0x80, 0x21, 0x7e,    0, 0x22,
  0x7e,
};
static const uint8 kDungeon_DrawObjectOffsets_BG2[33] = {
     0, 0x40, 0x7e,    2, 0x40, 0x7e,    4, 0x40, 0x7e,    6, 0x40, 0x7e, 0x80, 0x40, 0x7e, 0x82,
  0x40, 0x7e, 0x84, 0x40, 0x7e, 0x86, 0x40, 0x7e,    0, 0x41, 0x7e, 0x80, 0x41, 0x7e,    0, 0x42,
  0x7e,
};
static const uint16 kUploadBgSrcs[] = { 0x0, 0x1000, 0x0, 0x40, 0x40, 0x1040, 0x1000, 0x1040, 0x1000, 0x0, 0x40, 0x0, 0x1040, 0x40, 0x1040, 0x1000 };
static const uint8 kUploadBgDsts[] = { 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16 };
static const uint16 kTileAttrsByDoor[] = {
  0x8080, 0x8484, 0x0, 0x101, 0x8484, 0x8e8e, 0x0, 0x0, 0x8888, 0x8e8e, 0x8080, 0x8080, 0x8282, 0x8080, 0x8080, 0x8080,
  0x8080, 0x8080, 0x8080, 0x8080, 0x8282, 0x8e8e, 0x8080, 0x8282, 0x8080, 0x8080, 0x8080, 0x8282, 0x8282, 0x8080, 0x8080, 0x8080,
  0x8484, 0x8484, 0x8686, 0x8888, 0x8686, 0x8686, 0x8080, 0x8080,
};
static PlayerHandlerFunc *const kDungeon_Effect_Handler[28] = {
  &LayerEffect_Nothing,
  &LayerEffect_Nothing,
  &LayerEffect_Scroll,
  &LayerEffect_WaterRapids,
  &LayerEffect_Trinexx,
  &LayerEffect_Agahnim2,
  &LayerEffect_InvisibleFloor,
  &LayerEffect_Ganon,
};
static const int16 kPushBlockMoveDistances[] = { -0x100, 0x100, -0x4, 0x4 };
static HandlerFuncK *const kDungTagroutines[] = {
  &Dung_TagRoutine_0x00,
  &RoomTag_NorthWestTrigger,
  &Dung_TagRoutine_0x2A,
  &Dung_TagRoutine_0x2B,
  &Dung_TagRoutine_0x2C,
  &Dung_TagRoutine_0x2D,
  &Dung_TagRoutine_0x2E,
  &Dung_TagRoutine_0x2F,
  &Dung_TagRoutine_0x30,
  &RoomTag_QuadrantTrigger,
  &RoomTag_RoomTrigger,
  &RoomTag_NorthWestTrigger,
  &Dung_TagRoutine_0x2A,
  &Dung_TagRoutine_0x2B,
  &Dung_TagRoutine_0x2C,
  &Dung_TagRoutine_0x2D,
  &Dung_TagRoutine_0x2E,
  &Dung_TagRoutine_0x2F,
  &Dung_TagRoutine_0x30,
  &RoomTag_QuadrantTrigger,
  &RoomTag_RoomTrigger_BlockDoor,
  &RoomTag_PrizeTriggerDoorDoor,
  &RoomTag_SwitchTrigger_HoldDoor,
  &RoomTag_SwitchTrigger_ToggleDoor,
  &RoomTag_WaterOff,
  &RoomTag_WaterOn,
  &RoomTag_WaterGate,
  &Dung_TagRoutine_0x1B,
  &RoomTag_MovingWall_East,
  &RoomTag_MovingWall_West,
  &RoomTag_MovingWallTorchesCheck,
  &RoomTag_MovingWallTorchesCheck,
  &RoomTag_Switch_ExplodingWall,
  &RoomTag_Holes0,
  &RoomTag_ChestHoles0,
  &Dung_TagRoutine_0x23,
  &RoomTag_Holes2,
  &RoomTag_GetHeartForPrize,
  &RoomTag_KillRoomBlock,
  &RoomTag_TriggerChest,
  &RoomTag_PullSwitchExplodingWall,
  &RoomTag_NorthWestTrigger,
  &Dung_TagRoutine_0x2A,
  &Dung_TagRoutine_0x2B,
  &Dung_TagRoutine_0x2C,
  &Dung_TagRoutine_0x2D,
  &Dung_TagRoutine_0x2E,
  &Dung_TagRoutine_0x2F,
  &Dung_TagRoutine_0x30,
  &RoomTag_QuadrantTrigger,
  &RoomTag_RoomTrigger,
  &RoomTag_TorchPuzzleDoor,
  &Dung_TagRoutine_0x34,
  &Dung_TagRoutine_0x35,
  &Dung_TagRoutine_0x36,
  &Dung_TagRoutine_0x37,
  &RoomTag_Agahnim,
  &Dung_TagRoutine_0x39,
  &Dung_TagRoutine_0x3A,
  &Dung_TagRoutine_0x3B,
  &RoomTag_PushBlockForChest,
  &RoomTag_GanonDoor,
  &RoomTag_TorchPuzzleChest,
  &RoomTag_RekillableBoss,
};
static const uint16 kDoorAnimUpSrc[] = { 0x306a, 0x306a, 0x3082, 0x309a, 0x30b2 };
static const uint16 kDoorAnimDownSrc[] = { 0x30b2, 0x30ca, 0x30e2, 0x30fa, 0x3112 };
static const uint16 kDoorAnimLeftSrc[] = { 0x3112, 0x312a, 0x3142, 0x315a, 0x3172 };
static const uint16 kDoorAnimRightSrc[] = { 0x3172, 0x318a, 0x31a2, 0x31ba, 0x31D2 };
static PlayerHandlerFunc *const kDungeon_IntraRoomTrans[8] = {
  &DungeonTransition_Subtile_PrepTransition,
  &DungeonTransition_Subtile_ApplyFilter,
  &DungeonTransition_Subtile_ResetShutters,
  &DungeonTransition_ScrollRoom,
  &DungeonTransition_FindSubtileLanding,
  &Dungeon_IntraRoomTrans_State5,
  &DungeonTransition_Subtile_ApplyFilter,
  &DungeonTransition_Subtile_TriggerShutters,
};
static PlayerHandlerFunc *const kDungeon_InterRoomTrans[16] = {
  &Module07_02_00_InitializeTransition,
  &Module07_02_01_LoadNextRoom,
  &Module07_02_FadedFilter,
  &Dungeon_InterRoomTrans_State3,
  &Dungeon_InterRoomTrans_State4,
  &Dungeon_InterRoomTrans_notDarkRoom,
  &Dungeon_InterRoomTrans_State4,
  &Dungeon_InterRoomTrans_State7,
  &DungeonTransition_ScrollRoom,
  &Dungeon_InterRoomTrans_State9,
  &Dungeon_InterRoomTrans_State10,
  &Dungeon_InterRoomTrans_State9,
  &Dungeon_InterRoomTrans_State12,
  &Dungeon_InterRoomTrans_State13,
  &Module07_02_FadedFilter,
  &Dungeon_InterRoomTrans_State15,
};
static PlayerHandlerFunc *const kDungeon_Submodule_7_DownFloorTrans[18] = {
  &Module07_07_00_HandleMusicAndResetRoom,
  &ApplyPaletteFilter_bounce,
  &Dungeon_InitializeRoomFromSpecial,
  &DungeonTransition_TriggerBGC34UpdateAndAdvance,
  &DungeonTransition_TriggerBGC56UpdateAndAdvance,
  &DungeonTransition_LoadSpriteGFX,
  &Module07_07_06_SyncBG1and2,
  &Dungeon_InterRoomTrans_State4,
  &Dungeon_InterRoomTrans_notDarkRoom,
  &Dungeon_InterRoomTrans_State4,
  &Dungeon_InterRoomTrans_notDarkRoom,
  &Dungeon_InterRoomTrans_State4,
  &Dungeon_InterRoomTrans_notDarkRoom,
  &Dungeon_InterRoomTrans_State4,
  &Dungeon_Staircase14,
  &Module07_07_0F_FallingFadeIn,
  &Module07_07_10_LandLinkFromFalling,
  &Module07_07_11_CacheRoomAndSetMusic,
};
static PlayerHandlerFunc *const kWatergateFuncs[6] = {
  &FloodDam_PrepTiles_init,
  &Watergate_Main_State1,
  &Watergate_Main_State1,
  &Watergate_Main_State1,
  &FloodDam_Expand,
  &FloodDam_Fill,
};
static const int8 kSpiralStaircaseX[] = { -28, -28, 24, 24 };
static const int8 kSpiralStaircaseY[] = { 16, -10, -10, -32 };
static PlayerHandlerFunc *const kDungeon_SpiralStaircase[20] = {
  &Module07_0E_00_InitPriorityAndScreens,
  &Module07_0E_01_HandleMusicAndResetProps,
  &Module07_0E_02_ApplyFilterIf,
  &Dungeon_InitializeRoomFromSpecial,
  &DungeonTransition_TriggerBGC34UpdateAndAdvance,
  &DungeonTransition_TriggerBGC56UpdateAndAdvance,
  &DungeonTransition_LoadSpriteGFX,
  &Dungeon_SyncBackgroundsFromSpiralStairs,
  &Dungeon_InterRoomTrans_State4,
  &Dungeon_InterRoomTrans_notDarkRoom,
  &Dungeon_InterRoomTrans_State4,
  &Dungeon_SpiralStaircase11,
  &Dungeon_SpiralStaircase12,
  &Dungeon_SpiralStaircase11,
  &Dungeon_SpiralStaircase12,
  &Dungeon_DoubleApplyAndIncrementGrayscale,
  &Dungeon_AdvanceThenSetBossMusicUnorthodox,
  &Dungeon_SpiralStaircase17,
  &Dungeon_SpiralStaircase18,
  &Module07_0E_13_SetRoomAndLayerAndCache,
};
static PlayerHandlerFunc *const kDungeon_Submodule_F[2] = {
  &Module07_0F_00_InitSpotlight,
  &Module07_0F_01_OperateSpotlight,
};
static PlayerHandlerFunc *const kDungeon_StraightStaircase[2] = {
  &Module07_10_00_InitStairs,
  &Module07_10_01_ClimbStairs,
};
static PlayerHandlerFunc *const kDungeon_StraightStaircaseDown[2] = {
  &Module07_08_00_InitStairs,
  &Module07_08_01_ClimbStairs,
};
static PlayerHandlerFunc *const kDungeon_StraightStairs[19] = {
  &Module07_11_00_PrepAndReset,
  &Module07_11_01_FadeOut,
  &Module07_11_02_LoadAndPrepRoom,
  &Module07_11_03_FilterAndLoadBGChars,
  &Module07_11_04_FilterDoBGAndResetSprites,
  &Dungeon_SpiralStaircase11,
  &Dungeon_SpiralStaircase12,
  &Dungeon_SpiralStaircase11,
  &Dungeon_SpiralStaircase12,
  &Module07_11_09_LoadSpriteGraphics,
  &Module07_11_0A_ScrollCamera,
  &Module07_11_0B_PrepDestination,
  &Dungeon_InterRoomTrans_State4,
  &Dungeon_InterRoomTrans_notDarkRoom,
  &Dungeon_InterRoomTrans_State4,
  &Dungeon_DoubleApplyAndIncrementGrayscale,
  &Module07_11_19_SetSongAndFilter,
  &Module07_11_11_KeepSliding,
  &ResetThenCacheRoomEntryProperties,
};
static PlayerHandlerFunc *const kDungeon_Teleport[15] = {
  &ResetTransitionPropsAndAdvance_ResetInterface,
  &Module07_15_01_ApplyMosaicAndFilter,
  &Dungeon_InitializeRoomFromSpecial,
  &DungeonTransition_LoadSpriteGFX,
  &Module07_15_04_SyncRoomPropsAndBuildOverlay,
  &Dungeon_InterRoomTrans_State4,
  &Dungeon_InterRoomTrans_notDarkRoom,
  &Dungeon_InterRoomTrans_State4,
  &Dungeon_InterRoomTrans_notDarkRoom,
  &Dungeon_InterRoomTrans_State4,
  &Dungeon_InterRoomTrans_notDarkRoom,
  &Dungeon_InterRoomTrans_State4,
  &Dungeon_Staircase14,
  &Module07_15_0E_FadeInFromWarp,
  &Module07_15_0F_FinalizeAndCacheEntry,
};
static PlayerHandlerFunc *const kDungeonSubmodules[31] = {
  &Module07_00_PlayerControl,
  &Module07_01_SubtileTransition,
  &Module07_02_SupertileTransition,
  &Module07_03_OverlayChange,
  &Module07_04_UnlockDoor,
  &Module07_05_ControlShutters,
  &Module07_06_FatInterRoomStairs,
  &Module07_07_FallingTransition,
  &Module07_08_NorthIntraRoomStairs,
  &Module07_09_OpenCrackedDoor,
  &Module07_0A_ChangeBrightness,
  &Module07_0B_DrainSwampPool,
  &Module07_0C_FloodSwampWater,
  &Module07_0D_FloodDam,
  &Module07_0E_SpiralStairs,
  &Module07_0F_LandingWipe,
  &Module07_10_SouthIntraRoomStairs,
  &Module07_11_StraightInterroomStairs,
  &Module07_11_StraightInterroomStairs,
  &Module07_11_StraightInterroomStairs,
  &Module07_14_RecoverFromFall,
  &Module07_15_WarpPad,
  &Module07_16_UpdatePegs,
  &Module07_17_PressurePlate,
  &Module07_18_RescuedMaiden,
  &Module07_19_MirrorFade,
  &Module07_1A_RoomDraw_OpenTriforceDoor_bounce,
};
const uint8 kDungAnimatedTiles[24] = {
  0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5f, 0x5d, 0x5f, 0x5f, 0x5e, 0x5f, 0x5e, 0x5e, 0x5d,
  0x5d, 0x5e, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d, 0x5d,
};
uint16 *DstoPtr(uint16 d) {
  return (uint16 *)&(g_ram[dung_line_ptrs_row0 + d * 2]);
}

void Object_Fill_Nx1(int n, const uint16 *src, uint16 *dst) {
  int t = src[0];
  do *dst++ = t; while (--n);
}

void Object_Draw_5x4(const uint16 *src, uint16 *dst) {
  int n = 5;
  do {
    dst[XY(0, 0)] = src[0];
    dst[XY(1, 0)] = src[1];
    dst[XY(2, 0)] = src[2];
    dst[XY(3, 0)] = src[3];
    dst += XY(0, 1), src += 4;
  } while (--n);
}

void Object_Draw_4x2_BothBgs(const uint16 *src, uint16 dsto) {
  dung_bg1[dsto + XY(0, 0)] = dung_bg2[dsto + XY(0, 0)] = src[0];
  dung_bg1[dsto + XY(1, 0)] = dung_bg2[dsto + XY(1, 0)] = src[1];
  dung_bg1[dsto + XY(2, 0)] = dung_bg2[dsto + XY(2, 0)] = src[2];
  dung_bg1[dsto + XY(3, 0)] = dung_bg2[dsto + XY(3, 0)] = src[3];
  dung_bg1[dsto + XY(0, 1)] = dung_bg2[dsto + XY(0, 1)] = src[4];
  dung_bg1[dsto + XY(1, 1)] = dung_bg2[dsto + XY(1, 1)] = src[5];
  dung_bg1[dsto + XY(2, 1)] = dung_bg2[dsto + XY(2, 1)] = src[6];
  dung_bg1[dsto + XY(3, 1)] = dung_bg2[dsto + XY(3, 1)] = src[7];
}

void Object_ChestPlatform_Helper(const uint16 *src, int dsto) {
  dung_bg2[dsto] = src[0];
  int n = src[3];
  for (int i = dung_draw_width_indicator; i--; dsto++)
    dung_bg2[1 + dsto] = n;

  dung_bg2[1 + dsto] = src[6];
  dung_bg2[2 + dsto] = dung_bg2[3 + dsto] = dung_bg2[4 + dsto] = dung_bg2[5 + dsto] = src[9];

  dung_bg2[6 + dsto] = src[12];
  n = src[15];
  for (int i = dung_draw_width_indicator; i--; dsto++)
    dung_bg2[7 + dsto] = n;

  dung_bg2[7 + dsto] = src[18];
}

void Object_Hole(const uint16 *src, uint16 *dst) {
  Object_SizeAtoAplus15(4);
  int w = dung_draw_width_indicator;
  for (int i = 0; i < w; i++)
    Object_Fill_Nx1(w, src, dst + XY(0, i));
  // fill top/bottom
  src = SrcPtr(0x63c);
  dst[XY(0, 0)] = src[0];
  Object_Fill_Nx1(w - 2, src + 1, dst + XY(1, 0));
  dst[XY(w - 1, 0)] = src[2];

  dst[XY(0, w - 1)] = src[3];
  Object_Fill_Nx1(w - 2, src + 4, dst + XY(1, w - 1));
  dst[XY(w - 1, w - 1)] = src[5];

  // fill left/right edge
  src = SrcPtr(0x648);
  for (int i = 1; i < w - 1; i++) {
    dst[XY(0, i)] = src[0];
    dst[XY(w - 1, i)] = src[1];
  }
}

// dsto is half the value of Y
void LoadType1ObjectSubtype1(uint8 idx, uint16 *dst, uint16 dsto) {
  uint16 param1 = kObjectSubtype1Params[idx];
  const uint16 *src = SrcPtr(param1);
  int n;

  switch (idx) {
  case 0x0:  // RoomDraw_Rightwards2x2_1to15or32 - Ceiling
  case 0xb8: case 0xb9: // B8 -  Blue Switch Block [L-R]
    RoomDraw_GetObjectSize_1to15or32();
    do {
      RoomDraw_Rightwards2x2(src, dst), dst += XY(2, 0);
    } while (--dung_draw_width_indicator);
    break;
  case 0x1: case 0x2:  // RoomDraw_Rightwards2x4_1to15or26 - [N]Wall Horz: [L-R]
  case 0xb6: case 0xb7:  // B6 -  [N]Wall Decor: 1/2 [L-R]
    RoomDraw_GetObjectSize_1to15or26();
    do {
      RoomDraw_Object_Nx4(2, src, dst), dst += XY(2, 0);
    } while (--dung_draw_width_indicator);
    break;

  case 0x3: case 0x4:  // RoomDraw_Rightwards2x4spaced4_1to16 - 03 -  [N]Wall Horz: (LOW) [L-R]
    RoomDraw_GetObjectSize_1to16();
    do {
      dung_bg1[dsto + XY(0, 0)] = dung_bg2[dsto + XY(0, 0)] = src[0];
      dung_bg1[dsto + XY(0, 1)] = dung_bg2[dsto + XY(0, 1)] = src[1];
      dung_bg1[dsto + XY(0, 2)] = dung_bg2[dsto + XY(0, 2)] = src[2];
      dung_bg1[dsto + XY(0, 3)] = dung_bg2[dsto + XY(0, 3)] = src[3];
      dung_bg1[dsto + XY(1, 0)] = dung_bg2[dsto + XY(1, 0)] = src[4];
      dung_bg1[dsto + XY(1, 1)] = dung_bg2[dsto + XY(1, 1)] = src[5];
      dung_bg1[dsto + XY(1, 2)] = dung_bg2[dsto + XY(1, 2)] = src[6];
      dung_bg1[dsto + XY(1, 3)] = dung_bg2[dsto + XY(1, 3)] = src[7];
      dsto += XY(2, 0);
    } while (--dung_draw_width_indicator);
    break;

  case 0x5: case 0x6: // RoomDraw_Rightwards2x4spaced4_1to16_BothBG - 05 -  [N]Wall Column [L-R]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_Object_Nx4(2, src, dst), dst += XY(6, 0);
    } while (--dung_draw_width_indicator);
    break;

  case 0x7: case 0x8: case 0x53: // RoomDraw_Rightwards2x2_1to16 - 07 -  [N]Wall Pit [L-R]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_Rightwards2x2(src, dst), dst += XY(2, 0);
    } while (--dung_draw_width_indicator);
    break;

  case 0x9: case 0x0c: case 0x0d: case 0x10: case 0x11: case 0x14:  // 09 -  / Wall Wood Bot (HIGH) [NW]
    Object_SizeAtoAplus15(6);
    do {
      RoomDraw_DrawObject2x2and1(src, dst), dst += XY(1, -1);
    } while (--dung_draw_width_indicator);
    break;

  case 0x0a: case 0x0b: case 0x0e: case 0x0f: case 0x12: case 0x13:  // 12 -  \ Wall Tile2 Bot (HIGH) [SW]
    Object_SizeAtoAplus15(6);
    do {
      RoomDraw_DrawObject2x2and1(src, dst), dst += XY(1, 1);
    } while (--dung_draw_width_indicator);
    break;

  case 0x15: case 0x18: case 0x19: case 0x1C: case 0x1D: case 0x20:  // 15 -  / Wall Tile Top (LOW)[NW]
    Object_SizeAtoAplus15(6);
    do {
      dung_bg1[dsto + XY(0, 0)] = dung_bg2[dsto + XY(0, 0)] = src[0];
      dung_bg1[dsto + XY(0, 1)] = dung_bg2[dsto + XY(0, 1)] = src[1];
      dung_bg1[dsto + XY(0, 2)] = dung_bg2[dsto + XY(0, 2)] = src[2];
      dung_bg1[dsto + XY(0, 3)] = dung_bg2[dsto + XY(0, 3)] = src[3];
      dung_bg1[dsto + XY(0, 4)] = dung_bg2[dsto + XY(0, 4)] = src[4];
      dsto -= 63;
    } while (--dung_draw_width_indicator);
    break;

  case 0x16: case 0x17: case 0x1A: case 0x1B: case 0x1E: case 0x1F:  // 16 -  \ Wall Tile Top (LOW)[SW]
    Object_SizeAtoAplus15(6);
    do {
      dung_bg1[dsto + XY(0, 0)] = dung_bg2[dsto + XY(0, 0)] = src[0];
      dung_bg1[dsto + XY(0, 1)] = dung_bg2[dsto + XY(0, 1)] = src[1];
      dung_bg1[dsto + XY(0, 2)] = dung_bg2[dsto + XY(0, 2)] = src[2];
      dung_bg1[dsto + XY(0, 3)] = dung_bg2[dsto + XY(0, 3)] = src[3];
      dung_bg1[dsto + XY(0, 4)] = dung_bg2[dsto + XY(0, 4)] = src[4];
      dsto += 65;
    } while (--dung_draw_width_indicator);
    break;

  case 0x21:  // 21 -  Mini Stairs [L-R]
    dung_draw_width_indicator = (dung_draw_width_indicator << 2 | dung_draw_height_indicator) * 2 + 1;
    RoomDraw_1x3_rightwards(2, src, dst), dst += XY(2, 0);
    do {
      RoomDraw_1x3_rightwards(1, src + 3, dst), dst += XY(1, 0);
    } while (--dung_draw_width_indicator);
    RoomDraw_1x3_rightwards(1, src + 6, dst);
    break;

  case 0x22: { // 22 -  Horz: Rail Thin [L-R]
    Object_SizeAtoAplus15(2);
    if ((dst[0] & 0x3ff) != 0xe2)
      dst[0] = src[0];
    n = src[1];
    do *++dst = n; while (--dung_draw_width_indicator);
    dst[1] = src[2];
    break;
  }
  case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28:
  case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e:  // 23 -  Pit [N]Edge [L-R]
  case 0x3f: case 0x40: case 0x41: case 0x42: case 0x43: case 0x44:  // 3F -  Water Edge [L-R]
  case 0x45: case 0x46: case 0xb3: case 0xb4:
    RoomDraw_GetObjectSize_1to16();
    n = dst[0] & 0x3ff;
    if (n != 0x1db && n != 0x1a6 && n != 0x1dd && n != 0x1fc)
      dst[0] = src[0];
    n = src[1];
    do *++dst = n; while (--dung_draw_width_indicator);
    dst[1] = src[2];
    break;

  case 0x2f: // 2F -  Rail Wall [L-R]
    Object_SizeAtoAplus15(10);
    n = *src++;
    if ((dst[0] & 0x3ff) != 0xe2) {
      dst[XY(0, 0)] = src[0];
      dst[XY(1, 0)] = src[1];
      dst[XY(1, 1)] = dst[XY(0, 1)] = n;
      dst += 2;
    }
    src += 2;
    do {
      dst[XY(0, 0)] = src[0];
      dst[XY(0, 1)] = n;
    } while (dst++, --dung_draw_width_indicator);
    src++;
    dst[XY(0, 0)] = src[0];
    dst[XY(1, 0)] = src[1];
    dst[XY(1, 1)] = dst[XY(0, 1)] = n;
    break;

  case 0x30:  // 30 -  Rail Wall [L-R]
    Object_SizeAtoAplus15(10);
    n = *src++;
    if ((dst[XY(0, 1)] & 0x3ff) != 0xe2) {
      dst[XY(0, 0)] = dst[XY(1, 0)] = n;
      dst[XY(0, 1)] = src[0];
      dst[XY(1, 1)] = src[1];
      dst += 2;
    }
    src += 2;
    do {
      dst[XY(0, 0)] = n;
      dst[XY(0, 1)] = src[0];
    } while (dst++, --dung_draw_width_indicator);
    src++;
    dst[XY(0, 0)] = dst[XY(1, 0)] = n;
    dst[XY(0, 1)] = src[0];
    dst[XY(1, 1)] = src[1];
    break;
  case 0x31: case 0x32:  // 31 -  Unused -empty
  case 0x35: case 0x54: case 0x57:case 0x58:case 0x59:case 0x5A:
    break;
  case 0x33:  // 33 -  Red Carpet Floor [L-R]
  case 0xb2: case 0xba:  // B2 -  Floor? [L-R]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_4x4(src, dst), dst += XY(4, 0);
    } while (--dung_draw_width_indicator);
    break;
  case 0x34:  // 34 -  Red Carpet Floor Trim [L-R]
    Object_SizeAtoAplus15(4);
    n = src[0];
    do *dst++ = n; while (--dung_draw_width_indicator);
    break;
  case 0x36: case 0x37: // 36 -  [N]Curtain [L-R]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_4x4(src, dst), dst += XY(6, 0);
    } while (--dung_draw_width_indicator);
    break;
  case 0x38:  // 38 -  Statue [L-R]
    src = (uint16 *)((uint8 *)src - param1 + 0xe26);
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_1x3_rightwards(2, src, dst), dst += XY(4, 0);
    } while (--dung_draw_width_indicator);
    break;
  case 0x39: case 0x3d: // 39 -  Column [L-R]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_Object_Nx4(2, src, dst), dst += XY(6, 0);
    } while (--dung_draw_width_indicator);
    break;
  case 0x3a: case 0x3b: // 3A -  [N]Wall Decor: [L-R]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_1x3_rightwards(4, src, dst), dst += XY(8, 0);
    } while (--dung_draw_width_indicator);
    break;
  case 0x3c:  // 3C -  Double Chair [L-R]
    RoomDraw_GetObjectSize_1to16();
    do {
      const uint16 *src = SrcPtr(0x8ca);
      RoomDraw_Rightwards2x2(src + 0, dst);
      RoomDraw_Rightwards2x2(src + 4, dst + XY(0, 6));
      dst += 4;
    } while (--dung_draw_width_indicator);
    break;
  case 0x3e: case 0x4b: // 3E -  [N]Wall Column [L-R]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_Rightwards2x2(src, dst), dst += XY(14, 0);
    } while (--dung_draw_width_indicator);
    break;
  case 0x47:  // 47 -  Unused Waterfall [L-R]
    RoomDraw_GetObjectSize_1to16();
    dung_draw_width_indicator <<= 1;
    dst = RoomDraw_DrawObject2x2and1(src, dst) + 1;
    do {
      RoomDraw_DrawObject2x2and1(src + 5, dst);
    } while (dst++, --dung_draw_width_indicator);
    RoomDraw_DrawObject2x2and1(src + 10, dst);
    break;
  case 0x48:
    RoomDraw_GetObjectSize_1to16();
    dung_draw_width_indicator <<= 1;
    RoomDraw_1x3_rightwards(1, src, dst), dst += XY(1, 0);
    do {
      dst[XY(0, 0)] = src[3];
      dst[XY(0, 1)] = src[4];
      dst[XY(0, 2)] = src[5];
    } while (dst++, --dung_draw_width_indicator);
    RoomDraw_1x3_rightwards(1, src + 6, dst);
    break;
  case 0x49: case 0x4A:  // RoomDraw_RightwardsFloorTile4x2_1to16      ; 49 -  N/A
    RoomDraw_GetObjectSize_1to16();
    RoomDraw_Downwards4x2VariableSpacing(4, src, dst);
    break;
  case 0x4c:  // 4C -  Bar [L-R]
    RoomDraw_GetObjectSize_1to16();
    dung_draw_width_indicator <<= 1;
    dst = RoomDraw_RightwardBarSegment(src, dst) + 1;
    do {
      dst = RoomDraw_RightwardBarSegment(src + 3, dst) + 1;
    } while (--dung_draw_width_indicator);
    dst = RoomDraw_RightwardBarSegment(src + 6, dst) + 1;
    break;

  case 0x4d: case 0x4e: case 0x4f:  // 4C -  Bar [L-R]
    RoomDraw_GetObjectSize_1to16();
    RoomDraw_Object_Nx4(1, src, dst), dst += XY(1, 0);
    do {
      RoomDraw_Object_Nx4(2, src + 4, dst), dst += XY(2, 0);
    } while (--dung_draw_width_indicator);
    RoomDraw_RightwardShelfEnd(src + 12, dst);
    break;

  case 0x50:   // 50 -  Cane Ride [L-R]
    Object_SizeAtoAplus15(2);
    n = src[0];
    do *dst++ = n; while (--dung_draw_width_indicator);
    break;

  case 0x51: case 0x52: // 51 -  [N]Canon Hole [L-R]
  case 0x5B: case 0x5C:
    RoomDraw_GetObjectSize_1to16();
    RoomDraw_1x3_rightwards(2, src, dst), dst += XY(2, 0);
    while (--dung_draw_width_indicator)
      RoomDraw_1x3_rightwards(2, src + 6, dst), dst += XY(2, 0);
    RoomDraw_1x3_rightwards(2, src + 12, dst);
    break;

  case 0x55: case 0x56:  // 55 -  [N]Wall Torches [L-R]
    RoomDraw_GetObjectSize_1to16();
    RoomDraw_Downwards4x2VariableSpacing(12, src, dst);
    break;

  case 0x5D:  // 5D -  Large Horz: Rail [L-R]
    RoomDraw_GetObjectSize_1to16();
    dung_draw_width_indicator++;
    RoomDraw_1x3_rightwards(2, src, dst), dst += XY(2, 0);
    do {
      RoomDraw_RightwardBarSegment(src + 6, dst), dst += XY(1, 0);
    } while (--dung_draw_width_indicator);
    RoomDraw_1x3_rightwards(2, src + 9, dst);
    break;

  case 0x5E:  // 5E -  Block [L-R]
  case 0xbb:  // BB -  N/A
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_Rightwards2x2(src, dst), dst += XY(4, 0);
    } while (--dung_draw_width_indicator);
    break;

  case 0x5f: { // 5F -  Long Horz: Rail [L-R]
    Object_SizeAtoAplus15(21);
    if ((dst[0] & 0x3ff) != 0xe2)
      dst[0] = src[0];
    n = src[1];
    do *++dst = n; while (--dung_draw_width_indicator);
    dst[1] = src[2];
    break;
  }

  case 0x60:  // 60 -  Ceiling [U-D]
  case 0x92: case 0x93: // 92 -  Blue Peg Block [U-D]
    RoomDraw_GetObjectSize_1to15or32();
    do {
      RoomDraw_Rightwards2x2(src, dst), dst += XY(0, 2);
    } while (--dung_draw_width_indicator);
    break;

  case 0x61: case 0x62: case 0x90: case 0x91:  // 61 -  [W]Wall Vert: [U-D]
    RoomDraw_GetObjectSize_1to15or26();
    RoomDraw_Downwards4x2VariableSpacing(2 * 64, src, dst);
    break;

  case 0x63:
  case 0x64:  // RoomDraw_Downwards4x2_1to16_BothBG - 63 -  [W]Wall Vert: (LOW) [U-D]
    RoomDraw_GetObjectSize_1to16();
    do {
      dung_bg1[dsto + XY(0, 0)] = dung_bg2[dsto + XY(0, 0)] = src[0];
      dung_bg1[dsto + XY(1, 0)] = dung_bg2[dsto + XY(1, 0)] = src[1];
      dung_bg1[dsto + XY(2, 0)] = dung_bg2[dsto + XY(2, 0)] = src[2];
      dung_bg1[dsto + XY(3, 0)] = dung_bg2[dsto + XY(3, 0)] = src[3];
      dung_bg1[dsto + XY(0, 1)] = dung_bg2[dsto + XY(0, 1)] = src[4];
      dung_bg1[dsto + XY(1, 1)] = dung_bg2[dsto + XY(1, 1)] = src[5];
      dung_bg1[dsto + XY(2, 1)] = dung_bg2[dsto + XY(2, 1)] = src[6];
      dung_bg1[dsto + XY(3, 1)] = dung_bg2[dsto + XY(3, 1)] = src[7];
      dsto += XY(0, 2);
    } while (--dung_draw_width_indicator);
    break;

  case 0x65: case 0x66:  // 65 -  [W]Wall Column [U-D]
    RoomDraw_GetObjectSize_1to16();
    RoomDraw_Downwards4x2VariableSpacing(6 * 64, src, dst);
    break;

  case 0x67: case 0x68: // 67 -  [W]Wall Pit [U-D]
  case 0x7d:            // 7D -  Pipe Ride [U-D]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_Rightwards2x2(src, dst), dst += XY(0, 2);
    } while (--dung_draw_width_indicator);
    break;
  case 0x69:  // 69 -  Vert: Rail Thin [U-D]
    Object_SizeAtoAplus15(2);
    if ((dst[0] & 0x3ff) != 0xe3)
      dst[0] = src[0];
    n = src[1];
    do { dst += 64; *dst = n; } while (--dung_draw_width_indicator);
    dst[64] = src[2];
    break;
  case 0x6a: case 0x6b: // 6A -  [W]Pit Edge [U-D]
  case 0x79: case 0x7a: // 79 -  Water Edge [U-D]
  case 0x8d: case 0x8e: // 8D -  [W]Edge [U-D]
    RoomDraw_GetObjectSize_1to16();
    do {
      dst[0] = src[0], dst += XY(0, 1);
    } while (--dung_draw_width_indicator);
    break;
  case 0x6c: // 6C -  [W]Rail Wall [U-D]
    Object_SizeAtoAplus15(10);
    n = *src++;
    if ((dst[0] & 0x3ff) != 0xe3) {
      dst[XY(0, 0)] = src[0];
      dst[XY(0, 1)] = src[1];
      dst[XY(1, 0)] = dst[XY(1, 1)] = n;
      dst += XY(0, 2);
    }
    src += 2;
    do {
      dst[XY(0, 0)] = src[0];
      dst[XY(1, 0)] = n;
      dst += XY(0, 1);
    } while (--dung_draw_width_indicator);
    src += 1;
    dst[XY(0, 0)] = src[0];
    dst[XY(0, 1)] = src[1];
    dst[XY(1, 0)] = dst[XY(1, 1)] = n;
    break;
  case 0x6d:   // 6D -  [E]Rail Wall [U-D]
    Object_SizeAtoAplus15(10);
    n = *src++;
    if ((dst[XY(1, 0)] & 0x3ff) != 0xe3) {
      dst[XY(0, 0)] = dst[XY(0, 1)] = n;
      dst[XY(1, 0)] = src[0];
      dst[XY(1, 1)] = src[1];
      dst += XY(0, 2);
    }
    src += 2;
    do {
      dst[XY(0, 0)] = n;
      dst[XY(1, 0)] = src[0];
      dst += XY(0, 1);
    } while (--dung_draw_width_indicator);
    src += 1;
    dst[XY(0, 0)] = dst[XY(0, 1)] = n;
    dst[XY(1, 0)] = src[0];
    dst[XY(1, 1)] = src[1];
    break;
  case 0x6e: case 0x6f: // unused
  case 0x72: case 0x7e:
    break;
  case 0x70: case 0x94:  // 70 -  Red Floor/Wire Floor [U-D]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_4x4(src, dst), dst += XY(0, 4);
    } while (--dung_draw_width_indicator);
    break;
  case 0x71:  // 71 -  Red Carpet Floor Trim [U-D]
    Object_SizeAtoAplus15(4);
    do {
      *dst = src[0], dst += XY(0, 1);
    } while (--dung_draw_width_indicator);
    break;
  case 0x73: case 0x74:  // 73 -  [W]Curtain [U-D]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_4x4(src, dst), dst += XY(0, 6);
    } while (--dung_draw_width_indicator);
    break;
  case 0x75: case 0x87:  // 75 -  Column [U-D]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_Object_Nx4(2, src, dst), dst += XY(0, 6);
    } while (--dung_draw_width_indicator);
    break;
  case 0x76: case 0x77:  // 76 -  [W]Wall Decor: [U-D]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_Object_Nx4(3, src, dst), dst += XY(0, 8);
    } while (--dung_draw_width_indicator);
    break;
  case 0x78: case 0x7b:  // 78 -  [W]Wall Top Column [U-D]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_Rightwards2x2(src, dst), dst += XY(0, 14);
    } while (--dung_draw_width_indicator);
    break;
  case 0x7c:  // 7C -  Cane Ride [U-D]
    RoomDraw_GetObjectSize_1to16();
    dung_draw_width_indicator += 1;
    do {
      dst[0] = src[0], dst += XY(0, 1);
    } while (--dung_draw_width_indicator);
    break;
  case 0x7f: case 0x80:  // 7F -  [W]Wall Torches [U-D]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_Object_Nx4(2, src, dst), dst += XY(0, 12);
    } while (--dung_draw_width_indicator);
    break;
  case 0x81: case 0x82: case 0x83: case 0x84: // 81 -  [W]Wall Decor: [U-D]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_Object_Nx4(3, src, dst), dst += XY(0, 6);
    } while (--dung_draw_width_indicator);
    break;
  case 0x85: case 0x86:  // 85 -  [W]Wall Canon Hole [U-D]
    RoomDraw_GetObjectSize_1to16();
    Object_Draw_3x2(src, dst), dst += XY(0, 2);
    while (--dung_draw_width_indicator)
      Object_Draw_3x2(src + 6, dst), dst += XY(0, 2);
    Object_Draw_3x2(src + 12, dst);
    break;
  case 0x88: // 88 -  Large Vert: Rail [U-D]
    RoomDraw_GetObjectSize_1to16();
    RoomDraw_Rightwards2x2(src, dst), dst += XY(0, 2), src += 4;
    do {
      dst[XY(0, 0)] = src[0];
      dst[XY(1, 0)] = src[1];
      dst += XY(0, 1);
    } while (--dung_draw_width_indicator);
    RoomDraw_1x3_rightwards(2, src + 2, dst);
    break;
  case 0x89: // 89 -  Block Vert: [U-D]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_Rightwards2x2(src, dst), dst += XY(0, 4);
    } while (--dung_draw_width_indicator);
    break;
  case 0x8a: // 8A -  Long Vert: Rail [U-D]
    Object_SizeAtoAplus15(21);
    if ((dst[0] & 0x3ff) != 0xe3)
      dst[0] = src[0];
    n = src[1];
    do { dst += XY(0, 1); *dst = n; } while (--dung_draw_width_indicator);
    dst[XY(0, 1)] = src[2];
    break;
  case 0x8b: case 0x8c: // 8B -  [W]Vert: Jump Edge [U-D]
    Object_SizeAtoAplus15(8);
    do {
      dst[0] = src[0], dst += XY(0, 1);
    } while (--dung_draw_width_indicator);
    break;
  case 0x8f: // 8F -  N/A
    Object_SizeAtoAplus15(2);
    dung_draw_width_indicator <<= 1;
    dst[XY(0, 0)] = src[0], dst[XY(1, 0)] = src[1];
    do {
      dst[XY(0, 1)] = src[2], dst[XY(1, 1)] = src[3], dst += XY(0, 1);
    } while (--dung_draw_width_indicator);
    break;
  case 0x95:  // 95 -  Fake Pot [U-D]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_SinglePot(src, dst, dsto);
      dst += XY(0, 2), dsto += XY(0, 2);
    } while (--dung_draw_width_indicator);
    break;
  case 0x96:  // 96 -  Hammer Peg Block [U-D]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_HammerPegSingle(src, dst, dsto);
      dst += XY(0, 2), dsto += XY(0, 2);
    } while (--dung_draw_width_indicator);
    break;
  case 0x97: case 0x98: case 0x99: case 0x9a: case 0x9b: case 0x9c: case 0x9d: case 0x9e: case 0x9f:
  case 0xad: case 0xae: case 0xaf:
  case 0xbe: case 0xbf:
    break;
  case 0xa0:  // A0 -  / Ceiling [NW]
  case 0xa5: case 0xa9:  // A5 -  / Ceiling [Trans][NW]
    Object_SizeAtoAplus15(4);
    do {
      Object_Fill_Nx1(dung_draw_width_indicator, src, dst), dst += XY(0, 1);
    } while (--dung_draw_width_indicator);
    break;
  case 0xa1:  // A1 -  \ Ceiling [SW]
  case 0xa6: case 0xaa:  // A6 -  \ Ceiling [Trans][SW]
    Object_SizeAtoAplus15(4);
    n = 1;
    do {
      Object_Fill_Nx1(n++, src, dst), dst += XY(0, 1);
    } while (--dung_draw_width_indicator);
    break;
  case 0xa2:  // A2 -  \ Ceiling [NE]
  case 0xa7: case 0xab:  // A7 -  \ Ceiling [Trans][NE]
    Object_SizeAtoAplus15(4);
    do {
      Object_Fill_Nx1(dung_draw_width_indicator, src, dst), dst += XY(1, 1);
    } while (--dung_draw_width_indicator);
    break;
  case 0xa3:             // A3 -  / Ceiling [SE]
  case 0xa8: case 0xac:  // A8 -  / Ceiling [Trans][SE]
    Object_SizeAtoAplus15(4);
    do {
      Object_Fill_Nx1(dung_draw_width_indicator, src, dst), dst += XY(1, -1);
    } while (--dung_draw_width_indicator);
    break;
  case 0xa4: // A4 -  Hole [4-way]
    Object_Hole(src, dst);
    break;
  case 0xb0: case 0xb1: // B0 -  [S]Horz: Jump Edge [L-R]
    Object_SizeAtoAplus15(8);
    Object_Fill_Nx1(dung_draw_width_indicator, src, dst);
    break;
  case 0xb5:  // B5 -  N/A
    RoomDraw_GetObjectSize_1to16();
    do {
      src = SrcPtr(0xb16);
      RoomDraw_Object_Nx4(2, src, dst), dst += XY(2, 0);
    } while (--dung_draw_width_indicator);
    break;
  case 0xbc: // BC -  fake pots [L-R]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_SinglePot(src, dst, dsto);
      dst += XY(2, 0), dsto += XY(2, 0);
    } while (--dung_draw_width_indicator);
    break;
  case 0xbd: // BD -  Hammer Pegs [L-R]
    RoomDraw_GetObjectSize_1to16();
    do {
      RoomDraw_HammerPegSingle(src, dst, dsto);
      dst += XY(2, 0), dsto += XY(2, 0);
    } while (--dung_draw_width_indicator);
    break;
  case 0xc0: case 0xc2: // C0 -  Ceiling Large [4-way]
    n = src[0];
    for (int y = dung_draw_height_indicator; y-- >= 0; ) {
      uint16 *dst_org = dst;
      for (int x = dung_draw_width_indicator; x-- >= 0; dst += XY(4, 0)) {
        dst[XY(0, 0)] = dst[XY(1, 0)] = dst[XY(2, 0)] = dst[XY(3, 0)] = n;
        dst[XY(0, 1)] = dst[XY(1, 1)] = dst[XY(2, 1)] = dst[XY(3, 1)] = n;
        dst[XY(0, 2)] = dst[XY(1, 2)] = dst[XY(2, 2)] = dst[XY(3, 2)] = n;
        dst[XY(0, 3)] = dst[XY(1, 3)] = dst[XY(2, 3)] = dst[XY(3, 3)] = n;
      }
      dst = dst_org + XY(0, 4);
    }
    break;
  case 0xc1: { // C1 -  Chest Pedastal [4-way]
    dung_draw_width_indicator += 4;
    dung_draw_height_indicator += 1;
    // draw upper part
    uint16 *dsto = dst;
    RoomDraw_1x3_rightwards(3, src, dst), src += 9, dst += XY(3, 0);
    for (int i = dung_draw_width_indicator; i--; )
      RoomDraw_1x3_rightwards(2, src, dst), dst += XY(2, 0);
    RoomDraw_1x3_rightwards(3, src + 6, dst), src += 6 + 9;
    // draw center part
    dst = dsto + XY(0, 3);
    for (int i = dung_draw_height_indicator; i--; ) {
      uint16 *dt = dst;
      Object_Draw_3x2(src, dt), dt += XY(3, 0);
      for (int j = dung_draw_width_indicator; j--; )
        RoomDraw_Rightwards2x2(src + 6, dt), dt += XY(2, 0);
      Object_Draw_3x2(src + 10, dt);
      dst += XY(0, 2);
    }
    dsto = dst;
    src += 6 + 4 + 6;
    RoomDraw_1x3_rightwards(3, src, dst), src += 9, dst += XY(3, 0);
    for (int i = dung_draw_width_indicator; i--; )
      RoomDraw_1x3_rightwards(2, src, dst), dst += XY(2, 0);
    RoomDraw_1x3_rightwards(3, src + 6, dst), src += 6 + 9;

    src = SrcPtr(0x590);
    RoomDraw_Rightwards2x2(src, dsto + XY(dung_draw_width_indicator + 2, -(dung_draw_height_indicator + 1)));
    break;
  }
  case 0xc3:  // C3 -  Falling Edge Mask [4-way]
  case 0xd7:  // D7 -  overlay tile? [4-way]
    dung_draw_width_indicator++;
    dung_draw_height_indicator++;
    n = *src;
    do {
      uint16 *d = dst;
      for (int i = dung_draw_width_indicator; i--; d += XY(3, 0)) {
        d[XY(0, 0)] = d[XY(1, 0)] = d[XY(2, 0)] = n;
        d[XY(0, 1)] = d[XY(1, 1)] = d[XY(2, 1)] = n;
        d[XY(0, 2)] = d[XY(1, 2)] = d[XY(2, 2)] = n;
      }
      dst += XY(0, 3);
    } while (--dung_draw_height_indicator);
    break;

  case 0xc4: // C4 -  Doorless Room Transition
    src = SrcPtr(dung_floor_2_filler_tiles);
    goto fill_floor;

  case 0xdb: // C4 -  DB -  Floor2 [4-way]
    src = SrcPtr(dung_floor_1_filler_tiles);
    goto fill_floor;

  case 0xc5: case 0xc6: case 0xc7: case 0xc8: case 0xc9: case 0xca:
  case 0xd1: case 0xd2: case 0xd9:
  case 0xdf: case 0xe0: case 0xe1: case 0xe2: case 0xe3: case 0xe4:
  case 0xe5: case 0xe6: case 0xe7: case 0xe8:
fill_floor:
    dung_draw_width_indicator++;
    dung_draw_height_indicator++;
    do {
      RoomDraw_A_Many32x32Blocks(dung_draw_width_indicator, src, dst);
      dst += XY(0, 4);
    } while (--dung_draw_height_indicator);
    break;

  case 0xcd: { // CD -  Moving Wall Right [4-way]
    if (!RoomDraw_CheckIfWallIsMoved())
      return;
    dung_hdr_collision_2_mirror++;
    int size0 = kMovingWall_Sizes0[dung_draw_width_indicator];
    int size1 = kMovingWall_Sizes1[dung_draw_height_indicator];
    MovingWall_FillReplacementBuffer(dsto - size1 - 1);
    moving_wall_var2 = dung_draw_height_indicator * 2;
    src = SrcPtr(0x3d8);
    uint16 *dst1 = dst - size1;
    do {
      uint16 *dst2 = dst1;
      dst2[XY(0, 0)] = src[0];
      int n1 = size0 * 2 + 4;
      do {
        dst2[XY(0, 1)] = src[1];
        dst2 += XY(0, 1);
      } while (--n1);
      dst2[XY(0, 1)] = src[2];
      dst1++;
    } while (--size1);
    src = SrcPtr(0x72a);
    RoomDraw_1x3_rightwards(3, src, dst);
    dst += XY(0, 3);
    do {
      Object_Draw_3x2(src + 9, dst);
    } while (dst += XY(0, 2), --size0);
    RoomDraw_1x3_rightwards(3, src + 9 + 6, dst);
    break;
  }

  case 0xce: { // CE -  Moving Wall Left [4-way]
    if (!RoomDraw_CheckIfWallIsMoved())
      return;
    dung_hdr_collision_2_mirror++;
    src = SrcPtr(0x75a);
    int size1 = kMovingWall_Sizes1[dung_draw_height_indicator];
    int size0 = kMovingWall_Sizes0[dung_draw_width_indicator];
    moving_wall_var2 = dung_draw_height_indicator * 2;
    MovingWall_FillReplacementBuffer(dsto + 3 + size1);
    uint16 *dst1 = dst;
    RoomDraw_1x3_rightwards(3, src, dst1);
    dst1 += XY(0, 3);
    int n = size0;
    do {
      Object_Draw_3x2(src + 9, dst1);
      dst1 += XY(0, 2);
    } while (--n);
    RoomDraw_1x3_rightwards(3, src + 15, dst1);
    src = SrcPtr(0x3d8);
    dst1 = dst + XY(3, 0);
    do {
      uint16 *dst2 = dst1;
      dst2[XY(0, 0)] = src[0];
      int n1 = size0 * 2 + 4;
      do {
        dst2[XY(0, 1)] = src[1];
        dst2 += XY(0, 1);
      } while (--n1);
      dst2[XY(0, 1)] = src[2];
      dst1++;
    } while (--size1);
    break;
  }

  case 0xd8: {
    // loads of lava/water hdma stuff
    dung_draw_width_indicator += 2;
    water_hdma_var3 = (dung_draw_width_indicator << 4);
    dung_draw_height_indicator += 2;
    water_hdma_var2 = (dung_draw_height_indicator << 4);
    water_hdma_var4 = water_hdma_var2 - 24;
    water_hdma_var0 = (dsto & 0x3f) << 3;
    water_hdma_var0 += (dung_draw_width_indicator << 4) + dung_loade_bgoffs_h_copy;
    water_hdma_var1 = (dsto & 0xfc0) >> 3;
    water_hdma_var1 += (dung_draw_height_indicator << 4) + dung_loade_bgoffs_v_copy;
    if (dung_savegame_state_bits & 0x800) {
      dung_hdr_tag[1] = 0;
      dung_hdr_bg2_properties = 0;
      dung_num_interpseudo_upnorth_stairs = dung_num_inroom_upnorth_stairs_water;
      dung_some_stairs_unk4 = dung_num_activated_water_ladders;
      dung_num_activated_water_ladders = 0;
      dung_num_inroom_upnorth_stairs_water = 0;
      dung_num_stairs_wet = dung_num_inroom_upsouth_stairs_water;
      dung_num_inroom_upsouth_stairs_water = 0;
      dsto += (dung_draw_width_indicator - 1) << 1;
      dsto += (dung_draw_height_indicator - 1) << 7;
      DrawWaterThing(&dung_bg2[dsto], SrcPtr(0x1438));
    } else {
      src = SrcPtr(0x110);
      do {
        RoomDraw_A_Many32x32Blocks(dung_draw_width_indicator, src, dst);
        dst += XY(0, 4);
      } while (--dung_draw_height_indicator);
    }
    break;
  }

  case 0xda: {  // water hdma stuff
    dung_draw_width_indicator += 2;
    water_hdma_var3 = (dung_draw_width_indicator << 4) - 24;

    dung_draw_height_indicator += 2;
    water_hdma_var4 = (dung_draw_height_indicator << 4) - 8;

    water_hdma_var2 = water_hdma_var4 - 24;
    water_hdma_var5 = 0;
    water_hdma_var0 = (dsto & 0x3f) << 3;
    water_hdma_var0 += (dung_draw_width_indicator << 4) + dung_loade_bgoffs_h_copy;
    water_hdma_var1 = (dsto & 0xfc0) >> 3;
    water_hdma_var1 += (dung_draw_height_indicator << 4) + dung_loade_bgoffs_v_copy - 8;
    if (dung_savegame_state_bits & 0x800) {
      dung_hdr_tag[1] = 0;
    } else {
      dung_hdr_bg2_properties = 0;
      dung_num_interpseudo_upnorth_stairs = dung_num_inroom_upnorth_stairs_water;
      dung_some_stairs_unk4 = dung_num_activated_water_ladders;
      dung_num_activated_water_ladders = 0;
      dung_num_inroom_upnorth_stairs_water = 0;
      dung_num_stairs_wet = dung_num_inroom_upsouth_stairs_water;
      dung_num_inroom_upsouth_stairs_water = 0;
    }
    int n = dung_draw_height_indicator * 2 - 1;
    src = SrcPtr(0x110);
    do {
      uint16 *dst2 = dst;
      int j = dung_draw_width_indicator;
      do {
        dst[XY(0, 0)] = src[0];
        dst[XY(1, 0)] = src[1];
        dst[XY(2, 0)] = src[2];
        dst[XY(3, 0)] = src[3];
        dst[XY(0, 1)] = src[4];
        dst[XY(1, 1)] = src[5];
        dst[XY(2, 1)] = src[6];
        dst[XY(3, 1)] = src[7];
        dst += 4;
      } while (--j);
      dst = dst2 + XY(0, 2);
    } while (--n);
    break;
  }

  case 0xdc: { // DC -  Chest Platform? [4-way]
    dsto |= (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x1000);
    dung_draw_width_indicator++;
    dung_draw_height_indicator = dung_draw_height_indicator * 2 + 5;
    src = SrcPtr(0xAB4);
    do {
      Object_ChestPlatform_Helper(src, dsto), dsto += XY(0, 1);
    } while (--dung_draw_height_indicator);
    Object_ChestPlatform_Helper(src + 1, dsto), dsto += XY(0, 1);
    Object_ChestPlatform_Helper(src + 2, dsto), dsto += XY(0, 1);
    break;
  }
  case 0xdd: // DD -  Table / Rock [4-way]
    dung_draw_width_indicator++;
    dung_draw_height_indicator = dung_draw_height_indicator * 2 + 1;
    Object_Table_Helper(src, dst), dst += XY(0, 1);
    do {
      Object_Table_Helper(src + 4, dst), dst += XY(0, 1);
    } while (--dung_draw_height_indicator);
    Object_Table_Helper(src + 8, dst), dst += XY(0, 1);
    Object_Table_Helper(src + 12, dst), dst += XY(0, 1);
    break;

  case 0xde: // DE -  Spike Block [4-way]
    dung_draw_width_indicator++;
    dung_draw_height_indicator++;
    do {
      int n = dung_draw_width_indicator;
      uint16 *dst1 = dst;
      do {
        RoomDraw_Rightwards2x2(src, dst1), dst1 += XY(2, 0);
      } while (--n);
      dst += XY(0, 2);
    } while (--dung_draw_height_indicator);
    break;

  case 0xcb: case 0xcc: case 0xcf: case 0xd0:
  case 0xd3: case 0xd4: case 0xd5: case 0xd6:
  case 0xe9: case 0xea: case 0xeb: case 0xec: case 0xed: case 0xee: case 0xef:
  case 0xf0: case 0xf1: case 0xf2: case 0xf3: case 0xf4: case 0xf5: case 0xf6: case 0xf7:
    assert(0);
    break;
  default:
    assert(0);
  }
}

void Object_DrawNx3_BothBgs(int n, const uint16 *src, int dsto) {
  do {
    dung_bg1[dsto + XY(0, 0)] = dung_bg2[dsto + XY(0, 0)] = src[0];
    dung_bg1[dsto + XY(0, 1)] = dung_bg2[dsto + XY(0, 1)] = src[1];
    dung_bg1[dsto + XY(0, 2)] = dung_bg2[dsto + XY(0, 2)] = src[2];
    src += 3, dsto += 1;
  } while (--n);
}

void LoadType1ObjectSubtype2(uint8 idx, uint16 *dst, uint16 dsto) {
  uint16 params = kObjectSubtype2Params[idx];
  const uint16 *src = SrcPtr(params);
  int i;
  switch (idx) {
  case 0x00: case 0x01: case 0x02: case 0x03:
  case 0x04: case 0x05: case 0x06: case 0x07:  // 00 -  Wall Outer Corner (HIGH) [NW]
  case 0x1c: case 0x24: case 0x25: case 0x29:
    RoomDraw_Object_Nx4(4, src, dst);
    break;
  case 0x08: case 0x09: case 0x0a: case 0x0b:
  case 0x0c: case 0x0d: case 0x0e: case 0x0f:  // 08 -  Wall Outer Corner (LOW) [NW]
    Object_DrawNx4_BothBgs(4, src, dsto);
    break;
  case 0x10: case 0x11: case 0x12: case 0x13:  // 10 -  Wall S-Bend (LOW) [N1]
    Object_DrawNx4_BothBgs(3, src, dsto);
    break;
  case 0x14: case 0x15: case 0x16: case 0x17:  // 14 -  Wall S-Bend (LOW) [W1]
    Object_DrawNx3_BothBgs(4, src, dsto);
    break;
  case 0x18: case 0x19: case 0x1a: case 0x1b:  // 18 -  Wall Pit Corner (Lower) [NW]
  case 0x27: case 0x2b: case 0x34:
    RoomDraw_Rightwards2x2(src, dst);
    break;
  case 0x1d: case 0x21: case 0x26:             // 1D -  Statue
    RoomDraw_1x3_rightwards(2, src, dst);
    break;
  case 0x1e:                                   // 1E -  Star Tile Off
    RoomDraw_Rightwards2x2(src, dst);
    break;
  case 0x1f:                                   // 1F -  Star Tile On
    i = dung_num_star_shaped_switches >> 1;
    dung_num_star_shaped_switches += 2;
    star_shaped_switches_tile[i] = (dsto | (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x1000));
    RoomDraw_Rightwards2x2(src, dst);
    break;
  case 0x20:                                   // 20 -  Torch Lit
    dung_num_lit_torches++;
    RoomDraw_Rightwards2x2(src, dst);
    break;
  case 0x22: case 0x28:                        // 22 -  Weird Bed
    Object_Draw_5x4(src, dst);
    break;
  case 0x23:                                   // 23 -  Table
    RoomDraw_1x3_rightwards(4, src, dst);
    break;
  case 0x2a:                                   // 2A -  Wall Painting
    dung_draw_width_indicator = 1;
    RoomDraw_Downwards4x2VariableSpacing(1, src, dst);
    break;
  case 0x2c:                                   // 2C -  ???
    RoomDraw_1x3_rightwards(6, src, dst);
    break;
  case 0x2d:    // 2D -  Floor Stairs Up (room)
    i = dung_num_inter_room_upnorth_stairs >> 1;
    dung_inter_starcases[i] = (dsto | (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x1000));
    dung_num_inter_room_upnorth_stairs =
      dung_num_wall_upnorth_spiral_stairs =
      dung_num_wall_upnorth_spiral_stairs_2 =
      dung_num_inter_room_upnorth_straight_stairs =
      dung_num_inter_room_upsouth_straight_stairs =
      dung_num_inter_room_southdown_stairs =
      dung_num_wall_downnorth_spiral_stairs =
      dung_num_wall_downnorth_spiral_stairs_2 =
      dung_num_inter_room_downnorth_straight_stairs =
      dung_num_inter_room_downsouth_straight_stairs = dung_num_inter_room_upnorth_stairs + 2;
    RoomDraw_4x4(SrcPtr(0x1088), dst);
    break;
  case 0x2e:    // 2E -  Floor Stairs Down (room)
  case 0x2f:    // 2F -  Floor Stairs Down2 (room)
    i = dung_num_inter_room_southdown_stairs >> 1;
    dung_inter_starcases[i] = (dsto | (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x1000));
    dung_num_inter_room_southdown_stairs =
      dung_num_wall_downnorth_spiral_stairs =
      dung_num_wall_downnorth_spiral_stairs_2 =
      dung_num_inter_room_downnorth_straight_stairs =
      dung_num_inter_room_downsouth_straight_stairs = dung_num_inter_room_southdown_stairs + 2;
    RoomDraw_4x4(SrcPtr(0x10A8), dst);
    break;
  case 0x30:     // 30 -  Stairs [N](unused)
    assert(0);
    break;
  case 0x31:     // 31 -  Stairs [N](layer)
    i = dung_num_inroom_southdown_stairs >> 1;
    dung_stairs_table_1[i] = dsto;
    dung_num_inroom_southdown_stairs =
      dung_num_water_ladders =
      dung_some_stairs_unk4 = dung_num_inroom_southdown_stairs + 2;
    Object_DrawNx4_BothBgs(4, src, dsto);
    break;
  case 0x32:     // 32 -  Stairs [N](layer)
non_submerged:
    i = dung_num_interpseudo_upnorth_stairs >> 1;
    dung_stairs_table_1[i] = dsto;
    dung_num_interpseudo_upnorth_stairs =
      dung_num_water_ladders =
      dung_some_stairs_unk4 = dung_num_interpseudo_upnorth_stairs + 2;
    RoomDraw_4x4(src, dst);
    break;
  case 0x33:       // 33 -  Stairs Submerged [N](layer)
    if (dung_hdr_tag[1] == 27 && !(save_dung_info[dungeon_room_index] & 0x100)) {
      dung_hdr_bg2_properties = 0;
      src = SrcPtr(0x10C8);
      goto non_submerged;
    } else {
      i = dung_num_inroom_upnorth_stairs_water >> 1;
      dung_stairs_table_1[i] = dsto;
      dung_num_inroom_upnorth_stairs_water =
        dung_num_activated_water_ladders = dung_num_inroom_upnorth_stairs_water + 2;
      RoomDraw_4x4(SrcPtr(0x10C8), dst);
    }
    break;
  case 0x35:  // 35 -  Water Ladder
    if (dung_hdr_tag[1] == 27 && !(save_dung_info[dungeon_room_index] & 0x100))
      goto inactive_water_ladder;
    dung_stairs_table_1[dung_num_activated_water_ladders >> 1] = dsto;
    dung_num_activated_water_ladders += 2;
    dung_draw_width_indicator = 1;
    RoomDraw_Downwards4x2VariableSpacing(1, SrcPtr(0x1108), dst);
    break;
  case 0x36:  // 36 -  Water Ladder Inactive
inactive_water_ladder:
    dung_stairs_table_1[dung_num_water_ladders >> 1] = dsto;
    dung_some_stairs_unk4 = (dung_num_water_ladders += 2);
    Object_Draw_4x2_BothBgs(SrcPtr(0x1108), dsto);
    break;
  case 0x37:  // 37 -  Water Gate Large
    if (!(dung_savegame_state_bits & 0x800)) {
      RoomDraw_Object_Nx4(10, src, dst);
      watergate_var1 = 0xf;
      watergate_pos = dsto * 2;
    } else {
      RoomDraw_Object_Nx4(10, SrcPtr(0x13e8), dst);
      uint16 bak0 = dung_load_ptr;
      uint16 bak1 = dung_load_ptr_offs;
      uint8 bak2 = dung_load_ptr_bank;
      RoomTag_OperateWaterFlooring();
      dung_load_ptr_bank = bak2;
      dung_load_ptr_offs = bak1;
      dung_load_ptr = bak0;
    }
    break;
  case 0x38:  // 38 -  Door Staircase Up R
    i = dung_num_wall_upnorth_spiral_stairs >> 1;
    dung_inter_starcases[i] = (dsto - 0x40) | (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x1000);
    dung_num_wall_upnorth_spiral_stairs =
      dung_num_wall_upnorth_spiral_stairs_2 =
      dung_num_inter_room_upnorth_straight_stairs =
      dung_num_inter_room_upsouth_straight_stairs =
      dung_num_inter_room_southdown_stairs =
      dung_num_wall_downnorth_spiral_stairs =
      dung_num_wall_downnorth_spiral_stairs_2 =
      dung_num_inter_room_downnorth_straight_stairs =
      dung_num_inter_room_downsouth_straight_stairs = dung_num_wall_upnorth_spiral_stairs + 2;
    RoomDraw_1x3_rightwards(4, SrcPtr(0x1148), dst);
    dung_bg2[dsto - 1] |= 0x2000;
    dung_bg2[dsto + 4] |= 0x2000;
    break;
  case 0x39:  // 39 -  Door Staircase Down L
    i = dung_num_wall_downnorth_spiral_stairs >> 1;
    dung_inter_starcases[i] = (dsto - 0x40) | (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x1000);
    dung_num_wall_downnorth_spiral_stairs =
      dung_num_wall_downnorth_spiral_stairs_2 =
      dung_num_inter_room_downnorth_straight_stairs =
      dung_num_inter_room_downsouth_straight_stairs = dung_num_wall_downnorth_spiral_stairs + 2;
    RoomDraw_1x3_rightwards(4, SrcPtr(0x1160), dst);
    dung_bg2[dsto - 1] |= 0x2000;
    dung_bg2[dsto + 4] |= 0x2000;
    break;
  case 0x3a:  // 3A -  Door Staircase Up R (Lower)
    i = dung_num_wall_upnorth_spiral_stairs_2 >> 1;
    dung_inter_starcases[i] = (dsto - 0x40) | (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x1000);
    dung_num_wall_upnorth_spiral_stairs_2 =
      dung_num_inter_room_upnorth_straight_stairs =
      dung_num_inter_room_upsouth_straight_stairs =
      dung_num_inter_room_southdown_stairs =
      dung_num_wall_downnorth_spiral_stairs =
      dung_num_wall_downnorth_spiral_stairs_2 =
      dung_num_inter_room_downnorth_straight_stairs =
      dung_num_inter_room_downsouth_straight_stairs = dung_num_wall_upnorth_spiral_stairs_2 + 2;
    RoomDraw_1x3_rightwards(4, SrcPtr(0x1178), dst);
    dung_bg1[dsto - 1] |= 0x2000;
    dung_bg1[dsto + 4] |= 0x2000;
    break;
  case 0x3b:  // 3B -  Door Staircase Down L (Lower)
    i = dung_num_wall_downnorth_spiral_stairs_2 >> 1;
    dung_inter_starcases[i] = (dsto - 0x40) | (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x1000);
    dung_num_wall_downnorth_spiral_stairs_2 =
      dung_num_inter_room_downnorth_straight_stairs =
      dung_num_inter_room_downsouth_straight_stairs =
      dung_num_wall_downnorth_spiral_stairs_2 + 2;
    RoomDraw_1x3_rightwards(4, SrcPtr(0x1190), dst);
    dung_bg1[dsto - 1] |= 0x2000;
    dung_bg1[dsto + 4] |= 0x2000;
    break;
  case 0x3c:  // 3C -  Sanctuary Wall
    for (int i = 0; i < 6; i++) {
      dung_bg2[dsto + 0] = dung_bg2[dsto + 4] = dung_bg2[dsto + 8] =
        dung_bg2[dsto + 14] = dung_bg2[dsto + 18] = dung_bg2[dsto + 22] = src[0];
      dung_bg2[dsto + 1] = dung_bg2[dsto + 5] = dung_bg2[dsto + 9] =
        dung_bg2[dsto + 15] = dung_bg2[dsto + 19] = dung_bg2[dsto + 23] = src[0] | 0x4000;
      dung_bg2[dsto + 2] = dung_bg2[dsto + 6] = dung_bg2[dsto + 16] = dung_bg2[dsto + 20] = src[6];
      dung_bg2[dsto + 3] = dung_bg2[dsto + 7] = dung_bg2[dsto + 17] = dung_bg2[dsto + 21] = src[6] | 0x4000;
      dsto += XY(0, 1);
      src++;
    }
    RoomDraw_1x3_rightwards(4, src + 6, dst + 10);
    break;
  case 0x3e:  // 3E -  Church Pew
    RoomDraw_1x3_rightwards(6, src, dst);
    break;
  case 0x3f: { // 3F - used in hole at the smithy dwarves
    dsto |= (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x1000);
    dst = &dung_bg2[dsto];
    for (int i = 0; i < 8; i++) {
      dst[XY(0, 0)] = src[0];
      dst[XY(0, 1)] = src[1];
      dst[XY(0, 2)] = src[2];
      dst[XY(0, 3)] = src[3];
      dst[XY(0, 4)] = src[4];
      dst[XY(0, 5)] = src[5];
      dst[XY(0, 6)] = src[6];
      dst += XY(1, 0);
      src += 7;
    }
    break;
  }

  default:
    assert(0);
  }
}

void Object_BombableFloorHelper(uint16 a, const uint16 *src, const uint16 *src_below, uint16 *dst, uint16 dsto) {
  int i = dung_misc_objs_index >> 1;
  dung_replacement_tile_state[i] = a;
  dung_misc_objs_index += 2;
  dung_object_pos_in_objdata[i] = dung_load_ptr_offs;
  dung_object_tilemap_pos[i] = dsto * 2 | (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x2000);
  replacement_tilemap_UL[i] = src_below[0];
  replacement_tilemap_LL[i] = src_below[1];
  replacement_tilemap_UR[i] = src_below[2];
  replacement_tilemap_LR[i] = src_below[3];
  RoomDraw_Rightwards2x2(src, dst);
}

void LoadType1ObjectSubtype3(uint8 idx, uint16 *dst, uint16 dsto) {
  uint16 params = kObjectSubtype3Params[idx];
  const uint16 *src = SrcPtr(params);
  int i;

  switch (idx) {
  case 0x00:  // 00 -  Water Face Closed
    if (dung_hdr_tag[1] == 27) {
      if (save_dung_info[dungeon_room_index] & 0x100)
        goto water_face_open;
    } else if (dung_hdr_tag[1] == 25) {
      if (dung_savegame_state_bits & 0x800)
        goto water_face_open;
    }
    word_7E047C = dsto * 2;
    RoomDraw_WaterHoldingObject(3, src, dst);
    break;
  case 0x01:  // 01 -  Waterfall Face
water_face_open:
    RoomDraw_WaterHoldingObject(5, SrcPtr(0x162c), dst);
    break;
  case 0x02:  // 02 -  Waterfall Face Longer
    RoomDraw_WaterHoldingObject(7, src, dst);
    break;
  case 0x03: case 0x0e:  // 03 -  Cane Ride Spawn [?]Block
    dung_unk6++;
    dst[0] = src[0];
    break;
  case 0x04: case 0x05: case 0x06: case 0x07:  // 04 -  Cane Ride Node [4-way]
  case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0f:
    dst[0] = src[0];
    break;
  case 0x0d: case 0x17: { // 0D -  Prison Cell
    src = SrcPtr(0x1488);
    dsto |= (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x1000);
    uint16 *d = &dung_bg2[dsto], *dd = d;
    for (int i = 0; i < 5; i++, d++) {
      d[XY(2, 0)] = d[XY(9, 0)] = src[1];
      d[XY(2, 1)] = src[2];
      d[XY(9, 1)] = src[2] | 0x4000;
      d[XY(2, 2)] = src[4];
      d[XY(9, 2)] = src[4] | 0x4000;
      d[XY(2, 3)] = src[5];
      d[XY(9, 3)] = src[5] | 0x4000;
    }
    dd[XY(0, 0)] = src[0];
    dd[XY(15, 0)] = src[0] | 0x4000;
    dd[XY(1, 0)] = dd[XY(7, 0)] = dd[XY(8, 0)] = dd[XY(14, 0)] = src[1];
    dd[XY(1, 2)] = src[3];
    dd[XY(14, 2)] = src[3] | 0x4000;
    break;
  }
  case 0x10: case 0x11: case 0x13: case 0x1a: case 0x22: case 0x23:
  case 0x24: case 0x25:
  case 0x3e: case 0x3f: case 0x40: case 0x41: case 0x42:
  case 0x43: case 0x44: case 0x45: case 0x46: case 0x49: case 0x4a: case 0x4f:
  case 0x50: case 0x51: case 0x52: case 0x53: case 0x56: case 0x57: case 0x58: case 0x59:
  case 0x5e: case 0x5f: case 0x63: case 0x64: case 0x65:
  case 0x75: case 0x7c: case 0x7d: case 0x7e:
    RoomDraw_Rightwards2x2(src, dst);
    break;

  case 0x12:  // 12 -  Rupee Floor
    if (dung_savegame_state_bits & 0x1000)
      return;
    src = SrcPtr(0x1dd6);
    dst = &dung_bg2[dsto | (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x1000)];
    for (int i = 0; i < 3; i++) {
      dst[XY(0, 0)] = dst[XY(0, 3)] = dst[XY(0, 6)] = src[0];
      dst[XY(0, 1)] = dst[XY(0, 4)] = dst[XY(0, 7)] = src[1];
      dst += 2;
    }
    break;
  case 0x14: case 0x4E: // 14 -  Down Warp Door
  case 0x67: case 0x68: case 0x6c: case 0x6d: case 0x79:
    RoomDraw_1x3_rightwards(4, src, dst);
    break;
  case 0x15:  // 15 -  Kholdstare Shell - BG2
    if (dung_savegame_state_bits & 0x8000)
      return;
    src = SrcPtr(0x1dfa);
    RoomDraw_SomeBigDecors(10, src, dsto);
    break;
  case 0x16:   // 16 -  Single Hammer Peg
    RoomDraw_HammerPegSingle(src, dst, dsto);
    break;
  case 0x18:  // 18 -  Cell Lock
    i = dung_num_bigkey_locks_x2 >> 1;
    dung_num_bigkey_locks_x2 += 2;
    if (!(dung_savegame_state_bits & kChestOpenMasks[i])) {
      dung_chest_locations[i] = dsto * 2;
      RoomDraw_Rightwards2x2(SrcPtr(0x1494), dst);
    } else {
      dung_chest_locations[i] = 0;
    }
    break;
  case 0x19: {  // 19 -  Chest
    if (main_module_index == 26)
      return;
    i = dung_num_chests_x2 >> 1;
    dung_num_bigkey_locks_x2 = (dung_num_chests_x2 += 2);

    int h = -1;
    if (dung_hdr_tag[0] == 0x27 || dung_hdr_tag[0] == 0x3c ||
        dung_hdr_tag[0] == 0x3e || dung_hdr_tag[0] >= 0x29 && dung_hdr_tag[0] < 0x33)
      h = 0;
    else if (dung_hdr_tag[1] == 0x27 || dung_hdr_tag[1] == 0x3c ||
             dung_hdr_tag[1] == 0x3e || dung_hdr_tag[1] >= 0x29 && dung_hdr_tag[1] < 0x33)
      h = 1;

    dung_chest_locations[i] = 2 * (dsto | (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x1000));
    if (!(dung_savegame_state_bits & kChestOpenMasks[i])) {
      if (h >= 0) {
        if (!(dung_savegame_state_bits & kChestOpenMasks[h]))
          return;
        dung_hdr_tag[h] = 0;
      }
      RoomDraw_Rightwards2x2(SrcPtr(0x149c), dst);
    } else {
      dung_chest_locations[i] = 0;
      if (h >= 0)
        dung_hdr_tag[h] = 0;
      RoomDraw_Rightwards2x2(SrcPtr(0x14a4), dst);
    }
    break;
  }
  case 0x1b:  // 1B -  Stair
    dung_stairs_table_1[dung_num_stairs_1 >> 1] = dsto;
    dung_num_stairs_1 += 2;
stair1b:
    for (int i = 0; i < 4; i++) {
      dung_bg1[dsto + XY(0, 0)] = dung_bg2[dsto + XY(0, 0)] = src[0];
      dung_bg1[dsto + XY(0, 1)] = dung_bg2[dsto + XY(0, 1)] = src[1];
      dung_bg1[dsto + XY(0, 2)] = dung_bg2[dsto + XY(0, 2)] = src[2];
      dung_bg1[dsto + XY(0, 3)] = dung_bg2[dsto + XY(0, 3)] = src[3];
      src += 4, dsto += 1;
    }
    break;
  case 0x1c:  // 1C -  Stair [S](Layer)
    dung_stairs_table_2[dung_num_stairs_2 >> 1] = dsto;
    dung_num_stairs_2 += 2;
    goto stair1b;
  case 0x1d:  //  1D -  Stair Wet [S](Layer)
stairs_wet:
    dung_stairs_table_2[dung_num_stairs_wet >> 1] = dsto;
    dung_num_stairs_wet += 2;
    RoomDraw_4x4(src, dst);
    break;
  case 0x1e:  // 1E -  Staircase going Up(Up)
    dung_inter_starcases[dung_num_inter_room_upnorth_straight_stairs >> 1] = dsto;
    dung_num_inter_room_upnorth_straight_stairs =
      dung_num_inter_room_upsouth_straight_stairs =
      dung_num_inter_room_southdown_stairs =
      dung_num_wall_downnorth_spiral_stairs =
      dung_num_wall_downnorth_spiral_stairs_2 =
      dung_num_inter_room_downnorth_straight_stairs =
      dung_num_inter_room_downsouth_straight_stairs = dung_num_inter_room_upnorth_straight_stairs + 2;
    RoomDraw_Object_Nx4(4, src, dst);
    break;
  case 0x1f: // 1F -  Staircase Going Down (Up)
    dung_inter_starcases[dung_num_inter_room_downnorth_straight_stairs >> 1] = dsto;
    dung_num_inter_room_downnorth_straight_stairs =
      dung_num_inter_room_downsouth_straight_stairs = dung_num_inter_room_downnorth_straight_stairs + 2;
    RoomDraw_Object_Nx4(4, src, dst);
    break;
  case 0x20:  // 20 -  Staircase Going Up (Down)
    dung_inter_starcases[dung_num_inter_room_upsouth_straight_stairs >> 1] = dsto;
    dung_num_inter_room_upsouth_straight_stairs =
      dung_num_inter_room_southdown_stairs =
      dung_num_wall_downnorth_spiral_stairs =
      dung_num_wall_downnorth_spiral_stairs_2 =
      dung_num_inter_room_downnorth_straight_stairs =
      dung_num_inter_room_downsouth_straight_stairs = dung_num_inter_room_upsouth_straight_stairs + 2;
    RoomDraw_Object_Nx4(4, src, dst);
    break;
  case 0x21:  // 21 -  Staircase Going Down (Down)
    dung_inter_starcases[dung_num_inter_room_downsouth_straight_stairs >> 1] = dsto;
    dung_num_inter_room_downsouth_straight_stairs = dung_num_inter_room_downsouth_straight_stairs + 2;
    RoomDraw_Object_Nx4(4, src, dst);
    break;
  case 0x26:  // 26 -  Staircase Going Up (Lower)
    i = dung_num_inter_room_upnorth_straight_stairs >> 1;
    dung_inter_starcases[i] = dsto | (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x1000);
    dung_num_inter_room_upnorth_straight_stairs =
      dung_num_inter_room_upsouth_straight_stairs =
      dung_num_inter_room_southdown_stairs =
      dung_num_wall_downnorth_spiral_stairs =
      dung_num_wall_downnorth_spiral_stairs_2 =
      dung_num_inter_room_downnorth_straight_stairs =
      dung_num_inter_room_downsouth_straight_stairs = dung_num_inter_room_upnorth_straight_stairs + 2;
door26:
    for (int i = 0; i < 4; i++) {
      dung_bg1[dsto] = dung_bg2[dsto] = src[0];
      dung_bg1[dsto + XY(0, 1)] = src[1];
      dung_bg1[dsto + XY(0, 2)] = src[2];
      dung_bg1[dsto + XY(0, 3)] = src[3];
      src += 4, dsto++;
    }
    dsto += XY(-4, -4);
copy_door_bg2:
    dung_bg2[dsto + XY(0, 0)] |= 0x2000;
    dung_bg2[dsto + XY(0, 1)] |= 0x2000;
    dung_bg2[dsto + XY(0, 2)] |= 0x2000;
    dung_bg2[dsto + XY(0, 3)] |= 0x2000;
    break;
  case 0x27:  // 27 -  Staircase Going Up (Lower)
    i = dung_num_inter_room_downnorth_straight_stairs >> 1;
    dung_inter_starcases[i] = dsto | (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x1000);
    dung_num_inter_room_downnorth_straight_stairs =
      dung_num_inter_room_downsouth_straight_stairs =
      dung_num_inter_room_downnorth_straight_stairs + 2;
    goto door26;
  case 0x28:  // 28 -  Staircase Going Down (Lower)
    i = dung_num_inter_room_upsouth_straight_stairs >> 1;
    dung_inter_starcases[i] = dsto | (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x1000);
    dung_num_inter_room_upsouth_straight_stairs =
      dung_num_inter_room_southdown_stairs =
      dung_num_wall_downnorth_spiral_stairs =
      dung_num_wall_downnorth_spiral_stairs_2 =
      dung_num_inter_room_downnorth_straight_stairs =
      dung_num_inter_room_downsouth_straight_stairs =
      dung_num_inter_room_upsouth_straight_stairs + 2;
door28:
    for (int i = 0; i < 4; i++) {
      dung_bg1[dsto + XY(0, 0)] = src[0];
      dung_bg1[dsto + XY(0, 1)] = src[1];
      dung_bg1[dsto + XY(0, 2)] = src[2];
      dung_bg1[dsto + XY(0, 3)] = dung_bg2[dsto + XY(0, 3)] = src[3];
      src += 4, dsto++;
    }
    dsto += XY(-4, 4);
    goto copy_door_bg2;
  case 0x29:  // 29 -  Staircase Going Down (Lower)
    i = dung_num_inter_room_downsouth_straight_stairs >> 1;
    dung_inter_starcases[i] = dsto | (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x1000);
    dung_num_inter_room_downsouth_straight_stairs =
      dung_num_inter_room_downsouth_straight_stairs + 2;
    goto door28;
  case 0x2a:  // 2A -  Dark Room BG2 Mask
    RoomDraw_SingleLampCone(0x514, 0x16dc);
    RoomDraw_SingleLampCone(0x554, 0x17f6);
    RoomDraw_SingleLampCone(0x1514, 0x1914);
    RoomDraw_SingleLampCone(0x1554, 0x1a2a);
    break;
  case 0x2b:  // 2B -  Staircase Going Down (Lower) not really
    DrawBigGraySegment(0x1010, src, dst, dsto);
    break;
  case 0x2c: // 2C -  Large Pick Up Block
    DrawBigGraySegment(0x2020, SrcPtr(0xe62), dst, dsto);
    DrawBigGraySegment(0x2121, SrcPtr(0xe6a), dst + XY(2, 0), dsto + XY(2, 0));
    DrawBigGraySegment(0x2222, SrcPtr(0xe72), dst + XY(0, 2), dsto + XY(0, 2));
    DrawBigGraySegment(0x2323, SrcPtr(0xe7a), dst + XY(2, 2), dsto + XY(2, 2));
    break;
  case 0x2d: { // 2D -  Agahnim Altar
    src = SrcPtr(0x1b4a);
    uint16 *d = &dung_bg2[dsto];
    for (int j = 0; j < 14; j++) {
      i = src[0], d[0] = i, d[13] = i | 0x4000;
      i = src[14], d[1] = d[2] = i, d[11] = d[12] = i ^ 0x4000;
      i = src[28], d[3] = i, d[10] = i ^ 0x4000;
      i = src[42], d[4] = i, d[9] = i ^ 0x4000;
      i = src[56], d[5] = i, d[8] = i ^ 0x4000;
      i = src[70], d[6] = i, d[7] = i ^ 0x4000;
      src++, d += 64;
    }
    break;
  }
  case 0x2e:  // 2E -  Agahnim Room
    RoomDraw_AgahnimsWindows(dsto);
    break;
  case 0x2f:  // 2F -  Pot
    RoomDraw_SinglePot(src, dst, dsto);
    break;
  case 0x30: // 30 -  ??
    DrawBigGraySegment(0x1212, src, dst, dsto);
    break;
  case 0x31: // 31 -  Big Chest
    i = dung_num_chests_x2;
    dung_chest_locations[i >> 1] = dsto * 2 | 0x8000 | (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x2000);
    if (dung_savegame_state_bits & kChestOpenMasks[i >> 1]) {
      dung_chest_locations[i >> 1] = 0;
      dung_num_chests_x2 = dung_num_bigkey_locks_x2 = i + 2;
      RoomDraw_1x3_rightwards(4, SrcPtr(0x14c4), dst);
    } else {
      dung_num_chests_x2 = dung_num_bigkey_locks_x2 = i + 2;
      RoomDraw_1x3_rightwards(4, SrcPtr(0x14ac), dst);
    }
    break;
  case 0x32:  // 32 -  Big Chest Open
    RoomDraw_1x3_rightwards(4, src, dst);
    break;
  case 0x33:  // 33 -  Stairs Submerged [S](layer)
    if (dung_hdr_tag[1] == 27) {
      if (!(save_dung_info[dungeon_room_index] & 0x100)) {
        dung_hdr_bg2_properties = 0;
        goto stairs_wet;
      }
      CGWSEL_copy = 2;
      CGADSUB_copy = 0x62;
    }
    dung_stairs_table_2[dung_num_inroom_upsouth_stairs_water >> 1] = dsto;
    dung_num_inroom_upsouth_stairs_water += 2;
    RoomDraw_4x4(src, dst);
    break;
  case 0x34: case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
    assert(0);
    break;
  case 0x3a: case 0x3b: // 3A -  Pipe Ride Mouth [S]
    RoomDraw_1x3_rightwards(4, src, dst);
    RoomDraw_1x3_rightwards(4, src + 12, dst + XY(0, 3));
    break;
  case 0x3c: case 0x3d: case 0x5c:  // 3C -  Pipe Ride Mouth [E]
    RoomDraw_Object_Nx4(6, src, dst);
    break;
  case 0x47:  // 47 -  Bomb Floor
    RoomDraw_BombableFloor(src, dst, dsto);
    break;
  case 0x48: case 0x66: case 0x6b: case 0x7a:  // 48 -  Fake Bomb Floor
    RoomDraw_4x4(src, dst);
    break;
  case 0x4b: case 0x76: case 0x77:
    RoomDraw_1x3_rightwards(8, src, dst);
    break;
  case 0x4c:
    RoomDraw_SomeBigDecors(6, SrcPtr(0x1f92), dsto);
    break;
  case 0x4d: case 0x5d:  // 5D -  Forge
    RoomDraw_1x3_rightwards(6, src, dst);
    break;
  case 0x54:
    RoomDraw_FortuneTellerRoom(dsto);
    break;
  case 0x55:
  case 0x5b:  // 5B -  Water Troof
    dst[XY(0, 0)] = src[0];
    dst[XY(1, 0)] = src[1];
    dst[XY(2, 0)] = src[2];
    for (int i = 0; i < 3; i++) {
      dst[XY(0, 1)] = src[3];
      dst[XY(1, 1)] = src[4];
      dst[XY(2, 1)] = src[5];
      dst += XY(0, 1);
    }
    dst[XY(0, 1)] = src[6];
    dst[XY(1, 1)] = src[7];
    dst[XY(2, 1)] = src[8];
    break;

  case 0x5a:  // 5A -  Plate on Table
    RoomDraw_WaterHoldingObject(2, src, dst);
    break;

  case 0x60: case 0x61: // 60 -  Left/Right Warp Door
    RoomDraw_1x3_rightwards(3, src, dst);
    RoomDraw_1x3_rightwards(3, src + 9, dst + XY(0, 3));
    break;

  case 0x62: {  // 62 ??
    src = SrcPtr(0x20f6);
    uint16 *d = &dung_bg1[dsto];
    for (int i = 0; i < 22; i++) {
      d[XY(0, 0)] = src[0];
      d[XY(0, 1)] = src[1];
      d[XY(0, 2)] = src[2];
      d[XY(0, 3)] = src[3];
      d[XY(0, 4)] = src[4];
      d[XY(0, 5)] = src[5];
      d[XY(0, 6)] = src[6];
      d[XY(0, 7)] = src[7];
      d[XY(0, 8)] = src[8];
      d[XY(0, 9)] = src[9];
      d[XY(0, 10)] = src[10];
      d += XY(1, 0), src += 11;
    }
    d -= XY(1, 0) * 22;
    src = SrcPtr(0x22da);
    for (int i = 0; i < 3; i++) {
      d[XY(9, 11)] = src[0];
      d[XY(9, 12)] = src[3];
      d += XY(1, 0), src += 1;
    }
    break;
  }
  case 0x69: case 0x6a: case 0x6e: case 0x6f:  // 69 -  Left Crack Wall
    RoomDraw_Object_Nx4(3, src, dst);
    break;

  case 0x70:  // 70 -  Window Light
    RoomDraw_4x4(src, dst + XY(0, 0));
    RoomDraw_4x4(SrcPtr(0x2376), dst + XY(0, 2));
    RoomDraw_4x4(SrcPtr(0x2396), dst + XY(0, 6));
    break;

  case 0x71:  // 71 -  Floor Light Blind BG2
    if (!(save_dung_info[101] & 0x100))
      return;
    Object_Draw8x8(src, dst);
    break;
  case 0x72:  // 72 -  TrinexxShell  Boss Goo/Shell BG2
    if (dung_savegame_state_bits & 0x8000)
      return;
    RoomDraw_SomeBigDecors(10, src, dsto);
    break;
  case 0x73:  // 73 -  Entire floor is pit, Bg2 Full Mask
    RoomDraw_FloorChunks(SrcPtr(0xe0));
    break;
  case 0x74:  // 74 -  Boss Entrance
    Object_Draw8x8(src, dst);
    break;

  case 0x78:  // Triforce
    RoomDraw_4x4(src, dst);
    RoomDraw_4x4(src + 16, dst + XY(-2, 4));
    RoomDraw_4x4(src + 16, dst + XY(2, 4));
    break;
  case 0x7b: // 7B -  Vitreous Boss?
    RoomDraw_A_Many32x32Blocks(5, src, dst);
    RoomDraw_A_Many32x32Blocks(5, src, dst + XY(0, 4));
    break;
  default:
    assert(0);
  }
}

void RoomBounds_AddA(RoomBounds *r) {
  r->a0 += 0x100;
  r->a1 += 0x100;
}

void RoomBounds_AddB(RoomBounds *r) {
  r->b0 += 0x200;
  r->b1 += 0x200;
}

void RoomBounds_SubB(RoomBounds *r) {
  r->b0 -= 0x200;
  r->b1 -= 0x200;
}

void RoomBounds_SubA(RoomBounds *r) {
  r->a0 -= 0x100;
  r->a1 -= 0x100;
}

void Dungeon_StartInterRoomTrans_Left() {
  assert(submodule_index == 0);
  link_quadrant_x ^= 1;
  Dungeon_AdjustQuadrant();
  RoomBounds_SubA(&room_bounds_x);
  Dung_SaveDataForCurrentRoom();
  DungeonTransition_AdjustCamera_X(link_quadrant_x ^ 1);
  HandleEdgeTransition_AdjustCameraBoundaries(3);
  submodule_index = 1;
  if (link_quadrant_x) {
    RoomBounds_SubB(&room_bounds_x);
    BYTE(dungeon_room_index_prev) = dungeon_room_index;
    if ((link_tile_below & 0xcf) == 0x89) {
      dungeon_room_index = dung_hdr_travel_destinations[3];
      Dungeon_AdjustForTeleportDoors(dungeon_room_index + 1, 0xff);
    } else {
      if ((uint8)dungeon_room_index != (uint8)dungeon_room_index2) {
        BYTE(dungeon_room_index_prev) = dungeon_room_index2;
        Dungeon_AdjustAfterSpiralStairs();
      }
      dungeon_room_index--;
    }
    submodule_index = 2;
    if (room_transitioning_flags & 1) {
      link_is_on_lower_level ^= 1;
      link_is_on_lower_level_mirror = link_is_on_lower_level;
    }
    if (room_transitioning_flags & 2) {
      cur_palace_index_x2 ^= 2;
    }
  }
  room_transitioning_flags = 0;
  quadrant_fullsize_y = (dung_blastwall_flag_y || (kLayoutQuadrantFlags[composite_of_layout_and_quadrant] & (link_quadrant_y ? 8 : 4)) == 0) ? 2 : 0;
}

void Dung_StartInterRoomTrans_Left_Plus() {
  link_x_coord -= 8;
  Dungeon_StartInterRoomTrans_Left();
}

void Dungeon_StartInterRoomTrans_Up() {
  assert(submodule_index == 0);
  link_quadrant_y ^= 2;
  Dungeon_AdjustQuadrant();
  RoomBounds_SubA(&room_bounds_y);
  Dung_SaveDataForCurrentRoom();
  DungeonTransition_AdjustCamera_Y(link_quadrant_y ^ 2);
  HandleEdgeTransition_AdjustCameraBoundaries(1);
  submodule_index = 1;
  if (link_quadrant_y) {
    RoomBounds_SubB(&room_bounds_y);
    BYTE(dungeon_room_index_prev) = dungeon_room_index;
    if (link_tile_below == 0x8e) {
      Dung_HandleExitToOverworld();
      return;
    }

    if (dungeon_room_index == 0) {
      SaveDungeonKeys();
      main_module_index = 25;
      submodule_index = 0;
      subsubmodule_index = 0;
      return;
    }

    if (BYTE(dungeon_room_index2) == BYTE(dungeon_room_index)) {
      BYTE(dungeon_room_index_prev) = BYTE(dungeon_room_index2);
      Dungeon_AdjustAfterSpiralStairs();
    }
    BYTE(dungeon_room_index) -= 0x10;
    submodule_index = 2;
    if (room_transitioning_flags & 1) {
      link_is_on_lower_level ^= 1;
      link_is_on_lower_level_mirror = link_is_on_lower_level;
    }
    if (room_transitioning_flags & 2) {
      cur_palace_index_x2 ^= 2;
    }
  }
  room_transitioning_flags = 0;
  quadrant_fullsize_x = (dung_blastwall_flag_x || (kLayoutQuadrantFlags[composite_of_layout_and_quadrant] & (link_quadrant_x ? 2 : 1)) == 0) ? 2 : 0;
}

void Dungeon_StartInterRoomTrans_Down() {
  assert(submodule_index == 0);
  link_quadrant_y ^= 2;
  Dungeon_AdjustQuadrant();
  RoomBounds_AddA(&room_bounds_y);
  Dung_SaveDataForCurrentRoom();
  DungeonTransition_AdjustCamera_Y(link_quadrant_y);
  HandleEdgeTransition_AdjustCameraBoundaries(0);
  submodule_index = 1;
  if (!link_quadrant_y) {
    RoomBounds_AddB(&room_bounds_y);
    BYTE(dungeon_room_index_prev) = dungeon_room_index;
    if (link_tile_below == 0x8e) {
      Dung_HandleExitToOverworld();
      return;
    }

    if ((uint8)dungeon_room_index != (uint8)dungeon_room_index2) {
      BYTE(dungeon_room_index_prev) = dungeon_room_index2;
      Dungeon_AdjustAfterSpiralStairs();
    }
    BYTE(dungeon_room_index) += 16;
    submodule_index = 2;
    if (room_transitioning_flags & 1) {
      link_is_on_lower_level ^= 1;
      link_is_on_lower_level_mirror = link_is_on_lower_level;
    }
    if (room_transitioning_flags & 2) {
      cur_palace_index_x2 ^= 2;
    }
  }
  room_transitioning_flags = 0;
  quadrant_fullsize_x = (dung_blastwall_flag_x || (kLayoutQuadrantFlags[composite_of_layout_and_quadrant] & (link_quadrant_x ? 2 : 1)) == 0) ? 2 : 0;
}

void Dungeon_Store2x2(uint16 pos, uint16 t0, uint16 t1, uint16 t2, uint16 t3, uint8 attr) {
  uint16 *dst = &vram_upload_data[vram_upload_offset >> 1];
  dst[2] = t0;
  dst[5] = t1;
  dst[8] = t2;
  dst[11] = t3;
  overworld_tileattr[pos] = t0;
  overworld_tileattr[pos + 64] = t1;
  overworld_tileattr[pos + 1] = t2;
  overworld_tileattr[pos + 65] = t3;

  dung_bg2_attr_table[pos] = attr;
  dung_bg2_attr_table[pos + 64] = attr;
  dung_bg2_attr_table[pos + 1] = attr;
  dung_bg2_attr_table[pos + 65] = attr;

  dst[0] = Dungeon_MapVramAddr(pos + 0);
  dst[3] = Dungeon_MapVramAddr(pos + 64);
  dst[6] = Dungeon_MapVramAddr(pos + 1);
  dst[9] = Dungeon_MapVramAddr(pos + 65);

  dst[1] = 0x100;
  dst[4] = 0x100;
  dst[7] = 0x100;
  dst[10] = 0x100;

  dst[12] = 0xffff;

  vram_upload_offset += 24;
  nmi_load_bg_from_vram = 1;
}

uint16 Dungeon_MapVramAddr(uint16 pos) {
  pos *= 2;
  return swap16(((pos & 0x40) << 4) | ((pos & 0x303f) >> 1) | ((pos & 0xf80) >> 2));
}

uint16 Dungeon_MapVramAddrNoSwap(uint16 pos) {
  pos *= 2;
  return ((pos & 0x40) << 4) | ((pos & 0x303f) >> 1) | ((pos & 0xf80) >> 2);
}

void Door_Up_EntranceDoor(uint16 dsto) {
  // NOTE: This don't pass the right value to RoomDraw_FlagDoorsAndGetFinalType
  assert(0);
}

void Door_Down_EntranceDoor(uint16 dsto) {
  // NOTE: This don't pass the right value to RoomDraw_FlagDoorsAndGetFinalType
  assert(0);
}

void Door_Left_EntranceDoor(uint16 dsto) {
  // NOTE: This don't pass the right value to RoomDraw_FlagDoorsAndGetFinalType
  assert(0);
}

void Door_Right_EntranceDoor(uint16 dsto) {
  // NOTE: This don't pass the right value to RoomDraw_FlagDoorsAndGetFinalType
  assert(0);
}

void Door_Draw_Helper4(uint8 door_type, uint16 dsto) {
  int t = RoomDraw_FlagDoorsAndGetFinalType(1, door_type, dsto), new_type;
  if (t & 0x100)
    return;

  if ((new_type = kDoorType_Regular, t == kDoorType_1E || t == kDoorType_36) ||
      (new_type = kDoorType_ShuttersTwoWay, t == kDoorType_38)) {
    int i = (dung_cur_door_idx >> 1) - 1;
    door_type_and_slot[i] = (i << 8) | new_type;
    t = new_type;
  }

  const uint16 *src = SrcPtr(kDoorTypeSrcData2[t >> 1]);
  uint16 *dst = DstoPtr(dsto);
  for (int i = 0; i < 4; i++) {
    dst[XY(0, 1)] = src[0];
    dst[XY(0, 2)] = src[1];
    dst[XY(0, 3)] = src[2];
    dst++, src += 3;
  }
}

const uint16 *GetRoomDoorInfo(int room) {
  return (uint16 *)(kDungeonRoom + kDungeonRoomDoorOffs[room]);
}

const uint8 *GetRoomHeaderPtr(int room) {
  return kDungeonRoomHeaders + kDungeonRoomHeadersOffs[room];
}

const uint8 *GetDefaultRoomLayout(int i) {
  return kDungeonRoomDefault + kDungeonRoomDefaultOffs[i];
}

const uint8 *GetDungeonRoomLayout(int i) {
  return kDungeonRoom + kDungeonRoomOffs[i];
}

static inline void WriteAttr1(int j, uint16 attr) {
  dung_bg1_attr_table[j + 0] = attr;
  dung_bg1_attr_table[j + 1] = attr >> 8;
}

static inline void WriteAttr2(int j, uint16 attr) {
  dung_bg2_attr_table[j + 0] = attr;
  dung_bg2_attr_table[j + 1] = attr >> 8;
}

void Dung_TagRoutine_0x22_0x3B(int k, uint8 j) {
  if (dung_savegame_state_bits & 0x100) {
    dung_hdr_tag[k] = 0;
    dung_overlay_to_load = j;
    dung_load_ptr_offs = 0;
    subsubmodule_index = 0;
    sound_effect_2 = 0x1b;
    submodule_index = 3;
  }
}

void Sprite_HandlePushedBlocks_One(int i) {
  Oam_AllocateFromRegionB(4);

  int y = (uint8)pushedblocks_y_lo[i] | (uint8)pushedblocks_y_hi[i] << 8;
  int x = (uint8)pushedblocks_x_lo[i] | (uint8)pushedblocks_x_hi[i] << 8;

  y -= BG2VOFS_copy2 + 1;
  x -= BG2HOFS_copy2;

  if (pushedblocks_some_index < 3) {
    uint8 *oam = &g_ram[oam_cur_ptr];
    oam[0] = x;
    oam[1] = y;
    oam[2] = 12;
    oam[3] = 0x20;
    g_ram[oam_ext_cur_ptr] = 2;
  }
}

void Object_Draw_DoorLeft_3x4(uint16 src, int door) {
  const uint16 *s = SrcPtr(src);
  uint16 *dst = &dung_bg2[dung_door_tilemap_address[door] >> 1];
  for (int i = 0; i < 3; i++) {
    dst[XY(0, 0)] = s[0];
    dst[XY(0, 1)] = s[1];
    dst[XY(0, 2)] = s[2];
    dst[XY(0, 3)] = s[3];
    dst += 1, s += 4;
  }
}

void Object_Draw_DoorRight_3x4(uint16 src, int door) {
  const uint16 *s = SrcPtr(src);
  uint16 *dst = &dung_bg2[dung_door_tilemap_address[door] >> 1];
  for (int i = 0; i < 3; i++) {
    dst[XY(1, 0)] = s[0];
    dst[XY(1, 1)] = s[1];
    dst[XY(1, 2)] = s[2];
    dst[XY(1, 3)] = s[3];
    dst += 1, s += 4;
  }
}

void Dungeon_OpeningLockedDoor_Combined(bool skip_anim) {
  uint8 ctr = 2;
  int m, k, dma_ptr;

  if (skip_anim) {
    door_animation_step_indicator = 16;
    goto step12;
  }

  door_animation_step_indicator++;
  if (door_animation_step_indicator != 4) {
    if (door_animation_step_indicator != 12)
      goto middle;
step12:
    m = kUpperBitmasks[dung_bg2_attr_table[dung_cur_door_pos] & 7];
    dung_door_opened_incl_adjacent |= m;
    dung_door_opened |= m;
    ctr = 4;
  }
  door_open_closed_counter = ctr;

  k = dung_bg2_attr_table[dung_cur_door_pos] & 0xf;
  dma_ptr = DrawDoorOpening_Step1(k, 0);
  Dungeon_PrepOverlayDma_nextPrep(dma_ptr, dung_door_tilemap_address[k]);
  sound_effect_2 = 21;
  nmi_copy_packets_flag = 1;

middle:
  if (door_animation_step_indicator == 16) {
    Dungeon_LoadToggleDoorAttr_OtherEntry(dung_bg2_attr_table[dung_cur_door_pos] & 0xf);
    if (dung_bg2_attr_table[dung_cur_door_pos] >= 0xf0) {
      k = dung_bg2_attr_table[dung_cur_door_pos] & 0xf;
      uint8 door_type = door_type_and_slot[k];
      if (door_type >= kDoorType_StairMaskLocked0 && door_type <= kDoorType_StairMaskLocked3)
        DrawCompletelyOpenDoor();
    }
    submodule_index = 0;
  }
}

const DungPalInfo *GetDungPalInfo(int idx) {
  return &kDungPalinfos[idx];
}

uint16 Dungeon_GetTeleMsg(int room) {
  return kDungeonRoomTeleMsg[room];
}

uint8 GetEntranceMusicTrack(int entrance) {
  return kEntranceData_musicTrack[entrance];
}

bool Dungeon_IsPitThatHurtsPlayer() {
  for (int i = kDungeonPitsHurtPlayer_SIZE / 2 - 1; i >= 0; i--) {
    if (kDungeonPitsHurtPlayer[i] == dungeon_room_index)
      return true;
  }
  return false;

}

void Dungeon_PrepareNextRoomQuadrantUpload() {  // 80913f
  int ofs = (overworld_screen_transition & 0xf) + dung_cur_quadrant_upload;
  uint16 *src = &dung_bg2[kUploadBgSrcs[ofs] / 2];
  int p = 0;
  do {
    UploadVram_32x32 *d = (UploadVram_32x32 *)&g_ram[0x1000 + p * 2];
    do {
      d->row[0].col[0] = src[XY(0, 0)];
      d->row[0].col[1] = src[XY(1, 0)];
      d->row[1].col[0] = src[XY(0, 1)];
      d->row[1].col[1] = src[XY(1, 1)];
      d->row[2].col[0] = src[XY(0, 2)];
      d->row[2].col[1] = src[XY(1, 2)];
      d->row[3].col[0] = src[XY(0, 3)];
      d->row[3].col[1] = src[XY(1, 3)];
      d = (UploadVram_32x32 *)((uint16 *)d + 2);
      src += 2, p += 2;
    } while (p & 0x1f);
    src += 224;
    p += 128 - 32;
  } while (p != 0x400);
  dung_cur_quadrant_upload += 4;
  BYTE(nmi_load_target_addr) = kUploadBgDsts[ofs];
  nmi_subroutine_index = 1;
  nmi_disable_core_updates = 1;
}

void WaterFlood_BuildOneQuadrantForVRAM() {  // 8091c4

  // It never seems to be 25 here, so skip updating water stuff
  assert(dung_hdr_tag[0] != 25);
  TileMapPrep_NotWaterOnTag();
}

void TileMapPrep_NotWaterOnTag() {  // 8091d3
  int ofs = (overworld_screen_transition & 0xf) + dung_cur_quadrant_upload;
  uint16 *src = &dung_bg1[kUploadBgSrcs[ofs] / 2];
  int p = 0;
  do {
    UploadVram_32x32 *d = (UploadVram_32x32 *)&g_ram[0x1000 + p * 2];
    do {
      d->row[0].col[0] = src[XY(0, 0)];
      d->row[0].col[1] = src[XY(1, 0)];
      d->row[1].col[0] = src[XY(0, 1)];
      d->row[1].col[1] = src[XY(1, 1)];
      d->row[2].col[0] = src[XY(0, 2)];
      d->row[2].col[1] = src[XY(1, 2)];
      d->row[3].col[0] = src[XY(0, 3)];
      d->row[3].col[1] = src[XY(1, 3)];
      d = (UploadVram_32x32 *)((uint16 *)d + 2);
      src += 2, p += 2;
    } while (p & 0x1f);
    src += 224;
    p += 128 - 32;
  } while (p != 0x400);
  BYTE(nmi_load_target_addr) = kUploadBgDsts[ofs] + 0x10;
  nmi_subroutine_index = 1;
  nmi_disable_core_updates = 1;
}

void OrientLampLightCone() {  // 80f567
  static const uint16 kOrientLampBgTab0[] = { 0, 256, 0, 256 };
  static const uint16 kOrientLampBgTab1[] = { 0, 0, 256, 256 };
  static const int16 kOrientLampBgTab2[] = { 52, -2, 56, 6 };
  static const int16 kOrientLampBgTab3[] = { 64, 64, 82, -176 };
  static const int16 kOrientLampBgTab4[] = { 128, 384, 160, 160 };

  if (!hdr_dungeon_dark_with_lantern || submodule_index == 20)
    return;

  uint8 a = link_direction_facing >> 1, idx = a;
  if (is_standing_in_doorway) {
    idx = (is_standing_in_doorway & 0xfe);
    if (idx) {
      if (a < 2)
        idx += ((uint8)(link_x_coord + 8) >= 0x80);
      else
        idx = a;
    } else {
      if (a >= 2)
        idx += ((uint8)link_y_coord >= 0x80);
      else
        idx = a;
    }
  }

  if (idx < 2) {
    BG1HOFS_copy2 = BG2HOFS_copy2 - (link_x_coord - 0x77) + kOrientLampBgTab0[idx];
    uint16 t = BG2VOFS_copy2 - (link_y_coord - 0x58) + kOrientLampBgTab1[idx] + kOrientLampBgTab2[idx] + kOrientLampBgTab3[idx];
    if ((int16)t < 0) t = 0;
    if (t > kOrientLampBgTab4[idx]) t = kOrientLampBgTab4[idx];
    BG1VOFS_copy2 = t - kOrientLampBgTab3[idx];
  } else {
    BG1VOFS_copy2 = BG2VOFS_copy2 - (link_y_coord - 0x72) + kOrientLampBgTab1[idx];
    uint16 t = BG2HOFS_copy2 - (link_x_coord - 0x58) + kOrientLampBgTab0[idx] + kOrientLampBgTab2[idx] + kOrientLampBgTab3[idx];
    if ((int16)t < 0) t = 0;
    if (t > kOrientLampBgTab4[idx]) t = kOrientLampBgTab4[idx];
    BG1HOFS_copy2 = t - kOrientLampBgTab3[idx];
  }
}

void PrepareDungeonExitFromBossFight() {  // 80f945
  SavePalaceDeaths();
  SaveDungeonKeys();
  dung_savegame_state_bits |= 0x8000;
  Dungeon_FlagRoomData_Quadrants();

  int j = FindInByteArray(kDungeonExit_From, BYTE(dungeon_room_index), countof(kDungeonExit_From));
  assert(j >= 0);
  BYTE(dungeon_room_index) = kDungeonExit_To[j];
  if (BYTE(dungeon_room_index) == 0x20) {
    sram_progress_indicator = 3;
    save_ow_event_info[2] |= 0x20;
    savegame_is_darkworld ^= 0x40;
    Sprite_LoadGraphicsProperties_light_world_only();
    Ancilla_TerminateSelectInteractives(0);
    link_disable_sprite_damage = 0;
    button_b_frames = 0;
    button_mask_b_y = 0;
    link_force_hold_sword_up = 0;
    flag_is_link_immobilized = 1;
    saved_module_for_menu = 8;
    main_module_index = 21;
    submodule_index = 0;
    subsubmodule_index = 0;
  } else if (BYTE(dungeon_room_index) == 0xd) {
    main_module_index = 24;
    submodule_index = 0;
    overworld_map_state = 0;
    CGADSUB_copy = 0x20;
  } else {
    if (j >= 3) {
      music_control = 0xf1;
      music_unk1 = 0xf1;
      main_module_index = 22;
    } else {
      main_module_index = 19;
    }
    saved_module_for_menu = 8;
    submodule_index = 0;
    subsubmodule_index = 0;
  }
}

void SavePalaceDeaths() {  // 80f9dd
  int j = BYTE(cur_palace_index_x2);
  deaths_per_palace[j >> 1] = death_save_counter;
  if (j != 8)
    death_save_counter = 0;
}

void Dungeon_LoadRoom() {  // 81873a
  Dungeon_LoadHeader();
  dung_unk6 = 0;

  dung_hdr_collision_2_mirror = dung_hdr_collision_2;
  dung_hdr_collision_2_mirror_PADDING = dung_hdr_tag[0];
  dung_some_subpixel[0] = 0x30;
  dung_some_subpixel[1] = 0xff;
  dung_floor_move_flags = 0;
  word_7E0420 = 0;
  dung_floor_x_vel = dung_floor_y_vel = 0;
  dung_floor_x_offs = dung_floor_y_offs = 0;
  invisible_door_dir_and_index_x2 = 0xffff;
  dung_blastwall_flag_x = dung_blastwall_flag_y = 0;
  dung_unk_blast_walls_2 = dung_unk_blast_walls_3 = 0;
  water_hdma_var5 = 0;
  dung_num_toggle_floor = 0;
  dung_num_toggle_palace = 0;
  dung_unk2 = 0;
  dung_cur_quadrant_upload = 0;
  dung_num_inter_room_upnorth_stairs = 0;
  dung_num_inter_room_southdown_stairs = 0;
  dung_num_inroom_upnorth_stairs = 0;
  dung_num_inroom_southdown_stairs = 0;
  dung_num_interpseudo_upnorth_stairs = 0;
  dung_num_inroom_upnorth_stairs_water = 0;
  dung_num_activated_water_ladders = 0;
  dung_num_water_ladders = 0;
  dung_some_stairs_unk4 = 0;
  dung_num_stairs_1 = 0;
  dung_num_stairs_2 = 0;
  dung_num_stairs_wet = 0;
  dung_num_inroom_upsouth_stairs_water = 0;
  dung_num_wall_upnorth_spiral_stairs = 0;
  dung_num_wall_downnorth_spiral_stairs = 0;
  dung_num_wall_upnorth_spiral_stairs_2 = 0;
  dung_num_wall_downnorth_spiral_stairs_2 = 0;
  dung_num_inter_room_upnorth_straight_stairs = 0;
  dung_num_inter_room_upsouth_straight_stairs = 0;
  dung_num_inter_room_downnorth_straight_stairs = 0;
  dung_num_inter_room_downsouth_straight_stairs = 0;
  dung_exit_door_addresses[0] = 0;
  dung_exit_door_addresses[1] = 0;
  dung_exit_door_addresses[2] = 0;
  dung_exit_door_addresses[3] = 0;
  dung_exit_door_count = 0;
  dung_door_switch_triggered = 0;
  dung_num_star_shaped_switches = 0;
  dung_misc_objs_index = 0;
  dung_index_of_torches = 0;
  dung_num_chests_x2 = 0;
  dung_num_bigkey_locks_x2 = 0;
  dung_unk5 = 0;
  dung_cur_door_idx = 0;

  for (int i = 0; i < 16; i++) {
    dung_door_tilemap_address[i] = 0;
    door_type_and_slot[i] = 0;
    dung_door_direction[i] = 0;
    dung_torch_timers[i] = 0;
    dung_replacement_tile_state[i] = 0;
    dung_object_pos_in_objdata[i] = 0;
    dung_object_tilemap_pos[i] = 0;
  }

  const uint8 *cur_p0 = GetDungeonRoomLayout(dungeon_room_index);
  dung_load_ptr_offs = 0;
  RoomDraw_DrawFloors(cur_p0);

  uint16 old_offs = dung_load_ptr_offs;
  dung_layout_and_starting_quadrant = cur_p0[dung_load_ptr_offs];

  const uint8 *cur_p1 = GetDefaultRoomLayout(dung_layout_and_starting_quadrant >> 2);

  dung_load_ptr_offs = 0;
  RoomDraw_DrawAllObjects(cur_p1);

  dung_load_ptr_offs = old_offs + 1;

  RoomDraw_DrawAllObjects(cur_p0);  // Draw Layer 1 objects to BG2
  dung_load_ptr_offs += 2;

  memcpy(&dung_line_ptrs_row0, kDungeon_DrawObjectOffsets_BG2, 33);
  RoomDraw_DrawAllObjects(cur_p0);  // Draw Layer 2 objects to BG2
  dung_load_ptr_offs += 2;

  memcpy(&dung_line_ptrs_row0, kDungeon_DrawObjectOffsets_BG1, 33);
  RoomDraw_DrawAllObjects(cur_p0);  // Draw Layer 3 objects to BG2

  for (dung_load_ptr_offs = 0; dung_load_ptr_offs != 0x18C; dung_load_ptr_offs += 4) {
    MovableBlockData m = movable_block_datas[dung_load_ptr_offs >> 2];
    if (m.room == dungeon_room_index)
      DrawObjects_PushableBlock(m.tilemap, dung_load_ptr_offs);
  }

  uint16 t;

  dung_index_of_torches = dung_index_of_torches_start = dung_misc_objs_index;
  int i = 0;
  do {
    if (dung_torch_data[i >> 1] == dungeon_room_index) {
      i += 2;

      do {
        t = dung_torch_data[i >> 1];
        i += 2;
        DrawObjects_LightableTorch(t, i - 2);
      } while (dung_torch_data[i >> 1] != 0xffff);
      break;
    }
    i += 2;
    do {
      t = dung_torch_data[i >> 1];
      i += 2;
    } while (t != 0xffff);
  } while (i != 0x120);

  dung_load_ptr_offs = 0x120;
}

void RoomDraw_DrawAllObjects(const uint8 *level_data) {  // 8188e4
  for (;;) {
    dung_draw_width_indicator = dung_draw_height_indicator = 0;
    uint16 d = WORD(level_data[dung_load_ptr_offs]);
    if (d == 0xffff)
      return;
    if (d == 0xfff0)
      break;
    RoomData_DrawObject(d, level_data);
  }
  for (;;) {
    dung_load_ptr_offs += 2;
    uint16 d = WORD(level_data[dung_load_ptr_offs]);
    if (d == 0xffff)
      return;
    RoomData_DrawObject_Door(d);
  }
}

void RoomData_DrawObject_Door(uint16 a) {  // 818916
  uint8 door_type = a >> 8;
  uint8 position = a >> 4 & 0xf;

  switch (a & 3) {
  case 0: RoomDraw_Door_North(door_type, position); break;
  case 1: RoomDraw_Door_South(door_type, position); break;
  case 2: RoomDraw_Door_West(door_type, position); break;
  case 3: RoomDraw_Door_East(door_type, position); break;
  }
}

void RoomData_DrawObject(uint16 r0, const uint8 *level_data) {  // 81893c
  uint16 offs = dung_load_ptr_offs;
  uint8 idx = level_data[offs + 2];
  dung_load_ptr_offs = offs + 3;

  if ((r0 & 0xfc) != 0xfc) {
    dung_draw_width_indicator = (r0 & 3);
    dung_draw_height_indicator = (r0 >> 8) & 3;
    uint8 x = (uint8)r0 >> 2;
    uint8 y = (r0 >> 10);
    uint16 dst = y * 64 + x;
    if (idx < 0xf8) {
      LoadType1ObjectSubtype1(idx, DstoPtr(dst), dst);
    } else {
      idx = (idx & 7) << 4 | ((r0 >> 8) & 3) << 2 | (r0 & 3);
      LoadType1ObjectSubtype3(idx, DstoPtr(dst), dst);
    }
  } else {
    uint8 x = (r0 & 3) << 4 | (r0 >> 12) & 0xf;
    uint8 y = ((r0 >> 8) & 0xf) << 2 | (idx >> 6);
    uint16 dst = y * 64 + x;
    LoadType1ObjectSubtype2(idx & 0x3f, DstoPtr(dst), dst);
  }
}

void RoomDraw_DrawFloors(const uint8 *level_data) {  // 8189dc
  memcpy(&dung_line_ptrs_row0, kDungeon_DrawObjectOffsets_BG2, 33);
  uint8 ft = level_data[dung_load_ptr_offs++];
  dung_floor_1_filler_tiles = ft & 0xf0;
  RoomDraw_FloorChunks(SrcPtr(dung_floor_1_filler_tiles));
  memcpy(&dung_line_ptrs_row0, kDungeon_DrawObjectOffsets_BG1, 33);
  dung_floor_2_filler_tiles = (ft & 0xf) << 4;
  RoomDraw_FloorChunks(SrcPtr(dung_floor_2_filler_tiles));
}

void RoomDraw_FloorChunks(const uint16 *src) {  // 818a1f
  static const uint16 kDungeon_QuadrantOffsets[] = { 0x0, 0x40, 0x1000, 0x1040 };
  for (int i = 0; i < 4; i++) {
    uint16 *dst = DstoPtr(kDungeon_QuadrantOffsets[i] / 2);
    for (int j = 0; j < 8; j++) {
      RoomDraw_A_Many32x32Blocks(8, src, dst);
      dst += XY(0, 4);
    }
  }
}

void RoomDraw_A_Many32x32Blocks(int n, const uint16 *src, uint16 *dst) {  // 818a44
  do {
    // draw 4x2 twice
    for (int i = 0; i < 2; i++) {
      dst[XY(0, 0)] = src[0];
      dst[XY(1, 0)] = src[1];
      dst[XY(2, 0)] = src[2];
      dst[XY(3, 0)] = src[3];
      dst[XY(0, 1)] = src[4];
      dst[XY(1, 1)] = src[5];
      dst[XY(2, 1)] = src[6];
      dst[XY(3, 1)] = src[7];
      dst += XY(0, 2);
    }
    dst += XY(4, -4);
  } while (--n);
}

void RoomDraw_1x3_rightwards(int n, const uint16 *src, uint16 *dst) {  // 818d80
  do {
    dst[XY(0, 0)] = src[0];
    dst[XY(0, 1)] = src[1];
    dst[XY(0, 2)] = src[2];
    dst++, src += 3;
  } while (--n);
}

bool RoomDraw_CheckIfWallIsMoved() {  // 819298
  dung_some_subpixel[0] = dung_some_subpixel[1] = 0;
  dung_floor_move_flags = 0;
  int i;
  if ((i = 0, dung_hdr_tag[0] >= 0x1c && dung_hdr_tag[0] < 0x20) ||
      (i = 1, dung_hdr_tag[1] >= 0x1c && dung_hdr_tag[1] < 0x20)) {
    if (dung_savegame_state_bits & (0x1000 >> i)) {
      dung_hdr_collision = 0;
      dung_hdr_tag[i] = 0;
      dung_hdr_bg2_properties = 0;
      return false;
    }
  }
  return true;
}

void MovingWall_FillReplacementBuffer(int dsto) {  // 8192d1
  for (int i = 0; i < 64; i++)
    moving_wall_arr1[i] = 0x1ec;
  moving_wall_var1 = (dsto & 0x1f) | (dsto & 0x20 ? 0x400 : 0) | 0x1000;
}

void Object_Table_Helper(const uint16 *src, uint16 *dst) {  // 8193f7
  int n = dung_draw_width_indicator;
  dst[0] = src[0];
  do {
    dst[1] = src[1];
    dst[2] = src[2];
    dst += 2;
  } while (--n);
  dst[1] = src[3];
}

void DrawWaterThing(uint16 *dst, const uint16 *src) {  // 8195a0
  for (int i = 3; i >= 0; i--) {
    dst[0] = src[0];
    dst[1] = src[1];
    dst[2] = src[2];
    dst[3] = src[3];
    dst += XY(0, 1), src += 4;
  }
}

void RoomDraw_4x4(const uint16 *src, uint16 *dst) {  // 8197ed
  RoomDraw_Object_Nx4(4, src, dst);
}

void RoomDraw_Object_Nx4(int n, const uint16 *src, uint16 *dst) {  // 8197f0
  do {
    dst[XY(0, 0)] = src[0];
    dst[XY(0, 1)] = src[1];
    dst[XY(0, 2)] = src[2];
    dst[XY(0, 3)] = src[3];
    src += 4;
    dst += XY(1, 0);
  } while (--n);
}

void Object_DrawNx4_BothBgs(int n, const uint16 *src, int dsto) {  // 819819
  do {
    dung_bg1[dsto + XY(0, 0)] = dung_bg2[dsto + XY(0, 0)] = src[0];
    dung_bg1[dsto + XY(0, 1)] = dung_bg2[dsto + XY(0, 1)] = src[1];
    dung_bg1[dsto + XY(0, 2)] = dung_bg2[dsto + XY(0, 2)] = src[2];
    dung_bg1[dsto + XY(0, 3)] = dung_bg2[dsto + XY(0, 3)] = src[3];
    src += 4, dsto += 1;
  } while (--n);
}

void RoomDraw_Rightwards2x2(const uint16 *src, uint16 *dst) {  // 819895
  dst[XY(0, 0)] = src[0];
  dst[XY(0, 1)] = src[1];
  dst[XY(1, 0)] = src[2];
  dst[XY(1, 1)] = src[3];
}

void Object_Draw_3x2(const uint16 *src, uint16 *dst) {  // 819d04
  dst[XY(0, 0)] = src[0];
  dst[XY(1, 0)] = src[1];
  dst[XY(2, 0)] = src[2];
  dst[XY(0, 1)] = src[3];
  dst[XY(1, 1)] = src[4];
  dst[XY(2, 1)] = src[5];
}

void RoomDraw_WaterHoldingObject(int n, const uint16 *src, uint16 *dst) {  // 819d6f
  do {
    dst[0] = src[0];
    dst[1] = src[1];
    dst[2] = src[2];
    dst[3] = src[3];
    src += 4;
    dst += XY(0, 1);
  } while (--n);
}

void RoomDraw_SomeBigDecors(int n, const uint16 *src, uint16 dsto) {  // 819da2
  uint16 *dst = &dung_bg2[dsto | (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x1000)];
  for (int i = 0; i < 8; i++) {
    for (int j = 0; j < n; j++)
      dst[j] = src[j];
    dst += 64, src += n;
  }
}

void RoomDraw_SingleLampCone(uint16 a, uint16 y) {  // 819e06
  const uint16 *src = SrcPtr(y);
  uint16 *dst = &dung_bg1[a / 2];
  for (int i = 0; i < 12; i++) {
    for (int j = 0; j < 12; j++)
      *dst++ = *src++;
    dst += 64 - 12;
  }
}

void RoomDraw_AgahnimsWindows(uint16 dsto) {  // 819ea3
  const uint16 *src;

  uint16 *d = &dung_bg2[dsto];
  src = SrcPtr(0x1BF2);
  for (int i = 0; i < 6; i++) {
    d[XY(7, 4)] = d[XY(13, 4)] = d[XY(19, 4)] = src[0];
    d[XY(7, 5)] = d[XY(13, 5)] = d[XY(19, 5)] = src[1];
    d[XY(7, 6)] = d[XY(13, 6)] = d[XY(19, 6)] = src[2];
    d[XY(7, 7)] = d[XY(13, 7)] = d[XY(19, 7)] = src[3];
    src += 4, d += XY(1, 0);
  }
  d -= 6;

  src = SrcPtr(0x1c22);
  for (int i = 0; i < 5; i++) {
    int j = src[0];
    d[XY(2, 10)] = d[XY(3, 9)] = d[XY(4, 8)] = d[XY(5, 7)] = d[XY(6, 6)] = d[XY(7, 5)] = d[XY(8, 4)] = j;
    d[XY(23, 4)] = d[XY(24, 5)] = d[XY(25, 6)] = d[XY(26, 7)] = d[XY(27, 8)] = d[XY(28, 9)] = d[XY(29, 10)] = j | 0x4000;
    src++, d += XY(0, 1);
  }
  d -= XY(0, 1) * 5;

  src = SrcPtr(0x1c2c);
  for (int i = 0; i < 6; i++) {
    int j = src[0];
    d[XY(2, 11)] = d[XY(2, 17)] = d[XY(2, 23)] = j;
    d[XY(29, 11)] = d[XY(29, 17)] = d[XY(29, 23)] = j | 0x4000;
    j = src[1];
    d[XY(3, 11)] = d[XY(3, 17)] = d[XY(3, 23)] = j;
    d[XY(28, 11)] = d[XY(28, 17)] = d[XY(28, 23)] = j | 0x4000;
    j = src[2];
    d[XY(4, 11)] = d[XY(4, 17)] = d[XY(4, 23)] = j;
    d[XY(27, 11)] = d[XY(27, 17)] = d[XY(27, 23)] = j | 0x4000;
    j = src[3];
    d[XY(5, 11)] = d[XY(5, 17)] = d[XY(5, 23)] = j;
    d[XY(26, 11)] = d[XY(26, 17)] = d[XY(26, 23)] = j | 0x4000;
    src += 4, d += XY(0, 1);
  }
  d -= XY(0, 1) * 6;

  src = SrcPtr(0x1c5c);
  for (int i = 0; i < 6; i++) {
    d[XY(12, 9)] = d[XY(18, 9)] = src[0];
    d[XY(12, 10)] = d[XY(18, 10)] = src[6];
    src += 1, d += XY(1, 0);
  }
  d -= XY(1, 0) * 6;

  src = SrcPtr(0x1c74);
  for (int i = 0; i < 6; i++) {
    d[XY(7, 14)] = d[XY(7, 20)] = src[0];
    d[XY(8, 14)] = d[XY(8, 20)] = src[1];
    src += 2, d += XY(0, 1);
  }
  d -= XY(0, 1) * 6;

  src = SrcPtr(0x1c8c);
  for (int i = 0; i < 5; i++) {
    d[XY(7, 9)] = src[0];
    d[XY(7, 10)] = src[1];
    d[XY(7, 11)] = src[2];
    d[XY(7, 12)] = src[3];
    d[XY(7, 13)] = src[4];
    src += 5, d += XY(1, 0);
  }
  d -= XY(1, 0) * 5;

  for (int i = 0; i < 4; i++) {
    d[XY(14, 28)] |= 0x2000;
    d[XY(14, 29)] |= 0x2000;
    d += XY(1, 0);
  }
}

void RoomDraw_FortuneTellerRoom(uint16 dsto) {  // 81a095
  const uint16 *src = SrcPtr(0x202e), *src_org = src;
  uint16 *d = &dung_bg2[dsto];
  int j;

  for (int i = 0; i < 6; i++) {
    d[XY(1, 0)] =
      d[XY(2, 0)] =
      d[XY(1, 1)] =
      d[XY(2, 1)] = src[0];
    d[XY(1, 2)] = (j = src[1]);
    d[XY(2, 2)] = j | 0x4000;
    d += XY(2, 0);
  }
  d -= XY(2, 0) * 6;

  for (int i = 0; i < 3; i++) {
    d[XY(0, 3)] = d[XY(2, 3)] = d[XY(10, 3)] = d[XY(12, 3)] = (j = src[2]);
    d[XY(1, 3)] = d[XY(3, 3)] = d[XY(11, 3)] = d[XY(13, 3)] = j | 0x4000;
    d[XY(4, 3)] = d[XY(6, 3)] = d[XY(8, 3)] = (j = src[5]);
    d[XY(5, 3)] = d[XY(7, 3)] = d[XY(9, 3)] = j | 0x4000;
    src++, d += XY(0, 1);
  }
  d -= XY(0, 1) * 3;

  d[XY(0, 0)] = d[XY(0, 1)] = (j = src[5]);
  d[XY(13, 0)] = d[XY(13, 1)] = j | 0x4000;
  d[XY(0, 2)] = (j = src[6]);
  d[XY(13, 2)] = j | 0x4000;

  src = src_org;
  for (int i = 0; i < 4; i++) {
    d[XY(3, 10)] = (j = src[10]);
    d[XY(10, 10)] = j ^ 0x4000;
    d[XY(4, 10)] = (j = src[14]);
    d[XY(9, 10)] = j ^ 0x4000;
    d[XY(5, 10)] = (j = src[18]);
    d[XY(8, 10)] = j ^ 0x4000;
    d[XY(6, 10)] = (j = src[22]);
    d[XY(7, 10)] = j ^ 0x4000;
    src++, d += XY(0, 1);
  }
}

void Object_Draw8x8(const uint16 *src, uint16 *dst) {  // 81a7dc
  RoomDraw_4x4(src, dst + XY(0, 0));
  RoomDraw_4x4(src + 16, dst + XY(4, 0));
  RoomDraw_4x4(src + 32, dst + XY(0, 4));
  RoomDraw_4x4(src + 48, dst + XY(4, 4));
}

void RoomDraw_Door_North(int type, int pos_enum) {  // 81a81c
  uint16 dsto = kDoorPositionToTilemapOffs_Up[pos_enum] / 2;
  if (type == kDoorType_LgExplosion)
    RoomDraw_Door_ExplodingWall(pos_enum);
  else if (type == kDoorType_PlayerBgChange)
    RoomDraw_MarkLayerToggleDoor(dsto - 0xfe / 2);
  else if (type == kDoorType_Slashable)
    RoomDraw_NorthCurtainDoor(dsto);
  else if (type == kDoorType_EntranceDoor)
    Door_Up_EntranceDoor(dsto);
  else if (type == kDoorType_ThroneRoom)
    RoomDraw_MarkDungeonToggleDoor(dsto - 0xfe / 2);
  else if (type == kDoorType_Regular2) {
    RoomDraw_MakeDoorPartsHighPriority_Y(dsto & (0xF07F / 2));
    RoomDraw_NormalRangedDoors_North(type, dsto, pos_enum);
  } else if (type == kDoorType_ExitToOw) {
    dung_exit_door_addresses[dung_exit_door_count >> 1] = dsto * 2;
    dung_exit_door_count += 2;
  } else if (type == kDoorType_WaterfallTunnel) {
    RoomDraw_NormalRangedDoors_North(type, dsto, pos_enum);
    Door_PrioritizeCurDoor();
  } else if (type >= kDoorType_StairMaskLocked0 && type <= kDoorType_StairMaskLocked3) {
    Door_Up_StairMaskLocked(type, dsto);
  } else if (type >= kDoorType_RegularDoor33)
    RoomDraw_HighRangeDoor_North(type, dsto, pos_enum);
  else
    RoomDraw_NormalRangedDoors_North(type, dsto, pos_enum);

}

void Door_Up_StairMaskLocked(uint8 door_type, uint16 dsto) {  // 81a892
  int i = dung_cur_door_idx >> 1;
  dung_door_direction[i] = 0;
  dung_door_tilemap_address[i] = dsto * 2;
  door_type_and_slot[i] = i << 8 | door_type;
  if (dung_door_opened_incl_adjacent & kUpperBitmasks[i & 7]) {
    dung_cur_door_idx += 2;
    return;
  }

  if (door_type < kDoorType_StairMaskLocked2) {
    RoomDraw_OneSidedShutters_North(door_type, dsto);
    return;
  }

  uint8 t = RoomDraw_FlagDoorsAndGetFinalType(0, door_type, dsto);
  const uint16 *src = SrcPtr(kDoorTypeSrcData[t >> 1]);
  for (int i = 0; i < 4; i++) {
    dung_bg1[dsto + XY(0, 0)] = src[0];
    dung_bg1[dsto + XY(0, 1)] = src[1];
    dung_bg1[dsto + XY(0, 2)] = src[2];
    dsto++, src += 3;
  }
  Door_PrioritizeCurDoor();
}

void Door_PrioritizeCurDoor() {  // 81a8fa
  dung_door_tilemap_address[(dung_cur_door_idx >> 1) - 1] |= 0x2000;
}

void RoomDraw_NormalRangedDoors_North(uint8 door_type, uint16 dsto, int pos_enum) {  // 81a90f
  if (pos_enum >= 6) {
    uint16 bak = dung_cur_door_idx;
    dung_cur_door_idx |= 0x10;
    RoomDraw_CheckIfLowerLayerDoors_Y(door_type, kDoorPositionToTilemapOffs_Down[pos_enum - 6] / 2);
    dung_cur_door_idx = bak;
  }
  RoomDraw_OneSidedShutters_North(door_type, dsto);
}

void RoomDraw_OneSidedShutters_North(uint8 door_type, uint16 dsto) {  // 81a932
  int t = RoomDraw_FlagDoorsAndGetFinalType(0, door_type, dsto);
  if (t & 0x100)
    return;
  // Remap type
  if (t == 54 || t == 56) {
    int new_type = (t == 54) ? kDoorType_ShuttersTwoWay : kDoorType_Regular;
    int i = (dung_cur_door_idx >> 1) - 1;
    door_type_and_slot[i] = (i << 8) | new_type;
    t = new_type;
  }
  const uint16 *src = SrcPtr(kDoorTypeSrcData[t >> 1]);
  uint16 *dst = DstoPtr(dsto);
  for (int i = 0; i < 4; i++) {
    dst[XY(0, 0)] = src[0];
    dst[XY(0, 1)] = src[1];
    dst[XY(0, 2)] = src[2];
    dst++, src += 3;
  }
}

void RoomDraw_Door_South(int type, int pos_enum) {  // 81a984
  uint16 dsto = kDoorPositionToTilemapOffs_Down[pos_enum] / 2;
  if (type == kDoorType_PlayerBgChange)
    RoomDraw_MarkLayerToggleDoor(dsto + XY(1, 4));
  else if (type == kDoorType_EntranceDoor)
    Door_Down_EntranceDoor(dsto);
  else if (type == kDoorType_ThroneRoom)
    RoomDraw_MarkDungeonToggleDoor(dsto + XY(1, 4));
  else if (type == kDoorType_ExitToOw) {
    dung_exit_door_addresses[dung_exit_door_count >> 1] = dsto * 2;
    dung_exit_door_count += 2;
  } else if (type >= kDoorType_RegularDoor33) {
    RoomDraw_OneSidedLowerShutters_South(type, dsto);
  } else if (type == kDoorType_EntranceLarge) {
    RoomDraw_FlagDoorsAndGetFinalType(1, type, dsto);
    RoomDraw_SomeBigDecors(10, SrcPtr(0x2656), dsto + XY(-3, -4));
  } else if (type == kDoorType_EntranceLarge2) {
    dsto |= 0x1000;
    RoomDraw_FlagDoorsAndGetFinalType(1, type, dsto);
    dsto += XY(-3, -4);
    RoomDraw_SomeBigDecors(10, SrcPtr(0x2656), dsto);
    dsto += -0x1000 + XY(0, 7);
    for (int i = 0; i < 10; i++) {
      dung_bg2[dsto] = dung_bg1[dsto] | 0x2000;
      dsto += 1;
    }
  } else if (type == kDoorType_EntranceCave || type == kDoorType_EntranceCave2) {
    if (type == kDoorType_EntranceCave2)
      RoomDraw_MakeDoorPartsHighPriority_Y(dsto + XY(0, 4));
    RoomDraw_FlagDoorsAndGetFinalType(1, type, dsto);
    RoomDraw_4x4(SrcPtr(0x26f6), DstoPtr(dsto));
  } else if (type == kDoorType_4) {
    uint16 dsto_org = dsto;
    dsto |= 0x1000;
    RoomDraw_MakeDoorPartsHighPriority_Y(dsto + XY(0, 4));
    RoomDraw_FlagDoorsAndGetFinalType(1, type, dsto);
    RoomDraw_4x4(SrcPtr(0x26f6), DstoPtr(dsto));
    for (int i = 0; i < 4; i++) {
      dung_bg2[dsto_org + XY(0, 3)] = dung_bg1[dsto_org + XY(0, 3)] | 0x2000;
      dsto_org += 1;
    }
  } else {
    RoomDraw_CheckIfLowerLayerDoors_Y(type, dsto);
  }
}

void RoomDraw_CheckIfLowerLayerDoors_Y(uint8 door_type, uint16 dsto) {  // 81aa66
  if (door_type == kDoorType_Regular2) {
    RoomDraw_MakeDoorPartsHighPriority_Y(dsto + XY(0, 4));
    Door_Draw_Helper4(door_type, dsto);
  } else if (door_type == kDoorType_WaterfallTunnel) {
    Door_Draw_Helper4(door_type, dsto);
    Door_PrioritizeCurDoor();
  } else {
    Door_Draw_Helper4(door_type, dsto);
  }
}

void RoomDraw_Door_West(int type, int pos_enum) {  // 81aad7
  uint16 dsto = kDoorPositionToTilemapOffs_Left[pos_enum] / 2;
  if (type == kDoorType_PlayerBgChange)
    RoomDraw_MarkLayerToggleDoor(dsto + XY(-2, 1));
  else if (type == kDoorType_EntranceDoor)
    Door_Left_EntranceDoor(dsto);
  else if (type == kDoorType_ThroneRoom)
    RoomDraw_MarkDungeonToggleDoor(dsto + XY(-2, 1));
  else if (type == kDoorType_Regular2) {
    RoomDraw_MakeDoorPartsHighPriority_X(dsto & ~0x1f);
    RoomDraw_NormalRangedDoors_West(type, dsto, pos_enum);
  } else if (type == kDoorType_WaterfallTunnel) {
    RoomDraw_NormalRangedDoors_West(type, dsto, pos_enum);
    Door_PrioritizeCurDoor();
  } else if (type < kDoorType_RegularDoor33) {
    RoomDraw_NormalRangedDoors_West(type, dsto, pos_enum);
  } else {
    RoomDraw_HighRangeDoor_West(type, dsto, pos_enum);
  }
}

void RoomDraw_NormalRangedDoors_West(uint8 door_type, uint16 dsto, int pos_enum) {  // 81ab1f
  if (pos_enum >= 6) {
    uint16 bak = dung_cur_door_idx;
    dung_cur_door_idx |= 0x10;
    RoomDraw_NormalRangedDoors_East(door_type, kDoorPositionToTilemapOffs_Right[pos_enum - 6] / 2);
    dung_cur_door_idx = bak;
  }

  int t = RoomDraw_FlagDoorsAndGetFinalType(2, door_type, dsto), new_type;
  if (t & 0x100)
    return;

  if ((new_type = kDoorType_ShuttersTwoWay, t == kDoorType_36) ||
      (new_type = kDoorType_Regular, t == kDoorType_38)) {
    int i = (dung_cur_door_idx >> 1) - 1;
    door_type_and_slot[i] = (i << 8) | new_type;
    t = new_type;
  }

  const uint16 *src = SrcPtr(kDoorTypeSrcData3[t >> 1]);
  uint16 *dst = DstoPtr(dsto);
  for (int i = 0; i < 3; i++) {
    dst[XY(0, 0)] = src[0];
    dst[XY(0, 1)] = src[1];
    dst[XY(0, 2)] = src[2];
    dst[XY(0, 3)] = src[3];
    dst++, src += 4;
  }

}

void RoomDraw_Door_East(int type, int pos_enum) {  // 81ab99
  uint16 dsto = kDoorPositionToTilemapOffs_Right[pos_enum] / 2;
  if (type == kDoorType_PlayerBgChange)
    RoomDraw_MarkLayerToggleDoor(dsto + XY(4, 1));
  else if (type == kDoorType_EntranceDoor)
    Door_Right_EntranceDoor(dsto);
  else if (type == kDoorType_ThroneRoom)
    RoomDraw_MarkDungeonToggleDoor(dsto + XY(4, 1));
  else if (type < kDoorType_RegularDoor33) {
    RoomDraw_NormalRangedDoors_East(type, dsto);
  } else {
    RoomDraw_OneSidedLowerShutters_East(type, dsto);
  }
}

void RoomDraw_NormalRangedDoors_East(uint8 door_type, uint16 dsto) {  // 81abc8
  if (door_type == kDoorType_Regular2)
    RoomDraw_MakeDoorPartsHighPriority_X(dsto + XY(4, 0));
  if (door_type == kDoorType_WaterfallTunnel) {
    RoomDraw_OneSidedShutters_East(door_type, dsto);
    Door_PrioritizeCurDoor();
  } else {
    RoomDraw_OneSidedShutters_East(door_type, dsto);
  }
}

void RoomDraw_OneSidedShutters_East(uint8 door_type, uint16 dsto) {  // 81abe2
  int t = RoomDraw_FlagDoorsAndGetFinalType(3, door_type, dsto), new_type;
  if (t & 0x100)
    return;
  if ((new_type = kDoorType_Regular, t == kDoorType_36) ||
      (new_type = kDoorType_ShuttersTwoWay, t == kDoorType_38)) {
    int i = (dung_cur_door_idx >> 1) - 1;
    door_type_and_slot[i] = (i << 8) | new_type;
    t = new_type;
  }
  const uint16 *src = SrcPtr(kDoorTypeSrcData4[t >> 1]);
  uint16 *dst = DstoPtr(dsto) + 1;
  for (int i = 0; i < 3; i++) {
    dst[XY(0, 0)] = src[0];
    dst[XY(0, 1)] = src[1];
    dst[XY(0, 2)] = src[2];
    dst[XY(0, 3)] = src[3];
    dst++, src += 4;
  }
}

void RoomDraw_NorthCurtainDoor(uint16 dsto) {  // 81ac3b
  int rv = RoomDraw_FlagDoorsAndGetFinalType(0, kDoorType_Slashable, dsto);
  if (rv & 0x100) {
    RoomDraw_4x4(SrcPtr(0x78a), DstoPtr(dsto));
  } else {
    RoomDraw_4x4(SrcPtr(kDoorTypeSrcData[rv >> 1]), DstoPtr(dsto));
  }
}

void RoomDraw_Door_ExplodingWall(int pos_enum) {  // 81ac70
  uint16 dsto = kDoor_BlastWallUp_Dsts[pos_enum] / 2;
  int i = dung_cur_door_idx >> 1;
  dung_door_tilemap_address[i] = 2 * (dsto + 10);
  door_type_and_slot[i] = i << 8 | kDoorType_LgExplosion;
  if (!(dung_door_opened_incl_adjacent & kUpperBitmasks[i & 7])) {
    dung_door_direction[i] = 0;
    dung_cur_door_idx += 2;
    return;
  }
  int slot = dung_hdr_tag[0] != 0x20 && dung_hdr_tag[0] != 0x25 && dung_hdr_tag[0] != 0x28;
  dung_hdr_tag[slot] = 0;
  quadrant_fullsize_y = 2;
  dung_blastwall_flag_y = 1;
  RoomDraw_ExplodingWallSegment(SrcPtr(kDoorTypeSrcData2[42]), dsto);
  dung_cur_door_idx += 2;
  dung_unk2 |= 0x200;
  RoomDraw_ExplodingWallSegment(SrcPtr(kDoorTypeSrcData[42]), dsto + XY(0, 6));
}

void RoomDraw_ExplodingWallSegment(const uint16 *src, uint16 dsto) {  // 81ace4
  RoomDraw_ExplodingWallColumn(src, DstoPtr(dsto));
  src += 12, dsto += 2;
  int n = src[0];
  uint16 *d = &dung_bg2[dsto];
  dung_draw_width_indicator = 18;
  do {
    d[XY(0, 0)] = d[XY(0, 1)] = d[XY(0, 2)] = n;
    d[XY(0, 3)] = d[XY(0, 4)] = d[XY(0, 5)] = n;
    d++;
  } while (--dung_draw_width_indicator);
  RoomDraw_ExplodingWallColumn(src + 1, DstoPtr(dsto + 18));
}

void RoomDraw_ExplodingWallColumn(const uint16 *src, uint16 *dst) {  // 81ad25
  for (int i = 0; i < 6; i++) {
    dst[0] = src[0];
    dst[1] = src[6];
    dst += XY(0, 1), src += 1;
  }
}

void RoomDraw_HighRangeDoor_North(uint8 door_type, uint16 dsto, int pos_enum) {  // 81ad41
  if (pos_enum >= 6 && door_type != kDoorType_WarpRoomDoor) {
    uint16 bak = dung_cur_door_idx;
    dung_cur_door_idx |= 0x10;
    RoomDraw_OneSidedLowerShutters_South(door_type, kDoorPositionToTilemapOffs_Down[pos_enum - 6] / 2);
    dung_cur_door_idx = bak;
  }
  uint8 t = RoomDraw_FlagDoorsAndGetFinalType(0, door_type, dsto);
  if (t == kDoorType_ShutterTrapUR || t == kDoorType_ShutterTrapDL) {
    int new_type = (t == kDoorType_ShutterTrapUR) ? kDoorType_RegularDoor33 : kDoorType_Shutter;
    int i = (dung_cur_door_idx >> 1) - 1;
    door_type_and_slot[i] = (i << 8) | new_type;
    t = new_type;
  }
  uint16 dsto_org = dsto;
  const uint16 *src = SrcPtr(kDoorTypeSrcData[t >> 1]);
  for (int i = 0; i < 4; i++) {
    dung_bg2[dsto + XY(0, 0)] = src[0];
    dung_bg1[dsto + XY(0, 1)] = src[1];
    dung_bg1[dsto + XY(0, 2)] = src[2];
    dsto++, src += 3;
  }
  if (door_type != kDoorType_WarpRoomDoor)
    RoomDraw_MakeDoorHighPriority_North(dsto_org);
  Door_PrioritizeCurDoor();
}

void RoomDraw_OneSidedLowerShutters_South(uint8 door_type, uint16 dsto) {  // 81add4
  uint8 t = RoomDraw_FlagDoorsAndGetFinalType(1, door_type, dsto);
  if (t == kDoorType_ShutterTrapUR || t == kDoorType_ShutterTrapDL) {
    int new_type = (t == kDoorType_ShutterTrapUR) ? kDoorType_Shutter : kDoorType_RegularDoor33;
    int i = (dung_cur_door_idx >> 1) - 1;
    door_type_and_slot[i] = (i << 8) | new_type;
    t = new_type;
  }
  uint16 dsto_org = dsto;
  const uint16 *src = SrcPtr(kDoorTypeSrcData2[t >> 1]);
  for (int i = 0; i < 4; i++) {
    dung_bg1[dsto + XY(0, 1)] = src[0];
    dung_bg1[dsto + XY(0, 2)] = src[1];
    dung_bg2[dsto + XY(0, 3)] = src[2];
    dsto++, src += 3;
  }
  RoomDraw_MakeDoorHighPriority_South(dsto_org + XY(0, 4));
  Door_PrioritizeCurDoor();
}

void RoomDraw_HighRangeDoor_West(uint8 door_type, uint16 dsto, int pos_enum) {  // 81ae40
  if (pos_enum >= 6) {
    uint16 bak = dung_cur_door_idx;
    dung_cur_door_idx |= 0x10;
    RoomDraw_OneSidedLowerShutters_East(door_type, kDoorPositionToTilemapOffs_Right[pos_enum - 6] / 2);
    dung_cur_door_idx = bak;
  }

  uint8 t = RoomDraw_FlagDoorsAndGetFinalType(2, door_type, dsto), new_type;
  if ((new_type = kDoorType_Shutter, t == kDoorType_ShutterTrapUR) ||
      (new_type = kDoorType_RegularDoor33, t == kDoorType_ShutterTrapDL)) {
    int i = (dung_cur_door_idx >> 1) - 1;
    door_type_and_slot[i] = (i << 8) | new_type;
    t = new_type;
  }

  const uint16 *src = SrcPtr(kDoorTypeSrcData3[t >> 1]);
  uint16 dsto_org = dsto;
  dung_bg2[dsto + XY(0, 0)] = src[0];
  dung_bg2[dsto + XY(0, 1)] = src[1];
  dung_bg2[dsto + XY(0, 2)] = src[2];
  dung_bg2[dsto + XY(0, 3)] = src[3];
  dsto++, src += 4;
  for (int i = 0; i < 2; i++) {
    dung_bg1[dsto + XY(0, 0)] = src[0];
    dung_bg1[dsto + XY(0, 1)] = src[1];
    dung_bg1[dsto + XY(0, 2)] = src[2];
    dung_bg1[dsto + XY(0, 3)] = src[3];
    dsto++, src += 4;
  }
  RoomDraw_MakeDoorHighPriority_West(dsto_org);
  Door_PrioritizeCurDoor();
}

void RoomDraw_OneSidedLowerShutters_East(uint8 door_type, uint16 dsto) {  // 81aef0
  uint8 t = RoomDraw_FlagDoorsAndGetFinalType(3, door_type, dsto), new_type;
  if ((new_type = kDoorType_RegularDoor33, t == kDoorType_ShutterTrapUR) ||
      (new_type = kDoorType_Shutter, t == kDoorType_ShutterTrapDL)) {
    int i = (dung_cur_door_idx >> 1) - 1;
    door_type_and_slot[i] = (i << 8) | new_type;
    t = new_type;
  }

  uint16 dst_org = dsto;
  const uint16 *src = SrcPtr(kDoorTypeSrcData4[t >> 1]);
  for (int i = 0; i < 2; i++) {
    dung_bg1[dsto + XY(1, 0)] = src[0];
    dung_bg1[dsto + XY(1, 1)] = src[1];
    dung_bg1[dsto + XY(1, 2)] = src[2];
    dung_bg1[dsto + XY(1, 3)] = src[3];
    dsto++, src += 4;
  }
  dung_bg2[dsto + XY(1, 0)] = src[0];
  dung_bg2[dsto + XY(1, 1)] = src[1];
  dung_bg2[dsto + XY(1, 2)] = src[2];
  dung_bg2[dsto + XY(1, 3)] = src[3];
  RoomDraw_MakeDoorHighPriority_East(dst_org + XY(4, 0));
  Door_PrioritizeCurDoor();
}

void RoomDraw_MakeDoorHighPriority_North(uint16 dsto) {  // 81af8b
  uint16 dsto_org = dsto;
  dsto &= 0xF07F >> 1;
  do {
    dung_bg2[dsto + 0] |= 0x2000;
    dung_bg2[dsto + 1] |= 0x2000;
    dung_bg2[dsto + 2] |= 0x2000;
    dung_bg2[dsto + 3] |= 0x2000;
    dsto += XY(0, 1);
  } while (dsto != dsto_org);
}

void RoomDraw_MakeDoorHighPriority_South(uint16 dsto) {  // 81afd4
  do {
    dung_bg2[dsto + 0] |= 0x2000;
    dung_bg2[dsto + 1] |= 0x2000;
    dung_bg2[dsto + 2] |= 0x2000;
    dung_bg2[dsto + 3] |= 0x2000;
    dsto += XY(0, 1);
  } while (dsto & 0x7c0);
}

void RoomDraw_MakeDoorHighPriority_West(uint16 dsto) {  // 81b017
  uint16 dsto_org = dsto;
  dsto &= 0xffe0;
  do {
    dung_bg2[dsto + XY(0, 0)] |= 0x2000;
    dung_bg2[dsto + XY(0, 1)] |= 0x2000;
    dung_bg2[dsto + XY(0, 2)] |= 0x2000;
    dung_bg2[dsto + XY(0, 3)] |= 0x2000;
    dsto += XY(1, 0);
  } while (dsto != dsto_org);
}

void RoomDraw_MakeDoorHighPriority_East(uint16 dsto) {  // 81b05c
  uint16 *d = &dung_bg2[dsto];
  do {
    d[XY(0, 0)] |= 0x2000;
    d[XY(0, 1)] |= 0x2000;
    d[XY(0, 2)] |= 0x2000;
    d[XY(0, 3)] |= 0x2000;
    d += XY(1, 0), dsto += 1;
  } while (dsto & 0x1f);
}

void RoomDraw_MarkDungeonToggleDoor(uint16 dsto) {  // 81b092
  dung_toggle_palace_pos[dung_num_toggle_palace >> 1] = dsto;
  dung_num_toggle_palace += 2;
}

void RoomDraw_MarkLayerToggleDoor(uint16 dsto) {  // 81b09f
  dung_toggle_floor_pos[dung_num_toggle_floor >> 1] = dsto;
  dung_num_toggle_floor += 2;
}

void RoomDraw_GetObjectSize_1to16() {  // 81b0ac
  dung_draw_width_indicator = (dung_draw_width_indicator << 2 | dung_draw_height_indicator) + 1;
  dung_draw_height_indicator = 0;
}

void Object_SizeAtoAplus15(uint8 a) {  // 81b0af
  dung_draw_width_indicator = (dung_draw_width_indicator << 2 | dung_draw_height_indicator) + a;
  dung_draw_height_indicator = 0;
}

void RoomDraw_GetObjectSize_1to15or26() {  // 81b0be
  uint16 x = dung_draw_width_indicator << 2 | dung_draw_height_indicator;
  dung_draw_width_indicator = x ? x : 26;
}

void RoomDraw_GetObjectSize_1to15or32() {  // 81b0cc
  uint16 x = dung_draw_width_indicator << 2 | dung_draw_height_indicator;
  dung_draw_width_indicator = x ? x : 32;
}

// returns 0x100 on inverse carry
int RoomDraw_FlagDoorsAndGetFinalType(uint8 direction, uint8 door_type, uint16 dsto) {  // 81b0da
  int slot = dung_cur_door_idx >> 1;
  dung_door_direction[slot] = direction;
  dung_door_tilemap_address[slot] = dsto * 2;
  door_type_and_slot[slot] = slot << 8 | door_type;

  uint8 door_type_remapped = door_type;

  if ((slot & 7) < 4 && (dung_door_opened_incl_adjacent & kUpperBitmasks[slot & 7])) {
    if ((door_type == kDoorType_ShuttersTwoWay || door_type == kDoorType_Shutter) && dung_flag_trapdoors_down)
      goto dont_mark_opened;
    door_type_remapped = kDoorTypeRemap[door_type >> 1];

    if (door_type != kDoorType_ShuttersTwoWay && door_type != kDoorType_Shutter &&
        door_type >= kDoorType_InvisibleDoor && door_type != kDoorType_RegularDoor33 && door_type != kDoorType_WarpRoomDoor)
      dung_door_opened |= kUpperBitmasks[slot];
  }
dont_mark_opened:
  dung_cur_door_idx = slot * 2 + 2;

  if (door_type_remapped == kDoorType_Slashable || door_type_remapped == kDoorType_WaterfallTunnel)
    return 0x100 | door_type_remapped;

  if (door_type != kDoorType_InvisibleDoor)
    return door_type_remapped;

  invisible_door_dir_and_index_x2 = (slot << 8 | direction) * 2;
  //  if (direction * 2 == link_direction_facing || ((direction * 2) ^ 2) == link_direction_facing)
  //    return door_type_remapped;
  dung_door_opened_incl_adjacent |= kUpperBitmasks[slot];
  return kDoorType_Regular;
}

void RoomDraw_MakeDoorPartsHighPriority_Y(uint16 dsto) {  // 81b1a4
  uint16 *d = &dung_bg2[dsto];
  for (int i = 0; i < 7; i++) {
    d[XY(0, 0)] |= 0x2000;
    d[XY(1, 0)] |= 0x2000;
    d[XY(2, 0)] |= 0x2000;
    d[XY(3, 0)] |= 0x2000;
    d += XY(0, 1);
  }
}

void RoomDraw_MakeDoorPartsHighPriority_X(uint16 dsto) {  // 81b1e7
  uint16 *d = &dung_bg2[dsto];
  for (int i = 0; i < 5; i++) {
    d[XY(0, 0)] |= 0x2000;
    d[XY(0, 1)] |= 0x2000;
    d[XY(0, 2)] |= 0x2000;
    d[XY(0, 3)] |= 0x2000;
    d += XY(1, 0);
  }
}

void RoomDraw_Downwards4x2VariableSpacing(int increment, const uint16 *src, uint16 *dst) {  // 81b220
  do {
    dst[XY(0, 0)] = src[0];
    dst[XY(1, 0)] = src[1];
    dst[XY(2, 0)] = src[2];
    dst[XY(3, 0)] = src[3];
    dst[XY(0, 1)] = src[4];
    dst[XY(1, 1)] = src[5];
    dst[XY(2, 1)] = src[6];
    dst[XY(3, 1)] = src[7];
    dst += increment;
  } while (--dung_draw_width_indicator);
}

uint16 *RoomDraw_DrawObject2x2and1(const uint16 *src, uint16 *dst) {  // 81b279
  dst[XY(0, 0)] = src[0];
  dst[XY(0, 1)] = src[1];
  dst[XY(0, 2)] = src[2];
  dst[XY(0, 3)] = src[3];
  dst[XY(0, 4)] = src[4];
  return dst;
}

uint16 *RoomDraw_RightwardShelfEnd(const uint16 *src, uint16 *dst) {  // 81b2e1
  dst[XY(0, 0)] = src[0];
  dst[XY(0, 1)] = src[1];
  dst[XY(0, 2)] = src[2];
  dst[XY(0, 3)] = src[3];
  return dst;
}

uint16 *RoomDraw_RightwardBarSegment(const uint16 *src, uint16 *dst) {  // 81b2f6
  dst[XY(0, 0)] = src[0];
  dst[XY(0, 1)] = src[1];
  dst[XY(0, 2)] = src[2];
  return dst;
}

void DrawBigGraySegment(uint16 a, const uint16 *src, uint16 *dst, uint16 dsto) {  // 81b33a
  int i = dung_misc_objs_index >> 1;
  dung_replacement_tile_state[i] = a;
  dung_misc_objs_index += 2;
  dung_object_pos_in_objdata[i] = dung_load_ptr_offs;
  dung_object_tilemap_pos[i] = dsto * 2 | (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x2000);
  replacement_tilemap_UL[i] = dst[XY(0, 0)];
  replacement_tilemap_LL[i] = dst[XY(0, 1)];
  replacement_tilemap_UR[i] = dst[XY(1, 0)];
  replacement_tilemap_LR[i] = dst[XY(1, 1)];
  RoomDraw_Rightwards2x2(src, dst);
}

void RoomDraw_SinglePot(const uint16 *src, uint16 *dst, uint16 dsto) {  // 81b395
  int i = dung_misc_objs_index >> 1;
  dung_misc_objs_index += 2;
  dung_replacement_tile_state[i] = 0x1111;
  dung_object_pos_in_objdata[i] = dung_load_ptr_offs;
  dung_object_tilemap_pos[i] = (dsto * 2) | (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x2000);
  replacement_tilemap_UL[i] = 0x0d0e;
  replacement_tilemap_LL[i] = 0x0d1e;
  replacement_tilemap_UR[i] = 0x4d0e;
  replacement_tilemap_LR[i] = 0x4d1e;
  if (savegame_is_darkworld)
    src = SrcPtr(0xe92);
  RoomDraw_Rightwards2x2(src, dst);
}

void RoomDraw_BombableFloor(const uint16 *src, uint16 *dst, uint16 dsto) {  // 81b3e1
  if (dungeon_room_index == 101 && (dung_savegame_state_bits & 0x1000)) {
    dung_draw_width_indicator = 0;
    dung_draw_height_indicator = 0;
    Object_Hole(SrcPtr(0x5aa), dst);
    return;
  }

  src = SrcPtr(0x220);
  const uint16 *src_below = SrcPtr(0x5ba);

  Object_BombableFloorHelper(0x3030, src, src_below, dst, dsto);
  Object_BombableFloorHelper(0x3131, src + 4, src_below + 4, dst + XY(2, 0), dsto + XY(2, 0));
  Object_BombableFloorHelper(0x3232, src + 8, src_below + 8, dst + XY(0, 2), dsto + XY(0, 2));
  Object_BombableFloorHelper(0x3333, src + 12, src_below + 12, dst + XY(2, 2), dsto + XY(2, 2));
}

void RoomDraw_HammerPegSingle(const uint16 *src, uint16 *dst, uint16 dsto) {  // 81b493
  int i = dung_misc_objs_index >> 1;
  dung_misc_objs_index += 2;
  dung_replacement_tile_state[i] = 0x4040;
  dung_object_pos_in_objdata[i] = dung_load_ptr_offs;
  dung_object_tilemap_pos[i] = (dsto * 2) | (dung_line_ptrs_row0 != 0x4000 ? 0 : 0x2000);
  replacement_tilemap_UL[i] = 0x19d8;
  replacement_tilemap_LL[i] = 0x19d9;
  replacement_tilemap_UR[i] = 0x59d8;
  replacement_tilemap_LR[i] = 0x59d9;
  RoomDraw_Rightwards2x2(src, dst);
}

void DrawObjects_PushableBlock(uint16 dsto_x2, uint16 slot) {  // 81b4d6
  int x = dung_misc_objs_index >> 1;
  dung_misc_objs_index += 2;
  dung_replacement_tile_state[x] = 0;
  dung_object_pos_in_objdata[x] = slot;
  dung_object_tilemap_pos[x] = dsto_x2;
  uint16 *dst = DstoPtr((dsto_x2 >> 1) & 0x1fff);
  replacement_tilemap_UL[x] = dst[XY(0, 0)];
  replacement_tilemap_LL[x] = dst[XY(0, 1)];
  replacement_tilemap_UR[x] = dst[XY(1, 0)];
  replacement_tilemap_LR[x] = dst[XY(1, 1)];
  RoomDraw_Rightwards2x2(SrcPtr(0xe52), dst);
}

void DrawObjects_LightableTorch(uint16 dsto_x2, uint16 slot) {  // 81b509
  int x = dung_index_of_torches >> 1;
  dung_index_of_torches += 2;
  dung_object_tilemap_pos[x] = dsto_x2;
  dung_object_pos_in_objdata[x] = slot;
  uint16 src_img = 0xec2;
  uint16 *dst = DstoPtr((dsto_x2 >> 1) & 0x1fff);
  if (dsto_x2 & 0x8000) {
    src_img = 0xeca;
    if (dung_num_lit_torches < 3)
      dung_num_lit_torches++;
  }
  RoomDraw_Rightwards2x2(SrcPtr(src_img), dst);
}

void Dungeon_LoadHeader() {  // 81b564
  dung_flag_statechange_waterpuzzle = 0;
  dung_flag_somaria_block_switch = 0;
  dung_flag_movable_block_was_pushed = 0;

  static const int16 kAdjustment[] = { 256, -256 };

  if (submodule_index == 0) {
    dung_loade_bgoffs_h_copy = BG2HOFS_copy2 & ~0x1FF;
    dung_loade_bgoffs_v_copy = BG2VOFS_copy2 & ~0x1FF;
  } else if (submodule_index == 21 || submodule_index < 18 && submodule_index >= 6) {
    dung_loade_bgoffs_h_copy = (BG2HOFS_copy2 + 0x20) & ~0x1FF;
    dung_loade_bgoffs_v_copy = (BG2VOFS_copy2 + 0x20) & ~0x1FF;
  } else {
    if (((link_direction & 0xf) >> 1) < 2) {
      dung_loade_bgoffs_h_copy = (BG2HOFS_copy2 + kAdjustment[(link_direction & 0xf) >> 1]) & ~0x1FF;
      dung_loade_bgoffs_v_copy = (BG2VOFS_copy2 + 0x20) & ~0x1FF;
    } else {
      dung_loade_bgoffs_h_copy = (BG2HOFS_copy2 + 0x20) & ~0x1FF;
      dung_loade_bgoffs_v_copy = (BG2VOFS_copy2 + kAdjustment[(link_direction & 0xf) >> 3]) & ~0x1FF;
    }
  }

  const uint8 *hdr_ptr = GetRoomHeaderPtr(dungeon_room_index);

  dung_bg2_properties_backup = dung_hdr_bg2_properties;
  dung_hdr_bg2_properties = hdr_ptr[0] >> 5;
  dung_hdr_collision = (hdr_ptr[0] >> 2) & 7;
  dung_want_lights_out_copy = dung_want_lights_out;
  dung_want_lights_out = hdr_ptr[0] & 1;
  const DungPalInfo *dpi = &kDungPalinfos[hdr_ptr[1]];
  dung_hdr_palette_1 = dpi->pal0;
  overworld_palette_sp0 = dpi->pal1;
  sprite_aux1_palette = dpi->pal2;
  sprite_aux2_palette = dpi->pal3;
  aux_tile_theme_index = hdr_ptr[2];
  sprite_graphics_index = hdr_ptr[3] + 0x40;
  dung_hdr_collision_2 = hdr_ptr[4];
  dung_hdr_tag[0] = hdr_ptr[5];
  dung_hdr_tag[1] = hdr_ptr[6];
  dung_hdr_hole_teleporter_plane = hdr_ptr[7] & 3;
  dung_hdr_staircase_plane[0] = (hdr_ptr[7] >> 2) & 3;
  dung_hdr_staircase_plane[1] = (hdr_ptr[7] >> 4) & 3;
  dung_hdr_staircase_plane[2] = (hdr_ptr[7] >> 6) & 3;
  dung_hdr_staircase_plane[3] = hdr_ptr[8] & 3;
  dung_hdr_travel_destinations[0] = hdr_ptr[9];
  dung_hdr_travel_destinations[1] = hdr_ptr[10];
  dung_hdr_travel_destinations[2] = hdr_ptr[11];
  dung_hdr_travel_destinations[3] = hdr_ptr[12];
  dung_hdr_travel_destinations[4] = hdr_ptr[13];
  dung_flag_trapdoors_down = 1;
  dung_overlay_to_load = 0;
  dung_index_x3 = dungeon_room_index * 3;

  uint16 x = save_dung_info[dungeon_room_index];
  dung_door_opened = x & 0xf000;
  dung_door_opened_incl_adjacent = dung_door_opened | 0xf00;
  dung_savegame_state_bits = (x & 0xff0) << 4;
  dung_quadrants_visited = x & 0xf;

  const uint16 *dp = GetRoomDoorInfo(dungeon_room_index);
  int i = 0;
  for (; dp[i] != 0xffff; i++)
    dung_door_tilemap_address[i] = dp[i];
  dung_door_tilemap_address[i] = 0;

  if (((dungeon_room_index - 1) & 0xf) != 0xf)
    Dungeon_CheckAdjacentRoomsForOpenDoors(18, dungeon_room_index - 1);
  if (((dungeon_room_index + 1) & 0xf) != 0)
    Dungeon_CheckAdjacentRoomsForOpenDoors(12, dungeon_room_index + 1);
  if (dungeon_room_index - 16 >= 0)
    Dungeon_CheckAdjacentRoomsForOpenDoors(6, dungeon_room_index - 16);
  if (dungeon_room_index + 16 < 0x140)
    Dungeon_CheckAdjacentRoomsForOpenDoors(0, dungeon_room_index + 16);
}

void Dungeon_CheckAdjacentRoomsForOpenDoors(int idx, int room) {  // 81b759
  static const uint16 kLookup[] = {
    0x00, 0x10, 0x20, 0x30, 0x40, 0x50,
    0x61, 0x71, 0x81, 0x91, 0xa1, 0xb1,
    0x02, 0x12, 0x22, 0x32, 0x42, 0x52,
    0x63, 0x73, 0x83, 0x93, 0xa3, 0xb3,
  };
  static const uint16 kLookup2[] = {
    0x61, 0x71, 0x81, 0x91, 0xa1, 0xb1,
    0x0, 0x10, 0x20, 0x30, 0x40, 0x50,
    0x63, 0x73, 0x83, 0x93, 0xa3, 0xb3,
    0x02, 0x12, 0x22, 0x32, 0x42, 0x52,
  };
  Dungeon_LoadAdjacentRoomDoors(room);
  int i, j;
  uint16 a;
  for (i = 0; i != 8 && (a = adjacent_doors[i]) != 0xffff; i++) {
    a &= 0xff;
    j = idx;
    if (a == kLookup[j] || a == kLookup[++j] || a == kLookup[++j] || a == kLookup[++j] || a == kLookup[++j] || a == kLookup[++j]) {
      uint8 rev = kLookup2[j];
      for (j = 0; j != 8; j++) {
        if ((uint8)dung_door_tilemap_address[j] == rev) {
          uint8 k = dung_door_tilemap_address[j] >> 8;
          if (k == 0x30)
            break;
          if (k == 0x44 || k == 0x18) {
            // trapdoor
            if (room != dungeon_room_index_prev)
              break;
            dung_flag_trapdoors_down = 0;
          } else {
            // not trapdoor
            if (!(adjacent_doors_flags & kUpperBitmasks[i]))
              break;
          }
          dung_door_opened_incl_adjacent |= kUpperBitmasks[j];
          break;
        }
      }
    }
  }
}

void Dungeon_LoadAdjacentRoomDoors(int room) {  // 81b7ef
  const uint16 *dp = GetRoomDoorInfo(room);
  adjacent_doors_flags = (save_dung_info[room] & 0xf000) | 0xf00;
  for (int i = 0; ; i++) {
    uint16 a = dp[i];
    adjacent_doors[i] = a;
    if (a == 0xffff)
      break;
    if ((a & 0xff00) == 0x4000 || (a & 0xff00) < 0x200)
      adjacent_doors_flags |= kUpperBitmasks[i];
  }
}

void Dungeon_LoadAttribute_Selectable() {  // 81b8b4
  switch (overworld_map_state) {
  case 0:  // Dungeon_LoadBasicAttribute
    overworld_map_state = 1;
    dung_draw_width_indicator = dung_draw_height_indicator = 0;
  case 1:
    Dungeon_LoadBasicAttribute_full(0x40);
    break;
  case 2:
    Dungeon_LoadObjectAttribute();
    break;
  case 3:
    Dungeon_LoadDoorAttribute();
    break;
  case 4:
    overworld_map_state = 5;
    if (orange_blue_barrier_state)
      Dungeon_FlipCrystalPegAttribute();
    break;
  case 5:
    break;
  default:
    assert(0);
  }
}

void Dungeon_LoadAttributeTable() {  // 81b8bf
  dung_draw_width_indicator = dung_draw_height_indicator = 0;
  Dungeon_LoadBasicAttribute_full(0x1000);
  Dungeon_LoadObjectAttribute();
  Dungeon_LoadDoorAttribute();
  if (orange_blue_barrier_state)
    Dungeon_FlipCrystalPegAttribute();
  overworld_map_state = 0;
}

void Dungeon_LoadBasicAttribute_full(uint16 loops) {  // 81b8f3
  do {
    int i = dung_draw_width_indicator / 2;
    uint8 a0 = attributes_for_tile[dung_bg2[i] & 0x3ff];
    if (a0 >= 0x10 && a0 < 0x1c)
      a0 |= (dung_bg2[i] >> 14);  // vflip/hflip
    uint8 a1 = attributes_for_tile[dung_bg2[i + 1] & 0x3ff];
    if (a1 >= 0x10 && a1 < 0x1c)
      a1 |= (dung_bg2[i + 1] >> 14);  // vflip/hflip
    int j = dung_draw_height_indicator;
    dung_bg2_attr_table[j] = a0;
    dung_bg2_attr_table[j + 1] = a1;
    dung_draw_height_indicator = j + 2;
    dung_draw_width_indicator += 4;
  } while (--loops);
  if (dung_draw_height_indicator == 0x2000)
    overworld_map_state++;
}

void Dungeon_LoadObjectAttribute() {  // 81b967
  for (int i = 0; i != dung_num_star_shaped_switches; i += 2) {
    int j = star_shaped_switches_tile[i >> 1];
    WriteAttr2(j + XY(0, 0), 0x3b3b);
    WriteAttr2(j + XY(0, 1), 0x3b3b);
  }

  int i = 0, t = 0x3030;
  for (; i != dung_num_inter_room_upnorth_stairs; i += 2, t += 0x101) {
    int j = dung_inter_starcases[i >> 1];
    WriteAttr2(j + XY(1, 2), 0);
    WriteAttr2(j + XY(1, 0), 0x2626);
    WriteAttr2(j + XY(1, 1), t);
  }
  for (; i != dung_num_wall_upnorth_spiral_stairs; i += 2, t += 0x101) {
    int j = dung_inter_starcases[i >> 1];
    WriteAttr2(j + XY(1, 0), 0x5e5e);
    WriteAttr2(j + XY(1, 2), 0x5e5e);
    WriteAttr2(j + XY(1, 3), 0x5e5e);
    WriteAttr2(j + XY(1, 1), t);
  }
  for (; i != dung_num_wall_upnorth_spiral_stairs_2; i += 2, t += 0x101) {
    int j = dung_inter_starcases[i >> 1];
    WriteAttr2(j + XY(1, 0), 0x5f5f);
    WriteAttr2(j + XY(1, 2), 0x5f5f);
    WriteAttr2(j + XY(1, 3), 0x5f5f);
    WriteAttr2(j + XY(1, 1), t);
  }
  for (; i != dung_num_inter_room_upnorth_straight_stairs; i += 2, t += 0x101) {
    int j = dung_inter_starcases[i >> 1];
    WriteAttr2(j + XY(1, 0), 0x3838);
    WriteAttr2(j + XY(1, 2), 0);
    WriteAttr2(j + XY(1, 3), 0);
    WriteAttr2(j + XY(1, 1), t);
  }
  for (; i != dung_num_inter_room_upsouth_straight_stairs; i += 2, t += 0x101) {
    int j = dung_inter_starcases[i >> 1];
    WriteAttr2(j + XY(1, 0), 0);
    WriteAttr2(j + XY(1, 1), 0);
    WriteAttr2(j + XY(1, 2), t);
    WriteAttr2(j + XY(1, 3), 0x3939);
  }
  t = (t & 0x707) | 0x3434;
  for (; i != dung_num_inter_room_southdown_stairs; i += 2, t += 0x101) {
    int j = dung_inter_starcases[i >> 1];
    WriteAttr2(j + XY(1, 2), t);
    WriteAttr2(j + XY(1, 3), 0x2626);
  }
  for (; i != dung_num_wall_downnorth_spiral_stairs; i += 2, t += 0x101) {
    int j = dung_inter_starcases[i >> 1];
    WriteAttr2(j + XY(1, 0), 0x5e5e);
    WriteAttr2(j + XY(1, 1), t);
    WriteAttr2(j + XY(1, 2), 0x5e5e);
    WriteAttr2(j + XY(1, 3), 0x5e5e);
  }
  for (; i != dung_num_wall_downnorth_spiral_stairs_2; i += 2, t += 0x101) {
    int j = dung_inter_starcases[i >> 1];
    WriteAttr2(j + XY(1, 0), 0x5f5f);
    WriteAttr2(j + XY(1, 1), t);
    WriteAttr2(j + XY(1, 2), 0x5f5f);
    WriteAttr2(j + XY(1, 3), 0x5f5f);
  }
  for (; i != dung_num_inter_room_downnorth_straight_stairs; i += 2, t += 0x101) {
    int j = dung_inter_starcases[i >> 1];
    WriteAttr2(j + XY(1, 0), 0x3838);
    WriteAttr2(j + XY(1, 1), t);
    WriteAttr2(j + XY(1, 2), 0);
    WriteAttr2(j + XY(1, 3), 0);
  }
  for (; i != dung_num_inter_room_downsouth_straight_stairs; i += 2, t += 0x101) {
    int j = dung_inter_starcases[i >> 1];
    WriteAttr2(j + XY(1, 0), 0);
    WriteAttr2(j + XY(1, 1), 0);
    WriteAttr2(j + XY(1, 2), t);
    WriteAttr2(j + XY(1, 3), 0x3939);
  }

  i = 0;
  int type = 0, iend = dung_num_inroom_upnorth_stairs;
  uint16 attr = 0x1f1f;
  if (iend == 0) {
    type = 1, attr = 0x1e1e;
    iend = dung_num_inroom_southdown_stairs;
    if (iend == 0) {
      type = 2, attr = 0x1d1d;
      iend = dung_num_interpseudo_upnorth_stairs;
      if (iend == 0)
        goto skip3;
    }
  }
  kind_of_in_room_staircase = type;
  for (; i != iend; i += 2) {
    int j = dung_stairs_table_1[i >> 1];
    WriteAttr2(j + XY(0, 0), 0x02);
    WriteAttr1(j + XY(0, 3), 0x02);
    WriteAttr2(j + XY(2, 0), 0x0200);
    WriteAttr1(j + XY(2, 3), 0x0200);
    WriteAttr2(j + XY(0, 1), 0x01);
    WriteAttr1(j + XY(0, 2), 0x01);
    WriteAttr2(j + XY(2, 1), 0x0100);  // todo: use 8-bit write?
    WriteAttr1(j + XY(2, 2), 0x0100);
    WriteAttr2(j + XY(1, 1), attr);
    WriteAttr1(j + XY(1, 1), attr);
    WriteAttr2(j + XY(1, 2), attr);
    WriteAttr1(j + XY(1, 2), attr);
  }
skip3:
  if (i != dung_some_stairs_unk4) {
    kind_of_in_room_staircase = 2;
    for (; i != dung_some_stairs_unk4; i += 2) {
      int j = dung_stairs_table_1[i >> 1];
      WriteAttr2(j + XY(0, 0), 0xa03);
      WriteAttr1(j + XY(0, 0), 0xa03);
      WriteAttr2(j + XY(2, 0), 0x30a);
      WriteAttr1(j + XY(2, 0), 0x30a);
      WriteAttr2(j + XY(0, 1), 0x803);
      WriteAttr2(j + XY(2, 1), 0x308);
    }
  }
  i = 0;
  if (i != dung_num_inroom_upnorth_stairs_water) {
    kind_of_in_room_staircase = 2;
    for (; i != dung_num_inroom_upnorth_stairs_water; i += 2) {
      int j = dung_stairs_table_1[i >> 1];
      WriteAttr2(j + XY(0, 0), 0x003);
      WriteAttr2(j + XY(2, 0), 0x300);
      WriteAttr1(j + XY(0, 0), 0xa03);
      WriteAttr1(j + XY(2, 0), 0x30a);
      WriteAttr2(j + XY(0, 1), 0x808);
      WriteAttr2(j + XY(2, 1), 0x808);
    }
  }
  if (i != dung_num_activated_water_ladders) {
    kind_of_in_room_staircase = 2;
    for (; i != dung_num_activated_water_ladders; i += 2) {
      int j = dung_stairs_table_1[i >> 1];
      WriteAttr2(j + XY(0, 0), 0x003);
      WriteAttr2(j + XY(2, 0), 0x300);
      WriteAttr1(j + XY(0, 0), 0xa03);
      WriteAttr1(j + XY(2, 0), 0x30a);
    }
  }

  for (i = 0, t = 0x7070; i != dung_misc_objs_index; i += 2, t += 0x101) {
    uint16 k = dung_replacement_tile_state[i >> 1];
    if ((k & 0xf0) != 0x30) {
      int j = (dung_object_tilemap_pos[i >> 1] & 0x3fff) >> 1;
      WriteAttr2(j + XY(0, 0), t);
      WriteAttr2(j + XY(0, 1), t);
    }
  }

  if (i != dung_index_of_torches) {
    for (t = 0xc0c0; i != dung_index_of_torches; i += 2, t = (t & 0xefef) + 0x101) {
      int j = (dung_object_tilemap_pos[i >> 1] & 0x3fff) >> 1;
      WriteAttr2(j + XY(0, 0), t);
      WriteAttr2(j + XY(0, 1), t);
    }
    dung_index_of_torches = 0;
  }

  t = 0x5858, i = 0;
  if (dung_num_chests_x2) {
    if (dung_hdr_tag[0] == 0x27 || dung_hdr_tag[0] == 0x3c || dung_hdr_tag[0] == 0x3e || dung_hdr_tag[0] >= 0x29 && dung_hdr_tag[0] < 0x33)
      goto no_big_key_locks;
    if (dung_hdr_tag[1] == 0x27 || dung_hdr_tag[1] == 0x3c || dung_hdr_tag[1] == 0x3e || dung_hdr_tag[1] >= 0x29 && dung_hdr_tag[1] < 0x33)
      goto no_big_key_locks;

    for (; i != dung_num_chests_x2; i += 2, t += 0x101) {
      int k = dung_chest_locations[i >> 1];
      if (k != 0) {
        int j = (k & 0x7fff) >> 1;
        WriteAttr2(j + XY(0, 0), t);
        WriteAttr2(j + XY(0, 1), t);
        if (k & 0x8000) {
          dung_chest_locations[i >> 1] = k & 0x7fff;
          WriteAttr2(j + XY(2, 1), t);
          WriteAttr2(j + XY(0, 2), t);
          WriteAttr2(j + XY(2, 2), t);
        }
      }
    }
  }
  for (; i != dung_num_bigkey_locks_x2; i += 2, t += 0x101) {
    int k = dung_chest_locations[i >> 1];
    dung_chest_locations[i >> 1] = k | 0x8000;
    int j = (k & 0x7fff) >> 1;
    WriteAttr2(j + XY(0, 0), t);
    WriteAttr2(j + XY(0, 1), t);
  }
no_big_key_locks:

  i = 0;
  type = 0, iend = dung_num_stairs_1;
  attr = 0x3f3f;
  if (iend == 0) {
    type = 1, attr = 0x3e3e;
    iend = dung_num_stairs_2;
    if (iend == 0) {
      type = 2, attr = 0x3d3d;
      iend = dung_num_stairs_wet;
      if (iend == 0)
        goto skip7;
    }
  }
  kind_of_in_room_staircase = type;
  for (i = 0; i != iend; i += 2) {
    int j = dung_stairs_table_2[i >> 1];
    WriteAttr1(j + XY(0, 0), 0x02);
    WriteAttr2(j + XY(0, 3), 0x02);
    WriteAttr1(j + XY(0, 1), 0x01);
    WriteAttr2(j + XY(0, 2), 0x01);
    WriteAttr1(j + XY(2, 0), 0x0200);
    WriteAttr2(j + XY(2, 3), 0x0200);
    WriteAttr1(j + XY(2, 1), 0x0100);  // todo: use 8-bit write?
    WriteAttr2(j + XY(2, 2), 0x0100);
    WriteAttr1(j + XY(1, 1), attr);
    WriteAttr2(j + XY(1, 1), attr);
    WriteAttr1(j + XY(1, 2), attr);
    WriteAttr2(j + XY(1, 2), attr);
  }
skip7:

  if (dung_num_inroom_upsouth_stairs_water) {
    kind_of_in_room_staircase = 2;
    for (i = 0; i != dung_num_inroom_upsouth_stairs_water; i += 2) {
      int j = dung_stairs_table_2[i >> 1];
      WriteAttr1(j + XY(0, 3), 0xa03);
      WriteAttr1(j + XY(2, 3), 0x30a);
      WriteAttr2(j + XY(0, 3), 0x003);
      WriteAttr2(j + XY(2, 3), 0x300);
      WriteAttr2(j + XY(0, 2), 0x808);
      WriteAttr2(j + XY(2, 2), 0x808);
    }
  }
  overworld_map_state += 1;
}

void Dungeon_LoadDoorAttribute() {  // 81be17
  for (int i = 0; i != 16; i++) {
    if (dung_door_tilemap_address[i])
      Dungeon_LoadSingleDoorAttribute(i);
  }
  Dungeon_LoadSingleDoorTileAttribute();
  ChangeDoorToSwitch();
  overworld_map_state += 1;
}

void Dungeon_LoadSingleDoorAttribute(int k) {  // 81be35
  assert(k >= 0 && k < 16);
  uint8 t = door_type_and_slot[k] & 0xfe, dir;
  uint16 attr;
  int i, j;

  if (t == kDoorType_Regular || t == kDoorType_EntranceDoor || t == kDoorType_ExitToOw || t == kDoorType_EntranceLarge || t == kDoorType_EntranceCave)
    goto alpha;

  if (t == kDoorType_EntranceLarge2 || t == kDoorType_EntranceCave2 || t == kDoorType_4 || t == kDoorType_Regular2 || t == kDoorType_WaterfallTunnel)
    goto beta;

  if (t == kDoorType_LgExplosion)
    return;

  if (t >= kDoorType_RegularDoor33) {
    if (t == kDoorType_RegularDoor33 || t == kDoorType_WarpRoomDoor)
      goto beta;
    if (dung_door_opened_incl_adjacent & kUpperBitmasks[k])
      goto beta;

    j = dung_door_tilemap_address[k] >> 1;
    attr = (0xf0 + k) * 0x101;
    WriteAttr2(j + XY(1, 1), attr);
    WriteAttr2(j + XY(1, 2), attr);
    return;

  }

  i = (t == kDoorType_ShuttersTwoWay || t == kDoorType_Shutter) ? k : k & 7;
  if (!(dung_door_opened_incl_adjacent & kUpperBitmasks[i])) {
    j = dung_door_tilemap_address[k] >> 1;
    attr = (0xf0 + k) * 0x101;
    WriteAttr2(j + XY(1, 1), attr);
    WriteAttr2(j + XY(1, 2), attr);
    return;
  }

alpha:
  if (t >= kDoorType_StairMaskLocked0 && t <= kDoorType_StairMaskLocked3)
    return;
  attr = kTileAttrsByDoor[t >> 1];
  dir = dung_door_direction[k] & 3;
  if (dir == 0) {
    uint16 a = dung_door_tilemap_address[k];
    if (a == dung_exit_door_addresses[0] || a == dung_exit_door_addresses[1] || a == dung_exit_door_addresses[2] || a == dung_exit_door_addresses[3])
      attr = 0x8e8e;
    j = (a >> 1) & ~0x7c0;
    WriteAttr2(j + XY(1, 0), attr);
    WriteAttr2(j + XY(1, 1), attr);
    WriteAttr2(j + XY(1, 2), attr);
    WriteAttr2(j + XY(1, 3), attr);
    WriteAttr2(j + XY(1, 4), attr);
    WriteAttr2(j + XY(1, 5), attr);
    WriteAttr2(j + XY(1, 6), attr);
    WriteAttr2(j + XY(1, 7), 0);
  } else if (dir == 1) {
    uint16 a = dung_door_tilemap_address[k];
    if (t == kDoorType_EntranceLarge || t == kDoorType_EntranceCave ||
        a == dung_exit_door_addresses[0] || a == dung_exit_door_addresses[1] || a == dung_exit_door_addresses[2] || a == dung_exit_door_addresses[3])
      attr = 0x8e8e;
    j = a >> 1;
    WriteAttr2(j + XY(1, 1), attr);
    WriteAttr2(j + XY(1, 2), attr);
    WriteAttr2(j + XY(1, 3), attr);
    WriteAttr2(j + XY(1, 4), attr);
    WriteAttr2(j + XY(1, 5), attr);
  } else if (dir == 2) {
    j = (dung_door_tilemap_address[k] >> 1) & ~0x1f;
    WriteAttr2(j + XY(0, 1), attr + 0x101);
    WriteAttr2(j + XY(2, 1), attr + 0x101);
    WriteAttr2(j + XY(0, 2), attr + 0x101);
    WriteAttr2(j + XY(2, 2), attr + 0x101);
    WriteAttr2(j + XY(4, 1), (attr + 0x101) & 0xff);
    WriteAttr2(j + XY(4, 2), (attr + 0x101) & 0xff);
  } else {
    j = (dung_door_tilemap_address[k] >> 1);
    WriteAttr2(j + XY(2, 1), attr + 0x101);
    WriteAttr2(j + XY(4, 1), attr + 0x101);
    WriteAttr2(j + XY(2, 2), attr + 0x101);
    WriteAttr2(j + XY(4, 2), attr + 0x101);
    WriteAttr2(j + XY(0, 1), (attr + 0x101) & 0xff00);
    WriteAttr2(j + XY(0, 2), (attr + 0x101) & 0xff00);
  }
  return;

beta:
  attr = kTileAttrsByDoor[t >> 1];
  dir = dung_door_direction[k] & 3;
  if (dir == 0) {
    j = (dung_door_tilemap_address[k] >> 1) & ~0x7c0;
    WriteAttr2(j + XY(1, 0), attr);
    WriteAttr2(j + XY(1, 1), attr);
    WriteAttr2(j + XY(1, 2), attr);
    WriteAttr2(j + XY(1, 3), attr);
    WriteAttr2(j + XY(1, 4), attr);
    WriteAttr2(j + XY(1, 5), attr);
    WriteAttr2(j + XY(1, 6), attr);
    WriteAttr2(j + XY(1, 7), attr);
    WriteAttr2(j + XY(1, 8), attr);
    WriteAttr2(j + XY(1, 9), attr);
  } else if (dir == 1) {
    uint16 a = dung_door_tilemap_address[k] & 0x1fff;
    if (t == kDoorType_EntranceLarge2 || t == kDoorType_EntranceCave2 || t == kDoorType_4 ||
        a == dung_exit_door_addresses[0] || a == dung_exit_door_addresses[1] || a == dung_exit_door_addresses[2] || a == dung_exit_door_addresses[3])
      attr = 0x8e8e;
    j = dung_door_tilemap_address[k] >> 1;
    WriteAttr2(j + XY(1, 1), attr);
    WriteAttr2(j + XY(1, 2), attr);
    WriteAttr2(j + XY(1, 3), attr);
    WriteAttr2(j + XY(1, 4), attr);
    WriteAttr2(j + XY(1, 5), attr);
    WriteAttr2(j + XY(1, 6), attr);
    WriteAttr2(j + XY(1, 7), attr);
    WriteAttr2(j + XY(1, 8), attr);
  } else if (dir == 2) {
    j = (dung_door_tilemap_address[k] >> 1) & ~0x1f;
    WriteAttr2(j + XY(0, 1), attr + 0x101);
    WriteAttr2(j + XY(2, 1), attr + 0x101);
    WriteAttr2(j + XY(4, 1), attr + 0x101);
    WriteAttr2(j + XY(6, 1), attr + 0x101);
    WriteAttr2(j + XY(0, 2), attr + 0x101);
    WriteAttr2(j + XY(2, 2), attr + 0x101);
    WriteAttr2(j + XY(4, 2), attr + 0x101);
    WriteAttr2(j + XY(6, 2), attr + 0x101);
  } else {
    j = ((dung_door_tilemap_address[k] >> 1) + 1);
    WriteAttr2(j + XY(0, 1), attr + 0x101);
    WriteAttr2(j + XY(2, 1), attr + 0x101);
    WriteAttr2(j + XY(4, 1), attr + 0x101);
    WriteAttr2(j + XY(6, 1), attr + 0x101);
    WriteAttr2(j + XY(0, 2), attr + 0x101);
    WriteAttr2(j + XY(2, 2), attr + 0x101);
    WriteAttr2(j + XY(4, 2), attr + 0x101);
    WriteAttr2(j + XY(6, 2), attr + 0x101);
  }
}

void Door_LoadBlastWallAttr(int k) {  // 81bfc1
  int j = dung_door_tilemap_address[k] >> 1;
  if (!(dung_door_direction[k] & 2)) {
    for (int n = 12; n; n--) {
      WriteAttr2(j + XY(0, 0), 0x102);
      for (int i = 2; i < 20; i += 2)
        WriteAttr2(j + XY(i, 0), 0x0);
      WriteAttr2(j + XY(20, 0), 0x201);
      j += XY(0, 1);
    }
  } else {
    for (int n = 5; n; n--) {
      WriteAttr2(j + XY(0, 0), 0x101);
      WriteAttr2(j + XY(0, 21), 0x101);
      WriteAttr2(j + XY(0, 1), 0x202);
      WriteAttr2(j + XY(0, 20), 0x202);
      for (int i = 2; i < 20; i++)
        WriteAttr2(j + XY(0, i), 0x0);
      j += XY(2, 0);
    }
  }
}

void ChangeDoorToSwitch() {  // 81c1ba
  assert(dung_unk5 == 0);
}

void Dungeon_FlipCrystalPegAttribute() {  // 81c22a
  for (int i = 0xfff; i >= 0; i--) {
    if ((dung_bg2_attr_table[i] & ~1) == 0x66)
      dung_bg2_attr_table[i] ^= 1;
    if ((dung_bg1_attr_table[i] & ~1) == 0x66)
      dung_bg1_attr_table[i] ^= 1;
  }
}

void Dungeon_HandleRoomTags() {  // 81c2fd
  if (!flag_skip_call_tag_routines) {
    Dungeon_DetectStaircase();

    // Dungeon_DetectStaircase might change the submodule, so avoid
    // calling the tag routines cause they could also change the submodule,
    // causing items to spawn in incorrect locations cause link_x/y_coord gets
    // out of sync if you enter a staircase exactly when a room tag triggers.
    if (enhanced_features0 & kFeatures0_MiscBugFixes && submodule_index != 0)
      return;

    g_ram[14] = 0;
    kDungTagroutines[dung_hdr_tag[0]](0);
    g_ram[14] = 1;
    kDungTagroutines[dung_hdr_tag[1]](1);
  }
  flag_skip_call_tag_routines = 0;
}

void Dung_TagRoutine_0x00(int k) {  // 81c328
}

void Dungeon_DetectStaircase() {  // 81c329
  int k = link_direction & 12;
  if (!k)
    return;

  static const int8 kBuggyLookup[] = { 7, 24, 8, 8, 0, 0, -1, 17 };
  int pos = ((link_y_coord + kBuggyLookup[k >> 1]) & 0x1f8) << 3;
  pos |= (link_x_coord & 0x1f8) >> 3;
  pos |= (link_is_on_lower_level ? 0x1000 : 0);

  uint8 at = dung_bg2_attr_table[pos + (k == 4 ? 0x80 : 0)];
  if (!(at == 0x26 || at == 0x38 || at == 0x39 || at == 0x5e || at == 0x5f))
    return;

  uint8 attr2 = dung_bg2_attr_table[pos + XY(0, 1)];
  if ((attr2 & 0xf8) != 0x30)
    return;

  if (link_state_bits & 0x80) {
    link_y_coord = link_y_coord_prev;
    return;
  }

  which_staircase_index = attr2;
  which_staircase_index_PADDING = pos >> 8; // residual
  dungeon_room_index_prev = dungeon_room_index;
  Dungeon_FlagRoomData_Quadrants();

  if (at == 0x38 || at == 0x39) {
    staircase_var1 = 0x20;
    if (at == 0x38)
      Dungeon_StartInterRoomTrans_Up();
    else
      Dungeon_StartInterRoomTrans_Down();
  }

  int j = (which_staircase_index & 3);
  BYTE(dungeon_room_index) = dung_hdr_travel_destinations[j + 1];
  cur_staircase_plane = dung_hdr_staircase_plane[j];
  byte_7E0492 = (link_is_on_lower_level || link_is_on_lower_level_mirror) ? 2 : 0;
  subsubmodule_index = 0;
  bitmask_of_dragstate = 0;
  link_delay_timer_spin_attack = 0;
  button_mask_b_y = 0;
  button_b_frames = 0;
  link_cant_change_direction &= ~1;
  if (at == 0x26) {
    submodule_index = 6;
    sound_effect_1 = (cur_staircase_plane < 0x34 ? 22 : 24); // wtf?
  } else if (at == 0x38 || at == 0x39) {
    submodule_index = (at == 0x38) ? 18 : 19;
    link_timer_push_get_tired = 7;
  } else {
    UsedForStraightInterRoomStaircase();
    submodule_index = 14;
  }
}

void RoomTag_NorthWestTrigger(int k) {  // 81c432
  if (!(link_x_coord & 0x100) && !(link_y_coord & 0x100))
    RoomTag_QuadrantTrigger(k);
}

void Dung_TagRoutine_0x2A(int k) {  // 81c438
  if ((link_x_coord & 0x100) && !(link_y_coord & 0x100))
    RoomTag_QuadrantTrigger(k);
}

void Dung_TagRoutine_0x2B(int k) {  // 81c43e
  if (!(link_x_coord & 0x100) && (link_y_coord & 0x100))
    RoomTag_QuadrantTrigger(k);
}

void Dung_TagRoutine_0x2C(int k) {  // 81c444
  if ((link_x_coord & 0x100) && (link_y_coord & 0x100))
    RoomTag_QuadrantTrigger(k);
}

void Dung_TagRoutine_0x2D(int k) {  // 81c44a
  if (!(link_x_coord & 0x100))
    RoomTag_QuadrantTrigger(k);
}

void Dung_TagRoutine_0x2E(int k) {  // 81c450
  if (link_x_coord & 0x100)
    RoomTag_QuadrantTrigger(k);
}

void Dung_TagRoutine_0x2F(int k) {  // 81c456
  if (!(link_y_coord & 0x100))
    RoomTag_QuadrantTrigger(k);
}

void Dung_TagRoutine_0x30(int k) {  // 81c45c
  if (link_y_coord & 0x100)
    RoomTag_QuadrantTrigger(k);
}

void RoomTag_QuadrantTrigger(int k) {  // 81c461
  uint8 tag = dung_hdr_tag[k];
  if (tag >= 0xb) {
    if (tag >= 0x29) {
      if (Sprite_CheckIfScreenIsClear())
        RoomTag_OperateChestReveal(k);
    } else {
      uint8 a = (dung_flag_movable_block_was_pushed ^ 1);
      if (a != BYTE(dung_flag_trapdoors_down)) {
        BYTE(dung_flag_trapdoors_down) = a;
        sound_effect_2 = 37;
        submodule_index = 5;
        dung_cur_door_pos = 0;
        door_animation_step_indicator = 0;
      }
    }
  } else {
    if (Sprite_CheckIfScreenIsClear())
      Dung_TagRoutine_TrapdoorsUp();
  }
}

void Dung_TagRoutine_TrapdoorsUp() {  // 81c49e
  if (dung_flag_trapdoors_down) {
    dung_flag_trapdoors_down = 0;
    dung_cur_door_pos = 0;
    door_animation_step_indicator = 0;
    sound_effect_2 = 0x1b;
    submodule_index = 5;
  }
}

void RoomTag_RoomTrigger(int k) {  // 81c4bf
  if (dung_hdr_tag[k] == 10) {
    if (Sprite_CheckIfRoomIsClear())
      Dung_TagRoutine_TrapdoorsUp();
  } else {
    if (Sprite_CheckIfRoomIsClear())
      RoomTag_OperateChestReveal(k);
  }
}

void RoomTag_RekillableBoss(int k) {  // 81c4db
  if (Sprite_CheckIfRoomIsClear()) {
    flag_block_link_menu = 0;
    dung_hdr_tag[1] = 0;
  }
}

void RoomTag_RoomTrigger_BlockDoor(int k) {  // 81c4e7
  if (dung_flag_statechange_waterpuzzle && dung_flag_trapdoors_down) {
    dung_flag_trapdoors_down = 0;
    dung_cur_door_pos = 0;
    door_animation_step_indicator = 0;
    submodule_index = 5;
  }
}

// Used for bosses
void RoomTag_PrizeTriggerDoorDoor(int k) {  // 81c508
  int t = savegame_is_darkworld ? link_has_crystals : link_which_pendants;
  if (t & kDungeonCrystalPendantBit[BYTE(cur_palace_index_x2) >> 1]) {
    dung_flag_trapdoors_down = 0;
    dung_cur_door_pos = 0;
    door_animation_step_indicator = 0;
    submodule_index = 5;
    dung_hdr_tag[k] = 0;
  }
}

void RoomTag_SwitchTrigger_HoldDoor(int k) {  // 81c541
  uint16 i = -2, v;
  uint8 tmp;
  for (;;) {
    i += 2;
    if (i == dung_index_of_torches_start)
      break;
    if (dung_replacement_tile_state[i >> 1] == 5) {
      v = related_to_trapdoors_somehow;
      if (v != 0xffff)
        goto shortcut;
      break;
    }
  }
  v = !dung_flag_somaria_block_switch && !dung_flag_statechange_waterpuzzle && !RoomTag_CheckForPressedSwitch(&tmp);
shortcut:
  if (v != dung_flag_trapdoors_down) {
    dung_flag_trapdoors_down = v;
    dung_cur_door_pos = 0;
    door_animation_step_indicator = 0;
    if (v == 0)
      sound_effect_2 = 0x25;
    submodule_index = 5;
  }
}

void RoomTag_SwitchTrigger_ToggleDoor(int k) {  // 81c599
  uint8 attr;
  if (!dung_door_switch_triggered) {
    if (RoomTag_MaybeCheckShutters(&attr)) {
      dung_cur_door_pos = 0;
      door_animation_step_indicator = 0;
      sound_effect_2 = 0x25;
      PushPressurePlate(attr);
      dung_flag_trapdoors_down ^= 1;
      dung_door_switch_triggered = 1;
    }
  } else {
    if (!RoomTag_MaybeCheckShutters(&attr))
      dung_door_switch_triggered = 0;
  }
}

void PushPressurePlate(uint8 attr) {  // 81c5cf
  submodule_index = 5;
  if (attr == 0x23 || !word_7E04B6)
    return;
  saved_module_for_menu = submodule_index;
  submodule_index = 23;
  subsubmodule_index = 32;
  link_y_coord += 2;
  if ((WORD(dung_bg2_attr_table[word_7E04B6]) & 0xfe00) != 0x2400)
    word_7E04B6++;
  Dungeon_UpdateTileMapWithCommonTile((word_7E04B6 & 0x3f) << 3, (word_7E04B6 >> 3) & 0x1f8, 0x10);
}

void RoomTag_TorchPuzzleDoor(int k) {  // 81c629
  int j = 0;
  for (int i = 0; i < 16; i++)
    if (dung_object_tilemap_pos[i] & 0x8000)
      j++;
  int down = (j < 4);
  if (down != dung_flag_trapdoors_down) {
    dung_flag_trapdoors_down = down;
    dung_cur_door_pos = 0;
    door_animation_step_indicator = 0;
    sound_effect_2 = 0x1b;
    submodule_index = 5;
  }
}

void RoomTag_Switch_ExplodingWall(int k) {  // 81c67a
  uint8 yv;
  if (!RoomTag_MaybeCheckShutters(&yv))
    return;

  Dung_TagRoutine_BlastWallStuff(k);
}

void RoomTag_PullSwitchExplodingWall(int k) {  // 81c685
  if (!dung_flag_statechange_waterpuzzle)
    return;
  Dung_TagRoutine_BlastWallStuff(k);
}

void Dung_TagRoutine_BlastWallStuff(int k) {  // 81c68c
  static const uint8 kBlastWall_Tab0[5] = { 4, 6, 0, 0, 2 };
  static const uint16 kBlastWall_Tab1[5] = { 0, 0xa, 0, 0, 0x280 };

  dung_hdr_tag[k] = 0;

  int j = -1;
  do {
    j++;
  } while ((door_type_and_slot[j] & ~1) != 0x30);
  dung_unk_blast_walls_3 = j * 2;

  int i = ((link_y_coord >> 8 & 1) + 1) * 2;
  if (dung_door_direction[j] & 2)
    i = (link_x_coord >> 8 & 1);

  messaging_buf[0x1c / 2] = kBlastWall_Tab0[i];
  j = dung_door_tilemap_address[j] + kBlastWall_Tab1[i];

  messaging_buf[0x1a / 2] = (j & 0x7e) * 4 + dung_loade_bgoffs_h_copy;
  messaging_buf[0x18 / 2] = ((j & 0x1f80) >> 4) + dung_loade_bgoffs_v_copy;
  sound_effect_2 = 27;
  BYTE(dung_unk_blast_walls_2) = 1;
  AncillaAdd_BlastWall();
}

// Used for bosses
void RoomTag_GetHeartForPrize(int k) {  // 81c709
  static const uint8 kBossFinishedFallingItem[13] = { 0, 0, 1, 2, 0, 6, 6, 6, 6, 6, 3, 6, 6 };
  if (!(dung_savegame_state_bits & 0x8000))
    return;
  int t = savegame_is_darkworld ? link_has_crystals : link_which_pendants;
  if (!(t & kDungeonCrystalPendantBit[BYTE(cur_palace_index_x2) >> 1])) {
    byte_7E04C2 = 128;
    if (Ancilla_SpawnFallingPrize(kBossFinishedFallingItem[BYTE(cur_palace_index_x2) >> 1]) < 0)
      return; // Zelda bugfix. Price won't spawn if we're out of ancillas
  }
  dung_hdr_tag[k] = 0;
}

void RoomTag_Agahnim(int k) {  // 81c74e
  if (!(save_ow_event_info[0x5b] & 0x20) && dung_savegame_state_bits & 0x8000) {
    Palette_RevertTranslucencySwap();
    dung_hdr_tag[0] = 0;
    PrepareDungeonExitFromBossFight();
  }
}

void RoomTag_GanonDoor(int tagidx) {  // 81c767
  for (int k = 15; k >= 0; k--) {
    if (sprite_state[k] == 4 || !(sprite_flags4[k] & 64) && sprite_state[k])
      return;
  }
  if (link_player_handler_state != kPlayerState_FallingIntoHole) {
    flag_is_link_immobilized = 26;
    submodule_index = 26;
    subsubmodule_index = 0;
    dung_hdr_tag[0] = 0;
    link_force_hold_sword_up = 1;
    button_mask_b_y = 0;
    button_b_frames = 0;
    R16 = 0x364;
  }
}

void RoomTag_KillRoomBlock(int k) {  // 81c7a2
  if (link_x_coord & 0x100 && link_y_coord & 0x100) {
    if (Sprite_CheckIfScreenIsClear()) {
      sound_effect_2 = 0x1b;
      dung_hdr_tag[k] = 0;
    }
  }
}

void RoomTag_PushBlockForChest(int k) {  // 81c7c2
  if (!nmi_load_bg_from_vram && dung_flag_movable_block_was_pushed)
    RoomTag_OperateChestReveal(k);
}

void RoomTag_TriggerChest(int k) {  // 81c7cc
  uint8 attr;
  if (!countdown_for_blink && RoomTag_MaybeCheckShutters(&attr))
    RoomTag_OperateChestReveal(k);
}

void RoomTag_OperateChestReveal(int k) {  // 81c7d8
  dung_hdr_tag[k] = 0;
  vram_upload_offset = 0;
  WORD(overworld_map_state) = 0;
  uint16 attr = 0x5858;
  do {
    int pos = dung_chest_locations[WORD(overworld_map_state) >> 1] >> 1 & 0x1fff;

    WORD(dung_bg2_attr_table[pos + XY(0, 0)]) = attr;
    WORD(dung_bg2_attr_table[pos + XY(0, 1)]) = attr;
    attr += 0x101;

    const uint16 *src = SrcPtr(0x149c);
    dung_bg2[pos + XY(0, 0)] = src[0];
    dung_bg2[pos + XY(0, 1)] = src[1];
    dung_bg2[pos + XY(1, 0)] = src[2];
    dung_bg2[pos + XY(1, 1)] = src[3];

    uint16 yy = WORD(overworld_map_state);

    uint16 *dst = &vram_upload_data[vram_upload_offset >> 1];
    dst[0] = RoomTag_BuildChestStripes(XY(0, 0) * 2, yy);
    dst[3] = RoomTag_BuildChestStripes(XY(0, 1) * 2, yy);
    dst[6] = RoomTag_BuildChestStripes(XY(1, 0) * 2, yy);
    dst[9] = RoomTag_BuildChestStripes(XY(1, 1) * 2, yy);

    dst[2] = src[0];
    dst[5] = src[1];
    dst[8] = src[2];
    dst[11] = src[3];

    dst[1] = 0x100;
    dst[4] = 0x100;
    dst[7] = 0x100;
    dst[10] = 0x100;

    dst[12] = 0xffff;

    vram_upload_offset += 24;
    WORD(overworld_map_state) += 2;
  } while (WORD(overworld_map_state) != dung_num_chests_x2);
  WORD(overworld_map_state) = 0;
  sound_effect_2 = 26;
  nmi_load_bg_from_vram = 1;
}

void RoomTag_TorchPuzzleChest(int k) {  // 81c8ae
  int j = 0;
  for (int i = 0; i < 16; i++)
    if (dung_object_tilemap_pos[i] & 0x8000)
      j++;
  if (j >= 4)
    RoomTag_OperateChestReveal(k);
}

void RoomTag_MovingWall_East(int k) {  // 81c8d4
  static const int16 kMovingWall_Tab1[8] = { -63, -127, -191, -255, -71, -135, -199, -263 };

  if (!dung_floor_move_flags) {
    RoomTag_MovingWallTorchesCheck(k);
    dung_floor_x_vel = 0;
  } else {
    flag_unk1 = 1;
    RoomTag_MovingWallShakeItUp(k);
    dung_floor_x_vel = MovingWall_MoveALittle();
  }
  dung_floor_x_offs -= dung_floor_x_vel;
  BG1HOFS_copy2 = BG2HOFS_copy2 + dung_floor_x_offs;

  if (dung_floor_x_vel) {
    if (dung_floor_x_offs < (uint16)kMovingWall_Tab1[moving_wall_var2 >> 1] &&
        dung_floor_x_offs < (uint16)kMovingWall_Tab1[RoomTag_AdvanceGiganticWall(k) >> 1]) {
      sound_effect_2 = 0x1b;
      sound_effect_ambient = 5;
      dung_hdr_tag[k] = 0;
      flag_is_link_immobilized = 0;
      flag_unk1 = 0;
      bg1_x_offset = bg1_y_offset = 0;
    }
    nmi_subroutine_index = 5;
    nmi_load_target_addr = (moving_wall_var1 - ((-dung_floor_x_offs & 0x1f8) >> 3)) & 0x141f;
  }
}

void RoomTag_MovingWallShakeItUp(int k) {  // 81c969
  int i = frame_counter & 1;
  bg1_x_offset = i ? -1 : 1;
  bg1_y_offset = -bg1_x_offset;
  if (!dung_hdr_tag[k])
    bg1_x_offset = bg1_y_offset = 0;
}

void RoomTag_MovingWall_West(int k) {  // 81c98b
  static const uint16 kMovingWall_Tab0[8] = { 0x42, 0x82, 0xc2, 0x102, 0x4a, 0x8a, 0xca, 0x10a };

  if (!dung_floor_move_flags) {
    RoomTag_MovingWallTorchesCheck(k);
    dung_floor_x_vel = 0;
  } else {
    flag_unk1 = 1;
    RoomTag_MovingWallShakeItUp(k);
    dung_floor_x_vel = MovingWall_MoveALittle();
  }
  dung_floor_x_offs += dung_floor_x_vel;
  BG1HOFS_copy2 = BG2HOFS_copy2 + dung_floor_x_offs;
  if (dung_floor_x_vel) {
    if (dung_floor_x_offs >= kMovingWall_Tab0[moving_wall_var2 >> 1] &&
        dung_floor_x_offs >= kMovingWall_Tab0[RoomTag_AdvanceGiganticWall(k) >> 1]) {
      sound_effect_2 = 0x1b;
      sound_effect_ambient = 5;
      dung_hdr_tag[k] = 0;
      flag_is_link_immobilized = 0;
      flag_unk1 = 0;
      bg1_x_offset = bg1_y_offset = 0;
    }
    nmi_subroutine_index = 5;
    nmi_load_target_addr = moving_wall_var1 + ((dung_floor_x_offs & 0x1f8) >> 3);
    if (nmi_load_target_addr & 0x1020)
      nmi_load_target_addr = (nmi_load_target_addr & 0x1020) ^ 0x420;
  }
}

void RoomTag_MovingWallTorchesCheck(int k) {  // 81ca17
  if (!dung_flag_statechange_waterpuzzle) {
    int count = 0;
    for (int i = 0; i < 16; i++)
      count += (dung_object_tilemap_pos[i] & 0x8000) != 0;
    if (count < 4)
      return;
  }
  dung_floor_move_flags++;
  WORD(dung_flag_statechange_waterpuzzle) = 0;
  dung_savegame_state_bits |= 0x1000 >> k;
  sound_effect_ambient = 7;
  flag_is_link_immobilized = 1;
  flag_unk1 = 1;
}

int MovingWall_MoveALittle() {  // 81ca66
  int t = dung_some_subpixel[1] + 0x22;
  dung_some_subpixel[1] = t;
  return t >> 8;
}

int RoomTag_AdvanceGiganticWall(int k) {  // 81ca75
  int i = moving_wall_var2;
  if (dung_hdr_tag[k] < 0x20) {
    dung_hdr_collision = 0;
    TM_copy = 0x16;
    i += 8;
  }
  return i;
}

void RoomTag_WaterOff(int k) {  // 81ca94
  if (dung_flag_statechange_waterpuzzle) {
    W12SEL_copy = 3;
    W34SEL_copy = 0;
    WOBJSEL_copy = 0;
    TMW_copy = 22;
    TSW_copy = 1;
    turn_on_off_water_ctr = 1;
    AdjustWaterHDMAWindow();
    submodule_index = 11;
    palette_filter_countdown = 0;
    darkening_or_lightening_screen = 0;
    mosaic_target_level = 31;
    flag_update_cgram_in_nmi++;
    dung_hdr_tag[1] = 0;
    dung_savegame_state_bits |= 0x800;
    dung_flag_statechange_waterpuzzle = 0;
    int dsto = ((water_hdma_var1 & 0x1ff) - 0x10) << 3 | ((water_hdma_var0 & 0x1ff) - 0x10) >> 3;
    DrawWaterThing(&dung_bg2[dsto], SrcPtr(0x1438));
    Dungeon_PrepOverlayDma_nextPrep(0, dsto * 2);
    sound_effect_2 = 0x1b;
    sound_effect_1 = 0x2e;
    nmi_copy_packets_flag = 1;
  }
}

void RoomTag_WaterOn(int k) {  // 81cb1a
  if (dung_flag_statechange_waterpuzzle) {
    sound_effect_2 = 0x1b;
    sound_effect_1 = 0x2f;
    submodule_index = 12;
    subsubmodule_index = 0;
    BYTE(dung_floor_y_offs) = 1;
    dung_hdr_tag[1] = 0;
    dung_savegame_state_bits |= 0x800;
    dung_flag_statechange_waterpuzzle = 0;
    dung_cur_quadrant_upload = 0;
  }
}

void RoomTag_WaterGate(int k) {  // 81cb49
  if (dung_savegame_state_bits & 0x800 || !dung_flag_statechange_waterpuzzle)
    return;
  submodule_index = 13;
  subsubmodule_index = 0;
  dung_hdr_tag[1] = 0;
  dung_savegame_state_bits |= 0x800;
  dung_flag_statechange_waterpuzzle = 0;
  BYTE(water_hdma_var2) = 0;
  BYTE(spotlight_var4) = 0;
  W12SEL_copy = 3;
  W34SEL_copy = 0;
  WOBJSEL_copy = 0;
  TMW_copy = 0x16;
  TSW_copy = 1;
  CGWSEL_copy = 2;
  CGADSUB_copy = 0x62;
  save_ow_event_info[0x3b] |= 32;
  save_ow_event_info[0x7b] |= 32;
  save_dung_info[0x28] |= 0x100;
  RoomTag_OperateWaterFlooring();
  water_hdma_var0 = ((watergate_pos & 0x7e) << 2) + (dung_draw_width_indicator * 16 + dung_loade_bgoffs_h_copy + 40);
  word_7E0678 = spotlight_y_upper = (watergate_pos & 0x1f80) >> 4;
  water_hdma_var1 = word_7E0678 + dung_loade_bgoffs_v_copy;
  water_hdma_var3 = 0;
  sound_effect_2 = 0x1b;
  sound_effect_1 = 0x2f;
}

void Dung_TagRoutine_0x1B(int k) {  // 81cbff
  // empty
}

void RoomTag_Holes0(int k) {  // 81cc00
  Dung_TagRoutine_Func2(1);
}

void Dung_TagRoutine_0x23(int k) {  // 81cc04
  Dung_TagRoutine_Func2(3);
}

void Dung_TagRoutine_0x34(int k) {  // 81cc08
  Dung_TagRoutine_Func2(6);
}

void Dung_TagRoutine_0x35(int k) {  // 81cc0c
  Dung_TagRoutine_Func2(8);
}

void Dung_TagRoutine_0x36(int k) {  // 81cc10
  Dung_TagRoutine_Func2(10);
}

void Dung_TagRoutine_0x37(int k) {  // 81cc14
  Dung_TagRoutine_Func2(12);
}

void Dung_TagRoutine_0x39(int k) {  // 81cc18
  Dung_TagRoutine_Func2(14);
}

void Dung_TagRoutine_0x3A(int k) {  // 81cc1c
  Dung_TagRoutine_Func2(16);
}

void Dung_TagRoutine_Func2(uint8 av) {  // 81cc1e
  uint8 yv;
  if (!dung_overlay_to_load)
    dung_overlay_to_load = av;

  if (RoomTag_CheckForPressedSwitch(&yv) && (av += yv) != dung_overlay_to_load) {
    dung_overlay_to_load = av;
    dung_load_ptr_offs = 0;
    subsubmodule_index = 0;
    sound_effect_2 = 27;
    submodule_index = 3;
    byte_7E04BC ^= 1;
    Dungeon_RestoreStarTileChr();
  }
}

void RoomTag_ChestHoles0(int k) {  // 81cc5b
  Dung_TagRoutine_0x22_0x3B(k, 0x0);
}

void Dung_TagRoutine_0x3B(int k) {  // 81cc62
  Dung_TagRoutine_0x22_0x3B(k, 0x12);
}

void RoomTag_Holes2(int k) {  // 81cc89
  uint8 yv;

  if (!RoomTag_CheckForPressedSwitch(&yv))
    return;

  dung_hdr_tag[k] = 0;
  dung_overlay_to_load = 5;
  dung_load_ptr_offs = 0;
  subsubmodule_index = 0;
  sound_effect_2 = 0x1b;
  submodule_index = 3;
}

void RoomTag_OperateWaterFlooring() {  // 81cc95
  dung_load_ptr_offs = 0;
  const uint8 *layoutsrc = kWatergateLayout;
  for (;;) {
    dung_draw_width_indicator = 0;
    dung_draw_height_indicator = 0;
    uint16 t = WORD(*layoutsrc);
    if (t == 0xffff)
      break;
    dung_draw_width_indicator = (t & 3) + 1;
    dung_draw_height_indicator = (t >> 8 & 3) + 1;
    dung_load_ptr_offs += 3, layoutsrc += 3;
    const uint16 *src = SrcPtr(0x110);
    int dsto2 = (t & 0xfc) >> 2 | (t >> 10) << 6;
    do {
      int dsto = dsto2;
      int n = dung_draw_width_indicator;
      do {
        int nn = 2;
        do {
          dung_bg1[dsto + XY(0, 0)] = src[0];
          dung_bg1[dsto + XY(1, 0)] = src[1];
          dung_bg1[dsto + XY(2, 0)] = src[2];
          dung_bg1[dsto + XY(3, 0)] = src[3];
          dung_bg1[dsto + XY(0, 1)] = src[4];
          dung_bg1[dsto + XY(1, 1)] = src[5];
          dung_bg1[dsto + XY(2, 1)] = src[6];
          dung_bg1[dsto + XY(3, 1)] = src[7];
          dsto += XY(0, 2);
        } while (--nn);
        dsto += XY(4, -4);
      } while (--n);
      dsto2 += XY(0, 4);
    } while (--dung_draw_height_indicator);
  }
}

bool RoomTag_MaybeCheckShutters(uint8 *attr_out) {  // 81cd39
  int p, t;
  word_7E04B6 = 0;
  if (flag_is_link_immobilized || link_auxiliary_state)
    return false;
  p = RoomTag_GetTilemapCoords();
  t = WORD(dung_bg2_attr_table[p]);
  if (t == 0x2323 || t == 0x2424)
    goto done;
  t = WORD(dung_bg2_attr_table[p += 64]);
  if (t == 0x2323 || t == 0x2424)
    goto done;
  t = WORD(dung_bg2_attr_table[p -= 63]);
  if (t == 0x2323 || t == 0x2424)
    goto done;
  t = WORD(dung_bg2_attr_table[p += 64]);
  if (t == 0x2323 || t == 0x2424)
    goto done;
  return false;
done:
  if (t != WORD(dung_bg2_attr_table[p + 64]))
    return false;
  *attr_out = t;
  word_7E04B6 = p;
  return true;
}

int RoomTag_GetTilemapCoords() {  // 81cda5
  return ((link_x_coord - 1) & 0x1f8) >> 3 | ((link_y_coord + 14) & 0x1f8) << 3 | (link_is_on_lower_level ? 0x1000 : 0);

}

bool RoomTag_CheckForPressedSwitch(uint8 *y_out) {  // 81cdcc
  int p, t;
  word_7E04B6 = 0;
  if (flag_is_link_immobilized || link_auxiliary_state)
    return false;
  p = RoomTag_GetTilemapCoords();
  t = WORD(dung_bg2_attr_table[p]);
  if (t == 0x2323 || t == 0x3a3a || t == 0x3b3b)
    goto done;
  t = WORD(dung_bg2_attr_table[p += 64]);
  if (t == 0x2323 || t == 0x3a3a || t == 0x3b3b)
    goto done;
  t = WORD(dung_bg2_attr_table[p -= 63]);
  if (t == 0x2323 || t == 0x3a3a || t == 0x3b3b)
    goto done;
  t = WORD(dung_bg2_attr_table[p += 64]);
  if (t == 0x2323 || t == 0x3a3a || t == 0x3b3b)
    goto done;
  return false;
done:
  if (t != WORD(dung_bg2_attr_table[p + 64]))
    return false;
  *y_out = (t == 0x3b3b);
  word_7E04B6 = p;
  return true;
}

void Dungeon_ProcessTorchesAndDoors() {  // 81ce70
  static const int16 kDungLinkOffs1X[] = { 0, 0, -1, 17 };
  static const int16 kDungLinkOffs1Y[] = { 7, 24, 8, 8 };
  static const uint16 kDungLinkOffs1Pos[] = { 0x2, 0x2, 0x80, 0x80 };

  if ((frame_counter & 3) == 0 && !flag_custom_spell_anim_active) {
    for (int i = 0; i != 16; i++) {
      if (dung_torch_timers[i] && !--dung_torch_timers[i]) {
        byte_7E0333 = 0xc0 + i;
        Dungeon_ExtinguishTorch();
      }
    }
  }

  if (!flag_is_link_immobilized) {
    int dir = link_direction_facing >> 1;
    int pos = ((link_y_coord + kDungLinkOffs1Y[dir]) & 0x1f8) << 3;
    pos |= ((link_x_coord + kDungLinkOffs1X[dir]) & 0x1f8) >> 3;
    pos |= (link_is_on_lower_level ? 0x1000 : 0);

    if ((dung_bg2_attr_table[pos] & 0xf0) == 0xf0 ||
        (dung_bg2_attr_table[pos += kDungLinkOffs1Pos[dir]] & 0xf0) == 0xf0) {
      int k = dung_bg2_attr_table[pos] & 0xf;
      dung_which_key_x2 = 2 * k;

      if ((dung_door_direction[k] & 3) != dir)
        goto not_openable;

      uint8 door_type = door_type_and_slot[k] & 0xfe;
      if (door_type == kDoorType_BreakableWall) {
        if (link_is_running && link_dash_ctr < 63) {
          dung_cur_door_pos = pos;

          int db = AncillaAdd_DoorDebris();
          if (db >= 0) {
            door_debris_direction[db] = dung_door_direction[k] & 3;
            door_debris_x[db] = dung_loade_bgoffs_h_copy + (dung_door_tilemap_address[k] & 0x7e) * 4;
            door_debris_y[db] = dung_loade_bgoffs_v_copy + ((dung_door_tilemap_address[k] & 0x1f80) >> 4);
          }
          sound_effect_2 = 27;
          submodule_index = 9;
          Sprite_RepelDash();
          return;
        }
      } else if (door_type == kDoorType_1E) {
        door_animation_step_indicator = 0;
        dung_cur_door_pos = pos;
        if (link_bigkey & kUpperBitmasks[cur_palace_index_x2 >> 1])
          goto has_key_for_door;
        if (!big_key_door_message_triggered) {
          big_key_door_message_triggered = 1;
          dialogue_message_index = 0x7a;
          Main_ShowTextMessage();
        }
      } else if (door_type >= kDoorType_SmallKeyDoor && door_type < 0x2c && door_type != 0x2a && link_num_keys != 0) {
        link_num_keys -= 1;
has_key_for_door:
        door_animation_step_indicator = 0;
        dung_cur_door_pos = pos;
        submodule_index = 4;
        static const uint8 kOpenDoorPanning[] = { 0x0, 0x0, 0x80, 0x40 };
        sound_effect_2 = 20 | kOpenDoorPanning[dung_door_direction[k] & 3];
        return;
      }
    } else {
not_openable:
      big_key_door_message_triggered = 0;
    }
  }

  if (!(invisible_door_dir_and_index_x2 & 0x80) && !is_standing_in_doorway && (link_x_coord >> 8) == 0xc) {
    uint8 dir = invisible_door_dir_and_index_x2;
    int j = (invisible_door_dir_and_index_x2 >> 8) >> 1;
    uint16 m = dung_door_opened_incl_adjacent;
    if (dir != link_direction_facing && (dir ^ 2) == link_direction_facing)
      m |= kUpperBitmasks[j];
    else
      m &= ~kUpperBitmasks[j];
    if (m != dung_door_opened_incl_adjacent) {
      dung_door_opened_incl_adjacent = m;
      DrawEyeWatchDoor(j);
      Dungeon_PrepOverlayDma_nextPrep(0, dung_door_tilemap_address[j]);
      Dungeon_LoadToggleDoorAttr_OtherEntry(j);
      nmi_copy_packets_flag = 1;
      sound_effect_2 = 21;
      return;
    }
  }

  if (!(button_mask_b_y & 0x80) || button_b_frames != 4)
    return;

  int pos = ((link_y_coord + (int8)player_oam_y_offset) & 0x1f8) << 3;
  pos |= ((link_x_coord + (int8)player_oam_x_offset) & 0x1f8) >> 3;
  uint8 attr, y;

#define is_6c_fx(yv,x) (y=yv, ((attr = (dung_bg2_attr_table[x] & 0xfc)) == 0x6c || (attr & 0xf0) == 0xf0))

  if (!(is_6c_fx(0x41, pos) || is_6c_fx(0x40, pos += 1) || is_6c_fx(1, pos += 63) || is_6c_fx(0, pos += 1)))
    return;

  int addr;

  if (attr == 0x6c) {
    if (y & 0x40 && (dung_bg2_attr_table[pos -= 64] & 0xfc) != 0x6c)
      pos += 64;
    if (y & 1 && (dung_bg2_attr_table[pos -= 1] & 0xfc) != 0x6c)
      pos += 1;
    attr = dung_bg2_attr_table[pos];
    WriteAttr2(pos + XY(0, 0), 0x202);
    WriteAttr2(pos + XY(0, 1), 0x202);
    static const uint16 kSrcTiles1[] = { 0x7ea, 0x80a, 0x80a, 0x82a };
    addr = (pos - XY(1, 1)) * 2;
    RoomDraw_Object_Nx4(4, SrcPtr(kSrcTiles1[attr & 3]), &dung_bg2[addr >> 1]);
  } else {
    dung_cur_door_pos = pos;
    int k = attr & 0xf;

    uint8 door_type = door_type_and_slot[k];
    if (door_type != kDoorType_Slashable)
      return;
    sound_effect_2 = 27;
    addr = dung_door_tilemap_address[k];
    dung_door_opened_incl_adjacent |= kUpperBitmasks[k];
    dung_door_opened |= kUpperBitmasks[k];
    door_open_closed_counter = 0;
    dung_cur_door_idx = k * 2;
    dung_which_key_x2 = k * 2;
    RoomDraw_Object_Nx4(4, SrcPtr(kDoorTypeSrcData[0x56 / 2]), &dung_bg2[addr >> 1]);
    Dungeon_LoadToggleDoorAttr_OtherEntry(k);
  }

  Dungeon_PrepOverlayDma_nextPrep(0, addr);
  sound_effect_1 = 30 | CalculateSfxPan_Arbitrary((addr & 0x7f) * 2);
  nmi_copy_packets_flag = 1;
}

void Bomb_CheckForDestructibles(uint16 x, uint16 y, uint8 r14) {  // 81d1f4
  if (main_module_index != 7) {
    Overworld_BombTiles32x32(x, y);
    return;
  }
  int k = ((y & 0x1f8) << 3 | (x & 0x1f8) >> 3) - 0x82;
  uint8 a;
  for (int i = 2; i >= 0; i--) {
    a = dung_bg2_attr_table[k];
    if (a == 0x62) {
handle_62:
      if (dungeon_room_index == 0x65)
        dung_savegame_state_bits |= 0x1000;
      Point16U pt;
      printf("Wtf is R6\n");
      ThievesAttic_DrawLightenedHole(0, 0, &pt);
      sound_effect_2 = 0x1b;
      return;
    }
    if ((a & 0xf0) == 0xf0) {
      int j;
handle_f0:
      j = a & 0xf;
      a = door_type_and_slot[j] & 0xfe;
      if (a != kDoorType_BreakableWall && a != 0x2A && a != 0x2E)
        return;
      dung_cur_door_pos = k;
      door_debris_x[r14] = ((dung_door_tilemap_address[j] & 0x7e) << 2) + dung_loade_bgoffs_h_copy;
      door_debris_y[r14] = ((dung_door_tilemap_address[j] & 0x1f80) >> 4) + dung_loade_bgoffs_v_copy;
      door_debris_direction[r14] = dung_door_direction[j] & 3;
      sound_effect_2 = 0x1b;
      submodule_index = 9;
      return;
    }

    a = dung_bg2_attr_table[k += 2];
    if (a == 0x62) goto handle_62;
    if ((a & 0xf0) == 0xf0) goto handle_f0;

    a = dung_bg2_attr_table[k += 2];
    if (a == 0x62) goto handle_62;
    if ((a & 0xf0) == 0xf0) goto handle_f0;

    k += 0x7c;
  }

}

int DrawDoorOpening_Step1(int door, int dma_ptr) {  // 81d2e8
  dung_cur_door_idx = door * 2;
  dung_which_key_x2 = door * 2;
  switch (dung_door_direction[door] & 3) {
  case 0: return DoorDoorStep1_North(door, dma_ptr);
  case 1: return DoorDoorStep1_South(door, dma_ptr);
  case 2: return DoorDoorStep1_West(door, dma_ptr);
  case 3: return DoorDoorStep1_East(door, dma_ptr);
  }
  return 0;
}

void DrawShutterDoorSteps(int door) {  // 81d311
  dung_cur_door_idx = door * 2;
  dung_which_key_x2 = door * 2;
  switch (dung_door_direction[door] & 3) {
  case 0: GetDoorDrawDataIndex_North_clean_door_index(door); break;
  case 1: GetDoorDrawDataIndex_South_clean_door_index(door); break;
  case 2: GetDoorDrawDataIndex_West_clean_door_index(door); break;
  case 3: GetDoorDrawDataIndex_East_clean_door_index(door); break;
  }
}

void DrawEyeWatchDoor(int door) {  // 81d33a
  dung_cur_door_idx = door * 2;
  dung_which_key_x2 = door * 2;
  switch (dung_door_direction[door] & 3) {
  case 0: DrawDoorToTileMap_North(door, door); break;
  case 1: DrawDoorToTileMap_South(door, door); break;
  case 2: DrawDoorToTileMap_West(door, door); break;
  case 3: DrawDoorToTileMap_East(door, door); break;
  }
}

void Door_BlastWallExploding_Draw(int dsto) {  // 81d373
  uint16 *dst = &dung_bg2[dsto];
  const uint16 *src = SrcPtr(0x31ea);
  ClearExplodingWallFromTileMap_ClearOnePair(dst, src);
  dst += 2;
  uint16 v = src[24];
  for (int n = dung_unk_blast_walls_2 - 1; n; n--) {
    for (int j = 0; j < 12; j++)
      dst[XY(0, j)] = v;
    dst++;
  }
  ClearExplodingWallFromTileMap_ClearOnePair(dst, src + 25);
}

void OperateShutterDoors() {  // 81d38f
  int anim_dst = 0;
  uint8 y = 2;

  if (++door_animation_step_indicator != 4) {
    y = dung_flag_trapdoors_down ? 0 : 4;
    if (door_animation_step_indicator != 8)
      goto getout;
  }
  door_open_closed_counter = y;

  for (dung_cur_door_pos = 0; dung_cur_door_pos != 0x18; dung_cur_door_pos += 2) {
    int j = dung_cur_door_pos >> 1;
    uint8 door_type = door_type_and_slot[j] & 0xfe;
    if (door_type != kDoorType_Shutter && door_type != kDoorType_ShuttersTwoWay)
      continue;

    int mask = kUpperBitmasks[j];
    if (!dung_flag_trapdoors_down) {
      if (dung_door_opened_incl_adjacent & mask)
        continue;
      if (door_animation_step_indicator == 8) {
        sound_effect_2 = 21;
        dung_door_opened_incl_adjacent ^= mask;
      }
    } else {
      if (!(dung_door_opened_incl_adjacent & mask))
        continue;
      if (door_animation_step_indicator == 8) {
        sound_effect_2 = 22;
        dung_door_opened_incl_adjacent ^= mask;
      }
    }
    DrawShutterDoorSteps(j);
    anim_dst = Dungeon_PrepOverlayDma_nextPrep(anim_dst, dung_door_tilemap_address[j]);
    if (door_animation_step_indicator == 8)
      Dungeon_LoadToggleDoorAttr_OtherEntry(j);
  }
  dung_cur_door_pos -= 2;

  if (anim_dst != 0) {
    nmi_disable_core_updates = nmi_copy_packets_flag = 1;
getout:
    if (BYTE(door_animation_step_indicator) != 0x10)
      return;
  }
  submodule_index = 0;
  nmi_copy_packets_flag = 0;
}

void OpenCrackedDoor() {  // 81d469
  Dungeon_OpeningLockedDoor_Combined(true);
}

void Dungeon_LoadToggleDoorAttr_OtherEntry(int door) {  // 81d51c
  Dungeon_LoadSingleDoorAttribute(door);
  Dungeon_LoadSingleDoorTileAttribute();
}

void Dungeon_LoadSingleDoorTileAttribute() {  // 81d51f
  for (int i = 0; i != dung_num_toggle_floor; i += 2) {
    int j = dung_toggle_floor_pos[i >> 1];
    if ((dung_bg2_attr_table[j] & 0xf0) == 0x80) {
      uint16 attr = *(uint16 *)&dung_bg2_attr_table[j];
      WriteAttr2(j + XY(0, 0), attr | 0x1010);
      WriteAttr2(j + XY(0, 1), attr | 0x1010);
    } else {
      uint16 attr = *(uint16 *)&dung_bg1_attr_table[j];
      WriteAttr1(j + XY(0, 0), attr | 0x1010);
      WriteAttr1(j + XY(0, 1), attr | 0x1010);
    }
  }
  for (int i = 0; i != dung_num_toggle_palace; i += 2) {
    int j = dung_toggle_palace_pos[i >> 1];
    if ((dung_bg2_attr_table[j] & 0xf0) == 0x80) {
      uint16 attr = *(uint16 *)&dung_bg2_attr_table[j];
      WriteAttr2(j + XY(0, 0), attr | 0x2020);
      WriteAttr2(j + XY(0, 1), attr | 0x2020);
    } else {
      uint16 attr = *(uint16 *)&dung_bg1_attr_table[j];
      WriteAttr1(j + XY(0, 0), attr | 0x2020);
      WriteAttr1(j + XY(0, 1), attr | 0x2020);
    }
  }
}

void DrawCompletelyOpenDoor() {  // 81d5aa
  uint16 t;
  int i;

  for (i = 0, t = 0x3030; i != dung_num_inter_room_upnorth_stairs; i += 2, t += 0x101) {}

  for (; i != dung_num_wall_upnorth_spiral_stairs; i += 2, t += 0x101) {
    int pos = dung_inter_starcases[i >> 1];
    WriteAttr2(pos + XY(1, 0), 0x5e5e);
    WriteAttr2(pos + XY(1, 1), t);
    WriteAttr2(pos + XY(1, 2), 0);
    WriteAttr2(pos + XY(1, 3), 0);
  }

  for (; i != dung_num_wall_upnorth_spiral_stairs_2; i += 2, t += 0x101) {
    int pos = dung_inter_starcases[i >> 1];
    WriteAttr2(pos + XY(1, 0), 0x5f5f);
    WriteAttr2(pos + XY(1, 1), t);
    WriteAttr2(pos + XY(1, 2), 0);
    WriteAttr2(pos + XY(1, 3), 0);
  }

  for (; i != dung_num_inter_room_upnorth_straight_stairs; i += 2, t += 0x101) {}
  for (; i != dung_num_inter_room_upsouth_straight_stairs; i += 2, t += 0x101) {}

  t = (t & 0x707) | 0x3434;

  for (; i != dung_num_inter_room_southdown_stairs; i += 2, t += 0x101) {}

  for (; i != dung_num_wall_downnorth_spiral_stairs; i += 2, t += 0x101) {
    int pos = dung_inter_starcases[i >> 1];
    WriteAttr2(pos + XY(1, 0), 0x5e5e);
    WriteAttr2(pos + XY(1, 1), t);
    WriteAttr2(pos + XY(1, 2), 0);
    WriteAttr2(pos + XY(1, 3), 0);
  }

  for (; i != dung_num_wall_downnorth_spiral_stairs_2; i += 2, t += 0x101) {
    int pos = dung_inter_starcases[i >> 1];
    WriteAttr2(pos + XY(1, 0), 0x5f5f);
    WriteAttr2(pos + XY(1, 1), t);
    WriteAttr2(pos + XY(1, 2), 0);
    WriteAttr2(pos + XY(1, 3), 0);
  }
}

void Dungeon_ClearAwayExplodingWall() {  // 81d6c1
  flag_is_link_immobilized = 6;
  flag_unk1 = 6;
  if (BYTE(messaging_buf[0]) != 6)
    return;

  word_7E045E = 0;
  g_ram[12] = 0;
  door_animation_step_indicator = 0;
  dung_cur_door_idx = dung_unk_blast_walls_3;
  int dsto = (dung_door_tilemap_address[dung_unk_blast_walls_3 >> 1] -= 2) >> 1;

  Door_BlastWallExploding_Draw(dsto);
  ClearAndStripeExplodingWall(dsto);

  WORD(nmi_disable_core_updates) = 0xffff;
  dung_unk_blast_walls_2 += 2;

  if (dung_unk_blast_walls_2 == 21) {
    int m = kUpperBitmasks[dung_unk_blast_walls_3 >> 1];
    dung_door_opened_incl_adjacent |= m;
    dung_door_opened |= m;

    if (dung_door_direction[dung_unk_blast_walls_3 >> 1] & 2) {
      dung_blastwall_flag_x = 1;
      quadrant_fullsize_x = 2;
    } else {
      dung_blastwall_flag_y = 1;
      quadrant_fullsize_y = 2;
    }
    WORD(quadrant_fullsize_x_cached) = WORD(quadrant_fullsize_x);
    Door_LoadBlastWallAttr(dung_unk_blast_walls_3 >> 1);
    dung_unk_blast_walls_2 = 0;
    dung_unk_blast_walls_3 = 0;
    Dungeon_FlagRoomData_Quadrants();
    flag_is_link_immobilized = 0;
    flag_unk1 = 0;
  }
  nmi_copy_packets_flag = 3;
}

uint16 Dungeon_CheckForAndIDLiftableTile() {  // 81d748
  uint16 x = (link_x_coord + kDungeon_QueryIfTileLiftable_x[link_direction_facing >> 1]) & 0x1f8;
  uint16 y = (link_y_coord + kDungeon_QueryIfTileLiftable_y[link_direction_facing >> 1]) & 0x1f8;
  uint16 xy = (y << 3) | (x >> 3) | (link_is_on_lower_level ? 0x1000 : 0x0);

  uint8 attr = dung_bg2_attr_table[xy];
  if ((attr & 0xf0) != 0x70)
    return 0xffff;  // clc

  uint16 rt = dung_replacement_tile_state[attr & 0xf];
  if (rt == 0)
    return 0xffff;
  if ((rt & 0xf0f0) == 0x2020)
    return 0x55;
  return kDungeon_QueryIfTileLiftable_rv[rt & 0xf];
}

void Dungeon_PushBlock_Handler() {  // 81d81b
  while (dung_misc_objs_index != dung_index_of_torches_start) {
    int k = dung_misc_objs_index >> 1;
    int st = dung_replacement_tile_state[k];
    if (st == 1) {
      RoomDraw_16x16Single(k * 2);
      dung_object_tilemap_pos[k] += kPushBlockMoveDistances[push_block_direction >> 1];
      dung_replacement_tile_state[k] = 2;
    } else if (st == 2) {
      PushBlock_Slide(k * 2);

      if (dung_replacement_tile_state[dung_misc_objs_index >> 1] == 3) {
        PushBlock_CheckForPit(dung_misc_objs_index);
        dung_replacement_tile_state[dung_misc_objs_index >> 1]++;
      }
    } else if (st == 4) {
      PushBlock_HandleFalling(k * 2);
    }

    dung_misc_objs_index += 2;
  }
}

void RoomDraw_16x16Single(uint8 index) {  // 81d828
  index >>= 1;
  uint16 pos = (dung_object_tilemap_pos[index] & 0x3fff) >> 1;
  Dungeon_Store2x2(pos,
                   replacement_tilemap_UL[index],
                   replacement_tilemap_LL[index],
                   replacement_tilemap_UR[index],
                   replacement_tilemap_LR[index],
                   attributes_for_tile[replacement_tilemap_LR[index] & 0x3ff]);
}

void PushBlock_CheckForPit(uint8 y) {  // 81d8d4
  y >>= 1;
  if (!(dung_object_tilemap_pos[y] & 0x4000))
    dung_flag_movable_block_was_pushed ^= 1;

  int p = (dung_object_tilemap_pos[y] & 0x3fff) >> 1;
  uint8 attr = dung_bg2_attr_table[p];
  if (attr == 0x20) {  // fall into pit
    sound_effect_1 = 0x20;
    int k = dung_object_pos_in_objdata[y] >> 2;
    movable_block_datas[k].room = dung_hdr_travel_destinations[0];
    movable_block_datas[k].tilemap = dung_object_tilemap_pos[y];
    return;
  }

  int i = (index_of_changable_dungeon_objs[1] - 1) == y;
  index_of_changable_dungeon_objs[i] = 0;

  if (attr == 0x23) {
    related_to_trapdoors_somehow = dung_flag_trapdoors_down ^ 1;
    dung_replacement_tile_state[y] = 4;
  } else {
    dung_replacement_tile_state[y] = 0xffff;
  }
  Dungeon_Store2x2(p, 0x922, 0x932, 0x923, 0x933, 0x27);
}

uint8 Dungeon_LiftAndReplaceLiftable(Point16U *pt) {  // 81d9ec
  uint16 x = link_x_coord + kDungeon_QueryIfTileLiftable_x[link_direction_facing >> 1];
  uint16 y = link_y_coord + kDungeon_QueryIfTileLiftable_y[link_direction_facing >> 1];
  pt->x = x;
  pt->y = y;

  R16 = y;
  R18 = x;

  x &= 0x1f8;
  y &= 0x1f8;
  uint16 xy = (y << 3) | (x >> 3) | (link_is_on_lower_level ? 0x1000 : 0x0);

  uint8 attr = dung_bg2_attr_table[xy];

  assert((attr & 0x70) == 0x70);

  attr &= 0xf;
  uint16 rt = dung_replacement_tile_state[attr];

  if ((rt & 0xf0f0) == 0x1010) {
    dung_misc_objs_index = attr * 2;
    RevealPotItem(xy, dung_object_tilemap_pos[attr]);
    RoomDraw_16x16Single(dung_misc_objs_index);
    ManipBlock_Something(pt);
    return kDungeon_QueryIfTileLiftable_rv[rt & 0xf];
  } else if ((rt & 0xf0f0) == 0x2020) {
    return ThievesAttic_DrawLightenedHole(xy, (attr - (rt & 0xf)) * 2, pt);
  } else {
    return 0;
  }
  return 0;
}

uint8 ThievesAttic_DrawLightenedHole(uint16 pos6, uint16 a, Point16U *pt) {  // 81da71
  dung_misc_objs_index = a;
  RevealPotItem(pos6, dung_object_tilemap_pos[a >> 1]);
  RoomDraw_16x16Single(a);
  RoomDraw_16x16Single(a + 2);
  RoomDraw_16x16Single(a + 4);
  RoomDraw_16x16Single(a + 6);
  ManipBlock_Something(pt);
  return 0x55;
}

uint8 HandleItemTileAction_Dungeon(uint16 x, uint16 y) {  // 81dabb
  if (!(link_item_in_hand & 2)) {
    if (!(enhanced_features0 & kFeatures0_BreakPotsWithSword) ||
        button_b_frames == 0 || link_sword_type == 1)
      return 0;
  }
  uint16 pos = (y & 0x1f8) * 8 + x + (link_is_on_lower_level ? 0x1000 : 0);
  uint16 tile = dung_bg2_attr_table[pos];
  if ((tile & 0xf0) == 0x70) {
    uint16 tile2 = dung_replacement_tile_state[tile & 0xf];
    if ((tile2 & 0xf0f0) == 0x4040) {  // Hammer peg
      if (!(link_item_in_hand & 2))
        return 0;  // only hammers on pegs
      dung_misc_objs_index = (tile & 0xf) * 2;
      RoomDraw_16x16Single(dung_misc_objs_index);
      sound_effect_1 = 0x11;
    } else if ((tile2 & 0xf0f0) == 0x1010) {  // Pot
      dung_misc_objs_index = (tile & 0xf) * 2;
      RevealPotItem(pos, dung_object_tilemap_pos[tile & 0xf]);
      RoomDraw_16x16Single(dung_misc_objs_index);
      Point16U pt;
      ManipBlock_Something(&pt);
      BYTE(dung_secrets_unk1) |= 0x80;
      Sprite_SpawnImmediatelySmashedTerrain(1, pt.x, pt.y);
      AncillaAdd_BushPoof(pt.x, pt.y);  // return value wtf?
    }
  }
  return 0;
}

void ManipBlock_Something(Point16U *pt) {  // 81db41
  uint16 pos = dung_object_tilemap_pos[dung_misc_objs_index >> 1];
  pt->x = (link_x_coord & 0xfe00) | ((pos & 0x007e) << 2);
  pt->y = (link_y_coord & 0xfe00) | ((pos & 0x1f80) >> 4);
}

void RevealPotItem(uint16 pos6, uint16 pos4) {  // 81e6b2
  BYTE(dung_secrets_unk1) = 0;

  const uint8 *src_ptr = kDungeonSecrets + WORD(kDungeonSecrets[dungeon_room_index * 2]);

  int index = 0;
  for (;;) {
    uint16 test_pos = *(uint16 *)src_ptr;
    if (test_pos == 0xffff)
      return;
    assert(!(test_pos & 0x8000));
    if (test_pos == pos4)
      break;
    src_ptr += 3;
    index++;
  }

  uint8 data = src_ptr[2];
  if (data == 0)
    return;

  if (data < 0x80) {
    if (data != 8) {
      uint16 mask = 1 << index;
      uint16 *pr = &pots_revealed_in_room[dungeon_room_index];
      if (*pr & mask)
        return;
      *pr |= mask;
    }
    BYTE(dung_secrets_unk1) |= data;
  } else if (data != 0x88) {
    int j = dung_bg2_attr_table[pos6] & 0xf;
    int k = (j - (dung_replacement_tile_state[j] & 0xf));
    dung_misc_objs_index = 2 * k;
    sound_effect_2 = 0x1b;
    const uint16 *src = SrcPtr(0x5ba);
    for (int i = 0; i < 4; i++, k++, src += 4) {
      replacement_tilemap_UL[k] = src[0];
      replacement_tilemap_LL[k] = src[1];
      replacement_tilemap_UR[k] = src[2];
      replacement_tilemap_LR[k] = src[3];
    }
  } else {
    int k = dung_misc_objs_index >> 1;
    replacement_tilemap_UL[k] = 0xD0B;
    replacement_tilemap_LL[k] = 0xD1B;
    replacement_tilemap_UR[k] = 0x4D0B;
    replacement_tilemap_LR[k] = 0x4D1B;
  }
}

void Dungeon_UpdateTileMapWithCommonTile(int x, int y, uint8 v) {  // 81e7a9
  if (v == 8)
    Dungeon_PrepSpriteInducedDma(x + 16, y, v + 2);
  Dungeon_PrepSpriteInducedDma(x, y, v);
  nmi_load_bg_from_vram = 1;
}

void Dungeon_PrepSpriteInducedDma(int x, int y, uint8 v) {  // 81e7df
  static const uint16 kPrepSpriteInducedDma_Srcs[10] = { 0xe0, 0xade, 0x5aa, 0x198, 0x210, 0x218, 0x1f3a, 0xeaa, 0xeb2, 0x140 };
  int pos = ((y + 1) & 0x1f8) << 3 | (x & 0x1f8) >> 3;
  const uint16 *src = SrcPtr(kPrepSpriteInducedDma_Srcs[v >> 1]);
  uint16 *dst = &vram_upload_data[vram_upload_offset >> 1];
  dst[0] = Dungeon_MapVramAddr(pos + 0);
  dst[3] = Dungeon_MapVramAddr(pos + 64);
  dst[6] = Dungeon_MapVramAddr(pos + 1);
  dst[9] = Dungeon_MapVramAddr(pos + 65);
  uint8 attr = attributes_for_tile[src[3] & 0x3ff];
  dung_bg2_attr_table[pos + XY(0, 0)] = attr;
  dung_bg2_attr_table[pos + XY(0, 1)] = attr;
  dung_bg2_attr_table[pos + XY(1, 0)] = attr;
  dung_bg2_attr_table[pos + XY(1, 1)] = attr;
  dung_bg2[pos + XY(0, 0)] = dst[2] = src[0];
  dung_bg2[pos + XY(0, 1)] = dst[5] = src[1];
  dung_bg2[pos + XY(1, 0)] = dst[8] = src[2];
  dung_bg2[pos + XY(1, 1)] = dst[11] = src[3];
  dst[1] = 0x100;
  dst[4] = 0x100;
  dst[7] = 0x100;
  dst[10] = 0x100;
  dst[12] = 0xffff;
  vram_upload_offset += 24;
}

void Dungeon_DeleteRupeeTile(uint16 x, uint16 y) {  // 81e8bd
  int pos = (y & 0x1f8) * 8 | (x & 0x1f8) >> 3;
  uint16 *dst = &vram_upload_data[vram_upload_offset >> 1];
  dst[2] = 0x190f;
  dst[5] = 0x190f;
  dung_bg2[pos + XY(0, 0)] = 0x190f;
  dung_bg2[pos + XY(0, 1)] = 0x190f;
  uint16 attr = attributes_for_tile[0x190f & 0x3ff] * 0x101;
  WORD(dung_bg2_attr_table[pos + XY(0, 0)]) = attr;
  WORD(dung_bg2_attr_table[pos + XY(0, 1)]) = attr;
  dst[0] = Dungeon_MapVramAddr(pos + XY(0, 0));
  dst[3] = Dungeon_MapVramAddr(pos + XY(0, 1));
  dst[1] = 0x100;
  dst[4] = 0x100;
  dst[6] = 0xffff;
  vram_upload_offset += 24;
  dung_savegame_state_bits |= 0x1000;
  nmi_load_bg_from_vram = 1;
}

// This doesn't return exactly like the original
// Also returns in scratch_0
uint8 OpenChestForItem(uint8 tile, int *chest_position) {  // 81eb66
  static const uint16 kChestOpenMasks[] = { 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000 };
  if (tile == 0x63)
    return OpenMiniGameChest(chest_position);

  int chest_idx = tile - 0x58, chest_idx_org = chest_idx;

  uint16 loc = dung_chest_locations[chest_idx], pos, chest_room;
  uint8 data = 0xff;
  const uint16 *ptr;

  if (loc >= 0x8000) {
    // big key lock
    if (!(link_bigkey & kUpperBitmasks[cur_palace_index_x2 >> 1])) {
      dialogue_message_index = 0x7a;
      Main_ShowTextMessage();
      return 0xff;
    } else {
      dung_savegame_state_bits |= kChestOpenMasks[chest_idx];
      sound_effect_1 = 0x29;
      sound_effect_2 = 0x15;
      pos = (loc & 0x7fff) >> 1;

      ptr = SrcPtr(dung_floor_2_filler_tiles);
      overworld_tileattr[pos + 0] = ptr[0];
      overworld_tileattr[pos + 64] = ptr[1];
      overworld_tileattr[pos + 1] = ptr[2];
      overworld_tileattr[pos + 65] = ptr[3];
      goto afterStoreCrap;
    }
  } else {
    const uint8 *chest_data;
    int i;
    chest_data = kDungeonRoomChests;
    for (i = 0; i < kDungeonRoomChests_SIZE; i += 3, chest_data += 3) {
      chest_room = *(uint16 *)chest_data;
      if ((chest_room & 0x7fff) == dungeon_room_index && --chest_idx < 0) {
        data = chest_data[2];
        if (chest_room & 0x8000) {
          if (!(link_bigkey & kUpperBitmasks[cur_palace_index_x2 >> 1])) {
            dialogue_message_index = 0x7a;
            Main_ShowTextMessage();
            return 0xff;
          }
          dung_savegame_state_bits |= kChestOpenMasks[chest_idx_org];
          OpenBigChest(loc, chest_position);
          return data;
        } else {
          dung_savegame_state_bits |= kChestOpenMasks[chest_idx_org];
          ptr = SrcPtr(0x14A4);
          pos = loc >> 1;

          overworld_tileattr[pos + 0] = ptr[0];
          overworld_tileattr[pos + 64] = ptr[1];
          overworld_tileattr[pos + 1] = ptr[2];
          overworld_tileattr[pos + 65] = ptr[3];

          uint8 attr;
afterStoreCrap:
          attr = (loc < 0x8000) ? 0x27 : 0x00;

          dung_bg2_attr_table[pos + 0] = attr;
          dung_bg2_attr_table[pos + 64] = attr;
          dung_bg2_attr_table[pos + 1] = attr;
          dung_bg2_attr_table[pos + 65] = attr;

          uint16 *dst = &vram_upload_data[vram_upload_offset >> 1];
          dst[0] = Dungeon_MapVramAddr(pos + 0);
          dst[3] = Dungeon_MapVramAddr(pos + 64);
          dst[6] = Dungeon_MapVramAddr(pos + 1);
          dst[9] = Dungeon_MapVramAddr(pos + 65);

          dst[2] = ptr[0];
          dst[5] = ptr[1];
          dst[8] = ptr[2];
          dst[11] = ptr[3];

          dst[1] = 0x100;
          dst[4] = 0x100;
          dst[7] = 0x100;
          dst[10] = 0x100;

          dst[12] = 0xffff;

          vram_upload_offset += 24;
          nmi_load_bg_from_vram = 1;
          Dungeon_FlagRoomData_Quadrants();
          if (sound_effect_2 == 0)
            sound_effect_2 = 14;

          *chest_position = loc & 0x7fff;
          return data;
        }
      }
    }
    return 0xff;
  }
}

void OpenBigChest(uint16 loc, int *chest_position) {  // 81ed05
  uint16 pos = loc >> 1;
  const uint16 *src = SrcPtr(0x14C4);

  for (int i = 0; i < 4; i++) {
    dung_bg2[pos + XY(i, 0)] = src[0];
    dung_bg2[pos + XY(i, 1)] = src[1];
    dung_bg2[pos + XY(i, 2)] = src[2];
    src += 3;
  }

  Dungeon_PrepOverlayDma_nextPrep(0, loc);
  *chest_position = (loc + 2);
  WORD(dung_bg2_attr_table[pos + XY(0, 0)]) = 0x2727;
  WORD(dung_bg2_attr_table[pos + XY(2, 0)]) = 0x2727;
  WORD(dung_bg2_attr_table[pos + XY(0, 1)]) = 0x2727;
  WORD(dung_bg2_attr_table[pos + XY(2, 1)]) = 0x2727;
  WORD(dung_bg2_attr_table[pos + XY(0, 2)]) = 0x2727;
  WORD(dung_bg2_attr_table[pos + XY(2, 2)]) = 0x2727;
  Dungeon_FlagRoomData_Quadrants();
  sound_effect_2 = 14;
  nmi_copy_packets_flag = 1;
  byte_7E0B9E = 1;
}

uint8 OpenMiniGameChest(int *chest_position) {  // 81edab
  int t;
  if (minigame_credits == 0) {
    dialogue_message_index = 0x163;
    Main_ShowTextMessage();
    return 0xff;
  }
  if (minigame_credits == 255) {
    dialogue_message_index = 0x162;
    Main_ShowTextMessage();
    return 0xff;
  }
  minigame_credits--;

  int pos = ((link_y_coord - 4) & 0x1f8) * 8;
  pos |= ((link_x_coord + 7) & 0x1f8) >> 3;

  if (WORD(dung_bg2_attr_table[pos]) != 0x6363) {
    pos--;
    if (WORD(dung_bg2_attr_table[pos]) != 0x6363)
      pos += 2;
  }

  *chest_position = pos * 2;

  WORD(dung_bg2_attr_table[pos + XY(0, 0)]) = 0x202;
  WORD(dung_bg2_attr_table[pos + XY(0, 1)]) = 0x202;

  const uint16 *src = SrcPtr(0x14A4);

  int pos_wrong = pos + XY(0, 2);  // zelda bug?
  dung_bg2[pos_wrong + XY(0, 0)] = src[0];
  dung_bg2[pos_wrong + XY(0, 1)] = src[1];
  dung_bg2[pos_wrong + XY(1, 0)] = src[2];
  dung_bg2[pos_wrong + XY(1, 1)] = src[3];

  // The orig asm code seems to access invalid vram here because it indexes by 0x14a4
  uint16 *dst = &vram_upload_data[vram_upload_offset >> 1];
  dst[0] = Dungeon_MapVramAddr(pos + 0);
  dst[3] = Dungeon_MapVramAddr(pos + 64);
  dst[6] = Dungeon_MapVramAddr(pos + 1);
  dst[9] = Dungeon_MapVramAddr(pos + 65);

  dst[2] = src[0];
  dst[5] = src[1];
  dst[8] = src[2];
  dst[11] = src[3];

  dst[1] = 0x100;
  dst[4] = 0x100;
  dst[7] = 0x100;
  dst[10] = 0x100;

  dst[12] = 0xffff;

  vram_upload_offset += 24;

  uint8 rv;

  uint16 r16 = some_menu_ctr;

  t = GetRandomNumber();
  if (BYTE(dungeon_room_index) == 0) {
    t = t & 0xf;
    rv = kDungeon_RupeeChestMinigamePrizes[t & 0xf];

  } else if (BYTE(dungeon_room_index) == 0x18) {
    t = 0x10 + (t & 0xf);
    rv = kDungeon_RupeeChestMinigamePrizes[0x10 + (t & 0xf)];
  } else {
    t &= 7;
    if (t >= 2 && t == r16) {
      t = (t + 1) & 7;
    }
    if (t == 7) {
      if (dung_savegame_state_bits & 0x4000) {
        t = 0;
      } else {
        dung_savegame_state_bits |= 0x4000;
      }
    }
    rv = kDungeon_MinigameChestPrizes1[t];
  }
  some_menu_ctr = t;
  nmi_load_bg_from_vram = 1;
  sound_effect_2 = 14;
  return rv;
}

uint16 RoomTag_BuildChestStripes(uint16 pos, uint16 y) {  // 81ef0f
  pos += dung_chest_locations[y >> 1];
  return swap16(((pos & 0x40) << 4) | ((pos & 0x303f) >> 1) | ((pos & 0xf80) >> 2));
}

void Dungeon_SetAttrForActivatedWaterOff() {  // 81ef93
  CGWSEL_copy = 2;
  CGADSUB_copy = 0x32;
  TS_copy = 0;
  W12SEL_copy = 0;
  dung_hdr_collision = 0;
  WORD(TMW_copy) = 0;
  for (int j = 0; j != dung_num_inroom_upnorth_stairs_water; j += 2) {
    int dsto = dung_stairs_table_1[j >> 1];
    WriteAttr2(dsto + XY(1, 1), 0x1d1d);
    WriteAttr2(dsto + XY(1, 2), 0x1d1d);
  }
  for (int j = 0; j != dung_num_inroom_upsouth_stairs_water; j += 2) {
    int dsto = dung_stairs_table_2[j >> 1];
    WriteAttr2(dsto + XY(1, 1), 0x1d1d);
    WriteAttr2(dsto + XY(1, 2), 0x1d1d);
  }
  flag_update_cgram_in_nmi++;
  subsubmodule_index++;
}

void Dungeon_FloodSwampWater_PrepTileMap() {  // 81f046
  WaterFlood_BuildOneQuadrantForVRAM();
  dung_cur_quadrant_upload += 4;
  if (++subsubmodule_index == 6) {
    dung_cur_quadrant_upload = 0;
    subsubmodule_index = 0;
    submodule_index = 0;
  }
}

void Dungeon_AdjustWaterVomit(const uint16 *src, int depth) {  // 81f0c9
  int dsto = (word_7E047C >> 1) + XY(0, 2);
  uint16 *dst = &dung_bg2[dsto];
  do {
    dst[0] = src[0];
    dst[1] = src[1];
    dst[2] = src[2];
    dst[3] = src[3];
    dst += XY(0, 1), src += 4;
  } while (--depth);
  uint16 *vram = vram_upload_data;
  for (int i = 0; i < 4; i++) {
    uint16 *dst = &dung_bg2[dsto];
    vram[0] = Dungeon_MapVramAddr(dsto);
    vram[1] = 0x980;
    vram[2] = dst[XY(0, 0)];
    vram[3] = dst[XY(0, 1)];
    vram[4] = dst[XY(0, 2)];
    vram[5] = dst[XY(0, 3)];
    vram[6] = dst[XY(0, 4)];
    vram += 7, dsto++;
  }
  vram[0] = 0xffff;
  nmi_load_bg_from_vram = 1;
}

void Dungeon_SetAttrForActivatedWater() {  // 81f237
  WORD(TMW_copy) = 0;
  for (int j = 0; j != dung_num_interpseudo_upnorth_stairs; j += 2) {
    int dsto = dung_stairs_table_1[j >> 1];
    WriteAttr2(dsto + 0, 0x003);
    WriteAttr2(dsto + 2, 0x300);
    WriteAttr1(dsto + 0, 0xa03);
    WriteAttr1(dsto + 2, 0x30a);
    WriteAttr2(dsto + XY(0, 1), 0x808);
    WriteAttr2(dsto + XY(2, 1), 0x808);
    WriteAttr1(dsto + XY(0, 1), 0x808);
    WriteAttr1(dsto + XY(2, 1), 0x808);
    WriteAttr1(dsto + XY(0, 2), 0x808);
    WriteAttr1(dsto + XY(2, 2), 0x808);
    WriteAttr1(dsto + XY(0, 3), 0x808);
    WriteAttr1(dsto + XY(2, 3), 0x808);
  }

  for (int j = 0; j != dung_num_stairs_wet; j += 2) {
    int dsto = dung_stairs_table_2[j >> 1];
    WriteAttr2(dsto + XY(0, 3), 0x003);
    WriteAttr2(dsto + XY(2, 3), 0x300);
    WriteAttr1(dsto + XY(0, 3), 0xa03);
    WriteAttr1(dsto + XY(2, 3), 0x30a);
    WriteAttr2(dsto + XY(0, 2), 0x808);
    WriteAttr2(dsto + XY(2, 2), 0x808);
    WriteAttr1(dsto + XY(0, 0), 0x808);
    WriteAttr1(dsto + XY(2, 0), 0x808);
    WriteAttr1(dsto + XY(0, 1), 0x808);
    WriteAttr1(dsto + XY(2, 1), 0x808);
    WriteAttr1(dsto + XY(0, 2), 0x808);
    WriteAttr1(dsto + XY(2, 2), 0x808);
  }
  submodule_index = 0;
  nmi_boolean = 0; // wtf
  subsubmodule_index = 0;
}

void FloodDam_Expand() {  // 81f30c
  watergate_var1++;
  water_hdma_var3 = watergate_var1 >> 1;
  uint8 r0 = water_hdma_var3 - 8;
  BYTE(spotlight_y_upper) = word_7E0678;
  BYTE(spotlight_var4) += 1;
  BYTE(water_hdma_var2) = spotlight_var4 + r0;

  if (watergate_var1 & 0xf)
    return;

  if (watergate_var1 == 64)
    subsubmodule_index++;

  static const uint16 kWatergateSrcs1[] = { 0x12f8, 0x1348, 0x1398, 0x13e8 };
  RoomDraw_Object_Nx4(10, SrcPtr(kWatergateSrcs1[(watergate_var1 >> 4) - 1]), &dung_bg2[watergate_pos >> 1]);
  int pos = watergate_pos;
  int n = 3;
  int dma_ptr = 0;
  do {
    dma_ptr = Dungeon_PrepOverlayDma_watergate(dma_ptr, pos, 0x881, 4);
    pos += 6;
  } while (--n);
  nmi_copy_packets_flag = 1;
}

void FloodDam_PrepTiles_init() {  // 81f3a7
  dung_cur_quadrant_upload = 0;
  overworld_screen_transition = 0;
  WaterFlood_BuildOneQuadrantForVRAM();
  dung_cur_quadrant_upload += 4;
  subsubmodule_index++;
}

void Watergate_Main_State1() {  // 81f3aa
  overworld_screen_transition = 0;
  WaterFlood_BuildOneQuadrantForVRAM();
  dung_cur_quadrant_upload += 4;
  subsubmodule_index++;
}

void FloodDam_Fill() {  // 81f3bd
  BYTE(water_hdma_var2)++;
  uint8 t = water_hdma_var2 + spotlight_y_upper;
  if (t >= 225) {
    dung_cur_quadrant_upload = 0;
    submodule_index = 0;
    subsubmodule_index = 0;
    TMW_copy = 0;
    TSW_copy = 0;
    IrisSpotlight_ResetTable();
  }
}

void Ganon_ExtinguishTorch_adjust_translucency() {  // 81f496
  Palette_AssertTranslucencySwap();
  byte_7E0333 = 0xc0;
  Dungeon_ExtinguishTorch();
}

void Ganon_ExtinguishTorch() {  // 81f4a1
  byte_7E0333 = 193;
  Dungeon_ExtinguishTorch();
}

void Dungeon_ExtinguishTorch() {  // 81f4a6
  int y = (byte_7E0333 & 0xf) * 2 + dung_index_of_torches_start;

  uint16 r8 = (dung_object_tilemap_pos[y >> 1] &= 0x7fff);

  dung_torch_data[(dung_object_pos_in_objdata[y >> 1] & 0xff) >> 1] = r8;

  r8 &= 0x3fff;
  RoomDraw_AdjustTorchLightingChange(r8, 0xec2, r8);
  nmi_copy_packets_flag = 1;

  if (dung_want_lights_out && dung_num_lit_torches != 0 && --dung_num_lit_torches < 3) {
    if (dung_num_lit_torches == 0)
      TS_copy = 1;
    overworld_fixed_color_plusminus = kLitTorchesColorPlus[dung_num_lit_torches];
    submodule_index = 10;
    subsubmodule_index = 0;
  }

  dung_torch_timers[byte_7E0333 & 0xf] = 0;
  byte_7E0333 = 0;
}

void SpiralStairs_MakeNearbyWallsHighPriority_Entering() {  // 81f528
  int pos = dung_inter_starcases[which_staircase_index & 3] - 4;
  word_7E048C = pos * 2;
  uint16 *dst = &dung_bg2[pos];
  for (int i = 0; i < 5; i++) {
    dst[XY(0, 0)] |= 0x2000;
    dst[XY(0, 1)] |= 0x2000;
    dst[XY(0, 2)] |= 0x2000;
    dst[XY(0, 3)] |= 0x2000;
    dst += 1;
  }
  int dp = Dungeon_PrepOverlayDma_nextPrep(0, pos * 2);
  Dungeon_PrepOverlayDma_nextPrep(dp, pos * 2 + 8);
  nmi_copy_packets_flag = 1;
}

void SpiralStairs_MakeNearbyWallsLowPriority() {  // 81f585
  int pos = word_7E048C >> 1;
  uint16 *dst = &dung_bg2[pos];
  for (int i = 0; i < 5; i++) {
    dst[XY(0, 0)] &= ~0x2000;
    dst[XY(0, 1)] &= ~0x2000;
    dst[XY(0, 2)] &= ~0x2000;
    dst[XY(0, 3)] &= ~0x2000;
    dst += 1;
  }
  int dp = Dungeon_PrepOverlayDma_nextPrep(0, pos * 2);
  Dungeon_PrepOverlayDma_nextPrep(dp, pos * 2 + 8);
  nmi_copy_packets_flag = 1;
}

void ClearAndStripeExplodingWall(uint16 dsto) {  // 81f811
  static const uint16 kBlastWall_Tab2[16] = { 4, 8, 0xc, 0x10, 0x14, 0x18, 0x1c, 0x20, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700, 0x800 };

  uint16 r6 = 0x80;
  uint16 r14 = 0;
  uint16 r10 = dung_unk_blast_walls_2 + 3;
  uint16 r2 = 0;

  if (!sign16(r10 - 8)) {
    r2 = r10 - 6;
    r14 = 1;
    r10 = 3;
  }
  if (!(dung_door_direction[dung_cur_door_idx >> 1] & 2))
    r6++;

  uint16 *uvdata = &uvram.data[0];
  for (;;) {
    const uint16 *bg2 = &dung_bg2[dsto];
    do {
      uint16 vram_addr = Dungeon_MapVramAddrNoSwap(dsto);
      uvdata[0] = vram_addr;
      uvdata[1] = r6 | 0xa00;
      uvdata[2] = bg2[XY(0, 0)];
      uvdata[3] = bg2[XY(0, 1)];
      uvdata[4] = bg2[XY(0, 2)];
      uvdata[5] = bg2[XY(0, 3)];
      uvdata[6] = bg2[XY(0, 4)];
      uvdata[7] = vram_addr + 0x4a0;
      uvdata[8] = r6 | 0xe00;
      uvdata[9] = bg2[XY(0, 5)];
      uvdata[10] = bg2[XY(0, 6)];
      uvdata[11] = bg2[XY(0, 7)];
      uvdata[12] = bg2[XY(0, 8)];
      uvdata[13] = bg2[XY(0, 9)];
      uvdata[14] = bg2[XY(0, 10)];
      uvdata[15] = bg2[XY(0, 11)];
      dsto++, bg2++, uvdata += 16;
    } while (--r10);
    if (!r14)
      break;
    r14--;
    dsto += kBlastWall_Tab2[(r2 >> 1) + ((r6 & 1) ? 0 : 8) - 1] >> 1;
    r10 = 3;
  }
  uvdata[0] = 0xffff;
}

void Dungeon_DrawRoomOverlay(const uint8 *src) {  // 81f967
  for (;;) {
    dung_draw_width_indicator = 0;
    dung_draw_height_indicator = 0;
    uint16 a = WORD(*src);
    if (a == 0xffff)
      break;
    uint16 *p = &dung_bg2[(src[0] >> 2) | (src[1] >> 2) << 6];
    uint8 type = src[2];
    if (type == 0xa4) {
      p[XY(0, 1)] = p[XY(1, 1)] = p[XY(2, 1)] = p[XY(3, 1)] =
          p[XY(0, 2)] = p[XY(1, 2)] = p[XY(2, 2)] = p[XY(3, 2)] = SrcPtr(0x5aa)[0];
      p[XY(0, 0)] = p[XY(1, 0)] = p[XY(2, 0)] = p[XY(3, 0)] = SrcPtr(0x63c)[1];
      p[XY(0, 3)] = p[XY(1, 3)] = p[XY(2, 3)] = p[XY(3, 3)] = SrcPtr(0x642)[1];
    } else {
      const uint16 *sp = SrcPtr(dung_floor_2_filler_tiles);
      p[XY(0, 0)] = p[XY(2, 0)] = p[XY(0, 2)] = p[XY(2, 2)] = sp[0];
      p[XY(1, 0)] = p[XY(3, 0)] = p[XY(1, 2)] = p[XY(3, 2)] = sp[1];
      p[XY(0, 1)] = p[XY(2, 1)] = p[XY(0, 3)] = p[XY(2, 3)] = sp[4];
      p[XY(1, 1)] = p[XY(3, 1)] = p[XY(1, 3)] = p[XY(3, 3)] = sp[5];
    }
    src += 3;
  }
}

void GetDoorDrawDataIndex_North_clean_door_index(int door) {  // 81fa4a
  GetDoorDrawDataIndex_North(door, door);
}

int DoorDoorStep1_North(int door, int dma_ptr) {  // 81fa54
  int pos = dung_door_tilemap_address[door];
  if ((pos & 0x1fff) >= kDoorPositionToTilemapOffs_Up[6]) {
    pos -= 0x500;
    if ((door_type_and_slot[door] & 0xfe) >= 0x42)
      pos -= 0x300;
    GetDoorDrawDataIndex_South(door ^ 8, door & 7);
    dma_ptr = Dungeon_PrepOverlayDma_nextPrep(dma_ptr, pos);
    Dungeon_LoadSingleDoorAttribute(door ^ 8);
  }
  GetDoorDrawDataIndex_North(door, door & 7);
  return dma_ptr;
}

void GetDoorDrawDataIndex_North(int door, int r4_door) {  // 81faa0
  uint8 door_type = door_type_and_slot[door] & 0xfe;
  int x = door_open_closed_counter;
  if (x == 0 || x == 4) {
    DrawDoorToTileMap_North(door, r4_door);
    return;
  }
  x += (door_type == kDoorType_StairMaskLocked2 || door_type == kDoorType_StairMaskLocked3 || door_type >= 0x42) ? 4 : 0;
  x += (door_type == kDoorType_ShuttersTwoWay || door_type == kDoorType_Shutter) ? 2 : 0;
  //  assert(x < 8);
  Object_Draw_DoorUp_4x3(kDoorAnimUpSrc[x >> 1], door);
}

void DrawDoorToTileMap_North(int door, int r4_door) {  // 81fad7
  Object_Draw_DoorUp_4x3(kDoorTypeSrcData[GetDoorGraphicsIndex(door, r4_door) >> 1], door);
}

void Object_Draw_DoorUp_4x3(uint16 src, int door) {  // 81fae3
  const uint16 *s = SrcPtr(src);
  uint16 *dst = &dung_bg2[dung_door_tilemap_address[door] >> 1];
  for (int i = 0; i < 4; i++) {
    dst[XY(0, 0)] = s[0];
    dst[XY(0, 1)] = s[1];
    dst[XY(0, 2)] = s[2];
    dst += 1, s += 3;
  }
}

void GetDoorDrawDataIndex_South_clean_door_index(int door) {  // 81fb0b
  GetDoorDrawDataIndex_South(door, door);
}

int DoorDoorStep1_South(int door, int dma_ptr) {  // 81fb15
  int pos = dung_door_tilemap_address[door];
  if ((pos & 0x1fff) < kDoorPositionToTilemapOffs_Down[9]) {
    pos += 0x500;
    if ((door_type_and_slot[door] & 0xfe) >= 0x42)
      pos += 0x300;
    GetDoorDrawDataIndex_North(door ^ 8, door & 7);
    dma_ptr = Dungeon_PrepOverlayDma_nextPrep(dma_ptr, pos);
    Dungeon_LoadSingleDoorAttribute(door ^ 8);
  }
  GetDoorDrawDataIndex_South(door, door & 7);
  return dma_ptr;
}

void GetDoorDrawDataIndex_South(int door, int r4_door) {  // 81fb61
  uint8 door_type = door_type_and_slot[door] & 0xfe;
  int x = door_open_closed_counter;
  if (x == 0 || x == 4) {
    DrawDoorToTileMap_South(door, r4_door);
    return;
  }
  x += (door_type >= 0x42) ? 4 : 0;
  x += (door_type == kDoorType_ShuttersTwoWay || door_type == kDoorType_Shutter) ? 2 : 0;
  //  assert(x < 8);
  Object_Draw_DoorDown_4x3(kDoorAnimDownSrc[x >> 1], door);
}

void DrawDoorToTileMap_South(int door, int r4_door) {  // 81fb8e
  Object_Draw_DoorDown_4x3(kDoorTypeSrcData2[GetDoorGraphicsIndex(door, r4_door) >> 1], door);
}

void Object_Draw_DoorDown_4x3(uint16 src, int door) {  // 81fb9b
  const uint16 *s = SrcPtr(src);
  uint16 *dst = &dung_bg2[dung_door_tilemap_address[door] >> 1];
  for (int i = 0; i < 4; i++) {
    dst[XY(0, 1)] = s[0];
    dst[XY(0, 2)] = s[1];
    dst[XY(0, 3)] = s[2];
    dst += 1, s += 3;
  }
}

void GetDoorDrawDataIndex_West_clean_door_index(int door) {  // 81fbc2
  GetDoorDrawDataIndex_West(door, door);
}

int DoorDoorStep1_West(int door, int dma_ptr) {  // 81fbcc
  int pos = dung_door_tilemap_address[door];
  if ((pos & 0x7ff) >= kDoorPositionToTilemapOffs_Left[6]) {
    pos -= 16;
    if ((door_type_and_slot[door] & 0xfe) >= 0x42)
      pos -= 12;
    GetDoorDrawDataIndex_East(door ^ 8, door & 7);
    dma_ptr = Dungeon_PrepOverlayDma_nextPrep(dma_ptr, pos);
    Dungeon_LoadSingleDoorAttribute(door ^ 8);
  }
  GetDoorDrawDataIndex_West(door, door & 7);
  return dma_ptr;
}

void GetDoorDrawDataIndex_West(int door, int r4_door) {  // 81fc18
  uint8 door_type = door_type_and_slot[door] & 0xfe;
  int x = door_open_closed_counter;
  if (x == 0 || x == 4) {
    DrawDoorToTileMap_West(door, r4_door);
    return;
  }
  x += (door_type >= 0x42) ? 4 : 0;
  x += (door_type == kDoorType_ShuttersTwoWay || door_type == kDoorType_Shutter) ? 2 : 0;
  Object_Draw_DoorLeft_3x4(kDoorAnimLeftSrc[x >> 1], door);
}

void DrawDoorToTileMap_West(int door, int r4_door) {  // 81fc45
  Object_Draw_DoorLeft_3x4(kDoorTypeSrcData3[GetDoorGraphicsIndex(door, r4_door) >> 1], door);
}

void GetDoorDrawDataIndex_East_clean_door_index(int door) {  // 81fc80
  GetDoorDrawDataIndex_East(door, door);
}

int DoorDoorStep1_East(int door, int dma_ptr) {  // 81fc8a
  int pos = dung_door_tilemap_address[door];
  if ((pos & 0x7ff) < kDoorPositionToTilemapOffs_Right[6]) {
    pos += 16;
    if ((door_type_and_slot[door] & 0xfe) >= 0x42)
      pos += 12;
    GetDoorDrawDataIndex_West(door ^ 8, door & 7);
    dma_ptr = Dungeon_PrepOverlayDma_nextPrep(dma_ptr, pos);
    Dungeon_LoadSingleDoorAttribute(door ^ 8);
  }
  GetDoorDrawDataIndex_East(door, door & 7);
  return dma_ptr;
}

void GetDoorDrawDataIndex_East(int door, int r4_door) {  // 81fcd6
  uint8 door_type = door_type_and_slot[door] & 0xfe;
  int x = door_open_closed_counter;
  if (x == 0 || x == 4) {
    DrawDoorToTileMap_East(door, r4_door);
    return;
  }
  x += (door_type >= 0x42) ? 4 : 0;
  x += (door_type == kDoorType_ShuttersTwoWay || door_type == kDoorType_Shutter) ? 2 : 0;
  Object_Draw_DoorRight_3x4(kDoorAnimRightSrc[x >> 1], door);
}

void DrawDoorToTileMap_East(int door, int r4_door) {  // 81fd03
  Object_Draw_DoorRight_3x4(kDoorTypeSrcData4[GetDoorGraphicsIndex(door, r4_door) >> 1], door);
}

uint8 GetDoorGraphicsIndex(int door, int r4_door) {  // 81fd79
  uint8 door_type = door_type_and_slot[door] & 0xfe;
  if (dung_door_opened_incl_adjacent & kUpperBitmasks[r4_door])
    door_type = kDoorTypeRemap[door_type >> 1];
  return door_type;
}

void ClearExplodingWallFromTileMap_ClearOnePair(uint16 *dst, const uint16 *src) {  // 81fddb
  for (int i = 2; i != 0; i--) {
    for (int j = 0; j < 12; j++)
      dst[XY(0, j)] = src[j];
    dst++;
    src += 12;
  }
}

void Dungeon_DrawRoomOverlay_Apply(int p) {  // 81fe41
  for (int j = 0; j < 4; j++, p += 64) {
    for (int i = 0; i < 4; i++) {
      uint16 t = dung_bg2[p + i] & 0x3fe;
      dung_bg2_attr_table[p + i] = (t == 0xee || t == 0xfe) ? 0 : 0x20;
    }
  }
}

void ApplyGrayscaleFixed_Incremental() {  // 81feb0
  uint8 a = COLDATA_copy0 & 0x1f;
  if (a == overworld_fixed_color_plusminus)
    return;
  a += (a < overworld_fixed_color_plusminus) ? 1 : -1;
  Dungeon_ApproachFixedColor_variable(a);
}

void Dungeon_ApproachFixedColor_variable(uint8 a) {  // 81fec1
  COLDATA_copy0 = a | 0x20;
  COLDATA_copy1 = a | 0x40;
  COLDATA_copy2 = a | 0x80;
}

void Module_PreDungeon() {  // 82821e
  sound_effect_ambient = 5;
  sound_effect_1 = 0;
  dungeon_room_index = 0;
  dungeon_room_index_prev = 0;
  dung_savegame_state_bits = 0;

  agahnim_pal_setting[0] = agahnim_pal_setting[1] = agahnim_pal_setting[2] = 0;
  agahnim_pal_setting[3] = agahnim_pal_setting[4] = agahnim_pal_setting[5] = 0;

  Dungeon_LoadEntrance();
  uint8 d = cur_palace_index_x2;
  link_num_keys = (d != 0xff) ? link_keys_earned_per_dungeon[d == 2 ? 0 : (d >> 1)] : 0xff;
  Hud_Rebuild();
  dung_num_lit_torches = 0;
  hdr_dungeon_dark_with_lantern = 0;
  Dungeon_LoadAndDrawRoom();
  Dungeon_LoadCustomTileAttr();

  DecompressAnimatedDungeonTiles(kDungAnimatedTiles[main_tile_theme_index]);
  Dungeon_LoadAttributeTable();
  misc_sprites_graphics_index = 10;
  InitializeTilesets();
  palette_sp6 = 10;
  Dungeon_LoadPalettes();
  if (link_is_bunny_mirror | link_is_bunny)
    LoadGearPalettes_bunny();

  dung_loade_bgoffs_h_copy = (dungeon_room_index & 0xf) << 9;
  dung_loade_bgoffs_v_copy = swap16((dungeon_room_index & 0xff0) >> 3);

  if (dungeon_room_index == 0x104 && sram_progress_flags & 0x10)
    WORD(dung_want_lights_out) = 0;

  SetAndSaveVisitedQuadrantFlags();
  CGWSEL_copy = 2;
  CGADSUB_copy = 0xb3;

  uint8 x = dung_num_lit_torches;
  if (!dung_want_lights_out) {
    x = 3;
    CGADSUB_copy = dung_hdr_bg2_properties == 7 ? 0x32 :
      dung_hdr_bg2_properties == 4 ? 0x62 : 0x20;
  }
  overworld_fixed_color_plusminus = kLitTorchesColorPlus[x];
  Dungeon_ApproachFixedColor_variable(overworld_fixed_color_plusminus);
  BYTE(palette_filter_countdown) = 0x1f;
  mosaic_target_level = 0;
  BYTE(darkening_or_lightening_screen) = 2;
  overworld_palette_aux_or_main = 0;
  link_speed_modifier = 0;
  button_mask_b_y = 0;
  button_b_frames = 0;
  Dungeon_ResetTorchBackgroundAndPlayer();
  Link_CheckBunnyStatus();
  ResetThenCacheRoomEntryProperties();
  if (follower_indicator == 13) {
    follower_indicator = 0;
    super_bomb_indicator_unk2 = 0;
    Hud_RemoveSuperBombIndicator();
  }
  BGMODE_copy = 9;
  Follower_Initialize();
  Sprite_ResetAll();
  Dungeon_ResetSprites();
  byte_7E02F0 = 0;
  flag_skip_call_tag_routines++;
  if (!sram_progress_indicator && !(sram_progress_flags & 0x10)) {
    COLDATA_copy0 = 0x30;
    COLDATA_copy1 = 0x50;
    COLDATA_copy2 = 0x80;
    dung_want_lights_out = dung_want_lights_out_copy = 0;
    Link_TuckIntoBed();
  }
  saved_module_for_menu = 7;
  main_module_index = 7;
  submodule_index = 15;
  Dungeon_LoadSongBankIfNeeded();
  Module_PreDungeon_setAmbientSfx();
}

void Module_PreDungeon_setAmbientSfx() {  // 82838c
  if (sram_progress_indicator < 2) {
    sound_effect_ambient = 5;
    if (!sign8(dung_cur_floor) && dungeon_room_index != 2 && dungeon_room_index != 18)
      sound_effect_ambient = 3;
  }
}

void LoadOWMusicIfNeeded() {  // 82854c
  if (!flag_which_music_type)
    return;
  flag_which_music_type = 0;
  LoadOverworldSongs();
}

void Module07_Dungeon() {  // 8287a2
  Dungeon_HandleLayerEffect();
  kDungeonSubmodules[submodule_index]();

  // When having the somaria on door button and exiting in skull woods, 
  // don't overwrite submodule_index
  if (enhanced_features0 & kFeatures0_MiscBugFixes && main_module_index != 7)
    goto skip;

  dung_misc_objs_index = 0;
  Dungeon_PushBlock_Handler();
  if (submodule_index) goto skip;
  Graphics_LoadChrHalfSlot();
  Dungeon_HandleCamera();
  if (submodule_index) goto skip;
  Dungeon_HandleRoomTags();
  if (submodule_index) goto skip;
  Dungeon_ProcessTorchesAndDoors();
  if (dung_unk_blast_walls_2)
    Dungeon_ClearAwayExplodingWall();
  if (!is_standing_in_doorway)
    Dungeon_TryScreenEdgeTransition();
skip:
  OrientLampLightCone();

  int bg2x = BG2HOFS_copy2;
  int bg2y = BG2VOFS_copy2;
  int bg1x = BG1HOFS_copy2;
  int bg1y = BG1VOFS_copy2;

  BG2HOFS_copy2 = BG2HOFS_copy = bg2x + bg1_x_offset;
  BG2VOFS_copy2 = BG2VOFS_copy = bg2y + bg1_y_offset;
  BG1HOFS_copy2 = BG1HOFS_copy = bg1x + bg1_x_offset;
  BG1VOFS_copy2 = BG1VOFS_copy = bg1y + bg1_y_offset;

  if (dung_hdr_collision_2_mirror) {
    BG1HOFS_copy2 = BG1HOFS_copy = bg1x = BG2HOFS_copy2 + dung_floor_x_offs;
    BG1VOFS_copy2 = BG1VOFS_copy = bg1y = BG2VOFS_copy2 + dung_floor_y_offs;
  }

  Sprite_Dungeon_DrawAllPushBlocks();
  Sprite_Main();

  BG2HOFS_copy2 = bg2x;
  BG2VOFS_copy2 = bg2y;
  BG1HOFS_copy2 = bg1x;
  BG1VOFS_copy2 = bg1y;

  LinkOam_Main();
  Hud_RefillLogic();
  Hud_FloorIndicator();
}

void Dungeon_TryScreenEdgeTransition() {  // 82885e
  int dir;

  if (link_y_vel != 0) {
    int y = (link_y_coord & 0x1ff);
    if ((dir = 3, y < 4) || (dir = 2, y >= 476))
      goto trigger_trans;
  }

  if (link_x_vel != 0) {
    int y = (link_x_coord & 0x1ff);
    if ((dir = 1, y < 8) || (dir = 0, y >= 489))
      goto trigger_trans;
  }
  return;

trigger_trans:
  if (!Link_CheckForEdgeScreenTransition() && main_module_index == 7) {
    Dungeon_HandleEdgeTransitionMovement(dir);
    if (main_module_index == 7)
      submodule_index = 2;
  }
}

void Dungeon_HandleEdgeTransitionMovement(int dir) {  // 8288c5
  static const uint8 kLimitDirectionOnOneAxis[] = { 0x3, 0x3, 0xc, 0xc };
  link_direction &= kLimitDirectionOnOneAxis[dir];
  switch (dir) {
  case 0: Dungeon_StartInterRoomTrans_Right(); break;
  case 1: Dungeon_StartInterRoomTrans_Left(); break;
  case 2: Dungeon_StartInterRoomTrans_Down(); break;
  case 3: Dungeon_StartInterRoomTrans_Up(); break;
  default:
    assert(0);
  }
}

void Module07_00_PlayerControl() {  // 8288de
  if (!(flag_custom_spell_anim_active | flag_is_link_immobilized | flag_block_link_menu)) {
    if (filtered_joypad_H & 0x10) {  // start
      overworld_map_state = 0;
      submodule_index = 1;
      saved_module_for_menu = main_module_index;
      main_module_index = 14;
      return;
    } else if (DidPressButtonForMap()) {  // x
      if ((uint8)cur_palace_index_x2 != 0xff && (uint8)dungeon_room_index) {
        overworld_map_state = 0;
        submodule_index = 3;
        saved_module_for_menu = main_module_index;
        main_module_index = 14;
        return;
      }
    } else if (joypad1H_last & 0x20) {  // select
      if (sram_progress_indicator) {
        overworld_map_state = 0;
        DisplaySelectMenu();
        return;
      }
    }
    Hud_HandleItemSwitchInputs();
  }
  Link_Main();
}

void Module07_01_SubtileTransition() {  // 82897c
  link_y_coord_prev = link_y_coord;
  link_x_coord_prev = link_x_coord;
  Link_HandleMovingAnimation_FullLongEntry();
  kDungeon_IntraRoomTrans[subsubmodule_index]();
}

void DungeonTransition_Subtile_ResetShutters() {  // 828995
  BYTE(dung_flag_trapdoors_down) = 0;
  BYTE(door_animation_step_indicator) = 7;
  uint8 bak = submodule_index;
  OperateShutterDoors();
  submodule_index = bak;
  BYTE(palette_filter_countdown) = 31;
  mosaic_target_level = 0;
  subsubmodule_index++;
}

void DungeonTransition_Subtile_PrepTransition() {  // 8289b6
  darkening_or_lightening_screen = 0;
  palette_filter_countdown = 0;
  mosaic_target_level = 31;
  unused_config_gfx = 0;
  dung_flag_somaria_block_switch = 0;
  dung_flag_statechange_waterpuzzle = 0;
  subsubmodule_index++;
}

void DungeonTransition_Subtile_ApplyFilter() {  // 8289d8
  if (!dung_want_lights_out) {
    subsubmodule_index++;
    return;
  }
  ApplyPaletteFilter_bounce();
  if (BYTE(palette_filter_countdown))
    ApplyPaletteFilter_bounce();
}

void DungeonTransition_Subtile_TriggerShutters() {  // 8289f0
  ResetThenCacheRoomEntryProperties();
  if (!BYTE(dung_flag_trapdoors_down)) {
    BYTE(dung_flag_trapdoors_down)++;
    BYTE(dung_cur_door_pos) = 0;
    BYTE(door_animation_step_indicator) = 0;
    submodule_index = 5;
  }
}

void Module07_02_SupertileTransition() {  // 828a26
  link_y_coord_prev = link_y_coord;
  link_x_coord_prev = link_x_coord;
  if (subsubmodule_index != 0) {
    if (subsubmodule_index >= 7)
      Graphics_IncrementalVRAMUpload();
    Dungeon_LoadAttribute_Selectable();
  }
  Link_HandleMovingAnimation_FullLongEntry();
  kDungeon_InterRoomTrans[subsubmodule_index]();
}

void Module07_02_00_InitializeTransition() {  // 828a4f
  uint8 bak = hdr_dungeon_dark_with_lantern;
  ResetTransitionPropsAndAdvanceSubmodule();
  hdr_dungeon_dark_with_lantern = bak;
}

void Module07_02_01_LoadNextRoom() {  // 828a5b
  Dungeon_LoadRoom();
  ResetStarTileGraphics();
  LoadTransAuxGFX_sprite();
  subsubmodule_index++;
  overworld_map_state = 0;
  BYTE(dungeon_room_index2) = BYTE(dungeon_room_index);
  Dungeon_ResetSprites();
  if (!hdr_dungeon_dark_with_lantern)
    MirrorBg1Bg2Offs();
  hdr_dungeon_dark_with_lantern = 0;
}

void Dungeon_InterRoomTrans_State3() {  // 828a87
  if (dung_want_lights_out | dung_want_lights_out_copy)
    TS_copy = 0;
  Dungeon_AdjustForRoomLayout();
  LoadNewSpriteGFXSet();
  MirrorBg1Bg2Offs();
  WaterFlood_BuildOneQuadrantForVRAM();
  subsubmodule_index++;
}

void Dungeon_InterRoomTrans_State10() {  // 828aa5
  if (dung_want_lights_out | dung_want_lights_out_copy)
    ApplyPaletteFilter_bounce();
  Dungeon_InterRoomTrans_notDarkRoom();
}

void Dungeon_SpiralStaircase11() {  // 828aaf
  ApplyPaletteFilter_bounce();
  WaterFlood_BuildOneQuadrantForVRAM();
  subsubmodule_index++;
}

void Dungeon_InterRoomTrans_notDarkRoom() {  // 828ab3
  WaterFlood_BuildOneQuadrantForVRAM();
  subsubmodule_index++;
}

void Dungeon_InterRoomTrans_State9() {  // 828aba
  if (dung_want_lights_out | dung_want_lights_out_copy)
    ApplyPaletteFilter_bounce();
  Dungeon_InterRoomTrans_State4();
}

void Dungeon_SpiralStaircase12() {  // 828ac4
  ApplyPaletteFilter_bounce();
  Dungeon_PrepareNextRoomQuadrantUpload();
  subsubmodule_index++;
}

void Dungeon_InterRoomTrans_State4() {  // 828ac8
  Dungeon_PrepareNextRoomQuadrantUpload();
  subsubmodule_index++;
}

void Dungeon_InterRoomTrans_State12() {  // 828acf
  if (submodule_index == 2) {
    if (overworld_map_state != 5)
      return;
    SubtileTransitionCalculateLanding();
    if (dung_want_lights_out | dung_want_lights_out_copy)
      ApplyPaletteFilter_bounce();
  }
  subsubmodule_index++;
  Dungeon_ResetTorchBackgroundAndPlayer();
}

void Dungeon_Staircase14() {  // 828aed
  subsubmodule_index++;
  Dungeon_ResetTorchBackgroundAndPlayer();
}

void Dungeon_ResetTorchBackgroundAndPlayer() {  // 828aef
  uint8 ts = kSpiralTab1[dung_hdr_bg2_properties], tm = 0x16;
  if (sign8(ts))
    tm = 0x17, ts = 0;
  if (dung_hdr_bg2_properties == 2)
    ts = 3;
  TM_copy = tm;
  TS_copy = ts;
  Hud_RestoreTorchBackground();
  Dungeon_ResetTorchBackgroundAndPlayerInner();
}

void Dungeon_ResetTorchBackgroundAndPlayerInner() {  // 828b0c
  Ancilla_TerminateSelectInteractives(0);
  if (link_is_running && !(enhanced_features0 & kFeatures0_TurnWhileDashing)) {
    link_auxiliary_state = 0;
    link_incapacitated_timer = 0;
    link_actual_vel_z = 0xff;
    g_ram[0xc7] = 0xff;
    link_delay_timer_spin_attack = 0;
    link_speed_setting = 0;
    swimcoll_var5[0] &= ~0xff;
    link_is_running = 0;
    link_player_handler_state = 0;
  }
}

void Dungeon_InterRoomTrans_State7() {  // 828b2e
  BG1HOFS_copy2 = BG2HOFS_copy2;
  BG1VOFS_copy2 = BG2VOFS_copy2;

  if (dungeon_room_index != 54 && dungeon_room_index != 56) {
    uint16 y = kSpiralTab1[dung_hdr_bg2_properties] ? 0x116 : 0x16;
    if (y != (TM_copy | TS_copy << 8) && (TM_copy == 0x17 || (TM_copy | TS_copy) != 0x17))
      TM_copy = y, TS_copy = y >> 8;
  }
  DungeonTransition_RunFiltering();
}

void DungeonTransition_RunFiltering() {  // 828b67
  if (dung_want_lights_out | dung_want_lights_out_copy) {
    overworld_fixed_color_plusminus = kLitTorchesColorPlus[dung_want_lights_out ? dung_num_lit_torches : 3];
    Dungeon_ApproachFixedColor_variable(overworld_fixed_color_plusminus);
    mosaic_target_level = 0;
  }
  Dungeon_HandleTranslucencyAndPalette();
}

void Module07_02_FadedFilter() {  // 828b92
  if (dung_want_lights_out | dung_want_lights_out_copy) {
    ApplyPaletteFilter_bounce();
    if (BYTE(palette_filter_countdown))
      ApplyPaletteFilter_bounce();
  } else {
    subsubmodule_index++;
  }
}

void Dungeon_InterRoomTrans_State15() {  // 828bae
  ResetThenCacheRoomEntryProperties();
  if (!BYTE(dung_flag_trapdoors_down) && (BYTE(dungeon_room_index) != 172 || dung_savegame_state_bits & 0x3000)) {
    BYTE(dung_flag_trapdoors_down) = 1;
    BYTE(dung_cur_door_pos) = 0;
    BYTE(door_animation_step_indicator) = 0;
    submodule_index = 5;
  }
  Dungeon_PlayMusicIfDefeated();
}

void Dungeon_PlayMusicIfDefeated() {  // 828bd7
  uint8 x = 0x14;
  if (dungeon_room_index != 18) {
    x = 0x10;
    if (dungeon_room_index != 2) {
      if (FindInWordArray(kBossRooms, dungeon_room_index, countof(kBossRooms)) < 0)
        return;
      if (Sprite_CheckIfScreenIsClear())
        return;
      x = 0x15;
    }
  }
  music_control = x;
}

void Module07_03_OverlayChange() {  // 828c05
  const uint8 *overlay_p = kDungeonRoomOverlay + kDungeonRoomOverlayOffs[dung_overlay_to_load];
  Dungeon_DrawRoomOverlay(overlay_p);
  int dst_pos = 0;
  for (;;) {
    uint16 a = WORD(*overlay_p);
    if (a == 0xffff)
      break;
    int p = (overlay_p[0] >> 2) | (overlay_p[1] >> 2) << 6;
    dst_pos = Dungeon_PrepOverlayDma_nextPrep(dst_pos, p * 2);
    Dungeon_DrawRoomOverlay_Apply(p);
    overlay_p += 3;
  }
  nmi_copy_packets_flag = 1;
  submodule_index = 0;
}

void Module07_04_UnlockDoor() {  // 828c0a
  Dungeon_OpeningLockedDoor_Combined(false);
}

void Module07_05_ControlShutters() {  // 828c0f
  OperateShutterDoors();
}

void Module07_06_FatInterRoomStairs() {  // 828c14
  if (subsubmodule_index >= 3)
    Dungeon_LoadAttribute_Selectable();

  if (subsubmodule_index >= 13) {
    Graphics_IncrementalVRAMUpload();
    if (!staircase_var1)
      goto table;
    if (staircase_var1-- == 0x10)
      link_speed_modifier = 2;
    link_direction = which_staircase_index & 4 ? 4 : 8;
    Link_HandleVelocity();
    Dungeon_HandleCamera();
  }
  Link_HandleMovingAnimation_FullLongEntry();
table:
  switch (subsubmodule_index) {
  case 0: ResetTransitionPropsAndAdvance_ResetInterface(); break;
  case 1:
    ApplyPaletteFilter_bounce();
    if (BYTE(palette_filter_countdown))
      ApplyPaletteFilter_bounce();
    break;
  case 2: Dungeon_InitializeRoomFromSpecial(); break;
  case 3: DungeonTransition_TriggerBGC34UpdateAndAdvance(); break;
  case 4: DungeonTransition_TriggerBGC56UpdateAndAdvance(); break;
  case 5: DungeonTransition_LoadSpriteGFX(); break;
  case 6: DungeonTransition_AdjustForFatStairScroll(); break;
  case 7: Dungeon_InterRoomTrans_State4(); break;
  case 8: Dungeon_InterRoomTrans_notDarkRoom(); break;
  case 9: Dungeon_InterRoomTrans_State4(); break;
  case 10: Dungeon_SpiralStaircase11(); break;
  case 11: Dungeon_SpiralStaircase12(); break;
  case 12: Dungeon_SpiralStaircase11(); break;
  case 13: Dungeon_SpiralStaircase12(); break;
  case 14: Dungeon_DoubleApplyAndIncrementGrayscale(); break;
  case 15: Dungeon_Staircase14(); break;
  case 16:
    if (!(BYTE(darkening_or_lightening_screen) | BYTE(palette_filter_countdown)) && overworld_map_state == 5)
      ResetThenCacheRoomEntryProperties();
    break;
  }
}

void Module07_0E_01_HandleMusicAndResetProps() {  // 828c78
  if ((dungeon_room_index == 7 || dungeon_room_index == 23 && music_unk1 != 17) && !(link_which_pendants & 1))
    music_control = 0xf1;
  staircase_var1 = (which_staircase_index & 4) ? 106 : 88;
  overworld_map_state = 0;
  ResetTransitionPropsAndAdvanceSubmodule();
}

void ResetTransitionPropsAndAdvance_ResetInterface() {  // 828ca9
  overworld_map_state = 0;
  ResetTransitionPropsAndAdvanceSubmodule();
}

void ResetTransitionPropsAndAdvanceSubmodule() {  // 828cac
  WORD(mosaic_level) = 0;
  darkening_or_lightening_screen = 0;
  palette_filter_countdown = 0;
  mosaic_target_level = 31;
  unused_config_gfx = 0;
  dung_num_lit_torches = 0;
  if (hdr_dungeon_dark_with_lantern) {
    CGWSEL_copy = 0x02;
    CGADSUB_copy = 0xB3;
  }
  hdr_dungeon_dark_with_lantern = 0;
  Dungeon_ResetTorchBackgroundAndPlayerInner();
  Overworld_CopyPalettesToCache();
  subsubmodule_index += 1;
}

void Dungeon_InitializeRoomFromSpecial() {  // 828ce2
  Dungeon_AdjustAfterSpiralStairs();
  Dungeon_LoadRoom();
  ResetStarTileGraphics();
  LoadTransAuxGFX();
  Dungeon_LoadCustomTileAttr();
  BYTE(dungeon_room_index2) = BYTE(dungeon_room_index);
  Follower_Initialize();
  subsubmodule_index += 1;
}

void DungeonTransition_LoadSpriteGFX() {  // 828d10
  LoadNewSpriteGFXSet();
  Dungeon_ResetSprites();
  DungeonTransition_RunFiltering();
}

void DungeonTransition_AdjustForFatStairScroll() {  // 828d1b
  MirrorBg1Bg2Offs();
  Dungeon_AdjustForRoomLayout();
  uint8 ts = kSpiralTab1[dung_hdr_bg2_properties];
  uint8 tm = 0x16;
  if (sign8(ts))
    tm = 0x17, ts = 0;
  TM_copy = tm;
  TS_copy = ts;

  link_speed_modifier = 1;
  if (which_staircase_index & 4) {
    dung_cur_floor--;
    staircase_var1 = 32;
    sound_effect_1 = 0x19;
  } else {
    dung_cur_floor++;
    staircase_var1 = 48;
    sound_effect_1 = 0x17;
  }
  sound_effect_2 = 0x24;
  Dungeon_PlayBlipAndCacheQuadrantVisits();
  Dungeon_InterRoomTrans_notDarkRoom();
}

void ResetThenCacheRoomEntryProperties() {  // 828d71
  overworld_map_state = 0;
  subsubmodule_index = 0;
  overworld_screen_transition = 0;
  submodule_index = 0;
  dung_flag_statechange_waterpuzzle = 0;
  dung_flag_movable_block_was_pushed = 0;
  CacheCameraProperties();
}

void DungeonTransition_TriggerBGC34UpdateAndAdvance() {  // 828e0f
  PrepTransAuxGfx();
  nmi_subroutine_index = nmi_disable_core_updates = 9;
  subsubmodule_index += 1;
}

void DungeonTransition_TriggerBGC56UpdateAndAdvance() {  // 828e1d
  nmi_subroutine_index = nmi_disable_core_updates = 10;
  subsubmodule_index += 1;
}

void Module07_07_FallingTransition() {  // 828e27
  if (subsubmodule_index >= 6) {
    Graphics_IncrementalVRAMUpload();
    Dungeon_LoadAttribute_Selectable();
    ApplyGrayscaleFixed_Incremental();
  }
  kDungeon_Submodule_7_DownFloorTrans[subsubmodule_index]();
}

void Module07_07_00_HandleMusicAndResetRoom() {  // 828e63
  if (dungeon_room_index == 0x10 || dungeon_room_index == 7 || dungeon_room_index == 0x17)
    music_control = 0xf1;
  ResetTransitionPropsAndAdvance_ResetInterface();
}

void Module07_07_06_SyncBG1and2() {  // 828e80
  MirrorBg1Bg2Offs();
  Dungeon_AdjustForRoomLayout();
  uint8 ts = kSpiralTab1[dung_hdr_bg2_properties];
  uint8 tm = 0x16;
  if (sign8(ts))
    tm = 0x17, ts = 0;
  TM_copy = tm;
  TS_copy = ts;
  WaterFlood_BuildOneQuadrantForVRAM();
  subsubmodule_index++;
}

void Module07_07_0F_FallingFadeIn() {  // 828ea1
  ApplyPaletteFilter_bounce();
  if (BYTE(darkening_or_lightening_screen))
    return;
  HIBYTE(tiledetect_which_y_pos[0]) = HIBYTE(link_y_coord) + (BYTE(link_y_coord) >= BYTE(tiledetect_which_y_pos[0]));
  Dungeon_SetBossMusicUnorthodox();
  if (BYTE(dungeon_room_index) == 0x89 || BYTE(dungeon_room_index) == 0x4f)
    return;
  if (BYTE(dungeon_room_index) == 0xA7) {
    hud_floor_changed_timer = 0;
    dung_cur_floor = 1;
    return;
  }
  dung_cur_floor--;
  Dungeon_PlayBlipAndCacheQuadrantVisits();
}

void Dungeon_PlayBlipAndCacheQuadrantVisits() {  // 828ec9
  hud_floor_changed_timer = 1;
  sound_effect_2 = 36;
  SetAndSaveVisitedQuadrantFlags();
}

void Module07_07_10_LandLinkFromFalling() {  // 828ee0
  HandleDungeonLandingFromPit();
  if (submodule_index)
    return;
  submodule_index = 7;
  subsubmodule_index = 17;
  load_chr_halfslot_even_odd = 1;
  Graphics_LoadChrHalfSlot();
}

void Module07_07_11_CacheRoomAndSetMusic() {  // 828efa
  if (overworld_map_state == 5) {
    ResetThenCacheRoomEntryProperties();
    Dungeon_PlayMusicIfDefeated();
    Graphics_LoadChrHalfSlot();
  }
}

// straight staircase going down when walking south
void Module07_08_NorthIntraRoomStairs() {  // 828f0c
  uint8 t = staircase_var1;
  if (t) {
    staircase_var1--;
    if (t == 20)
      link_speed_modifier = 2;
    Link_HandleVelocity();
    ApplyLinksMovementToCamera();
    Dungeon_HandleCamera();
    Link_HandleMovingAnimation_FullLongEntry();
  }
  kDungeon_StraightStaircaseDown[subsubmodule_index]();
}

void Module07_08_00_InitStairs() {  // 828f35
  draw_water_ripples_or_grass = 0;

  uint8 v1 = 0x3c, sfx = 25;
  if (link_direction & 8) {
    v1 = 0x38, sfx = 23;
    link_is_on_lower_level_mirror = 0;
    if ((uint8)kind_of_in_room_staircase != 2)
      link_is_on_lower_level = 0;
  }
  staircase_var1 = v1;
  sound_effect_1 = sfx;
  link_speed_modifier = 1;
  subsubmodule_index++;
}

void Module07_08_01_ClimbStairs() {  // 828f5f
  if (staircase_var1)
    return;
  if (link_direction & 4) {
    link_is_on_lower_level_mirror = 1;
    if ((uint8)kind_of_in_room_staircase != 2)
      link_is_on_lower_level = 1;
  }
  subsubmodule_index = 0;
  overworld_screen_transition = 0;
  submodule_index = 0;
  SetAndSaveVisitedQuadrantFlags();
}

// straight staircase going up when walking south
void Module07_10_SouthIntraRoomStairs() {  // 828f88
  uint8 t = staircase_var1;
  if (t) {
    staircase_var1--;
    if (t == 20)
      link_speed_modifier = 2;
    Link_HandleVelocity();
    ApplyLinksMovementToCamera();
    Dungeon_HandleCamera();
    Link_HandleMovingAnimation_FullLongEntry();
  }
  kDungeon_StraightStaircase[subsubmodule_index]();
}

void Module07_10_00_InitStairs() {  // 828fb1
  uint8 v1 = 0x3c, sfx = 25;
  if (link_direction & 4) {
    v1 = 0x38, sfx = 23;
    link_is_on_lower_level_mirror ^= 1;
    if ((uint8)kind_of_in_room_staircase != 2)
      link_is_on_lower_level ^= 1;
  }
  staircase_var1 = v1;
  sound_effect_1 = sfx;
  link_speed_modifier = 1;
  subsubmodule_index++;
}

void Module07_10_01_ClimbStairs() {  // 828fe1
  if (staircase_var1)
    return;
  if (link_direction & 8) {
    link_is_on_lower_level_mirror ^= 1;
    if ((uint8)kind_of_in_room_staircase != 2)
      link_is_on_lower_level ^= 1;
  }
  subsubmodule_index = 0;
  overworld_screen_transition = 0;
  submodule_index = 0;
  SetAndSaveVisitedQuadrantFlags();
}

void Module07_09_OpenCrackedDoor() {  // 82900f
  OpenCrackedDoor();
}

// Used when lighting a lamp
void Module07_0A_ChangeBrightness() {  // 829014
  OrientLampLightCone();
  ApplyGrayscaleFixed_Incremental();
  if ((COLDATA_copy0 & 0x1f) != overworld_fixed_color_plusminus)
    return;
  submodule_index = 0;
  subsubmodule_index = 0;
}

void Module07_0B_DrainSwampPool() {  // 82902d
  static const int8 kTurnOffWater_Tab0[16] = { -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1 };
  switch (subsubmodule_index) {
  case 0: {
    if (!(turn_on_off_water_ctr & 7)) {
      int k = (turn_on_off_water_ctr >> 2) & 3;
      if (water_hdma_var2 == water_hdma_var4) {
        Dungeon_SetAttrForActivatedWaterOff();
        return;
      }
      water_hdma_var2 += kTurnOffWater_Tab0[k];
      water_hdma_var3 += kTurnOffWater_Tab0[k];
    }
    turn_on_off_water_ctr++;
    AdjustWaterHDMAWindow();
    break;
  }
  case 1: {
    uint16 v = SrcPtr(0x1e0)[0];
    for (int i = 0; i < 0x1000; i++)
      dung_bg1[i] = v;
    dung_cur_quadrant_upload = 0;
    subsubmodule_index++;
    break;
  }
  case 2: case 3: case 4: case 5:
    Dungeon_FloodSwampWater_PrepTileMap();
    break;
  }
}

void Module07_0C_FloodSwampWater() {  // 82904a
  int k;
  static const int8 kTurnOnWater_Tab2[4] = { 1, 1, 1, -1 };
  static const int8 kTurnOnWater_Tab1[4] = { 1, 2, 1, -1 };
  static const int8 kTurnOnWater_Tab0[4] = { 1, -1, 1, -1 };

  switch (subsubmodule_index) {
  case 0: case 1: case 2: case 3:
    Dungeon_FloodSwampWater_PrepTileMap();
    break;
  case 4: case 5: case 6: case 7: case 8:
    if (!--turn_on_off_water_ctr) {
      turn_on_off_water_ctr = 4;
      int depth = ++subsubmodule_index - 4;
      water_hdma_var3 = 8;
      water_hdma_var5 = 0;
      water_hdma_var2 = 0x30;
      Dungeon_AdjustWaterVomit(SrcPtr(0x1654 + 0x10), depth);
    }
    break;
  case 9:
    W12SEL_copy = 3;
    W34SEL_copy = 0;
    WOBJSEL_copy = 0;
    TMW_copy = 22;
    TSW_copy = 1;
    TS_copy = 1;
    CGWSEL_copy = 2;
    CGADSUB_copy = 98;
    turn_on_off_water_ctr = 0;
    subsubmodule_index++;
    // fall through
  case 10: {
    k = (turn_on_off_water_ctr & 3);
    uint16 r0 = 0x688 - BG2VOFS_copy2 - 0x24;
    water_hdma_var3 += kTurnOnWater_Tab0[k];
    water_hdma_var5 += kTurnOnWater_Tab1[k];
    if (water_hdma_var5 >= r0) {
      dung_hdr_bg2_properties = 7;
      subsubmodule_index++;
    }
    turn_on_off_water_ctr++;
    spotlight_y_lower = 0x688 - BG2VOFS_copy2 - water_hdma_var2;
    spotlight_y_upper = spotlight_y_lower + water_hdma_var5;
    AdjustWaterHDMAWindow_X(spotlight_y_upper);
    break;
  }
  case 11: {
    if (!(turn_on_off_water_ctr & 7)) {
      k = (turn_on_off_water_ctr >> 2) & 3;
      if (water_hdma_var2 == water_hdma_var4) {
        Dungeon_SetAttrForActivatedWater();
        return;
      }
      water_hdma_var2 += kTurnOnWater_Tab2[k];
      water_hdma_var3 += kTurnOnWater_Tab2[k];

      uint16 a = water_hdma_var4 - water_hdma_var2;
      if (a == 0 || a == 8)
        Dungeon_AdjustWaterVomit(SrcPtr(a == 0 ? 0x16b4 : 0x168c), 5);
    }
    turn_on_off_water_ctr++;
    AdjustWaterHDMAWindow();
    break;
  }
  }
}

void Module07_0D_FloodDam() {  // 82904f
  FloodDam_PrepFloodHDMA();
  kWatergateFuncs[subsubmodule_index]();
}

void Module07_0E_SpiralStairs() {  // 829054
  if (subsubmodule_index >= 7) {
    Graphics_IncrementalVRAMUpload();
    Dungeon_LoadAttribute_Selectable();
  }
  HandleLinkOnSpiralStairs();
  kDungeon_SpiralStaircase[subsubmodule_index]();
}

void Dungeon_DoubleApplyAndIncrementGrayscale() {  // 829094
  ApplyPaletteFilter_bounce();
  ApplyPaletteFilter_bounce();
  ApplyGrayscaleFixed_Incremental();
}

void Module07_0E_02_ApplyFilterIf() {  // 8290a1
  if (staircase_var1 < 9) {
    ApplyPaletteFilter_bounce();
    if (palette_filter_countdown)
      ApplyPaletteFilter_bounce();
  }
  if (staircase_var1 != 0) {
    staircase_var1--;
    return;
  }
  tagalong_var5 = link_visibility_status = 12;
}

void Dungeon_SyncBackgroundsFromSpiralStairs() {  // 8290c7
  if (follower_indicator == 6 && BYTE(dungeon_room_index) == 100)
    follower_indicator = 0;
  uint8 bak = link_is_on_lower_level;
  link_y_coord += which_staircase_index & 4 ? 48 : -48;
  link_is_on_lower_level = kTeleportPitLevel2[cur_staircase_plane];
  SpiralStairs_MakeNearbyWallsHighPriority_Exiting();
  link_is_on_lower_level = bak;
  link_y_coord += which_staircase_index & 4 ? -48 : 48;
  BG1HOFS_copy2 = BG2HOFS_copy2;
  BG1VOFS_copy2 = BG2VOFS_copy2;
  Dungeon_AdjustForRoomLayout();
  uint8 ts = kSpiralTab1[dung_hdr_bg2_properties], tm = 0x16;
  if (sign8(ts))
    tm = 0x17, ts = 0;
  if (dung_hdr_bg2_properties == 2)
    ts = 3;
  TM_copy = tm;
  TS_copy = ts;
  dung_cur_floor += (which_staircase_index & 4) ? -1 : 1;
  staircase_var1 = 24;
  Dungeon_PlayBlipAndCacheQuadrantVisits();
  Hud_RestoreTorchBackground();
  Dungeon_InterRoomTrans_notDarkRoom();
}

void Dungeon_AdvanceThenSetBossMusicUnorthodox() {  // 82915b
  Dungeon_ResetTorchBackgroundAndPlayerInner();
  staircase_var1 = 0x38;
  subsubmodule_index++;
  Dungeon_SetBossMusicUnorthodox();
}

void Dungeon_SetBossMusicUnorthodox() {  // 829165
  uint8 x = 0x1c;
  if (dungeon_room_index != 16) {
    x = 0x15;
    if (dungeon_room_index != 7) {
      x = 0x11;
      if (dungeon_room_index != 23 || music_unk1 == 17)
        return;
    }
    if (music_unk1 != 0xf1 && (link_which_pendants & 1))
      return;
  }
  music_control = x;
}

void Dungeon_SpiralStaircase17() {  // 82919b
  SpiralStairs_FindLandingSpot();
  if (!--staircase_var1) {
    staircase_var1 = which_staircase_index & 4 ? 10 : 24;
    subsubmodule_index++;
  }
}

void Dungeon_SpiralStaircase18() {  // 8291b5
  SpiralStairs_FindLandingSpot();
  if (!--staircase_var1) {
    subsubmodule_index++;
    overworld_map_state = 0;
  }
}

void Module07_0E_00_InitPriorityAndScreens() {  // 8291c4
  SpiralStairs_MakeNearbyWallsHighPriority_Entering();
  if (link_is_on_lower_level) {
    TM_copy &= 0xf;
    TS_copy |= 0x10;
    link_is_on_lower_level = 3;
  }
  subsubmodule_index++;
}

void Module07_0E_13_SetRoomAndLayerAndCache() {  // 8291dd
  link_is_on_lower_level_mirror = kTeleportPitLevel1[cur_staircase_plane];
  link_is_on_lower_level = kTeleportPitLevel2[cur_staircase_plane];
  TM_copy |= 0x10;
  TS_copy &= 0xf;
  if (!(which_staircase_index & 4))
    SpiralStairs_MakeNearbyWallsLowPriority();
  BYTE(dungeon_room_index2) = BYTE(dungeon_room_index);
  ResetThenCacheRoomEntryProperties();
}

void RepositionLinkAfterSpiralStairs() {  // 82921a
  link_visibility_status = 0;
  tagalong_var5 = 0;

  int i = (cur_staircase_plane == 0 && byte_7E0492 != 0) ? 1 : 0;
  i += (which_staircase_index & 4) ? 2 : 0;

  link_x_coord += kSpiralStaircaseX[i];
  link_y_coord += kSpiralStaircaseY[i];

  if (TM_copy & 0x10) {
    if (cur_staircase_plane == 2) {
      link_is_on_lower_level = 3;
      TM_copy &= 0xf;
      TS_copy |= 0x10;
      if (byte_7E0492 != 2)
        link_y_coord += 24;
    }
    Follower_Initialize();
  } else {
    if (cur_staircase_plane != 2) {
      TM_copy |= 0x10;
      TS_copy &= 0xf;
      if (byte_7E0492 != 2)
        link_y_coord -= 24;
    }
    Follower_Initialize();
  }
}

void SpiralStairs_MakeNearbyWallsHighPriority_Exiting() {  // 8292b1
  if (which_staircase_index & 4)
    return;
  int lf = (word_7E048C + 8) & 0x7f;
  int x = 0, p;
  while ((((p = dung_inter_starcases[x]) * 2) & 0x7f) != lf)
    x++;
  p -= 4;
  word_7E048C = p * 2;
  uint16 *dst = &dung_bg2[p];
  for (int i = 0; i < 5; i++) {
    dst[XY(0, 0)] |= 0x2000;
    dst[XY(0, 1)] |= 0x2000;
    dst[XY(0, 2)] |= 0x2000;
    dst[XY(0, 3)] |= 0x2000;
    dst += 1;
  }
}

void Module07_0F_LandingWipe() {  // 82931d
  kDungeon_Submodule_F[subsubmodule_index]();
  Link_HandleMovingAnimation_FullLongEntry();
  LinkOam_Main();
}

void Module07_0F_00_InitSpotlight() {  // 82932d
  Spotlight_open();
  subsubmodule_index++;
}

void Module07_0F_01_OperateSpotlight() {  // 829334
  Sprite_Main();
  IrisSpotlight_ConfigureTable();
  if (!submodule_index) {
    W12SEL_copy = 0;
    W34SEL_copy = 0;
    WOBJSEL_copy = 0;
    TMW_copy = 0;
    TSW_copy = 0;
    subsubmodule_index = 0;
    if (buffer_for_playing_songs != 0xff)
      music_control = buffer_for_playing_songs;
  }
}

// This is used for straight inter room stairs for example stairs to throne room in first dung
void Module07_11_StraightInterroomStairs() {  // 829357
  if (subsubmodule_index >= 3)
    Dungeon_LoadAttribute_Selectable();
  if (subsubmodule_index >= 13)
    Graphics_IncrementalVRAMUpload();
  if (staircase_var1) {
    if (staircase_var1-- == 16)
      link_speed_modifier = 2;
    link_direction = (submodule_index == 18) ? 8 : 4;
    Link_HandleVelocity();
  }
  Link_HandleMovingAnimation_FullLongEntry();
  kDungeon_StraightStairs[subsubmodule_index]();
}

void Module07_11_00_PrepAndReset() {  // 8293bb
  if (link_is_running) {
    link_is_running = 0;
    link_speed_setting = 2;
  }
  sound_effect_1 = (which_staircase_index & 4) ? 24 : 22;
  if (dungeon_room_index == 48 || dungeon_room_index == 64)
    music_control = 0xf1;
  ResetTransitionPropsAndAdvance_ResetInterface();
}

void Module07_11_01_FadeOut() {  // 8293ed
  if (staircase_var1 < 9) {
    ApplyPaletteFilter_bounce();
    if (BYTE(palette_filter_countdown) == 23)
      subsubmodule_index++;
  }
}

void Module07_11_02_LoadAndPrepRoom() {  // 829403
  ApplyPaletteFilter_bounce();
  Dungeon_LoadRoom();
  Dungeon_RestoreStarTileChr();
  LoadTransAuxGFX();
  Dungeon_LoadCustomTileAttr();
  Dungeon_AdjustForRoomLayout();
  Follower_Initialize();
  subsubmodule_index++;
}

void Module07_11_03_FilterAndLoadBGChars() {  // 829422
  ApplyPaletteFilter_bounce();
  DungeonTransition_TriggerBGC34UpdateAndAdvance();
}

void Module07_11_04_FilterDoBGAndResetSprites() {  // 82942a
  ApplyPaletteFilter_bounce();
  DungeonTransition_TriggerBGC56UpdateAndAdvance();
  BYTE(dungeon_room_index2) = BYTE(dungeon_room_index);
  Dungeon_ResetSprites();
}

void Module07_11_0B_PrepDestination() {  // 82943b
  uint8 ts = kSpiralTab1[dung_hdr_bg2_properties], tm = 0x16;
  if (sign8(ts))
    tm = 0x17, ts = 0;
  TM_copy = tm;
  TS_copy = ts;

  link_speed_modifier = 1;
  dung_cur_floor += (which_staircase_index & 4) ? -1 : 1;
  staircase_var1 = (which_staircase_index & 4) ? 0x32 : 0x3c;
  sound_effect_1 = (which_staircase_index & 4) ? 25 : 23;

  uint8 r0 = 0;
  if (link_is_on_lower_level) {
    link_y_coord += (submodule_index == 18) ? -32 : 32;
    r0++;
  }
  link_is_on_lower_level_mirror = kTeleportPitLevel1[cur_staircase_plane];
  link_is_on_lower_level = kTeleportPitLevel2[cur_staircase_plane];
  if (link_is_on_lower_level) {
    link_y_coord += (submodule_index == 18) ? -32 : 32;
    r0++;
  }

  if (!r0) {
    if (submodule_index == 18) {
      link_y_coord += (which_staircase_index & 4) ? -24 : -8;
    } else {
      link_y_coord += 12;
    }
  }

  Dungeon_PlayBlipAndCacheQuadrantVisits();
  Hud_RestoreTorchBackground();
  Dungeon_InterRoomTrans_notDarkRoom();
}

void Module07_11_09_LoadSpriteGraphics() {  // 8294e0
  ApplyPaletteFilter_bounce();
  subsubmodule_index--;
  LoadNewSpriteGFXSet();
  Dungeon_HandleTranslucencyAndPalette();
}

void Module07_11_19_SetSongAndFilter() {  // 8294ed
  if (overworld_map_state == 5 && !BYTE(darkening_or_lightening_screen)) {
    subsubmodule_index++;
    if (dungeon_room_index == 48)
      music_control = 0x1c;
    else if (dungeon_room_index == 64)
      music_control = 0x10;
  }
  ApplyGrayscaleFixed_Incremental();
}

void Module07_11_11_KeepSliding() {  // 829518
  if (staircase_var1 == 0)
    subsubmodule_index++;
  else
    ApplyGrayscaleFixed_Incremental();
}

void Module07_14_RecoverFromFall() {  // 829520
  switch (subsubmodule_index) {
  case 0:
    Module07_14_00_ScrollCamera();
    break;
  case 1:
    RecoverPositionAfterDrowning();
    break;
  }
}

void Module07_14_00_ScrollCamera() {  // 82952a
  for (int i = 0; i < 2; i++) {
    if (BG2HOFS_copy2 != BG2HOFS_copy2_cached)
      BG2HOFS_copy2 += BG2HOFS_copy2 < BG2HOFS_copy2_cached ? 1 : -1;
    if (BG2VOFS_copy2 != BG2VOFS_copy2_cached)
      BG2VOFS_copy2 += BG2VOFS_copy2 < BG2VOFS_copy2_cached ? 1 : -1;
  }
  if (BG2HOFS_copy2 == BG2HOFS_copy2_cached && BG2VOFS_copy2 == BG2VOFS_copy2_cached)
    subsubmodule_index++;
  if (!hdr_dungeon_dark_with_lantern)
    MirrorBg1Bg2Offs();
}

void Module07_15_WarpPad() {  // 82967a
  if (subsubmodule_index >= 3) {
    Graphics_IncrementalVRAMUpload();
    Dungeon_LoadAttribute_Selectable();
  }
  kDungeon_Teleport[subsubmodule_index]();
}

void Module07_15_01_ApplyMosaicAndFilter() {  // 8296ac
  ConditionalMosaicControl();
  MOSAIC_copy = mosaic_level | 3;
  ApplyPaletteFilter_bounce();
}

void Module07_15_04_SyncRoomPropsAndBuildOverlay() {  // 8296ba
  ApplyGrayscaleFixed_Incremental();
  if (dungeon_room_index == 0x17)
    dung_cur_floor = 4;
  MirrorBg1Bg2Offs();
  Dungeon_AdjustForRoomLayout();
  uint8 ts = kSpiralTab1[dung_hdr_bg2_properties], tm = 0x16;
  if (sign8(ts))
    tm = 0x17, ts = 0;
  TM_copy = tm;
  TS_copy = ts;
  WaterFlood_BuildOneQuadrantForVRAM();
  subsubmodule_index++;
}

void Module07_15_0E_FadeInFromWarp() {  // 8296ec
  if (palette_filter_countdown & 1 && mosaic_level != 0)
    mosaic_level -= 0x10;
  BGMODE_copy = 9;
  MOSAIC_copy = mosaic_level | 3;
  ApplyPaletteFilter_bounce();
}

void Module07_15_0F_FinalizeAndCacheEntry() {  // 82970f
  if (overworld_map_state == 5) {
    SetAndSaveVisitedQuadrantFlags();
    submodule_index = 0;
    ResetThenCacheRoomEntryProperties();
  }
}

void Module07_16_UpdatePegs() {  // 82972a
  if (++subsubmodule_index & 3)
    return;
  switch (subsubmodule_index >> 2) {
  case 0:
  case 1: Module07_16_UpdatePegs_Step1(); break;
  case 2: Module07_16_UpdatePegs_Step2(); break;
  case 3: RecoverPegGFXFromMapping(); break;
  case 4:
    Dungeon_FlipCrystalPegAttribute();
    subsubmodule_index = 0;
    submodule_index = 0;
    break;
  }
}

void Module07_17_PressurePlate() {  // 8297c8
  if (--subsubmodule_index)
    return;
  link_y_coord -= 2;
  Dungeon_UpdateTileMapWithCommonTile((word_7E04B6 & 0x3f) << 3, (word_7E04B6 >> 3) & 0x1f8, 0xe);
  submodule_index = saved_module_for_menu;
}

void Module07_18_RescuedMaiden() {  // 82980a
  switch (subsubmodule_index) {
  case 0:
    PaletteFilter_RestoreBGSubstractiveStrict();
    main_palette_buffer[0] = main_palette_buffer[32];
    if (BYTE(darkening_or_lightening_screen) != 255)
      return;
    for (int i = 0; i < 0x1000; i++)
      dung_bg2[i] = dung_bg1[i] = 0x1ec;
    bg1_y_offset = 0;
    bg1_x_offset = 0;
    dung_floor_x_offs = 0;
    dung_floor_y_offs = 0;
    overworld_screen_transition = 0;
    dung_cur_quadrant_upload = 0;
    subsubmodule_index++;
    break;
  case 1: {
    static const uint16 kCrystal_Tab0[7] = { 0x1618, 0x1658, 0x1658, 0x1618, 0x658, 0x1618, 0x1658 };
    PaletteFilter_Crystal();
    TS_copy = 1;
    flag_is_link_immobilized = 2;
    int j = FindInWordArray(kBossRooms, dungeon_room_index, countof(kBossRooms)) - 4;
    uint16 *dst = &dung_bg1[kCrystal_Tab0[j] >> 1];
    for (int n = 0, t = 0; n != 4; n++) {
      for (int i = 0; i != 8; i++, t++) {
        dst[i + XY(0, 0)] = 0x1f80 | t;
        dst[i + XY(0, 4)] = 0x1f88 | t;
      }
      t += 8, dst += XY(0, 1);
    }
    subsubmodule_index++;
    break;
  }
  case 2: case 4: case 6: case 8:
    Dungeon_InterRoomTrans_notDarkRoom();
    break;
  case 3: case 5: case 7: case 9:
    Dungeon_InterRoomTrans_State4();
    break;
  case 10:
    is_nmi_thread_active++;
    Polyhedral_InitializeThread();
    CrystalCutscene_Initialize();
    submodule_index = 0;
    subsubmodule_index = 0;
    break;
  }
}

void Module07_19_MirrorFade() {  // 8298f7
  // When using mirror
  Overworld_ResetMosaic_alwaysIncrease();
  if (!--INIDISP_copy) {
    main_module_index = 5;
    submodule_index = 0;
    nmi_load_bg_from_vram = 0;
    last_music_control = music_unk1;
    if (overworld_palette_swap_flag)
      Palette_RevertTranslucencySwap();
  }
}

void Module07_1A_RoomDraw_OpenTriforceDoor_bounce() {  // 829916
  static const uint16 kOpenGanonDoor_Tab[4] = { 0x2556, 0x2596, 0x25d6, 0x2616 };

  flag_is_link_immobilized = 1;
  if (R16 != 0) {
    if (--BYTE(R16) || --HIBYTE(R16))
      return;
    sound_effect_ambient = 21;
    link_force_hold_sword_up = 0;
    link_cant_change_direction = 0;
  }
  flag_is_link_immobilized = 0;
  if (++subsubmodule_index & 3)
    return;

  const uint16 *src = SrcPtr(kOpenGanonDoor_Tab[(subsubmodule_index - 4) >> 2]);
  uint16 *dst = &dung_bg2[0];
  for (int i = 0; i < 8; i++) {
    dst[XY(44, 3)] = src[0];
    dst[XY(44, 4)] = src[1];
    dst[XY(44, 5)] = src[2];
    dst[XY(44, 6)] = src[3];
    dst += XY(1, 0), src += 4;
  }

  Dungeon_PrepOverlayDma_watergate(0, 0x1d8, 0x881, 8);
  if (subsubmodule_index == 16) {
    WriteAttr2(XY(44, 5), 0x202);
    WriteAttr2(XY(44, 6), 0x202);
    WriteAttr2(XY(50, 5), 0x200);
    WriteAttr2(XY(50, 6), 0x200);
    for (int i = 0; i != 6; i += 2) {
      WriteAttr2(XY(45 + i, 0), 0x0);
      WriteAttr2(XY(45 + i, 1), 0x0);
      WriteAttr2(XY(45 + i, 2), 0x0);
      WriteAttr2(XY(45 + i, 3), 0x0);
      WriteAttr2(XY(45 + i, 4), 0x0);
      WriteAttr2(XY(45 + i, 5), 0x0);
      WriteAttr2(XY(45 + i, 6), 0x0);
    }
    room_bounds_y.a0 = -64;
    submodule_index = 0;
    subsubmodule_index = 0;
  }
  nmi_copy_packets_flag = 1;
}

void Module11_DungeonFallingEntrance() {  // 829af9
  switch (subsubmodule_index) {
  case 0:  // Module_11_00_SetSongAndInit
    if (kEntranceData_musicTrack[which_entrance] != 3 || sram_progress_indicator >= 2)
      music_control = 0xf1;
    ResetTransitionPropsAndAdvance_ResetInterface();
    break;
  case 1:
    if (!(frame_counter & 1))
      ApplyPaletteFilter_bounce();
    break;
  case 2:
    Module11_02_LoadEntrance();
    break;
  case 3:
    DungeonTransition_LoadSpriteGFX();
    break;
  case 4:
    INIDISP_copy = (INIDISP_copy + 1) & 0xf;
    if (INIDISP_copy == 15)
      subsubmodule_index++;
  case 5:
    HandleDungeonLandingFromPit();
    if (submodule_index)
      return;
    main_module_index = 7;
    flag_skip_call_tag_routines++;
    Dungeon_PlayBlipAndCacheQuadrantVisits();
    ResetThenCacheRoomEntryProperties();
    music_control = buffer_for_playing_songs;
    last_music_control = music_unk1;
    break;
  }
}

void Module11_02_LoadEntrance() {  // 829b1c
  EnableForceBlank();
  CGWSEL_copy = 2;
  Dungeon_LoadEntrance();

  uint8 dung = BYTE(cur_palace_index_x2);
  link_num_keys = (dung != 255) ? link_keys_earned_per_dungeon[((dung == 2) ? 0 : dung) >> 1] : 255;
  Hud_Rebuild();
  link_this_controls_sprite_oam = 4;
  player_near_pit_state = 3;
  link_visibility_status = 12;
  link_speed_modifier = 16;

  uint8 y = link_y_coord - BG2VOFS_copy2;
  link_state_bits = 0;
  link_picking_throw_state = 0;
  some_animation_timer = 0;
  dungeon_room_index_prev = dungeon_room_index;
  tiledetect_which_y_pos[0] = link_y_coord;
  link_y_coord -= y + 16;

  uint8 bak = subsubmodule_index;
  dung_num_lit_torches = 0;
  hdr_dungeon_dark_with_lantern = 0;
  Dungeon_LoadAndDrawRoom();
  Dungeon_LoadCustomTileAttr();
  DecompressAnimatedDungeonTiles(kDungAnimatedTiles[main_tile_theme_index]);
  Dungeon_LoadAttributeTable();
  subsubmodule_index = bak + 1;
  misc_sprites_graphics_index = 10;
  InitializeTilesets();
  palette_sp6 = 10;
  Dungeon_LoadPalettes();
  Hud_RestoreTorchBackground();
  button_mask_b_y = 0;
  button_b_frames = 0;
  Dungeon_ResetTorchBackgroundAndPlayer();
  if (link_is_bunny_mirror)
    LoadGearPalettes_bunny();
  HDMAEN_copy = 0x80;
  Hud_RefillLogic();
  Module_PreDungeon_setAmbientSfx();
  submodule_index = 7;
  Dungeon_LoadSongBankIfNeeded();
}

void Dungeon_LoadSongBankIfNeeded() {  // 829bd7
  if (buffer_for_playing_songs == 0xff || buffer_for_playing_songs == 0xf2)
    return;

  if (buffer_for_playing_songs == 3 || buffer_for_playing_songs == 7 || buffer_for_playing_songs == 14) {
    LoadOWMusicIfNeeded();
  } else {
    if (flag_which_music_type)
      return;
    flag_which_music_type = 1;
    LoadDungeonSongs();
  }
}

void Mirror_SaveRoomData() {  // 82a1b1
  if (cur_palace_index_x2 == 0xff) {
    sound_effect_1 = 60;
    return;
  }
  submodule_index = 25;
  subsubmodule_index = 0;
  sound_effect_1 = 51;
  Dungeon_FlagRoomData_Quadrants();
  SaveDungeonKeys();
}

void SaveDungeonKeys() {  // 82a1c7
  uint8 idx = cur_palace_index_x2;
  if (idx == 0xff)
    return;
  if (idx == 2)
    idx = 0;
  link_keys_earned_per_dungeon[idx >> 1] = link_num_keys;
}

void Dungeon_AdjustAfterSpiralStairs() {  // 82a2f0
  int xd = ((dungeon_room_index & 0xf) - (dungeon_room_index_prev & 0xf)) * 0x200;
  link_x_coord += xd;
  BG2HOFS_copy2 += xd;
  room_bounds_x.a1 += xd;
  room_bounds_x.b1 += xd;
  room_bounds_x.a0 += xd;
  room_bounds_x.b0 += xd;

  int yd = (((dungeon_room_index & 0xf0) >> 4) - ((dungeon_room_index_prev & 0xf0) >> 4)) * 0x200;
  link_y_coord += yd;
  BG2VOFS_copy2 += yd;
  room_bounds_y.a1 += yd;
  room_bounds_y.b1 += yd;
  room_bounds_y.a0 += yd;
  room_bounds_y.b0 += yd;
}

void Dungeon_AdjustForTeleportDoors(uint8 room, uint8 flag) {  // 82a37c
  dungeon_room_index2 = room;
  dungeon_room_index_prev = room;

  uint16 xx = (room & 0xf) * 2 - (link_x_coord >> 8) + flag;
  link_x_coord += (xx << 8);
  BG2HOFS_copy2 += (xx << 8);
  room_bounds_x.a1 += (xx << 8);
  room_bounds_x.b1 += (xx << 8);
  room_bounds_x.a0 += (xx << 8);
  room_bounds_x.b0 += (xx << 8);

  xx = ((room & 0xf0) >> 3) - (link_y_coord >> 8);
  link_y_coord += (xx << 8);
  BG2VOFS_copy2 += (xx << 8);
  room_bounds_y.a1 += (xx << 8);
  room_bounds_y.b1 += (xx << 8);
  room_bounds_y.a0 += (xx << 8);
  room_bounds_y.b0 += (xx << 8);

  for (int i = 0; i < 20; i++)
    tagalong_y_hi[i] = link_y_coord >> 8;
}

void Dungeon_AdjustForRoomLayout() {  // 82b5dc
  Dungeon_AdjustQuadrant();
  quadrant_fullsize_x = (dung_blastwall_flag_x || (kLayoutQuadrantFlags[composite_of_layout_and_quadrant] & (link_quadrant_x ? 2 : 1)) == 0) ? 2 : 0;
  quadrant_fullsize_y = (dung_blastwall_flag_y || (kLayoutQuadrantFlags[composite_of_layout_and_quadrant] & (link_quadrant_y ? 8 : 4)) == 0) ? 2 : 0;
  if ((uint8)dung_unk2)
    quadrant_fullsize_x = (uint8)dung_unk2;
  if ((uint8)(dung_unk2 >> 8))
    quadrant_fullsize_y = (uint8)(dung_unk2 >> 8);
}

void HandleEdgeTransitionMovementEast_RightBy8() {  // 82b62e
  link_x_coord += 8;
  Dungeon_StartInterRoomTrans_Right();
}

void Dungeon_StartInterRoomTrans_Right() {  // 82b63a
  assert(submodule_index == 0);
  link_quadrant_x ^= 1;
  Dungeon_AdjustQuadrant();
  RoomBounds_AddA(&room_bounds_x);
  Dung_SaveDataForCurrentRoom();
  DungeonTransition_AdjustCamera_X(link_quadrant_x);
  HandleEdgeTransition_AdjustCameraBoundaries(2);
  submodule_index = 1;
  if (!link_quadrant_x) {
    RoomBounds_AddB(&room_bounds_x);
    BYTE(dungeon_room_index_prev) = dungeon_room_index;
    if ((link_tile_below & 0xcf) == 0x89) {
      dungeon_room_index = dung_hdr_travel_destinations[4];
      Dungeon_AdjustForTeleportDoors(dungeon_room_index - 1, 1);
    } else {
      if ((uint8)dungeon_room_index != (uint8)dungeon_room_index2) {
        BYTE(dungeon_room_index_prev) = dungeon_room_index2;
        Dungeon_AdjustAfterSpiralStairs();
      }
      dungeon_room_index += 1;
    }
    submodule_index = 2;
    if (room_transitioning_flags & 1) {
      link_is_on_lower_level ^= 1;
      link_is_on_lower_level_mirror = link_is_on_lower_level;
    }
    if (room_transitioning_flags & 2) {
      cur_palace_index_x2 ^= 2;
    }
  }
  room_transitioning_flags = 0;
  quadrant_fullsize_y = (dung_blastwall_flag_y || (kLayoutQuadrantFlags[composite_of_layout_and_quadrant] & (link_quadrant_y ? 8 : 4)) == 0) ? 2 : 0;
}

void HandleEdgeTransitionMovementSouth_DownBy16() {  // 82b76e
  link_y_coord += 16;
  Dungeon_StartInterRoomTrans_Down();
}

void Dung_HandleExitToOverworld() {  // 82b7ae
  SaveDungeonKeys();
  SaveQuadrantsToSram();
  saved_module_for_menu = 8;
  main_module_index = 15;
  submodule_index = 0;
  subsubmodule_index = 0;
  Dungeon_ResetTorchBackgroundAndPlayerInner();
}

void AdjustQuadrantAndCamera_right() {  // 82b8bd
  link_quadrant_x ^= 1;
  Dungeon_AdjustQuadrant();
  RoomBounds_AddA(&room_bounds_x);
  SetAndSaveVisitedQuadrantFlags();
}

void SetAndSaveVisitedQuadrantFlags() {  // 82b8cb
  dung_quadrants_visited |= kQuadrantVisitingFlags[(quadrant_fullsize_y << 2) + (quadrant_fullsize_x << 1) + link_quadrant_y + link_quadrant_x];
  save_dung_info[dungeon_room_index] |= dung_quadrants_visited;
}

void SaveQuadrantsToSram() {  // 82b8e5
  save_dung_info[dungeon_room_index] |= dung_quadrants_visited;
}

void AdjustQuadrantAndCamera_left() {  // 82b8f9
  link_quadrant_x ^= 1;
  Dungeon_AdjustQuadrant();
  RoomBounds_SubA(&room_bounds_x);
  SetAndSaveVisitedQuadrantFlags();
}

void AdjustQuadrantAndCamera_down() {  // 82b909
  link_quadrant_y ^= 2;
  Dungeon_AdjustQuadrant();
  RoomBounds_AddA(&room_bounds_y);
  SetAndSaveVisitedQuadrantFlags();
}

void AdjustQuadrantAndCamera_up() {  // 82b919
  link_quadrant_y ^= 2;
  Dungeon_AdjustQuadrant();
  RoomBounds_SubA(&room_bounds_y);
  SetAndSaveVisitedQuadrantFlags();
}

void Dungeon_FlagRoomData_Quadrants() {  // 82b929
  dung_quadrants_visited |= kQuadrantVisitingFlags[(quadrant_fullsize_y << 2) + (quadrant_fullsize_x << 1) + link_quadrant_y + link_quadrant_x];
  Dung_SaveDataForCurrentRoom();
}

void Dung_SaveDataForCurrentRoom() {  // 82b947
  save_dung_info[dungeon_room_index] =
    (dung_savegame_state_bits >> 4) |
    (dung_door_opened & 0xf000) |
    dung_quadrants_visited;
}

void HandleEdgeTransition_AdjustCameraBoundaries(uint8 arg) {  // 82b9dc
  static const uint16 kCameraBoundsX[] = { 127, 383, 127, 383 };
  static const uint16 kCameraBoundsY[] = { 120, 376, 136, 392 };
  overworld_screen_transition = arg;
  if (link_direction & 3) {
    uint8 t = link_direction & 1 ? 0 : 2;
    if (link_quadrant_x) t += 1;
    camera_x_coord_scroll_low = kCameraBoundsX[t];
    camera_x_coord_scroll_hi = camera_x_coord_scroll_low + 2;
  } else {
    uint8 t = link_direction & 4 ? 0 : 2;
    if (link_quadrant_y) t += 1;
    camera_y_coord_scroll_low = kCameraBoundsY[t];
    camera_y_coord_scroll_hi = camera_y_coord_scroll_low + 2;
  }
}

void Dungeon_AdjustQuadrant() {  // 82ba27
  composite_of_layout_and_quadrant = dung_layout_and_starting_quadrant | link_quadrant_y | link_quadrant_x;
}

void Dungeon_HandleCamera() {  // 82ba31
  if (link_y_vel) {
    int z = (allow_scroll_z && link_z_coord != 0xffff) ? link_z_coord : 0;
    int y = ((link_y_coord - z) & 0x1ff) + 12;
    int scrollamt = 1;
    int y_vel_abs = sign8(link_y_vel) ? (scrollamt = -1, -(int8)link_y_vel) : link_y_vel;
    do {
      int qm = quadrant_fullsize_y >> 1;
      if (sign8(link_y_vel)) {
        if (y > camera_y_coord_scroll_low)
          continue;
      } else {
        if (y < camera_y_coord_scroll_hi)
          continue;
        qm += 2;
      }
      if (BG2VOFS_copy2 == room_bounds_y.v[qm])
        continue;
      BG2VOFS_copy2 += scrollamt;
      if (dungeon_room_index == 0xffff)
        continue;

      BG1VOFS_subpixel += 0x8000;
      BG1VOFS_copy2 += (scrollamt >> 1) + ((BG1VOFS_subpixel & 0x8000) == 0);
      camera_y_coord_scroll_low += scrollamt;
      camera_y_coord_scroll_hi = camera_y_coord_scroll_low + 2;
    } while (--y_vel_abs);
  }
  if (link_x_vel) {
    int x = (link_x_coord & 0x1ff) + 8;
    int scrollamt = 1;
    int x_vel_abs = sign8(link_x_vel) ? (scrollamt = -1, -(int8)link_x_vel) : link_x_vel;
    do {
      int qm = quadrant_fullsize_x >> 1;
      if (sign8(link_x_vel)) {
        if (x > camera_x_coord_scroll_low)
          continue;
      } else {
        if (x < camera_x_coord_scroll_hi)
          continue;
        qm += 2;
      }
      if (BG2HOFS_copy2 == room_bounds_x.v[qm])
        continue;
      BG2HOFS_copy2 += scrollamt;
      if (dungeon_room_index == 0xffff)
        continue;
      BG1HOFS_subpixel += 0x8000;
      BG1HOFS_copy2 += (scrollamt >> 1) + ((BG1HOFS_subpixel & 0x8000) == 0);
      camera_x_coord_scroll_low += scrollamt;
      camera_x_coord_scroll_hi = camera_x_coord_scroll_low + 2;
    } while (--x_vel_abs);
  }
  if (dungeon_room_index != 0xffff) {
    if (dung_hdr_bg2_properties == 0 || dung_hdr_bg2_properties == 2 || dung_hdr_bg2_properties == 3 || dung_hdr_bg2_properties == 4 || dung_hdr_bg2_properties >= 6) {
      BG1HOFS_copy2 = BG2HOFS_copy2;
      BG1VOFS_copy2 = BG2VOFS_copy2;
    }
  }
}

void MirrorBg1Bg2Offs() {  // 82bb7b
  BG1HOFS_copy2 = BG2HOFS_copy2;
  BG1VOFS_copy2 = BG2VOFS_copy2;
}

void DungeonTransition_AdjustCamera_X(uint8 arg) {  // 82bdc8
  static const uint16 kUpDownScroll[4] = { 0, 256, 256, 0 };
  left_right_scroll_target = kUpDownScroll[arg * 2];
  left_right_scroll_target_end = kUpDownScroll[arg * 2 + 1];
}

void DungeonTransition_AdjustCamera_Y(uint8 arg) {  // 82bde2
  static const uint16 kUpDownScroll[4] = { 0, 272, 256, 16 };
  up_down_scroll_target = kUpDownScroll[arg];
  up_down_scroll_target_end = kUpDownScroll[arg + 1];
}

void DungeonTransition_ScrollRoom() {  // 82be03
  transition_counter++;
  int i = overworld_screen_transition;
  bg1_y_offset = bg1_x_offset = 0;
  uint16 t;

  if (i >= 2) {
    t = BG1HOFS_copy2 = BG2HOFS_copy2 = (BG2HOFS_copy2 + kStaircaseTab3[i]) & ~1;
    if (transition_counter >= kStaircaseTab4[i])
      link_x_coord += kStaircaseTab3[i];
  } else {
    t = BG1VOFS_copy2 = BG2VOFS_copy2 = (BG2VOFS_copy2 + kStaircaseTab3[i]) & ~1;
    if (transition_counter >= kStaircaseTab4[i])
      link_y_coord += kStaircaseTab3[i];
  }

  if ((t & 0x1fc) == (&up_down_scroll_target)[i]) {
    SetAndSaveVisitedQuadrantFlags();
    subsubmodule_index++;
    transition_counter = 0;
    if (submodule_index == 2)
      WaterFlood_BuildOneQuadrantForVRAM();
  }

}

void Module07_11_0A_ScrollCamera() {  // 82be75
  link_visibility_status = tagalong_var5 = 12;
  int i = overworld_screen_transition;
  BG1VOFS_copy2 = BG2VOFS_copy2 = (BG2VOFS_copy2 + kStaircaseTab3[i]) & ~3;
  if ((BG1VOFS_copy2 & 0x1fc) == (&up_down_scroll_target)[i]) {
    if (submodule_index >= 18)
      i += 2;
    link_y_coord += kStaircaseTab5[i];
    link_visibility_status = tagalong_var5 = 0;
    subsubmodule_index++;
  }
}

void DungeonTransition_FindSubtileLanding() {  // 82c110
  Dungeon_ResetTorchBackgroundAndPlayerInner();
  SubtileTransitionCalculateLanding();
  subsubmodule_index++;
  save_dung_info[dungeon_room_index] |= dung_quadrants_visited;
}

void SubtileTransitionCalculateLanding() {  // 82c12c
  int st = overworld_screen_transition;
  int a = CalculateTransitionLanding();
  if (a == 2)
    a = 1;
  else if (a == 4)
    a = 2;
  a += overworld_screen_transition * 5;

  int8 v = kStaircaseTab2[a];
  v -= (v < 0) ? -8 : 8;
  if (st & 2)
    BYTE(link_x_coord) = v;
  else
    BYTE(link_y_coord) = v;
  link_visibility_status = 0;
}

void Dungeon_InterRoomTrans_State13() {  // 82c162
  if (dung_want_lights_out | dung_want_lights_out_copy)
    ApplyPaletteFilter_bounce();
  Dungeon_IntraRoomTrans_State5();
}

void Dungeon_IntraRoomTrans_State5() {  // 82c170
  Link_HandleMovingAnimation_FullLongEntry();
  if (!DungeonTransition_MoveLinkOutDoor())
    return;
  if (byte_7E004E == 2 || byte_7E004E == 4)
    is_standing_in_doorway = 0;
  // todo: write to tiledetect_diag_state
  BYTE(force_move_any_direction) = 0;
  byte_7E004E = 0;
  overworld_screen_transition = 0;
  subsubmodule_index++;
}

bool DungeonTransition_MoveLinkOutDoor() {  // 82c191
  uint8 x = kStaircaseTab2[byte_7E004E + overworld_screen_transition * 5];
  int r0 = overworld_screen_transition & 1 ? -2 : 2;
  if ((overworld_screen_transition & 2) == 0) {
    link_y_coord += r0;
    return (BYTE(link_y_coord) & 0xfe) == x;
  } else {
    link_x_coord += r0;
    return (BYTE(link_x_coord) & 0xfe) == x;
  }
}

uint8 CalculateTransitionLanding() {  // 82c1e5
  int pos = ((link_y_coord + 12) & 0x1f8) << 3;
  pos |= ((link_x_coord + 8) & 0x1f8) >> 3;
  pos |= (link_is_on_lower_level ? 0x1000 : 0);
  uint8 a = dung_bg2_attr_table[pos];
  uint8 r = (a == 0 || a == 9) ? 0 :
    ((a &= 0x8e) == 0x80) ? 1 :
    (a == 0x82) ? 2 :
    (a == 0x84 || a == 0x88) ? 3 :
    (a == 0x86) ? 4 : 2;
  return byte_7E004E = r;
}

// This gets called when entering a dungeon from ow.
void Dungeon_LoadAndDrawRoom() {  // 82c57b
  int bak = HDMAEN_copy;
  HDMAEN_copy = 0;
  Dungeon_LoadRoom();
  overworld_screen_transition = 0;
  overworld_map_state = 0;
  for (dung_cur_quadrant_upload = 0; dung_cur_quadrant_upload != 16; ) {
    TileMapPrep_NotWaterOnTag();
    NMI_UploadTilemap();
    Dungeon_PrepareNextRoomQuadrantUpload();
    NMI_UploadTilemap();
  }
  HDMAEN_copy = bak;
  nmi_subroutine_index = 0;
  overworld_map_state = 0;
  subsubmodule_index = 0;
}

void Dungeon_LoadEntrance() {  // 82d8b3
  player_is_indoors = 1;

  if (death_var5) {
    death_var5 = 0;
  } else {
    overworld_area_index_exit = overworld_area_index;
    TM_copy_exit = WORD(TM_copy);
    BG2VOFS_copy2_exit = BG2VOFS_copy2;
    BG2HOFS_copy2_exit = BG2HOFS_copy2;
    link_y_coord_exit = link_y_coord;
    link_x_coord_exit = link_x_coord;
    camera_y_coord_scroll_low_exit = camera_y_coord_scroll_low;
    camera_x_coord_scroll_low_exit = camera_x_coord_scroll_low;
    overworld_screen_index_exit = overworld_screen_index;
    map16_load_src_off_exit = map16_load_src_off;
    overworld_screen_index = 0;
    overlay_index = 0;
    ow_scroll_vars0_exit = ow_scroll_vars0;
    up_down_scroll_target_exit = up_down_scroll_target;
    up_down_scroll_target_end_exit = up_down_scroll_target_end;
    left_right_scroll_target_exit = left_right_scroll_target;
    left_right_scroll_target_end_exit = left_right_scroll_target_end;
    overworld_unk1_exit = overworld_unk1;
    overworld_unk1_neg_exit = overworld_unk1_neg;
    overworld_unk3_exit = overworld_unk3;
    overworld_unk3_neg_exit = overworld_unk3_neg;
    byte_7EC164 = byte_7E0AA0;
    main_tile_theme_index_exit = main_tile_theme_index;
    aux_tile_theme_index_exit = aux_tile_theme_index;
    sprite_graphics_index_exit = sprite_graphics_index;
  }
  bg1_y_offset = bg1_x_offset = 0;
  WORD(death_var5) = 0;
  if (WORD(follower_indicator) == 4 || WORD(death_var4)) {
    int i = which_starting_point;
    WORD(which_entrance) = kStartingPoint_entrance[i];
    dungeon_room_index = dungeon_room_index2 = kStartingPoint_rooms[i];
    BG1VOFS_copy = BG2VOFS_copy = BG1VOFS_copy2 = BG2VOFS_copy2 = kStartingPoint_scrollY[i];
    BG1HOFS_copy = BG2HOFS_copy = BG1HOFS_copy2 = BG2HOFS_copy2 = kStartingPoint_scrollX[i];
    if (WORD(sram_progress_indicator)) {
      link_y_coord = kStartingPoint_playerY[i];
      link_x_coord = kStartingPoint_playerX[i];
    }
    camera_y_coord_scroll_low = kStartingPoint_cameraY[i];
    camera_y_coord_scroll_hi = camera_y_coord_scroll_low + 2;
    camera_x_coord_scroll_low = kStartingPoint_cameraX[i];
    camera_x_coord_scroll_hi = camera_x_coord_scroll_low + 2;
    tilemap_location_calc_mask = 0x1f8;
    ow_entrance_value = kStartingPoint_doorSettings[i];
    up_down_scroll_target = 0;
    up_down_scroll_target_end = 0x110;
    left_right_scroll_target = 0;
    left_right_scroll_target_end = 0x100;
    room_bounds_y.a0 = kStartingPoint_relativeCoords[i * 8 + 0] << 8;
    room_bounds_y.b0 = kStartingPoint_relativeCoords[i * 8 + 1] << 8;
    room_bounds_y.a1 = kStartingPoint_relativeCoords[i * 8 + 2] << 8 | 0x10;
    room_bounds_y.b1 = kStartingPoint_relativeCoords[i * 8 + 3] << 8 | 0x10;
    room_bounds_x.a0 = kStartingPoint_relativeCoords[i * 8 + 4] << 8;
    room_bounds_x.b0 = kStartingPoint_relativeCoords[i * 8 + 5] << 8;
    room_bounds_x.a1 = kStartingPoint_relativeCoords[i * 8 + 6] << 8;
    room_bounds_x.b1 = kStartingPoint_relativeCoords[i * 8 + 7] << 8;

    link_direction_facing = 2;
    main_tile_theme_index = kStartingPoint_blockset[i];
    dung_cur_floor = kStartingPoint_floor[i];
    BYTE(cur_palace_index_x2) = kStartingPoint_palace[i];
    is_standing_in_doorway = 0;
    link_is_on_lower_level = kStartingPoint_startingBg[i] >> 4;
    link_is_on_lower_level_mirror = kStartingPoint_startingBg[i] & 0xf;
    quadrant_fullsize_x = kStartingPoint_quadrant1[i] >> 4;
    quadrant_fullsize_y = kStartingPoint_quadrant1[i] & 0xf;
    link_quadrant_x = kStartingPoint_quadrant2[i] >> 4;
    link_quadrant_y = kStartingPoint_quadrant2[i] & 0xf;

    buffer_for_playing_songs = kStartingPoint_musicTrack[i];
    if (i == 0 && sram_progress_indicator == 0)
      buffer_for_playing_songs = 0xff;
    death_var4 = 0;
  } else {
    int i = which_entrance;
    dungeon_room_index = dungeon_room_index2 = kEntranceData_rooms[i];
    BG1VOFS_copy = BG2VOFS_copy = BG1VOFS_copy2 = BG2VOFS_copy2 = kEntranceData_scrollY[i];
    BG1HOFS_copy = BG2HOFS_copy = BG1HOFS_copy2 = BG2HOFS_copy2 = kEntranceData_scrollX[i];
    if (WORD(sram_progress_indicator)) {
      link_y_coord = kEntranceData_playerY[i];
      link_x_coord = kEntranceData_playerX[i];
    }
    camera_y_coord_scroll_low = kEntranceData_cameraY[i];
    camera_y_coord_scroll_hi = camera_y_coord_scroll_low + 2;
    camera_x_coord_scroll_low = kEntranceData_cameraX[i];
    camera_x_coord_scroll_hi = camera_x_coord_scroll_low + 2;
    tilemap_location_calc_mask = 0x1f8;
    ow_entrance_value = kEntranceData_doorSettings[i];
    big_rock_starting_address = 0;
    up_down_scroll_target = 0;
    up_down_scroll_target_end = 0x110;
    left_right_scroll_target = 0;
    left_right_scroll_target_end = 0x100;

    room_bounds_y.a0 = kEntranceData_relativeCoords[i * 8 + 0] << 8;
    room_bounds_y.b0 = kEntranceData_relativeCoords[i * 8 + 1] << 8;
    room_bounds_y.a1 = kEntranceData_relativeCoords[i * 8 + 2] << 8 | 0x10;
    room_bounds_y.b1 = kEntranceData_relativeCoords[i * 8 + 3] << 8 | 0x10;

    room_bounds_x.a0 = kEntranceData_relativeCoords[i * 8 + 4] << 8;
    room_bounds_x.b0 = kEntranceData_relativeCoords[i * 8 + 5] << 8;
    room_bounds_x.a1 = kEntranceData_relativeCoords[i * 8 + 6] << 8;
    room_bounds_x.b1 = kEntranceData_relativeCoords[i * 8 + 7] << 8;

    link_direction_facing = (i == 0 || i == 0x43) ? 2 : 0;
    main_tile_theme_index = kEntranceData_blockset[i];
    buffer_for_playing_songs = kEntranceData_musicTrack[i];
    if (buffer_for_playing_songs == 3 && sram_progress_indicator >= 2)
      buffer_for_playing_songs = 18;

    dung_cur_floor = kEntranceData_floor[i];
    BYTE(cur_palace_index_x2) = kEntranceData_palace[i];
    is_standing_in_doorway = kEntranceData_doorwayOrientation[i];
    link_is_on_lower_level = kEntranceData_startingBg[i] >> 4;
    link_is_on_lower_level_mirror = kEntranceData_startingBg[i] & 0xf;
    quadrant_fullsize_x = kEntranceData_quadrant1[i] >> 4;
    quadrant_fullsize_y = kEntranceData_quadrant1[i] & 0xf;
    link_quadrant_x = kEntranceData_quadrant2[i] >> 4;
    link_quadrant_y = kEntranceData_quadrant2[i] & 0xf;

    if (dungeon_room_index >= 0x100)
      dung_cur_floor = 0;
  }
  player_oam_x_offset = player_oam_y_offset = 0x80;
  link_direction_mask_a = link_direction_mask_b = 0xf;
  BYTE(link_z_coord) = link_actual_vel_z = 0xff;
  memcpy(movable_block_datas, kMovableBlockDataInit, kMovableBlockDataInit_SIZE);
  memcpy(&movable_block_datas[99], kTorchDataInit, 116); // junk
  memcpy(dung_torch_data, kTorchDataInit, kTorchDataInit_SIZE);
  memcpy(&dung_torch_data[144], kTorchDataJunk, kTorchDataJunk_SIZE);

  memset(memorized_tile_addr, 0, 0x100);
  memset(pots_revealed_in_room, 0, 0x280);
  orange_blue_barrier_state = 0;
  byte_7E04BC = 0;
}

void PushBlock_Slide(uint8 j) {  // 87edb5
  if (submodule_index)
    return;
  int i = (index_of_changable_dungeon_objs[1] - 1) * 2 == j;
  pushedblocks_maybe_timeout = 9;
  pushedblocks_some_index = 0;
  PushBlock_ApplyVelocity(i);
  int y = (uint8)pushedblocks_y_lo[i] | (uint8)pushedblocks_y_hi[i] << 8;
  int x = (uint8)pushedblocks_x_lo[i] | (uint8)pushedblocks_x_hi[i] << 8;
  PushBlock_HandleCollision(i, x, y);
}

void PushBlock_HandleFalling(uint8 y) {  // 87edf9
  y >>= 1;

  if (!sign8(--pushedblocks_maybe_timeout))
    return;

  pushedblocks_maybe_timeout = 9;

  if (++pushedblocks_some_index == 4) {
    BYTE(dung_replacement_tile_state[y]) = 0;
    pushedblocks_some_index = 0;
    int i = (index_of_changable_dungeon_objs[1] - 1) == y;
    index_of_changable_dungeon_objs[i] = 0;
  }
}

void PushBlock_ApplyVelocity(uint8 i) {  // 87ee35
  static const uint8 kPushedBlockDirMask[] = { 0x8, 0x4, 0x2, 0x1 };
  uint8 m = kPushedBlockDirMask[(uint8)pushedblock_facing[i] >> 1];
  uint32 o;
  link_actual_vel_x = link_actual_vel_y = 0;
  if (m & 3) {
    int8 vel = (m & 2) ? -12 : 12;
    link_actual_vel_x = vel;
    o = (pushedblocks_subpixel[i] | pushedblocks_x_lo[i] << 8 | pushedblocks_x_hi[i] << 16) + vel * 16;
    pushedblocks_subpixel[i] = (uint8)o;
    pushedblocks_x_lo[i] = (uint8)(o >> 8);
    pushedblocks_x_hi[i] = (uint8)(o >> 16);
  } else {
    int8 vel = (m & 8) ? -12 : 12;
    link_actual_vel_y = vel;
    o = (pushedblocks_subpixel[i] | pushedblocks_y_lo[i] << 8 | pushedblocks_y_hi[i] << 16);
    o += vel * 16;
    pushedblocks_subpixel[i] = (uint8)o;
    pushedblocks_y_lo[i] = (uint8)(o >> 8);
    pushedblocks_y_hi[i] = (uint8)(o >> 16);
  }
  if (((o >> 8) & 0xf) == (uint8)pushedblocks_target[i]) {
    int j = index_of_changable_dungeon_objs[i] - 1;
    dung_replacement_tile_state[j]++;
    link_cant_change_direction &= ~0x4;
    bitmask_of_dragstate &= ~0x4;
  }
  uint16 x = pushedblocks_x_lo[i] | pushedblocks_x_hi[i] << 8;
  uint16 y = pushedblocks_y_lo[i] | pushedblocks_y_hi[i] << 8;
  for (int j = 15; j >= 0; j--) {
    if (sprite_state[j] >= 9) {
      uint16 sx = sprite_x_lo[j] | sprite_x_hi[j] << 8;
      uint16 sy = sprite_y_lo[j] | sprite_y_hi[j] << 8;
      if ((uint16)(x - sx + 0x10) < 0x20 && (uint16)(y - sy + 0x10) < 0x20) {
        sprite_F[j] = 8;
        static const uint8 kPushBlockTab1[] = { 0x0, 0x0, 0xe0, 0x20 };
        static const uint8 kPushBlockTab2[] = { 0xe0, 0x20, 0x0, 0x0 };
        int k = (uint8)pushedblock_facing[i] >> 1;
        sprite_x_recoil[j] = kPushBlockTab1[k];
        sprite_y_recoil[j] = kPushBlockTab2[k];
      }
    }
  }
}

void PushBlock_HandleCollision(uint8 i, uint16 x, uint16 y) {  // 87efb9
  static const uint8 kPushBlock_A[] = { 0, 0, 8, 8 };
  static const uint8 kPushBlock_B[] = { 15, 15, 23, 23 };
  static const uint8 kPushBlock_D[] = { 15, 15, 15, 15 };
  static const uint8 kPushBlock_C[] = { 0x0, 0x0, 0x0, 0x0 };
  static const uint8 kPushBlock_E[] = { 8, 24, 0, 16 };
  static const uint8 kPushBlock_F[] = { 15, 0, 15, 0 };

  link_y_coord_safe_return_hi = link_y_coord >> 8;
  link_x_coord_safe_return_hi = link_x_coord >> 8;

  int dir = 3;
  uint8 m = link_direction & 0xf;
  while (!(m & 1)) {
    m >>= 1;
    if (--dir < 0)
      return;
  }
  int l = (dir < 2) ? link_x_coord : link_y_coord;
  int o = (dir < 2) ? x : y;

  uint16 r0 = l + kPushBlock_A[dir];
  uint16 r2 = l + kPushBlock_B[dir];
  uint16 r4 = o + kPushBlock_C[dir];
  uint16 r6 = o + kPushBlock_D[dir];

  uint16 *coord_p = (dir < 2) ? &link_y_coord : &link_x_coord;
  uint16 r8 = *coord_p + kPushBlock_E[dir];
  uint16 r10 = ((dir < 2) ? y : x) + kPushBlock_F[dir];

  bitmask_of_dragstate &= ~4;

  if (r0 >= r4 && r0 < r6 || r2 >= r4 && r2 < r6) {
    if (link_direction_facing == pushedblock_facing[i])
      bitmask_of_dragstate |= index_of_changable_dungeon_objs[i] ? 4 : 1;
    if (dir & 1 ? (r8 >= r10 && (uint16)(r8 - r10) < 8) : (uint16)(r8 - r10) >= 0xfff8) {
      *coord_p -= r8 - r10;
      *(dir & 2 ? &link_x_vel : &link_y_vel) -= r8 - r10;
    }
  }
  HandleIndoorCameraAndDoors();
}

void Sprite_Dungeon_DrawAllPushBlocks() {  // 87f0ac
  for (int i = 1; i >= 0; i--)
    if (index_of_changable_dungeon_objs[i])
      Sprite_HandlePushedBlocks_One(i);
}

void UsedForStraightInterRoomStaircase() {  // 87f25a
  int i = 9;
  do {
    if (ancilla_type[i] == 13)
      ancilla_type[i] = 0;
  } while (--i >= 0);
  if (link_animation_steps >= 5)
    link_animation_steps = 0;
  link_subpixel_x = 0;
  link_subpixel_y = 0;
  some_animation_timer_steps = 0;
  link_timer_push_get_tired = 28;
  countdown_timer_for_staircases = 32;
  link_disable_sprite_damage = 1;
  Ancilla_Sfx2_Near(which_staircase_index & 4 ? 0x18 : 0x16);

  tiledetect_which_y_pos[1] = link_x_coord + (which_staircase_index & 4 ? -15 : 16);
  tiledetect_which_y_pos[0] = link_y_coord;
}

void HandleLinkOnSpiralStairs() {  // 87f2c1
  link_x_coord_prev = link_x_coord;
  link_y_coord_prev = link_y_coord;
  if (some_animation_timer_steps)
    return;

  link_give_damage = 0;
  link_incapacitated_timer = 0;
  link_auxiliary_state = 0;

  if (which_staircase_index & 4) {
    link_actual_vel_y = -2;
    if (sign8(--link_timer_push_get_tired)) {
      link_timer_push_get_tired = 0;
      link_actual_vel_y = 0;
      link_actual_vel_x = -2;
    }
  } else {
    link_actual_vel_y = -2;
    if (sign8(--link_timer_push_get_tired)) {
      link_timer_push_get_tired = 0;
      link_actual_vel_y = -2;
      link_actual_vel_x = 2;
    }
  }
  Link_MovePosition();
  Link_HandleMovingAnimation_StartWithDash();
  if (!link_timer_push_get_tired && sign8(--countdown_timer_for_staircases)) {
    countdown_timer_for_staircases = 0;
    link_direction_facing = (which_staircase_index & 4) ? 4 : 6;
  }

  int8 xd = link_x_coord - tiledetect_which_y_pos[1];
  if (xd < 0)
    xd = -xd;
  if (xd)
    return;

  RepositionLinkAfterSpiralStairs();
  if (follower_indicator)
    Follower_Initialize();

  tiledetect_which_y_pos[1] = link_x_coord + ((which_staircase_index & 4) ? -8 : 12);
  some_animation_timer_steps = 1;
  countdown_timer_for_staircases = 6;
  Ancilla_Sfx2_Near(which_staircase_index & 4 ? 25 : 23);
}

void SpiralStairs_FindLandingSpot() {  // 87f391
  link_give_damage = 0;
  link_incapacitated_timer = 0;
  link_auxiliary_state = 0;
  link_disable_sprite_damage = 0;
  link_x_coord_prev = link_x_coord;
  link_y_coord_prev = link_y_coord;
  if (sign8(--countdown_timer_for_staircases)) {
    countdown_timer_for_staircases = 0;
    link_direction_facing = 2;
  }
  link_actual_vel_x = 4, link_actual_vel_y = 0;
  if (which_staircase_index & 4)
    link_actual_vel_x = -4, link_actual_vel_y = 2;
  if (some_animation_timer_steps == 2)
    link_actual_vel_x = 0, link_actual_vel_y = 16;
  Link_MovePosition();
  Link_HandleMovingAnimation_StartWithDash();
  if ((uint8)link_x_coord == (uint8)tiledetect_which_y_pos[1])
    some_animation_timer_steps = 2;
}

void Dungeon_HandleLayerEffect() {  // 8afe80
  kDungeon_Effect_Handler[dung_hdr_collision_2]();
}

void LayerEffect_Nothing() {  // 8afe87
}

void LayerEffect_Scroll() {  // 8afe88
  if (dung_savegame_state_bits & 0x8000) {
    dung_hdr_collision_2 = 0;
    return;
  }
  dung_floor_x_vel = dung_floor_y_vel = 0;
  if (dung_floor_move_flags & 1)
    return;
  int t = dung_some_subpixel[1] + 0x80;
  dung_some_subpixel[1] = t;
  t >>= 8;
  if (dung_floor_move_flags & 2)
    t = -t;

  if (dung_floor_move_flags < 4) {
    dung_floor_x_vel = t;
    dung_floor_x_offs -= t;
    BG1HOFS_copy2 = BG2HOFS_copy2 + dung_floor_x_offs;
  } else {
    dung_floor_y_vel = t;
    dung_floor_y_offs -= t;
    BG1VOFS_copy2 = BG2VOFS_copy2 + dung_floor_y_offs;
  }
}

void LayerEffect_Trinexx() {  // 8afeee
  dung_floor_x_offs += dung_floor_x_vel;
  dung_floor_y_offs += dung_floor_y_vel;
  dung_floor_x_vel = 0;
  dung_floor_y_vel = 0;
}

void LayerEffect_Agahnim2() {  // 8aff0d
  int j = frame_counter & 0x7f;
  if (j == 3 || j == 36) {
    main_palette_buffer[0x6d] = 0x1d59;
    main_palette_buffer[0x6e] = 0x25ff;
    main_palette_buffer[0x77] = main_palette_buffer[0x6f] = 0x1a;
    flag_update_cgram_in_nmi++;
  } else if (j == 5 || j == 38) {
    main_palette_buffer[0x6d] = aux_palette_buffer[0x6d];
    main_palette_buffer[0x6e] = aux_palette_buffer[0x6e];
    main_palette_buffer[0x77] = main_palette_buffer[0x6f] = aux_palette_buffer[0x6f];
    flag_update_cgram_in_nmi++;
  }
  TS_copy = 2;
}

void LayerEffect_InvisibleFloor() {  // 8aff5d
  int count = 0;
  for (int i = 0; i < 16; i++)
    count += (dung_object_tilemap_pos[i] & 0x8000) != 0;

  uint16 x = 0x2940, y = 0x4e60;
  if (count == 0)
    x = y = 0;

  if (aux_palette_buffer[0x7b] != x) {
    main_palette_buffer[0x7b] = aux_palette_buffer[0x7b] = x;
    main_palette_buffer[0x7c] = aux_palette_buffer[0x7c] = y;
    flag_update_cgram_in_nmi++;
  }
  TS_copy = 2;
}

void LayerEffect_Ganon() {  // 8affa4
  int count = 0;
  for (int i = 0; i < 16; i++)
    count += (dung_object_tilemap_pos[i] & 0x8000) != 0;

  byte_7E04C5 = count;
  if (count == 0) {
    TS_copy = 0;
    CGADSUB_copy = 0xb3;
  } else if (count == 1) {
    TS_copy = 2;
    CGADSUB_copy = 0x70;
  } else {
    TS_copy = 0;
    CGADSUB_copy = 0x70;
  }
}

void LayerEffect_WaterRapids() {  // 8affde
  int t;
  dung_some_subpixel[1] = t = dung_some_subpixel[1] + 0x80;
  dung_floor_x_vel = -(t >> 8);
}

void Dungeon_LoadCustomTileAttr() {  // 8e942a
  memcpy(&attributes_for_tile[0x140], &kDungAttrsForTile[kDungAttrsForTile_Offs[aux_tile_theme_index]], 0x80);
}

void Link_CheckBunnyStatus() {  // 8ffd22
  if (link_player_handler_state == kPlayerState_RecoilWall) {
    link_player_handler_state =
      !link_is_bunny_mirror ? kPlayerState_Ground :
      link_item_moon_pearl ? kPlayerState_TempBunny : kPlayerState_PermaBunny;
  }
}

void CrystalCutscene_Initialize() {  // 9ecce3
  static const uint16 kCrystalMaiden_Pal[8] = { 0, 0x3821, 0x4463, 0x54a5, 0x5ce7, 0x6d29, 0x79ad, 0x7e10 };

  CGADSUB_copy = 0x33;
  BYTE(palette_filter_countdown) = 0;
  BYTE(darkening_or_lightening_screen) = 0;
  Palette_AssertTranslucencySwap();
  PaletteFilter_Crystal();
  for (int i = 0; i < 8; i++)
    main_palette_buffer[112 + i] = kCrystalMaiden_Pal[i];
  flag_update_cgram_in_nmi++;
  CrystalCutscene_SpawnMaiden();
  CrystalCutscene_InitializePolyhedral();
}

void CrystalCutscene_SpawnMaiden() {  // 9ecd48
  memset(sprite_state, 0, 16);
  SpriteSpawnInfo info;
  int j = Sprite_SpawnDynamically(0, 0xab, &info);
  sprite_x_hi[j] = link_x_coord >> 8;
  sprite_y_hi[j] = link_y_coord >> 8;
  sprite_x_lo[j] = 0x78;
  sprite_y_lo[j] = 0x7c;
  sprite_D[j] = 1;
  sprite_oam_flags[j] = 0xb;
  sprite_subtype2[j] = 0;
  sprite_floor[j] = 0;
  sprite_A[j] = Ancilla_TerminateSelectInteractives(j);
  item_receipt_method = 0;
  if (BYTE(cur_palace_index_x2) == 24) {
    sprite_oam_flags[j] = 9;
    follower_indicator = 1;
  } else {
    follower_indicator = 6;
  }
  LoadFollowerGraphics();
  follower_indicator = 0;
  dung_floor_x_offs = BG2HOFS_copy2 - link_x_coord + 0x79;
  dung_floor_y_offs = 0x30 - (uint8)BG1VOFS_copy2;
  dung_hdr_collision_2_mirror = 1;
}